@@ -136,21 +136,25 @@ def call_claude_api(api_key: str, change_summary: str, ts_context: Dict[str, str
1361363. Type exports in models.ts for new models
1371374. Cache implementations if model structures changed
138138
139- Return JSON with this structure:
139+ Return ONLY a valid JSON object with this exact structure:
140140{{
141141 "summary": "Brief description of what changed and why updates are needed",
142142 "files": {{
143143 "relative/path.ts": {{
144144 "action": "update",
145145 "changes": "Description of specific changes made",
146- "content": "Complete updated file content"
146+ "content": "Complete updated file content as a single string "
147147 }}
148148 }},
149149 "breaking_changes": ["list any breaking changes"],
150150 "notes": "Additional notes for human reviewer"
151151}}
152152
153- Only include files that actually need changes. Keep existing patterns and style."""
153+ IMPORTANT:
154+ - Return ONLY the JSON object, no markdown formatting
155+ - The "content" field must be a single escaped string, not template literals
156+ - Use \\ n for newlines in the content field
157+ - Only include files that actually need changes"""
154158
155159 try :
156160 with httpx .Client (timeout = 60.0 ) as client :
@@ -175,25 +179,47 @@ def call_claude_api(api_key: str, change_summary: str, ts_context: Dict[str, str
175179 result = response .json ()
176180 ai_response = result ["content" ][0 ]["text" ]
177181
178- # Extract JSON from response
182+ # Extract JSON from response - handle markdown code blocks
179183 import re
180- json_match = re .search (r'\{.*\}' , ai_response , re .DOTALL )
181- if json_match :
184+
185+ # First try to find JSON in markdown code blocks
186+ code_block_match = re .search (r'```(?:json)?\s*(\{.*?\})\s*```' , ai_response , re .DOTALL )
187+ if code_block_match :
188+ json_content = code_block_match .group (1 )
189+ else :
190+ # Fallback to finding raw JSON
191+ json_match = re .search (r'\{.*\}' , ai_response , re .DOTALL )
192+ if json_match :
193+ json_content = json_match .group ()
194+ else :
195+ print ("❌ No JSON found in AI response" )
196+ print (f"Raw response: { ai_response [:500 ]} " )
197+ return {}
198+
199+ try :
200+ ai_json = json .loads (json_content )
201+ print (f"✅ AI generation successful" )
202+ print (f"Summary: { ai_json .get ('summary' , 'No summary' )} " )
203+ print (f"Files to update: { len (ai_json .get ('files' , {}))} " )
204+ return ai_json
205+
206+ except json .JSONDecodeError as e :
207+ print (f"❌ JSON parse error: { e } " )
208+ print (f"Trying to clean JSON content..." )
209+
210+ # Try to clean up common JSON issues
182211 try :
183- ai_json = json .loads (json_match .group ())
184- print (f"✅ AI generation successful" )
212+ # Remove any trailing commas and fix common issues
213+ cleaned = re .sub (r',\s*}' , '}' , json_content )
214+ cleaned = re .sub (r',\s*]' , ']' , cleaned )
215+ ai_json = json .loads (cleaned )
216+ print (f"✅ AI generation successful (after cleanup)" )
185217 print (f"Summary: { ai_json .get ('summary' , 'No summary' )} " )
186218 print (f"Files to update: { len (ai_json .get ('files' , {}))} " )
187219 return ai_json
188-
189- except json .JSONDecodeError as e :
190- print (f"❌ JSON parse error: { e } " )
191- print (f"Raw response: { ai_response [:500 ]} " )
220+ except json .JSONDecodeError :
221+ print (f"Raw response: { ai_response [:1000 ]} " )
192222 return {}
193- else :
194- print ("❌ No JSON found in AI response" )
195- print (f"Raw response: { ai_response [:500 ]} " )
196- return {}
197223 else :
198224 print (f"❌ Claude API error: { response .status_code } " )
199225 print (response .text [:500 ])
0 commit comments