Skip to content

Add profile usage summary command#4

Open
Hobeom wants to merge 1 commit into
Digital-Threads:masterfrom
Hobeom:feat/profile-usage
Open

Add profile usage summary command#4
Hobeom wants to merge 1 commit into
Digital-Threads:masterfrom
Hobeom:feat/profile-usage

Conversation

@Hobeom
Copy link
Copy Markdown

@Hobeom Hobeom commented May 14, 2026

Summary

  • add aimux usage to summarize transcript token usage by profile
  • support --profile, --since, and --all filters
  • deduplicate repeated transcript entries by request id and surface unmapped sessions as unknown
  • document the new command in the README

Notes

This builds on the existing session attribution logic from .claude.json and aimux session history. Historical shared transcripts that cannot be mapped to a profile are intentionally shown as unknown rather than guessed.

Closes #1

Test plan

  • npm test
  • npm run build
  • node dist/cli.js usage --since 24h

Copy link
Copy Markdown

@shahinyanm1-work shahinyanm1-work left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for picking this up — the structure is good and the tests are a nice touch. Two things blocking the merge plus a couple of smaller notes.

Blockers

  1. requestKey includes sessionId, so when a session gets forked (claude --resume <id> --fork-session, which aimux does automatically on cross-profile attach), the original transcript and the forked one share the same requestId but live under different session ids. The dedupe key treats them as two requests and double-counts the tokens. Drop sessionId from the key when a requestId is present — request:<requestId> is enough.

  2. The walker reads every .jsonl under projects/ without filtering subagent transcripts. Real installs have thousands of those (classifier, memory, task-journal, etc.) — each one is a real assistant turn with message.usage, so they'll inflate "unknown" by a huge factor. sessionScanner.ts already has the logic — quickFirstLineType(path) === 'queue-operation' and parseSessionJsonl(path).isSubagent. Reuse those before parsing usage lines.

Should fix before merge

  1. estimatedCostUsd is summed into the summary but never shown in the table. Either add a COST column (formatted as $X.XX) or drop the field — right now it's dead work.

  2. In requestKey, the fallback when no id/uuid/requestId is found is Math.random(). That defeats the dedupe — every line gets a unique key. Either use a stable surrogate like ${sessionId}:line:${index} or just continue and skip the line.

Nit

  1. usage.test.ts calls setAimuxDir in beforeEach but never restores it. If something else in the test run depends on the default aimux dir, it'll pick up the tmpdir from this test. Save the previous value and restore in afterEach.

Happy to discuss any of these if my read of the code is off. Once 1 and 2 are sorted I'll take another pass.

@Shahinyanm Shahinyanm dismissed shahinyanm1-work’s stale review May 26, 2026 12:52

Re-posting from primary maintainer account.

Copy link
Copy Markdown
Member

@Shahinyanm Shahinyanm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for picking this up — structure is good and the tests are a nice touch. Two things blocking the merge plus a couple of smaller notes.

Blockers

  1. requestKey includes sessionId, so when a session gets forked (claude --resume <id> --fork-session, which aimux does automatically on cross-profile attach), the original transcript and the forked one share the same requestId but live under different session ids. The dedupe key treats them as two requests and double-counts the tokens. Drop sessionId from the key when a requestId is present — request:<requestId> is enough.

  2. The walker reads every .jsonl under projects/ without filtering subagent transcripts. Real installs have thousands of those (classifier, memory, task-journal, etc.) — each one is a real assistant turn with message.usage, so they'll inflate "unknown" by a huge factor. sessionScanner.ts already has the logic — quickFirstLineType(path) === 'queue-operation' and parseSessionJsonl(path).isSubagent. Reuse those before parsing usage lines.

Should fix before merge

  1. estimatedCostUsd is summed into the summary but never shown in the table. Either add a COST column (formatted as $X.XX) or drop the field — right now it's dead work.

  2. In requestKey, the fallback when no id/uuid/requestId is found is Math.random(). That defeats the dedupe — every line gets a unique key. Either use a stable surrogate like ${sessionId}:line:${index} or just continue and skip the line.

Nit

  1. usage.test.ts calls setAimuxDir in beforeEach but never restores it. If something else in the test run depends on the default aimux dir, it'll pick up the tmpdir from this test. Save the previous value and restore in afterEach.

Happy to discuss any of these if my read is off. Once 1 and 2 are sorted I'll take another pass.

@Hobeom Hobeom force-pushed the feat/profile-usage branch 2 times, most recently from 9fd3424 to a9d0be4 Compare May 27, 2026 01:37
- Change requestId dedupe to use request:<requestId> without sessionId, so forked sessions do not double-count.

- Reuse the existing subagent filters before parsing usage lines.

- Keep estimatedCostUsd and surface it via a COST column.

- Replace the random fallback key with a stable session line key.

- Restore the previous aimux dir in usage tests after each run.

- Add usage-specific hardening for malformed/non-numeric token values.
@Hobeom Hobeom force-pushed the feat/profile-usage branch from a9d0be4 to 530db32 Compare May 27, 2026 05:52
@Hobeom
Copy link
Copy Markdown
Author

Hobeom commented May 27, 2026

Thanks for the careful review. I updated the branch to address each item.

Blockers

  1. requestKey includes sessionId

Fixed by changing the requestId path to use request:<requestId> without sessionId. This keeps forked transcripts that share the same request id from being counted twice. I also added coverage for the forked-session case.

  1. The walker reads every .jsonl under projects/ without filtering subagent transcripts

Fixed by reusing the existing scanner checks before parsing usage lines: the fast quickFirstLineType(filePath) === 'queue-operation' check, followed by parseSessionJsonl(filePath).isSubagent.

Should fix before merge

  1. estimatedCostUsd is summed but not shown

Kept estimatedCostUsd and surfaced it in the output table as a COST column formatted as $X.XX.

  1. Math.random() fallback defeats dedupe

Replaced the random fallback with a stable ${sessionId}:line:${lineIndex} key.

Nit

  1. setAimuxDir is not restored in tests

The usage tests now save the previous aimux dir and restore it in afterEach.

I also included one usage-specific hardening case: malformed or non-numeric usage values are ignored instead of corrupting totals, with test coverage.

Validation:

  • npm test — 100 passed
  • npm run build
  • node dist/cli.js usage --since 24h

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.

Feature request: show usage per profile

3 participants