11import type { AgentAdapter , AgentResult } from "../core/agent-adapter" ;
22import type { ResolvedProjectConfig } from "../core/types" ;
33import { getClaudeBinaryPath } from "../utils/claude-path" ;
4+ import { logger , normalizeError } from "../utils/logger" ;
45import { assertCommandOk , runCommand } from "../utils/shell" ;
56
67export class ClaudeCodeAdapter implements AgentAdapter {
@@ -14,16 +15,44 @@ export class ClaudeCodeAdapter implements AgentAdapter {
1415 return this . runClaude ( prompt ) ;
1516 }
1617
17- async resume ( _sessionId : string , prompt : string ) : Promise < AgentResult > {
18- return this . runClaudeContinue ( prompt ) ;
18+ async resume ( sessionId : string , prompt : string ) : Promise < AgentResult > {
19+ return this . runClaudeResume ( sessionId , prompt ) ;
1920 }
2021
2122 async runReview ( prompt : string ) : Promise < AgentResult > {
2223 return this . runClaude ( prompt ) ;
2324 }
2425
26+ private buildModelArgs ( ) : string [ ] {
27+ const model = this . config . agent ?. model ;
28+ if ( ! model ) return [ ] ;
29+ return [ "--model" , model ] ;
30+ }
31+
32+ private buildMaxTurnsArgs ( ) : string [ ] {
33+ const maxTurns = this . config . agent ?. maxTurns ;
34+ if ( ! maxTurns || maxTurns <= 0 ) return [ ] ;
35+ return [ "--max-turns" , String ( maxTurns ) ] ;
36+ }
37+
38+ private buildAllowedToolsArgs ( ) : string [ ] {
39+ const tools = this . config . agent ?. allowedTools ;
40+ if ( ! tools || tools . length === 0 ) return [ ] ;
41+ return [ "--allowedTools" , ...tools ] ;
42+ }
43+
44+ private buildCommonArgs ( ) : string [ ] {
45+ return [
46+ "--output-format" ,
47+ "json" ,
48+ ...this . buildModelArgs ( ) ,
49+ ...this . buildMaxTurnsArgs ( ) ,
50+ ...this . buildAllowedToolsArgs ( ) ,
51+ ] ;
52+ }
53+
2554 private async runClaude ( prompt : string ) : Promise < AgentResult > {
26- const args = [ "-p" , prompt , "--output-format" , "json" ] ;
55+ const args = [ "-p" , prompt , ... this . buildCommonArgs ( ) ] ;
2756
2857 const result = await runCommand ( this . claudePath , args , {
2958 cwd : this . config . executionPath ,
@@ -32,7 +61,10 @@ export class ClaudeCodeAdapter implements AgentAdapter {
3261 stdinMode : "ignore" ,
3362 } ) ;
3463
35- assertCommandOk ( this . claudePath , args , result ) ;
64+ if ( result . code !== 0 ) {
65+ throw mapClaudeError ( this . claudePath , args , result ) ;
66+ }
67+
3668 const finalMessage = extractFinalMessage ( result . stdout ) ;
3769 const sessionId = extractSessionId ( result . stdout ) ;
3870 const usage = extractUsage ( result . stdout ) ;
@@ -45,8 +77,22 @@ export class ClaudeCodeAdapter implements AgentAdapter {
4577 } ;
4678 }
4779
48- private async runClaudeContinue ( prompt : string ) : Promise < AgentResult > {
49- const args = [ "--continue" , prompt , "--output-format" , "json" ] ;
80+ private async runClaudeResume (
81+ sessionId : string ,
82+ prompt : string ,
83+ ) : Promise < AgentResult > {
84+ const args = [
85+ "--resume" ,
86+ sessionId ,
87+ "-p" ,
88+ prompt ,
89+ ...this . buildCommonArgs ( ) ,
90+ ] ;
91+
92+ logger . debug (
93+ { sessionId, claudePath : this . claudePath } ,
94+ "Resuming Claude Code session" ,
95+ ) ;
5096
5197 const result = await runCommand ( this . claudePath , args , {
5298 cwd : this . config . executionPath ,
@@ -55,13 +101,16 @@ export class ClaudeCodeAdapter implements AgentAdapter {
55101 stdinMode : "ignore" ,
56102 } ) ;
57103
58- assertCommandOk ( this . claudePath , args , result ) ;
104+ if ( result . code !== 0 ) {
105+ throw mapClaudeError ( this . claudePath , args , result ) ;
106+ }
107+
59108 const finalMessage = extractFinalMessage ( result . stdout ) ;
60- const sessionId = extractSessionId ( result . stdout ) ;
109+ const resumedSessionId = extractSessionId ( result . stdout ) ?? sessionId ;
61110 const usage = extractUsage ( result . stdout ) ;
62111
63112 return {
64- sessionId,
113+ sessionId : resumedSessionId ,
65114 finalMessage,
66115 stdout : result . stdout ,
67116 usage,
@@ -126,3 +175,42 @@ export function extractUsage(
126175 } catch { }
127176 return undefined ;
128177}
178+
179+ function mapClaudeError (
180+ command : string ,
181+ args : string [ ] ,
182+ result : { code : number ; stdout : string ; stderr : string } ,
183+ ) : Error {
184+ const output = result . stderr || result . stdout ;
185+ const base = `${ command } ${ args . join ( " " ) } failed with exit code ${ result . code } ` ;
186+
187+ if ( output . includes ( "rate limit" ) || output . includes ( "429" ) ) {
188+ return new Error (
189+ `${ base } \nClaude API rate limit hit. Wait a moment and retry, or set CLAUDE_CODE_MODEL to a model with higher limits.` ,
190+ ) ;
191+ }
192+
193+ if (
194+ output . includes ( "authentication" ) ||
195+ output . includes ( "API key" ) ||
196+ output . includes ( "ANTHROPIC_API_KEY" )
197+ ) {
198+ return new Error (
199+ `${ base } \nClaude Code authentication failed. Run 'claude' interactively once to log in, or set ANTHROPIC_API_KEY in your environment.` ,
200+ ) ;
201+ }
202+
203+ if ( output . includes ( "model" ) && output . includes ( "not found" ) ) {
204+ return new Error (
205+ `${ base } \nThe specified model was not found. Check CLAUDE_CODE_MODEL in your .env file.` ,
206+ ) ;
207+ }
208+
209+ if ( result . code === 127 ) {
210+ return new Error (
211+ `${ base } \nClaude Code binary not found. Install with: npm install -g @anthropic-ai/claude-code` ,
212+ ) ;
213+ }
214+
215+ return new Error ( `${ base } \n${ output } ` ) ;
216+ }
0 commit comments