Commit graph

640 commits

Author SHA1 Message Date
iamtoruk
0843efdecb docs(mcp): add implementation plan for codeburn MCP server 2026-06-02 01:33:08 -07:00
iamtoruk
dc7a1a7b0e docs(mcp): add design spec for codeburn MCP server 2026-06-02 01:20:58 -07:00
iamtoruk
69b1736365 refactor(cli): share persistent-codeburn resolver; tighten Antigravity hook ownership
Some checks are pending
CI / semgrep (push) Waiting to run
Extract the duplicated codeburn-binary lookup (PATH building, npx-shim skipping,
default lookup dirs, resolver) from menubar-installer and the Antigravity
statusline installer into src/persistent-codeburn.ts. Both now import it and
keep their own user-facing "install globally" message.

Match the Antigravity statusLine hook on the trailing agy-statusline-hook token
instead of a bare substring, so a custom command that merely mentions the token
is treated as custom (protected, backed up) rather than silently overwritten.
2026-05-31 20:07:57 -07:00
iamtoruk
b246e822b7 Merge pull request #410 from ozymandiashh/codex/antigravity-hook-stale-path
Fix Antigravity hook stale CLI paths: install the statusLine hook through a
persistent codeburn binary resolved from PATH and repair stale CodeBurn-owned
hooks on re-install.
2026-05-31 20:04:11 -07:00
iamtoruk
3fa255fefc fix(models): match model names on version boundary, not bare prefix
A bare startsWith let an unlisted future minor collapse into the base entry
(gpt-5.6 -> "GPT-5"), the same mis-bucketing class as #420 but for non-Claude
families. Require an exact match or a key-plus-dash boundary in both
getShortModelName and the Codex provider so new versions fall through to their
raw id instead of a wrong name and pricing tier.
2026-05-31 05:37:12 -07:00
iamtoruk
0520ecbde8 fix(claude): derive model display names via getShortModelName
The Claude provider kept its own shortNames map with no claude-opus-4-8
entry, so the models table and menubar bucketed it into "Opus 4" — the
actual #420 path (the earlier models.ts map is unused here). Delegating to
getShortModelName removes the duplicate and picks up new versions for free.
2026-05-31 05:31:56 -07:00
iamtoruk
aa9bd9f0f1 feat(models): derive Claude names and fast multipliers automatically
New Claude releases no longer need a hand-maintained SHORT_NAMES entry or
FAST_MULTIPLIERS row. Display names are derived from the claude-<family>-<major>-<minor>
id, and the fast-mode multiplier rides along as a 5th element in the LiteLLM
snapshot tuple (provider_specific_entry.fast). Fixes #420: claude-opus-4-8 gets
its own line and correct pricing instead of falling into the Opus 4 bucket.
2026-05-31 05:17:14 -07:00
iamtoruk
ca41021a51 fix(menubar): treat the CLI credential store as the source of truth
The menubar kept its own copy of each provider's OAuth grant and refreshed
it on a timer, racing the CLI. Both Claude and Codex use single-use refresh
tokens that rotate on every refresh, so the menubar's self-rotation could
invalidate the user's own CLI login and surfaced as "disconnected" after a
long idle period.

Codex: read ~/.codex/auth.json fresh each cycle; only self-refresh when
last_refresh is older than 8 days; on 401 re-read the source before spending
our token; write rotated tokens back to auth.json (atomic, preserving other
keys); recover from reuse/invalid_grant by re-reading instead of going
terminal.

