Sits between 'Reading the dashboard' (what the numbers mean) and
'How it reads data' (where the data comes from) so the feature lands
as the natural next step: here is how to act on the patterns you saw.
Rename slice(0, N) and length - N literals used in waste-finding
preview strings to GHOST_NAMES_PREVIEW, GHOST_CLEANUP_COMMANDS_LIMIT,
TOP_ITEMS_PREVIEW, MISSING_IGNORE_PATHS_PREVIEW, JUNK_DIRS_IGNORE_PREVIEW.
Drop the punctuation double-hyphen from the config-health header.
The menubar status output computes per-provider today costs by iterating
all providers after the main period blocks. That loop bypassed the
project filter, so --project/--exclude affected the main totals but not
the provider breakdown shown below them.
Adds unit tests for the project-filter helper: include OR semantics,
exclude AND-negation, case-insensitive matching against both project name
and projectPath, ordering (exclude applied after include), empty-string
edge case, and input immutability.
The TopSessions row layout summed to the full panel width without
leaving room for the Box border (round) + paddingX, so Ink truncated
the last 4 characters -- landing exactly on the calls column and
producing rows like "$182.58 ..." with no calls value.
Introduce PANEL_CHROME = 4 and subtract it from the name column so the
row fits inside the panel's inner area.
Adds two new repeatable flags to all commands (report, today, month, status, export):
- --project <name>: include only projects matching name (substring, case-insensitive)
- --exclude <name>: exclude projects matching name (substring, case-insensitive)
Both flags can be specified multiple times to match multiple projects.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- resolve dashboard.tsx conflicts: keep optimize view + context budget column alongside main's all-time period and TopSessions panel
- ProjectBreakdown: add avg/s column from main plus overhead column from optimize, widths 30/40
- StatusBar: 1-5 periods including all-time, plus o-optimize when findings exist
- DashboardContent: all-time period handling and TopSessions panel preserved
Copilot provider and its 253 tests from main merged cleanly as additions.
Outputs full dashboard data as structured JSON to stdout, including:
overview, daily breakdown, projects, models with token counts,
activities with one-shot rates, core tools, MCP servers, and
shell commands.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Release notes cover the two merged PRs:
- #44: GitHub Copilot provider parsing ~/.copilot/session-state/ with
model tracking via session.model_change events. Adds fallback pricing
for six gpt/o3/o4 models. Copilot logs only output tokens, so cost
rows sit below actual API cost; documented in README and CHANGELOG.
- #51: All Time period (key 5, -p all), avg/s column in By Project,
and a new Top Sessions panel showing the five most expensive sessions
across all projects.
README: provider list updated (Copilot added, output-tokens-only caveat
alongside Cursor's Auto-mode estimation note), usage examples include
`-p all` and key `5`, provider filter list includes `copilot`.
CHANGELOG: 0.6.0 entry lists both features with contributor credits,
plus two fixes (longest-key-first model display sort and empty
firstTimestamp placeholder).
114 tests pass. Build succeeds at 132KB.
- ContextBudgetPanel in dashboard.tsx was defined but never rendered
after the per-project overhead column replaced it in Phase 1.
- detectLowReadEditRatio accepted a dateRange parameter that was
never used inside the function (trend is computed via recent-call
flags on ToolCall).
Verified with tsc --noUnusedLocals --noUnusedParameters: 0 errors.
160 tests pass.
- init currentModel to '' and skip assistant messages before first
session.model_change to avoid silent misattribution
- add comment documenting why inputTokens is always 0
- fix delete_file tool mapping ('Edit' -> 'Delete')
- add schema doc comment to ToolRequest optional fields
- remove catch-all from CopilotEvent union for proper TS narrowing
- add tests: pre-model-change skip, workspace.yaml quote/comment strip,
longest-prefix model display name match
- ScanData.versions and ScanFileResult.versions were collected but never
read in scanAndDetect. Per-call version lives on ApiCallMeta.version
which is what detectCacheBloat actually uses. Dropped the unused
aggregation path end-to-end.
- Extract DEFAULT_CACHE_BASELINE_TOKENS, CACHE_BASELINE_QUANTILE,
CACHE_VERSION_MIN_SAMPLES, CACHE_VERSION_DIFF_THRESHOLD as named
module-scope constants. Honors the "no magic numbers" project rule
across the full optimize engine.
- Rename "ctx" column to "overhead" with wider padding for legibility
in the By Project panel.
- Name magic numbers for the project column widths.
- Empty-state now includes a one-line description of what optimize
does, so first-time users with no findings still understand the
feature.
Solves the problem where users who fixed an issue continued to see
the finding for the remainder of the period. Findings now show
visible progress or disappear entirely.
Mechanism (no state file, no new I/O):
- ToolCall and ApiCallMeta gain a `recent` boolean, set when the
entry's timestamp falls inside a rolling 48-hour window.
- Each session-based detector counts recent vs total occurrences.
- computeTrend classifies each finding:
active -- recent rate matches baseline
improving -- recent rate under half of baseline (green arrow)
resolved -- zero recent waste AND confirmed recent activity
- Resolved findings are suppressed. Improving findings render with
a green "improving down-arrow" badge next to the impact label.
- When no recent activity exists, findings default to active so a
user who simply paused is not told everything is fixed.
Applies to the four session-based detectors: junk reads, duplicate
reads, low read:edit ratio, cache bloat. The filesystem detectors
(missing .claudeignore, bloated CLAUDE.md, unused MCP, ghost agents
/ skills / commands, bash limit) already self-heal on next run.
5 new tests cover computeTrend edge cases. 126 tests pass.
- TopSessions: show '----------' placeholder when session.firstTimestamp
is empty (Copilot provider yields '' when timestamp is missing)
- DashboardContent: add comment explaining undefined days tri-state
- tests/dashboard.test.ts: cover top-5 selection (fewer than 5 sessions,
tied costs, descending sort) and avg/s returning '-' for zero sessions
Authored-by: AgentSeal <hello@agentseal.org>
- runWithConcurrency helper runs file reads with configurable parallelism
(default 16) instead of sequential await.
- isFileStaleForRange skips files whose mtime is older than the date range
start, avoiding unnecessary reads for narrow periods.
- Result-level cache keyed on (dateRange, project fingerprint) with
60s TTL. Warm dashboard 'o' press now hits cache instead of rescanning.
- Early-return when projects array is empty so empty-state path does not
trigger filesystem walk.
Measured CLI cold scan on 12K files, 1833 sessions week:
before: 12-17s
after: 6-7s
Dashboard warm cache hit: <50ms.
Phase 1 hardening pass.
Bug fixes:
- Move cwd/version collection inside date-range filter. 7d and 30d
now produce different findings for filesystem detectors.
- detectGhostSkills threshold aligned with peer detectors.
- detectUnusedMcp gets 24-hour grace period via config file mtime so
newly added servers are not flagged as unused.
- detectCacheBloat replaces hardcoded 50K baseline with user-derived
p25 of cache writes. Flags only when median exceeds 1.4x baseline.
- detectBashBloat scans user shell profiles instead of the auditor's
process.env.
- @-import pattern requires ./ ../ or / to avoid matching email
addresses or npm scopes.
- Command usage pattern requires leading whitespace/start-of-line
before /cmd so path references like /tmp are not counted as usage.
- AVG_TOKENS_PER_READ lowered from 1500 to 600 and
CLAUDEMD_TOKENS_PER_LINE lowered from 25 to 13 for realistic
prose/config sizing.
Code quality:
- Every magic number extracted to named module-scope constants.
- Dead code removed (IMPACT_ORDER, unused stat import).
- Shared loadMcpConfigs helper deduplicates config walking.
- Shared shortHomePath, isReadTool, inRange helpers.
- All detectors and computeHealth exported for real tests.
- Ghost detectors run in parallel via Promise.all.
- Cost rate defaults to 0 when unknown so UI can suppress instead of
showing fabricated numbers.
Tests:
- Replaced 17 fake tests that re-implemented detector logic with
26 tests importing and exercising the real exports.
- Cover threshold boundaries, impact scaling, edge cases.
- 121 tests pass.
UX header: "Setup" renamed to "Health", issue count shown inline.
CLAUDE.md: adds rule against "steal/copy/rip-off" wording in
public-facing text.
Expanded the optimize engine with new detectors and scoring:
1. Health score + letter grade (A-F) in optimize header. Weighted
per-impact with caps. Gives users an instant "is my setup healthy"
read that doubles as a shareable number.
2. Urgency score replaces impact-enum sort. Weighted 0.7 * impact
+ 0.3 * normalized tokens. Produces better-ranked findings.
3. Three new ghost detectors:
- Ghost agents: files in ~/.claude/agents/ never invoked via
Agent/Task tool
- Ghost skills: SKILL.md directories never triggered
- Ghost slash-commands: ~/.claude/commands/ files never referenced
in user messages
4. @-import chain expansion for CLAUDE.md. Recursively follows
@path/to/file imports (max depth 5) so bloat detection counts
transitive load, not just the base file. Fixes undercounting for
users with modular CLAUDE.md setups.
9 new tests covering health scoring and import expansion.
- Add 'all' period (key 5) to dashboard showing data from all time
- Daily Activity panel shows all available days when All Time is active
- Add avg cost per session column to Project breakdown
- Add Top Sessions panel highlighting the 5 most expensive sessions
Lists common signals in the dashboard (cache hit, retry rate,
model mix, tool patterns) and what each typically means.
Points readers toward interpreting their own data without
overpromising automatic diagnosis.
- Add Pi entry to PROVIDER_COLORS (pink) and PROVIDER_DISPLAY_NAMES.
- Update README with Pi in supported providers, requirements,
command examples, and the data-format section.
dispatch_agent is Pi's delegation tool. Mapping it to Agent makes
the classifier route those turns into the Delegation category
instead of Conversation, matching the existing semantics for the
task tool.
- Move modelDisplayEntries sort to module scope so it runs once
instead of on every modelDisplayName call.
- Add bash-utils tests for the env var prefix and true/false
skip behaviors that came in with the Pi commit.
Verified Pi token semantics against real session data:
input + cacheRead + cacheWrite + output equals totalTokens
exactly, confirming Anthropic-style accounting (cached tokens
disjoint from input). No double-counting.
Maps Pi's lowercase tool names (bash, read, edit, write...) to
the capitalized form used by every other provider, so the
dashboard shows consistent tool names across providers and the
activity classifier works without extra lowercase entries.
Reverts the lowercase additions to classifier.ts since the
provider now normalizes tool names at the source.
- Adds support for Pi (pi.ai) as a new session provider.
- Pi sessions are stored as JSONL files under `~/.pi/agent/sessions/<project-dir>/` and use OpenAI-compatible model IDs (gpt-5, gpt-5.4, gpt-4o, etc.).
- `src/providers/pi.ts` (new): Pi provider - discovers JSONL session files, parses assistant turns, extracts token counts, tool calls, and bash commands, deduplicates via response ID with line-index fallback
- `src/providers/types.ts`: added bashCommands field to `ParsedProviderCall` so all providers carry extracted bash command lists
- `src/providers/index.ts`: registered Pi as a core provider alongside Claude and Codex
- `src/providers/codex.ts`, `cursor.ts`: added `bashCommands: []` to satisfy the new required field on `ParsedProviderCall`
- `src/parser.ts`: fixed bug where `providerCallToTurn` always emitted an empty bashCommands array instead of passing through the parsed commands
- `src/classifier.ts`: added lowercase tool name variants (bash, edit, read, write) to match Pi's tool naming convention in JSONL output
- `src/bash-utils.ts`: exclude `true`, `false`, and shell variable assignments from extracted commands; scan past leading `NAME=val` tokens so `FOO=bar ls` correctly records `ls` rather than being dropped
- `package.json`: added pi to keywords
- `tests/providers/pi.test.ts` (new): 16 unit tests covering session discovery, multi-turn parsing, tool/bash extraction, deduplication, zero-token filtering, and display name mapping
- `tests/provider-registry.test.ts`: updated core provider list to include pi
- [X] Unit tests pass (`npx vitest run`, 56 tests across 6 files);
- [X] Manually verified via `npx tsx src/cli.ts` report and showing Pi sessions alongside Claude and Codex in the dashboard.
Adds a cache hit percentage column to the By Model panel and
fixes the cache hit math in the Overview panel. Both were
treating cache_creation tokens as cache hits, when they are
actually cache misses being written to cache for future use.
The corrected formula counts both fresh input and cache writes
in the denominator, so a heavy Claude Code session now shows
~97% cache hit instead of the misleading 100%.
Also extracts column widths into named constants per CLAUDE.md
no-magic-numbers rule, and tightens the model name column to a
fixed 14 chars so columns hug the name like the Activity panel.