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,185 @@
1
+ /**
2
+ * Unit tests for CopilotClient abstraction.
3
+ *
4
+ * T050: SDK mcpServers forwarding
5
+ * T052: SDK hooks integration
6
+ */
7
+ import { describe, it, expect, vi } from 'vitest';
8
+
9
+ import { createFakeCopilotClient } from '../../../src/shared/copilotClient.js';
10
+ import type { SessionOptions } from '../../../src/shared/copilotClient.js';
11
+
12
+ describe('CopilotClient', () => {
13
+ it('createFakeCopilotClient returns a client', () => {
14
+ const client = createFakeCopilotClient([{ role: 'assistant', content: 'Hello!' }]);
15
+ expect(client).toBeDefined();
16
+ expect(typeof client.createSession).toBe('function');
17
+ });
18
+
19
+ it('fake client session sends messages and gets responses', async () => {
20
+ const client = createFakeCopilotClient([{ role: 'assistant', content: 'I understand.' }]);
21
+ const session = await client.createSession({ systemPrompt: 'You are a facilitator.' });
22
+ const events: string[] = [];
23
+ for await (const event of session.send({ role: 'user', content: 'Tell me about AI' })) {
24
+ if (event.type === 'TextDelta') {
25
+ events.push(event.text);
26
+ }
27
+ }
28
+ expect(events.join('')).toBe('I understand.');
29
+ });
30
+
31
+ it('fake client supports multi-turn conversation', async () => {
32
+ const client = createFakeCopilotClient([
33
+ { role: 'assistant', content: 'First response' },
34
+ { role: 'assistant', content: 'Second response' },
35
+ ]);
36
+ const session = await client.createSession({ systemPrompt: 'Test' });
37
+
38
+ const first: string[] = [];
39
+ for await (const event of session.send({ role: 'user', content: 'Turn 1' })) {
40
+ if (event.type === 'TextDelta') first.push(event.text);
41
+ }
42
+ expect(first.join('')).toBe('First response');
43
+
44
+ const second: string[] = [];
45
+ for await (const event of session.send({ role: 'user', content: 'Turn 2' })) {
46
+ if (event.type === 'TextDelta') second.push(event.text);
47
+ }
48
+ expect(second.join('')).toBe('Second response');
49
+ });
50
+
51
+ it('fake client can simulate tool calls', async () => {
52
+ const client = createFakeCopilotClient(
53
+ [{ role: 'assistant', content: 'Let me search for that.' }],
54
+ {
55
+ tools: [{ name: 'web.search', description: 'Search the web' }],
56
+ },
57
+ );
58
+ const session = await client.createSession({ systemPrompt: 'Test' });
59
+ expect(session).toBeDefined();
60
+ });
61
+
62
+ // ── T050: SessionOptions.mcpServers forwarding ──────────────────────────
63
+
64
+ describe('SessionOptions.mcpServers (T050)', () => {
65
+ it('accepts mcpServers as a Record and forwards to createSession', async () => {
66
+ const mcpServers: SessionOptions['mcpServers'] = {
67
+ github: {
68
+ type: 'http',
69
+ url: 'https://api.githubcopilot.com/mcp/',
70
+ tools: ['search_repositories'],
71
+ timeout: 60_000,
72
+ },
73
+ context7: {
74
+ type: 'stdio',
75
+ command: 'npx',
76
+ args: ['-y', '@upstash/context7-mcp'],
77
+ tools: ['resolve-library-id', 'query-docs'],
78
+ },
79
+ };
80
+
81
+ // Spy on createSession to capture the options passed
82
+ const createSessionSpy = vi.fn();
83
+ const client = createFakeCopilotClient([{ role: 'assistant', content: 'OK' }]);
84
+ const originalCreateSession = client.createSession.bind(client);
85
+ client.createSession = async (opts: SessionOptions) => {
86
+ createSessionSpy(opts);
87
+ return originalCreateSession(opts);
88
+ };
89
+
90
+ const session = await client.createSession({
91
+ systemPrompt: 'Test',
92
+ mcpServers,
93
+ });
94
+
95
+ expect(createSessionSpy).toHaveBeenCalledWith(
96
+ expect.objectContaining({
97
+ mcpServers: expect.objectContaining({
98
+ github: expect.objectContaining({
99
+ type: 'http',
100
+ url: 'https://api.githubcopilot.com/mcp/',
101
+ }),
102
+ context7: expect.objectContaining({ type: 'stdio', command: 'npx' }),
103
+ }),
104
+ }),
105
+ );
106
+ expect(session).toBeDefined();
107
+ });
108
+
109
+ it('does not include mcpServers when omitted', async () => {
110
+ const createSessionSpy = vi.fn();
111
+ const client = createFakeCopilotClient([{ role: 'assistant', content: 'OK' }]);
112
+ const originalCreateSession = client.createSession.bind(client);
113
+ client.createSession = async (opts: SessionOptions) => {
114
+ createSessionSpy(opts);
115
+ return originalCreateSession(opts);
116
+ };
117
+
118
+ await client.createSession({ systemPrompt: 'Test' });
119
+
120
+ const passedOpts = createSessionSpy.mock.calls[0][0] as SessionOptions;
121
+ expect(passedOpts.mcpServers).toBeUndefined();
122
+ });
123
+
124
+ it('does not include mcpServers when empty object', async () => {
125
+ const createSessionSpy = vi.fn();
126
+ const client = createFakeCopilotClient([{ role: 'assistant', content: 'OK' }]);
127
+ const originalCreateSession = client.createSession.bind(client);
128
+ client.createSession = async (opts: SessionOptions) => {
129
+ createSessionSpy(opts);
130
+ return originalCreateSession(opts);
131
+ };
132
+
133
+ await client.createSession({ systemPrompt: 'Test', mcpServers: {} });
134
+
135
+ const passedOpts = createSessionSpy.mock.calls[0][0] as SessionOptions;
136
+ expect(passedOpts.mcpServers).toEqual({});
137
+ });
138
+ });
139
+
140
+ // ── T052: SDK hooks integration ─────────────────────────────────────────
141
+
142
+ describe('SessionOptions hooks (T052)', () => {
143
+ it('accepts a hooks object with onPreToolUse and onPostToolUse callbacks', async () => {
144
+ const hooks: SessionOptions['hooks'] = {
145
+ onPreToolUse: vi.fn(),
146
+ onPostToolUse: vi.fn(),
147
+ onErrorOccurred: vi.fn(),
148
+ };
149
+
150
+ const createSessionSpy = vi.fn();
151
+ const client = createFakeCopilotClient([{ role: 'assistant', content: 'OK' }]);
152
+ const originalCreateSession = client.createSession.bind(client);
153
+ client.createSession = async (opts: SessionOptions) => {
154
+ createSessionSpy(opts);
155
+ return originalCreateSession(opts);
156
+ };
157
+
158
+ await client.createSession({
159
+ systemPrompt: 'Test',
160
+ hooks,
161
+ });
162
+
163
+ const passedOpts = createSessionSpy.mock.calls[0][0] as SessionOptions;
164
+ expect(passedOpts.hooks).toBeDefined();
165
+ expect(passedOpts.hooks!.onPreToolUse).toBe(hooks!.onPreToolUse);
166
+ expect(passedOpts.hooks!.onPostToolUse).toBe(hooks!.onPostToolUse);
167
+ expect(passedOpts.hooks!.onErrorOccurred).toBe(hooks!.onErrorOccurred);
168
+ });
169
+
170
+ it('does not include hooks when omitted', async () => {
171
+ const createSessionSpy = vi.fn();
172
+ const client = createFakeCopilotClient([{ role: 'assistant', content: 'OK' }]);
173
+ const originalCreateSession = client.createSession.bind(client);
174
+ client.createSession = async (opts: SessionOptions) => {
175
+ createSessionSpy(opts);
176
+ return originalCreateSession(opts);
177
+ };
178
+
179
+ await client.createSession({ systemPrompt: 'Test' });
180
+
181
+ const passedOpts = createSessionSpy.mock.calls[0][0] as SessionOptions;
182
+ expect(passedOpts.hooks).toBeUndefined();
183
+ });
184
+ });
185
+ });
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Error classifier tests (T055).
3
+ *
4
+ * Validates centralized error classification and user-facing messages:
5
+ * - Maps known error codes to categories
6
+ * - Provides actionable messages
7
+ * - Handles unknown errors gracefully
8
+ * - Supports MCP, network, auth, and session errors
9
+ */
10
+ import { describe, it, expect } from 'vitest';
11
+
12
+ import {
13
+ classifyError,
14
+ toUserMessage,
15
+ type ErrorClassification,
16
+ } from '../../../src/shared/errorClassifier.js';
17
+
18
+ describe('errorClassifier', () => {
19
+ describe('classifyError', () => {
20
+ it('classifies ECONNREFUSED as connection error', () => {
21
+ const err = Object.assign(new Error('connect ECONNREFUSED'), { code: 'ECONNREFUSED' });
22
+ const result = classifyError(err);
23
+ expect(result.category).toBe('connection');
24
+ expect(result.recoverable).toBe(true);
25
+ });
26
+
27
+ it('classifies ENOTFOUND as dns error', () => {
28
+ const err = Object.assign(new Error('getaddrinfo ENOTFOUND'), { code: 'ENOTFOUND' });
29
+ const result = classifyError(err);
30
+ expect(result.category).toBe('dns');
31
+ expect(result.recoverable).toBe(true);
32
+ });
33
+
34
+ it('classifies ETIMEDOUT as timeout error', () => {
35
+ const err = Object.assign(new Error('connect ETIMEDOUT'), { code: 'ETIMEDOUT' });
36
+ const result = classifyError(err);
37
+ expect(result.category).toBe('timeout');
38
+ expect(result.recoverable).toBe(true);
39
+ });
40
+
41
+ it('classifies 401 as auth error', () => {
42
+ const err = Object.assign(new Error('Unauthorized'), { statusCode: 401 });
43
+ const result = classifyError(err);
44
+ expect(result.category).toBe('auth');
45
+ expect(result.recoverable).toBe(false);
46
+ });
47
+
48
+ it('classifies 403 as auth error', () => {
49
+ const err = Object.assign(new Error('Forbidden'), { statusCode: 403 });
50
+ const result = classifyError(err);
51
+ expect(result.category).toBe('auth');
52
+ });
53
+
54
+ it('classifies 429 as rate-limit error', () => {
55
+ const err = Object.assign(new Error('Too Many Requests'), { statusCode: 429 });
56
+ const result = classifyError(err);
57
+ expect(result.category).toBe('rate-limit');
58
+ expect(result.recoverable).toBe(true);
59
+ });
60
+
61
+ it('classifies ENOENT as not-found error', () => {
62
+ const err = Object.assign(new Error('ENOENT: no such file'), { code: 'ENOENT' });
63
+ const result = classifyError(err);
64
+ expect(result.category).toBe('not-found');
65
+ });
66
+
67
+ it('classifies session validation errors', () => {
68
+ const err = new Error('Invalid session: missing required field');
69
+ err.name = 'ZodError';
70
+ const result = classifyError(err);
71
+ expect(result.category).toBe('validation');
72
+ });
73
+
74
+ it('classifies unknown errors as internal', () => {
75
+ const err = new Error('Something unexpected happened');
76
+ const result = classifyError(err);
77
+ expect(result.category).toBe('internal');
78
+ expect(result.recoverable).toBe(false);
79
+ });
80
+
81
+ it('classifies MCP-related errors', () => {
82
+ const err = new Error('MCP server workiq failed to start');
83
+ const result = classifyError(err);
84
+ expect(result.category).toBe('mcp');
85
+ });
86
+
87
+ it('handles non-Error objects', () => {
88
+ const result = classifyError('string error');
89
+ expect(result.category).toBe('internal');
90
+ expect(result.originalError).toBe('string error');
91
+ });
92
+ });
93
+
94
+ describe('toUserMessage', () => {
95
+ it('returns actionable message for connection errors', () => {
96
+ const classification: ErrorClassification = {
97
+ category: 'connection',
98
+ recoverable: true,
99
+ message: 'Connection refused',
100
+ originalError: new Error('ECONNREFUSED'),
101
+ };
102
+ const msg = toUserMessage(classification);
103
+ expect(msg).toContain('connection');
104
+ });
105
+
106
+ it('returns actionable message for auth errors', () => {
107
+ const classification: ErrorClassification = {
108
+ category: 'auth',
109
+ recoverable: false,
110
+ message: 'Unauthorized',
111
+ originalError: new Error('401'),
112
+ };
113
+ const msg = toUserMessage(classification);
114
+ expect(msg).toContain('auth');
115
+ });
116
+
117
+ it('returns actionable message for timeout errors', () => {
118
+ const classification: ErrorClassification = {
119
+ category: 'timeout',
120
+ recoverable: true,
121
+ message: 'Request timed out',
122
+ originalError: new Error('ETIMEDOUT'),
123
+ };
124
+ const msg = toUserMessage(classification);
125
+ expect(msg).toContain('timed out');
126
+ });
127
+
128
+ it('returns generic message for internal errors', () => {
129
+ const classification: ErrorClassification = {
130
+ category: 'internal',
131
+ recoverable: false,
132
+ message: 'Unexpected error',
133
+ originalError: new Error('kaboom'),
134
+ };
135
+ const msg = toUserMessage(classification);
136
+ expect(msg.length).toBeGreaterThan(0);
137
+ });
138
+
139
+ it('does not include stack traces', () => {
140
+ const err = new Error('test');
141
+ err.stack = 'Error: test\n at something.js:1:2';
142
+ const classification: ErrorClassification = {
143
+ category: 'internal',
144
+ recoverable: false,
145
+ message: 'test',
146
+ originalError: err,
147
+ };
148
+ const msg = toUserMessage(classification);
149
+ expect(msg).not.toContain('at something');
150
+ });
151
+ });
152
+ });
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Unit tests for event model.
3
+ */
4
+ import { describe, it, expect } from 'vitest';
5
+
6
+ import {
7
+ createTextDeltaEvent,
8
+ createActivityEvent,
9
+ createToolCallEvent,
10
+ createToolResultEvent,
11
+ createPhaseChangedEvent,
12
+ createErrorEvent,
13
+ type SofiaEvent,
14
+ } from '../../../src/shared/events.js';
15
+
16
+ describe('event model', () => {
17
+ it('createTextDeltaEvent creates a valid event', () => {
18
+ const event = createTextDeltaEvent('Hello ');
19
+ expect(event.type).toBe('TextDelta');
20
+ expect(event.text).toBe('Hello ');
21
+ expect(event.timestamp).toBeTruthy();
22
+ });
23
+
24
+ it('createActivityEvent creates a valid event', () => {
25
+ const event = createActivityEvent('Connecting to MCP...');
26
+ expect(event.type).toBe('Activity');
27
+ expect(event.message).toBe('Connecting to MCP...');
28
+ });
29
+
30
+ it('createToolCallEvent includes tool name and args', () => {
31
+ const event = createToolCallEvent('web.search', { query: 'AI trends' });
32
+ expect(event.type).toBe('ToolCall');
33
+ expect(event.toolName).toBe('web.search');
34
+ expect(event.args).toEqual({ query: 'AI trends' });
35
+ });
36
+
37
+ it('createToolResultEvent includes tool name and result', () => {
38
+ const event = createToolResultEvent('web.search', { results: [] });
39
+ expect(event.type).toBe('ToolResult');
40
+ expect(event.toolName).toBe('web.search');
41
+ });
42
+
43
+ it('createPhaseChangedEvent includes old and new phase', () => {
44
+ const event = createPhaseChangedEvent('Discover', 'Ideate');
45
+ expect(event.type).toBe('PhaseChanged');
46
+ expect(event.fromPhase).toBe('Discover');
47
+ expect(event.toPhase).toBe('Ideate');
48
+ });
49
+
50
+ it('createErrorEvent includes code and message', () => {
51
+ const event = createErrorEvent('MCP_TIMEOUT', 'WorkIQ timed out');
52
+ expect(event.type).toBe('Error');
53
+ expect(event.code).toBe('MCP_TIMEOUT');
54
+ expect(event.message).toBe('WorkIQ timed out');
55
+ });
56
+
57
+ it('all events have a timestamp', () => {
58
+ const events: SofiaEvent[] = [
59
+ createTextDeltaEvent('x'),
60
+ createActivityEvent('y'),
61
+ createToolCallEvent('t', {}),
62
+ createToolResultEvent('t', {}),
63
+ createPhaseChangedEvent('Discover', 'Ideate'),
64
+ createErrorEvent('E', 'msg'),
65
+ ];
66
+ for (const e of events) {
67
+ expect(e.timestamp).toBeTruthy();
68
+ expect(typeof e.timestamp).toBe('string');
69
+ }
70
+ });
71
+ });
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Unit tests for markdown renderer.
3
+ */
4
+ import { describe, it, expect } from 'vitest';
5
+
6
+ import { renderMarkdown } from '../../../src/shared/markdownRenderer.js';
7
+
8
+ describe('renderMarkdown', () => {
9
+ it('renders a heading', () => {
10
+ const output = renderMarkdown('# Hello World');
11
+ expect(output).toBeTruthy();
12
+ expect(output.length).toBeGreaterThan(0);
13
+ });
14
+
15
+ it('renders a list', () => {
16
+ const output = renderMarkdown('- One\n- Two\n- Three');
17
+ expect(output).toContain('One');
18
+ expect(output).toContain('Two');
19
+ });
20
+
21
+ it('renders a code block', () => {
22
+ const output = renderMarkdown('```json\n{"key": "value"}\n```');
23
+ expect(output).toContain('key');
24
+ });
25
+
26
+ it('returns plain text in non-TTY mode', () => {
27
+ const output = renderMarkdown('# Hello World', { isTTY: false });
28
+ // Should not contain ANSI codes in non-TTY mode
29
+ expect(output).toContain('Hello World');
30
+ });
31
+
32
+ it('handles empty string', () => {
33
+ const output = renderMarkdown('');
34
+ expect(output).toBe('');
35
+ });
36
+
37
+ it('handles raw Markdown preservation in non-TTY json mode', () => {
38
+ const output = renderMarkdown('# Title\n\nBody text', { isTTY: false, jsonMode: true });
39
+ // In json mode, return raw markdown
40
+ expect(output).toContain('# Title');
41
+ });
42
+ });
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Tests for markdownRenderer handling of partial/incremental chunks (T081).
3
+ *
4
+ * Verifies that renderMarkdown doesn't crash on partial markdown input
5
+ * (split headings, incomplete bold, partial tables).
6
+ */
7
+ import { describe, it, expect } from 'vitest';
8
+
9
+ import { renderMarkdown } from '../../../src/shared/markdownRenderer.js';
10
+
11
+ describe('renderMarkdown incremental chunk handling (T081)', () => {
12
+ it('handles a partial heading (no newline)', () => {
13
+ // During streaming, a heading might arrive without a trailing newline
14
+ const result = renderMarkdown('## Start of head', { isTTY: true });
15
+ expect(result).toBeTruthy();
16
+ expect(result).toContain('Start of head');
17
+ });
18
+
19
+ it('handles incomplete bold syntax', () => {
20
+ // Bold started but not closed
21
+ const result = renderMarkdown('Some **bold text without', { isTTY: true });
22
+ expect(result).toBeTruthy();
23
+ expect(result).toContain('bold text without');
24
+ });
25
+
26
+ it('handles incomplete code block', () => {
27
+ // Code block opened but not closed
28
+ const result = renderMarkdown('```typescript\nconst x = 1;\n', { isTTY: true });
29
+ expect(result).toBeTruthy();
30
+ expect(result).toContain('const x = 1');
31
+ });
32
+
33
+ it('handles partial table row', () => {
34
+ const result = renderMarkdown('| Column 1 | Column 2 |\n| ----', { isTTY: true });
35
+ expect(result).toBeTruthy();
36
+ });
37
+
38
+ it('handles empty chunk', () => {
39
+ const result = renderMarkdown('', { isTTY: true });
40
+ expect(result).toBe('');
41
+ });
42
+
43
+ it('handles single character chunk', () => {
44
+ const result = renderMarkdown('H', { isTTY: true });
45
+ expect(result).toBeTruthy();
46
+ });
47
+
48
+ it('handles newline-only chunk without throwing', () => {
49
+ const result = renderMarkdown('\n', { isTTY: true });
50
+ // A newline-only chunk may produce empty string; it must not throw
51
+ expect(typeof result).toBe('string');
52
+ });
53
+
54
+ it('handles incomplete link syntax', () => {
55
+ const result = renderMarkdown('Click [here](http://example', { isTTY: true });
56
+ expect(result).toBeTruthy();
57
+ });
58
+
59
+ it('handles partial list item', () => {
60
+ const result = renderMarkdown('- Item one\n- Item tw', { isTTY: true });
61
+ expect(result).toBeTruthy();
62
+ expect(result).toContain('Item one');
63
+ });
64
+
65
+ it('renders complete markdown correctly in TTY mode', () => {
66
+ const md = '## Title\n\n- **Bold item**\n- Regular item\n\n```js\nconst x = 1;\n```\n';
67
+ const result = renderMarkdown(md, { isTTY: true });
68
+ expect(result).toContain('Title');
69
+ expect(result).toContain('Bold item');
70
+ expect(result).toContain('Regular item');
71
+ });
72
+
73
+ it('returns raw markdown in JSON mode regardless of content', () => {
74
+ const md = '## Partial **heading';
75
+ const result = renderMarkdown(md, { isTTY: true, jsonMode: true });
76
+ expect(result).toBe(md);
77
+ });
78
+
79
+ it('handles non-TTY mode with partial markdown', () => {
80
+ const result = renderMarkdown('**incomplete bold', { isTTY: false });
81
+ expect(result).toBeTruthy();
82
+ });
83
+ });
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Unit tests for table renderer.
3
+ */
4
+ import { describe, it, expect } from 'vitest';
5
+
6
+ import { renderTable } from '../../../src/shared/tableRenderer.js';
7
+
8
+ describe('renderTable', () => {
9
+ it('renders a simple table', () => {
10
+ const output = renderTable({
11
+ head: ['Name', 'Score'],
12
+ rows: [
13
+ ['Idea A', '85'],
14
+ ['Idea B', '72'],
15
+ ],
16
+ });
17
+ expect(output).toContain('Name');
18
+ expect(output).toContain('Score');
19
+ expect(output).toContain('Idea A');
20
+ expect(output).toContain('85');
21
+ });
22
+
23
+ it('handles empty rows', () => {
24
+ const output = renderTable({
25
+ head: ['Col1'],
26
+ rows: [],
27
+ });
28
+ expect(output).toContain('Col1');
29
+ });
30
+
31
+ it('renders without headers', () => {
32
+ const output = renderTable({
33
+ rows: [['a', 'b'], ['c', 'd']],
34
+ });
35
+ expect(output).toContain('a');
36
+ expect(output).toContain('d');
37
+ });
38
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
+ "esModuleInterop": true,
7
+ "strict": true,
8
+ "skipLibCheck": true,
9
+ "forceConsistentCasingInFileNames": true,
10
+ "resolveJsonModule": true,
11
+ "rootDir": "./",
12
+ "outDir": "dist",
13
+ "baseUrl": "./",
14
+ "types": ["node", "vitest"],
15
+ "paths": {
16
+ "zod": ["src/vendor/zod"]
17
+ }
18
+ },
19
+ "include": ["src/**/*", "tests/**/*"]
20
+ }
@@ -0,0 +1,15 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ globals: true,
6
+ setupFiles: ['tests/setup/loadEnv.ts'],
7
+ include: ['tests/**/*.{test,spec}.ts'],
8
+ exclude: ['tests/live/**', 'tests/fixtures/**'],
9
+ coverage: {
10
+ enabled: true,
11
+ provider: 'v8',
12
+ },
13
+ testTimeout: 10_000, // Default timeout for tests (can be overridden per test)
14
+ },
15
+ });
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Vitest config for live (slow) tests that hit the real Copilot SDK.
3
+ * Run with: npm run test:live
4
+ */
5
+ import { defineConfig } from 'vitest/config';
6
+
7
+ export default defineConfig({
8
+ test: {
9
+ globals: true,
10
+ setupFiles: ['tests/setup/loadEnv.ts'],
11
+ include: ['tests/live/**/*.{test,spec}.ts'],
12
+ testTimeout: 120_000,
13
+ hookTimeout: 60_000,
14
+ coverage: {
15
+ enabled: true,
16
+ provider: 'v8',
17
+ },
18
+ },
19
+ });