sofia-cli 0.1.1

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 (435) hide show
  1. package/.github/agents/copilot-instructions.md +39 -0
  2. package/.github/agents/speckit.analyze.agent.md +184 -0
  3. package/.github/agents/speckit.checklist.agent.md +294 -0
  4. package/.github/agents/speckit.clarify.agent.md +181 -0
  5. package/.github/agents/speckit.constitution.agent.md +84 -0
  6. package/.github/agents/speckit.implement.agent.md +135 -0
  7. package/.github/agents/speckit.plan.agent.md +90 -0
  8. package/.github/agents/speckit.specify.agent.md +258 -0
  9. package/.github/agents/speckit.tasks.agent.md +137 -0
  10. package/.github/agents/speckit.taskstoissues.agent.md +30 -0
  11. package/.github/copilot-instructions.md +257 -0
  12. package/.github/prompts/speckit.analyze.prompt.md +3 -0
  13. package/.github/prompts/speckit.checklist.prompt.md +3 -0
  14. package/.github/prompts/speckit.clarify.prompt.md +3 -0
  15. package/.github/prompts/speckit.constitution.prompt.md +3 -0
  16. package/.github/prompts/speckit.implement.prompt.md +3 -0
  17. package/.github/prompts/speckit.plan.prompt.md +3 -0
  18. package/.github/prompts/speckit.specify.prompt.md +3 -0
  19. package/.github/prompts/speckit.tasks.prompt.md +3 -0
  20. package/.github/prompts/speckit.taskstoissues.prompt.md +3 -0
  21. package/.github/workflows/ci.yml +38 -0
  22. package/.prettierrc +6 -0
  23. package/.specify/memory/constitution.md +181 -0
  24. package/.specify/scripts/bash/check-prerequisites.sh +166 -0
  25. package/.specify/scripts/bash/common.sh +156 -0
  26. package/.specify/scripts/bash/create-new-feature.sh +297 -0
  27. package/.specify/scripts/bash/setup-plan.sh +61 -0
  28. package/.specify/scripts/bash/update-agent-context.sh +810 -0
  29. package/.specify/templates/agent-file-template.md +28 -0
  30. package/.specify/templates/checklist-template.md +40 -0
  31. package/.specify/templates/constitution-template.md +50 -0
  32. package/.specify/templates/plan-template.md +113 -0
  33. package/.specify/templates/spec-template.md +115 -0
  34. package/.specify/templates/tasks-template.md +251 -0
  35. package/.vscode/mcp.json +42 -0
  36. package/.vscode/settings.json +19 -0
  37. package/CODE_OF_CONDUCT.md +128 -0
  38. package/LICENSE +21 -0
  39. package/README.md +213 -0
  40. package/dist/src/cli/developCommand.js +240 -0
  41. package/dist/src/cli/directCommands.js +143 -0
  42. package/dist/src/cli/envLoader.js +16 -0
  43. package/dist/src/cli/exportCommand.js +53 -0
  44. package/dist/src/cli/index.js +203 -0
  45. package/dist/src/cli/ioContext.js +109 -0
  46. package/dist/src/cli/preflight.js +57 -0
  47. package/dist/src/cli/statusCommand.js +110 -0
  48. package/dist/src/cli/workshopCommand.js +400 -0
  49. package/dist/src/develop/checkpointState.js +86 -0
  50. package/dist/src/develop/codeGenerator.js +319 -0
  51. package/dist/src/develop/dynamicScaffolder.js +226 -0
  52. package/dist/src/develop/githubMcpAdapter.js +122 -0
  53. package/dist/src/develop/index.js +15 -0
  54. package/dist/src/develop/mcpContextEnricher.js +195 -0
  55. package/dist/src/develop/pocScaffolder.js +542 -0
  56. package/dist/src/develop/ralphLoop.js +659 -0
  57. package/dist/src/develop/templateRegistry.js +364 -0
  58. package/dist/src/develop/testRunner.js +202 -0
  59. package/dist/src/logging/logger.js +58 -0
  60. package/dist/src/loop/conversationLoop.js +227 -0
  61. package/dist/src/loop/phaseSummarizer.js +87 -0
  62. package/dist/src/mcp/mcpManager.js +267 -0
  63. package/dist/src/mcp/mcpTransport.js +391 -0
  64. package/dist/src/mcp/retryPolicy.js +47 -0
  65. package/dist/src/mcp/webSearch.js +254 -0
  66. package/dist/src/phases/contextSummarizer.js +101 -0
  67. package/dist/src/phases/discoveryEnricher.js +156 -0
  68. package/dist/src/phases/phaseExtractors.js +222 -0
  69. package/dist/src/phases/phaseHandlers.js +328 -0
  70. package/dist/src/prompts/design.md +51 -0
  71. package/dist/src/prompts/develop-boundary.md +51 -0
  72. package/dist/src/prompts/develop.md +111 -0
  73. package/dist/src/prompts/discover.md +58 -0
  74. package/dist/src/prompts/ideate.md +56 -0
  75. package/dist/src/prompts/plan.md +51 -0
  76. package/dist/src/prompts/promptLoader.js +167 -0
  77. package/dist/src/prompts/promptLoader.ts +198 -0
  78. package/dist/src/prompts/select.md +47 -0
  79. package/dist/src/prompts/summarize/README.md +8 -0
  80. package/dist/src/prompts/summarize/design-summary.md +37 -0
  81. package/dist/src/prompts/summarize/develop-summary.md +25 -0
  82. package/dist/src/prompts/summarize/ideate-summary.md +27 -0
  83. package/dist/src/prompts/summarize/plan-summary.md +27 -0
  84. package/dist/src/prompts/summarize/select-summary.md +21 -0
  85. package/dist/src/prompts/system.md +28 -0
  86. package/dist/src/sessions/exportPaths.js +22 -0
  87. package/dist/src/sessions/exportWriter.js +406 -0
  88. package/dist/src/sessions/sessionManager.js +81 -0
  89. package/dist/src/sessions/sessionStore.js +65 -0
  90. package/dist/src/shared/activitySpinner.js +91 -0
  91. package/dist/src/shared/copilotClient.js +129 -0
  92. package/dist/src/shared/data/cards.json +1249 -0
  93. package/dist/src/shared/data/cardsLoader.js +51 -0
  94. package/dist/src/shared/errorClassifier.js +120 -0
  95. package/dist/src/shared/events.js +28 -0
  96. package/dist/src/shared/markdownRenderer.js +34 -0
  97. package/dist/src/shared/schemas/session.js +265 -0
  98. package/dist/src/shared/tableRenderer.js +20 -0
  99. package/dist/src/vendor/chalk.js +2 -0
  100. package/dist/src/vendor/cli-table3.js +3 -0
  101. package/dist/src/vendor/commander.js +2 -0
  102. package/dist/src/vendor/marked-terminal.js +3 -0
  103. package/dist/src/vendor/marked.js +2 -0
  104. package/dist/src/vendor/ora.js +2 -0
  105. package/dist/src/vendor/pino.js +2 -0
  106. package/dist/src/vendor/zod.js +2 -0
  107. package/dist/tests/e2e/developE2e.spec.js +126 -0
  108. package/dist/tests/e2e/developFailureE2e.spec.js +247 -0
  109. package/dist/tests/e2e/developPty.spec.js +75 -0
  110. package/dist/tests/e2e/discoveryWebSearchRelevance.spec.js +84 -0
  111. package/dist/tests/e2e/harness.spec.js +83 -0
  112. package/dist/tests/e2e/mcpLive.spec.js +120 -0
  113. package/dist/tests/e2e/newSession.e2e.spec.js +177 -0
  114. package/dist/tests/e2e/ralphLoopEnrichmentComparison.spec.js +62 -0
  115. package/dist/tests/e2e/workiqEnrichment.spec.js +56 -0
  116. package/dist/tests/e2e/zavaSimulation.spec.js +452 -0
  117. package/dist/tests/fixtures/test-fixture-project/src/add.js +3 -0
  118. package/dist/tests/fixtures/test-fixture-project/tests/failing.test.js +6 -0
  119. package/dist/tests/fixtures/test-fixture-project/tests/hanging.test.js +8 -0
  120. package/dist/tests/fixtures/test-fixture-project/tests/passing.test.js +10 -0
  121. package/dist/tests/fixtures/test-fixture-project/vitest.config.js +6 -0
  122. package/dist/tests/integration/autoStartConversation.spec.js +138 -0
  123. package/dist/tests/integration/defaultCommand.spec.js +147 -0
  124. package/dist/tests/integration/directCommandNonTty.spec.js +224 -0
  125. package/dist/tests/integration/directCommandTty.spec.js +151 -0
  126. package/dist/tests/integration/discoveryEnrichmentFlow.spec.js +175 -0
  127. package/dist/tests/integration/exportArtifacts.spec.js +202 -0
  128. package/dist/tests/integration/exportFallbackFlow.spec.js +99 -0
  129. package/dist/tests/integration/mcpDegradationFlow.spec.js +190 -0
  130. package/dist/tests/integration/mcpTransportFlow.spec.js +139 -0
  131. package/dist/tests/integration/newSessionFlow.spec.js +343 -0
  132. package/dist/tests/integration/pocGithubMcp.spec.js +186 -0
  133. package/dist/tests/integration/pocLocalFallback.spec.js +171 -0
  134. package/dist/tests/integration/pocScaffold.spec.js +163 -0
  135. package/dist/tests/integration/ralphLoopFlow.spec.js +359 -0
  136. package/dist/tests/integration/ralphLoopPartial.spec.js +368 -0
  137. package/dist/tests/integration/resumeAndBacktrack.spec.js +247 -0
  138. package/dist/tests/integration/spinnerLifecycle.spec.js +220 -0
  139. package/dist/tests/integration/summarizationFlow.spec.js +115 -0
  140. package/dist/tests/integration/testRunnerReal.spec.js +52 -0
  141. package/dist/tests/integration/webSearchAgent.spec.js +128 -0
  142. package/dist/tests/live/copilotSdkLive.spec.js +107 -0
  143. package/dist/tests/live/zavaFullWorkshop.spec.js +392 -0
  144. package/dist/tests/setup/loadEnv.js +3 -0
  145. package/dist/tests/unit/cli/developCommand.spec.js +567 -0
  146. package/dist/tests/unit/cli/directCommands.spec.js +279 -0
  147. package/dist/tests/unit/cli/envLoader.spec.js +58 -0
  148. package/dist/tests/unit/cli/ioContext.spec.js +119 -0
  149. package/dist/tests/unit/cli/preflight.spec.js +108 -0
  150. package/dist/tests/unit/cli/statusCommand.spec.js +111 -0
  151. package/dist/tests/unit/cli/workshopClientFallback.spec.js +80 -0
  152. package/dist/tests/unit/cli/workshopCommand.spec.js +329 -0
  153. package/dist/tests/unit/config/vitestEnvSetup.spec.js +13 -0
  154. package/dist/tests/unit/develop/checkpointState.spec.js +315 -0
  155. package/dist/tests/unit/develop/codeGenerator.spec.js +355 -0
  156. package/dist/tests/unit/develop/githubMcpAdapter.spec.js +231 -0
  157. package/dist/tests/unit/develop/mcpContextEnricher.spec.js +433 -0
  158. package/dist/tests/unit/develop/outputValidator.spec.js +119 -0
  159. package/dist/tests/unit/develop/pocScaffolder.spec.js +353 -0
  160. package/dist/tests/unit/develop/ralphLoop.spec.js +1248 -0
  161. package/dist/tests/unit/develop/templateRegistry.spec.js +85 -0
  162. package/dist/tests/unit/develop/testRunner.spec.js +249 -0
  163. package/dist/tests/unit/infraBicep.spec.js +92 -0
  164. package/dist/tests/unit/infraDeploy.spec.js +82 -0
  165. package/dist/tests/unit/infraTeardown.spec.js +63 -0
  166. package/dist/tests/unit/logging/logger.spec.js +43 -0
  167. package/dist/tests/unit/loop/conversationLoop.spec.js +592 -0
  168. package/dist/tests/unit/loop/phaseSummarizer.spec.js +141 -0
  169. package/dist/tests/unit/loop/streamingMarkdown.spec.js +147 -0
  170. package/dist/tests/unit/mcp/mcpManager.spec.js +279 -0
  171. package/dist/tests/unit/mcp/mcpTransport.spec.js +529 -0
  172. package/dist/tests/unit/mcp/retryPolicy.spec.js +218 -0
  173. package/dist/tests/unit/mcp/timeoutValidation.spec.js +46 -0
  174. package/dist/tests/unit/mcp/webSearch.spec.js +567 -0
  175. package/dist/tests/unit/phases/contextSummarizer.spec.js +140 -0
  176. package/dist/tests/unit/phases/discoveryEnricher.repeatCalls.spec.js +93 -0
  177. package/dist/tests/unit/phases/discoveryEnricher.spec.js +411 -0
  178. package/dist/tests/unit/phases/phaseExtractors.spec.js +352 -0
  179. package/dist/tests/unit/phases/phaseHandlers.spec.js +425 -0
  180. package/dist/tests/unit/prompts/promptLoader.spec.js +118 -0
  181. package/dist/tests/unit/schemas/pocSchemas.spec.js +412 -0
  182. package/dist/tests/unit/schemas/session.spec.js +257 -0
  183. package/dist/tests/unit/sessions/exportPaths.spec.js +31 -0
  184. package/dist/tests/unit/sessions/exportWriter.spec.js +655 -0
  185. package/dist/tests/unit/sessions/sessionManager.spec.js +151 -0
  186. package/dist/tests/unit/sessions/sessionStore.spec.js +116 -0
  187. package/dist/tests/unit/shared/activitySpinner.spec.js +175 -0
  188. package/dist/tests/unit/shared/cardsLoader.spec.js +76 -0
  189. package/dist/tests/unit/shared/copilotClient.spec.js +155 -0
  190. package/dist/tests/unit/shared/errorClassifier.spec.js +131 -0
  191. package/dist/tests/unit/shared/events.spec.js +55 -0
  192. package/dist/tests/unit/shared/markdownRenderer.spec.js +35 -0
  193. package/dist/tests/unit/shared/markdownRendererChunks.spec.js +70 -0
  194. package/dist/tests/unit/shared/tableRenderer.spec.js +34 -0
  195. package/dist/vitest.config.js +14 -0
  196. package/dist/vitest.live.config.js +18 -0
  197. package/docs/README.md +35 -0
  198. package/docs/architecture.md +169 -0
  199. package/docs/cli-usage.md +207 -0
  200. package/docs/environment.md +66 -0
  201. package/docs/export-format.md +146 -0
  202. package/docs/session-model.md +113 -0
  203. package/eslint.config.js +35 -0
  204. package/infra/deploy.sh +193 -0
  205. package/infra/gather-env.sh +211 -0
  206. package/infra/main.bicep +90 -0
  207. package/infra/main.bicepparam +18 -0
  208. package/infra/resources.bicep +134 -0
  209. package/infra/teardown.sh +114 -0
  210. package/package.json +63 -0
  211. package/specs/001-cli-workshop-rebuild/checklists/requirements.md +35 -0
  212. package/specs/001-cli-workshop-rebuild/contracts/cli.md +59 -0
  213. package/specs/001-cli-workshop-rebuild/contracts/export-summary-json.md +23 -0
  214. package/specs/001-cli-workshop-rebuild/contracts/session-json.md +30 -0
  215. package/specs/001-cli-workshop-rebuild/data-model.md +210 -0
  216. package/specs/001-cli-workshop-rebuild/plan.md +361 -0
  217. package/specs/001-cli-workshop-rebuild/quickstart.md +83 -0
  218. package/specs/001-cli-workshop-rebuild/research.md +116 -0
  219. package/specs/001-cli-workshop-rebuild/spec.md +240 -0
  220. package/specs/001-cli-workshop-rebuild/tasks.md +476 -0
  221. package/specs/002-poc-generation/contracts/poc-output.md +172 -0
  222. package/specs/002-poc-generation/contracts/ralph-loop.md +113 -0
  223. package/specs/002-poc-generation/data-model.md +172 -0
  224. package/specs/002-poc-generation/plan.md +109 -0
  225. package/specs/002-poc-generation/quickstart.md +97 -0
  226. package/specs/002-poc-generation/research.md +786 -0
  227. package/specs/002-poc-generation/spec.md +81 -0
  228. package/specs/002-poc-generation/tasks-fix.md +198 -0
  229. package/specs/002-poc-generation/tasks.md +252 -0
  230. package/specs/003-mcp-transport-integration/checklists/requirements.md +37 -0
  231. package/specs/003-mcp-transport-integration/contracts/context-enricher.md +220 -0
  232. package/specs/003-mcp-transport-integration/contracts/discovery-enricher.md +267 -0
  233. package/specs/003-mcp-transport-integration/contracts/github-adapter.md +149 -0
  234. package/specs/003-mcp-transport-integration/contracts/mcp-transport.md +288 -0
  235. package/specs/003-mcp-transport-integration/data-model.md +326 -0
  236. package/specs/003-mcp-transport-integration/plan.md +114 -0
  237. package/specs/003-mcp-transport-integration/quickstart.md +311 -0
  238. package/specs/003-mcp-transport-integration/research.md +395 -0
  239. package/specs/003-mcp-transport-integration/spec.md +234 -0
  240. package/specs/003-mcp-transport-integration/tasks.md +324 -0
  241. package/specs/003-next-spec-gaps.md +150 -0
  242. package/specs/004-dev-resume-hardening/checklists/requirements.md +37 -0
  243. package/specs/004-dev-resume-hardening/contracts/cli.md +160 -0
  244. package/specs/004-dev-resume-hardening/data-model.md +321 -0
  245. package/specs/004-dev-resume-hardening/plan.md +107 -0
  246. package/specs/004-dev-resume-hardening/quickstart.md +115 -0
  247. package/specs/004-dev-resume-hardening/research.md +142 -0
  248. package/specs/004-dev-resume-hardening/spec.md +221 -0
  249. package/specs/004-dev-resume-hardening/tasks.md +333 -0
  250. package/specs/005-ai-search-deploy/checklists/requirements.md +39 -0
  251. package/specs/005-ai-search-deploy/contracts/web-search-tool.md +241 -0
  252. package/specs/005-ai-search-deploy/data-model.md +130 -0
  253. package/specs/005-ai-search-deploy/plan.md +93 -0
  254. package/specs/005-ai-search-deploy/quickstart.md +96 -0
  255. package/specs/005-ai-search-deploy/research.md +187 -0
  256. package/specs/005-ai-search-deploy/spec.md +143 -0
  257. package/specs/005-ai-search-deploy/tasks.md +284 -0
  258. package/specs/006-workshop-extraction-fixes/checklists/requirements.md +61 -0
  259. package/specs/006-workshop-extraction-fixes/contracts/summarization-and-export.md +131 -0
  260. package/specs/006-workshop-extraction-fixes/data-model.md +149 -0
  261. package/specs/006-workshop-extraction-fixes/plan.md +123 -0
  262. package/specs/006-workshop-extraction-fixes/quickstart.md +101 -0
  263. package/specs/006-workshop-extraction-fixes/research.md +143 -0
  264. package/specs/006-workshop-extraction-fixes/spec.md +210 -0
  265. package/specs/006-workshop-extraction-fixes/tasks.md +316 -0
  266. package/src/cli/developCommand.ts +308 -0
  267. package/src/cli/directCommands.ts +195 -0
  268. package/src/cli/envLoader.ts +17 -0
  269. package/src/cli/exportCommand.ts +65 -0
  270. package/src/cli/index.ts +249 -0
  271. package/src/cli/ioContext.ts +139 -0
  272. package/src/cli/preflight.ts +86 -0
  273. package/src/cli/statusCommand.ts +118 -0
  274. package/src/cli/workshopCommand.ts +496 -0
  275. package/src/develop/checkpointState.ts +121 -0
  276. package/src/develop/codeGenerator.ts +402 -0
  277. package/src/develop/dynamicScaffolder.ts +284 -0
  278. package/src/develop/githubMcpAdapter.ts +199 -0
  279. package/src/develop/index.ts +34 -0
  280. package/src/develop/mcpContextEnricher.ts +279 -0
  281. package/src/develop/pocScaffolder.ts +646 -0
  282. package/src/develop/ralphLoop.ts +1044 -0
  283. package/src/develop/templateRegistry.ts +427 -0
  284. package/src/develop/testRunner.ts +276 -0
  285. package/src/logging/logger.ts +73 -0
  286. package/src/loop/conversationLoop.ts +355 -0
  287. package/src/loop/phaseSummarizer.ts +114 -0
  288. package/src/mcp/mcpManager.ts +365 -0
  289. package/src/mcp/mcpTransport.ts +562 -0
  290. package/src/mcp/retryPolicy.ts +87 -0
  291. package/src/mcp/webSearch.ts +388 -0
  292. package/src/originalPrompts/design_thinking.md +178 -0
  293. package/src/originalPrompts/design_thinking_persona.md +76 -0
  294. package/src/originalPrompts/document_generator_example.md +77 -0
  295. package/src/originalPrompts/document_generator_persona.md +47 -0
  296. package/src/originalPrompts/facilitator_persona.md +125 -0
  297. package/src/originalPrompts/guardrails.md +47 -0
  298. package/src/phases/contextSummarizer.ts +154 -0
  299. package/src/phases/discoveryEnricher.ts +223 -0
  300. package/src/phases/phaseExtractors.ts +247 -0
  301. package/src/phases/phaseHandlers.ts +450 -0
  302. package/src/prompts/design.md +51 -0
  303. package/src/prompts/develop-boundary.md +51 -0
  304. package/src/prompts/develop.md +111 -0
  305. package/src/prompts/discover.md +58 -0
  306. package/src/prompts/ideate.md +56 -0
  307. package/src/prompts/plan.md +51 -0
  308. package/src/prompts/promptLoader.ts +198 -0
  309. package/src/prompts/select.md +47 -0
  310. package/src/prompts/summarize/README.md +8 -0
  311. package/src/prompts/summarize/design-summary.md +37 -0
  312. package/src/prompts/summarize/develop-summary.md +25 -0
  313. package/src/prompts/summarize/ideate-summary.md +27 -0
  314. package/src/prompts/summarize/plan-summary.md +27 -0
  315. package/src/prompts/summarize/select-summary.md +21 -0
  316. package/src/prompts/system.md +28 -0
  317. package/src/sessions/exportPaths.ts +28 -0
  318. package/src/sessions/exportWriter.ts +490 -0
  319. package/src/sessions/sessionManager.ts +119 -0
  320. package/src/sessions/sessionStore.ts +69 -0
  321. package/src/shared/activitySpinner.ts +108 -0
  322. package/src/shared/copilotClient.ts +291 -0
  323. package/src/shared/data/cards.json +1249 -0
  324. package/src/shared/data/cardsLoader.ts +70 -0
  325. package/src/shared/errorClassifier.ts +160 -0
  326. package/src/shared/events.ts +103 -0
  327. package/src/shared/markdownRenderer.ts +44 -0
  328. package/src/shared/schemas/session.ts +346 -0
  329. package/src/shared/tableRenderer.ts +28 -0
  330. package/src/types/marked-terminal.d.ts +5 -0
  331. package/src/vendor/chalk.ts +2 -0
  332. package/src/vendor/cli-table3.ts +3 -0
  333. package/src/vendor/commander.ts +2 -0
  334. package/src/vendor/marked-terminal.ts +3 -0
  335. package/src/vendor/marked.ts +2 -0
  336. package/src/vendor/ora.ts +2 -0
  337. package/src/vendor/pino.ts +3 -0
  338. package/src/vendor/zod.ts +3 -0
  339. package/tests/e2e/developE2e.spec.ts +152 -0
  340. package/tests/e2e/developFailureE2e.spec.ts +289 -0
  341. package/tests/e2e/developPty.spec.ts +86 -0
  342. package/tests/e2e/discoveryWebSearchRelevance.spec.ts +103 -0
  343. package/tests/e2e/harness.spec.ts +104 -0
  344. package/tests/e2e/mcpLive.spec.ts +149 -0
  345. package/tests/e2e/newSession.e2e.spec.ts +245 -0
  346. package/tests/e2e/ralphLoopEnrichmentComparison.spec.ts +70 -0
  347. package/tests/e2e/workiqEnrichment.spec.ts +72 -0
  348. package/tests/e2e/zava-assessment/agent-interaction-script.md +258 -0
  349. package/tests/e2e/zava-assessment/company-profile.md +98 -0
  350. package/tests/e2e/zava-assessment/expected-results-checklist.md +454 -0
  351. package/tests/e2e/zavaSimulation.spec.ts +511 -0
  352. package/tests/fixtures/completedSession.json +141 -0
  353. package/tests/fixtures/test-fixture-project/package-lock.json +1585 -0
  354. package/tests/fixtures/test-fixture-project/package.json +12 -0
  355. package/tests/fixtures/test-fixture-project/src/add.ts +3 -0
  356. package/tests/fixtures/test-fixture-project/tests/failing.test.ts +7 -0
  357. package/tests/fixtures/test-fixture-project/tests/hanging.test.ts +9 -0
  358. package/tests/fixtures/test-fixture-project/tests/passing.test.ts +13 -0
  359. package/tests/fixtures/test-fixture-project/vitest.config.ts +7 -0
  360. package/tests/integration/autoStartConversation.spec.ts +168 -0
  361. package/tests/integration/defaultCommand.spec.ts +179 -0
  362. package/tests/integration/directCommandNonTty.spec.ts +260 -0
  363. package/tests/integration/directCommandTty.spec.ts +185 -0
  364. package/tests/integration/discoveryEnrichmentFlow.spec.ts +209 -0
  365. package/tests/integration/exportArtifacts.spec.ts +232 -0
  366. package/tests/integration/exportFallbackFlow.spec.ts +115 -0
  367. package/tests/integration/mcpDegradationFlow.spec.ts +231 -0
  368. package/tests/integration/mcpTransportFlow.spec.ts +178 -0
  369. package/tests/integration/newSessionFlow.spec.ts +406 -0
  370. package/tests/integration/pocGithubMcp.spec.ts +224 -0
  371. package/tests/integration/pocLocalFallback.spec.ts +205 -0
  372. package/tests/integration/pocScaffold.spec.ts +220 -0
  373. package/tests/integration/ralphLoopFlow.spec.ts +430 -0
  374. package/tests/integration/ralphLoopPartial.spec.ts +416 -0
  375. package/tests/integration/resumeAndBacktrack.spec.ts +278 -0
  376. package/tests/integration/spinnerLifecycle.spec.ts +270 -0
  377. package/tests/integration/summarizationFlow.spec.ts +135 -0
  378. package/tests/integration/testRunnerReal.spec.ts +63 -0
  379. package/tests/integration/webSearchAgent.spec.ts +155 -0
  380. package/tests/live/copilotSdkLive.spec.ts +149 -0
  381. package/tests/live/zavaFullWorkshop.spec.ts +515 -0
  382. package/tests/setup/loadEnv.ts +5 -0
  383. package/tests/unit/cli/developCommand.spec.ts +679 -0
  384. package/tests/unit/cli/directCommands.spec.ts +325 -0
  385. package/tests/unit/cli/envLoader.spec.ts +73 -0
  386. package/tests/unit/cli/ioContext.spec.ts +148 -0
  387. package/tests/unit/cli/preflight.spec.ts +125 -0
  388. package/tests/unit/cli/statusCommand.spec.ts +134 -0
  389. package/tests/unit/cli/workshopClientFallback.spec.ts +100 -0
  390. package/tests/unit/cli/workshopCommand.spec.ts +378 -0
  391. package/tests/unit/config/vitestEnvSetup.spec.ts +24 -0
  392. package/tests/unit/develop/checkpointState.spec.ts +378 -0
  393. package/tests/unit/develop/codeGenerator.spec.ts +447 -0
  394. package/tests/unit/develop/githubMcpAdapter.spec.ts +283 -0
  395. package/tests/unit/develop/mcpContextEnricher.spec.ts +564 -0
  396. package/tests/unit/develop/outputValidator.spec.ts +134 -0
  397. package/tests/unit/develop/pocScaffolder.spec.ts +451 -0
  398. package/tests/unit/develop/ralphLoop.spec.ts +1439 -0
  399. package/tests/unit/develop/templateRegistry.spec.ts +106 -0
  400. package/tests/unit/develop/testRunner.spec.ts +294 -0
  401. package/tests/unit/infraBicep.spec.ts +116 -0
  402. package/tests/unit/infraDeploy.spec.ts +102 -0
  403. package/tests/unit/infraTeardown.spec.ts +77 -0
  404. package/tests/unit/logging/logger.spec.ts +50 -0
  405. package/tests/unit/loop/conversationLoop.spec.ts +719 -0
  406. package/tests/unit/loop/phaseSummarizer.spec.ts +169 -0
  407. package/tests/unit/loop/streamingMarkdown.spec.ts +180 -0
  408. package/tests/unit/mcp/mcpManager.spec.ts +336 -0
  409. package/tests/unit/mcp/mcpTransport.spec.ts +689 -0
  410. package/tests/unit/mcp/retryPolicy.spec.ts +278 -0
  411. package/tests/unit/mcp/timeoutValidation.spec.ts +55 -0
  412. package/tests/unit/mcp/webSearch.spec.ts +718 -0
  413. package/tests/unit/phases/contextSummarizer.spec.ts +158 -0
  414. package/tests/unit/phases/discoveryEnricher.repeatCalls.spec.ts +125 -0
  415. package/tests/unit/phases/discoveryEnricher.spec.ts +512 -0
  416. package/tests/unit/phases/phaseExtractors.spec.ts +406 -0
  417. package/tests/unit/phases/phaseHandlers.spec.ts +483 -0
  418. package/tests/unit/prompts/promptLoader.spec.ts +144 -0
  419. package/tests/unit/schemas/pocSchemas.spec.ts +457 -0
  420. package/tests/unit/schemas/session.spec.ts +328 -0
  421. package/tests/unit/sessions/exportPaths.spec.ts +38 -0
  422. package/tests/unit/sessions/exportWriter.spec.ts +737 -0
  423. package/tests/unit/sessions/sessionManager.spec.ts +174 -0
  424. package/tests/unit/sessions/sessionStore.spec.ts +136 -0
  425. package/tests/unit/shared/activitySpinner.spec.ts +211 -0
  426. package/tests/unit/shared/cardsLoader.spec.ts +89 -0
  427. package/tests/unit/shared/copilotClient.spec.ts +185 -0
  428. package/tests/unit/shared/errorClassifier.spec.ts +152 -0
  429. package/tests/unit/shared/events.spec.ts +71 -0
  430. package/tests/unit/shared/markdownRenderer.spec.ts +42 -0
  431. package/tests/unit/shared/markdownRendererChunks.spec.ts +83 -0
  432. package/tests/unit/shared/tableRenderer.spec.ts +38 -0
  433. package/tsconfig.json +20 -0
  434. package/vitest.config.ts +15 -0
  435. package/vitest.live.config.ts +19 -0
