Skip to content

feat: add status.json writer for macOS menu bar app#1

Open
damahua wants to merge 12 commits into
masterfrom
feature/menubar-status
Open

feat: add status.json writer for macOS menu bar app#1
damahua wants to merge 12 commits into
masterfrom
feature/menubar-status

Conversation

@damahua

@damahua damahua commented Mar 23, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds lib/write-status.mjs that aggregates today's session metrics and writes ~/.claude/hook-hero/status.json atomically
  • Hooks (SessionStart, SessionEnd, Stop, UserPromptSubmit) trigger status file updates as a best-effort side effect
  • Adds interaction_time_sec accumulator to session buffers (tracks AI turn duration)
  • Adds last_prompt_ts to buffers (records when user last prompted)
  • Persists interaction_time_sec in session summaries on SessionEnd
  • Adds config/status-schema.json as a consumer reference for the menu bar app

Status File

Written to ~/.claude/hook-hero/status.json with schema:

  • active_sessions — count of open sessions
  • today.sessions_total, cost_usd, tokens, prompts, tool_calls, git
  • today.total_duration_sec — wall-clock session time
  • today.interaction_time_sec — AI interaction time
  • active[] — per-session breakdown (project, duration, cost, prompts)
  • Atomic writes (temp file + rename)

Test plan

  • 54 tests pass (17 new for write-status, 3 new for stop, 1 new for user-prompt-submit, 2 new for session-end, 2 integration tests)
  • Manual smoke test: install plugin, verify status.json updates on hook events
  • Verify menu bar app reads and displays correctly

🤖 Generated with Claude Code

damahua and others added 12 commits March 22, 2026 12:45
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant