macro-agent 0.1.0 → 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 (337) hide show
  1. package/.claude/settings.local.json +3 -1
  2. package/.sudocode/specs.jsonl +4 -0
  3. package/CLAUDE.md +16 -14
  4. package/README.md +11 -29
  5. package/dist/acp/macro-agent.d.ts +15 -0
  6. package/dist/acp/macro-agent.d.ts.map +1 -1
  7. package/dist/acp/macro-agent.js +131 -35
  8. package/dist/acp/macro-agent.js.map +1 -1
  9. package/dist/acp/types.d.ts +32 -1
  10. package/dist/acp/types.d.ts.map +1 -1
  11. package/dist/acp/types.js.map +1 -1
  12. package/dist/agent/agent-manager.d.ts +65 -1
  13. package/dist/agent/agent-manager.d.ts.map +1 -1
  14. package/dist/agent/agent-manager.js +464 -183
  15. package/dist/agent/agent-manager.js.map +1 -1
  16. package/dist/agent/types.d.ts +1 -1
  17. package/dist/agent/types.d.ts.map +1 -1
  18. package/dist/api/server.d.ts +3 -0
  19. package/dist/api/server.d.ts.map +1 -1
  20. package/dist/api/server.js +37 -6
  21. package/dist/api/server.js.map +1 -1
  22. package/dist/auth/index.d.ts +2 -0
  23. package/dist/auth/index.d.ts.map +1 -0
  24. package/dist/auth/index.js +2 -0
  25. package/dist/auth/index.js.map +1 -0
  26. package/dist/auth/token.d.ts +41 -0
  27. package/dist/auth/token.d.ts.map +1 -0
  28. package/dist/auth/token.js +73 -0
  29. package/dist/auth/token.js.map +1 -0
  30. package/dist/cli/acp.d.ts +2 -23
  31. package/dist/cli/acp.d.ts.map +1 -1
  32. package/dist/cli/acp.js +127 -61
  33. package/dist/cli/acp.js.map +1 -1
  34. package/dist/cli/index.js +147 -15
  35. package/dist/cli/index.js.map +1 -1
  36. package/dist/cli/mcp.d.ts +6 -0
  37. package/dist/cli/mcp.d.ts.map +1 -1
  38. package/dist/cli/mcp.js +268 -181
  39. package/dist/cli/mcp.js.map +1 -1
  40. package/dist/cli/parse-args.d.ts +20 -0
  41. package/dist/cli/parse-args.d.ts.map +1 -0
  42. package/dist/cli/parse-args.js +43 -0
  43. package/dist/cli/parse-args.js.map +1 -0
  44. package/dist/cli/stable-instance-id.d.ts +8 -0
  45. package/dist/cli/stable-instance-id.d.ts.map +1 -0
  46. package/dist/cli/stable-instance-id.js +14 -0
  47. package/dist/cli/stable-instance-id.js.map +1 -0
  48. package/dist/config/project-config.d.ts +74 -7
  49. package/dist/config/project-config.d.ts.map +1 -1
  50. package/dist/config/project-config.js +123 -20
  51. package/dist/config/project-config.js.map +1 -1
  52. package/dist/map/adapter/acp-over-map.d.ts +17 -0
  53. package/dist/map/adapter/acp-over-map.d.ts.map +1 -1
  54. package/dist/map/adapter/acp-over-map.js +384 -23
  55. package/dist/map/adapter/acp-over-map.js.map +1 -1
  56. package/dist/map/adapter/connection-manager.d.ts.map +1 -1
  57. package/dist/map/adapter/connection-manager.js +3 -0
  58. package/dist/map/adapter/connection-manager.js.map +1 -1
  59. package/dist/map/adapter/event-log.d.ts +87 -0
  60. package/dist/map/adapter/event-log.d.ts.map +1 -0
  61. package/dist/map/adapter/event-log.js +122 -0
  62. package/dist/map/adapter/event-log.js.map +1 -0
  63. package/dist/map/adapter/event-translator.js +6 -6
  64. package/dist/map/adapter/event-translator.js.map +1 -1
  65. package/dist/map/adapter/extensions/agent-lifecycle.d.ts +82 -0
  66. package/dist/map/adapter/extensions/agent-lifecycle.d.ts.map +1 -0
  67. package/dist/map/adapter/extensions/agent-lifecycle.js +164 -0
  68. package/dist/map/adapter/extensions/agent-lifecycle.js.map +1 -0
  69. package/dist/map/adapter/extensions/index.d.ts +10 -1
  70. package/dist/map/adapter/extensions/index.d.ts.map +1 -1
  71. package/dist/map/adapter/extensions/index.js +34 -0
  72. package/dist/map/adapter/extensions/index.js.map +1 -1
  73. package/dist/map/adapter/extensions/mcp-bridge.d.ts +57 -0
  74. package/dist/map/adapter/extensions/mcp-bridge.d.ts.map +1 -0
  75. package/dist/map/adapter/extensions/mcp-bridge.js +745 -0
  76. package/dist/map/adapter/extensions/mcp-bridge.js.map +1 -0
  77. package/dist/map/adapter/extensions/rename.d.ts +29 -0
  78. package/dist/map/adapter/extensions/rename.d.ts.map +1 -0
  79. package/dist/map/adapter/extensions/rename.js +49 -0
  80. package/dist/map/adapter/extensions/rename.js.map +1 -0
  81. package/dist/map/adapter/extensions/task.d.ts.map +1 -1
  82. package/dist/map/adapter/extensions/task.js +10 -0
  83. package/dist/map/adapter/extensions/task.js.map +1 -1
  84. package/dist/map/adapter/extensions/update-metadata.d.ts +29 -0
  85. package/dist/map/adapter/extensions/update-metadata.d.ts.map +1 -0
  86. package/dist/map/adapter/extensions/update-metadata.js +67 -0
  87. package/dist/map/adapter/extensions/update-metadata.js.map +1 -0
  88. package/dist/map/adapter/index.d.ts +2 -1
  89. package/dist/map/adapter/index.d.ts.map +1 -1
  90. package/dist/map/adapter/index.js +8 -2
  91. package/dist/map/adapter/index.js.map +1 -1
  92. package/dist/map/adapter/interface.d.ts +2 -0
  93. package/dist/map/adapter/interface.d.ts.map +1 -1
  94. package/dist/map/adapter/map-adapter.d.ts +3 -0
  95. package/dist/map/adapter/map-adapter.d.ts.map +1 -1
  96. package/dist/map/adapter/map-adapter.js +258 -35
  97. package/dist/map/adapter/map-adapter.js.map +1 -1
  98. package/dist/map/adapter/subscription-manager.d.ts.map +1 -1
  99. package/dist/map/adapter/subscription-manager.js +5 -1
  100. package/dist/map/adapter/subscription-manager.js.map +1 -1
  101. package/dist/map/adapter/types.d.ts +2 -0
  102. package/dist/map/adapter/types.d.ts.map +1 -1
  103. package/dist/mcp/map-client.d.ts +39 -0
  104. package/dist/mcp/map-client.d.ts.map +1 -0
  105. package/dist/mcp/map-client.js +129 -0
  106. package/dist/mcp/map-client.js.map +1 -0
  107. package/dist/mcp/mcp-server.d.ts +14 -0
  108. package/dist/mcp/mcp-server.d.ts.map +1 -1
  109. package/dist/mcp/mcp-server.js +113 -85
  110. package/dist/mcp/mcp-server.js.map +1 -1
  111. package/dist/mcp/types.d.ts +9 -1
  112. package/dist/mcp/types.d.ts.map +1 -1
  113. package/dist/mcp/types.js.map +1 -1
  114. package/dist/metrics/metrics.js +1 -1
  115. package/dist/metrics/metrics.js.map +1 -1
  116. package/dist/roles/capabilities.d.ts +3 -1
  117. package/dist/roles/capabilities.d.ts.map +1 -1
  118. package/dist/roles/capabilities.js +17 -7
  119. package/dist/roles/capabilities.js.map +1 -1
  120. package/dist/roles/config-loader.d.ts +6 -6
  121. package/dist/roles/config-loader.d.ts.map +1 -1
  122. package/dist/roles/config-loader.js +6 -6
  123. package/dist/roles/config-loader.js.map +1 -1
  124. package/dist/roles/registry.d.ts +2 -2
  125. package/dist/roles/registry.js +2 -2
  126. package/dist/server/combined-server.d.ts +20 -0
  127. package/dist/server/combined-server.d.ts.map +1 -1
  128. package/dist/server/combined-server.js +107 -8
  129. package/dist/server/combined-server.js.map +1 -1
  130. package/dist/store/event-store.d.ts +2 -1
  131. package/dist/store/event-store.d.ts.map +1 -1
  132. package/dist/store/event-store.js +69 -20
  133. package/dist/store/event-store.js.map +1 -1
  134. package/dist/store/types/agents.d.ts +18 -0
  135. package/dist/store/types/agents.d.ts.map +1 -1
  136. package/dist/store/types/events.d.ts +1 -1
  137. package/dist/store/types/events.d.ts.map +1 -1
  138. package/dist/task/backend/index.d.ts +47 -29
  139. package/dist/task/backend/index.d.ts.map +1 -1
  140. package/dist/task/backend/index.js +109 -71
  141. package/dist/task/backend/index.js.map +1 -1
  142. package/dist/task/backend/memory.d.ts +1 -0
  143. package/dist/task/backend/memory.d.ts.map +1 -1
  144. package/dist/task/backend/memory.js +3 -0
  145. package/dist/task/backend/memory.js.map +1 -1
  146. package/dist/task/backend/opentasks/backend.d.ts +140 -0
  147. package/dist/task/backend/opentasks/backend.d.ts.map +1 -0
  148. package/dist/task/backend/opentasks/backend.js +1023 -0
  149. package/dist/task/backend/opentasks/backend.js.map +1 -0
  150. package/dist/task/backend/opentasks/client.d.ts +337 -0
  151. package/dist/task/backend/opentasks/client.d.ts.map +1 -0
  152. package/dist/task/backend/opentasks/client.js +225 -0
  153. package/dist/task/backend/opentasks/client.js.map +1 -0
  154. package/dist/task/backend/opentasks/daemon-manager.d.ts +89 -0
  155. package/dist/task/backend/opentasks/daemon-manager.d.ts.map +1 -0
  156. package/dist/task/backend/opentasks/daemon-manager.js +195 -0
  157. package/dist/task/backend/opentasks/daemon-manager.js.map +1 -0
  158. package/dist/task/backend/opentasks/index.d.ts +21 -0
  159. package/dist/task/backend/opentasks/index.d.ts.map +1 -0
  160. package/dist/task/backend/opentasks/index.js +21 -0
  161. package/dist/task/backend/opentasks/index.js.map +1 -0
  162. package/dist/task/backend/opentasks/mapping.d.ts +48 -0
  163. package/dist/task/backend/opentasks/mapping.d.ts.map +1 -0
  164. package/dist/task/backend/opentasks/mapping.js +77 -0
  165. package/dist/task/backend/opentasks/mapping.js.map +1 -0
  166. package/dist/task/backend/types.d.ts +33 -53
  167. package/dist/task/backend/types.d.ts.map +1 -1
  168. package/dist/task/backend/types.js +7 -11
  169. package/dist/task/backend/types.js.map +1 -1
  170. package/dist/task/backend/unified-tool-provider.d.ts +57 -0
  171. package/dist/task/backend/unified-tool-provider.d.ts.map +1 -0
  172. package/dist/task/backend/unified-tool-provider.js +623 -0
  173. package/dist/task/backend/unified-tool-provider.js.map +1 -0
  174. package/dist/teams/team-loader.d.ts +2 -2
  175. package/dist/teams/team-loader.js +3 -3
  176. package/dist/teams/team-loader.js.map +1 -1
  177. package/dist/teams/team-runtime.d.ts.map +1 -1
  178. package/dist/teams/team-runtime.js +2 -0
  179. package/dist/teams/team-runtime.js.map +1 -1
  180. package/docs/architecture.md +7 -6
  181. package/docs/configuration.md +26 -62
  182. package/docs/implementation-details.md +5 -5
  183. package/docs/implementation-summary.md +17 -17
  184. package/docs/plan-self-driving-support.md +4 -4
  185. package/docs/spec-self-driving-support.md +10 -10
  186. package/docs/team-templates.md +2 -2
  187. package/docs/teams.md +3 -3
  188. package/docs/troubleshooting.md +10 -11
  189. package/package.json +6 -4
  190. package/src/__tests__/e2e/agent-spawn-visibility.e2e.test.ts +761 -0
  191. package/src/__tests__/e2e/full-agent-conflict-resolution.e2e.test.ts +2 -2
  192. package/src/__tests__/e2e/mcp-thin-client-bridge.e2e.test.ts +304 -0
  193. package/src/__tests__/e2e/mcp-tools-available.e2e.test.ts +324 -0
  194. package/src/__tests__/e2e/multi-agent.e2e.test.ts +5 -5
  195. package/src/__tests__/e2e/spawn-session-streaming.e2e.test.ts +563 -0
  196. package/src/acp/__tests__/integration.test.ts +56 -31
  197. package/src/acp/__tests__/macro-agent.test.ts +16 -7
  198. package/src/acp/macro-agent.ts +170 -36
  199. package/src/acp/types.ts +46 -1
  200. package/src/agent/__tests__/agent-manager.test.ts +228 -2
  201. package/src/agent/agent-manager.ts +714 -261
  202. package/src/agent/types.ts +3 -1
  203. package/src/api/server.ts +41 -7
  204. package/src/auth/__tests__/token.test.ts +100 -0
  205. package/src/auth/index.ts +1 -0
  206. package/src/auth/token.ts +82 -0
  207. package/src/cli/__tests__/acp.test.ts +1 -1
  208. package/src/cli/__tests__/stable-instance-id.test.ts +1 -1
  209. package/src/cli/acp.ts +130 -72
  210. package/src/cli/index.ts +120 -14
  211. package/src/cli/mcp.ts +311 -207
  212. package/src/cli/parse-args.ts +54 -0
  213. package/src/cli/stable-instance-id.ts +14 -0
  214. package/src/config/project-config.ts +190 -27
  215. package/src/lifecycle/__tests__/cascade-termination.test.ts +1 -1
  216. package/src/map/adapter/__tests__/acp-over-map-cancel.test.ts +22 -4
  217. package/src/map/adapter/__tests__/acp-over-map-getmodels.test.ts +355 -0
  218. package/src/map/adapter/__tests__/acp-over-map-history.test.ts +263 -0
  219. package/src/map/adapter/__tests__/acp-over-map-persistence.e2e.test.ts +1 -1
  220. package/src/map/adapter/__tests__/event-broadcast.test.ts +420 -0
  221. package/src/map/adapter/__tests__/event-log.test.ts +527 -0
  222. package/src/map/adapter/__tests__/event-translator.test.ts +3 -3
  223. package/src/map/adapter/__tests__/extensions.test.ts +408 -0
  224. package/src/map/adapter/__tests__/map-adapter.test.ts +99 -0
  225. package/src/map/adapter/__tests__/mcp-bridge.test.ts +1187 -0
  226. package/src/map/adapter/__tests__/multi-client-broadcast.test.ts +711 -0
  227. package/src/map/adapter/__tests__/websocket-integration.test.ts +218 -0
  228. package/src/map/adapter/acp-over-map.ts +678 -66
  229. package/src/map/adapter/connection-manager.ts +3 -0
  230. package/src/map/adapter/event-log.ts +208 -0
  231. package/src/map/adapter/event-translator.ts +6 -6
  232. package/src/map/adapter/extensions/agent-lifecycle.ts +267 -0
  233. package/src/map/adapter/extensions/index.ts +60 -0
  234. package/src/map/adapter/extensions/mcp-bridge.ts +995 -0
  235. package/src/map/adapter/extensions/task.ts +11 -0
  236. package/src/map/adapter/extensions/update-metadata.ts +126 -0
  237. package/src/map/adapter/index.ts +28 -0
  238. package/src/map/adapter/interface.ts +2 -0
  239. package/src/map/adapter/map-adapter.ts +312 -47
  240. package/src/map/adapter/subscription-manager.ts +5 -1
  241. package/src/map/adapter/types.ts +2 -0
  242. package/src/mcp/__tests__/map-client.test.ts +386 -0
  243. package/src/mcp/__tests__/mcp-server-thin-client.test.ts +368 -0
  244. package/src/mcp/__tests__/mcp-server.test.ts +100 -1
  245. package/src/mcp/map-client.ts +177 -0
  246. package/src/mcp/mcp-server.ts +191 -100
  247. package/src/mcp/types.ts +6 -1
  248. package/src/metrics/metrics.ts +1 -1
  249. package/src/monitor/__tests__/stale-agent-flow.integration.test.ts +1 -1
  250. package/src/roles/__tests__/config-loader.test.ts +7 -7
  251. package/src/roles/capabilities.ts +17 -7
  252. package/src/roles/config-loader.ts +6 -6
  253. package/src/roles/registry.ts +2 -2
  254. package/src/server/__tests__/combined-server.test.ts +94 -21
  255. package/src/server/combined-server.ts +189 -33
  256. package/src/steering/__tests__/steering-integration.test.ts +1 -1
  257. package/src/store/__tests__/event-store.test.ts +196 -1
  258. package/src/store/__tests__/instance.test.ts +3 -3
  259. package/src/store/event-store.ts +80 -21
  260. package/src/store/types/agents.ts +15 -0
  261. package/src/store/types/events.ts +1 -1
  262. package/src/task/backend/__tests__/create-task-backend.test.ts +225 -0
  263. package/src/task/backend/__tests__/e2e/unified-tool-provider-opentasks.e2e.test.ts +524 -0
  264. package/src/task/backend/__tests__/unified-tool-provider.test.ts +579 -0
  265. package/src/task/backend/index.ts +156 -106
  266. package/src/task/backend/memory.ts +4 -0
  267. package/src/task/backend/opentasks/__tests__/backend.test.ts +968 -0
  268. package/src/task/backend/opentasks/__tests__/daemon-manager.test.ts +406 -0
  269. package/src/task/backend/opentasks/__tests__/mapping.test.ts +84 -0
  270. package/src/task/backend/opentasks/__tests__/opentasks-backend.e2e.test.ts +1338 -0
  271. package/src/task/backend/opentasks/backend.ts +1323 -0
  272. package/src/task/backend/opentasks/client.ts +652 -0
  273. package/src/task/backend/opentasks/daemon-manager.ts +253 -0
  274. package/src/task/backend/opentasks/index.ts +69 -0
  275. package/src/task/backend/opentasks/mapping.ts +94 -0
  276. package/src/task/backend/types.ts +42 -66
  277. package/src/task/backend/unified-tool-provider.ts +779 -0
  278. package/src/teams/__tests__/cross-subsystem.integration.test.ts +1 -1
  279. package/src/teams/team-loader.ts +3 -3
  280. package/src/teams/team-runtime.ts +2 -0
  281. package/test_fixtures/README.md +2 -3
  282. package/test_fixtures/fixtures/index.ts +0 -3
  283. package/test_fixtures/fixtures/projects/project-with-specs.ts +7 -149
  284. package/test_fixtures/fixtures/repos/index.ts +1 -3
  285. package/test_fixtures/fixtures/repos/temp-repo-factory.ts +0 -116
  286. package/test_fixtures/fixtures/repos/types.ts +0 -11
  287. package/test_fixtures/harness/__tests__/fixtures.test.ts +10 -102
  288. package/test_fixtures/harness/__tests__/temp-repo-and-simulator.test.ts +0 -33
  289. package/test_fixtures/harness/simulator/agent-simulator.ts +4 -4
  290. package/vitest.config.ts +1 -1
  291. package/vitest.e2e.config.ts +1 -1
  292. package/vitest.setup.ts +1 -30
  293. package/.macro-agent/teams/self-driving/prompts/grinder.md +0 -27
  294. package/.macro-agent/teams/self-driving/prompts/judge.md +0 -27
  295. package/.macro-agent/teams/self-driving/prompts/planner.md +0 -33
  296. package/.macro-agent/teams/self-driving/roles/grinder.yaml +0 -17
  297. package/.macro-agent/teams/self-driving/roles/judge.yaml +0 -24
  298. package/.macro-agent/teams/self-driving/roles/planner.yaml +0 -18
  299. package/.macro-agent/teams/self-driving/team.yaml +0 -103
  300. package/.macro-agent/teams/structured/prompts/developer.md +0 -26
  301. package/.macro-agent/teams/structured/prompts/lead.md +0 -25
  302. package/.macro-agent/teams/structured/prompts/reviewer.md +0 -24
  303. package/.macro-agent/teams/structured/roles/developer.yaml +0 -12
  304. package/.macro-agent/teams/structured/roles/lead.yaml +0 -11
  305. package/.macro-agent/teams/structured/roles/reviewer.yaml +0 -19
  306. package/.macro-agent/teams/structured/team.yaml +0 -89
  307. package/docs/sudocode-integration.md +0 -383
  308. package/src/task/backend/__tests__/backend-parity.test.ts +0 -451
  309. package/src/task/backend/__tests__/tool-provider-edge-cases.test.ts +0 -430
  310. package/src/task/backend/__tests__/tool-provider.test.ts +0 -983
  311. package/src/task/backend/sudocode/__tests__/backend-edge-cases.test.ts +0 -575
  312. package/src/task/backend/sudocode/__tests__/backend.test.ts +0 -1194
  313. package/src/task/backend/sudocode/__tests__/client-integration.test.ts +0 -418
  314. package/src/task/backend/sudocode/__tests__/client.test.ts +0 -345
  315. package/src/task/backend/sudocode/__tests__/e2e/backend.e2e.test.ts +0 -753
  316. package/src/task/backend/sudocode/__tests__/e2e/server-client.e2e.test.ts +0 -680
  317. package/src/task/backend/sudocode/__tests__/e2e-workflow.test.ts +0 -666
  318. package/src/task/backend/sudocode/__tests__/integration/standalone-client.integration.test.ts +0 -396
  319. package/src/task/backend/sudocode/__tests__/integration/sudocode-cli.integration.test.ts +0 -328
  320. package/src/task/backend/sudocode/__tests__/integration/test-utils.ts +0 -175
  321. package/src/task/backend/sudocode/__tests__/mapping-edge-cases.test.ts +0 -265
  322. package/src/task/backend/sudocode/__tests__/server-client.test.ts +0 -675
  323. package/src/task/backend/sudocode/__tests__/sync-policy-edge-cases.test.ts +0 -521
  324. package/src/task/backend/sudocode/__tests__/sync-policy.test.ts +0 -519
  325. package/src/task/backend/sudocode/__tests__/tools.test.ts +0 -471
  326. package/src/task/backend/sudocode/backend.ts +0 -1237
  327. package/src/task/backend/sudocode/client.ts +0 -515
  328. package/src/task/backend/sudocode/index.ts +0 -120
  329. package/src/task/backend/sudocode/mapping.ts +0 -93
  330. package/src/task/backend/sudocode/server-client.ts +0 -522
  331. package/src/task/backend/sudocode/standalone-client.ts +0 -623
  332. package/src/task/backend/sudocode/sync-policy.ts +0 -387
  333. package/src/task/backend/sudocode/tools.ts +0 -896
  334. package/src/task/backend/tool-provider.ts +0 -506
  335. package/test_fixtures/fixtures/sudocode/index.ts +0 -29
  336. package/test_fixtures/fixtures/sudocode/issues.ts +0 -185
  337. package/test_fixtures/fixtures/sudocode/specs.ts +0 -159