Claude: never HTTP-refresh the CLI-owned token. On expiry/401 re-read the
keychain with a no-UI query (LAContext.interactionNotAllowed) to adopt a
token the CLI already rotated; when none is available yet, report a transient
sourceTokenStale rather than a terminal disconnect.
2026-05-31 05:01:19 -07:00
iamtoruk
92dbad9503 refactor(menubar): remove dead distributed-notification listener
Nothing posts com.codeburn.refresh since the launchd fetcher was dropped,
so the listener and its heartbeat handler were vestigial.
2026-05-31 05:01:10 -07:00
iamtoruk
515a7a1467 feat(menubar): drop launchd fetcher; GUI writes its own badge backstop
A launchd-spawned process gets separate TCC attribution from the LaunchServices
app, so the out-of-process refresher prompted for "access data from other apps"
on every run regardless of the GUI's grant. Remove it entirely: the in-app loop
(timer survives sleep, popover-open recovery) is the source of truth and now
writes menubar-status.json on each successful refresh. On upgrade, any leftover
com.codeburn.refresh LaunchAgent is unloaded and deleted.
2026-05-30 13:35:07 -07:00
iamtoruk
a2ab52aafd build(menubar): allow signing with a stable identity via CODESIGN_IDENTITY 2026-05-30 13:18:46 -07:00
iamtoruk
b158af09e7 fix(menubar): read period from standard defaults in headless refresh 2026-05-30 13:06:54 -07:00
iamtoruk
c350dd2a55 feat(menubar): refresh via headless app binary instead of node script
Run CodeBurn's own signed binary in --refresh-once mode under the LaunchAgent
so the spawned CLI inherits CodeBurn's TCC grant. The bare-node shell script
prompted "node would like to access data from other apps" because launchd-
spawned children lose the signed app's TCC attribution. Drops the generated
shell script, scriptEnvironment, and per-period regeneration; the plist is now
static and period is read from UserDefaults at refresh time.
2026-05-30 13:05:48 -07:00
iamtoruk
d9a2e829ae docs(menubar): note deliberate unquoted argv interpolation in refresh script 2026-05-30 12:40:28 -07:00
iamtoruk
8884e212e3 test(menubar): lock in Codex terminal-failure classification 2026-05-30 12:32:19 -07:00
iamtoruk
51b90aa1a5 feat(menubar): badge falls back to launchd status file
refreshStatusButton now uses the fresher of the in-memory payload and
the file written by the LaunchAgent, so the menubar number advances
within ~30s even if the in-app loop is dead.
2026-05-30 00:48:12 -07:00
iamtoruk
3f4298103b feat(menubar): write refresh script on launch and period change 2026-05-30 00:46:54 -07:00
iamtoruk
1269406c7a feat(menubar): static LaunchAgent runs menubar-refresh.sh
Migrate off the osascript distributed-notification form (dropped by
napped apps) to a static /bin/sh runner. The plist no longer encodes
period, so it migrates once; period changes rewrite only the script.
2026-05-30 00:45:45 -07:00
iamtoruk
341f6e959d test(menubar): integration test for refresh-script round trip 2026-05-30 00:45:04 -07:00
iamtoruk
1341b626a2 feat(menubar): generate self-contained menubar-refresh.sh
The script runs the CLI for the badge period and atomically writes
menubar-status.json, so a static LaunchAgent can refresh the badge
out of process.
2026-05-30 00:40:03 -07:00
iamtoruk
be7b8278c5 feat(menubar): add CodeburnCLI.scriptEnvironment for shell-script embedding
Exposes the validated argv and augmented PATH so the LaunchAgent runner
script can be generated without re-resolving the binary or PATH.
2026-05-30 00:10:29 -07:00
iamtoruk
bdbd7b19f6 feat(menubar): add MenubarStatusCache read side for badge backstop 2026-05-30 00:01:07 -07:00
iamtoruk
75edff64ec fix(menubar): popover-open always force-recovers current key
Opening the popover now unconditionally restarts a dead timer and
clears stuck loading bookkeeping before force-fetching, so a user
looking at a stuck tab always recovers within one CLI round-trip.
2026-05-29 23:51:48 -07:00
iamtoruk
ea64dca52e fix(menubar): keep refresh timer alive across sleep
The sleep handler tore down the DispatchSource timer; a missed wake
notification then left it nil forever, stranding the refresh loop.
Cancel only in-flight tasks and let the kernel-paused timer resume.
2026-05-29 23:46:16 -07:00
Resham Joshi
d7a4048b9f
Merge pull request #412 from getagentseal/fix/menubar-stuck-loading-orphaned-inflight
Some checks failed
CI / semgrep (push) Has been cancelled
fix(menubar): recover from stuck loading when in-flight entry is orphaned
2026-05-28 14:07:22 -07:00
iamtoruk
372681cae2 fix(menubar): recover from stuck loading when in-flight entry is orphaned
A quiet refresh torn down across sleep/wake (or a generation reset) can
leave an orphaned inFlightKeys entry for the current key. The stuck-loading
recovery guard bailed whenever any in-flight entry existed, so the popover's
retry loop no-oped forever and the spinner ("Loading Today...") never
cleared — observed on a long-lived instance that crossed the midnight day
rollover.

