diff --git a/src/utils/jsonl-metadata.ts b/src/utils/jsonl-metadata.ts index c06b23ec..61899682 100644 --- a/src/utils/jsonl-metadata.ts +++ b/src/utils/jsonl-metadata.ts @@ -8,6 +8,8 @@ export type TranscriptThinkingEffort = 'low' | 'medium' | 'high' | 'max'; const MODEL_STDOUT_PREFIX = 'Set model to '; const MODEL_STDOUT_EFFORT_REGEX = /^Set model to[\s\S]*? with (low|medium|high|max) effort<\/local-command-stdout>$/i; +const EFFORT_STDOUT_PREFIX = 'Set effort level to '; +const EFFORT_STDOUT_REGEX = /^Set effort level to (low|medium|high|max)\b/i; interface TranscriptEntry { message?: { content?: string } } @@ -44,6 +46,14 @@ export function getTranscriptThinkingEffort(transcriptPath: string | undefined): } const visibleContent = getVisibleText(entry.message.content).trim(); + + if (visibleContent.startsWith(EFFORT_STDOUT_PREFIX)) { + const effortMatch = EFFORT_STDOUT_REGEX.exec(visibleContent); + if (effortMatch) { + return normalizeThinkingEffort(effortMatch[1]); + } + } + if (!visibleContent.startsWith(MODEL_STDOUT_PREFIX)) { continue; } diff --git a/src/widgets/__tests__/ThinkingEffort.test.ts b/src/widgets/__tests__/ThinkingEffort.test.ts index c233835f..59a8aaa7 100644 --- a/src/widgets/__tests__/ThinkingEffort.test.ts +++ b/src/widgets/__tests__/ThinkingEffort.test.ts @@ -27,6 +27,10 @@ const MODEL_WITH_HIGH_EFFORT = 'Set model to \u001b[1mopus const MODEL_WITH_LOW_EFFORT = 'Set model to \u001b[1msonnet (claude-sonnet-4-5)\u001b[22m with \u001b[1mlow\u001b[22m effort'; const MODEL_WITH_MAX_EFFORT = 'Set model to \u001b[1mopus (claude-opus-4-6)\u001b[22m with \u001b[1mmax\u001b[22m effort'; const MODEL_WITHOUT_EFFORT = 'Set model to \u001b[1msonnet (claude-sonnet-4-5)\u001b[22m'; +const EFFORT_HIGH = 'Set effort level to \u001b[1mhigh\u001b[22m: Comprehensive implementation with extensive testing and documentation'; +const EFFORT_LOW = 'Set effort level to \u001b[1mlow\u001b[22m: Quick, minimal-effort response'; +const EFFORT_MEDIUM = 'Set effort level to \u001b[1mmedium\u001b[22m: Balanced response with good coverage'; +const EFFORT_MAX = 'Set effort level to \u001b[1mmax\u001b[22m (this session only): Maximum capability with deepest reasoning (Opus 4.6 only)'; let tempDir: string; @@ -155,6 +159,61 @@ describe('ThinkingEffortWidget', () => { }); }); + describe('/effort command source', () => { + it('reads effort from /effort transcript stdout', () => { + const result = render({ fileContent: makeTranscriptEntry(EFFORT_HIGH) }); + expect(result).toBe('Thinking: high'); + }); + + it('supports low effort from /effort command', () => { + const result = render({ fileContent: makeTranscriptEntry(EFFORT_LOW) }); + expect(result).toBe('Thinking: low'); + }); + + it('supports medium effort from /effort command', () => { + const result = render({ fileContent: makeTranscriptEntry(EFFORT_MEDIUM) }); + expect(result).toBe('Thinking: medium'); + }); + + it('supports max effort from /effort command', () => { + const result = render({ fileContent: makeTranscriptEntry(EFFORT_MAX) }); + expect(result).toBe('Thinking: max'); + }); + + it('returns raw effort from /effort command', () => { + const result = render({ fileContent: makeTranscriptEntry(EFFORT_HIGH), rawValue: true }); + expect(result).toBe('high'); + }); + + it('/effort overrides earlier /model when it is newer', () => { + const result = render({ + fileContent: [ + makeTranscriptEntry(MODEL_WITH_LOW_EFFORT), + makeTranscriptEntry(EFFORT_MAX) + ].join('\n') + }); + expect(result).toBe('Thinking: max'); + }); + + it('/model overrides earlier /effort when it is newer', () => { + const result = render({ + fileContent: [ + makeTranscriptEntry(EFFORT_MAX), + makeTranscriptEntry(MODEL_WITH_LOW_EFFORT) + ].join('\n') + }); + expect(result).toBe('Thinking: low'); + }); + + it('/effort overrides settings fallback', () => { + const result = render({ + fileContent: makeTranscriptEntry(EFFORT_HIGH), + settingsValue: { effortLevel: 'low' } + }); + expect(result).toBe('Thinking: high'); + }); + }); + describe('Claude settings fallback', () => { it('falls back to effortLevel when the latest /model output has no effort', () => { const result = render({