@@ -0,0 +1,199 @@
1
+ /**
2
+ * GitHub MCP Adapter.
3
+ *
4
+ * Wraps GitHub MCP tool calls for PoC repository creation and file pushes.
5
+ * When GitHub MCP is unavailable, all methods degrade gracefully by returning
6
+ * `{ available: false }` or undefined.
7
+ *
8
+ * Contract: specs/002-poc-generation/contracts/poc-output.md (GitHub MCP integration)
9
+ */
10
+ import type { McpManager } from '../mcp/mcpManager.js';
11
+
12
+ // ── Types ────────────────────────────────────────────────────────────────────
13
+
14
+ export interface CreateRepositoryOptions {
15
+ name: string;
16
+ description?: string;
17
+ private?: boolean;
18
+ }
19
+
20
+ export type CreateRepositoryResult =
21
+ | {
22
+ available: true;
23
+ repoUrl: string;
24
+ repoName: string;
25
+ }
26
+ | {
27
+ available: false;
28
+ reason?: string;
29
+ };
30
+
31
+ export interface PushFilesOptions {
32
+ repoUrl: string;
33
+ files: Array<{ path: string; content: string }>;
34
+ commitMessage: string;
35
+ branch?: string;
36
+ }
37
+
38
+ export type PushFilesResult =
39
+ | {
40
+ available: true;
41
+ commitSha?: string;
42
+ }
43
+ | {
44
+ available: false;
45
+ reason?: string;
46
+ };
47
+
48
+ // ── GitHubMcpAdapter ─────────────────────────────────────────────────────────
49
+
50
+ /**
51
+ * Adapter for GitHub MCP repository operations.
52
+ *
53
+ * All methods check isAvailable() before attempting tool calls.
54
+ * On failure or unavailability, returns `{ available: false }` without throwing.
55
+ */
56
+ export class GitHubMcpAdapter {
57
+ private readonly mcpManager: McpManager;
58
+ private _repoUrl: string | undefined;
59
+
60
+ constructor(mcpManager: McpManager) {
61
+ this.mcpManager = mcpManager;
62
+ }
63
+
64
+ /**
65
+ * Check if GitHub MCP is available.
66
+ */
67
+ isAvailable(): boolean {
68
+ return this.mcpManager.isAvailable('github');
69
+ }
70
+
71
+ /**
72
+ * Create a GitHub repository via MCP.
73
+ *
74
+ * Calls `mcpManager.callTool('github', 'create_repository', ...)` and parses
75
+ * the response for `html_url`. Returns `{ available: false }` if GitHub MCP
76
+ * is unavailable or if creation fails.
77
+ */
78
+ async createRepository(options: CreateRepositoryOptions): Promise<CreateRepositoryResult> {
79
+ if (!this.isAvailable()) {
80
+ return { available: false, reason: 'GitHub MCP not available' };
81
+ }
82
+
83
+ try {
84
+ const response = await this.mcpManager.callTool(
85
+ 'github',
86
+ 'create_repository',
87
+ {
88
+ name: options.name,
89
+ description: options.description ?? '',
90
+ private: options.private ?? true,
91
+ },
92
+ { timeoutMs: 60_000 },
93
+ );
94
+
95
+ const repoUrl =
96
+ (response.html_url as string) ?? (response.url as string) ?? (response.clone_url as string);
97
+ const repoName = (response.name as string) ?? (response.full_name as string) ?? options.name;
98
+
99
+ if (!repoUrl) {
100
+ return { available: false, reason: 'MCP response missing repository URL' };
101
+ }
102
+
103
+ this._repoUrl = repoUrl;
104
+
105
+ return {
106
+ available: true,
107
+ repoUrl,
108
+ repoName,
109
+ };
110
+ } catch (err: unknown) {
111
+ const reason = err instanceof Error ? err.message : 'Unknown error';
112
+ return { available: false, reason };
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Push files to a GitHub repository via MCP.
118
+ *
119
+ * Calls `mcpManager.callTool('github', 'push_files', ...)` with file paths
120
+ * and contents. Returns `{ available: false }` if GitHub MCP is unavailable
121
+ * or if push fails.
122
+ */
123
+ async pushFiles(options: PushFilesOptions): Promise<PushFilesResult> {
124
+ if (!this.isAvailable()) {
125
+ return { available: false, reason: 'GitHub MCP not available' };
126
+ }
127
+
128
+ try {
129
+ // Extract owner and repo from repoUrl
130
+ const { owner, repo } = extractOwnerRepo(options.repoUrl);
131
+
132
+ const response = await this.mcpManager.callTool(
133
+ 'github',
134
+ 'push_files',
135
+ {
136
+ owner,
137
+ repo,
138
+ files: options.files,
139
+ message: options.commitMessage,
140
+ branch: options.branch ?? 'main',
141
+ },
142
+ { timeoutMs: 60_000 },
143
+ );
144
+
145
+ const commitSha =
146
+ (response.sha as string | undefined) ??
147
+ ((response.commit as Record<string, unknown> | undefined)?.sha as string | undefined);
148
+
149
+ return {
150
+ available: true,
151
+ commitSha,
152
+ };
153
+ } catch (err: unknown) {
154
+ const reason = err instanceof Error ? err.message : 'Unknown error';
155
+ return { available: false, reason };
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Get the repository URL from the last successful createRepository call.
161
+ */
162
+ getRepoUrl(): string | undefined {
163
+ return this._repoUrl;
164
+ }
165
+ }
166
+
167
+ // ── Helpers ──────────────────────────────────────────────────────────────────
168
+
169
+ /**
170
+ * Extract owner and repo from a GitHub repository URL.
171
+ * Supports formats like:
172
+ * https://github.com/owner/repo
173
+ * https://github.com/owner/repo.git
174
+ * https://api.github.com/repos/owner/repo
175
+ */
176
+ function extractOwnerRepo(repoUrl: string): { owner: string; repo: string } {
177
+ // Try github.com/{owner}/{repo} pattern
178
+ const ghMatch = repoUrl.match(/github\.com\/([^/]+)\/([^/.]+)/);
179
+ if (ghMatch) {
180
+ return { owner: ghMatch[1], repo: ghMatch[2] };
181
+ }
182
+
183
+ // Fallback: try api.github.com/repos/{owner}/{repo}
184
+ const apiMatch = repoUrl.match(/api\.github\.com\/repos\/([^/]+)\/([^/.]+)/);
185
+ if (apiMatch) {
186
+ return { owner: apiMatch[1], repo: apiMatch[2] };
187
+ }
188
+
189
+ // Last resort: try extracting last two path segments
190
+ const segments = new URL(repoUrl).pathname.split('/').filter(Boolean);
191
+ if (segments.length >= 2) {
192
+ return {
193
+ owner: segments[segments.length - 2],
194
+ repo: segments[segments.length - 1].replace('.git', ''),
195
+ };
196
+ }
197
+
198
+ throw new Error(`Cannot extract owner/repo from URL: ${repoUrl}`);
199
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Develop phase module.
3
+ *
4
+ * Exports all components of the PoC Generation & Ralph Loop feature (Feature 002).
5
+ * Orchestrates: scaffold → install → iterate (test → fix → repeat) → complete.
6
+ */
7
+
8
+ export { PocScaffolder } from './pocScaffolder.js';
9
+ export type { ScaffoldContext, ScaffoldResult } from './pocScaffolder.js';
10
+
11
+ export { generateDynamicScaffold } from './dynamicScaffolder.js';
12
+ export type { DynamicScaffoldContext, DynamicScaffoldResult } from './dynamicScaffolder.js';
13
+
14
+ export { TestRunner } from './testRunner.js';
15
+
16
+ export { CodeGenerator } from './codeGenerator.js';
17
+
18
+ export { McpContextEnricher } from './mcpContextEnricher.js';
19
+
20
+ export { GitHubMcpAdapter } from './githubMcpAdapter.js';
21
+
22
+ export { RalphLoop } from './ralphLoop.js';
23
+ export type { RalphLoopOptions, RalphLoopResult } from './ralphLoop.js';
24
+
25
+ export { deriveCheckpointState } from './checkpointState.js';
26
+ export type { CheckpointState } from './checkpointState.js';
27
+
28
+ export {
29
+ selectTemplate,
30
+ createDefaultRegistry,
31
+ NODE_TS_VITEST_TEMPLATE,
32
+ PYTHON_PYTEST_TEMPLATE,
33
+ } from './templateRegistry.js';
34
+ export type { TemplateEntry, TemplateRegistry } from './templateRegistry.js';
@@ -0,0 +1,279 @@
1
+ /**
2
+ * MCP Context Enricher.
3
+ *
4
+ * Conditionally queries MCP services to enrich the LLM iteration prompt
5
+ * with up-to-date library docs and architecture guidance:
6
+ *
7
+ * - Context7: library documentation for PoC dependencies
8
+ * - Azure MCP / Microsoft Docs: when plan references Azure services
9
+ * - web.search: when stuck (2+ consecutive iterations with same failures)
10
+ *
11
+ * All MCP queries degrade gracefully — returns empty context when unavailable.
12
+ *
13
+ * Contract: specs/002-poc-generation/tasks.md (T048/T049)
14
+ */
15
+ import type { McpManager } from '../mcp/mcpManager.js';
16
+ import { isWebSearchConfigured } from '../mcp/webSearch.js';
17
+
18
+ // ── Types ────────────────────────────────────────────────────────────────────
19
+
20
+ export interface EnricherOptions {
21
+ /** MCP manager to check service availability (legacy, optional) */
22
+ mcpManager?: McpManager;
23
+ /** Dependencies listed in the plan (e.g., ["express", "zod"]) */
24
+ dependencies?: string[];
25
+ /** Plan architecture notes (checked for Azure keywords) */
26
+ architectureNotes?: string;
27
+ /** Number of consecutive stuck iterations (same failures) */
28
+ stuckIterations?: number;
29
+ /** Current failing test names for stuck detection */
30
+ failingTests?: string[];
31
+ }
32
+
33
+ export interface EnrichedContext {
34
+ /** Context7 library docs */
35
+ libraryDocs?: string;
36
+ /** Azure/cloud guidance */
37
+ azureGuidance?: string;
38
+ /** Web search results */
39
+ webSearchResults?: string;
40
+ /** Combined context string for prompt injection */
41
+ combined: string;
42
+ }
43
+
44
+ // ── Azure keyword detection ───────────────────────────────────────────────────
45
+
46
+ const AZURE_KEYWORDS = [
47
+ 'azure',
48
+ 'cosmos db',
49
+ 'cosmosdb',
50
+ 'blob storage',
51
+ 'service bus',
52
+ 'event hub',
53
+ 'app service',
54
+ 'azure sql',
55
+ 'key vault',
56
+ 'azure functions',
57
+ 'azure openai',
58
+ ];
59
+
60
+ function mentionsAzure(text: string): boolean {
61
+ const lower = text.toLowerCase();
62
+ return AZURE_KEYWORDS.some((kw) => lower.includes(kw));
63
+ }
64
+
65
+ // ── McpContextEnricher ────────────────────────────────────────────────────────
66
+
67
+ /**
68
+ * Enriches LLM iteration prompts with context from MCP services.
69
+ *
70
+ * Each method degrades gracefully — returns undefined when service
71
+ * is unavailable rather than throwing.
72
+ */
73
+ export class McpContextEnricher {
74
+ /** @internal Exposed for RalphLoop integration */
75
+ readonly mcpManager: McpManager;
76
+
77
+ constructor(mcpManager: McpManager) {
78
+ this.mcpManager = mcpManager;
79
+ }
80
+
81
+ /**
82
+ * Enrich the iteration context with MCP-sourced information.
83
+ *
84
+ * Queries are conditional on:
85
+ * 1. Context7: available AND dependencies listed in plan
86
+ * 2. Azure MCP: available AND plan references Azure services
87
+ * 3. web.search: configured AND stuckIterations >= 2
88
+ */
89
+ async enrich(options: EnricherOptions): Promise<EnrichedContext> {
90
+ const { dependencies = [], architectureNotes = '', stuckIterations = 0 } = options;
91
+
92
+ const parts: string[] = [];
93
+ let libraryDocs: string | undefined;
94
+ let azureGuidance: string | undefined;
95
+ let webSearchResults: string | undefined;
96
+
97
+ // Context7 + Azure MCP: run concurrently via Promise.allSettled()
98
+ const concurrent: Promise<void>[] = [];
99
+
100
+ if (this.mcpManager.isAvailable('context7') && dependencies.length > 0) {
101
+ concurrent.push(
102
+ this.queryContext7(dependencies).then((result) => {
103
+ libraryDocs = result;
104
+ }),
105
+ );
106
+ }
107
+
108
+ if (this.mcpManager.isAvailable('azure') && mentionsAzure(architectureNotes)) {
109
+ concurrent.push(
110
+ this.queryAzureMcp(architectureNotes).then((result) => {
111
+ azureGuidance = result;
112
+ }),
113
+ );
114
+ }
115
+
116
+ await Promise.allSettled(concurrent);
117
+
118
+ if (libraryDocs) parts.push(`### Library Documentation (Context7)\n\n${libraryDocs}`);
119
+ if (azureGuidance) parts.push(`### Azure Architecture Guidance\n\n${azureGuidance}`);
120
+
121
+ // web.search: when stuck for 2+ iterations
122
+ if (isWebSearchConfigured() && stuckIterations >= 2 && options.failingTests?.length) {
123
+ webSearchResults = await this.queryWebSearch(options.failingTests);
124
+ if (webSearchResults) parts.push(`### Web Search Results\n\n${webSearchResults}`);
125
+ }
126
+
127
+ return {
128
+ libraryDocs,
129
+ azureGuidance,
130
+ webSearchResults,
131
+ combined: parts.join('\n\n'),
132
+ };
133
+ }
134
+
135
+ /**
136
+ * Query Context7 for library documentation.
137
+ *
138
+ * Calls `mcpManager.callTool('context7', 'resolve-library-id', ...)` to
139
+ * resolve each dependency, then `query-docs` for the resolved library.
140
+ * Degrades gracefully when the service is unavailable or errors.
141
+ */
142
+ private async queryContext7(dependencies: string[]): Promise<string | undefined> {
143
+ if (!this.mcpManager.isAvailable('context7')) return undefined;
144
+
145
+ try {
146
+ const docs: string[] = [];
147
+
148
+ for (const dep of dependencies.slice(0, 5)) {
149
+ // Filter out type-only and well-known packages that don't need lookup
150
+ if (dep.startsWith('@types/') || dep === 'typescript' || dep === 'vitest') {
151
+ continue;
152
+ }
153
+
154
+ try {
155
+ // Step 1: Resolve library ID
156
+ const resolved = await this.mcpManager.callTool(
157
+ 'context7',
158
+ 'resolve-library-id',
159
+ {
160
+ libraryName: dep,
161
+ },
162
+ { timeoutMs: 30_000 },
163
+ );
164
+ const libraryId = (resolved.libraryId as string) ?? (resolved.id as string);
165
+ if (!libraryId) {
166
+ docs.push(
167
+ `- **${dep}**: See https://www.npmjs.com/package/${dep} for API documentation`,
168
+ );
169
+ continue;
170
+ }
171
+
172
+ // Step 2: Query docs with the resolved ID
173
+ const docResult = await this.mcpManager.callTool(
174
+ 'context7',
175
+ 'query-docs',
176
+ {
177
+ libraryId,
178
+ topic: dep,
179
+ },
180
+ { timeoutMs: 30_000 },
181
+ );
182
+ const content = (docResult.content as string) ?? (docResult.text as string);
183
+ if (content) {
184
+ docs.push(`- **${dep}**:\n${content}`);
185
+ } else {
186
+ docs.push(
187
+ `- **${dep}**: See https://www.npmjs.com/package/${dep} for API documentation`,
188
+ );
189
+ }
190
+ } catch {
191
+ // Individual dep failure — fallback link
192
+ docs.push(`- **${dep}**: See https://www.npmjs.com/package/${dep} for API documentation`);
193
+ }
194
+ }
195
+
196
+ return docs.length > 0 ? docs.join('\n') : undefined;
197
+ } catch {
198
+ return undefined;
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Query Azure MCP for architecture guidance.
204
+ *
205
+ * Calls `mcpManager.callTool('azure', 'documentation', ...)` with detected
206
+ * Azure keywords. Degrades gracefully when Azure MCP is unavailable.
207
+ */
208
+ private async queryAzureMcp(architectureNotes: string): Promise<string | undefined> {
209
+ if (!this.mcpManager.isAvailable('azure')) return undefined;
210
+
211
+ try {
212
+ const detected = AZURE_KEYWORDS.filter((kw) => architectureNotes.toLowerCase().includes(kw));
213
+ if (detected.length === 0) return undefined;
214
+
215
+ try {
216
+ const response = await this.mcpManager.callTool(
217
+ 'azure',
218
+ 'documentation',
219
+ {
220
+ query: `Best practices for ${detected.join(', ')}`,
221
+ },
222
+ { timeoutMs: 30_000 },
223
+ );
224
+ const content = (response.content as string) ?? (response.text as string);
225
+ if (content) {
226
+ return content;
227
+ }
228
+ } catch {
229
+ // MCP call failed — fall back to static guidance
230
+ }
231
+
232
+ return [
233
+ `Detected Azure services: ${detected.join(', ')}`,
234
+ 'Use the @azure/identity DefaultAzureCredential for authentication.',
235
+ 'Prefer connection strings from environment variables (never hardcode).',
236
+ ].join('\n');
237
+ } catch {
238
+ return undefined;
239
+ }
240
+ }
241
+
242
+ /**
243
+ * Query web search for solutions to failing tests.
244
+ *
245
+ * Only called when stuckIterations >= 2 to avoid unnecessary API calls.
246
+ * Uses `mcpManager.callTool` to search for error messages.
247
+ */
248
+ private async queryWebSearch(failingTests: string[]): Promise<string | undefined> {
249
+ if (!isWebSearchConfigured()) return undefined;
250
+
251
+ try {
252
+ const query = failingTests.slice(0, 3).join('; ');
253
+
254
+ // Attempt real web search via MCP if available
255
+ if (this.mcpManager.isAvailable('websearch')) {
256
+ try {
257
+ const response = await this.mcpManager.callTool(
258
+ 'websearch',
259
+ 'search',
260
+ {
261
+ query: `how to fix: ${query}`,
262
+ },
263
+ { timeoutMs: 30_000 },
264
+ );
265
+ const content = (response.content as string) ?? (response.text as string);
266
+ if (content) {
267
+ return content;
268
+ }
269
+ } catch {
270
+ // Web search MCP unavailable — fall through to static message
271
+ }
272
+ }
273
+
274
+ return `Web search for: "${query}" — no results available in this environment.`;
275
+ } catch {
276
+ return undefined;
277
+ }
278
+ }
279
+ }