Commit graph

184 commits

Author SHA1 Message Date
AgentSeal
8c87947980
Merge pull request #83 from AgentSeal/chore/block-claude-coauthor-prs
CI: block Co-authored-by Claude/Anthropic trailers on PRs
2026-04-18 09:04:54 -07:00
AgentSeal
e834f64c22 ci: block Co-authored-by Claude/Anthropic trailers on PRs
New GitHub Actions check that scans every PR commit for
`Co-authored-by: ... claude ...` or `... anthropic ...` trailers and
fails the PR with a clear remediation message if found. Contributors
can still use AI tools; the trailer attribution must be removed before
the PR is eligible to merge, consistent with the project contributor
guidelines.

The workflow scans only commits introduced by the PR
(base.sha..head.sha), so existing history is untouched.
2026-04-18 09:02:48 -07:00
AgentSeal
7a5cb32e4c Merge: restore agent tabs + Connect Claude button 2026-04-18 07:17:49 -07:00
AgentSeal
43a938ff9e feat(mac): add Connect Claude button to Plan pane
The Plan pane previously told users to "run claude login in your
terminal, then retry" with no way to start the flow from the app.
Added a primary Connect Claude button on both the no-credentials and
failed states that launches Terminal.app with `claude login`, so the
OAuth flow is one click away.

TerminalLauncher.openClaudeLogin() uses a hardcoded literal, so no
user input reaches AppleScript. Refactored the common path into
runInTerminal(command:preValidated:) which re-validates any non-
literal input against CodeburnCLI.isSafe as defense-in-depth.

