Commit graph

324 commits

Author SHA1 Message Date
Resham Joshi
bc54f85e34
Merge pull request #132 from maucher/fix/streaming-oom-readSessionLines
fix: switch scanJsonlFile and parseSessionFile to readSessionLines to prevent OOM
2026-04-22 03:58:46 -07:00
Łukasz Majcher
5e49f17e64 fix: switch scanJsonlFile and parseSessionFile to readSessionLines to prevent OOM
readViaStream (used for files ≥8 MB) reconstructs the full file as a
single string via chunks.join('\n'), giving the same peak allocation as
readFile. Callers then call content.split('\n'), creating a second copy.
With FILE_READ_CONCURRENCY=16 and files up to 128 MB this can exhaust
the V8 heap (~6 GB theoretical peak).

readSessionLines already exists as a proper async generator that yields
one line at a time. Switch both hot-path callers to iterate it directly
so the full file string is never held in memory.

Adds two tests: a spy test confirming readSessionLines is called (not
readSessionFile), and a 500-entry correctness test.

Fixes #131
2026-04-22 10:11:13 +00:00
Resham Joshi
d4e07de18f
Merge pull request #128 from getagentseal/chore/changelog-omp-model-alias
Some checks are pending
CI / semgrep (push) Waiting to run
docs: backfill 0.8.7 changelog with OMP provider and model-alias
2026-04-21 14:00:48 -07:00
iamtoruk
104ad85df6 docs: backfill 0.8.7 changelog with OMP provider and model-alias
The PR #59 merge landed on main (ac31883) before the 0.8.7 bump
(cb44cc6), so the code shipped in 0.8.7 but the initial changelog entry
only credited MiniMax and the menubar fixes. Adds three entries under
Added for the OMP provider, the model-alias command, and the built-in
Anthropic proxy aliases. Adds one entry under Fixed (CLI) for the
prototype-pollution guard in resolveAlias. Updates the CLI parity note
so users on 0.8.5 know whether they can stay there.

Filed before 0.8.7 hits npm.
2026-04-21 13:59:59 -07:00
Resham Joshi
2323587ab6
Merge pull request #127 from getagentseal/chore/bump-0.8.7
chore: bump to 0.8.7, align CLI and menubar release
2026-04-21 13:45:53 -07:00
iamtoruk
cb44cc6deb chore: bump version to 0.8.7, add changelog entry
Skips 0.8.6 to match mac-v0.8.7 and keep CLI + menubar versions aligned.
CLI change: MiniMax-M2.7 and MiniMax-M2.7-highspeed pricing. macOS menubar
change: three stability fixes culminating in the App Nap opt-out that keeps
the refresh loop ticking while the icon is idle in the background.
2026-04-21 13:43:49 -07:00
iamtoruk
33c7bb4d7a Merge remote-tracking branch 'origin/main' 2026-04-21 13:38:50 -07:00
Resham Joshi
2ffd5b46c1
Merge pull request #126 from getagentseal/fix/menubar-disable-appnap
fix(menubar): opt out of App Nap so refresh loop keeps ticking
2026-04-21 13:38:26 -07:00
iamtoruk
6ed0799b36 fix(menubar): opt out of App Nap so the refresh loop keeps ticking
Confirmed in the system log: while the menubar icon sits idle in the
background, macOS flips _kLSApplicationWouldBeTerminatedByTALKey to 1,
which is the Automatic Termination and App Nap subsystem declaring the
app a suspend candidate. Once that happens the 15s refresh Task's sleep
stretches arbitrarily, so the status bar label freezes until the user
clicks the icon (which triggers NSApp.activate and wakes everything up).

Hold a ProcessInfo.beginActivity handle for the life of the app with
.userInitiated + .automaticTerminationDisabled + .suddenTerminationDisabled
so macOS leaves the app alone. Released implicitly when the app exits.
2026-04-21 13:05:56 -07:00
Resham Joshi
90aa342a34
Merge pull request #125 from getagentseal/fix/menubar-force-redraw
Some checks are pending
CI / semgrep (push) Waiting to run
fix(menubar): force status bar redraw and lift subprocess QoS
2026-04-21 12:47:58 -07:00
iamtoruk
ac31883edc Merge PR #59: OMP provider and model alias mapping
Adds Oh My Pi support by parameterizing the Pi JSONL reader to accept a
providerName, so sessions at ~/.omp/agent/sessions/ are discovered and
tracked alongside Pi. Ships a model-alias CLI command plus five
built-in aliases for the anthropic--claude-X.Y-tier double-dash format
that some Anthropic-compatible proxies emit, so cost rows no longer
read $0.00 for those names.

