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,364 @@
1
+ // ── Template Definitions ─────────────────────────────────────────────────────
2
+ export const NODE_TS_VITEST_TEMPLATE = {
3
+ id: 'node-ts-vitest',
4
+ displayName: 'TypeScript + Node.js + Vitest',
5
+ techStack: {
6
+ language: 'TypeScript',
7
+ runtime: 'Node.js 20',
8
+ testRunner: 'npm test',
9
+ buildCommand: 'npm run build',
10
+ },
11
+ installCommand: 'npm install',
12
+ testCommand: 'npm test -- --reporter=json',
13
+ matchPatterns: ['typescript', 'node', 'vitest', 'ts'],
14
+ files: [
15
+ {
16
+ path: '.gitignore',
17
+ skipIfExists: false,
18
+ content: `node_modules/
19
+ dist/
20
+ coverage/
21
+ *.tsbuildinfo
22
+ .env
23
+ `,
24
+ },
25
+ {
26
+ path: 'package.json',
27
+ skipIfExists: true,
28
+ content: (ctx) => JSON.stringify({
29
+ name: ctx.projectName,
30
+ version: '0.1.0',
31
+ private: true,
32
+ type: 'module',
33
+ scripts: {
34
+ build: 'tsc',
35
+ start: 'node dist/index.js',
36
+ test: 'vitest run',
37
+ },
38
+ dependencies: {},
39
+ devDependencies: {
40
+ typescript: '^5.0.0',
41
+ vitest: '^3.0.0',
42
+ '@types/node': '^20.0.0',
43
+ },
44
+ }, null, 2) + '\n',
45
+ },
46
+ {
47
+ path: 'tsconfig.json',
48
+ skipIfExists: true,
49
+ content: JSON.stringify({
50
+ compilerOptions: {
51
+ target: 'ES2022',
52
+ module: 'Node16',
53
+ moduleResolution: 'Node16',
54
+ strict: true,
55
+ outDir: 'dist',
56
+ rootDir: 'src',
57
+ declaration: true,
58
+ esModuleInterop: true,
59
+ skipLibCheck: true,
60
+ },
61
+ include: ['src'],
62
+ }, null, 2) + '\n',
63
+ },
64
+ {
65
+ path: 'README.md',
66
+ skipIfExists: true,
67
+ content: (ctx) => `# ${ctx.ideaTitle}
68
+
69
+ ${ctx.ideaDescription}
70
+
71
+ ## Generated by
72
+
73
+ sofIA — AI Discovery Workshop CLI
74
+ Session: \`${ctx.sessionId}\`
75
+ Generated: ${new Date().toISOString()}
76
+
77
+ ## Prerequisites
78
+
79
+ - Node.js 20+
80
+ - npm 9+
81
+
82
+ ## Quick Start
83
+
84
+ \`\`\`bash
85
+ npm install
86
+ npm test
87
+ npm start
88
+ \`\`\`
89
+
90
+ ## How It Works
91
+
92
+ ${ctx.planSummary}
93
+
94
+ ## Technology Stack
95
+
96
+ - **Language**: ${ctx.techStack.language}
97
+ - **Runtime**: ${ctx.techStack.runtime}
98
+ - **Test Runner**: ${ctx.techStack.testRunner}
99
+ ${ctx.techStack.framework ? `- **Framework**: ${ctx.techStack.framework}\n` : ''}
100
+ `,
101
+ },
102
+ {
103
+ path: 'src/index.ts',
104
+ skipIfExists: true,
105
+ content: (ctx) => `/**
106
+ * ${ctx.ideaTitle}
107
+ *
108
+ * ${ctx.ideaDescription}
109
+ *
110
+ * Generated by sofIA (session: ${ctx.sessionId})
111
+ */
112
+
113
+ /**
114
+ * Main entry point for the ${ctx.projectName} PoC.
115
+ *
116
+ * TODO: Implement the core functionality described in the plan.
117
+ * The test file (tests/index.test.ts) describes the expected behavior.
118
+ */
119
+ export function main(): string {
120
+ return 'Hello from ${ctx.projectName}!';
121
+ }
122
+
123
+ // Run if called directly
124
+ const isMain = process.argv[1]?.endsWith('index.js') || process.argv[1]?.endsWith('index.ts');
125
+ if (isMain) {
126
+ console.log(main());
127
+ }
128
+ `,
129
+ },
130
+ {
131
+ path: 'tests/index.test.ts',
132
+ skipIfExists: true,
133
+ content: (ctx) => `/**
134
+ * Tests for ${ctx.ideaTitle} PoC.
135
+ *
136
+ * These tests describe the expected behavior of the implementation.
137
+ * They are intentionally failing initially — the Ralph loop will
138
+ * iteratively fix the implementation until they pass.
139
+ *
140
+ * Generated by sofIA (session: ${ctx.sessionId})
141
+ */
142
+ import { describe, it, expect } from 'vitest';
143
+ import { main } from '../src/index.js';
144
+
145
+ describe('${ctx.projectName}', () => {
146
+ it('should initialize successfully', () => {
147
+ const result = main();
148
+ expect(result).toBeDefined();
149
+ expect(typeof result).toBe('string');
150
+ });
151
+
152
+ it('should implement core functionality', () => {
153
+ // TODO: sofIA Ralph loop will refine this test to match the actual implementation
154
+ // Plan: ${ctx.planSummary.substring(0, 200)}
155
+ const result = main();
156
+ expect(result).toContain('${ctx.projectName}');
157
+ });
158
+ });
159
+ `,
160
+ },
161
+ {
162
+ path: '.sofia-metadata.json',
163
+ skipIfExists: false,
164
+ content: (ctx) => JSON.stringify({
165
+ sessionId: ctx.sessionId,
166
+ featureSpec: '002-poc-generation',
167
+ generatedAt: new Date().toISOString(),
168
+ ideaTitle: ctx.ideaTitle,
169
+ totalIterations: 0,
170
+ finalStatus: null,
171
+ terminationReason: null,
172
+ techStack: {
173
+ language: ctx.techStack.language.toLowerCase(),
174
+ runtime: ctx.techStack.runtime.toLowerCase(),
175
+ testRunner: ctx.techStack.testRunner,
176
+ },
177
+ }, null, 2) + '\n',
178
+ },
179
+ ],
180
+ };
181
+ export const PYTHON_PYTEST_TEMPLATE = {
182
+ id: 'python-pytest',
183
+ displayName: 'Python + pytest',
184
+ techStack: {
185
+ language: 'Python',
186
+ runtime: 'Python 3.11',
187
+ testRunner: 'pytest',
188
+ },
189
+ installCommand: 'pip install -r requirements.txt',
190
+ testCommand: 'pytest --tb=short -q --json-report --json-report-file=-',
191
+ matchPatterns: ['python', 'fastapi', 'flask', 'django', 'pytest'],
192
+ files: [
193
+ {
194
+ path: '.gitignore',
195
+ skipIfExists: false,
196
+ content: `__pycache__/
197
+ *.pyc
198
+ .venv/
199
+ venv/
200
+ dist/
201
+ *.egg-info/
202
+ .env
203
+ .pytest_cache/
204
+ `,
205
+ },
206
+ {
207
+ path: 'requirements.txt',
208
+ skipIfExists: true,
209
+ content: (ctx) => `# ${ctx.projectName} dependencies
210
+ pytest>=7.0.0
211
+ pytest-json-report>=1.5.0
212
+ `,
213
+ },
214
+ {
215
+ path: 'pytest.ini',
216
+ skipIfExists: true,
217
+ content: `[pytest]
218
+ testpaths = tests
219
+ python_files = test_*.py
220
+ python_classes = Test*
221
+ python_functions = test_*
222
+ `,
223
+ },
224
+ {
225
+ path: 'README.md',
226
+ skipIfExists: true,
227
+ content: (ctx) => `# ${ctx.ideaTitle}
228
+
229
+ ${ctx.ideaDescription}
230
+
231
+ ## Generated by
232
+
233
+ sofIA — AI Discovery Workshop CLI
234
+ Session: \`${ctx.sessionId}\`
235
+ Generated: ${new Date().toISOString()}
236
+
237
+ ## Prerequisites
238
+
239
+ - Python 3.11+
240
+ - pip
241
+
242
+ ## Quick Start
243
+
244
+ \`\`\`bash
245
+ pip install -r requirements.txt
246
+ pytest
247
+ python src/main.py
248
+ \`\`\`
249
+
250
+ ## How It Works
251
+
252
+ ${ctx.planSummary}
253
+
254
+ ## Technology Stack
255
+
256
+ - **Language**: ${ctx.techStack.language}
257
+ - **Runtime**: ${ctx.techStack.runtime}
258
+ - **Test Runner**: ${ctx.techStack.testRunner}
259
+ ${ctx.techStack.framework ? `- **Framework**: ${ctx.techStack.framework}\n` : ''}
260
+ `,
261
+ },
262
+ {
263
+ path: 'src/__init__.py',
264
+ skipIfExists: true,
265
+ content: '',
266
+ },
267
+ {
268
+ path: 'src/main.py',
269
+ skipIfExists: true,
270
+ content: (ctx) => `"""
271
+ ${ctx.ideaTitle}
272
+
273
+ ${ctx.ideaDescription}
274
+
275
+ Generated by sofIA (session: ${ctx.sessionId})
276
+ """
277
+
278
+
279
+ def main() -> str:
280
+ """Main entry point for the ${ctx.projectName} PoC.
281
+
282
+ TODO: Implement the core functionality described in the plan.
283
+ The test file (tests/test_main.py) describes the expected behavior.
284
+ """
285
+ return "Hello from ${ctx.projectName}!"
286
+
287
+
288
+ if __name__ == "__main__":
289
+ print(main())
290
+ `,
291
+ },
292
+ {
293
+ path: 'tests/test_main.py',
294
+ skipIfExists: true,
295
+ content: (ctx) => `"""
296
+ Tests for ${ctx.ideaTitle} PoC.
297
+
298
+ These tests describe the expected behavior of the implementation.
299
+ They are intentionally failing initially — the Ralph loop will
300
+ iteratively fix the implementation until they pass.
301
+
302
+ Generated by sofIA (session: ${ctx.sessionId})
303
+ """
304
+ from src.main import main
305
+
306
+
307
+ def test_main_returns_string():
308
+ result = main()
309
+ assert isinstance(result, str)
310
+
311
+
312
+ def test_main_contains_project_name():
313
+ result = main()
314
+ assert "${ctx.projectName}" in result
315
+ `,
316
+ },
317
+ {
318
+ path: '.sofia-metadata.json',
319
+ skipIfExists: false,
320
+ content: (ctx) => JSON.stringify({
321
+ sessionId: ctx.sessionId,
322
+ featureSpec: '002-poc-generation',
323
+ generatedAt: new Date().toISOString(),
324
+ ideaTitle: ctx.ideaTitle,
325
+ totalIterations: 0,
326
+ finalStatus: null,
327
+ terminationReason: null,
328
+ techStack: {
329
+ language: ctx.techStack.language.toLowerCase(),
330
+ runtime: ctx.techStack.runtime.toLowerCase(),
331
+ testRunner: ctx.techStack.testRunner,
332
+ },
333
+ }, null, 2) + '\n',
334
+ },
335
+ ],
336
+ };
337
+ // ── Default Registry ─────────────────────────────────────────────────────────
338
+ /**
339
+ * Create the default template registry with built-in templates.
340
+ */
341
+ export function createDefaultRegistry() {
342
+ const registry = new Map();
343
+ registry.set(NODE_TS_VITEST_TEMPLATE.id, NODE_TS_VITEST_TEMPLATE);
344
+ registry.set(PYTHON_PYTEST_TEMPLATE.id, PYTHON_PYTEST_TEMPLATE);
345
+ return registry;
346
+ }
347
+ // ── Selection ────────────────────────────────────────────────────────────────
348
+ /**
349
+ * Select a template from the registry based on plan architecture notes and dependencies.
350
+ *
351
+ * Uses first-match-wins logic: concatenates architectureNotes + dependencies into a
352
+ * search string, then checks each template's matchPatterns (case-insensitive).
353
+ * Falls back to `node-ts-vitest` if no match is found.
354
+ */
355
+ export function selectTemplate(registry, architectureNotes, dependencies) {
356
+ const searchText = [architectureNotes ?? '', ...(dependencies ?? [])].join(' ').toLowerCase();
357
+ for (const entry of registry.values()) {
358
+ if (entry.matchPatterns.some((p) => searchText.includes(p.toLowerCase()))) {
359
+ return entry;
360
+ }
361
+ }
362
+ // Default fallback
363
+ return registry.get('node-ts-vitest');
364
+ }
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Test Runner.
3
+ *
4
+ * Spawns the PoC's test suite via `npm test -- --reporter=json` in the
5
+ * output directory, parses Vitest JSON reporter output into a structured
6
+ * TestResults object, and enforces a 60-second timeout.
7
+ *
8
+ * Contract: specs/002-poc-generation/contracts/ralph-loop.md
9
+ */
10
+ import { spawn } from 'node:child_process';
11
+ // ── Constants ────────────────────────────────────────────────────────────────
12
+ const DEFAULT_TIMEOUT_MS = 60_000;
13
+ const MAX_RAW_OUTPUT_CHARS = 2_000;
14
+ export class TestRunner {
15
+ timeoutMs;
16
+ testCommand;
17
+ constructor(options = {}) {
18
+ this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
19
+ this.testCommand = options.testCommand ?? 'npm test -- --reporter=json';
20
+ }
21
+ /**
22
+ * Run the test suite in the given output directory.
23
+ *
24
+ * Spawns `npm test -- --reporter=json` and parses the JSON output.
25
+ * On timeout, returns an error result.
26
+ */
27
+ async run(outputDir) {
28
+ const startTime = Date.now();
29
+ let rawOutput = '';
30
+ let timedOut = false;
31
+ try {
32
+ rawOutput = await this.spawnTests(outputDir, (timed) => {
33
+ timedOut = timed;
34
+ });
35
+ }
36
+ catch (err) {
37
+ const msg = err instanceof Error ? err.message : String(err);
38
+ return this.buildErrorResult(startTime, `Test runner error: ${msg}`);
39
+ }
40
+ if (timedOut) {
41
+ return this.buildErrorResult(startTime, `Test runner timed out after ${this.timeoutMs}ms`);
42
+ }
43
+ // Truncate rawOutput to max chars from the end
44
+ const truncatedOutput = rawOutput.length > MAX_RAW_OUTPUT_CHARS
45
+ ? rawOutput.slice(-MAX_RAW_OUTPUT_CHARS)
46
+ : rawOutput;
47
+ return this.parseOutput(truncatedOutput, startTime, rawOutput);
48
+ }
49
+ /**
50
+ * Spawn test process and collect output.
51
+ */
52
+ spawnTests(outputDir, onTimeout) {
53
+ return new Promise((resolve, reject) => {
54
+ const stdoutChunks = [];
55
+ const stderrChunks = [];
56
+ const parts = this.testCommand.split(/\s+/);
57
+ const cmd = parts[0];
58
+ const args = parts.slice(1);
59
+ const child = spawn(cmd, args, {
60
+ cwd: outputDir,
61
+ shell: false,
62
+ env: {
63
+ ...process.env,
64
+ // Disable color output for reliable JSON parsing
65
+ NO_COLOR: '1',
66
+ FORCE_COLOR: '0',
67
+ },
68
+ });
69
+ child.stdout.on('data', (chunk) => {
70
+ stdoutChunks.push(chunk);
71
+ });
72
+ child.stderr.on('data', (chunk) => {
73
+ stderrChunks.push(chunk);
74
+ });
75
+ const timer = setTimeout(() => {
76
+ onTimeout(true);
77
+ child.kill('SIGTERM');
78
+ // Force kill after 5 more seconds
79
+ setTimeout(() => {
80
+ if (!child.killed)
81
+ child.kill('SIGKILL');
82
+ }, 5_000);
83
+ }, this.timeoutMs);
84
+ child.on('close', () => {
85
+ clearTimeout(timer);
86
+ // Combine stdout and stderr for parsing (Vitest may output to stderr)
87
+ const combined = [
88
+ Buffer.concat(stdoutChunks).toString('utf-8'),
89
+ Buffer.concat(stderrChunks).toString('utf-8'),
90
+ ]
91
+ .filter(Boolean)
92
+ .join('\n');
93
+ resolve(combined);
94
+ });
95
+ child.on('error', (err) => {
96
+ clearTimeout(timer);
97
+ reject(err);
98
+ });
99
+ });
100
+ }
101
+ /**
102
+ * Parse Vitest JSON reporter output into TestResults.
103
+ * Exposed as protected for unit testing purposes.
104
+ */
105
+ parseOutput(output, startTime, rawOutput) {
106
+ const durationMs = Date.now() - startTime;
107
+ // Try to extract JSON from the output (Vitest may mix JSON with other output)
108
+ const json = this.extractJson(output);
109
+ if (!json) {
110
+ // Could not parse JSON — return raw output as error info
111
+ return {
112
+ passed: 0,
113
+ failed: 0,
114
+ skipped: 0,
115
+ total: 0,
116
+ durationMs,
117
+ failures: [],
118
+ rawOutput: rawOutput.slice(-MAX_RAW_OUTPUT_CHARS),
119
+ };
120
+ }
121
+ const passed = json.numPassedTests ?? 0;
122
+ const failed = json.numFailedTests ?? 0;
123
+ const skipped = json.numPendingTests ?? 0;
124
+ const total = json.numTotalTests ?? passed + failed + skipped;
125
+ const failures = this.extractFailures(json);
126
+ return {
127
+ passed,
128
+ failed,
129
+ skipped,
130
+ total,
131
+ durationMs,
132
+ failures: failures.slice(0, 10), // max 10 failures
133
+ rawOutput: rawOutput.slice(-MAX_RAW_OUTPUT_CHARS),
134
+ };
135
+ }
136
+ /**
137
+ * Extract JSON object from potentially mixed output.
138
+ * Protected for subclass testing.
139
+ */
140
+ extractJson(output) {
141
+ // Try to find a JSON object/array in the output
142
+ const lines = output.split('\n');
143
+ // Try each line (Vitest may output JSON on a single line)
144
+ for (const line of lines) {
145
+ const trimmed = line.trim();
146
+ if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
147
+ try {
148
+ return JSON.parse(trimmed);
149
+ }
150
+ catch {
151
+ // Not valid JSON on this line
152
+ }
153
+ }
154
+ }
155
+ // Try the entire output
156
+ const start = output.indexOf('{');
157
+ const end = output.lastIndexOf('}');
158
+ if (start !== -1 && end !== -1 && end > start) {
159
+ try {
160
+ return JSON.parse(output.slice(start, end + 1));
161
+ }
162
+ catch {
163
+ // Not valid JSON
164
+ }
165
+ }
166
+ return null;
167
+ }
168
+ /**
169
+ * Extract TestFailure objects from Vitest JSON output.
170
+ */
171
+ extractFailures(json) {
172
+ const failures = [];
173
+ for (const testResult of json.testResults ?? []) {
174
+ for (const assertion of testResult.assertionResults ?? []) {
175
+ if (assertion.status === 'failed') {
176
+ const message = assertion.failureMessages?.join('\n') ?? 'Test failed';
177
+ failures.push({
178
+ testName: assertion.fullName || assertion.title,
179
+ message: message.substring(0, 500), // Truncate individual messages
180
+ file: testResult.testFilePath,
181
+ });
182
+ }
183
+ }
184
+ }
185
+ return failures;
186
+ }
187
+ /**
188
+ * Build an error result (timeout or spawn failure).
189
+ * Protected for subclass testing.
190
+ */
191
+ buildErrorResult(startTime, errorMessage) {
192
+ return {
193
+ passed: 0,
194
+ failed: 0,
195
+ skipped: 0,
196
+ total: 0,
197
+ durationMs: Date.now() - startTime,
198
+ failures: [],
199
+ rawOutput: errorMessage,
200
+ };
201
+ }
202
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Logger setup with pino.
3
+ *
4
+ * Features:
5
+ * - File logging when --log-file is provided
6
+ * - Stderr logging by default
7
+ * - Automatic redaction of secrets/PII (password, token, secret, key, auth)
8
+ */
9
+ import pino from 'pino';
10
+ import { mkdirSync } from 'node:fs';
11
+ import { dirname } from 'node:path';
12
+ const REDACT_PATHS = [
13
+ 'password',
14
+ 'token',
15
+ 'secret',
16
+ 'apiKey',
17
+ 'api_key',
18
+ 'authorization',
19
+ 'auth',
20
+ 'credential',
21
+ 'credentials',
22
+ ];
23
+ /**
24
+ * Create a configured pino logger.
25
+ *
26
+ * If logFile is specified, logs go to the file.
27
+ * Otherwise, logs go to stderr (to keep stdout clean for CLI output).
28
+ */
29
+ export function createLogger(options = {}) {
30
+ const { logFile, level = 'info', name = 'sofia' } = options;
31
+ const pinoOpts = {
32
+ name,
33
+ level,
34
+ redact: {
35
+ paths: REDACT_PATHS,
36
+ censor: '[REDACTED]',
37
+ },
38
+ };
39
+ if (logFile) {
40
+ mkdirSync(dirname(logFile), { recursive: true });
41
+ const dest = pino.destination({ dest: logFile, sync: false });
42
+ return pino(pinoOpts, dest);
43
+ }
44
+ // Default: write to stderr to keep stdout clean
45
+ return pino(pinoOpts, pino.destination({ dest: 2, sync: false }));
46
+ }
47
+ /** Global logger instance; call initGlobalLogger() to configure it. */
48
+ let globalLogger;
49
+ export function initGlobalLogger(options = {}) {
50
+ globalLogger = createLogger(options);
51
+ return globalLogger;
52
+ }
53
+ export function getLogger() {
54
+ if (!globalLogger) {
55
+ globalLogger = createLogger();
56
+ }
57
+ return globalLogger;
58
+ }