On machines without Terminal.app (iTerm/Ghostty/Warp), the button
surfaces an inline instruction to run `claude login` manually instead
of failing silently.
2026-04-18 06:54:57 -07:00
AgentSeal
9483d66e65 fix(mac): restore agent tab strip to show all detected providers
Tabs were filtering on `value > 0` (today's spend), which hid the row
whenever only one provider had activity today. The CLI's providers map
already contains only providers detected on the system, so showing the
map as-is matches user intent: a tab for each installed tool,
regardless of today's spend. Tab strip only hides when nothing is
detected.

This also makes the Plan pill reachable again: it gates on
`selectedProvider == .claude`, which required clicking the Claude tab
to select.
2026-04-18 06:54:06 -07:00
AgentSeal
29e175c735 Merge: hide Mac agent tabs when fewer than two providers have spend
Brings in the Mac menubar's agent tab visibility rule: tabs only appear when
two or more providers have non-zero spend in the all-provider today view.
Also expands ProviderFilter to include every provider the CLI supports
(OpenCode, Pi) so their tabs appear when those tools produce sessions.
2026-04-18 05:08:10 -07:00
AgentSeal
85d7bea7ea feat(mac): hide agent tabs when fewer than two providers have spend
The tab strip was visible for everyone regardless of which tools they
actually run, which produced a row of All + one provider for Claude-only
users and a row of All + zeros for users on exotic stacks. Hide the
whole row until a second provider has real spend, matching the behavior
the GNOME extension ships with.

Also expand ProviderFilter to include every provider the CLI supports
(OpenCode and Pi were missing) so their tabs appear when those tools
produce sessions. The CLI already emits pi and opencode in the payload's
providers map; the Mac app just wasn't offering a tab for them.

visibleFilters now filters on value > 0 instead of key presence, because
the CLI includes zero-cost entries for discovered-but-unused providers
and we don't want those rendering as blank tabs.
2026-04-18 05:07:36 -07:00
AgentSeal
b232b3cfbe
Merge pull request #76 from AgentSeal/fix/drop-prebuild-install
Drop better-sqlite3 to remove deprecated prebuild-install (#75)
2026-04-18 10:39:26 +02:00
AgentSeal
7aefd674fc fix: drop better-sqlite3 to remove deprecated prebuild-install (#75)
npm was warning on every install that prebuild-install@7.1.3 is no
longer maintained. prebuild-install ships as a transitive dependency
of better-sqlite3 and upstream PR #1446 to replace it is still open,
so we switch to Node's built-in node:sqlite module (stable in Node 24,
experimental in Node 22/23) and remove the better-sqlite3 dep entirely.

- src/sqlite.ts: uses DatabaseSync from node:sqlite. The one-shot
  ExperimentalWarning about SQLite on Node 22/23 is silenced for that
  specific warning; other warnings pass through unchanged.
- package.json: engines.node bumped to >=22 (Node 20 EOL 2026-04-30),
  better-sqlite3 and @types/better-sqlite3 removed, @types/node added
  (it was coming in transitively via @types/better-sqlite3).
- tests/providers/opencode.test.ts: fixture DB creation switched to
  node:sqlite (API parity for the CREATE TABLE + INSERT + prepare
  path we use).

End-user install footprint shrinks from 167 to 40 packages and prints
zero deprecation warnings.

Credit: @primeminister for the report.
2026-04-18 01:26:23 -07:00
AgentSeal
2b15256189 docs: actually update README to the renamed screenshot path
The prior rename commit moved the PNG but forgot to stage the matching
README edit, leaving the live image tag pointing at a path that no
longer existed. This fixes it.
2026-04-17 17:43:09 -07:00
AgentSeal
1037d2c527 docs: rename README screenshot so CDN+Camo re-fetch
The ?v=0.7.2 query bust wasn't enough; GitHub's Camo proxy was still
serving the SwiftBar-era image to viewers. Renaming the asset to a
new path forces every downstream cache to treat it as a new resource.
2026-04-17 17:41:28 -07:00
AgentSeal
feda92124d docs: bust CDN cache on menubar screenshot
Appends ?v=0.7.2 to the image URL so GitHub's Camo proxy re-fetches
the new 0.7.2 screenshot instead of serving its stale copy of the
SwiftBar-era one.
2026-04-17 17:29:16 -07:00
AgentSeal
d6381d1eeb
Merge pull request #73 from AgentSeal/feat/mac-menubar-app
Native macOS menubar app (replaces SwiftBar)
2026-04-18 02:14:10 +02:00
Resham Joshi
0b96ff182e docs: scrub private strategy notes from public text
Removes references to future signing decisions, dollar amounts, and
star thresholds from the menubar README, the CHANGELOG, the release
workflow (its YAML comments and the auto-generated release body), and
the packaging script. The technical description stays; the 'we are
not paying for X right now' framing is out.
2026-04-17 17:12:48 -07:00
AgentSeal
818a249c87 chore: release 0.7.2 native macOS menubar app
See CHANGELOG.md for the full breakdown. Highlights:

- Native Swift + SwiftUI menubar app under mac/ replaces the SwiftBar
  plugin. Install via `npx codeburn menubar`.
- `status --format menubar-json` payload builder.
- `export -f csv` now writes a folder of clean per-table CSVs; `-o`
  path guard prevents arbitrary deletion.
- `status` terminal Today/Month bucketed by local date instead of UTC.
- FX rate values clamped to sane bounds in both runtimes.
- SwiftBar plugin, install-menubar / uninstall-menubar, and
  `--format menubar` removed.
2026-04-17 17:08:24 -07:00
Resham Joshi
03f12ce81f fix(status): bucket Today/Month by local date, not UTC
renderStatusBar computed `today` via `new Date().toISOString().slice(0,10)`,
which is the UTC date. Session timestamps are also UTC ISO strings, but
the user's expectation of "today" is their wall-clock day. During the
window between local midnight and UTC midnight (e.g. 17:00 PDT on
2026-04-17, which is already 00:00 UTC on 2026-04-18), every session
bucketed under local April 17 missed the UTC-April-18 filter and the
status bar read `Today $0.00  0 calls` even while `--format json`
and the menubar app correctly showed the spend.

Both sides of the comparison now use the local date of each session
timestamp, so the terminal status and the JSON / menubar paths agree.
Verified at UTC midnight (the regression moment that surfaced the bug):

  Before: Today $0.0000  0 calls
  After:  Today $339.87  1839 calls

Caught during the fresh-clone review of the menubar PR.
2026-04-17 17:05:08 -07:00
Resham Joshi
3482478a49 fix(mac): drop empty Resources/ reference from Package.swift
Caught in a fresh-clone smoke test: SwiftPM refused to build because
.process("../../Resources") pointed at an empty directory that git
does not track. The bundle has no assets to ship yet (icon will land
with signing work), so the reference is gone. Build passes on a clean
checkout and the packaging script produces the universal .app zip as
expected.
2026-04-17 17:01:00 -07:00
Resham Joshi
495a254338 feat(mac): native Swift menubar app + one-command install
Introduces mac/ with a native SwiftUI menubar app that replaces the
previous SwiftBar plugin entirely. Install via `npx codeburn menubar`,
which downloads the .app from GitHub Releases, strips Gatekeeper
quarantine, and drops it into ~/Applications.

Highlights

- mac/ SwiftUI app: agent tabs, Today/7/30/Month/All period switcher,
  Trend/Forecast/Pulse/Stats/Plan insights, activity + model
  breakdowns, optimize findings, CSV/JSON export, Star-on-GitHub
  banner, live 60s refresh, instant currency switching with offline FX
  cache.
- Security: CodeburnCLI argv-based spawn (no shell interpretation),
  SafeFile symlink guards + O_NOFOLLOW writes, FX rate clamping to
  [0.0001, 1_000_000], keychain filtered to account == "default",
  removed byte-window credential log, in-flight refresh guard, POSIX
  flock on config.json writes, TerminalLauncher validates argv before
  AppleScript interpolation.
- Performance: shared static NumberFormatter (thousands of allocations
  per popover redraw eliminated), concurrent pipe drain with 20 MB cap
  + 60s timeout in DataClient, Observation-tracked reactive UI, 5-min
  payload cache keyed on (period, provider).
- CLI: new `codeburn menubar` subcommand that downloads + installs +
  launches the .app (no clone, no build). New `status --format
  menubar-json` payload builder. `export` rewritten to produce a
  folder of one-table-per-file CSVs with a `.codeburn-export` marker
  so arbitrary -o paths cannot be silently deleted.
- Removed: src/menubar.ts (SwiftBar plugin generator),
  install-menubar / uninstall-menubar subcommands, `status --format
  menubar` directive output, tests/menubar.test.ts,
  tests/security/menubar-injection.test.ts.
- Release: .github/workflows/release-menubar.yml builds universal
  binary, assembles .app, ad-hoc signs, zips, uploads on mac-v* tag
  push. Runs on the free macos-latest runner.

Tests

- 230 TypeScript tests pass
- 10 Swift CapacityEstimator tests pass
- TypeScript typecheck clean
- Swift release build clean
2026-04-17 16:55:56 -07:00
AgentSeal
69268a9e91 docs: remove .claudeignore references from README 2026-04-17 05:24:16 -07:00
AgentSeal
41c84b1e51
Merge pull request #66 from jeisaacs/fix/menubar-node-version-path
fix: prepend install-time node bin dir to menubar plugin PATH
2026-04-17 14:21:58 +02:00
AgentSeal
77257bcb89
Merge pull request #68 from lfl1337/fix/remove-claudeignore-references
docs(optimize): remove references to .claudeignore (#61)
2026-04-17 14:20:50 +02:00
AgentSeal
09cea9bc79 chore: release 0.7.1 security hardening 2026-04-17 05:08:37 -07:00
AgentSeal
774d1917d4
Merge pull request #67 from lfl1337/fix/security-hardening-2026-04
fix: security hardening from external audit
2026-04-17 14:06:32 +02:00
Ninym
d5ecedb188 chore(git): ignore local .claude/ directory
Prevents agent-scratchpad files under .claude/ from being accidentally
staged by tooling. Matches the existing pattern for docs/superpowers/.
2026-04-17 08:36:24 +02:00
Ninym
8f5927153e feat(cli): add --verbose flag for stderr warnings
Sets CODEBURN_VERBOSE=1 via commander preAction, which the fs-utils
helpers check before emitting stderr lines on skipped or failed reads.
Closes LOW-1 from the 2026-04-16 audit.
2026-04-17 08:32:20 +02:00
Ninym
646635c262 fix(menubar): sanitize SwiftBar labels via allowlist
Replaces any character outside [A-Za-z0-9 ._/-] with ? in model and
category labels and truncates to 14 chars before padEnd. Closes the
MEDIUM-2 finding from the 2026-04-16 audit: an attacker-controlled
JSONL with a crafted model name no longer injects SwiftBar directives
or ANSI escapes.
2026-04-17 08:32:20 +02:00
Ninym
71461fb352 test(security): add failing test for MEDIUM-2 menubar injection
Three cases (pipe-in-model, ANSI-in-model, pipe-in-category) reproduce
the audit's SwiftBar directive-separator attack. Tests fail against
current menubar.ts -- Task 13 will close with an allowlist sanitizer.
2026-04-17 08:32:20 +02:00
Ninym
216782391a fix(optimize): use bounded read helpers
All four read paths in the optimizer (async session scan + three sync
config/import/profile scans) now pass through the 128 MB-capped
helpers. JSON.parse in readJsonFile stays wrapped in try/catch.
MEDIUM-1 coverage for the optimize module.
2026-04-17 08:32:20 +02:00
Ninym
1bdbac4927 fix(context-budget): use bounded readSessionFile helper
Config JSON, CLAUDE.md scans, and session-discovery reads now pass
through the 128 MB-capped helper. JSON.parse remains wrapped in
try/catch to preserve the previous 'null on malformed JSON' contract.
MEDIUM-1 coverage for the context-budget module.
2026-04-17 08:32:19 +02:00
Ninym
716e080cb3 fix(pi): use bounded readSessionFile helper
Both Pi session read paths (first-entry meta and full-session parse)
now pass through the 128 MB-capped helper. MEDIUM-1 coverage for the
Pi provider.
2026-04-17 08:32:19 +02:00
Ninym
9f6827d528 fix(copilot): use bounded readSessionFile helper
Events JSONL and workspace.yaml reads now pass through the 128 MB-capped
helper. The workspace.yaml path stays non-fatal: a null read skips cwd
derivation but still pushes the session with sessionId as the fallback
project label. MEDIUM-1 coverage for the Copilot provider.
2026-04-17 08:32:19 +02:00
Ninym
1de0baf329 fix(codex): use bounded readSessionFile helper
Both Codex session read paths (first-line meta and full-session parse)
now pass through the 128 MB-capped helper. MEDIUM-1 coverage for the
Codex provider.
2026-04-17 08:32:19 +02:00
Ninym
ee738a1b26 fix(parser): use bounded readSessionFile helper
Replaces the unbounded readFile in parseSessionFile with the 128 MB-capped
helper from src/fs-utils. Addresses MEDIUM-1 for the Claude provider
hot path.

Verbose-mode stderr output replaces the previous silent catch,
closing LOW-1 as a side effect.
2026-04-17 08:32:19 +02:00
Ninym
82c3125638 feat(fs-utils): bounded session-file read helper
Adds readSessionFile / readSessionFileSync / readSessionLines with a
128 MB hard cap and 8 MB streaming threshold. Verbose mode
(CODEBURN_VERBOSE=1) logs skipped and failed reads to stderr.

Prepares the MEDIUM-1 migration of all provider read paths.
2026-04-17 08:32:19 +02:00
Ninym
0ab66f6fe9 test(fs-utils): add failing test for bounded read helper
Tests the to-be-built readSessionFile helper: under-cap fast path,
at-threshold stream path, over-cap null+skip, verbose stderr warning,
and stat-failure graceful fallback. Fails against missing module --
Task 5 will implement src/fs-utils.ts to flip GREEN.
2026-04-17 08:32:18 +02:00
Ninym
5b810161e7 fix(parser): block prototype pollution via Object.create(null)
Initialize the four breakdown maps (model, tool, mcp, bash) with null
prototype so attacker-controlled keys named __proto__ create own
properties on the map instead of mutating Object.prototype.

Closes the HIGH-1 finding from the 2026-04-16 external security audit.
2026-04-17 08:32:18 +02:00
Ninym
e890d9bfc3 test(security): add failing test for HIGH-1 prototype pollution
Three PoC fixtures (tool name, bash command, model name) reproduce
the audit's HIGH-1 attack. Tests assert Object.prototype.calls stays
undefined after parsing. They fail against current parser.ts -- Task 3
will close the pollution sink with Object.create(null).
2026-04-17 08:32:18 +02:00
Ninym
bd71377fdd docs(optimize): remove references to non-existent .claudeignore
Claude Code does not document or implement a .claudeignore feature.
The junk-reads detector's fix is now a CLAUDE.md instruction asking
Claude to avoid generated/dependency directories. The separate
detectMissingClaudeignore finding and its tests are removed; checking
for the presence of a non-existent file has no signal.

Closes #61.
2026-04-17 08:32:07 +02:00
Jonathan
276b14adea fix: prepend install-time node bin dir to menubar plugin PATH
SwiftBar/xbar launch plugins with a minimal environment. If an older
system Node (e.g. v18.x from Homebrew) appears earlier on PATH than the
NVM-managed Node used to install codeburn, the plugin silently fails
because string-width uses the /v regex flag (requires Node >= 20.11).

Resolve the node binary directory at install time via process.execPath
and prepend it to PATH in the generated plugin script. Also explicitly
set HOME, which SwiftBar does not always inherit.

Fixes #63
2026-04-17 07:29:14 +01:00
AgentSeal
f2d1753d3a
Merge pull request #60 from AgentSeal/feat/optimize
feat: codeburn optimize -- find waste and get copy-paste fixes
2026-04-17 01:31:52 +02:00
AgentSeal
005bdaaf07 chore: release 0.7.0 -- codeburn optimize
New top-level command plus in-TUI view for finding token waste and
getting copy-paste fixes. 11 detectors, A-F setup health grade,
recent-vs-baseline trend tracking, per-project context budget column.
198 tests.
2026-04-16 16:31:19 -07:00
AgentSeal
40b7de6841 docs: add optimize section with screenshot and command reference
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.
2026-04-16 16:27:33 -07:00
AgentSeal
f958756861 chore(optimize): name preview-count literals and drop punctuation dashes
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.
2026-04-16 16:20:53 -07:00
AgentSeal
3bd3c0d7b4 chore(optimize): extract trend period magic number to named constant 2026-04-16 16:09:19 -07:00
AgentSeal
1cd96ea19f Merge origin/main into feat/optimize
Resolve conflicts in src/dashboard.tsx: keep optimize view plumbing
(setOptimizeResult, OptimizeResult state, o/b keys) while integrating
project/exclude filters on reloadData and renderDashboard entry.
2026-04-16 16:08:20 -07:00
AgentSeal
3ea2c0b255 chore: release 0.6.1 -- JSON output, project filters, claude-opus-4-7, Top Sessions fix 2026-04-16 15:55:29 -07:00
AgentSeal
87210da598 Merge pull request #52 from mallek/feature/project-filter
feat: add --project and --exclude filters for project-level filtering
2026-04-16 15:52:57 -07:00
AgentSeal
d15532af0b fix: apply --project/--exclude to menubar per-provider today totals
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.
2026-04-16 15:50:00 -07:00
AgentSeal
98c1e266d7 test: cover filterProjectsByName include/exclude semantics
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.
2026-04-16 15:49:57 -07:00
AgentSeal
d90042ec3c fix: stop Top Sessions panel from truncating the calls column
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.
2026-04-16 15:44:39 -07:00