Clear stale loading/in-flight state via the existing watchdog before the
in-flight guard, so an orphaned entry can no longer trap recovery. A healthy
in-flight fetch (younger than the watchdog) is still respected.
2026-05-28 14:06:13 -07:00
ozymandiashh
6ae80c651c Fix Antigravity hook stale path repair 2026-05-28 18:50:41 +03:00
iamtoruk
8d3d773cc2 docs: update menubar screenshot to 0.9.11
Some checks failed
CI / semgrep (push) Has been cancelled
2026-05-27 06:37:19 -07:00
iamtoruk
11b3e1be58 fix(menubar): re-check CLI version on every update cycle 2026-05-27 06:35:00 -07:00
iamtoruk
af99516633 feat(menubar): show CLI update banner when a newer version is available 2026-05-27 06:27:49 -07:00
Resham Joshi
a555c747d6
Merge pull request #406 from getagentseal/fix/pre-release-cleanup
fix: pre-release cleanup - opencode refactor, watchdog backoff, forge dedup
2026-05-27 05:57:07 -07:00
iamtoruk
14026a806a bump version to 0.9.11 2026-05-27 05:51:49 -07:00
iamtoruk
90f4edb6cc docs: update changelog for pre-release fixes, correct provider count to 25 2026-05-27 05:47:21 -07:00
iamtoruk
8d88bfd675 fix: validate subagentTypes in cache, forge dedup key, status flag conflicts
Add subagentTypes to session-cache validateCall for consistency with
other string-array fields (tools, bashCommands, skills).

Stabilize Forge dedup key fallback: use model+tokens instead of array
index so deletions between scans don't cause double-counting.

Add mutual exclusivity validation for --day/--days/--from/--to on the
status command, matching the report command's existing checks.
2026-05-27 04:46:32 -07:00
iamtoruk
3751b3381c fix(menubar): add watchdog backoff, remove dead RefreshBackoff code
PR #393 accidentally deleted the RefreshBackoff integration from PR #388,
leaving RefreshBackoff.swift as dead code and tests calling nonexistent
methods. Delete the dead code and fix the broken test target.

Add exponential backoff to the loading watchdog (8s, 16s, 32s... up to
60s, max 6 attempts) so it stops hammering the CLI when it's unavailable.
After exhausting retries, show an error overlay with a Retry button.

Fix recoverFromStuckLoading to skip recovery when a fetch is already
in-flight (avoids killing healthy fetches via generation bump).