Contributed by @cgrossde.
2026-04-21 11:52:13 -07:00
iamtoruk
4f1138290e Merge main into feat/omp-support-model-aliases
Second merge of main since the PR was opened. Main moved 30+ commits
(0.8.5 bump, plan tracking feature, MiniMax pricing, menubar
prefetchAll walk-back, aicrowd cache rewrite revert) so the branch
needed another reconciliation before merging to main.

Two new conflicts resolved. Took main's text in both cases per the
policy of favoring main when the feature work is neutral:

  README.md             Kept main's Node 20+ / better-sqlite3
                        Requirements wording and main's shorter src/
                        tree listing. Added OMP to the Requirements
                        line.

  src/providers/pi.ts   Main dropped the discovery-cache snapshot and
                        the rich source-metadata fields as part of the
                        aicrowd revert. Took main's simpler structure
                        and only kept the providerName parameter so
                        OMP sources still report the correct provider
                        in the session source and dedup key.

Earlier fixups carried forward from the prior merge commit:
  - Object.hasOwn guards in resolveAlias against prototype-pollution
    via a model literally named '__proto__'.
  - source.provider in the dedup key prefix so OMP rows no longer
    stamp 'pi:'.
  - Combined pi.js imports in providers/index.ts.
  - Trailing newline on pi.ts.
  - Unknown-model fallback in cursor-agent.ts from yesterday's PR #117
    fixup (preserved via main).

353 tests pass (count dropped from 378 because main deleted the
parse-progress / parser-cache / provider-colors / source-cache test
files alongside the cache-rewrite revert).

Feature work by @cgrossde.
2026-04-21 11:51:20 -07:00
iamtoruk
556f2bf78c fix(menubar): force status bar redraw and lift subprocess QoS
Two independent causes for the stuck-label / only-refreshes-on-click
behaviour, both fixed here.

1. NSStatusItem button defers the status bar paint for accessory apps
   that are not foreground, so after refreshStatusButton sets the new
   attributed title the menu bar visually froze until the user opened
   the popover (which triggers NSApp.activate and a forced redraw
   cycle). Explicit needsDisplay + display() forces the paint every
   cycle.

2. The codeburn subprocess inherited the accessory app's default QoS,
   which macOS background-throttles. That could stretch a sub-1-second
   parse into tens of seconds on large corpora and overrun the 15s
   refresh cadence. Set .userInitiated so the CLI runs at the same
   priority it does from a user-interactive terminal.
2026-04-21 11:50:28 -07:00
Resham Joshi
d69aa344ab
Merge pull request #123 from getagentseal/fix/menubar-remove-prefetchall
fix(menubar): drop prefetchAll to keep Today label fresh
2026-04-21 07:39:54 -07:00
iamtoruk
eb0d096c62 fix(menubar): drop prefetchAll to keep Today label fresh
The detached prefetch spawned four codeburn subprocesses (week, 30days,
month, all) that competed with the main refresh loop's Today fetch for
disk and parser time. On large session corpora that starved the Today
refresh for minutes at a time, and the status label drifted stale until
the prefetch queue finally drained. Users perceived this as the menubar
"stopping" until they clicked a provider tab, which coincided with the
queue clearing.