@@ -1,418 +0,0 @@
1
- /**
2
- * SudocodeClient Integration Tests
3
- *
4
- * Tests for client integration edge cases and potential bugs.
5
- *
6
- * @module task/backend/sudocode/__tests__/client-integration.test
7
- * @see s-8472 Pluggable Task Backend Integration
8
- * @see s-1zcx Multi-Agent Orchestration Testing Strategy
9
- */
10
-
11
- import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
12
- import { mkdtempSync, rmSync, existsSync } from "fs";
13
- import { tmpdir } from "os";
14
- import { join } from "path";
15
- import {
16
- checkServerHealth,
17
- createSudocodeClient,
18
- type SudocodeClientConfig,
19
- type IssueChangeEvent,
20
- } from "../client.js";
21
- import { createStandaloneClient, StandaloneClient } from "../standalone-client.js";
22
-
23
- // Mock fetch for server health checks
24
- const mockFetch = vi.fn();
25
- vi.stubGlobal("fetch", mockFetch);
26
-
27
- // Mock WebSocket
28
- class MockWebSocket {
29
- static instances: MockWebSocket[] = [];
30
- onopen: (() => void) | null = null;
31
- onclose: (() => void) | null = null;
32
- onerror: ((error: unknown) => void) | null = null;
33
- onmessage: ((event: { data: string }) => void) | null = null;
34
- readyState = 0;
35
-
36
- constructor(public url: string) {
37
- MockWebSocket.instances.push(this);
38
- setTimeout(() => {
39
- this.readyState = 1;
40
- if (this.onopen) this.onopen();
41
- }, 0);
42
- }
43
-
44
- close() {
45
- this.readyState = 3;
46
- if (this.onclose) this.onclose();
47
- }
48
-
49
- send(_data: string) {
50
- // Mock send
51
- }
52
-
53
- static clearInstances() {
54
- MockWebSocket.instances = [];
55
- }
56
-
57
- static simulateMessage(data: unknown) {
58
- for (const ws of MockWebSocket.instances) {
59
- if (ws.onmessage) {
60
- ws.onmessage({ data: JSON.stringify(data) });
61
- }
62
- }
63
- }
64
- }
65
-
66
- vi.stubGlobal("WebSocket", MockWebSocket);
67
-
68
- describe("Client Integration", () => {
69
- let tmpDir: string;
70
-
71
- beforeEach(() => {
72
- vi.clearAllMocks();
73
- MockWebSocket.clearInstances();
74
- tmpDir = mkdtempSync(join(tmpdir(), "sudocode-client-test-"));
75
- });
76
-
77
- afterEach(() => {
78
- vi.restoreAllMocks();
79
- if (existsSync(tmpDir)) {
80
- rmSync(tmpDir, { recursive: true, force: true });
81
- }
82
- });
83
-
84
- describe("StandaloneClient initialization", () => {
85
- it("should create .sudocode directory if it doesn't exist", async () => {
86
- const client = await createStandaloneClient({ projectPath: tmpDir });
87
-
88
- try {
89
- expect(existsSync(join(tmpDir, ".sudocode"))).toBe(true);
90
- expect(existsSync(join(tmpDir, ".sudocode", "cache.db"))).toBe(true);
91
- } finally {
92
- client.close();
93
- }
94
- });
95
-
96
- it("should throw if init is not called on raw StandaloneClient", async () => {
97
- const client = new StandaloneClient({ projectPath: tmpDir });
98
-
99
- // Operations should fail before init
100
- await expect(client.getIssue("i-test")).rejects.toThrow("not initialized");
101
-
102
- client.close();
103
- });
104
-
105
- it("should handle double init gracefully", async () => {
106
- const client = new StandaloneClient({ projectPath: tmpDir });
107
- await client.init();
108
- await client.init(); // Second init should be a no-op
109
-
110
- expect(client.isReady()).toBe(true);
111
- client.close();
112
- });
113
-
114
- it("should handle close before init", () => {
115
- const client = new StandaloneClient({ projectPath: tmpDir });
116
- // Should not throw
117
- expect(() => client.close()).not.toThrow();
118
- expect(client.isReady()).toBe(false);
119
- });
120
- });
121
-
122
- describe("StandaloneClient issue operations", () => {
123
- let client: StandaloneClient;
124
-
125
- beforeEach(async () => {
126
- client = await createStandaloneClient({ projectPath: tmpDir });
127
- });
128
-
129
- afterEach(() => {
130
- client.close();
131
- });
132
-
133
- it("should return null for non-existent issue", async () => {
134
- const issue = await client.getIssue("i-nonexistent");
135
- expect(issue).toBeNull();
136
- });
137
-
138
- it("should list issues with empty database", async () => {
139
- const issues = await client.listIssues();
140
- expect(issues).toEqual([]);
141
- });
142
-
143
- it("should list ready issues with empty database", async () => {
144
- const ready = await client.getReadyIssues();
145
- expect(ready).toEqual([]);
146
- });
147
-
148
- it("should throw when updating non-existent issue", async () => {
149
- await expect(
150
- client.updateIssue("i-nonexistent", { title: "Updated" })
151
- ).rejects.toThrow();
152
- });
153
- });
154
-
155
- describe("StandaloneClient spec operations", () => {
156
- let client: StandaloneClient;
157
-
158
- beforeEach(async () => {
159
- client = await createStandaloneClient({ projectPath: tmpDir });
160
- });
161
-
162
- afterEach(() => {
163
- client.close();
164
- });
165
-
166
- it("should return null for non-existent spec", async () => {
167
- const spec = await client.getSpec("s-nonexistent");
168
- expect(spec).toBeNull();
169
- });
170
-
171
- it("should list specs with empty database", async () => {
172
- const specs = await client.listSpecs();
173
- expect(specs).toEqual([]);
174
- });
175
- });
176
-
177
- describe("StandaloneClient relationship operations", () => {
178
- let client: StandaloneClient;
179
-
180
- beforeEach(async () => {
181
- client = await createStandaloneClient({ projectPath: tmpDir });
182
- });
183
-
184
- afterEach(() => {
185
- client.close();
186
- });
187
-
188
- it("should return empty array for blockers of non-existent issue", async () => {
189
- const blockers = await client.getBlockers("i-nonexistent");
190
- expect(blockers).toEqual([]);
191
- });
192
-
193
- it("should return empty array for blocking of non-existent issue", async () => {
194
- const blocking = await client.getBlocking("i-nonexistent");
195
- expect(blocking).toEqual([]);
196
- });
197
-
198
- it("should infer entity type from ID prefix", async () => {
199
- // This tests the internal inferEntityType function indirectly
200
- // by trying to create links with different ID prefixes
201
-
202
- // Should not throw for valid prefixes (even if entities don't exist)
203
- // The operation may fail for other reasons but not for entity type inference
204
- await expect(
205
- client.createLink("i-from", "i-to", "blocks")
206
- ).rejects.toThrow(); // Will throw because entities don't exist, but not for type inference
207
-
208
- await expect(
209
- client.createLink("s-from", "i-to", "implements")
210
- ).rejects.toThrow();
211
- });
212
-
213
- it("should throw for invalid entity ID prefix", async () => {
214
- await expect(
215
- client.createLink("x-invalid", "i-to", "blocks")
216
- ).rejects.toThrow("Cannot infer entity type");
217
- });
218
- });
219
-
220
- describe("StandaloneClient event subscriptions", () => {
221
- let client: StandaloneClient;
222
-
223
- beforeEach(async () => {
224
- client = await createStandaloneClient({
225
- projectPath: tmpDir,
226
- pollInterval: 100, // Short interval for testing
227
- });
228
- });
229
-
230
- afterEach(() => {
231
- client.close();
232
- });
233
-
234
- it("should subscribe to global issue changes", async () => {
235
- const events: IssueChangeEvent[] = [];
236
- const unsubscribe = client.onIssueChange((event) => {
237
- events.push(event);
238
- });
239
-
240
- // Unsubscribe should work
241
- unsubscribe();
242
- expect(typeof unsubscribe).toBe("function");
243
- });
244
-
245
- it("should subscribe to specific issue changes", async () => {
246
- const events: IssueChangeEvent[] = [];
247
- const unsubscribe = client.onIssueChange("i-specific", (event) => {
248
- events.push(event);
249
- });
250
-
251
- unsubscribe();
252
- expect(typeof unsubscribe).toBe("function");
253
- });
254
-
255
- it("should stop polling when all subscribers unsubscribe", async () => {
256
- const unsub1 = client.onIssueChange(() => {});
257
- const unsub2 = client.onIssueChange(() => {});
258
-
259
- unsub1();
260
- // Polling should still be active (unsub2 still subscribed)
261
-
262
- unsub2();
263
- // Now polling should stop
264
- });
265
-
266
- it("should handle callback errors gracefully", async () => {
267
- const goodEvents: IssueChangeEvent[] = [];
268
-
269
- // Subscribe with a callback that throws
270
- client.onIssueChange(() => {
271
- throw new Error("Callback error");
272
- });
273
-
274
- // Subscribe with a good callback
275
- client.onIssueChange((event) => {
276
- goodEvents.push(event);
277
- });
278
-
279
- // Trigger an event manually (this tests error handling in emitChangeEvent)
280
- // The good callback should still receive events
281
- });
282
- });
283
-
284
- describe("Client factory auto mode", () => {
285
- it("should timeout properly when server is slow", async () => {
286
- // Make fetch return a promise that respects abort signal
287
- // fetch signature is fetch(url, options) where options.signal is the AbortSignal
288
- mockFetch.mockImplementation((_url: string, options?: { signal?: AbortSignal }) => {
289
- return new Promise((resolve, reject) => {
290
- const timeout = setTimeout(() => resolve({ ok: true }), 5000);
291
- if (options?.signal) {
292
- options.signal.addEventListener("abort", () => {
293
- clearTimeout(timeout);
294
- reject(new Error("Aborted"));
295
- });
296
- }
297
- });
298
- });
299
-
300
- const config: SudocodeClientConfig = {
301
- mode: "auto",
302
- projectPath: tmpDir,
303
- autoDetect: {
304
- timeout: 100, // Very short timeout
305
- preferManaged: true,
306
- },
307
- };
308
-
309
- const client = await createSudocodeClient(config);
310
-
311
- try {
312
- // Should have fallen back to standalone due to timeout
313
- expect(MockWebSocket.instances).toHaveLength(0);
314
- expect(client.isReady()).toBe(true);
315
- } finally {
316
- client.close();
317
- }
318
- });
319
-
320
- it("should use custom server URL for health check", async () => {
321
- mockFetch.mockResolvedValueOnce({ ok: true });
322
-
323
- const config: SudocodeClientConfig = {
324
- mode: "auto",
325
- autoDetect: {
326
- serverUrl: "http://custom-server:9999",
327
- },
328
- };
329
-
330
- const client = await createSudocodeClient(config);
331
-
332
- try {
333
- expect(mockFetch).toHaveBeenCalledWith(
334
- "http://custom-server:9999/health",
335
- expect.anything()
336
- );
337
- } finally {
338
- client.close();
339
- }
340
- });
341
- });
342
-
343
- describe("checkServerHealth edge cases", () => {
344
- it("should handle network errors", async () => {
345
- mockFetch.mockRejectedValueOnce(new Error("ECONNREFUSED"));
346
-
347
- const result = await checkServerHealth("http://localhost:3001");
348
- expect(result).toBe(false);
349
- });
350
-
351
- it("should handle 500 response", async () => {
352
- mockFetch.mockResolvedValueOnce({ ok: false, status: 500 });
353
-
354
- const result = await checkServerHealth("http://localhost:3001");
355
- expect(result).toBe(false);
356
- });
357
-
358
- it("should handle 404 response", async () => {
359
- mockFetch.mockResolvedValueOnce({ ok: false, status: 404 });
360
-
361
- const result = await checkServerHealth("http://localhost:3001");
362
- expect(result).toBe(false);
363
- });
364
- });
365
-
366
- describe("StandaloneClient feedback operations", () => {
367
- let client: StandaloneClient;
368
-
369
- beforeEach(async () => {
370
- client = await createStandaloneClient({ projectPath: tmpDir });
371
- });
372
-
373
- afterEach(() => {
374
- client.close();
375
- });
376
-
377
- it("should handle addFeedback gracefully even if not supported", async () => {
378
- // addFeedback should not throw even if the CLI doesn't support it
379
- await expect(
380
- client.addFeedback("i-from", "s-to", {
381
- type: "comment",
382
- content: "Test feedback",
383
- })
384
- ).resolves.not.toThrow();
385
- });
386
- });
387
-
388
- describe("StandaloneClient lifecycle", () => {
389
- it("should clean up resources on close", async () => {
390
- const client = await createStandaloneClient({
391
- projectPath: tmpDir,
392
- pollInterval: 100,
393
- });
394
-
395
- // Subscribe to start polling
396
- client.onIssueChange(() => {});
397
-
398
- expect(client.isReady()).toBe(true);
399
-
400
- client.close();
401
-
402
- expect(client.isReady()).toBe(false);
403
-
404
- // Operations should fail after close
405
- await expect(client.getIssue("i-test")).rejects.toThrow("not initialized");
406
- });
407
-
408
- it("should handle multiple close calls", async () => {
409
- const client = await createStandaloneClient({ projectPath: tmpDir });
410
-
411
- client.close();
412
- client.close(); // Should not throw
413
- client.close(); // Should not throw
414
-
415
- expect(client.isReady()).toBe(false);
416
- });
417
- });
418
- });