Fix selectedDay to return nil for multi-day selections, and pass days
through startInteractiveSelectionRefresh so the cache key matches
currentKey.
2026-05-27 04:38:42 -07:00
iamtoruk
40dcb410a5 refactor: opencode uses shared sqlite-session-parser 2026-05-27 04:32:15 -07:00
Resham Joshi
16deaa6d40
Merge pull request #405 from getagentseal/fix/menubar-recovery-kilocode-sqlite
Fix menubar stuck loading + KiloCode SQLite support
2026-05-27 04:09:58 -07:00
iamtoruk
f1de8f96ec feat: add KiloCode SQLite session discovery (#404)
KiloCode >= 7.2.21 stores sessions in ~/.local/share/kilo/kilo.db
instead of VSCode globalStorage. Extract a shared SQLite session parser
from the OpenCode pattern and wire KiloCode to discover from both the
legacy globalStorage path and the new SQLite database.

Closes #404
2026-05-27 04:06:54 -07:00
iamtoruk
ac35746209 fix(menubar): recover from stuck loading state
Commit 5800179 (multi-day calendar) accidentally removed stuck-loading
recovery. Re-add an 8-second watchdog loop in MenuBarContent that calls
recoverFromStuckLoading(). Change inFlightKeys from Set to timestamped
Dict so the watchdog can detect orphaned quiet-refresh entries. Stop
nuking payloadRefreshGeneration in the watchdog (it was killing healthy
in-flight fetches). Bump Package.swift platform to macOS 15 to match
NSAttributedString(attachment:) usage.
2026-05-27 04:06:48 -07:00
Stephan Leicht Vogt
ff139bcf48
feat(providers): add forge provider support (#401)
Some checks are pending
CI / semgrep (push) Waiting to run
* Add Forge provider support

* Add Forge provider documentation

* Avoid loading Forge contexts during discovery
2026-05-26 03:36:57 -07:00
Linus
afdb63c2cd
fix(cli): restore antigravity-hook command accidentally removed in conflict resolution (#399)
Some checks are pending
CI / semgrep (push) Waiting to run
In commit 5800179, the Commander configuration for `antigravity-hook` and `agy-statusline-hook` commands in `src/main.ts` was accidentally deleted during conflict resolution. This commit restores them along with the missing imports.
2026-05-25 23:58:21 -07:00
Carlo Fuselli
b067136147
fix: group Cowork sessions by space name and unify cross-provider project entries (#398)
Some checks are pending
CI / semgrep (push) Waiting to run
* feat(claude): group Cowork sessions by space name instead of sanitized path

Claude Desktop local-agent-mode (Cowork) sessions were appearing as
separate projects with cryptic sanitized-path names like
`-Users-carlo-Library-Application-Support-Claude-...`. This meant
sessions from the same Cowork space (e.g. PhD_Thesis) showed up as
dozens of unrelated entries.

Changes:
- `discoverSessions()` detects paths inside the desktop sessions dir
  (`~/Library/Application Support/Claude/local-agent-mode-sessions`)
  and resolves the human-readable space name by reading the sibling
  `local_<id>.json` (for spaceId) and `spaces.json` (id → name map)
- `CODEBURN_DESKTOP_SESSIONS_DIR` env var overrides the desktop path
  for testability
- Sessions without a spaceId fall back to `userSelectedFolders[0]`
  basename, then the session title (instead of the unintelligible slug)
- `isCoworkOutputPath()` in parser.ts prevents the ephemeral
  `outputs/` cwd from being used as the canonical project key,
  which would have given every session a unique grouping key
- Parse-version bump (`cowork-space-grouping-v1`) invalidates stale
  cache so all files re-scan with the new logic on first run
- 5 new integration tests covering spaceId grouping, cross-space
  separation, folder fallback, title fallback, and no-metadata fallback

* test: use generic Project1/Project2/SubFolder names in cowork tests

* fix(parser): detect container-mode Cowork sessions by file path, not just cwd

Cowork sessions run in two modes:
- Local-mode: cwd is an ephemeral per-session outputs/ dir inside the
  desktop sessions directory — already handled.
- Container-mode: session runs inside Docker, so cwd is a container-local
  path like /sessions/<adjective-name> that does not exist on the host.
  The JSONL file still lives inside the desktop sessions directory, so we
  detect these by also checking the file path.

Rename isCoworkOutputPath → isCoworkSession and check both cwd and filePath.
Without this, container-mode sessions leaked their Docker cwd as the
canonical project path and showed as e.g. "sessions/trusting-inspiring-ri"
instead of grouping under the Cowork space name.

Also add a test covering the container-mode case and use generic names
(Project1, Project2, SubFolder) throughout the Cowork test suite.

* feat(parser): merge same-project sessions across AI providers by path

When a repository has sessions from multiple tools (e.g. both Claude Code
and Codex), parseAllSessions now groups them into a single ProjectSummary
instead of showing duplicate entries.

The previous outer merge keyed on p.project (a string name), so Claude's
"LibricicloAgain" and Codex's "Users-carlo-Desktop-LibricicloAgain" were
treated as different projects despite referring to the same directory.

The root cause is that Codex's sanitizeProject() strips the leading '/'
from cwds, producing "Users/carlo/Desktop/foo" instead of
"/Users/carlo/Desktop/foo". The new crossProviderKey normalises both by
stripping leading slashes and lowercasing before comparison. Non-path
identifiers (like Cowork space names: "PhD_Thesis") have no '/' and fall
back to comparing the project name, so they are unaffected.

* feat(parser): resolve other-provider worktrees before cross-provider merge

Codex (and similar tools) store worktrees under paths like
~/.codex/worktrees/<hash>/Repo. These were not being resolved to their
main-repo path because canonicalizeProviderCallProject only operates on
call.projectPath, which Codex doesn't set — the project is inferred from
the session source slug instead.

Fix: before the cross-provider merge in parseAllSessions, run worktree
resolution on each non-Claude ProjectSummary's projectPath. Restore the
missing leading '/' (Codex strips it via sanitizeProject) before calling
resolveCanonicalProjectPath so the stat() can find the .git file. If the
path is a worktree, replace project name and path with the canonical
main-repo values so the downstream crossProviderKey matches.

* feat(dashboard): show more projects in By Project panel to fill panel height

The panel was hardcoded to 8 projects regardless of the Daily Activity
panel height alongside it. Pass the same `days` value used by Daily Activity
so the two panels stay in sync: 14 for week/6-months views, 31 for
month/30-days, 20 for all-time, and a minimum of 8 for the today view.

* feat(parser): resolve subdirectory sessions to their git repo root

When a cwd like ~/Repo/src/subdir is recorded, it should group with the
rest of the ~/Repo project. Previously resolveCanonicalProjectPath only
checked for a .git entry at the exact cwd, so subdirectory sessions
created separate ProjectSummary entries.

Walk up the directory tree until a .git entry is found. Guard against
foreign paths (a Windows path like C:\… on macOS cannot be walked on
the local filesystem) by only walking paths that look like absolute
paths on the current platform.

This also folds non-Claude provider subdirectory sessions into the parent
repo via resolveOtherProjectWorktrees, which now triggers whenever
canonical.path differs from the input path (not only for git worktrees).

* Address reviewer feedback: deduplicate getDesktopSessionsDir and fix test

Export getDesktopSessionsDir from claude.ts and import it in parser.ts
instead of maintaining an identical copy in both files.

Update the 'merges by sanitized slug' test to reflect the new path-aware
merge behavior: sessions with the same slug but different cwds now stay
separate (crossProviderKey keys by path, not slug), which is correct since
they represent different repos.
2026-05-25 13:41:53 -07:00
Resham Joshi
ce043631ed
Add icon-only menubar display mode (#396)
Some checks are pending
CI / semgrep (push) Waiting to run
Adds "Icon Only" option to the Metric picker in Settings. When
selected, the menubar shows only the flame icon without any cost
or token text. Flame tinting for quota/budget alerts still works.
2026-05-24 18:05:15 -07:00
Resham Joshi
820a5bd2b7
Fix OpenCode provider yielding zero usage for newer schema variants (#394)
Some checks are pending
CI / semgrep (push) Waiting to run
Broaden message/part parsing to handle OpenCode versions that store
tokens at the session level, use alternative field names, or emit
different part type identifiers. Adds session-level token fallback
when per-message parsing yields nothing.

Closes #392
2026-05-24 10:39:46 -07:00
Resham Joshi
5800179da9
Add multi-day calendar selection with custom popover UI (#393)
* Add multi-day calendar selection with custom popover UI

Replace native DatePicker with a custom-themed CalendarPopover supporting
non-contiguous multi-day selection. Add --days CLI flag for comma-separated
date filtering that post-filters from the bounding range scan.

CLI: parseDaysFlag() parses and validates dates, filterProjectsByDays()
filters turns to only selected days. Verified that sum of individual --day
calls matches --days output across cost, calls, activities, models, and
providers.

Swift: DataClient passes --days for multi-day, with single-day fallback.
AppStore tracks selectedDays as Set<String> with PayloadCacheKey support.
CalendarPopover uses pending state committed only on Done tap.

* Clear + Done in calendar resets to current period

When user clears all selected days and taps Done, switch back to the
active period instead of silently keeping the old day selection.
2026-05-24 09:49:59 -07:00
iamtoruk
aa69857e30 Add configurable menubar status period (#302)
Some checks are pending
CI / semgrep (push) Waiting to run
The menubar status item can now track Today, Week, Month, or 6 Months
independently from the popover's active period. Setting is persisted
in UserDefaults and changeable from Settings or via `defaults write`.

Thanks @ozymandiashh. Closes #291.
2026-05-24 03:15:39 -07:00
Resham Joshi
1cf36aed2d
Change Discord link to new invite
Updated Discord invite link in README.
2026-05-24 03:07:56 -07:00
iamtoruk
78b413bf7a Add VSCodium storage discovery for VS Code-family providers (#233)
Copilot, Roo Code, and KiloCode now scan VSCodium and VS Code Insiders
storage roots in addition to VS Code. Also updates Discord invite link.
2026-05-24 02:32:32 -07:00
iamtoruk
82d6c7fa4d Fix Kiro post-February storage discovery (#339) 2026-05-24 02:25:50 -07:00
iamtoruk
6e24f826bd Fix Kiro post-February storage discovery (#339)
Adds extensionless session index and nested execution file discovery
while preserving legacy .chat support. Modern execution JSON is parsed
for identifiers, timestamps, model IDs, conversation text, structured
tools, and token estimates. Also fixes dedup key poisoning on invalid
timestamps in the legacy parser.
2026-05-24 02:25:09 -07:00