@polderlabs/bizar 6.2.5 → 6.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (329) hide show
  1. package/.claude/CLAUDE.md +15 -0
  2. package/.claude/agents/_shared/AGENT_BASELINE.md +168 -0
  3. package/.claude/agents/_shared/CLAUDE_TOOLS.md +412 -0
  4. package/.claude/agents/_shared/SKILLS.md +109 -0
  5. package/.claude/agents/agent-browser.md +80 -0
  6. package/.claude/agents/baldr.md +49 -0
  7. package/.claude/agents/forseti.md +51 -0
  8. package/.claude/agents/frigg.md +42 -0
  9. package/.claude/agents/heimdall.md +33 -0
  10. package/.claude/agents/hermod.md +53 -0
  11. package/.claude/agents/mimir.md +49 -0
  12. package/.claude/agents/odin.md +287 -0
  13. package/.claude/agents/quick.md +34 -0
  14. package/.claude/agents/semble-search.md +50 -0
  15. package/.claude/agents/thor.md +53 -0
  16. package/.claude/agents/tyr.md +56 -0
  17. package/.claude/agents/vidarr.md +54 -0
  18. package/.claude/agents/vor.md +53 -0
  19. package/.claude/commands/audit.md +25 -0
  20. package/.claude/commands/bizar.md +22 -0
  21. package/.claude/commands/explain.md +17 -0
  22. package/.claude/commands/init.md +29 -0
  23. package/.claude/commands/learn.md +44 -0
  24. package/.claude/commands/plan.md +35 -0
  25. package/.claude/commands/plow-through.md +50 -0
  26. package/.claude/commands/pr-review.md +49 -0
  27. package/.claude/commands/setup-provider.md +96 -0
  28. package/.claude/commands/tailscale-serve.md +100 -0
  29. package/.claude/commands/team.md +132 -0
  30. package/.claude/commands/test.md +62 -0
  31. package/.claude/commands/validate.md +68 -0
  32. package/.claude/commands/visual-plan.md +24 -0
  33. package/.claude/hooks/README.md +92 -0
  34. package/.claude/hooks/posttooluse-editwrite.mjs +91 -0
  35. package/.claude/hooks/pretooluse-bash.mjs +81 -0
  36. package/.claude/hooks/pretooluse-editwrite.mjs +139 -0
  37. package/.claude/hooks/sessionend-recall.mjs +74 -0
  38. package/.claude/hooks/sessionstart-prime.mjs +80 -0
  39. package/.claude/hooks/userpromptsubmit-tag.mjs +80 -0
  40. package/.claude/settings.json +116 -0
  41. package/.claude/skills/9router/SKILL.md +80 -0
  42. package/.claude/skills/9router-chat/SKILL.md +73 -0
  43. package/.claude/skills/9router-embeddings/SKILL.md +69 -0
  44. package/.claude/skills/9router-image/SKILL.md +86 -0
  45. package/.claude/skills/9router-stt/SKILL.md +79 -0
  46. package/.claude/skills/9router-tts/SKILL.md +80 -0
  47. package/.claude/skills/9router-web-fetch/SKILL.md +99 -0
  48. package/.claude/skills/9router-web-search/SKILL.md +91 -0
  49. package/.claude/skills/bizar/README.md +9 -0
  50. package/.claude/skills/bizar/SKILL.md +450 -0
  51. package/.claude/skills/cpp-coding-standards/README.md +28 -0
  52. package/.claude/skills/cpp-coding-standards/SKILL.md +634 -0
  53. package/.claude/skills/cpp-coding-standards/references/concurrency.md +320 -0
  54. package/.claude/skills/cpp-coding-standards/references/error-handling.md +229 -0
  55. package/.claude/skills/cpp-coding-standards/references/memory-safety.md +216 -0
  56. package/.claude/skills/cpp-coding-standards/references/modern-idioms.md +282 -0
  57. package/.claude/skills/cpp-coding-standards/references/review-checklist.md +96 -0
  58. package/.claude/skills/cpp-testing/README.md +28 -0
  59. package/.claude/skills/cpp-testing/SKILL.md +304 -0
  60. package/.claude/skills/cpp-testing/references/coverage.md +370 -0
  61. package/.claude/skills/cpp-testing/references/framework-compare.md +175 -0
  62. package/.claude/skills/cpp-testing/references/host-test-for-embedded.md +499 -0
  63. package/.claude/skills/cpp-testing/references/mocking.md +364 -0
  64. package/.claude/skills/cpp-testing/references/tdd-workflow.md +308 -0
  65. package/.claude/skills/cubesandbox/SKILL.md +148 -0
  66. package/.claude/skills/embedded-esp-idf/README.md +41 -0
  67. package/.claude/skills/embedded-esp-idf/SKILL.md +439 -0
  68. package/.claude/skills/embedded-esp-idf/references/freertos-patterns.md +214 -0
  69. package/.claude/skills/embedded-esp-idf/references/host-tests.md +164 -0
  70. package/.claude/skills/embedded-esp-idf/references/idf-py-commands.md +157 -0
  71. package/.claude/skills/embedded-esp-idf/references/kconfig.md +159 -0
  72. package/.claude/skills/embedded-esp-idf/references/logging-discipline.md +118 -0
  73. package/.claude/skills/embedded-esp-idf/references/memory-and-iram.md +137 -0
  74. package/.claude/skills/embedded-esp-idf/references/nvs.md +121 -0
  75. package/.claude/skills/embedded-esp-idf/references/packed-structs.md +192 -0
  76. package/.claude/skills/embedded-esp-idf/scripts/idf_env.sh +47 -0
  77. package/.claude/skills/embedded-esp-idf/scripts/size_check.sh +77 -0
  78. package/.claude/skills/glyph/SKILL.md +163 -0
  79. package/.claude/skills/harness-engineering/SKILL.md +143 -0
  80. package/.claude/skills/lightrag/SKILL.md +81 -0
  81. package/.claude/skills/memory-protocol/SKILL.md +105 -0
  82. package/.claude/skills/obsidian/SKILL.md +306 -0
  83. package/.claude/skills/read-the-damn-docs/SKILL.md +113 -0
  84. package/.claude/skills/self-improvement/SKILL.md +64 -0
  85. package/README.md +87 -59
  86. package/bizar-dash/dist/assets/{EnvVarsSection-DXM8gRm_.js → EnvVarsSection-B58aiJiE.js} +1 -1
  87. package/bizar-dash/dist/assets/{EnvVarsSection-DXM8gRm_.js.map → EnvVarsSection-B58aiJiE.js.map} +1 -1
  88. package/bizar-dash/dist/assets/{MobileChat-BnKN_Ks_.js → MobileChat-BJrqwVDd.js} +1 -1
  89. package/bizar-dash/dist/assets/{MobileChat-BnKN_Ks_.js.map → MobileChat-BJrqwVDd.js.map} +1 -1
  90. package/bizar-dash/dist/assets/{MobileSettings-DjCPxC-Q.js → MobileSettings-CEQNJNLJ.js} +1 -1
  91. package/bizar-dash/dist/assets/{MobileSettings-DjCPxC-Q.js.map → MobileSettings-CEQNJNLJ.js.map} +1 -1
  92. package/bizar-dash/dist/assets/{main-DYiZqMrn.js → main-IvfQAOfy.js} +1 -1
  93. package/bizar-dash/dist/assets/{main-DYiZqMrn.js.map → main-IvfQAOfy.js.map} +1 -1
  94. package/bizar-dash/dist/assets/{markdown-C6mXtQxD.js → markdown-tOLaD6nm.js} +1 -1
  95. package/bizar-dash/dist/assets/{markdown-C6mXtQxD.js.map → markdown-tOLaD6nm.js.map} +1 -1
  96. package/bizar-dash/dist/assets/{mobile-CMHqtLV2.js → mobile-DYCHcUpq.js} +1 -1
  97. package/bizar-dash/dist/assets/{mobile-CMHqtLV2.js.map → mobile-DYCHcUpq.js.map} +1 -1
  98. package/bizar-dash/dist/assets/{mobile-layout-3jIhHX_p.js → mobile-layout-CBHjpwsb.js} +2 -2
  99. package/bizar-dash/dist/assets/{mobile-layout-3jIhHX_p.js.map → mobile-layout-CBHjpwsb.js.map} +1 -1
  100. package/bizar-dash/dist/assets/{useSlashCommands-DtITw8Xv.js → useSlashCommands-Bd7_FA6U.js} +2 -2
  101. package/bizar-dash/dist/assets/{useSlashCommands-DtITw8Xv.js.map → useSlashCommands-Bd7_FA6U.js.map} +1 -1
  102. package/bizar-dash/dist/assets/{vendor-CeHGtduv.js → vendor-C843201K.js} +12 -12
  103. package/bizar-dash/dist/assets/vendor-C843201K.js.map +1 -0
  104. package/bizar-dash/dist/index.html +6 -6
  105. package/bizar-dash/dist/mobile.html +2 -2
  106. package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  107. package/bizar-dash/src/server/api.mjs +4 -4
  108. package/bizar-dash/src/server/background-store.mjs +15 -16
  109. package/bizar-dash/src/server/bg-poller.mjs +25 -35
  110. package/bizar-dash/src/server/bg-retry.mjs +66 -194
  111. package/bizar-dash/src/server/claude-bg-spawner.mjs +390 -0
  112. package/bizar-dash/src/server/claude-info.mjs +412 -0
  113. package/bizar-dash/src/server/{cline-runner.mjs → claude-runner.mjs} +53 -48
  114. package/bizar-dash/src/server/claude-sdk.mjs +191 -0
  115. package/bizar-dash/src/server/providers-store.mjs +22 -49
  116. package/bizar-dash/src/server/routes/background.mjs +24 -20
  117. package/bizar-dash/src/server/routes/chat.mjs +223 -326
  118. package/bizar-dash/src/server/routes/claude-session-detail.mjs +329 -0
  119. package/bizar-dash/src/server/routes/claude-sessions.mjs +259 -0
  120. package/bizar-dash/src/server/routes/tasks.mjs +20 -28
  121. package/bizar-dash/src/server/task-delegator.mjs +76 -77
  122. package/bizar-dash/src/web/components/chat/useChat.ts +23 -27
  123. package/bizar-dash/src/web/views/Chat.tsx +8 -8
  124. package/cli/bin.mjs +17 -18
  125. package/cli/commands/claude-cmd.mjs +348 -0
  126. package/cli/commands/install.mjs +12 -11
  127. package/cli/commands/validate.mjs +252 -325
  128. package/cli/dev-link.test.mjs +11 -11
  129. package/cli/doctor.mjs +131 -213
  130. package/cli/install.mjs +33 -37
  131. package/cli/install.test.mjs +9 -9
  132. package/cli/provision-claude.mjs +893 -0
  133. package/cli/provision.mjs +224 -227
  134. package/cli/utils.mjs +72 -58
  135. package/config/AGENTS.md +28 -28
  136. package/install.sh +66 -44
  137. package/package.json +19 -11
  138. package/packages/sdk/ARCHITECTURE.md +40 -36
  139. package/packages/sdk/package-lock.json +32 -0
  140. package/packages/sdk/package.json +15 -5
  141. package/{plugins/bizar → packages/sdk}/src/dangerous-patterns.ts +6 -28
  142. package/packages/sdk/src/fingerprint.ts +0 -0
  143. package/packages/sdk/src/index.ts +38 -73
  144. package/packages/sdk/src/mcp/bin.ts +51 -0
  145. package/packages/sdk/src/mcp/server.ts +498 -0
  146. package/{plugins/bizar/src/memory-vault.ts → packages/sdk/src/memory/index.ts} +18 -21
  147. package/packages/sdk/tests/sdk.test.mjs +148 -0
  148. package/packages/sdk/vitest.config.ts +1 -1
  149. package/scripts/bh-full-e2e.mjs +166 -363
  150. package/scripts/check-deps.mjs +28 -46
  151. package/scripts/mirror-claude-md.sh +78 -0
  152. package/scripts/test-in-container.sh +31 -9
  153. package/templates/clean-state-checklist.md +3 -3
  154. package/templates/sprint-contract.md +2 -2
  155. package/bizar-dash/.bizar/activity.log +0 -3
  156. package/bizar-dash/.omx/logs/omx-2026-07-07.jsonl +0 -1
  157. package/bizar-dash/.omx/state/session.json +0 -10
  158. package/bizar-dash/bizar-design/EXPLANATION.md +0 -307
  159. package/bizar-dash/bizar-design/INSTRUCTIONS.md +0 -149
  160. package/bizar-dash/bizar-design/canvas.html +0 -281
  161. package/bizar-dash/bizar-design/canvas.html.artifact.json +0 -19
  162. package/bizar-dash/bizar-design/components.css +0 -1665
  163. package/bizar-dash/bizar-design/components.html +0 -557
  164. package/bizar-dash/bizar-design/components.html.artifact.json +0 -19
  165. package/bizar-dash/bizar-design/desktop/agents.html +0 -74
  166. package/bizar-dash/bizar-design/desktop/agents.html.artifact.json +0 -19
  167. package/bizar-dash/bizar-design/desktop/memory.html +0 -75
  168. package/bizar-dash/bizar-design/desktop/memory.html.artifact.json +0 -19
  169. package/bizar-dash/bizar-design/desktop/metrics.html +0 -63
  170. package/bizar-dash/bizar-design/desktop/metrics.html.artifact.json +0 -19
  171. package/bizar-dash/bizar-design/desktop/overview.html +0 -95
  172. package/bizar-dash/bizar-design/desktop/overview.html.artifact.json +0 -19
  173. package/bizar-dash/bizar-design/desktop/settings.html +0 -70
  174. package/bizar-dash/bizar-design/desktop/settings.html.artifact.json +0 -19
  175. package/bizar-dash/bizar-design/icons.svg +0 -44
  176. package/bizar-dash/bizar-design/index.html +0 -192
  177. package/bizar-dash/bizar-design/index.html.artifact.json +0 -19
  178. package/bizar-dash/bizar-design/mobile/agents.html +0 -29
  179. package/bizar-dash/bizar-design/mobile/agents.html.artifact.json +0 -19
  180. package/bizar-dash/bizar-design/mobile/memory.html +0 -28
  181. package/bizar-dash/bizar-design/mobile/memory.html.artifact.json +0 -19
  182. package/bizar-dash/bizar-design/mobile/metrics.html +0 -29
  183. package/bizar-dash/bizar-design/mobile/metrics.html.artifact.json +0 -19
  184. package/bizar-dash/bizar-design/mobile/overview.html +0 -33
  185. package/bizar-dash/bizar-design/mobile/overview.html.artifact.json +0 -19
  186. package/bizar-dash/bizar-design/mobile/settings.html +0 -28
  187. package/bizar-dash/bizar-design/mobile/settings.html.artifact.json +0 -19
  188. package/bizar-dash/bizar-design/prototype.js +0 -114
  189. package/bizar-dash/bizar-design/tokens.css +0 -106
  190. package/bizar-dash/dist/assets/vendor-CeHGtduv.js.map +0 -1
  191. package/bizar-dash/node_modules/.package-lock.json +0 -6
  192. package/bizar-dash/package-lock.json +0 -6
  193. package/bizar-dash/src/server/cline-sdk.mjs +0 -132
  194. package/bizar-dash/src/server/routes/cline-session-detail.mjs +0 -559
  195. package/bizar-dash/src/server/routes/cline-sessions.mjs +0 -291
  196. package/cli/commands/cline-cmd.mjs +0 -289
  197. package/cli/commands/validate.test.mjs +0 -379
  198. package/cli/doctor.test.mjs +0 -350
  199. package/config/cline.json.template +0 -342
  200. package/packages/sdk/src/client.ts +0 -188
  201. package/packages/sdk/src/cline-events.ts +0 -134
  202. package/packages/sdk/src/cline-types.ts +0 -66
  203. package/packages/sdk/src/cline.ts +0 -339
  204. package/packages/sdk/src/errors.ts +0 -129
  205. package/packages/sdk/src/events.ts +0 -153
  206. package/packages/sdk/src/types.ts +0 -176
  207. package/packages/sdk/tests/client.test.ts +0 -217
  208. package/packages/sdk/tests/errors.test.ts +0 -108
  209. package/packages/sdk/tests/events.test.ts +0 -139
  210. package/packages/sdk/tests/fixtures/fetch-mock.ts +0 -152
  211. package/packages/sdk/tests/fixtures/sse-mock.ts +0 -30
  212. package/plugins/bizar/ARCHITECTURE.md +0 -142
  213. package/plugins/bizar/CONSTRAINTS.md +0 -67
  214. package/plugins/bizar/LICENSE +0 -21
  215. package/plugins/bizar/README.md +0 -448
  216. package/plugins/bizar/index.ts +0 -925
  217. package/plugins/bizar/package.json +0 -39
  218. package/plugins/bizar/scripts/check-forbidden-imports.sh +0 -33
  219. package/plugins/bizar/src/background-state.ts +0 -641
  220. package/plugins/bizar/src/background.ts +0 -1806
  221. package/plugins/bizar/src/cline-runner.ts +0 -203
  222. package/plugins/bizar/src/clineruntime.ts +0 -227
  223. package/plugins/bizar/src/commands-impl.ts +0 -151
  224. package/plugins/bizar/src/commands.ts +0 -1799
  225. package/plugins/bizar/src/compaction.d.mts +0 -48
  226. package/plugins/bizar/src/compaction.mjs +0 -192
  227. package/plugins/bizar/src/dashboard-client.ts +0 -233
  228. package/plugins/bizar/src/event-stream.ts +0 -606
  229. package/plugins/bizar/src/fingerprint.ts +0 -120
  230. package/plugins/bizar/src/handoff.ts +0 -79
  231. package/plugins/bizar/src/hooks/memory-flush-on-compact.ts +0 -123
  232. package/plugins/bizar/src/hooks/memory-inject.ts +0 -247
  233. package/plugins/bizar/src/hooks/memory-write-on-end.ts +0 -188
  234. package/plugins/bizar/src/hooks/skill-curator.ts +0 -180
  235. package/plugins/bizar/src/http-client.ts +0 -467
  236. package/plugins/bizar/src/key-rotation.ts +0 -218
  237. package/plugins/bizar/src/logger.ts +0 -144
  238. package/plugins/bizar/src/loop-engineering.ts +0 -241
  239. package/plugins/bizar/src/loop.ts +0 -176
  240. package/plugins/bizar/src/mistake-recovery.ts +0 -98
  241. package/plugins/bizar/src/odin.ts +0 -227
  242. package/plugins/bizar/src/options.ts +0 -470
  243. package/plugins/bizar/src/plan-fs.ts +0 -323
  244. package/plugins/bizar/src/reasoning-clean.ts +0 -454
  245. package/plugins/bizar/src/report.ts +0 -178
  246. package/plugins/bizar/src/research-prompt.ts +0 -35
  247. package/plugins/bizar/src/serve-info.ts +0 -228
  248. package/plugins/bizar/src/serve.ts +0 -496
  249. package/plugins/bizar/src/settings.ts +0 -349
  250. package/plugins/bizar/src/state.ts +0 -298
  251. package/plugins/bizar/src/tool-discipline.ts +0 -105
  252. package/plugins/bizar/src/tools/agent-browser.ts +0 -315
  253. package/plugins/bizar/src/tools/bg-collect.ts +0 -131
  254. package/plugins/bizar/src/tools/bg-get-comments.ts +0 -266
  255. package/plugins/bizar/src/tools/bg-kill.ts +0 -116
  256. package/plugins/bizar/src/tools/bg-pause.ts +0 -99
  257. package/plugins/bizar/src/tools/bg-report-progress.ts +0 -115
  258. package/plugins/bizar/src/tools/bg-resume.ts +0 -94
  259. package/plugins/bizar/src/tools/bg-send-message.ts +0 -223
  260. package/plugins/bizar/src/tools/bg-spawn.ts +0 -502
  261. package/plugins/bizar/src/tools/bg-status.ts +0 -130
  262. package/plugins/bizar/src/tools/graph-query.ts +0 -278
  263. package/plugins/bizar/src/tools/loop-engineering.ts +0 -193
  264. package/plugins/bizar/src/tools/memory-list.ts +0 -43
  265. package/plugins/bizar/src/tools/memory-read.ts +0 -69
  266. package/plugins/bizar/src/tools/memory-search.ts +0 -47
  267. package/plugins/bizar/src/tools/memory-write.ts +0 -54
  268. package/plugins/bizar/src/tools/open-kb.ts +0 -198
  269. package/plugins/bizar/src/tools/plan-action.ts +0 -785
  270. package/plugins/bizar/src/tools/read-glyph-feedback.ts +0 -191
  271. package/plugins/bizar/src/tools/sandbox.ts +0 -232
  272. package/plugins/bizar/src/tools/team-spawn.ts +0 -73
  273. package/plugins/bizar/src/tools/team-status.ts +0 -76
  274. package/plugins/bizar/src/tools/wait-for-feedback.ts +0 -415
  275. package/plugins/bizar/src/trajectory.ts +0 -104
  276. package/plugins/bizar/tests/README.md +0 -99
  277. package/plugins/bizar/tests/attach-handler-bug.test.ts +0 -169
  278. package/plugins/bizar/tests/background-state.test.ts +0 -277
  279. package/plugins/bizar/tests/background.test.ts +0 -402
  280. package/plugins/bizar/tests/block.test.ts +0 -195
  281. package/plugins/bizar/tests/canonical-key-order.test.ts +0 -75
  282. package/plugins/bizar/tests/clineruntime-config.test.ts +0 -283
  283. package/plugins/bizar/tests/commands-impl.test.ts +0 -316
  284. package/plugins/bizar/tests/commands.test.ts +0 -584
  285. package/plugins/bizar/tests/compaction.test.ts +0 -264
  286. package/plugins/bizar/tests/config.test.ts +0 -128
  287. package/plugins/bizar/tests/dashboard-client.test.ts +0 -159
  288. package/plugins/bizar/tests/dispose.test.ts +0 -336
  289. package/plugins/bizar/tests/event-stream.test.ts +0 -409
  290. package/plugins/bizar/tests/event.test.ts +0 -262
  291. package/plugins/bizar/tests/fingerprint.test.ts +0 -190
  292. package/plugins/bizar/tests/http-client.test.ts +0 -404
  293. package/plugins/bizar/tests/init-helpers.test.ts +0 -203
  294. package/plugins/bizar/tests/integration/slash-command.test.ts +0 -349
  295. package/plugins/bizar/tests/integration/tool-routing.test.ts +0 -98
  296. package/plugins/bizar/tests/key-rotation.test.ts +0 -396
  297. package/plugins/bizar/tests/loop-engineering.test.ts +0 -168
  298. package/plugins/bizar/tests/loop.test.ts +0 -397
  299. package/plugins/bizar/tests/memory-write-on-end.test.ts +0 -92
  300. package/plugins/bizar/tests/mistake-recovery.test.ts +0 -116
  301. package/plugins/bizar/tests/odin.test.ts +0 -125
  302. package/plugins/bizar/tests/options.test.ts +0 -329
  303. package/plugins/bizar/tests/reasoning-clean.test.ts +0 -422
  304. package/plugins/bizar/tests/safety.test.ts +0 -281
  305. package/plugins/bizar/tests/serve.test.ts +0 -339
  306. package/plugins/bizar/tests/settings.test.ts +0 -351
  307. package/plugins/bizar/tests/stall-think.test.ts +0 -750
  308. package/plugins/bizar/tests/state.test.ts +0 -276
  309. package/plugins/bizar/tests/tool-discipline.test.ts +0 -77
  310. package/plugins/bizar/tests/tools/agent-browser.test.ts +0 -98
  311. package/plugins/bizar/tests/tools/bg-collect.test.ts +0 -337
  312. package/plugins/bizar/tests/tools/bg-get-comments.test.ts +0 -485
  313. package/plugins/bizar/tests/tools/bg-kill.test.ts +0 -235
  314. package/plugins/bizar/tests/tools/bg-pause.test.ts +0 -61
  315. package/plugins/bizar/tests/tools/bg-report-progress.test.ts +0 -79
  316. package/plugins/bizar/tests/tools/bg-resume.test.ts +0 -40
  317. package/plugins/bizar/tests/tools/bg-send-message.test.ts +0 -116
  318. package/plugins/bizar/tests/tools/bg-spawn-delegation.test.ts +0 -147
  319. package/plugins/bizar/tests/tools/bg-spawn-http.test.ts +0 -233
  320. package/plugins/bizar/tests/tools/bg-spawn.test.ts +0 -311
  321. package/plugins/bizar/tests/tools/bg-status.test.ts +0 -217
  322. package/plugins/bizar/tests/tools/cline-runner.test.ts +0 -115
  323. package/plugins/bizar/tests/tools/plan-action.test.ts +0 -599
  324. package/plugins/bizar/tests/tools/read-glyph-feedback.test.ts +0 -253
  325. package/plugins/bizar/tests/tools/sandbox.test.ts +0 -117
  326. package/plugins/bizar/tests/tools/wait-for-feedback.test.ts +0 -390
  327. package/plugins/bizar/tests/update-deadlock.test.ts +0 -151
  328. package/plugins/bizar/tsconfig.json +0 -29
  329. package/scripts/mirror-agents-md.sh +0 -69
@@ -1,203 +0,0 @@
1
- /**
2
- * Init-helpers regression tests (v0.5.2).
3
- *
4
- * Covers the postmortem-2026-06-18 Layer 1 fix: `client.session.list()`
5
- * is now raced against a 1-second timeout. Without this, a slow or
6
- * broken session store would hang the plugin's `init()` forever and
7
- * stall the UI on a blank screen.
8
- *
9
- * Two layers of test:
10
- * 1) `withTimeout` — pure unit tests on the timeout helper itself.
11
- * 2) `readValidSessionIds` — integration-style test that passes a
12
- * fake `PluginInput` whose `client.session.list()` never resolves
13
- * and verifies that the function returns within ~1.1s.
14
- *
15
- * The second test is what would have caught the original bug: the
16
- * function must NOT block forever on a slow session store.
17
- */
18
-
19
- import { describe, test, expect } from "bun:test";
20
-
21
- import {
22
- withTimeout,
23
- readValidSessionIds,
24
- } from "../index.js";
25
-
26
- // ---------------------------------------------------------------------------
27
- // `withTimeout` — pure unit tests
28
- // ---------------------------------------------------------------------------
29
-
30
- describe("withTimeout", () => {
31
- test("resolves with the promise's value when it completes in time", async () => {
32
- const result = await withTimeout(
33
- Promise.resolve("ok"),
34
- 1000,
35
- "test",
36
- );
37
- expect(result).toBe("ok");
38
- });
39
-
40
- test("rejects with a labeled error when the promise takes too long", async () => {
41
- const hanging = new Promise<string>(() => {
42
- // never resolves
43
- });
44
- const start = Date.now();
45
- let caught: Error | null = null;
46
- try {
47
- await withTimeout(hanging, 100, "hanging-promise");
48
- } catch (err) {
49
- caught = err as Error;
50
- }
51
- const elapsed = Date.now() - start;
52
- expect(caught).not.toBeNull();
53
- expect(caught?.message).toContain("hanging-promise");
54
- expect(caught?.message).toContain("100ms");
55
- // Should fire within ~100ms (allow 50ms of slack for event-loop jitter).
56
- expect(elapsed).toBeGreaterThanOrEqual(95);
57
- expect(elapsed).toBeLessThan(500);
58
- });
59
-
60
- test("propagates rejections from the underlying promise", async () => {
61
- const failing = Promise.reject(new Error("underlying failure"));
62
- let caught: Error | null = null;
63
- try {
64
- await withTimeout(failing, 1000, "test");
65
- } catch (err) {
66
- caught = err as Error;
67
- }
68
- expect(caught).not.toBeNull();
69
- expect(caught?.message).toBe("underlying failure");
70
- });
71
-
72
- test("clears the timer on success (no late rejection)", async () => {
73
- // Resolve after 50ms, well within the 1000ms timeout. The timer
74
- // should be cleared; we can't directly observe the cleanup, but
75
- // the test verifies the function returns the value without ever
76
- // throwing (the cleared timer's callback never fires).
77
- const result = await withTimeout(
78
- new Promise((resolve) => setTimeout(() => resolve("late"), 50)),
79
- 1000,
80
- "test",
81
- );
82
- expect(result).toBe("late");
83
- // Wait long enough that a leaked timer would have fired.
84
- await new Promise((r) => setTimeout(r, 100));
85
- });
86
- });
87
-
88
- // ---------------------------------------------------------------------------
89
- // `readValidSessionIds` — the postmortem Layer 1 fix
90
- // ---------------------------------------------------------------------------
91
-
92
- /**
93
- * Minimal `PluginInput` shape for testing `readValidSessionIds`. We
94
- * only need `client`; the other fields are never read by the
95
- * function under test.
96
- */
97
- function makeInput(client: unknown): { client: unknown } {
98
- return { client } as never;
99
- }
100
-
101
- describe("readValidSessionIds — postmortem Layer 1 fix (v0.5.2)", () => {
102
- test("returns the session IDs when client.session.list() resolves", async () => {
103
- const input = makeInput({
104
- session: {
105
- list: async () => ({
106
- data: [
107
- { id: "ses_a" },
108
- { id: "ses_b" },
109
- ],
110
- }),
111
- },
112
- });
113
- const ids = await readValidSessionIds(input as never);
114
- expect(ids).toBeInstanceOf(Set);
115
- expect([...ids].sort()).toEqual(["ses_a", "ses_b"]);
116
- });
117
-
118
- test("returns the session IDs when client.session.list() returns an array directly", async () => {
119
- const input = makeInput({
120
- session: {
121
- list: async () => [{ id: "ses_x" }, { id: "ses_y" }],
122
- },
123
- });
124
- const ids = await readValidSessionIds(input as never);
125
- expect([...ids].sort()).toEqual(["ses_x", "ses_y"]);
126
- });
127
-
128
- test("returns empty set when client.session is missing", async () => {
129
- const input = makeInput({});
130
- const ids = await readValidSessionIds(input as never);
131
- expect(ids.size).toBe(0);
132
- });
133
-
134
- test("returns empty set when client.session.list is not a function", async () => {
135
- const input = makeInput({ session: { list: "not-a-function" } });
136
- const ids = await readValidSessionIds(input as never);
137
- expect(ids.size).toBe(0);
138
- });
139
-
140
- test("returns empty set when client.session.list() rejects", async () => {
141
- const input = makeInput({
142
- session: {
143
- list: async () => {
144
- throw new Error("session store down");
145
- },
146
- },
147
- });
148
- const ids = await readValidSessionIds(input as never);
149
- expect(ids.size).toBe(0);
150
- });
151
-
152
- test(
153
- "REGRESSION: returns within 1.5s when client.session.list() hangs forever (postmortem Layer 1)",
154
- async () => {
155
- // This is the actual bug from the 2026-06-18 postmortem. Before
156
- // the fix, the call would hang forever and the plugin's `init()`
157
- // would never resolve.
158
- const input = makeInput({
159
- session: {
160
- list: () => new Promise(() => {
161
- // never resolves — simulates a stuck session store
162
- }),
163
- },
164
- });
165
- const start = Date.now();
166
- const ids = await readValidSessionIds(input as never);
167
- const elapsed = Date.now() - start;
168
- // The function should return within ~1s (the timeout). We allow
169
- // 500ms of slack for event-loop jitter.
170
- expect(elapsed).toBeLessThan(1500);
171
- // The fallback is an empty set — the age-based cleanup still
172
- // runs (spec §4.6).
173
- expect(ids.size).toBe(0);
174
- },
175
- );
176
-
177
- test(
178
- "REGRESSION: late rejection from hanging list() does not crash the process",
179
- async () => {
180
- // After the 1s timeout fires, the original `list()` call may
181
- // eventually reject (or it may never). The function attaches a
182
- // no-op `.catch(() => undefined)` to suppress the unhandled-
183
- // rejection. We can't directly observe unhandled rejections in
184
- // bun:test, but we can verify the function returns cleanly.
185
- let rejectFn: ((err: Error) => void) | undefined;
186
- const input = makeInput({
187
- session: {
188
- list: () => new Promise((_, reject: (err: Error) => void) => {
189
- rejectFn = reject;
190
- }),
191
- },
192
- });
193
- const ids = await readValidSessionIds(input as never);
194
- expect(ids.size).toBe(0);
195
- // Now reject the hanging promise AFTER the function returned.
196
- // If the no-op catch is missing, this would be an unhandled
197
- // rejection. With it, the process keeps running.
198
- rejectFn?.(new Error("late rejection"));
199
- // Give the microtask queue a chance to run.
200
- await new Promise((r) => setTimeout(r, 10));
201
- },
202
- );
203
- });
@@ -1,349 +0,0 @@
1
- import { type AgentTool } from "@cline/sdk";
2
- /**
3
- * slash-command.test.ts — Integration tests for the full slash-command
4
- * hook → parser → side-effect → file-I/O path.
5
- *
6
- * Exercises:
7
- * - `/plan new foo` → `plans/foo/meta.json` + `plans/foo/plan.json` on disk
8
- * - `/plan list` → response contains slugs of seeded plans
9
- * - `/plan add foo --title X --type task` → element written to `plan.json`
10
- * - `/plan status foo approved` → `meta.json` status updated
11
- * - `/plan delete foo <elementId>` → element removed from `plan.json`
12
- * - `/plan wait foo` → deferred response (< 100 ms, no blocking)
13
- * - `/plan new foo` on existing slug → refusal, no clobber
14
- * - `/plan comment foo "text"` → comment written to `plan.json`
15
- * - `/plan comments foo` → response lists the comment
16
- */
17
-
18
- import { describe, test, expect, beforeEach, afterEach, afterAll } from "bun:test";
19
- import {
20
- mkdtempSync,
21
- rmSync,
22
- existsSync,
23
- readFileSync,
24
- writeFileSync,
25
- mkdirSync,
26
- } from "node:fs";
27
- import { join } from "node:path";
28
- import { tmpdir } from "node:os";
29
-
30
- import { parseSlashCommand } from "../../src/commands";
31
- import {
32
- executeSideEffect,
33
- type ExecutorContext,
34
- type ExecuteOptions,
35
- } from "../../src/commands-impl";
36
- import { createPlanActionTool } from "../../src/tools/plan-action";
37
- import { createBgGetCommentsTool } from "../../src/tools/bg-get-comments";
38
- import { createWaitForFeedbackTool } from "../../src/tools/wait-for-feedback";
39
- import { DEFAULT_PLAN_SETTINGS, type PlanSettings } from "../../src/settings";
40
-
41
- // ---------------------------------------------------------------------------
42
- // Mock logger
43
- // ---------------------------------------------------------------------------
44
-
45
- class MockLogger {
46
- messages: Array<{ level: string; message: string }> = [];
47
- log(opts: { level: string; message: string }) { this.messages.push(opts); }
48
- debug(m: string) { this.messages.push({ level: "debug", message: m }); }
49
- info(m: string) { this.messages.push({ level: "info", message: m }); }
50
- warn(m: string) { this.messages.push({ level: "warn", message: m }); }
51
- error(m: string) { this.messages.push({ level: "error", message: m }); }
52
- }
53
-
54
- // ---------------------------------------------------------------------------
55
- // Per-test temp worktree
56
- // ---------------------------------------------------------------------------
57
-
58
- let worktree: string;
59
- let logger: MockLogger;
60
-
61
- beforeEach(() => {
62
- worktree = mkdtempSync(join(tmpdir(), "bizar-integration-"));
63
- logger = new MockLogger();
64
- });
65
-
66
- afterEach(() => {
67
- if (worktree) rmSync(worktree, { recursive: true, force: true });
68
- });
69
-
70
- afterAll(() => {
71
- // already cleaned in afterEach, but keep for safety
72
- if (worktree) rmSync(worktree, { recursive: true, force: true });
73
- });
74
-
75
- // ---------------------------------------------------------------------------
76
- // Helper: build minimal ExecutorContext + ExecuteOptions
77
- // ---------------------------------------------------------------------------
78
-
79
- function makeCtx(): ExecutorContext {
80
- return { worktree, directory: worktree, logger };
81
- }
82
-
83
- function makeOpts(): ExecuteOptions {
84
- return {
85
- tools: {
86
- bizar_plan_action: createPlanActionTool({ worktree, logger }) as unknown as AgentTool<unknown, unknown>,
87
- bizar_get_plan_comments: createBgGetCommentsTool({ worktree, logger }) as unknown as AgentTool<unknown, unknown>,
88
- bizar_wait_for_feedback: createWaitForFeedbackTool({ worktree, logger }) as unknown as AgentTool<unknown, unknown>,
89
- },
90
- defaultTemplate: "blank",
91
- defaultPort: 4321,
92
- };
93
- }
94
-
95
- // ---------------------------------------------------------------------------
96
- // Test cases
97
- // ---------------------------------------------------------------------------
98
-
99
- describe("integration: /plan new", () => {
100
- test("/plan new foo → creates plans/foo/meta.json and plan.json with correct content", async () => {
101
- const result = parseSlashCommand("/plan new foo", {
102
- currentSettings: DEFAULT_PLAN_SETTINGS,
103
- availablePlanSlugs: [],
104
- defaultPort: 4321,
105
- });
106
- expect(result).not.toBeNull();
107
- expect(result!.sideEffect!.kind).toBe("create_plan");
108
-
109
- await executeSideEffect(result!.sideEffect!, makeCtx(), makeOpts());
110
-
111
- const metaPath = join(worktree, "plans", "foo", "meta.json");
112
- const planPath = join(worktree, "plans", "foo", "plan.json");
113
- expect(existsSync(metaPath)).toBe(true);
114
- expect(existsSync(planPath)).toBe(true);
115
-
116
- const meta = JSON.parse(readFileSync(metaPath, "utf-8"));
117
- expect(meta.status).toBe("draft");
118
- expect(meta.slug).toBeUndefined(); // slug not stored, title is titleCase
119
- expect(typeof meta.lastEdited).toBe("string");
120
-
121
- const canvas = JSON.parse(readFileSync(planPath, "utf-8"));
122
- expect(canvas.schemaVersion).toBe(2);
123
- expect(canvas.elements).toEqual([]);
124
- expect(canvas.connections).toEqual([]);
125
- expect(canvas.comments).toEqual([]);
126
- });
127
- });
128
-
129
- describe("integration: /plan list", () => {
130
- test("/plan list after creating foo and bar → response contains both slugs", async () => {
131
- // Seed two plans
132
- for (const slug of ["foo", "bar"]) {
133
- const r = parseSlashCommand(`/plan new ${slug}`, {
134
- currentSettings: DEFAULT_PLAN_SETTINGS,
135
- availablePlanSlugs: [],
136
- defaultPort: 4321,
137
- });
138
- await executeSideEffect(r!.sideEffect!, makeCtx(), makeOpts());
139
- }
140
-
141
- const listResult = parseSlashCommand("/plan list", {
142
- currentSettings: DEFAULT_PLAN_SETTINGS,
143
- availablePlanSlugs: ["foo", "bar"],
144
- defaultPort: 4321,
145
- });
146
- expect(listResult).not.toBeNull();
147
- const execResult = await executeSideEffect(listResult!.sideEffect!, makeCtx(), makeOpts());
148
- const response = execResult.responseOverride ?? listResult!.response;
149
- expect(response).toMatch(/foo/);
150
- expect(response).toMatch(/bar/);
151
- });
152
- });
153
-
154
- describe("integration: /plan add", () => {
155
- test("/plan add foo --title X --type task → plan.json contains the element", async () => {
156
- // First create the plan
157
- const newR = parseSlashCommand("/plan new foo", {
158
- currentSettings: DEFAULT_PLAN_SETTINGS,
159
- availablePlanSlugs: [],
160
- defaultPort: 4321,
161
- });
162
- await executeSideEffect(newR!.sideEffect!, makeCtx(), makeOpts());
163
-
164
- // Now add an element
165
- const addR = parseSlashCommand('/plan add foo --title X --type task', {
166
- currentSettings: DEFAULT_PLAN_SETTINGS,
167
- availablePlanSlugs: ["foo"],
168
- defaultPort: 4321,
169
- });
170
- expect(addR).not.toBeNull();
171
- expect(addR!.sideEffect!.kind).toBe("tool_invocation");
172
- const execR = await executeSideEffect(addR!.sideEffect!, makeCtx(), makeOpts());
173
-
174
- const planPath = join(worktree, "plans", "foo", "plan.json");
175
- const canvas = JSON.parse(readFileSync(planPath, "utf-8"));
176
- expect(canvas.elements).toHaveLength(1);
177
- expect(canvas.elements[0]!.title).toBe("X");
178
- expect(canvas.elements[0]!.type).toBe("task");
179
- });
180
- });
181
-
182
- describe("integration: /plan status", () => {
183
- test("/plan status foo approved → meta.json status is approved", async () => {
184
- // Create the plan first
185
- const newR = parseSlashCommand("/plan new foo", {
186
- currentSettings: DEFAULT_PLAN_SETTINGS,
187
- availablePlanSlugs: [],
188
- defaultPort: 4321,
189
- });
190
- await executeSideEffect(newR!.sideEffect!, makeCtx(), makeOpts());
191
-
192
- const statusR = parseSlashCommand("/plan status foo approved", {
193
- currentSettings: DEFAULT_PLAN_SETTINGS,
194
- availablePlanSlugs: ["foo"],
195
- defaultPort: 4321,
196
- });
197
- await executeSideEffect(statusR!.sideEffect!, makeCtx(), makeOpts());
198
-
199
- const metaPath = join(worktree, "plans", "foo", "meta.json");
200
- const meta = JSON.parse(readFileSync(metaPath, "utf-8"));
201
- expect(meta.status).toBe("approved");
202
- });
203
- });
204
-
205
- describe("integration: /plan delete", () => {
206
- test("/plan delete foo <elementId> → element removed from plan.json", async () => {
207
- // Create plan and add an element
208
- const newR = parseSlashCommand("/plan new foo", {
209
- currentSettings: DEFAULT_PLAN_SETTINGS,
210
- availablePlanSlugs: [],
211
- defaultPort: 4321,
212
- });
213
- await executeSideEffect(newR!.sideEffect!, makeCtx(), makeOpts());
214
-
215
- const addR = parseSlashCommand('/plan add foo --title Elem1 --type task', {
216
- currentSettings: DEFAULT_PLAN_SETTINGS,
217
- availablePlanSlugs: ["foo"],
218
- defaultPort: 4321,
219
- });
220
- await executeSideEffect(addR!.sideEffect!, makeCtx(), makeOpts());
221
-
222
- // Read back to get element id
223
- const planPath = join(worktree, "plans", "foo", "plan.json");
224
- let canvas = JSON.parse(readFileSync(planPath, "utf-8")) as {
225
- elements: Array<{ id: string; title: string }>;
226
- };
227
- expect(canvas.elements).toHaveLength(1);
228
- const elementId = canvas.elements[0]!.id;
229
-
230
- // Now delete it
231
- const delR = parseSlashCommand(`/plan delete foo ${elementId}`, {
232
- currentSettings: DEFAULT_PLAN_SETTINGS,
233
- availablePlanSlugs: ["foo"],
234
- defaultPort: 4321,
235
- });
236
- await executeSideEffect(delR!.sideEffect!, makeCtx(), makeOpts());
237
-
238
- canvas = JSON.parse(readFileSync(planPath, "utf-8"));
239
- expect(canvas.elements).toHaveLength(0);
240
- });
241
- });
242
-
243
- describe("integration: /plan wait", () => {
244
- test("/plan wait foo → returns deferred response, does NOT block hook (< 100ms)", async () => {
245
- const r = parseSlashCommand("/plan wait foo", {
246
- currentSettings: DEFAULT_PLAN_SETTINGS,
247
- availablePlanSlugs: ["foo"],
248
- defaultPort: 4321,
249
- });
250
- expect(r).not.toBeNull();
251
- expect(r!.response).toMatch(/deferred/i);
252
- expect(r!.response).toMatch(/bizar_wait_for_feedback/);
253
- expect(r!.sideEffect).toBeUndefined(); // wait is NOT a tool_invocation
254
-
255
- // Verify it returns instantly (no async I/O)
256
- const start = Date.now();
257
- await executeSideEffect({ kind: "list_plans" }, makeCtx(), makeOpts()); // no-op
258
- const elapsed = Date.now() - start;
259
- expect(elapsed).toBeLessThan(100);
260
- });
261
- });
262
-
263
- describe("integration: /plan new on existing slug", () => {
264
- test("/plan new foo on existing slug → refusal response, existing files intact", async () => {
265
- // Create the plan
266
- const r1 = parseSlashCommand("/plan new foo", {
267
- currentSettings: DEFAULT_PLAN_SETTINGS,
268
- availablePlanSlugs: [],
269
- defaultPort: 4321,
270
- });
271
- await executeSideEffect(r1!.sideEffect!, makeCtx(), makeOpts());
272
-
273
- const planDir = join(worktree, "plans", "foo");
274
- const metaBefore = readFileSync(join(planDir, "meta.json"), "utf-8");
275
- const planBefore = readFileSync(join(planDir, "plan.json"), "utf-8");
276
-
277
- // Try to create again
278
- const r2 = parseSlashCommand("/plan new foo", {
279
- currentSettings: DEFAULT_PLAN_SETTINGS,
280
- availablePlanSlugs: ["foo"],
281
- defaultPort: 4321,
282
- });
283
- const execR = await executeSideEffect(r2!.sideEffect!, makeCtx(), makeOpts());
284
-
285
- // Should have refused
286
- expect(execR.responseOverride).toMatch(/already exists/);
287
-
288
- // Files should be unchanged
289
- expect(readFileSync(join(planDir, "meta.json"), "utf-8")).toBe(metaBefore);
290
- expect(readFileSync(join(planDir, "plan.json"), "utf-8")).toBe(planBefore);
291
- });
292
- });
293
-
294
- describe("integration: /plan comment", () => {
295
- test('/plan comment foo "first comment" → plan.json contains the comment', async () => {
296
- // Create the plan first
297
- const newR = parseSlashCommand("/plan new foo", {
298
- currentSettings: DEFAULT_PLAN_SETTINGS,
299
- availablePlanSlugs: [],
300
- defaultPort: 4321,
301
- });
302
- await executeSideEffect(newR!.sideEffect!, makeCtx(), makeOpts());
303
-
304
- const commentR = parseSlashCommand('/plan comment foo "first comment"', {
305
- currentSettings: DEFAULT_PLAN_SETTINGS,
306
- availablePlanSlugs: ["foo"],
307
- defaultPort: 4321,
308
- });
309
- await executeSideEffect(commentR!.sideEffect!, makeCtx(), makeOpts());
310
-
311
- const planPath = join(worktree, "plans", "foo", "plan.json");
312
- const canvas = JSON.parse(readFileSync(planPath, "utf-8")) as {
313
- comments: Array<{ id: string; text: string; author: string }>;
314
- };
315
- expect(canvas.comments).toHaveLength(1);
316
- expect(canvas.comments[0]!.text).toBe("first comment");
317
- expect(canvas.comments[0]!.author).toBe("user");
318
- });
319
- });
320
-
321
- describe("integration: /plan comments", () => {
322
- test("/plan comments foo → response lists the comment", async () => {
323
- // Create the plan first
324
- const newR = parseSlashCommand("/plan new foo", {
325
- currentSettings: DEFAULT_PLAN_SETTINGS,
326
- availablePlanSlugs: [],
327
- defaultPort: 4321,
328
- });
329
- await executeSideEffect(newR!.sideEffect!, makeCtx(), makeOpts());
330
-
331
- // Add a comment
332
- const commentR = parseSlashCommand('/plan comment foo "Hello world"', {
333
- currentSettings: DEFAULT_PLAN_SETTINGS,
334
- availablePlanSlugs: ["foo"],
335
- defaultPort: 4321,
336
- });
337
- await executeSideEffect(commentR!.sideEffect!, makeCtx(), makeOpts());
338
-
339
- // Now list comments
340
- const listR = parseSlashCommand("/plan comments foo", {
341
- currentSettings: DEFAULT_PLAN_SETTINGS,
342
- availablePlanSlugs: ["foo"],
343
- defaultPort: 4321,
344
- });
345
- const execR = await executeSideEffect(listR!.sideEffect!, makeCtx(), makeOpts());
346
- const response = execR.responseOverride ?? listR!.response;
347
- expect(response).toMatch(/Hello world/);
348
- });
349
- });
@@ -1,98 +0,0 @@
1
- /**
2
- * tool-routing.test.ts — Integration tests for the tool_invocation path
3
- * in slash commands. Phase 2 — Cline SDK port: tests the new
4
- * `AgentToolContext` shape and the Cline `createTool` API.
5
- */
6
-
7
- import { describe, test, expect, beforeAll, afterAll } from "bun:test";
8
- import { mkdtempSync, rmSync } from "node:fs";
9
- import { join } from "node:path";
10
- import { tmpdir } from "node:os";
11
-
12
- import {
13
- executeSideEffect,
14
- buildSyntheticToolContext,
15
- type ExecutorContext,
16
- type ExecuteOptions,
17
- } from "../../src/commands-impl";
18
- import { createPlanActionTool } from "../../src/tools/plan-action";
19
- import { createBgGetCommentsTool } from "../../src/tools/bg-get-comments";
20
- import { createWaitForFeedbackTool } from "../../src/tools/wait-for-feedback";
21
- import type { SideEffect } from "../../src/commands";
22
- import type { AgentTool, AgentToolContext } from "@cline/sdk";
23
-
24
- class MockLogger {
25
- messages: Array<{ level: string; message: string }> = [];
26
- debug(m: string) { this.messages.push({ level: "debug", message: m }); }
27
- info(m: string) { this.messages.push({ level: "info", message: m }); }
28
- warn(m: string) { this.messages.push({ level: "warn", message: m }); }
29
- error(m: string) { this.messages.push({ level: "error", message: m }); }
30
- log(_o: unknown) { /* swallow */ }
31
- }
32
-
33
- let worktree: string;
34
- const logger = new MockLogger();
35
-
36
- beforeAll(() => { worktree = mkdtempSync(join(tmpdir(), "bizar-tool-routing-")); });
37
- afterAll(() => { if (worktree) rmSync(worktree, { recursive: true, force: true }); });
38
-
39
- function ctx(): ExecutorContext { return { worktree, directory: worktree, logger }; }
40
-
41
- function optsWith(tools: Record<string, AgentTool>): ExecuteOptions {
42
- return { tools, defaultTemplate: "blank", defaultPort: 4321 };
43
- }
44
-
45
- const planTools = (): Record<string, AgentTool> => ({
46
- bizar_plan_action: createPlanActionTool({ worktree, logger }) as unknown as AgentTool,
47
- bizar_get_plan_comments: createBgGetCommentsTool({ worktree, logger }) as unknown as AgentTool,
48
- bizar_wait_for_feedback: createWaitForFeedbackTool({ worktree, logger }) as unknown as AgentTool,
49
- });
50
-
51
- describe("ToolContext — required fields", () => {
52
- test("synthetic context has all required fields", () => {
53
- const tctx = buildSyntheticToolContext(ctx()) as unknown as Record<string, unknown>;
54
- expect(tctx.sessionId).toBe("slash-command");
55
- expect(tctx.agentId).toBe("slash-command");
56
- expect(tctx.runId).toBeDefined();
57
- expect(tctx.iteration).toBe(0);
58
- expect(tctx.signal).toBeDefined();
59
- expect(tctx.metadata).toBeDefined();
60
- });
61
-
62
- test("abort.signal is NOT aborted", () => {
63
- const tctx = buildSyntheticToolContext(ctx()) as unknown as { signal: AbortSignal };
64
- expect(tctx.signal.aborted).toBe(false);
65
- });
66
-
67
- test("each call gets a fresh AbortSignal (not shared)", () => {
68
- const a = buildSyntheticToolContext(ctx()) as unknown as { signal: AbortSignal };
69
- const b = buildSyntheticToolContext(ctx()) as unknown as { signal: AbortSignal };
70
- expect(a.signal).not.toBe(b.signal);
71
- });
72
- });
73
-
74
- describe("ToolInvocation — args validation via Zod", () => {
75
- test("malformed args (invalid slug) → refusal response, tool NOT invoked", async () => {
76
- const sideEffect: SideEffect = { kind: "tool_invocation", toolName: "bizar_plan_action", args: { action: "get_canvas", planSlug: "UPPERCASE" } };
77
- const res = await executeSideEffect(sideEffect, ctx(), optsWith(planTools()));
78
- expect(res.responseOverride).toBeDefined();
79
- expect(res.responseOverride).toMatch(/Invalid arguments/);
80
- });
81
-
82
- test("missing required arg (no planSlug) → refusal response, tool NOT invoked", async () => {
83
- const sideEffect: SideEffect = { kind: "tool_invocation", toolName: "bizar_plan_action", args: { action: "get_canvas" } };
84
- const res = await executeSideEffect(sideEffect, ctx(), optsWith(planTools()));
85
- expect(res.responseOverride).toBeDefined();
86
- expect(res.responseOverride).toMatch(/Invalid arguments/);
87
- });
88
-
89
- test("valid args → tool IS invoked (spy verifies factory call)", async () => {
90
- // Pre-create a plan first.
91
- await executeSideEffect({ kind: "create_plan", slug: "routing-spy", template: null }, ctx(), optsWith(planTools()));
92
- const sideEffect: SideEffect = { kind: "tool_invocation", toolName: "bizar_plan_action", args: { action: "get_canvas", planSlug: "routing-spy" } };
93
- const res = await executeSideEffect(sideEffect, ctx(), optsWith(planTools()));
94
- expect(res.responseOverride).toBeDefined();
95
- // Should not be a validation error
96
- expect(res.responseOverride).not.toMatch(/Invalid arguments/);
97
- });
98
- });