Drop prefetchAll. Period tabs in the popover now fetch lazily on first
click (3-10 seconds once per app lifetime per period), same as they did
in 0.8.1. Today's label stays fresh because nothing else is hammering
the parser in the background.
2026-04-21 07:36:00 -07:00
Resham Joshi
cdfda9507c
Merge pull request #122 from getagentseal/chore/firstlook-workflow-dispatch
chore: enable firstlook workflow_dispatch for backlog scans
2026-04-21 06:33:50 -07:00
iamtoruk
b3bb5efc58 chore: enable firstlook workflow_dispatch for backlog scans
Adds pr-number input and workflow_dispatch trigger so existing open PRs
can be scanned without waiting for a new push. Leave pr-number empty to
loop all open PRs; supply a number to rescan one.
2026-04-21 06:33:09 -07:00
Resham Joshi
41cc525e06
Merge pull request #121 from getagentseal/chore/add-firstlook-workflow
chore: add firstlook PR-author reputation check
2026-04-21 06:18:45 -07:00
iamtoruk
2ed1d5275c chore: add firstlook workflow for PR author reputation check
Runs on every PR open/reopen/synchronize against getagentseal/firstlook
and fails the check when the author's score is 'unknown' (new or
untracked accounts). Skips bot accounts so dependabot and renovate pass
through.

This screens the same drive-by pattern that landed in PR #118 (octo-patch,
fresh automation account) without requiring a manual tier check on every
submission.
2026-04-21 06:17:56 -07:00
Resham Joshi
a13c193075
Merge pull request #120 from getagentseal/feat/add-minimax-pricing
feat: add MiniMax-M2.7 model pricing
2026-04-21 05:52:47 -07:00
iamtoruk
81b5cda173 feat: add MiniMax-M2.7 and MiniMax-M2.7-highspeed model pricing
Adds FALLBACK_PRICING entries plus display names so MiniMax sessions
show up with the right cost and readable labels when users route MiniMax
through providers like OpenCode. Pricing verified against the live
MiniMax paygo page:

  MiniMax-M2.7           input $0.3/M  output $1.2/M  cache-read $0.06/M  cache-write $0.375/M
  MiniMax-M2.7-highspeed input $0.6/M  output $2.4/M  cache-read $0.06/M  cache-write $0.375/M
2026-04-21 05:50:52 -07:00
Resham Joshi
8ee84b0041
Merge pull request #119 from getagentseal/fix/revert-aicrowd-cache-rewrite
Revert source cache, fix stale Today totals
2026-04-21 05:24:06 -07:00
iamtoruk
2467578a52 fix(menubar): run prefetchAll in a detached task so the refresh loop starts immediately
prefetchAll was awaited inside the refresh loop after the first cycle.
On large corpora the All Time parse takes 60+ seconds and kept the Today
refresh blocked for that whole window, so the status label got stuck for
minutes at a time while the CLI was returning fresh numbers.

