Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Unreleased

### Added (CLI)
- **Redacted share bundles.** New `codeburn share` command writes a local JSON support bundle with project/session/turn structure while pseudonymizing project labels and redacting common emails, local paths, credentials, bearer tokens, and API keys. Prompt text is omitted by default and can be explicitly included with `--include-prompts`. Supports period, date range, provider, project, exclude, and output-path filters.
- **Multiple subscription plans can be tracked at the same time.**
`codeburn plan set` now stores plans in a provider-keyed `plans` map, so
setting a Codex custom plan no longer overwrites an existing Claude plan.
Expand Down
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ codeburn status # compact one-liner (today + month)
codeburn status --format json
codeburn export # CSV with today, 7 days, 30 days
codeburn export -f json # JSON export
codeburn share # redacted JSON bundle; prompts omitted by default
codeburn optimize # find waste, get copy-paste fixes
codeburn optimize -p week # scope the scan to last 7 days
codeburn compare # side-by-side model comparison
Expand Down Expand Up @@ -127,7 +128,7 @@ Provider logos are trademarks of their respective owners. The icon set was sourc

CodeBurn auto-detects which AI coding tools you use. If multiple providers have session data on disk, press `p` in the dashboard to toggle between them.

The `--provider` flag filters any command to a single provider: `codeburn report --provider claude`, `codeburn today --provider codex`, `codeburn export --provider cursor`. Works on all commands: `report`, `today`, `month`, `status`, `export`, `optimize`, `compare`, `yield`.
The `--provider` flag filters any command to a single provider: `codeburn report --provider claude`, `codeburn today --provider codex`, `codeburn export --provider cursor`. Works on all commands: `report`, `today`, `month`, `status`, `export`, `share`, `optimize`, `compare`, `yield`.

### Provider Notes

Expand Down Expand Up @@ -334,6 +335,20 @@ codeburn today --format json | jq '.overview.cost'

For lighter output, use `status --format json` (today and month totals only) or file exports (`export -f json`).

### Redacted Share

Create a local support bundle without posting raw prompts, project labels, absolute paths, emails, or common API tokens:

```bash
codeburn share # 7-day redacted JSON bundle
codeburn share -p 30days # last 30 days
codeburn share --provider claude # provider-specific bundle
codeburn share --project api -o api-share.json
codeburn share --include-prompts # opt in to redacted prompt text
```

The default bundle omits prompt text (`userMessage: null`) and keeps enough structure to debug provider parsing and cost attribution: pseudonymous projects, sessions, turns, models, token usage, tools, activity categories, and costs. `--include-prompts` keeps best-effort redacted prompt text for cases where maintainers explicitly need it. Review the generated file before posting it publicly.

## Menu Bar

```bash
Expand Down
57 changes: 57 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { formatDateRangeLabel, parseDateRangeFlags, getDateRange, toPeriod, type
import { runOptimize, scanAndDetect } from './optimize.js'
import { renderCompare } from './compare.js'
import { getAllProviders } from './providers/index.js'
import { buildRedactedShare, writeRedactedShare } from './share.js'
import { clearPlan, readConfig, readPlan, readPlans, saveConfig, savePlan, getConfigFilePath, type Plan, type PlanId, type PlanProvider } from './config.js'
import { clampResetDay, getPlanUsageOrNull, getPlanUsages, type PlanUsage } from './plan-usage.js'
import { getPresetPlan, isPlanId, isPlanProvider, PLAN_IDS, PLAN_PROVIDERS, planDisplayName } from './plans.js'
Expand Down Expand Up @@ -695,6 +696,62 @@ program
console.log(`\n Exported (${exportedLabel}) to: ${savedPath}\n`)
})

program
.command('share')
.description('Export a redacted local JSON bundle for debugging or support')
.option('-p, --period <period>', 'Share period: today, week, 30days, month, all', 'week')
.option('--from <date>', 'Start date (YYYY-MM-DD). Overrides --period when set')
.option('--to <date>', 'End date (YYYY-MM-DD). Overrides --period when set')
.option('-o, --output <path>', 'Output file path')
.option('--provider <provider>', 'Filter by provider (e.g. claude, gemini, cursor, copilot)', 'all')
.option('--project <name>', 'Include only projects matching name (repeatable)', collect, [])
.option('--exclude <name>', 'Exclude projects matching name (repeatable)', collect, [])
.option('--include-prompts', 'Include redacted user prompts. By default prompts are omitted.')
.action(async (opts) => {
let customRange: DateRange | null = null
try {
customRange = parseDateRangeFlags(opts.from, opts.to)
} catch (err) {
const message = err instanceof Error ? err.message : String(err)
console.error(`\n Error: ${message}\n`)
process.exit(1)
}

await loadPricing()

const period = toPeriod(opts.period)
const rangeInfo = customRange
? { range: customRange, label: formatDateRangeLabel(opts.from, opts.to) }
: getDateRange(period)

const projects = filterProjectsByName(
await parseAllSessions(rangeInfo.range, opts.provider),
opts.project,
opts.exclude,
)

if (projects.length === 0) {
console.log('\n No usage data found.\n')
return
}

const now = new Date()
const timeSuffix = `${now.toTimeString().slice(0, 8).replace(/:/g, '')}${String(now.getMilliseconds()).padStart(3, '0')}`
const defaultName = `codeburn-share-${toDateString(now)}-${timeSuffix}.json`
const outputPath = opts.output ?? defaultName
const share = buildRedactedShare(projects, {
label: rangeInfo.label,
range: rangeInfo.range,
provider: opts.provider,
project: opts.project,
exclude: opts.exclude,
includePrompts: opts.includePrompts === true,
})
const savedPath = await writeRedactedShare(share, outputPath)
console.log(`\n Redacted share exported to: ${savedPath}`)
console.log(' Review before posting publicly; redaction is best-effort.\n')
})

program
.command('menubar')
.description('Install and launch the macOS menubar app (one command, no clone)')
Expand Down
Loading
Loading