@@ -69,6 +69,77 @@ function shouldRetry(status) {
6969 return status === 408 || status === 429 || status >= 500
7070}
7171
72+ function tryParseJson ( text ) {
73+ try {
74+ return JSON . parse ( text )
75+ } catch {
76+ return null
77+ }
78+ }
79+
80+ function extractLastJsonObject ( text ) {
81+ const cleaned = text
82+ . trim ( )
83+ . replace ( / ^ ` ` ` (?: j s o n ) ? \s * / i, "" )
84+ . replace ( / \s * ` ` ` $ / , "" )
85+
86+ const direct = tryParseJson ( cleaned )
87+ if ( direct && typeof direct === "object" ) {
88+ return direct
89+ }
90+
91+ let start = - 1
92+ let depth = 0
93+ let inString = false
94+ let escaping = false
95+ const candidates = [ ]
96+
97+ for ( let i = 0 ; i < cleaned . length ; i += 1 ) {
98+ const ch = cleaned [ i ]
99+
100+ if ( escaping ) {
101+ escaping = false
102+ continue
103+ }
104+
105+ if ( ch === "\\" ) {
106+ escaping = true
107+ continue
108+ }
109+
110+ if ( ch === '"' ) {
111+ inString = ! inString
112+ continue
113+ }
114+
115+ if ( inString ) continue
116+
117+ if ( ch === "{" ) {
118+ if ( depth === 0 ) start = i
119+ depth += 1
120+ continue
121+ }
122+
123+ if ( ch === "}" ) {
124+ if ( depth === 0 ) continue
125+ depth -= 1
126+ if ( depth === 0 && start >= 0 ) {
127+ candidates . push ( cleaned . slice ( start , i + 1 ) )
128+ start = - 1
129+ }
130+ }
131+ }
132+
133+ for ( let i = candidates . length - 1 ; i >= 0 ; i -= 1 ) {
134+ const parsed = tryParseJson ( candidates [ i ] )
135+ if ( parsed && typeof parsed === "object" && typeof parsed . patch === "string" ) {
136+ return parsed
137+ }
138+ }
139+
140+ return null
141+ }
142+
72143async function requestClaude ( ) {
73144 const endpoint = `${ anthropicBaseUrl . replace ( / \/ $ / , "" ) } /v1/messages`
74145
@@ -154,15 +225,8 @@ if (!outputText) {
154225 process . exit ( 1 )
155226}
156227
157- const normalized = outputText
158- . trim ( )
159- . replace ( / ^ ` ` ` (?: j s o n ) ? \s * / i, "" )
160- . replace ( / \s * ` ` ` $ / , "" )
161-
162- let parsed
163- try {
164- parsed = JSON . parse ( normalized )
165- } catch {
228+ const parsed = extractLastJsonObject ( outputText )
229+ if ( ! parsed ) {
166230 console . error ( "Model output is not valid JSON" )
167231 console . error ( outputText )
168232 process . exit ( 1 )
0 commit comments