Detach prefetchAll into its own @MainActor task. The popover still gets
the benefit of pre-warmed period caches, the refresh loop ticks every 15
seconds regardless.
2026-04-21 05:22:34 -07:00
iamtoruk
f2a2b53ae7 Merge remote-tracking branch 'origin/main' into fix/revert-aicrowd-cache-rewrite 2026-04-21 05:08:31 -07:00
iamtoruk
5a4e14f0f6 docs: remove stray Unreleased heading, note removed --no-cache flag
Also adds a regression test for the midnight-straddle bucketing invariant
that was flagged by the pre-push review: if someone reverts the assistant-
timestamp bucketing back to user-timestamp, this test will catch it.
2026-04-21 04:54:50 -07:00
iamtoruk
07d2fb07aa chore: bump version to 0.8.5, add changelog entry explaining revert
Reverts the persistent source cache added in 0.8.2 back to v0.8.1's
full-reparse path for Claude sessions; keeps the plan pill, pricing fix,
cursor-agent provider, and the Mac menubar prefetch+timezone work added
between 0.8.2 and 0.8.4.
2026-04-21 04:44:23 -07:00
iamtoruk
b491a1f590 fix: bucket turns by assistant timestamp, filter at turn level
A turn that straddles midnight (user typed at 23:58, assistant responded
at 00:30) was bucketed and filtered inconsistently across call sites.
parseSessionFile filtered entries by timestamp, producing orphan assistant
calls that groupIntoTurns pushed as turns with empty timestamp. Some
downstream code counted those (buildPeriodData summing project totals)
and other code dropped them (renderStatusBar's empty-timestamp skip).

The menubar showed today = $32 while the terminal status showed today = $27
for the same dataset; each was internally consistent but used a different
turn-bucket rule.

Fix both: parseSessionFile now builds all turns first, then filters each
turn by its first assistant call timestamp (the moment cost was incurred).
renderStatusBar buckets the same way. day-aggregator.ts already bucketed
on assistant time, so it is now consistent too.

Net effect: a turn is counted in the day the API call actually ran in.
2026-04-21 04:40:44 -07:00
iamtoruk
68e9c63088 fix(cursor-agent): drop unused SessionSource fields reintroduced by revert
cursor-agent was authored on top of the Sharada cache rewrite and referenced
fingerprintPath, cacheStrategy, progressLabel, and parserVersion. With the
persistent source cache reverted, these fields no longer exist on SessionSource.
Strip the references; cursor-agent continues to work on the v0.8.1 discover +
parse path like every other provider.
2026-04-21 04:23:20 -07:00
iamtoruk
41839edd96 fix(menubar): prefetch periods and align dashboard dates with local timezone
Loading overlay no longer flashes on every 15s poll. isLoading now only
toggles when the cache is cold, and all periods prefetch once on launch
so tab switching is instant.

Heatmap tooltip, trend bars, forecast, and all-time stats were computing
on UTC dates while the CLI reports on local dates, so the two disagreed
at day boundaries. Switched every date formatter and calendar in these
paths to .current so the menubar matches codeburn today output.
2026-04-21 04:22:10 -07:00
iamtoruk
0725fe2fbb fix(cursor-agent): preserve raw model name for unknown Cursor models
The fallback path in modelDisplayName returned "Auto (Sonnet est.) (est.)"
for any model not listed in modelDisplayNames, double-tagging the est.
suffix and hiding the real model ID. New Cursor model IDs now surface as
their raw name with a single (est.) suffix until the display map is
updated. Adds a regression test.
2026-04-21 04:21:06 -07:00
iamtoruk
9de0b1ad7b chore: remove CLAUDE.md from public repo
Internal development rules do not belong in a public repository.
File is kept locally and added to .gitignore.
2026-04-21 04:21:06 -07:00
iamtoruk
c336adf2f1 docs: add cached-vs-uncached verification rule for parser PRs 2026-04-21 04:21:05 -07:00
Matt Van Horn
620ca32219 feat(cursor-agent): add provider for cursor-agent CLI sessions
Discovers transcripts at ~/.cursor/projects/*/agent-transcripts/*.txt
and joins against ~/.cursor/ai-tracking/ai-code-tracking.db for model
attribution. Token counts are estimated from transcript character
length since the attribution DB does not carry them; the model label
surfaces the estimation with an (est.) suffix on every row.

Deduplication keys prefix cursor-agent: to stay disjoint from the
existing cursor: prefix so the two providers do not cross-dedupe
on shared conversationId namespaces.

Tests cover: empty ~/.cursor/projects/, single transcript, multiple
projects, missing ai-code-tracking.db, unrecognized transcript format
skip, non-UUID filename fallback, and sqlite metadata join.

Closes #55
2026-04-21 04:21:01 -07:00
iamtoruk
0803005083 fix(config): restore catch-all in readConfig to prevent CLI crash on malformed config 2026-04-21 04:20:55 -07:00
Trevin Chow
2c43ec1ad0 fix(plan): resolve type errors in plan summary and isActivePlan guard
Two pre-existing type errors surfaced during the rebase against main:

1. JsonPlanSummary.id was hardcoded to four plan ids, but PlanId now
   includes 'none' (PLAN_IDS was extended when 'codeburn plan clear'
   was added). toJsonPlanSummary only runs for active plans at runtime,
   but the static type still had to be widened. Use PlanId directly
   instead of the hand-rolled union.

2. isActivePlan used Boolean(plan) as the nullish guard, which doesn't
   narrow plan's type in TypeScript. Switch to an explicit
   'plan !== undefined' so the subsequent .id and .monthlyUsd accesses
   type-check.

npx tsc --noEmit is now clean; all 285 tests still pass.
2026-04-21 04:20:55 -07:00
Trevin Chow
cb4c3ee305 fix(plan): scope TUI plan row to billing period, use currency-aware formatting
Address review feedback on #74:

1. TUI plan row previously used the active tab's filtered projects as
   plan spend, so 'Today' showed today's cost as plan spent. Switch
   renderDashboard and reloadData to getPlanUsageOrNull(), which uses
   the plan's own billing period regardless of tab.

2. Plan row rendered via a local formatUsd that hardcoded USD. Replace
   every call with formatCost so 'codeburn currency EUR' flows through.
   Removes the adjacent '$3,425.52' vs '$32.07' style mismatch.

3. renderPlanBar capped filled width at 100%, so 105% and 1700% looked
   identical. Past 100%, render a full bar plus chevron tail sized by
   order of magnitude (log10): 1.05x -> 1 chevron, 17x -> 2, 170x -> 3.

4. 'running on API overage pricing' is wrong for Claude Pro/Max (rate
   limited, not charged overage). Drop that claim; keep the Nx-over
   multiplier and match the under/near projection line structure.

5. Spell out 'equiv' as 'API-equivalent' in the plan label.

Dead code cleanup: getPlanUsageOrNullForProjects is now unused; remove
it. getPlanUsageFromProjects stays (unit tests still use it).
2026-04-21 04:20:54 -07:00
Trevin Chow
3f7470d29b feat(plan): subscription plan tracking with usage progress bar
Adds `codeburn plan set <id>` to configure a subscription plan (Claude Pro,
Claude Max, Cursor Pro, or custom). When set, the Overview panel renders
an API-equivalent progress bar against subscription price with a
projected month-end cost.

Closes the loudest demand signal on the repo: issue #11 ("Subscription
vs API Use") from two independent voices, plus the routing-decision use
case raised in #12.

- src/config.ts: extends CodeburnConfig with Plan, adds readPlan/savePlan/clearPlan
- src/plans.ts: presets (claude-pro $20, claude-max $200, cursor-pro $20)
- src/plan-usage.ts: getPlanUsage, resetDay-aware period math (1-28),
  median-of-7-day-trailing projection
- src/cli.ts: `codeburn plan [show|set|reset]` subcommand, plan wired
  into JSON outputs for report/today/month/status (only when active)
- src/dashboard.tsx: Plan row in Overview, color-coded (green under 80%,
  orange near, red over), with days-until-reset
- README.md: Plans section with honest framing (API-equivalent vs
  subscription price, not token allowance)
- tests/plan-usage.test.ts, tests/plans.test.ts, tests/cli-plan.test.ts:
  period math, presets, CLI round-trip

Resets respect resetDay across month boundaries. Uses median daily spend
(not mean) so one huge day doesn't distort the month-end projection.

Fixes #11
2026-04-21 04:20:50 -07:00
iamtoruk
8e39a89fe0 fix: pricing accuracy, stream leak, CSV injection hardening
- Remove bidirectional fuzzy match in getModelCosts that could return
  wrong pricing when a short canonical name prefix-matched a longer key
- Use explicit undefined check in parseLiteLLMEntry so free models with
  zero cost are not silently dropped from the LiteLLM pricing database
- Destroy read stream in finally block of readSessionLines to prevent
  file descriptor leaks when the generator is abandoned early
- Extend CSV injection escaping to cover tab and carriage-return prefixes
- Add optional chaining fallback for empty periods in exportCsv/exportJson
- Add regression tests for all fixes (models, export, fs-utils)
2026-04-21 04:20:46 -07:00
iamtoruk
361b9a7f76 docs: add star history chart to README 2026-04-21 04:20:43 -07:00
iamtoruk
31367f2c01 fix(menubar): use numeric version comparison for update check
Compare versions with .orderedDescending instead of != to prevent
showing update button when installed version is newer than cached.
2026-04-21 04:20:42 -07:00
iamtoruk
9e7916d24d docs: credit CodexBar as inspiration 2026-04-21 04:20:42 -07:00
iamtoruk
fec5868379 chore: update readme badges and menubar screenshot
Remove bundlephobia and monthly downloads badges, update menubar
screenshot to 0.8.0.
2026-04-21 04:20:38 -07:00
iamtoruk
c2ab80d6e2 Merge main into feat/omp-support-model-aliases
Brings the PR branch up to the current main so the OMP provider and the
model-alias command can land cleanly. Resolves six merge conflicts and
applies a handful of small fixups alongside the resolution so the
feature matches the conventions set by the cursor-agent merge earlier
today.

Conflict resolutions:

  README.md               Combine cursor-agent and OMP rows in provider
                          list, Requirements, and data-location table;
                          take main's Node 22+ and node:sqlite text.
  src/cli.ts              Keep both new commands: model-alias and plan.
  src/config.ts           Add modelAliases alongside plan on the config
                          type.
  src/providers/index.ts  Keep the cursor-agent lazy-loader from main
                          and add omp to coreProviders. Fold the two
                          pi-module imports into one statement.
  src/providers/pi.ts     Keep the discovery-cache snapshot path from
                          main and the providerName parameterization
                          from the PR. Propagate providerName through
                          saveDiscoveryCache, loadDiscoveryCache, the
                          parserVersion tag, and the dedup key prefix
                          so OMP sources no longer stamp 'pi:' inside
                          their cache entries or dedup keys.
  tests/models.test.ts    Keep main's pricing-and-short-name tests and
                          add the PR's alias tests alongside, sharing a
                          single loadPricing setup and an afterEach
                          alias reset.

Fixups in the same commit:

  src/models.ts           Replace ?? chain in resolveAlias with
                          Object.hasOwn checks. The previous form
                          returned Object.prototype for a model named
                          '__proto__' and broke downstream
                          canonical.startsWith calls. Caught by the
                          existing prototype-pollution test suite.
  src/providers/pi.ts     Use source.provider in the dedup key prefix
                          and add a trailing newline to the file.
  tests/providers/omp.test.ts  Expect 'omp:' in the dedup key for OMP
                          sources, matching the fix above.

Feature work by @cgrossde.
2026-04-21 03:16:28 -07:00
iamtoruk
28c825ff15 Merge PR #117: cursor-agent provider
Some checks are pending
CI / semgrep (push) Waiting to run
Adds a cursor-agent provider that reads sessions from
~/.cursor/projects/*/agent-transcripts/*.txt and joins against the
ai-code-tracking.db for model attribution, closing issue #55. Token
counts are estimated from transcript character length since neither the
transcript nor the attribution DB carries real counts, and every row
carries an (est.) label to surface that.

Contributed by @mvanhorn.
2026-04-20 19:26:32 -07:00
iamtoruk
3c2aab2207 fix(menubar): prefetch periods and align dashboard dates with local timezone
Loading overlay no longer flashes on every 15s poll. isLoading now only
toggles when the cache is cold, and all periods prefetch once on launch
so tab switching is instant.

Heatmap tooltip, trend bars, forecast, and all-time stats were computing
on UTC dates while the CLI reports on local dates, so the two disagreed
at day boundaries. Switched every date formatter and calendar in these
paths to .current so the menubar matches codeburn today output.
2026-04-20 19:25:14 -07:00
iamtoruk
ed5512144a fix(cursor-agent): preserve raw model name for unknown Cursor models
The fallback path in modelDisplayName returned "Auto (Sonnet est.) (est.)"
for any model not listed in modelDisplayNames, double-tagging the est.
suffix and hiding the real model ID. New Cursor model IDs now surface as
their raw name with a single (est.) suffix until the display map is
updated. Adds a regression test.
2026-04-20 19:20:15 -07:00
iamtoruk
988060cd09 chore: bump version to 0.8.4, update changelog 2026-04-20 18:28:02 -07:00
iamtoruk
bf17d20476 fix: remove noCache override for menubar-json format
Source cache fixes (empty-session guards, date range reordering) make
the cache safe for menubar use. Forcing noCache on every 15s poll was
re-parsing 5800+ files each time, causing the menubar to hang.
2026-04-20 18:25:43 -07:00
iamtoruk
78438e52f2 chore: remove CLAUDE.md from public repo
Internal development rules do not belong in a public repository.
File is kept locally and added to .gitignore.
2026-04-20 18:24:18 -07:00
iamtoruk
594013c5ce docs: add cached-vs-uncached verification rule for parser PRs 2026-04-20 18:08:38 -07:00