Merge pull request #1049 from avs-io/fix/optimize-provider-remediation-copy

fix(optimize): scope remediation copy to --provider
This commit is contained in:
Resham Joshi 2026-08-21 05:23:03 -07:00 committed by GitHub
commit f0c6e58008
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 307 additions and 31 deletions

View file

@ -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'
@ -249,6 +249,88 @@ 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_INSTRUCTION_FILES: Record<string, string> = {
claude: 'CLAUDE.md',
codex: 'AGENTS.md',
}
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: providerDisplayName(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'
}
}
/// 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)
// 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 [
`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.',
]
}
export type WasteAction =
| { type: 'paste'; label: string; text: string; destination?: PasteDestination }
| { type: 'command'; label: string; text: string }
@ -1695,6 +1777,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 +1816,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 +2009,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 +2072,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 +3204,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 +3239,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 +3314,7 @@ export function findContextBloatCandidates(projects: ProjectSummary[]): ContextB
return candidates
}
export function detectContextBloat(projects: ProjectSummary[], excludedSessionIds?: ReadonlySet<string>): WasteFinding | null {
export function detectContextBloat(projects: ProjectSummary[], excludedSessionIds?: ReadonlySet<string>, provider?: string): WasteFinding | null {
const candidates = findContextBloatCandidates(projects)
.filter(c => !excludedSessionIds?.has(c.sessionId))
if (candidates.length === 0) return null
@ -3272,13 +3355,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<string>): WasteFinding | null {
export function detectSessionOutliers(projects: ProjectSummary[], excludedSessionIds?: ReadonlySet<string>, provider?: string): WasteFinding | null {
type Outlier = {
project: string
sessionId: string
@ -3353,7 +3436,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 +3634,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 +3707,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 +3720,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 +3750,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 +3819,9 @@ export function renderOptimize(
previouslyApplied?: Record<string, string>,
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 +3844,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(' token waste: junk directory reads, duplicate file reads, unused'))
lines.push(chalk.dim(' agents/skills/MCP servers, bloated CLAUDE.md, and more.'))
for (const line of optimizeEmptyScanLines(provider)) {
lines.push(chalk.dim(` ${line}`))
}
lines.push('')
lines.push(...renderAppliedFixes(appliedFixes))
lines.push(...renderWorkflowSection(reworkedFiles, coachingNotes))
@ -3799,7 +3877,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 +3946,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)
}

View file

@ -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<string, string> = {
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).

View file

@ -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({

View file

@ -28,6 +28,12 @@ import {
buildOptimizeJsonReport,
renderOptimize,
findingBasis,
optimizeRemediationCopy,
optimizePasteHeader,
optimizeTuiPasteHeader,
optimizeEmptyScanLines,
sessionOpenerLabel,
askAgentLabel,
type FindingId,
type ToolCall,
type ApiCallMeta,
@ -1443,3 +1449,137 @@ 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 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)).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', () => {
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')
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')
})
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 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:')
const json = buildOptimizeJsonReport(
[projectWithSessions([1])],
'lifetime',
{ findings: findings as WasteFinding[], costRate: 0.00001, healthScore: 80, healthGrade: 'B', modelRecommendations: [] },
)
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', () => {
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')
})
})

View file

@ -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)
}
})
})