From fb477f9916fac867add3ab59d4e2a9ee49224b54 Mon Sep 17 00:00:00 2001
From: Aditya Vikram Singh <247195684+avs-io@users.noreply.github.com>
Date: Thu, 20 Aug 2026 00:20:27 +0530
Subject: [PATCH 1/3] fix(optimize): scope remediation copy to --provider
Cross-provider optimize findings and destination headers were
Claude-hardcoded after #1002 already scoped the detectors.
---
src/dashboard.tsx | 33 ++++--------
src/optimize.ts | 118 +++++++++++++++++++++++++++++++----------
tests/optimize.test.ts | 103 +++++++++++++++++++++++++++++++++++
3 files changed, 202 insertions(+), 52 deletions(-)
diff --git a/src/dashboard.tsx b/src/dashboard.tsx
index 7eeeaf35..2ea3c0f7 100644
--- a/src/dashboard.tsx
+++ b/src/dashboard.tsx
@@ -10,7 +10,7 @@ import { findUnpricedModels, isExpectedFreeModel, loadPricing } from './models.j
import { aggregateModelTotals } from './model-breakdown.js'
import { buildDurablePeriod } from './usage-aggregator.js'
import { getAllProviders } from './providers/index.js'
-import { classHeaderLine, classTotals, findingBasis, findingClass, scanAndDetect, type FindingClass, type WasteFinding, type WasteAction, type OptimizeResult } from './optimize.js'
+import { classHeaderLine, classTotals, findingBasis, findingClass, optimizePasteHeader, optimizeRemediationCopy, scanAndDetect, type FindingClass, type WasteFinding, type WasteAction, type OptimizeResult } from './optimize.js'
import { appliedFixGlyph, formatAppliedFix, type AppliedFix } from './act/types.js'
import { aggregateFileChurn, buildCoachingNotes, computePricingCoverage, medianTimeToFirstEditMs, scanUserCorrections, worstOneShotCategory, type ReworkedFile } from './workflow-insights.js'
import { estimateContextBudget, type ContextBudget } from './context-budget.js'
@@ -1033,34 +1033,21 @@ function PeriodTabs({ active, providerName, showProvider }: { active: Period; pr
/// permanent CLAUDE.md rule from a one-time session opener so they don't
/// accidentally bake a single-run constraint into their project's permanent
/// instructions. Issue #277.
-function actionDestinationHeader(action: WasteAction): string {
+function actionDestinationHeader(action: WasteAction, provider?: string): string {
switch (action.type) {
case 'file-content':
return `── Suggested ${action.path} addition `.padEnd(64, '─')
case 'command':
return '── Run this command '.padEnd(64, '─')
case 'paste': {
- switch (action.destination) {
- case 'claude-md':
- return '── Suggested CLAUDE.md addition (permanent rule) '.padEnd(64, '─')
- case 'session-opener':
- return '── One-time session opener (do not add to CLAUDE.md) '.padEnd(64, '─')
- case 'prompt':
- return '── Ask Claude in the current session '.padEnd(64, '─')
- case 'shell-config':
- return '── Add to your shell config '.padEnd(64, '─')
- case 'manual':
- return '── Manual action '.padEnd(64, '─')
- default:
- return '── Suggested action '.padEnd(64, '─')
- }
+ return `── ${optimizePasteHeader(action.destination, optimizeRemediationCopy(provider))} `.padEnd(64, '─')
}
}
}
-function FindingAction({ action }: { action: WasteAction }) {
+function FindingAction({ action, provider }: { action: WasteAction; provider?: string }) {
const lines = action.type === 'file-content' ? action.content.split('\n') : action.type === 'command' ? action.text.split('\n') : [action.text]
- const header = actionDestinationHeader(action)
+ const header = actionDestinationHeader(action, provider)
return (
<>
{header}
@@ -1070,7 +1057,7 @@ function FindingAction({ action }: { action: WasteAction }) {
)
}
-function FindingPanel({ index, finding, costRate, width }: { index: number; finding: WasteFinding; costRate: number; width: number }) {
+function FindingPanel({ index, finding, costRate, width, provider }: { index: number; finding: WasteFinding; costRate: number; width: number; provider?: string }) {
const costSaved = finding.tokensSaved * costRate
const color = IMPACT_PANEL_COLORS[finding.impact] ?? DIM
const label = finding.impact.charAt(0).toUpperCase() + finding.impact.slice(1)
@@ -1086,7 +1073,7 @@ function FindingPanel({ index, finding, costRate, width }: { index: number; find
{finding.explanation}
Savings: ~{formatTokens(finding.tokensSaved)} tokens (~{formatCost(costSaved)}) {findingBasis(finding)}
-
+
)
}
@@ -1106,7 +1093,7 @@ const APPLIED_FIX_COLORS: Record = {
pending: DIM,
}
-function OptimizeView({ findings, costRate, projects, label, width, healthScore, healthGrade, cursor, appliedFixes = [] }: { findings: WasteFinding[]; costRate: number; projects: ProjectSummary[]; label: string; width: number; healthScore: number; healthGrade: string; cursor: number; appliedFixes?: AppliedFix[] }) {
+function OptimizeView({ findings, costRate, projects, label, width, healthScore, healthGrade, cursor, appliedFixes = [], provider }: { findings: WasteFinding[]; costRate: number; projects: ProjectSummary[]; label: string; width: number; healthScore: number; healthGrade: string; cursor: number; appliedFixes?: AppliedFix[]; provider?: string }) {
const periodCost = projects.reduce((s, p) => s + p.totalCostUSD, 0)
const totalTokens = findings.reduce((s, f) => s + f.tokensSaved, 0)
const totalCost = totalTokens * costRate
@@ -1140,7 +1127,7 @@ function OptimizeView({ findings, costRate, projects, label, width, healthScore,
return (
{cls !== previous && {classHeaderLine(cls, totals[cls], costRate)}}
-
+
)
})}
@@ -1666,7 +1653,7 @@ export function InteractiveDashboard({ initialProjects, initialDailyHistoryProje
{view === 'compare'
? setView('dashboard')} />
: view === 'optimize' && optimizeResult
- ?
+ ?
: }
{coachingNote && (
diff --git a/src/optimize.ts b/src/optimize.ts
index 2ecd348b..c6d94a63 100644
--- a/src/optimize.ts
+++ b/src/optimize.ts
@@ -249,6 +249,70 @@ export type PasteDestination =
| 'shell-config' // append to ~/.zshrc / ~/.bashrc
| 'manual' // instructions the user carries out directly
+/// Provider-scoped remediation nouns. Cross-provider detectors and both
+/// render surfaces (CLI + TUI) share this so `--provider codex` cannot still
+/// tell the user to ask Claude or edit CLAUDE.md. Claude / unset / `all`
+/// keep the shipped CLAUDE.md copy. Only `codex` has a file CodeBurn already
+/// names (`AGENTS.md` in the Codex parser); every other provider stays on
+/// the generic "project instructions" rather than inventing a filename.
+export type OptimizeRemediationCopy = {
+ agent: string
+ instructionFile: string
+}
+
+const REMEDIATION_AGENT_NAMES: Record = {
+ claude: 'Claude',
+ codex: 'Codex',
+ cursor: 'Cursor',
+ copilot: 'Copilot',
+ gemini: 'Gemini',
+ opencode: 'OpenCode',
+ antigravity: 'Antigravity',
+ windsurf: 'Windsurf',
+ cline: 'Cline',
+ hermes: 'Hermes',
+ kimi: 'Kimi',
+ kimicode: 'Kimi Code',
+ 'ibm-bob': 'IBM Bob',
+ pi: 'Pi',
+}
+
+const REMEDIATION_INSTRUCTION_FILES: Record = {
+ claude: 'CLAUDE.md',
+ codex: 'AGENTS.md',
+}
+
+function humanizeProviderId(id: string): string {
+ return id.split(/[-_]/).map(part => part ? part[0]!.toUpperCase() + part.slice(1) : part).join(' ')
+}
+
+export function optimizeRemediationCopy(provider?: string): OptimizeRemediationCopy {
+ const key = !provider || provider === 'all' ? 'claude' : provider.toLowerCase()
+ return {
+ agent: REMEDIATION_AGENT_NAMES[key] ?? humanizeProviderId(key),
+ instructionFile: REMEDIATION_INSTRUCTION_FILES[key] ?? 'project instructions',
+ }
+}
+
+export function sessionOpenerLabel(copy: OptimizeRemediationCopy): string {
+ return `Paste at the start of your NEXT expensive thread (one-time, do not add to ${copy.instructionFile}):`
+}
+
+export function askAgentLabel(copy: OptimizeRemediationCopy, rest: string): string {
+ return `Ask ${copy.agent} to ${rest}:`
+}
+
+export function optimizePasteHeader(destination: PasteDestination | undefined, copy: OptimizeRemediationCopy): string {
+ switch (destination) {
+ case 'claude-md': return `Suggested ${copy.instructionFile} addition (permanent rule)`
+ case 'session-opener': return `One-time session opener (do NOT add to ${copy.instructionFile})`
+ case 'prompt': return `Ask ${copy.agent} in the current session`
+ case 'shell-config': return 'Add to your shell config'
+ case 'manual': return 'Manual action'
+ default: return 'Suggested action'
+ }
+}
+
export type WasteAction =
| { type: 'paste'; label: string; text: string; destination?: PasteDestination }
| { type: 'command'; label: string; text: string }
@@ -1695,6 +1759,7 @@ function collectMcpProjectProfiles(
export function detectMcpProfileAdvisor(
projects: ProjectSummary[],
coverage = aggregateMcpCoverage(projects),
+ provider?: string,
): WasteFinding | null {
const candidates = collectMcpProjectProfiles(projects, coverage)
if (candidates.length === 0) return null
@@ -1733,7 +1798,7 @@ export function detectMcpProfileAdvisor(
fix: {
type: 'paste',
destination: 'prompt',
- label: 'Ask Claude to turn this into a project-scoped MCP profile:',
+ label: askAgentLabel(optimizeRemediationCopy(provider), 'turn this into a project-scoped MCP profile'),
text: [
`Review these MCP profile recommendations before changing config (${preview.length} of ${candidates.length} shown):`,
...preview.map(candidate => {
@@ -1926,7 +1991,7 @@ function findCapabilityReliabilityCandidates(projects: ProjectSummary[]): Capabi
return candidates
}
-export function detectCapabilityReliability(projects: ProjectSummary[]): WasteFinding | null {
+export function detectCapabilityReliability(projects: ProjectSummary[], provider?: string): WasteFinding | null {
projects = userStartedProjects(projects)
const candidates = findCapabilityReliabilityCandidates(projects)
if (candidates.length === 0) return null
@@ -1989,7 +2054,7 @@ export function detectCapabilityReliability(projects: ProjectSummary[]): WasteFi
fix: {
type: 'paste',
destination: 'prompt',
- label: 'Ask Claude to audit the retry-heavy capability before changing config:',
+ label: askAgentLabel(optimizeRemediationCopy(provider), 'audit the retry-heavy capability before changing config'),
text: `Investigate these retry-correlated capabilities: ${names}. Compare edit turns with retries against one-shot edit turns, identify whether the MCP server or skill actually caused rework, then propose a scoped MCP config or skill-instruction change with session evidence. Do not remove a capability solely because it appears in this report.`,
},
}
@@ -3121,7 +3186,7 @@ export function findLowWorthCandidates(projects: ProjectSummary[]): LowWorthCand
return candidates
}
-export function detectLowWorthSessions(projects: ProjectSummary[]): WasteFinding | null {
+export function detectLowWorthSessions(projects: ProjectSummary[], provider?: string): WasteFinding | null {
const candidates = findLowWorthCandidates(projects)
if (candidates.length === 0) return null
@@ -3156,7 +3221,7 @@ export function detectLowWorthSessions(projects: ProjectSummary[]): WasteFinding
fix: {
type: 'paste',
destination: 'session-opener',
- label: 'Paste at the start of your NEXT expensive thread (one-time, do not add to CLAUDE.md):',
+ label: sessionOpenerLabel(optimizeRemediationCopy(provider)),
text: LOW_WORTH_OPENER,
},
}
@@ -3231,7 +3296,7 @@ export function findContextBloatCandidates(projects: ProjectSummary[]): ContextB
return candidates
}
-export function detectContextBloat(projects: ProjectSummary[], excludedSessionIds?: ReadonlySet): WasteFinding | null {
+export function detectContextBloat(projects: ProjectSummary[], excludedSessionIds?: ReadonlySet, provider?: string): WasteFinding | null {
const candidates = findContextBloatCandidates(projects)
.filter(c => !excludedSessionIds?.has(c.sessionId))
if (candidates.length === 0) return null
@@ -3272,13 +3337,13 @@ export function detectContextBloat(projects: ProjectSummary[], excludedSessionId
fix: {
type: 'paste',
destination: 'session-opener',
- label: 'Paste at the start of your NEXT expensive thread (one-time, do not add to CLAUDE.md):',
+ label: sessionOpenerLabel(optimizeRemediationCopy(provider)),
text: CONTEXT_HEAVY_OPENER,
},
}
}
-export function detectSessionOutliers(projects: ProjectSummary[], excludedSessionIds?: ReadonlySet): WasteFinding | null {
+export function detectSessionOutliers(projects: ProjectSummary[], excludedSessionIds?: ReadonlySet, provider?: string): WasteFinding | null {
type Outlier = {
project: string
sessionId: string
@@ -3353,7 +3418,7 @@ export function detectSessionOutliers(projects: ProjectSummary[], excludedSessio
fix: {
type: 'paste',
destination: 'session-opener',
- label: 'Paste at the start of your NEXT expensive thread (one-time, do not add to CLAUDE.md):',
+ label: sessionOpenerLabel(optimizeRemediationCopy(provider)),
text: 'Before making changes, summarize the smallest viable plan. Keep context narrow, avoid broad searches, and stop after the first working patch so I can review before continuing.',
},
}
@@ -3551,15 +3616,15 @@ export async function scanAndDetect(
claudeOnly(() => detectDuplicateReads(toolCalls, dateRange)),
claudeOnly(() => detectUnusedMcp(toolCalls, projects, projectCwds, mcpCoverage)),
() => detectMcpToolCoverage(projects, mcpCoverage, localMcpServerNames(projectCwds)),
- () => detectMcpProfileAdvisor(projects, mcpCoverage),
+ () => detectMcpProfileAdvisor(projects, mcpCoverage, provider),
// mcp-deferral-gaps family (#614): detection only, no apply plans yet.
claudeOnly(() => detectMcpDeferralOff(toolCalls, projects, projectCwds, apiCalls)),
claudeOnly(() => detectMcpAlwaysLoadHygiene(projects, projectCwds, apiCalls, mcpCoverage)),
claudeOnly(() => detectMcpDeferThreshold(projects, projectCwds)),
- () => detectCapabilityReliability(behavioralProjects),
- () => detectLowWorthSessions(behavioralProjects),
- () => detectContextBloat(behavioralProjects, lowWorthSessionIds),
- () => detectSessionOutliers(behavioralProjects, outlierExclusions),
+ () => detectCapabilityReliability(behavioralProjects, provider),
+ () => detectLowWorthSessions(behavioralProjects, provider),
+ () => detectContextBloat(behavioralProjects, lowWorthSessionIds, provider),
+ () => detectSessionOutliers(behavioralProjects, outlierExclusions, provider),
claudeOnly(() => detectBloatedClaudeMd(projectCwds)),
claudeOnly(() => detectBashBloat()),
claudeOnly(() => detectRecurringContext(openers)),
@@ -3624,7 +3689,7 @@ function wrap(text: string, width: number, indent: string): string {
/// destination. Issue #277: users were dropping one-time session openers
/// into CLAUDE.md as permanent rules because the prompts had no labeled
/// home in the output.
-function renderActionHeader(action: WasteAction): string {
+function renderActionHeader(action: WasteAction, copy: OptimizeRemediationCopy): string {
const headerWidth = PANEL_WIDTH - 4
const fillTo = (label: string): string => {
const inner = ` ${label} `
@@ -3637,18 +3702,11 @@ function renderActionHeader(action: WasteAction): string {
case 'command':
return fillTo('Run this command')
case 'paste':
- switch (action.destination) {
- case 'claude-md': return fillTo('Suggested CLAUDE.md addition (permanent rule)')
- case 'session-opener': return fillTo('One-time session opener (do NOT add to CLAUDE.md)')
- case 'prompt': return fillTo('Ask Claude in the current session')
- case 'shell-config': return fillTo('Add to your shell config')
- case 'manual': return fillTo('Manual action')
- default: return fillTo('Suggested action')
- }
+ return fillTo(optimizePasteHeader(action.destination, copy))
}
}
-function renderFinding(n: number, f: WasteFinding, costRate: number): string[] {
+function renderFinding(n: number, f: WasteFinding, costRate: number, copy: OptimizeRemediationCopy): string[] {
const lines: string[] = []
const costSaved = f.tokensSaved * costRate
const impactLabel = f.impact.charAt(0).toUpperCase() + f.impact.slice(1)
@@ -3674,7 +3732,7 @@ function renderFinding(n: number, f: WasteFinding, costRate: number): string[] {
// permanent rules and one-time prompts are no longer interchangeable in
// the output.
const a = f.fix
- lines.push(chalk.hex(ORANGE)(` ${renderActionHeader(a)}`))
+ lines.push(chalk.hex(ORANGE)(` ${renderActionHeader(a, copy)}`))
lines.push(chalk.hex(DIM)(` ${a.label}`))
if (a.type === 'file-content') {
for (const line of a.content.split('\n')) lines.push(chalk.hex(CYAN)(` ${line}`))
@@ -3743,7 +3801,9 @@ export function renderOptimize(
previouslyApplied?: Record,
modelRecommendations?: ModelDefaultRecommendation[],
appliedFixes: AppliedFix[] = [],
+ provider?: string,
): string {
+ const copy = optimizeRemediationCopy(provider)
const lines: string[] = []
lines.push('')
lines.push(` ${chalk.bold.hex(ORANGE)('CodeBurn config health')}${chalk.dim(' ' + periodLabel)}`)
@@ -3766,9 +3826,9 @@ export function renderOptimize(
if (findings.length === 0) {
lines.push(chalk.hex(GREEN)(' Nothing to fix. Your setup is lean.'))
lines.push('')
- lines.push(chalk.dim(' CodeBurn optimize scans your Claude Code sessions and config for'))
+ lines.push(chalk.dim(` CodeBurn optimize scans your ${copy.agent} sessions and config for`))
lines.push(chalk.dim(' token waste: junk directory reads, duplicate file reads, unused'))
- lines.push(chalk.dim(' agents/skills/MCP servers, bloated CLAUDE.md, and more.'))
+ lines.push(chalk.dim(` agents/skills/MCP servers, bloated ${copy.instructionFile}, and more.`))
lines.push('')
lines.push(...renderAppliedFixes(appliedFixes))
lines.push(...renderWorkflowSection(reworkedFiles, coachingNotes))
@@ -3799,7 +3859,7 @@ export function renderOptimize(
for (const f of group) {
const appliedOn = previouslyApplied?.[f.id]
const shown = appliedOn ? { ...f, title: `${f.title} (previously applied ${appliedOn}, re-flagged)` } : f
- lines.push(...renderFinding(++n, shown, costRate))
+ lines.push(...renderFinding(++n, shown, costRate, copy))
}
}
@@ -3868,7 +3928,7 @@ export async function runOptimize(
}
const { topReworkedFiles, coachingNotes } = buildWorkflowReport(projects)
- const output = renderOptimize(findings, costRate, periodLabel, periodCost, sessionCount, callCount, healthScore, healthGrade, topReworkedFiles, coachingNotes, opts.appliedHeader, opts.previouslyApplied, result.modelRecommendations, opts.appliedFixes)
+ const output = renderOptimize(findings, costRate, periodLabel, periodCost, sessionCount, callCount, healthScore, healthGrade, topReworkedFiles, coachingNotes, opts.appliedHeader, opts.previouslyApplied, result.modelRecommendations, opts.appliedFixes, opts.provider)
console.log(output)
}
diff --git a/tests/optimize.test.ts b/tests/optimize.test.ts
index def5d390..343abe9f 100644
--- a/tests/optimize.test.ts
+++ b/tests/optimize.test.ts
@@ -28,6 +28,10 @@ import {
buildOptimizeJsonReport,
renderOptimize,
findingBasis,
+ optimizeRemediationCopy,
+ optimizePasteHeader,
+ sessionOpenerLabel,
+ askAgentLabel,
type FindingId,
type ToolCall,
type ApiCallMeta,
@@ -1443,3 +1447,102 @@ describe('renderOptimize applied-fixes section', () => {
expect(render([], [])).not.toContain('Applied fixes')
})
})
+
+describe('provider-scoped remediation copy (#1044)', () => {
+ const strip = (s: string): string => s.replace(/\u001b\[[0-9;]*m/g, '')
+
+ function promptFinding(label: string): WasteFinding {
+ return {
+ id: 'retry-heavy-capabilities',
+ title: 'retry-heavy',
+ explanation: 'why',
+ impact: 'medium',
+ tokensSaved: 1000,
+ fix: { type: 'paste', destination: 'prompt', label, text: 'audit' },
+ }
+ }
+
+ function openerFinding(label: string): WasteFinding {
+ return {
+ id: 'low-worth-sessions',
+ title: 'low-worth',
+ explanation: 'why',
+ impact: 'low',
+ tokensSaved: 1000,
+ fix: { type: 'paste', destination: 'session-opener', label, text: 'open' },
+ }
+ }
+
+ it('keeps Claude / CLAUDE.md for unset, all, and claude', () => {
+ for (const provider of [undefined, 'all', 'claude'] as const) {
+ const copy = optimizeRemediationCopy(provider)
+ expect(copy).toEqual({ agent: 'Claude', instructionFile: 'CLAUDE.md' })
+ expect(optimizePasteHeader('prompt', copy)).toBe('Ask Claude in the current session')
+ expect(optimizePasteHeader('session-opener', copy)).toBe('One-time session opener (do NOT add to CLAUDE.md)')
+ expect(sessionOpenerLabel(copy)).toContain('CLAUDE.md')
+ expect(askAgentLabel(copy, 'audit the retry-heavy capability before changing config'))
+ .toBe('Ask Claude to audit the retry-heavy capability before changing config:')
+ }
+ })
+
+ it('uses Codex / AGENTS.md for --provider codex', () => {
+ const copy = optimizeRemediationCopy('codex')
+ expect(copy).toEqual({ agent: 'Codex', instructionFile: 'AGENTS.md' })
+ expect(optimizePasteHeader('prompt', copy)).toBe('Ask Codex in the current session')
+ expect(optimizePasteHeader('session-opener', copy)).toBe('One-time session opener (do NOT add to AGENTS.md)')
+ expect(sessionOpenerLabel(copy)).toContain('AGENTS.md')
+ expect(sessionOpenerLabel(copy)).not.toContain('CLAUDE.md')
+ })
+
+ it('does not invent a filename for providers CodeBurn has not named', () => {
+ const copy = optimizeRemediationCopy('cursor')
+ expect(copy.agent).toBe('Cursor')
+ expect(copy.instructionFile).toBe('project instructions')
+ expect(optimizePasteHeader('session-opener', copy)).toBe('One-time session opener (do NOT add to project instructions)')
+ })
+
+ it('scopes cross-provider detector labels, including JSON', () => {
+ const project = projectWithLowWorthSessions([
+ lowWorthSession(4, 0, { turns: [lowWorthTurn({ hasEdits: false })] }),
+ ])
+ const claude = detectLowWorthSessions([project])
+ const codex = detectLowWorthSessions([project], 'codex')
+ expect(claude!.fix.label).toContain('CLAUDE.md')
+ expect(codex!.fix.label).toContain('AGENTS.md')
+ expect(codex!.fix.label).not.toContain('CLAUDE.md')
+
+ const turns = Array.from({ length: 5 }, (_, i) => reliabilityTurn(i, {
+ retries: i < 3 ? 1 : 0,
+ call: { tools: ['Edit', 'Skill'], skills: ['reviewer'] },
+ }))
+ const retry = detectCapabilityReliability([projectWithReliabilityTurns(turns)], 'codex')
+ expect(retry!.fix.label).toBe('Ask Codex to audit the retry-heavy capability before changing config:')
+ expect(retry!.fix.label).not.toContain('Claude')
+
+ const json = buildOptimizeJsonReport(
+ [project],
+ 'lifetime',
+ { findings: [codex!], costRate: 0.00001, healthScore: 80, healthGrade: 'B', modelRecommendations: [] },
+ )
+ expect(json.findings[0]!.fix.label).toContain('AGENTS.md')
+ expect(json.findings[0]!.fix.label).not.toContain('CLAUDE.md')
+ })
+
+ it('renders destination headers from the selected provider, not the finding text', () => {
+ const out = strip(renderOptimize(
+ [promptFinding('Ask Claude to audit the retry-heavy capability before changing config:')],
+ 0.00001, 'lifetime', 10, 5, 100, 80, 'B', [], [],
+ undefined, undefined, undefined, [], 'codex',
+ ))
+ expect(out).toContain('Ask Codex in the current session')
+ expect(out).not.toContain('Ask Claude in the current session')
+
+ const opener = strip(renderOptimize(
+ [openerFinding('Paste at the start of your NEXT expensive thread (one-time, do not add to CLAUDE.md):')],
+ 0.00001, 'lifetime', 10, 5, 100, 80, 'B', [], [],
+ undefined, undefined, undefined, [], 'codex',
+ ))
+ expect(opener).toContain('do NOT add to AGENTS.md')
+ expect(opener).not.toContain('do NOT add to CLAUDE.md')
+ })
+})
From 8b79927d29d230a6da5efec3b3da6c32b2da9fce Mon Sep 17 00:00:00 2001
From: Aditya Vikram Singh <247195684+avs-io@users.noreply.github.com>
Date: Thu, 20 Aug 2026 00:37:41 +0530
Subject: [PATCH 2/3] fix(optimize): preserve Claude copy and use provider
display names
Extra High on #1049: keep the shipped Claude Code empty-state and
TUI header strings; resolve agent nouns from Provider.displayName;
cover all five cross-provider detector labels.
---
src/dashboard.tsx | 4 +-
src/optimize.ts | 63 ++++++++++++++---------
src/providers/index.ts | 23 +++++++++
tests/mcp-coverage.test.ts | 23 +++++++++
tests/optimize.test.ts | 79 +++++++++++++++++++++--------
tests/provider-display-name.test.ts | 12 +++++
6 files changed, 156 insertions(+), 48 deletions(-)
create mode 100644 tests/provider-display-name.test.ts
diff --git a/src/dashboard.tsx b/src/dashboard.tsx
index 2ea3c0f7..a804cc94 100644
--- a/src/dashboard.tsx
+++ b/src/dashboard.tsx
@@ -10,7 +10,7 @@ import { findUnpricedModels, isExpectedFreeModel, loadPricing } from './models.j
import { aggregateModelTotals } from './model-breakdown.js'
import { buildDurablePeriod } from './usage-aggregator.js'
import { getAllProviders } from './providers/index.js'
-import { classHeaderLine, classTotals, findingBasis, findingClass, optimizePasteHeader, optimizeRemediationCopy, scanAndDetect, type FindingClass, type WasteFinding, type WasteAction, type OptimizeResult } from './optimize.js'
+import { classHeaderLine, classTotals, findingBasis, findingClass, optimizeTuiPasteHeader, scanAndDetect, type FindingClass, type WasteFinding, type WasteAction, type OptimizeResult } from './optimize.js'
import { appliedFixGlyph, formatAppliedFix, type AppliedFix } from './act/types.js'
import { aggregateFileChurn, buildCoachingNotes, computePricingCoverage, medianTimeToFirstEditMs, scanUserCorrections, worstOneShotCategory, type ReworkedFile } from './workflow-insights.js'
import { estimateContextBudget, type ContextBudget } from './context-budget.js'
@@ -1040,7 +1040,7 @@ function actionDestinationHeader(action: WasteAction, provider?: string): string
case 'command':
return '── Run this command '.padEnd(64, '─')
case 'paste': {
- return `── ${optimizePasteHeader(action.destination, optimizeRemediationCopy(provider))} `.padEnd(64, '─')
+ return optimizeTuiPasteHeader(action.destination, provider)
}
}
}
diff --git a/src/optimize.ts b/src/optimize.ts
index c6d94a63..1d4a8f31 100644
--- a/src/optimize.ts
+++ b/src/optimize.ts
@@ -8,7 +8,7 @@ import { homedir } from 'os'
import { isReadShapedBashCommand } from './bash-utils.js'
import { readSessionLines, readSessionFileSync } from './fs-utils.js'
-import { discoverAllSessions } from './providers/index.js'
+import { discoverAllSessions, providerDisplayName } from './providers/index.js'
import { parseJsonlLine, shouldSkipLine } from './parser.js'
import type { DateRange, ProjectSummary, SessionSummary } from './types.js'
import { formatCost } from './currency.js'
@@ -260,36 +260,19 @@ export type OptimizeRemediationCopy = {
instructionFile: string
}
-const REMEDIATION_AGENT_NAMES: Record = {
- claude: 'Claude',
- codex: 'Codex',
- cursor: 'Cursor',
- copilot: 'Copilot',
- gemini: 'Gemini',
- opencode: 'OpenCode',
- antigravity: 'Antigravity',
- windsurf: 'Windsurf',
- cline: 'Cline',
- hermes: 'Hermes',
- kimi: 'Kimi',
- kimicode: 'Kimi Code',
- 'ibm-bob': 'IBM Bob',
- pi: 'Pi',
-}
-
const REMEDIATION_INSTRUCTION_FILES: Record = {
claude: 'CLAUDE.md',
codex: 'AGENTS.md',
}
-function humanizeProviderId(id: string): string {
- return id.split(/[-_]/).map(part => part ? part[0]!.toUpperCase() + part.slice(1) : part).join(' ')
+export function isDefaultClaudeProvider(provider?: string): boolean {
+ return !provider || provider === 'all' || provider === 'claude'
}
export function optimizeRemediationCopy(provider?: string): OptimizeRemediationCopy {
const key = !provider || provider === 'all' ? 'claude' : provider.toLowerCase()
return {
- agent: REMEDIATION_AGENT_NAMES[key] ?? humanizeProviderId(key),
+ agent: providerDisplayName(key),
instructionFile: REMEDIATION_INSTRUCTION_FILES[key] ?? 'project instructions',
}
}
@@ -313,6 +296,38 @@ export function optimizePasteHeader(destination: PasteDestination | undefined, c
}
}
+/// Exact pre-#1049 TUI strings for unset/`all`/`claude`. Non-Claude providers
+/// reuse the CLI header table so the two surfaces cannot drift on new copy.
+export function optimizeTuiPasteHeader(destination: PasteDestination | undefined, provider?: string): string {
+ if (isDefaultClaudeProvider(provider)) {
+ switch (destination) {
+ case 'claude-md': return '── Suggested CLAUDE.md addition (permanent rule) '.padEnd(64, '─')
+ case 'session-opener': return '── One-time session opener (do not add to CLAUDE.md) '.padEnd(64, '─')
+ case 'prompt': return '── Ask Claude in the current session '.padEnd(64, '─')
+ case 'shell-config': return '── Add to your shell config '.padEnd(64, '─')
+ case 'manual': return '── Manual action '.padEnd(64, '─')
+ default: return '── Suggested action '.padEnd(64, '─')
+ }
+ }
+ return `── ${optimizePasteHeader(destination, optimizeRemediationCopy(provider))} `.padEnd(64, '─')
+}
+
+export function optimizeEmptyScanLines(provider?: string): [string, string, string] {
+ if (isDefaultClaudeProvider(provider)) {
+ return [
+ 'CodeBurn optimize scans your Claude Code sessions and config for',
+ 'token waste: junk directory reads, duplicate file reads, unused',
+ 'agents/skills/MCP servers, bloated CLAUDE.md, and more.',
+ ]
+ }
+ const copy = optimizeRemediationCopy(provider)
+ return [
+ `CodeBurn optimize scans your ${copy.agent} sessions and config for`,
+ 'token waste: junk directory reads, duplicate file reads, unused',
+ `agents/skills/MCP servers, bloated ${copy.instructionFile}, and more.`,
+ ]
+}
+
export type WasteAction =
| { type: 'paste'; label: string; text: string; destination?: PasteDestination }
| { type: 'command'; label: string; text: string }
@@ -3826,9 +3841,9 @@ export function renderOptimize(
if (findings.length === 0) {
lines.push(chalk.hex(GREEN)(' Nothing to fix. Your setup is lean.'))
lines.push('')
- lines.push(chalk.dim(` CodeBurn optimize scans your ${copy.agent} sessions and config for`))
- lines.push(chalk.dim(' token waste: junk directory reads, duplicate file reads, unused'))
- lines.push(chalk.dim(` agents/skills/MCP servers, bloated ${copy.instructionFile}, and more.`))
+ for (const line of optimizeEmptyScanLines(provider)) {
+ lines.push(chalk.dim(` ${line}`))
+ }
lines.push('')
lines.push(...renderAppliedFixes(appliedFixes))
lines.push(...renderWorkflowSection(reworkedFiles, coachingNotes))
diff --git a/src/providers/index.ts b/src/providers/index.ts
index 70af252c..0eaadded 100644
--- a/src/providers/index.ts
+++ b/src/providers/index.ts
@@ -199,6 +199,29 @@ const coreProviders: Provider[] = [claude, cline, clineCli, codewhale, codebuff,
// when an optional module fails to load. Must stay in sync with getAllProviders.
const lazyProviderNames = ['antigravity', 'forge', 'goose', 'cursor', 'opencode', 'cursor-agent', 'crush', 'warp', 'vercel-gateway', 'zcode', 'zed']
+// Display names for lazy providers. Must match the `displayName` on the
+// loaded Provider object; `providerDisplayName` + getAllProviders() test
+// is the drift check.
+const lazyProviderDisplayNames: Record = {
+ antigravity: 'Antigravity',
+ forge: 'Forge',
+ goose: 'Goose',
+ cursor: 'Cursor',
+ opencode: 'OpenCode',
+ 'cursor-agent': 'Cursor Agent',
+ crush: 'Crush',
+ warp: 'Warp',
+ 'vercel-gateway': 'Vercel AI Gateway',
+ zcode: 'ZCode',
+ zed: 'Zed',
+}
+
+export function providerDisplayName(name: string): string {
+ const core = coreProviders.find(p => p.name === name)
+ if (core) return core.displayName
+ return lazyProviderDisplayNames[name] ?? name
+}
+
// Canonical set of every provider name (core + lazy), used to validate the
// --provider CLI flag. Computed lazily so importing this module never depends on
// every provider object being defined at load time (e.g. under test mocks).
diff --git a/tests/mcp-coverage.test.ts b/tests/mcp-coverage.test.ts
index e173f008..612d3c4f 100644
--- a/tests/mcp-coverage.test.ts
+++ b/tests/mcp-coverage.test.ts
@@ -769,6 +769,29 @@ describe('detectMcpProfileAdvisor', () => {
}
})
+ it('scopes the remediation label to --provider codex', () => {
+ const hotTurns = [makeTurn([
+ makeCall({ tools: ['mcp__github__t0'], cacheCreation: 10_000 }),
+ makeCall({ tools: ['mcp__github__t1'], cacheCreation: 10_000 }),
+ ])]
+ const coldTurns = [makeTurn([makeCall({ cacheCreation: 10_000 })])]
+ const projects = [
+ projectNamed('api', [
+ makeSession({ inventory: smallInventory, turns: hotTurns, mcpBreakdown: { github: { calls: 2 } } }),
+ ]),
+ projectNamed('web', [
+ makeSession({ inventory: smallInventory, turns: coldTurns, mcpBreakdown: { github: { calls: 0 } } }),
+ ]),
+ projectNamed('docs', [
+ makeSession({ inventory: smallInventory, turns: coldTurns, mcpBreakdown: { github: { calls: 0 } } }),
+ ]),
+ ]
+ const finding = detectMcpProfileAdvisor(projects, undefined, 'codex')
+ expect(finding!.fix.label).toBe('Ask Codex to turn this into a project-scoped MCP profile:')
+ expect(finding!.fix.label).not.toContain('Claude')
+ expect(finding!.fix.label).not.toContain('CLAUDE.md')
+ })
+
it('does not flag servers used evenly across loaded projects', () => {
const projects = ['api', 'web', 'docs'].map(name => projectNamed(name, [
makeSession({
diff --git a/tests/optimize.test.ts b/tests/optimize.test.ts
index 343abe9f..f42db53e 100644
--- a/tests/optimize.test.ts
+++ b/tests/optimize.test.ts
@@ -30,6 +30,8 @@ import {
findingBasis,
optimizeRemediationCopy,
optimizePasteHeader,
+ optimizeTuiPasteHeader,
+ optimizeEmptyScanLines,
sessionOpenerLabel,
askAgentLabel,
type FindingId,
@@ -1473,16 +1475,29 @@ describe('provider-scoped remediation copy (#1044)', () => {
}
}
- it('keeps Claude / CLAUDE.md for unset, all, and claude', () => {
+ it('keeps the exact shipped Claude / CLAUDE.md strings', () => {
for (const provider of [undefined, 'all', 'claude'] as const) {
const copy = optimizeRemediationCopy(provider)
expect(copy).toEqual({ agent: 'Claude', instructionFile: 'CLAUDE.md' })
expect(optimizePasteHeader('prompt', copy)).toBe('Ask Claude in the current session')
expect(optimizePasteHeader('session-opener', copy)).toBe('One-time session opener (do NOT add to CLAUDE.md)')
- expect(sessionOpenerLabel(copy)).toContain('CLAUDE.md')
+ expect(sessionOpenerLabel(copy)).toBe('Paste at the start of your NEXT expensive thread (one-time, do not add to CLAUDE.md):')
expect(askAgentLabel(copy, 'audit the retry-heavy capability before changing config'))
.toBe('Ask Claude to audit the retry-heavy capability before changing config:')
+ expect(optimizeEmptyScanLines(provider)).toEqual([
+ 'CodeBurn optimize scans your Claude Code sessions and config for',
+ 'token waste: junk directory reads, duplicate file reads, unused',
+ 'agents/skills/MCP servers, bloated CLAUDE.md, and more.',
+ ])
+ expect(optimizeTuiPasteHeader('session-opener', provider))
+ .toBe('── One-time session opener (do not add to CLAUDE.md) '.padEnd(64, '─'))
+ expect(optimizeTuiPasteHeader('prompt', provider))
+ .toBe('── Ask Claude in the current session '.padEnd(64, '─'))
}
+ const empty = strip(renderOptimize([], 0, 'lifetime', 0, 0, 0, 100, 'A', [], []))
+ expect(empty).toContain('CodeBurn optimize scans your Claude Code sessions and config for')
+ expect(empty).toContain('bloated CLAUDE.md')
+ expect(empty).not.toContain('Claude sessions')
})
it('uses Codex / AGENTS.md for --provider codex', () => {
@@ -1492,40 +1507,60 @@ describe('provider-scoped remediation copy (#1044)', () => {
expect(optimizePasteHeader('session-opener', copy)).toBe('One-time session opener (do NOT add to AGENTS.md)')
expect(sessionOpenerLabel(copy)).toContain('AGENTS.md')
expect(sessionOpenerLabel(copy)).not.toContain('CLAUDE.md')
+ expect(optimizeEmptyScanLines('codex')[0]).toBe('CodeBurn optimize scans your Codex sessions and config for')
+ expect(optimizeEmptyScanLines('codex')[2]).toContain('bloated AGENTS.md')
+ expect(optimizeTuiPasteHeader('prompt', 'codex')).toContain('Ask Codex in the current session')
+ expect(optimizeTuiPasteHeader('session-opener', 'codex')).toContain('do NOT add to AGENTS.md')
+ expect(optimizeTuiPasteHeader('session-opener', 'codex')).not.toContain('CLAUDE.md')
})
- it('does not invent a filename for providers CodeBurn has not named', () => {
- const copy = optimizeRemediationCopy('cursor')
- expect(copy.agent).toBe('Cursor')
- expect(copy.instructionFile).toBe('project instructions')
- expect(optimizePasteHeader('session-opener', copy)).toBe('One-time session opener (do NOT add to project instructions)')
+ it('uses the canonical Provider.displayName, not a title-cased id', () => {
+ expect(optimizeRemediationCopy('hermes').agent).toBe('Hermes Agent')
+ expect(optimizeRemediationCopy('cursor').agent).toBe('Cursor')
+ expect(optimizeRemediationCopy('cursor').instructionFile).toBe('project instructions')
+ expect(optimizePasteHeader('session-opener', optimizeRemediationCopy('cursor')))
+ .toBe('One-time session opener (do NOT add to project instructions)')
})
- it('scopes cross-provider detector labels, including JSON', () => {
- const project = projectWithLowWorthSessions([
- lowWorthSession(4, 0, { turns: [lowWorthTurn({ hasEdits: false })] }),
- ])
- const claude = detectLowWorthSessions([project])
- const codex = detectLowWorthSessions([project], 'codex')
- expect(claude!.fix.label).toContain('CLAUDE.md')
- expect(codex!.fix.label).toContain('AGENTS.md')
- expect(codex!.fix.label).not.toContain('CLAUDE.md')
-
+ it('scopes every cross-provider detector label, including JSON', () => {
+ const lowWorth = detectLowWorthSessions([
+ projectWithLowWorthSessions([lowWorthSession(4, 0, { turns: [lowWorthTurn({ hasEdits: false })] })]),
+ ], 'codex')
+ const context = detectContextBloat([
+ projectWithContextSessions([contextSession(0, {
+ totalInputTokens: 90_000,
+ totalCacheReadTokens: 30_000,
+ totalOutputTokens: 2_000,
+ })]),
+ ], undefined, 'codex')
+ const outliers = detectSessionOutliers([projectWithSessions([1, 1, 1, 10])], undefined, 'codex')
const turns = Array.from({ length: 5 }, (_, i) => reliabilityTurn(i, {
retries: i < 3 ? 1 : 0,
call: { tools: ['Edit', 'Skill'], skills: ['reviewer'] },
}))
const retry = detectCapabilityReliability([projectWithReliabilityTurns(turns)], 'codex')
+
+ const findings = [lowWorth, context, outliers, retry]
+ expect(findings.every(Boolean)).toBe(true)
+ for (const finding of findings) {
+ expect(finding!.fix.label).not.toContain('Claude')
+ expect(finding!.fix.label).not.toContain('CLAUDE.md')
+ }
+ expect(lowWorth!.fix.label).toContain('AGENTS.md')
+ expect(context!.fix.label).toContain('AGENTS.md')
+ expect(outliers!.fix.label).toContain('AGENTS.md')
expect(retry!.fix.label).toBe('Ask Codex to audit the retry-heavy capability before changing config:')
- expect(retry!.fix.label).not.toContain('Claude')
const json = buildOptimizeJsonReport(
- [project],
+ [projectWithSessions([1])],
'lifetime',
- { findings: [codex!], costRate: 0.00001, healthScore: 80, healthGrade: 'B', modelRecommendations: [] },
+ { findings: findings as WasteFinding[], costRate: 0.00001, healthScore: 80, healthGrade: 'B', modelRecommendations: [] },
)
- expect(json.findings[0]!.fix.label).toContain('AGENTS.md')
- expect(json.findings[0]!.fix.label).not.toContain('CLAUDE.md')
+ expect(json.findings).toHaveLength(4)
+ for (const row of json.findings) {
+ expect(row.fix.label).not.toContain('Claude')
+ expect(row.fix.label).not.toContain('CLAUDE.md')
+ }
})
it('renders destination headers from the selected provider, not the finding text', () => {
diff --git a/tests/provider-display-name.test.ts b/tests/provider-display-name.test.ts
new file mode 100644
index 00000000..3e0e016e
--- /dev/null
+++ b/tests/provider-display-name.test.ts
@@ -0,0 +1,12 @@
+import { describe, expect, it } from 'vitest'
+import { getAllProviders, providerDisplayName } from '../src/providers/index.js'
+
+describe('providerDisplayName', () => {
+ it('matches every loaded Provider.displayName', async () => {
+ const loaded = await getAllProviders()
+ expect(loaded.length).toBeGreaterThan(20)
+ for (const provider of loaded) {
+ expect(providerDisplayName(provider.name)).toBe(provider.displayName)
+ }
+ })
+})
From c12c1c6267bd2ab51e1fb13b190369348fb62d4f Mon Sep 17 00:00:00 2001
From: Aditya Vikram Singh <247195684+avs-io@users.noreply.github.com>
Date: Fri, 21 Aug 2026 15:58:05 +0530
Subject: [PATCH 3/3] fix(optimize): do not claim a session scan for non-Claude
providers
Maintainer review on #1049: empty-state copy under --provider codex
named detectors that scanSessions never ran. Say the session-scan
detectors do not cover that provider yet. Drop the dead TUI provider
threading; optimize view is still gated to all|claude.
---
src/dashboard.tsx | 33 +++++++++++++++++++++++----------
src/optimize.ts | 9 ++++++---
tests/optimize.test.ts | 6 ++++--
3 files changed, 33 insertions(+), 15 deletions(-)
diff --git a/src/dashboard.tsx b/src/dashboard.tsx
index a804cc94..7eeeaf35 100644
--- a/src/dashboard.tsx
+++ b/src/dashboard.tsx
@@ -10,7 +10,7 @@ import { findUnpricedModels, isExpectedFreeModel, loadPricing } from './models.j
import { aggregateModelTotals } from './model-breakdown.js'
import { buildDurablePeriod } from './usage-aggregator.js'
import { getAllProviders } from './providers/index.js'
-import { classHeaderLine, classTotals, findingBasis, findingClass, optimizeTuiPasteHeader, scanAndDetect, type FindingClass, type WasteFinding, type WasteAction, type OptimizeResult } from './optimize.js'
+import { classHeaderLine, classTotals, findingBasis, findingClass, scanAndDetect, type FindingClass, type WasteFinding, type WasteAction, type OptimizeResult } from './optimize.js'
import { appliedFixGlyph, formatAppliedFix, type AppliedFix } from './act/types.js'
import { aggregateFileChurn, buildCoachingNotes, computePricingCoverage, medianTimeToFirstEditMs, scanUserCorrections, worstOneShotCategory, type ReworkedFile } from './workflow-insights.js'
import { estimateContextBudget, type ContextBudget } from './context-budget.js'
@@ -1033,21 +1033,34 @@ function PeriodTabs({ active, providerName, showProvider }: { active: Period; pr
/// permanent CLAUDE.md rule from a one-time session opener so they don't
/// accidentally bake a single-run constraint into their project's permanent
/// instructions. Issue #277.
-function actionDestinationHeader(action: WasteAction, provider?: string): string {
+function actionDestinationHeader(action: WasteAction): string {
switch (action.type) {
case 'file-content':
return `── Suggested ${action.path} addition `.padEnd(64, '─')
case 'command':
return '── Run this command '.padEnd(64, '─')
case 'paste': {
- return optimizeTuiPasteHeader(action.destination, provider)
+ switch (action.destination) {
+ case 'claude-md':
+ return '── Suggested CLAUDE.md addition (permanent rule) '.padEnd(64, '─')
+ case 'session-opener':
+ return '── One-time session opener (do not add to CLAUDE.md) '.padEnd(64, '─')
+ case 'prompt':
+ return '── Ask Claude in the current session '.padEnd(64, '─')
+ case 'shell-config':
+ return '── Add to your shell config '.padEnd(64, '─')
+ case 'manual':
+ return '── Manual action '.padEnd(64, '─')
+ default:
+ return '── Suggested action '.padEnd(64, '─')
+ }
}
}
}
-function FindingAction({ action, provider }: { action: WasteAction; provider?: string }) {
+function FindingAction({ action }: { action: WasteAction }) {
const lines = action.type === 'file-content' ? action.content.split('\n') : action.type === 'command' ? action.text.split('\n') : [action.text]
- const header = actionDestinationHeader(action, provider)
+ const header = actionDestinationHeader(action)
return (
<>
{header}
@@ -1057,7 +1070,7 @@ function FindingAction({ action, provider }: { action: WasteAction; provider?: s
)
}
-function FindingPanel({ index, finding, costRate, width, provider }: { index: number; finding: WasteFinding; costRate: number; width: number; provider?: string }) {
+function FindingPanel({ index, finding, costRate, width }: { index: number; finding: WasteFinding; costRate: number; width: number }) {
const costSaved = finding.tokensSaved * costRate
const color = IMPACT_PANEL_COLORS[finding.impact] ?? DIM
const label = finding.impact.charAt(0).toUpperCase() + finding.impact.slice(1)
@@ -1073,7 +1086,7 @@ function FindingPanel({ index, finding, costRate, width, provider }: { index: nu
{finding.explanation}
Savings: ~{formatTokens(finding.tokensSaved)} tokens (~{formatCost(costSaved)}) {findingBasis(finding)}
-
+
)
}
@@ -1093,7 +1106,7 @@ const APPLIED_FIX_COLORS: Record = {
pending: DIM,
}
-function OptimizeView({ findings, costRate, projects, label, width, healthScore, healthGrade, cursor, appliedFixes = [], provider }: { findings: WasteFinding[]; costRate: number; projects: ProjectSummary[]; label: string; width: number; healthScore: number; healthGrade: string; cursor: number; appliedFixes?: AppliedFix[]; provider?: string }) {
+function OptimizeView({ findings, costRate, projects, label, width, healthScore, healthGrade, cursor, appliedFixes = [] }: { findings: WasteFinding[]; costRate: number; projects: ProjectSummary[]; label: string; width: number; healthScore: number; healthGrade: string; cursor: number; appliedFixes?: AppliedFix[] }) {
const periodCost = projects.reduce((s, p) => s + p.totalCostUSD, 0)
const totalTokens = findings.reduce((s, f) => s + f.tokensSaved, 0)
const totalCost = totalTokens * costRate
@@ -1127,7 +1140,7 @@ function OptimizeView({ findings, costRate, projects, label, width, healthScore,
return (
{cls !== previous && {classHeaderLine(cls, totals[cls], costRate)}}
-
+
)
})}
@@ -1653,7 +1666,7 @@ export function InteractiveDashboard({ initialProjects, initialDailyHistoryProje
{view === 'compare'
? setView('dashboard')} />
: view === 'optimize' && optimizeResult
- ?
+ ?
: }
{coachingNote && (
diff --git a/src/optimize.ts b/src/optimize.ts
index 1d4a8f31..134424c4 100644
--- a/src/optimize.ts
+++ b/src/optimize.ts
@@ -321,10 +321,13 @@ export function optimizeEmptyScanLines(provider?: string): [string, string, stri
]
}
const copy = optimizeRemediationCopy(provider)
+ // scanSessions is Claude-only. Naming the provider's instruction file here
+ // would claim a scan that did not run (Health A / 100 under --provider
+ // codex with every listed detector claudeOnly-disabled).
return [
- `CodeBurn optimize scans your ${copy.agent} sessions and config for`,
- 'token waste: junk directory reads, duplicate file reads, unused',
- `agents/skills/MCP servers, bloated ${copy.instructionFile}, and more.`,
+ `Session-scan detectors do not cover ${copy.agent} yet.`,
+ 'junk directory reads, duplicate file reads, unused agents/skills/MCP,',
+ 'and bloated instruction files currently scan Claude Code only.',
]
}
diff --git a/tests/optimize.test.ts b/tests/optimize.test.ts
index f42db53e..4c34cb74 100644
--- a/tests/optimize.test.ts
+++ b/tests/optimize.test.ts
@@ -1507,8 +1507,10 @@ describe('provider-scoped remediation copy (#1044)', () => {
expect(optimizePasteHeader('session-opener', copy)).toBe('One-time session opener (do NOT add to AGENTS.md)')
expect(sessionOpenerLabel(copy)).toContain('AGENTS.md')
expect(sessionOpenerLabel(copy)).not.toContain('CLAUDE.md')
- expect(optimizeEmptyScanLines('codex')[0]).toBe('CodeBurn optimize scans your Codex sessions and config for')
- expect(optimizeEmptyScanLines('codex')[2]).toContain('bloated AGENTS.md')
+ expect(optimizeEmptyScanLines('codex')[0]).toBe('Session-scan detectors do not cover Codex yet.')
+ expect(optimizeEmptyScanLines('codex')[2]).toContain('currently scan Claude Code only')
+ expect(optimizeEmptyScanLines('codex').join(' ')).not.toContain('scans your Codex')
+ expect(optimizeEmptyScanLines('codex').join(' ')).not.toContain('bloated AGENTS.md')
expect(optimizeTuiPasteHeader('prompt', 'codex')).toContain('Ask Codex in the current session')
expect(optimizeTuiPasteHeader('session-opener', 'codex')).toContain('do NOT add to AGENTS.md')
expect(optimizeTuiPasteHeader('session-opener', 'codex')).not.toContain('CLAUDE.md')