feat: add status.json writer for macOS menu bar app#1
Open
damahua wants to merge 12 commits into
Open
Conversation
Design for showing agent metrics (active sessions, interaction time, cost) in the macOS menu bar. Two-project approach: Hook Hero writes a status.json file, a separate Swift app reads and displays it. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fixes from spec review: - Add schema_version field for forward compatibility - Add cache_write to token schema - Document atomic write strategy (temp file + rename) - Add interaction_time_sec accumulator to avoid event file scans - Clarify duration_sec is wall-clock, not interaction time - Document midnight reset mechanism - Acknowledge staleness during long AI turns Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use positive match (/\.(json|buf)$/) for buffer file filter instead of deny-list, avoiding .debug/.lock/.batch phantom entries - Add nowMs parameter to handleStop for deterministic interaction time tests (inject known time instead of depending on Date.now) - Add test for missing last_prompt_ts edge case Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implements writeStatus(store, costRates?) which aggregates completed session summaries (sessions/YYYY-MM-DD/*.json) and active buffers (buffer/*.buf|json) into a single atomic status.json write. Covers tasks 1-4: empty-state schema, completed-session aggregation with 2-decimal cost rounding, active buffer session counting/token accumulation, and per-session cost calculation via calculateCost(). Skips .lock/.debug/.batch files. Includes 17 passing tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Round cost_usd once after all sources summed (not per-phase) - Simplify calculateCost null check (returns null, not NaN) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tasks 5 & 6: Stop hook now accepts an optional nowMs param for testability and accumulates interaction_time_sec by computing the delta from last_prompt_ts to stop time. UserPromptSubmit records last_prompt_ts in the buffer so Stop can measure how long the user waited for each AI turn. Also add entry-point guards to both modules so they can be imported in tests without triggering process.exit(0). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Stop hook accumulates interaction_time_sec in buffer (time since last user prompt to agent stop). Accepts optional nowMs for testing. - UserPromptSubmit records last_prompt_ts in buffer. - Fix pre-existing stop tests to use transcript_path API. - Add entry-point guards to prevent process.exit during test imports. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…me_sec - Task 7: aggregate interaction_time_sec from both completed sessions and active buffers in write-status.mjs - Task 8: include interaction_time_sec in session summary written on SessionEnd - Task 9: add best-effort writeStatus() call at the end of each hook handler (session-start, session-end, stop, user-prompt-submit) so status.json stays current after every hook invocation - Add tests for all three tasks including a full lifecycle integration test Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sum wall-clock duration from completed sessions (timing.duration_seconds) and active buffers (now - start_time) for total time in sessions today. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sessions without a model field (e.g., reconstructed buffers) now use claude-sonnet-4-6 rates instead of showing $0.00. Also show directory name instead of "unknown" for non-git sessions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Recalculate cost from raw tokens instead of trusting baked estimated_cost_usd (null for 67% of sessions with unknown models) - Use opus rates as default model (matches dashboard hardcoded rates) - Skip completed sessions that have active buffers (avoids double-counting) - Include active sessions in sessions_total (matches dashboard) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
lib/write-status.mjsthat aggregates today's session metrics and writes~/.claude/hook-hero/status.jsonatomicallyinteraction_time_secaccumulator to session buffers (tracks AI turn duration)last_prompt_tsto buffers (records when user last prompted)interaction_time_secin session summaries on SessionEndconfig/status-schema.jsonas a consumer reference for the menu bar appStatus File
Written to
~/.claude/hook-hero/status.jsonwith schema:active_sessions— count of open sessionstoday.sessions_total,cost_usd,tokens,prompts,tool_calls,gittoday.total_duration_sec— wall-clock session timetoday.interaction_time_sec— AI interaction timeactive[]— per-session breakdown (project, duration, cost, prompts)Test plan
🤖 Generated with Claude Code