wogiflow 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. package/.workflow/agents/reviewer.md +81 -0
  2. package/.workflow/agents/security.md +94 -0
  3. package/.workflow/agents/story-writer.md +58 -0
  4. package/.workflow/bridges/base-bridge.js +395 -0
  5. package/.workflow/bridges/claude-bridge.js +434 -0
  6. package/.workflow/bridges/index.js +130 -0
  7. package/.workflow/lib/assumption-detector.js +481 -0
  8. package/.workflow/lib/config-substitution.js +371 -0
  9. package/.workflow/lib/failure-categories.js +478 -0
  10. package/.workflow/state/app-map.md.template +15 -0
  11. package/.workflow/state/architecture.md.template +24 -0
  12. package/.workflow/state/component-index.json.template +5 -0
  13. package/.workflow/state/decisions.md.template +15 -0
  14. package/.workflow/state/feedback-patterns.md.template +9 -0
  15. package/.workflow/state/knowledge-sync.json.template +6 -0
  16. package/.workflow/state/progress.md.template +14 -0
  17. package/.workflow/state/ready.json.template +7 -0
  18. package/.workflow/state/request-log.md.template +14 -0
  19. package/.workflow/state/session-state.json.template +11 -0
  20. package/.workflow/state/stack.md.template +33 -0
  21. package/.workflow/state/testing.md.template +36 -0
  22. package/.workflow/templates/claude-md.hbs +257 -0
  23. package/.workflow/templates/correction-report.md +67 -0
  24. package/.workflow/templates/gemini-md.hbs +52 -0
  25. package/README.md +1802 -0
  26. package/bin/flow +205 -0
  27. package/lib/index.js +33 -0
  28. package/lib/installer.js +467 -0
  29. package/lib/release-channel.js +269 -0
  30. package/lib/skill-registry.js +526 -0
  31. package/lib/upgrader.js +401 -0
  32. package/lib/utils.js +305 -0
  33. package/package.json +64 -0
  34. package/scripts/flow +985 -0
  35. package/scripts/flow-adaptive-learning.js +1259 -0
  36. package/scripts/flow-aggregate.js +488 -0
  37. package/scripts/flow-archive +133 -0
  38. package/scripts/flow-auto-context.js +1015 -0
  39. package/scripts/flow-auto-learn.js +615 -0
  40. package/scripts/flow-bridge.js +223 -0
  41. package/scripts/flow-browser-suggest.js +316 -0
  42. package/scripts/flow-bug.js +247 -0
  43. package/scripts/flow-cascade.js +711 -0
  44. package/scripts/flow-changelog +85 -0
  45. package/scripts/flow-checkpoint.js +483 -0
  46. package/scripts/flow-cli.js +403 -0
  47. package/scripts/flow-code-intelligence.js +760 -0
  48. package/scripts/flow-complexity.js +502 -0
  49. package/scripts/flow-config-set.js +152 -0
  50. package/scripts/flow-constants.js +157 -0
  51. package/scripts/flow-context +152 -0
  52. package/scripts/flow-context-init.js +482 -0
  53. package/scripts/flow-context-monitor.js +384 -0
  54. package/scripts/flow-context-scoring.js +886 -0
  55. package/scripts/flow-correct.js +458 -0
  56. package/scripts/flow-damage-control.js +985 -0
  57. package/scripts/flow-deps +101 -0
  58. package/scripts/flow-diff.js +700 -0
  59. package/scripts/flow-done +151 -0
  60. package/scripts/flow-done.js +489 -0
  61. package/scripts/flow-durable-session.js +1541 -0
  62. package/scripts/flow-entropy-monitor.js +345 -0
  63. package/scripts/flow-export-profile +349 -0
  64. package/scripts/flow-export-scanner.js +1046 -0
  65. package/scripts/flow-figma-confirm.js +400 -0
  66. package/scripts/flow-figma-extract.js +496 -0
  67. package/scripts/flow-figma-generate.js +683 -0
  68. package/scripts/flow-figma-index.js +909 -0
  69. package/scripts/flow-figma-match.js +617 -0
  70. package/scripts/flow-figma-mcp-server.js +518 -0
  71. package/scripts/flow-figma-pipeline.js +414 -0
  72. package/scripts/flow-file-ops.js +301 -0
  73. package/scripts/flow-gate-confidence.js +825 -0
  74. package/scripts/flow-guided-edit.js +659 -0
  75. package/scripts/flow-health +185 -0
  76. package/scripts/flow-health.js +413 -0
  77. package/scripts/flow-hooks.js +556 -0
  78. package/scripts/flow-http-client.js +249 -0
  79. package/scripts/flow-hybrid-detect.js +167 -0
  80. package/scripts/flow-hybrid-interactive.js +591 -0
  81. package/scripts/flow-hybrid-test.js +152 -0
  82. package/scripts/flow-import-profile +439 -0
  83. package/scripts/flow-init +253 -0
  84. package/scripts/flow-instruction-richness.js +827 -0
  85. package/scripts/flow-jira-integration.js +579 -0
  86. package/scripts/flow-knowledge-router.js +522 -0
  87. package/scripts/flow-knowledge-sync.js +589 -0
  88. package/scripts/flow-linear-integration.js +631 -0
  89. package/scripts/flow-links.js +774 -0
  90. package/scripts/flow-log-manager.js +559 -0
  91. package/scripts/flow-loop-enforcer.js +1246 -0
  92. package/scripts/flow-loop-retry-learning.js +630 -0
  93. package/scripts/flow-lsp.js +923 -0
  94. package/scripts/flow-map-index +348 -0
  95. package/scripts/flow-map-sync +201 -0
  96. package/scripts/flow-memory-blocks.js +668 -0
  97. package/scripts/flow-memory-compactor.js +350 -0
  98. package/scripts/flow-memory-db.js +1110 -0
  99. package/scripts/flow-memory-sync.js +484 -0
  100. package/scripts/flow-metrics.js +353 -0
  101. package/scripts/flow-migrate-ids.js +370 -0
  102. package/scripts/flow-model-adapter.js +802 -0
  103. package/scripts/flow-model-router.js +884 -0
  104. package/scripts/flow-models.js +1231 -0
  105. package/scripts/flow-morning.js +517 -0
  106. package/scripts/flow-multi-approach.js +660 -0
  107. package/scripts/flow-new-feature +86 -0
  108. package/scripts/flow-onboard +1042 -0
  109. package/scripts/flow-orchestrate-llm.js +459 -0
  110. package/scripts/flow-orchestrate.js +3592 -0
  111. package/scripts/flow-output.js +123 -0
  112. package/scripts/flow-parallel-detector.js +399 -0
  113. package/scripts/flow-parallel-dispatch.js +987 -0
  114. package/scripts/flow-parallel.js +428 -0
  115. package/scripts/flow-pattern-enforcer.js +600 -0
  116. package/scripts/flow-prd-manager.js +282 -0
  117. package/scripts/flow-progress.js +323 -0
  118. package/scripts/flow-project-analyzer.js +975 -0
  119. package/scripts/flow-prompt-composer.js +487 -0
  120. package/scripts/flow-providers.js +1381 -0
  121. package/scripts/flow-queue.js +308 -0
  122. package/scripts/flow-ready +82 -0
  123. package/scripts/flow-ready.js +189 -0
  124. package/scripts/flow-regression.js +396 -0
  125. package/scripts/flow-response-parser.js +450 -0
  126. package/scripts/flow-resume.js +284 -0
  127. package/scripts/flow-rules-sync.js +439 -0
  128. package/scripts/flow-run-trace.js +718 -0
  129. package/scripts/flow-safety.js +587 -0
  130. package/scripts/flow-search +104 -0
  131. package/scripts/flow-security.js +481 -0
  132. package/scripts/flow-session-end +106 -0
  133. package/scripts/flow-session-end.js +437 -0
  134. package/scripts/flow-session-state.js +671 -0
  135. package/scripts/flow-setup-hooks +216 -0
  136. package/scripts/flow-setup-hooks.js +377 -0
  137. package/scripts/flow-skill-create.js +329 -0
  138. package/scripts/flow-skill-creator.js +572 -0
  139. package/scripts/flow-skill-generator.js +1046 -0
  140. package/scripts/flow-skill-learn.js +880 -0
  141. package/scripts/flow-skill-matcher.js +578 -0
  142. package/scripts/flow-spec-generator.js +820 -0
  143. package/scripts/flow-stack-wizard.js +895 -0
  144. package/scripts/flow-standup +162 -0
  145. package/scripts/flow-start +74 -0
  146. package/scripts/flow-start.js +235 -0
  147. package/scripts/flow-status +110 -0
  148. package/scripts/flow-status.js +301 -0
  149. package/scripts/flow-step-browser.js +83 -0
  150. package/scripts/flow-step-changelog.js +217 -0
  151. package/scripts/flow-step-comments.js +306 -0
  152. package/scripts/flow-step-complexity.js +234 -0
  153. package/scripts/flow-step-coverage.js +218 -0
  154. package/scripts/flow-step-knowledge.js +193 -0
  155. package/scripts/flow-step-pr-tests.js +364 -0
  156. package/scripts/flow-step-regression.js +89 -0
  157. package/scripts/flow-step-review.js +516 -0
  158. package/scripts/flow-step-security.js +162 -0
  159. package/scripts/flow-step-silent-failures.js +290 -0
  160. package/scripts/flow-step-simplifier.js +346 -0
  161. package/scripts/flow-story +105 -0
  162. package/scripts/flow-story.js +500 -0
  163. package/scripts/flow-suspend.js +252 -0
  164. package/scripts/flow-sync-daemon.js +654 -0
  165. package/scripts/flow-task-analyzer.js +606 -0
  166. package/scripts/flow-team-dashboard.js +748 -0
  167. package/scripts/flow-team-sync.js +752 -0
  168. package/scripts/flow-team.js +977 -0
  169. package/scripts/flow-tech-options.js +528 -0
  170. package/scripts/flow-templates.js +812 -0
  171. package/scripts/flow-tiered-learning.js +728 -0
  172. package/scripts/flow-trace +204 -0
  173. package/scripts/flow-transcript-chunking.js +1106 -0
  174. package/scripts/flow-transcript-digest.js +7918 -0
  175. package/scripts/flow-transcript-language.js +465 -0
  176. package/scripts/flow-transcript-parsing.js +1085 -0
  177. package/scripts/flow-transcript-stories.js +2194 -0
  178. package/scripts/flow-update-map +224 -0
  179. package/scripts/flow-utils.js +2242 -0
  180. package/scripts/flow-verification.js +644 -0
  181. package/scripts/flow-verify.js +1177 -0
  182. package/scripts/flow-voice-input.js +638 -0
  183. package/scripts/flow-watch +168 -0
  184. package/scripts/flow-workflow-steps.js +521 -0
  185. package/scripts/flow-workflow.js +1029 -0
  186. package/scripts/flow-worktree.js +489 -0
  187. package/scripts/hooks/adapters/base-adapter.js +102 -0
  188. package/scripts/hooks/adapters/claude-code.js +359 -0
  189. package/scripts/hooks/adapters/index.js +79 -0
  190. package/scripts/hooks/core/component-check.js +341 -0
  191. package/scripts/hooks/core/index.js +35 -0
  192. package/scripts/hooks/core/loop-check.js +241 -0
  193. package/scripts/hooks/core/session-context.js +294 -0
  194. package/scripts/hooks/core/task-gate.js +177 -0
  195. package/scripts/hooks/core/validation.js +230 -0
  196. package/scripts/hooks/entry/claude-code/post-tool-use.js +65 -0
  197. package/scripts/hooks/entry/claude-code/pre-tool-use.js +89 -0
  198. package/scripts/hooks/entry/claude-code/session-end.js +87 -0
  199. package/scripts/hooks/entry/claude-code/session-start.js +46 -0
  200. package/scripts/hooks/entry/claude-code/stop.js +43 -0
  201. package/scripts/postinstall.js +139 -0
  202. package/templates/browser-test-flow.json +56 -0
  203. package/templates/bug-report.md +43 -0
  204. package/templates/component-detail.md +42 -0
  205. package/templates/component.stories.tsx +49 -0
  206. package/templates/context/constraints.md +83 -0
  207. package/templates/context/conventions.md +177 -0
  208. package/templates/context/stack.md +60 -0
  209. package/templates/correction-report.md +90 -0
  210. package/templates/feature-proposal.md +35 -0
  211. package/templates/hybrid/_base.md +254 -0
  212. package/templates/hybrid/_patterns.md +45 -0
  213. package/templates/hybrid/create-component.md +127 -0
  214. package/templates/hybrid/create-file.md +56 -0
  215. package/templates/hybrid/create-hook.md +145 -0
  216. package/templates/hybrid/create-service.md +70 -0
  217. package/templates/hybrid/fix-bug.md +33 -0
  218. package/templates/hybrid/modify-file.md +55 -0
  219. package/templates/story.md +68 -0
  220. package/templates/task.json +56 -0
  221. package/templates/trace.md +69 -0
@@ -0,0 +1,450 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Flow - Response Parser
5
+ *
6
+ * Cleans LLM responses to extract usable content.
7
+ * Handles common artifacts that cause "failures" which are actually fixable:
8
+ * - Thinking tags (<thinking>...</thinking>)
9
+ * - Markdown fences around code
10
+ * - Preambles before code ("Here's the code:", "Sure, I'll...", etc.)
11
+ * - Claude artifacts like <reflection> tags
12
+ *
13
+ * Usage:
14
+ * const { parseResponse, cleanCodeBlock } = require('./flow-response-parser');
15
+ * const cleaned = parseResponse(llmResponse);
16
+ *
17
+ * Part of v1.8.0 - Council Review Fixes
18
+ */
19
+
20
+ // ============================================================
21
+ // Core Parser Functions
22
+ // ============================================================
23
+
24
+ /**
25
+ * Parse and clean an LLM response
26
+ * @param {string} response - Raw LLM response
27
+ * @param {Object} options - Parsing options
28
+ * @returns {Object} - { content, artifacts, metadata }
29
+ */
30
+ function parseResponse(response, options = {}) {
31
+ if (!response || typeof response !== 'string') {
32
+ return { content: '', artifacts: [], metadata: {} };
33
+ }
34
+
35
+ const {
36
+ stripThinking = true,
37
+ stripReflection = true,
38
+ extractCode = false,
39
+ removePreamble = true,
40
+ preserveMarkdown = false
41
+ } = options;
42
+
43
+ let content = response;
44
+ const artifacts = [];
45
+ const metadata = {};
46
+
47
+ // 1. Strip thinking tags
48
+ if (stripThinking) {
49
+ const thinkingMatch = content.match(/<thinking>([\s\S]*?)<\/thinking>/gi);
50
+ if (thinkingMatch) {
51
+ artifacts.push({ type: 'thinking', count: thinkingMatch.length });
52
+ content = content.replace(/<thinking>[\s\S]*?<\/thinking>/gi, '');
53
+ }
54
+ }
55
+
56
+ // 2. Strip reflection tags
57
+ if (stripReflection) {
58
+ const reflectionMatch = content.match(/<reflection>([\s\S]*?)<\/reflection>/gi);
59
+ if (reflectionMatch) {
60
+ artifacts.push({ type: 'reflection', count: reflectionMatch.length });
61
+ content = content.replace(/<reflection>[\s\S]*?<\/reflection>/gi, '');
62
+ }
63
+ }
64
+
65
+ // 3. Strip artifact tags
66
+ const artifactMatch = content.match(/<artifact[^>]*>([\s\S]*?)<\/artifact>/gi);
67
+ if (artifactMatch) {
68
+ artifacts.push({ type: 'artifact', count: artifactMatch.length });
69
+ // Extract content from within artifacts
70
+ const artifactContent = artifactMatch.map(a => {
71
+ const inner = a.match(/<artifact[^>]*>([\s\S]*?)<\/artifact>/i);
72
+ return inner ? inner[1].trim() : '';
73
+ }).join('\n\n');
74
+ content = artifactContent || content.replace(/<artifact[^>]*>[\s\S]*?<\/artifact>/gi, '');
75
+ }
76
+
77
+ // 4. Extract code from markdown fences if requested
78
+ if (extractCode) {
79
+ const codeBlocks = extractCodeBlocks(content);
80
+ if (codeBlocks.length > 0) {
81
+ metadata.codeBlocks = codeBlocks.length;
82
+ metadata.languages = [...new Set(codeBlocks.map(b => b.language).filter(Boolean))];
83
+ // Return just the code if only one block
84
+ if (codeBlocks.length === 1) {
85
+ content = codeBlocks[0].code;
86
+ } else {
87
+ // Multiple blocks - join with separators
88
+ content = codeBlocks.map(b => b.code).join('\n\n');
89
+ }
90
+ }
91
+ } else if (!preserveMarkdown) {
92
+ // Just clean up the fences but keep structure
93
+ content = cleanMarkdownFences(content);
94
+ }
95
+
96
+ // 5. Remove preambles
97
+ if (removePreamble) {
98
+ content = removePreambles(content);
99
+ }
100
+
101
+ // 6. Clean up whitespace
102
+ content = content.trim();
103
+ content = content.replace(/\n{3,}/g, '\n\n'); // Max 2 consecutive newlines
104
+
105
+ return {
106
+ content,
107
+ artifacts,
108
+ metadata,
109
+ wasModified: content !== response.trim()
110
+ };
111
+ }
112
+
113
+ /**
114
+ * Extract code blocks from markdown
115
+ * @param {string} content - Content with potential code blocks
116
+ * @returns {Array} - Array of { language, code, raw }
117
+ */
118
+ function extractCodeBlocks(content) {
119
+ const blocks = [];
120
+ const codeBlockRegex = /```(\w*)\n([\s\S]*?)```/g;
121
+ let match;
122
+
123
+ while ((match = codeBlockRegex.exec(content)) !== null) {
124
+ blocks.push({
125
+ language: match[1] || null,
126
+ code: match[2].trim(),
127
+ raw: match[0]
128
+ });
129
+ }
130
+
131
+ return blocks;
132
+ }
133
+
134
+ /**
135
+ * Clean markdown fences while preserving code
136
+ * @param {string} content - Content with code fences
137
+ * @returns {string} - Content with cleaned fences
138
+ */
139
+ function cleanMarkdownFences(content) {
140
+ // Keep the fence structure but clean up
141
+ return content
142
+ // Remove empty code blocks
143
+ .replace(/```\w*\n\s*```/g, '')
144
+ // Normalize code block spacing
145
+ .replace(/```(\w*)\n\n+/g, '```$1\n')
146
+ .replace(/\n\n+```/g, '\n```');
147
+ }
148
+
149
+ /**
150
+ * Remove common LLM preambles
151
+ * @param {string} content - Content with potential preambles
152
+ * @returns {string} - Content without preambles
153
+ */
154
+ function removePreambles(content) {
155
+ // Common preamble patterns
156
+ const preamblePatterns = [
157
+ // Opening statements
158
+ /^(?:Here(?:'s| is) (?:the |your |a )?(?:code|implementation|solution|function|class|file)[^:]*?:?\s*\n+)/i,
159
+ /^(?:Sure[,!]?\s*(?:I(?:'ll| will| can)|let me)[^.]*?\.\s*\n+)/i,
160
+ /^(?:I(?:'ll| will| can)[^.]*?\.\s*\n+)/i,
161
+ /^(?:Let me[^.]*?\.\s*\n+)/i,
162
+ /^(?:Certainly[,!]?\s*(?:Here|I)[^.]*?\.\s*\n+)/i,
163
+ /^(?:Of course[,!]?\s*[^.]*?\.\s*\n+)/i,
164
+ /^(?:Absolutely[,!]?\s*[^.]*?\.\s*\n+)/i,
165
+
166
+ // Acknowledgments
167
+ /^(?:I understand[^.]*?\.\s*\n+)/i,
168
+ /^(?:Got it[,!]?\s*[^.]*?\.\s*\n+)/i,
169
+ /^(?:Understood[,!]?\s*[^.]*?\.\s*\n+)/i,
170
+
171
+ // Technical context
172
+ /^(?:Based on[^,]*?,\s*(?:here|I)[^.]*?\.\s*\n+)/i,
173
+ /^(?:Looking at[^,]*?,\s*[^.]*?\.\s*\n+)/i,
174
+ /^(?:After (?:reviewing|analyzing)[^,]*?,\s*[^.]*?\.\s*\n+)/i
175
+ ];
176
+
177
+ let result = content;
178
+
179
+ for (const pattern of preamblePatterns) {
180
+ result = result.replace(pattern, '');
181
+ }
182
+
183
+ return result;
184
+ }
185
+
186
+ /**
187
+ * Clean a single code block response
188
+ * Specialized for when you expect just code
189
+ * @param {string} response - LLM response that should be code
190
+ * @param {string} expectedLanguage - Expected language (optional)
191
+ * @returns {string} - Clean code
192
+ */
193
+ function cleanCodeBlock(response, expectedLanguage = null) {
194
+ if (!response) return '';
195
+
196
+ let content = response;
197
+
198
+ // Strip thinking/reflection first
199
+ content = content.replace(/<thinking>[\s\S]*?<\/thinking>/gi, '');
200
+ content = content.replace(/<reflection>[\s\S]*?<\/reflection>/gi, '');
201
+
202
+ // Extract from markdown fence
203
+ const fenceMatch = content.match(/```(?:\w*)\n([\s\S]*?)```/);
204
+ if (fenceMatch) {
205
+ content = fenceMatch[1];
206
+ }
207
+
208
+ // Remove preambles
209
+ content = removePreambles(content);
210
+
211
+ // Clean trailing explanations (after code)
212
+ const explanationPatterns = [
213
+ /\n+(?:This (?:code|function|implementation)[^.]*?\.|Note that[^.]*?\.|The above[^.]*?\.)[\s\S]*$/i,
214
+ /\n+(?:Key (?:changes|features|points)[^:]*?:[\s\S]*?)$/i,
215
+ /\n+(?:Explanation[^:]*?:[\s\S]*?)$/i
216
+ ];
217
+
218
+ for (const pattern of explanationPatterns) {
219
+ content = content.replace(pattern, '');
220
+ }
221
+
222
+ return content.trim();
223
+ }
224
+
225
+ /**
226
+ * Detect if response needs parsing
227
+ * Quick check to avoid unnecessary processing
228
+ * @param {string} response - Raw response
229
+ * @returns {boolean} - True if parsing would modify the response
230
+ */
231
+ function needsParsing(response) {
232
+ if (!response) return false;
233
+
234
+ // Check for artifacts that would be stripped
235
+ if (/<thinking>|<reflection>|<artifact/i.test(response)) {
236
+ return true;
237
+ }
238
+
239
+ // Check for code fences
240
+ if (/```\w*\n/.test(response)) {
241
+ return true;
242
+ }
243
+
244
+ // Check for common preambles
245
+ if (/^(?:Here's|Sure|I'll|Let me|Certainly)/i.test(response)) {
246
+ return true;
247
+ }
248
+
249
+ return false;
250
+ }
251
+
252
+ /**
253
+ * Parse response on error retry (conservative mode)
254
+ * Only parses if there was an error and parsing might help
255
+ * @param {string} response - Raw response
256
+ * @param {Error} error - The error that occurred
257
+ * @returns {Object} - { content, shouldRetry }
258
+ */
259
+ function parseOnRetry(response, error) {
260
+ // Check if this is a parse-fixable error
261
+ const parseFixableErrors = [
262
+ 'SyntaxError',
263
+ 'Unexpected token',
264
+ 'Invalid JSON',
265
+ 'Parse error'
266
+ ];
267
+
268
+ const isParseFixable = parseFixableErrors.some(e =>
269
+ error?.message?.includes(e) || error?.name === e
270
+ );
271
+
272
+ if (!isParseFixable && !needsParsing(response)) {
273
+ return { content: response, shouldRetry: false };
274
+ }
275
+
276
+ const parsed = parseResponse(response, { extractCode: true });
277
+
278
+ return {
279
+ content: parsed.content,
280
+ shouldRetry: parsed.wasModified,
281
+ artifacts: parsed.artifacts
282
+ };
283
+ }
284
+
285
+ // ============================================================
286
+ // Specialized Parsers
287
+ // ============================================================
288
+
289
+ /**
290
+ * Parse JSON from LLM response
291
+ * @param {string} response - Response that should contain JSON
292
+ * @returns {Object|null} - Parsed JSON or null
293
+ */
294
+ function parseJsonResponse(response) {
295
+ const cleaned = parseResponse(response, {
296
+ extractCode: true,
297
+ stripThinking: true,
298
+ stripReflection: true,
299
+ removePreamble: true
300
+ });
301
+
302
+ let content = cleaned.content;
303
+
304
+ // Try direct parse first
305
+ try {
306
+ return JSON.parse(content);
307
+ } catch {}
308
+
309
+ // Extract JSON from markdown fence
310
+ const jsonMatch = content.match(/```(?:json)?\n([\s\S]*?)```/);
311
+ if (jsonMatch) {
312
+ try {
313
+ return JSON.parse(jsonMatch[1].trim());
314
+ } catch {}
315
+ }
316
+
317
+ // Look for JSON object pattern - find balanced braces
318
+ const objectStart = content.indexOf('{');
319
+ if (objectStart !== -1) {
320
+ const jsonStr = extractBalancedJson(content, objectStart, '{', '}');
321
+ if (jsonStr) {
322
+ try {
323
+ return JSON.parse(jsonStr);
324
+ } catch {}
325
+ }
326
+ }
327
+
328
+ // Look for JSON array pattern - find balanced brackets
329
+ const arrayStart = content.indexOf('[');
330
+ if (arrayStart !== -1) {
331
+ const jsonStr = extractBalancedJson(content, arrayStart, '[', ']');
332
+ if (jsonStr) {
333
+ try {
334
+ return JSON.parse(jsonStr);
335
+ } catch {}
336
+ }
337
+ }
338
+
339
+ return null;
340
+ }
341
+
342
+ /**
343
+ * Extract a balanced JSON structure starting at the given index
344
+ * Handles nested structures correctly (won't match across unrelated objects)
345
+ */
346
+ function extractBalancedJson(content, startIdx, openChar, closeChar) {
347
+ if (content[startIdx] !== openChar) return null;
348
+
349
+ let depth = 0;
350
+ let inString = false;
351
+ let escape = false;
352
+
353
+ for (let i = startIdx; i < content.length; i++) {
354
+ const char = content[i];
355
+
356
+ if (escape) {
357
+ escape = false;
358
+ continue;
359
+ }
360
+
361
+ if (char === '\\' && inString) {
362
+ escape = true;
363
+ continue;
364
+ }
365
+
366
+ if (char === '"' && !escape) {
367
+ inString = !inString;
368
+ continue;
369
+ }
370
+
371
+ if (!inString) {
372
+ if (char === openChar) {
373
+ depth++;
374
+ } else if (char === closeChar) {
375
+ depth--;
376
+ if (depth === 0) {
377
+ return content.substring(startIdx, i + 1);
378
+ }
379
+ }
380
+ }
381
+ }
382
+
383
+ return null; // Unbalanced
384
+ }
385
+
386
+ /**
387
+ * Parse file content from LLM response
388
+ * For when LLM generates file content
389
+ * @param {string} response - Response containing file content
390
+ * @param {string} filename - Expected filename (for language detection)
391
+ * @returns {string} - Clean file content
392
+ */
393
+ function parseFileContent(response, filename = '') {
394
+ // Detect language from filename
395
+ const extMatch = filename.match(/\.(\w+)$/);
396
+ const ext = extMatch ? extMatch[1] : '';
397
+
398
+ const cleaned = parseResponse(response, {
399
+ extractCode: true,
400
+ stripThinking: true,
401
+ stripReflection: true,
402
+ removePreamble: true
403
+ });
404
+
405
+ let content = cleaned.content;
406
+
407
+ // If we got code blocks, use the one matching the expected language
408
+ if (cleaned.metadata.codeBlocks > 1) {
409
+ const blocks = extractCodeBlocks(response);
410
+ const languageMap = {
411
+ js: ['javascript', 'js'],
412
+ ts: ['typescript', 'ts'],
413
+ tsx: ['tsx', 'typescript'],
414
+ jsx: ['jsx', 'javascript'],
415
+ py: ['python', 'py'],
416
+ rs: ['rust', 'rs'],
417
+ go: ['go', 'golang']
418
+ };
419
+
420
+ const expectedLangs = languageMap[ext] || [ext];
421
+ const matchingBlock = blocks.find(b =>
422
+ b.language && expectedLangs.includes(b.language.toLowerCase())
423
+ );
424
+
425
+ if (matchingBlock) {
426
+ content = matchingBlock.code;
427
+ }
428
+ }
429
+
430
+ return content;
431
+ }
432
+
433
+ // ============================================================
434
+ // Exports
435
+ // ============================================================
436
+
437
+ module.exports = {
438
+ // Core functions
439
+ parseResponse,
440
+ extractCodeBlocks,
441
+ cleanMarkdownFences,
442
+ removePreambles,
443
+ cleanCodeBlock,
444
+ needsParsing,
445
+ parseOnRetry,
446
+
447
+ // Specialized parsers
448
+ parseJsonResponse,
449
+ parseFileContent
450
+ };