gsd-pi 2.78.0 → 2.78.1-dev.82bcf6b71

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 (577) hide show
  1. package/README.md +60 -23
  2. package/dist/bundled-resource-path.d.ts +7 -0
  3. package/dist/bundled-resource-path.js +34 -2
  4. package/dist/claude-cli-check.js +104 -33
  5. package/dist/cli-policy.d.ts +13 -0
  6. package/dist/cli-policy.js +17 -0
  7. package/dist/cli.js +95 -55
  8. package/dist/headless-query.d.ts +22 -0
  9. package/dist/headless-query.js +43 -8
  10. package/dist/headless.d.ts +10 -0
  11. package/dist/headless.js +16 -1
  12. package/dist/loader.js +9 -13
  13. package/dist/onboarding.d.ts +10 -0
  14. package/dist/onboarding.js +2 -2
  15. package/dist/provider-migrations.d.ts +2 -2
  16. package/dist/provider-migrations.js +5 -2
  17. package/dist/resource-loader.d.ts +5 -2
  18. package/dist/resource-loader.js +30 -13
  19. package/dist/resources/.managed-resources-content-hash +1 -0
  20. package/dist/resources/extensions/claude-code-cli/readiness.js +128 -32
  21. package/dist/resources/extensions/google-search/index.js +2 -6
  22. package/dist/resources/extensions/gsd/auto/loop.js +23 -0
  23. package/dist/resources/extensions/gsd/auto/phases.js +5 -13
  24. package/dist/resources/extensions/gsd/auto/run-unit.js +26 -12
  25. package/dist/resources/extensions/gsd/auto/session.js +5 -6
  26. package/dist/resources/extensions/gsd/auto-dashboard.js +3 -2
  27. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +55 -21
  28. package/dist/resources/extensions/gsd/auto-dispatch.js +18 -6
  29. package/dist/resources/extensions/gsd/auto-prompts.js +69 -2
  30. package/dist/resources/extensions/gsd/auto-recovery.js +43 -4
  31. package/dist/resources/extensions/gsd/auto-runtime-state.js +31 -0
  32. package/dist/resources/extensions/gsd/auto-start.js +1 -1
  33. package/dist/resources/extensions/gsd/auto-tool-tracking.js +2 -2
  34. package/dist/resources/extensions/gsd/auto-worktree.js +75 -13
  35. package/dist/resources/extensions/gsd/auto.js +39 -14
  36. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +14 -2
  37. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +7 -5
  38. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +2 -2
  39. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +5 -4
  40. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +112 -31
  41. package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +11 -6
  42. package/dist/resources/extensions/gsd/bootstrap/subagent-input.js +22 -0
  43. package/dist/resources/extensions/gsd/bootstrap/system-context.js +45 -8
  44. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +121 -3
  45. package/dist/resources/extensions/gsd/commands/catalog.js +76 -5
  46. package/dist/resources/extensions/gsd/commands/handlers/core.js +23 -1
  47. package/dist/resources/extensions/gsd/commands/handlers/ops.js +8 -0
  48. package/dist/resources/extensions/gsd/commands-config.js +3 -2
  49. package/dist/resources/extensions/gsd/commands-extensions.js +46 -3
  50. package/dist/resources/extensions/gsd/commands-handlers.js +3 -2
  51. package/dist/resources/extensions/gsd/commands-mcp-status.js +3 -1
  52. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +10 -1
  53. package/dist/resources/extensions/gsd/commands-worktree.js +309 -0
  54. package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -1
  55. package/dist/resources/extensions/gsd/docs/preferences-reference.md +10 -0
  56. package/dist/resources/extensions/gsd/doctor-providers.js +2 -1
  57. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +39 -1
  58. package/dist/resources/extensions/gsd/error-classifier.js +1 -1
  59. package/dist/resources/extensions/gsd/forensics.js +10 -8
  60. package/dist/resources/extensions/gsd/git-service.js +12 -5
  61. package/dist/resources/extensions/gsd/gsd-db.js +11 -2
  62. package/dist/resources/extensions/gsd/guided-flow.js +25 -24
  63. package/dist/resources/extensions/gsd/home-dir.js +16 -0
  64. package/dist/resources/extensions/gsd/key-manager.js +2 -1
  65. package/dist/resources/extensions/gsd/memory-store.js +66 -31
  66. package/dist/resources/extensions/gsd/migrate/command.js +3 -2
  67. package/dist/resources/extensions/gsd/milestone-id-reservation.js +36 -0
  68. package/dist/resources/extensions/gsd/model-router.js +114 -9
  69. package/dist/resources/extensions/gsd/native-git-bridge.js +7 -1
  70. package/dist/resources/extensions/gsd/preferences-models.js +91 -15
  71. package/dist/resources/extensions/gsd/preferences-types.js +2 -0
  72. package/dist/resources/extensions/gsd/preferences-validation.js +32 -0
  73. package/dist/resources/extensions/gsd/preferences.js +5 -3
  74. package/dist/resources/extensions/gsd/prompt-loader.js +23 -12
  75. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +10 -0
  76. package/dist/resources/extensions/gsd/prompts/complete-slice.md +10 -0
  77. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  78. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +2 -0
  79. package/dist/resources/extensions/gsd/prompts/plan-slice.md +10 -0
  80. package/dist/resources/extensions/gsd/prompts/refine-slice.md +10 -0
  81. package/dist/resources/extensions/gsd/prompts/rewrite-docs.md +2 -0
  82. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +9 -3
  83. package/dist/resources/extensions/gsd/state.js +42 -0
  84. package/dist/resources/extensions/gsd/templates/PREFERENCES.md +1 -0
  85. package/dist/resources/extensions/gsd/tools/memory-tools.js +18 -1
  86. package/dist/resources/extensions/gsd/unit-context-manifest.js +29 -4
  87. package/dist/resources/extensions/gsd/visualizer-overlay.js +1 -1
  88. package/dist/resources/extensions/gsd/watch/header-renderer.js +3 -1
  89. package/dist/resources/extensions/gsd/worktree-command.js +26 -46
  90. package/dist/resources/extensions/gsd/worktree-manager.js +20 -1
  91. package/dist/resources/extensions/gsd/worktree-resolver.js +28 -13
  92. package/dist/resources/extensions/gsd/worktree-root.js +124 -0
  93. package/dist/resources/extensions/gsd/worktree-session-state.js +33 -0
  94. package/dist/resources/extensions/gsd/worktree.js +4 -115
  95. package/dist/resources/extensions/mcp-client/index.js +6 -9
  96. package/dist/resources/extensions/ollama/index.js +15 -2
  97. package/dist/resources/extensions/ollama/model-capabilities.js +31 -0
  98. package/dist/resources/extensions/ollama/ollama-client.js +40 -4
  99. package/dist/resources/extensions/slash-commands/create-extension.js +36 -22
  100. package/dist/resources/extensions/subagent/index.js +324 -178
  101. package/dist/resources/skills/create-gsd-extension/SKILL.md +9 -5
  102. package/dist/resources/skills/create-gsd-extension/references/custom-commands.md +1 -1
  103. package/dist/resources/skills/create-gsd-extension/references/custom-rendering.md +5 -5
  104. package/dist/resources/skills/create-gsd-extension/references/custom-tools.md +4 -4
  105. package/dist/resources/skills/create-gsd-extension/references/custom-ui.md +6 -6
  106. package/dist/resources/skills/create-gsd-extension/references/events-reference.md +3 -3
  107. package/dist/resources/skills/create-gsd-extension/references/packaging-distribution.md +1 -1
  108. package/dist/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +3 -3
  109. package/dist/resources/skills/create-gsd-extension/workflows/create-extension.md +32 -12
  110. package/dist/resources/skills/lint/SKILL.md +4 -0
  111. package/dist/resources/skills/review/SKILL.md +4 -0
  112. package/dist/resources/skills/test/SKILL.md +3 -0
  113. package/dist/rtk-shared.d.ts +3 -0
  114. package/dist/rtk-shared.js +17 -0
  115. package/dist/rtk.d.ts +2 -5
  116. package/dist/rtk.js +3 -20
  117. package/dist/runtime-checks.d.ts +27 -0
  118. package/dist/runtime-checks.js +38 -0
  119. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  120. package/dist/web/standalone/.next/BUILD_ID +1 -1
  121. package/dist/web/standalone/.next/app-path-routes-manifest.json +19 -19
  122. package/dist/web/standalone/.next/build-manifest.json +4 -4
  123. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  124. package/dist/web/standalone/.next/react-loadable-manifest.json +44 -4
  125. package/dist/web/standalone/.next/required-server-files.json +3 -3
  126. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  127. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  128. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  129. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  130. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  131. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  132. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  133. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  134. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  135. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  136. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  137. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  138. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  139. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  140. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  141. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  142. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  143. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  144. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  145. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  146. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  153. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  165. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  166. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  172. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  175. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  180. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  181. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  182. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  183. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  184. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  185. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  186. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  187. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  188. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  189. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  190. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  191. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  192. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  193. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  194. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  195. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  196. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  197. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  198. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  199. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  200. package/dist/web/standalone/.next/server/app/api/session/events/route.js +4 -2
  201. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  202. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  203. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  204. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  205. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  206. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  207. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  208. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  209. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  210. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  211. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  212. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  213. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  214. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  215. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  216. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  217. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  218. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  219. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  220. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  221. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  222. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  223. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  224. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  225. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  226. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  227. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  228. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  229. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  230. package/dist/web/standalone/.next/server/app/index.html +1 -1
  231. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  232. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  233. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  234. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  235. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  236. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  237. package/dist/web/standalone/.next/server/app/page.js +2 -2
  238. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  239. package/dist/web/standalone/.next/server/app-paths-manifest.json +19 -19
  240. package/dist/web/standalone/.next/server/chunks/63.js +3 -3
  241. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  242. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  243. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  244. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  245. package/dist/web/standalone/.next/server/middleware.js +2 -2
  246. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  247. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  248. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  249. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  250. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  251. package/dist/web/standalone/.next/server/webpack-runtime.js +1 -1
  252. package/dist/web/standalone/.next/static/chunks/2556.0527fea66e123b7f.js +1 -0
  253. package/dist/web/standalone/.next/static/chunks/2824.08296bc2f9654698.js +1 -0
  254. package/dist/web/standalone/.next/static/chunks/3026.3af53b279375f082.js +1 -0
  255. package/dist/web/standalone/.next/static/chunks/315.6f68ae79b67d25cf.js +1 -0
  256. package/dist/web/standalone/.next/static/chunks/3497.4bfc60a3b3dea717.js +1 -0
  257. package/dist/web/standalone/.next/static/chunks/5516.4a07c872b5c3a663.js +1 -0
  258. package/dist/web/standalone/.next/static/chunks/8336.31b019697882acfb.js +10 -0
  259. package/dist/web/standalone/.next/static/chunks/8845.c9702695e8c5a9c5.js +2 -0
  260. package/dist/web/standalone/.next/static/chunks/9058.01ef3a463bda88f1.js +20 -0
  261. package/dist/web/standalone/.next/static/chunks/9441.1081da1125d1764f.js +1 -0
  262. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  263. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  264. package/dist/web/standalone/.next/static/chunks/app/page-9bf2e0c50fb2ca05.js +1 -0
  265. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  266. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  267. package/dist/web/standalone/.next/static/chunks/webpack-f9f0dc45e4f3ac10.js +1 -0
  268. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  269. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  270. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  271. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  272. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  273. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  274. package/dist/web/standalone/package.json +2 -1
  275. package/dist/web/standalone/server.js +1 -1
  276. package/dist/welcome-screen.js +27 -1
  277. package/dist/worktree-cli.d.ts +1 -0
  278. package/dist/worktree-cli.js +9 -3
  279. package/dist/worktree-status-banner.d.ts +1 -0
  280. package/dist/worktree-status-banner.js +132 -0
  281. package/package.json +1 -3
  282. package/packages/daemon/package.json +2 -2
  283. package/packages/mcp-server/dist/alias-telemetry.d.ts +8 -0
  284. package/packages/mcp-server/dist/alias-telemetry.d.ts.map +1 -0
  285. package/packages/mcp-server/dist/alias-telemetry.js +30 -0
  286. package/packages/mcp-server/dist/alias-telemetry.js.map +1 -0
  287. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  288. package/packages/mcp-server/dist/workflow-tools.js +74 -46
  289. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  290. package/packages/mcp-server/package.json +2 -2
  291. package/packages/mcp-server/src/alias-telemetry.test.ts +78 -0
  292. package/packages/mcp-server/src/alias-telemetry.ts +30 -0
  293. package/packages/mcp-server/src/workflow-tools.test.ts +78 -0
  294. package/packages/mcp-server/src/workflow-tools.ts +93 -58
  295. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  296. package/packages/native/package.json +1 -1
  297. package/packages/native/tsconfig.tsbuildinfo +1 -1
  298. package/packages/pi-agent-core/package.json +1 -1
  299. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  300. package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.d.ts +2 -0
  301. package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.d.ts.map +1 -0
  302. package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.js +231 -0
  303. package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.js.map +1 -0
  304. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
  305. package/packages/pi-ai/dist/providers/anthropic-shared.js +48 -19
  306. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
  307. package/packages/pi-ai/dist/types.d.ts +13 -0
  308. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  309. package/packages/pi-ai/dist/types.js.map +1 -1
  310. package/packages/pi-ai/dist/utils/repair-tool-json.d.ts.map +1 -1
  311. package/packages/pi-ai/dist/utils/repair-tool-json.js +24 -3
  312. package/packages/pi-ai/dist/utils/repair-tool-json.js.map +1 -1
  313. package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js +26 -0
  314. package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js.map +1 -1
  315. package/packages/pi-ai/package.json +1 -1
  316. package/packages/pi-ai/src/providers/anthropic-shared.cache-breakpoint.test.ts +289 -0
  317. package/packages/pi-ai/src/providers/anthropic-shared.ts +52 -20
  318. package/packages/pi-ai/src/types.ts +13 -0
  319. package/packages/pi-ai/src/utils/repair-tool-json.ts +24 -3
  320. package/packages/pi-ai/src/utils/tests/repair-tool-json.test.ts +32 -0
  321. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  322. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  323. package/packages/pi-coding-agent/dist/core/agent-session.js +6 -0
  324. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  325. package/packages/pi-coding-agent/dist/core/messages.d.ts.map +1 -1
  326. package/packages/pi-coding-agent/dist/core/messages.js +4 -0
  327. package/packages/pi-coding-agent/dist/core/messages.js.map +1 -1
  328. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +19 -2
  329. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  330. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +10 -0
  331. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  332. package/packages/pi-coding-agent/dist/core/model-registry.js +18 -0
  333. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  334. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +13 -0
  335. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  336. package/packages/pi-coding-agent/dist/core/system-prompt.js +20 -16
  337. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  338. package/packages/pi-coding-agent/dist/core/token-telemetry.d.ts +37 -0
  339. package/packages/pi-coding-agent/dist/core/token-telemetry.d.ts.map +1 -0
  340. package/packages/pi-coding-agent/dist/core/token-telemetry.js +49 -0
  341. package/packages/pi-coding-agent/dist/core/token-telemetry.js.map +1 -0
  342. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.d.ts +2 -0
  343. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.d.ts.map +1 -0
  344. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +133 -0
  345. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -0
  346. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +1 -1
  347. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  348. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +14 -1
  349. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  350. package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.d.ts +2 -0
  351. package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.d.ts.map +1 -0
  352. package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.js +78 -0
  353. package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.js.map +1 -0
  354. package/packages/pi-coding-agent/dist/tests/token-telemetry.test.d.ts +2 -0
  355. package/packages/pi-coding-agent/dist/tests/token-telemetry.test.d.ts.map +1 -0
  356. package/packages/pi-coding-agent/dist/tests/token-telemetry.test.js +181 -0
  357. package/packages/pi-coding-agent/dist/tests/token-telemetry.test.js.map +1 -0
  358. package/packages/pi-coding-agent/package.json +1 -1
  359. package/packages/pi-coding-agent/src/core/agent-session.ts +7 -0
  360. package/packages/pi-coding-agent/src/core/messages.ts +4 -0
  361. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +32 -2
  362. package/packages/pi-coding-agent/src/core/model-registry.ts +21 -0
  363. package/packages/pi-coding-agent/src/core/system-prompt.ts +33 -15
  364. package/packages/pi-coding-agent/src/core/token-telemetry.ts +77 -0
  365. package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +212 -0
  366. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +17 -1
  367. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +1 -1
  368. package/packages/pi-coding-agent/src/tests/system-prompt-cache-stability.test.ts +102 -0
  369. package/packages/pi-coding-agent/src/tests/token-telemetry.test.ts +200 -0
  370. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  371. package/packages/pi-tui/dist/__tests__/autocomplete.test.js +17 -3
  372. package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
  373. package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.d.ts +2 -0
  374. package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.d.ts.map +1 -0
  375. package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.js +161 -0
  376. package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.js.map +1 -0
  377. package/packages/pi-tui/package.json +1 -1
  378. package/packages/pi-tui/src/__tests__/autocomplete.test.ts +20 -3
  379. package/packages/pi-tui/src/components/__tests__/leak-fixes-runtime.test.ts +219 -0
  380. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  381. package/packages/rpc-client/package.json +1 -1
  382. package/pkg/package.json +1 -1
  383. package/src/resources/extensions/claude-code-cli/readiness.ts +130 -30
  384. package/src/resources/extensions/google-search/index.ts +2 -9
  385. package/src/resources/extensions/gsd/auto/loop.ts +24 -2
  386. package/src/resources/extensions/gsd/auto/phases.ts +6 -14
  387. package/src/resources/extensions/gsd/auto/run-unit.ts +26 -12
  388. package/src/resources/extensions/gsd/auto/session.ts +5 -6
  389. package/src/resources/extensions/gsd/auto/types.ts +1 -0
  390. package/src/resources/extensions/gsd/auto-dashboard.ts +3 -2
  391. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +60 -24
  392. package/src/resources/extensions/gsd/auto-dispatch.ts +18 -6
  393. package/src/resources/extensions/gsd/auto-prompts.ts +66 -2
  394. package/src/resources/extensions/gsd/auto-recovery.ts +46 -8
  395. package/src/resources/extensions/gsd/auto-runtime-state.ts +51 -0
  396. package/src/resources/extensions/gsd/auto-start.ts +1 -1
  397. package/src/resources/extensions/gsd/auto-tool-tracking.ts +2 -4
  398. package/src/resources/extensions/gsd/auto-worktree.ts +97 -12
  399. package/src/resources/extensions/gsd/auto.ts +37 -10
  400. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +15 -13
  401. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +8 -7
  402. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +2 -2
  403. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +10 -9
  404. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +121 -31
  405. package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +12 -6
  406. package/src/resources/extensions/gsd/bootstrap/subagent-input.ts +20 -0
  407. package/src/resources/extensions/gsd/bootstrap/system-context.ts +50 -8
  408. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +141 -11
  409. package/src/resources/extensions/gsd/commands/catalog.ts +82 -5
  410. package/src/resources/extensions/gsd/commands/handlers/core.ts +23 -1
  411. package/src/resources/extensions/gsd/commands/handlers/ops.ts +10 -0
  412. package/src/resources/extensions/gsd/commands-config.ts +3 -2
  413. package/src/resources/extensions/gsd/commands-extensions.ts +43 -3
  414. package/src/resources/extensions/gsd/commands-handlers.ts +3 -2
  415. package/src/resources/extensions/gsd/commands-mcp-status.ts +3 -1
  416. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +15 -1
  417. package/src/resources/extensions/gsd/commands-worktree.ts +383 -0
  418. package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -1
  419. package/src/resources/extensions/gsd/docs/preferences-reference.md +10 -0
  420. package/src/resources/extensions/gsd/doctor-providers.ts +2 -1
  421. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +39 -1
  422. package/src/resources/extensions/gsd/doctor-types.ts +3 -1
  423. package/src/resources/extensions/gsd/error-classifier.ts +1 -1
  424. package/src/resources/extensions/gsd/forensics.ts +12 -7
  425. package/src/resources/extensions/gsd/git-service.ts +13 -5
  426. package/src/resources/extensions/gsd/gsd-db.ts +12 -2
  427. package/src/resources/extensions/gsd/guided-flow.ts +27 -26
  428. package/src/resources/extensions/gsd/home-dir.ts +19 -0
  429. package/src/resources/extensions/gsd/journal.ts +4 -1
  430. package/src/resources/extensions/gsd/key-manager.ts +2 -1
  431. package/src/resources/extensions/gsd/memory-store.ts +81 -28
  432. package/src/resources/extensions/gsd/migrate/command.ts +3 -2
  433. package/src/resources/extensions/gsd/milestone-id-reservation.ts +47 -0
  434. package/src/resources/extensions/gsd/model-router.ts +172 -9
  435. package/src/resources/extensions/gsd/native-git-bridge.ts +7 -1
  436. package/src/resources/extensions/gsd/preferences-models.ts +101 -15
  437. package/src/resources/extensions/gsd/preferences-types.ts +6 -0
  438. package/src/resources/extensions/gsd/preferences-validation.ts +35 -0
  439. package/src/resources/extensions/gsd/preferences.ts +16 -2
  440. package/src/resources/extensions/gsd/prompt-loader.ts +26 -12
  441. package/src/resources/extensions/gsd/prompts/complete-milestone.md +10 -0
  442. package/src/resources/extensions/gsd/prompts/complete-slice.md +10 -0
  443. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  444. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +2 -0
  445. package/src/resources/extensions/gsd/prompts/plan-slice.md +10 -0
  446. package/src/resources/extensions/gsd/prompts/refine-slice.md +10 -0
  447. package/src/resources/extensions/gsd/prompts/rewrite-docs.md +2 -0
  448. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +9 -3
  449. package/src/resources/extensions/gsd/state.ts +42 -0
  450. package/src/resources/extensions/gsd/templates/PREFERENCES.md +1 -0
  451. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +179 -1
  452. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +58 -0
  453. package/src/resources/extensions/gsd/tests/auto-session-encapsulation.test.ts +24 -5
  454. package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +21 -4
  455. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +1 -1
  456. package/src/resources/extensions/gsd/tests/budget-prediction.test.ts +138 -211
  457. package/src/resources/extensions/gsd/tests/bundled-skill-triggers.test.ts +50 -27
  458. package/src/resources/extensions/gsd/tests/commands-extensions-version-compare.test.ts +58 -0
  459. package/src/resources/extensions/gsd/tests/commands-worktree-clean.test.ts +48 -0
  460. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +142 -59
  461. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +7 -4
  462. package/src/resources/extensions/gsd/tests/completed-at-reconcile.test.ts +89 -32
  463. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +41 -23
  464. package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +3 -43
  465. package/src/resources/extensions/gsd/tests/debug-logger.test.ts +5 -3
  466. package/src/resources/extensions/gsd/tests/deferred-milestone-dir-4996.test.ts +116 -0
  467. package/src/resources/extensions/gsd/tests/discuss-empty-db-fallback.test.ts +22 -87
  468. package/src/resources/extensions/gsd/tests/discuss-queued-milestones.test.ts +7 -118
  469. package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +18 -60
  470. package/src/resources/extensions/gsd/tests/doctor-orphan-milestone-4996.test.ts +100 -0
  471. package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +14 -76
  472. package/src/resources/extensions/gsd/tests/ensure-preconditions-guard-4996.test.ts +93 -0
  473. package/src/resources/extensions/gsd/tests/false-degraded-mode-warning.test.ts +22 -83
  474. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +1 -63
  475. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed-runtime.test.ts +47 -0
  476. package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +26 -1
  477. package/src/resources/extensions/gsd/tests/gitignore-bg-shell-runtime.test.ts +63 -0
  478. package/src/resources/extensions/gsd/tests/google-search-stub.test.ts +25 -65
  479. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +30 -0
  480. package/src/resources/extensions/gsd/tests/gsd-no-project-error-runtime.test.ts +81 -0
  481. package/src/resources/extensions/gsd/tests/headless-answers.test.ts +14 -4
  482. package/src/resources/extensions/gsd/tests/health-widget.test.ts +22 -12
  483. package/src/resources/extensions/gsd/tests/help-menu-coverage.test.ts +57 -0
  484. package/src/resources/extensions/gsd/tests/home-dir.test.ts +52 -0
  485. package/src/resources/extensions/gsd/tests/import-done-milestones-runtime.test.ts +145 -0
  486. package/src/resources/extensions/gsd/tests/init-prefs-routing.test.ts +64 -1
  487. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +72 -1
  488. package/src/resources/extensions/gsd/tests/integration/token-savings.test.ts +0 -23
  489. package/src/resources/extensions/gsd/tests/memory-store.test.ts +128 -0
  490. package/src/resources/extensions/gsd/tests/memory-tools.test.ts +33 -1
  491. package/src/resources/extensions/gsd/tests/merge-self-branch-guard.test.ts +124 -0
  492. package/src/resources/extensions/gsd/tests/milestone-id-gap-reuse-4996.test.ts +152 -0
  493. package/src/resources/extensions/gsd/tests/milestone-report-path.test.ts +18 -1
  494. package/src/resources/extensions/gsd/tests/model-router.test.ts +169 -8
  495. package/src/resources/extensions/gsd/tests/native-git-infra-errors.test.ts +50 -0
  496. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +8 -0
  497. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +32 -43
  498. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +4 -10
  499. package/src/resources/extensions/gsd/tests/preferences.test.ts +127 -0
  500. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +16 -0
  501. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +7 -0
  502. package/src/resources/extensions/gsd/tests/quick-turn-end-cleanup.test.ts +6 -6
  503. package/src/resources/extensions/gsd/tests/register-hooks-compaction-checkpoint.test.ts +93 -0
  504. package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +34 -0
  505. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +168 -19
  506. package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +7 -1
  507. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +23 -1
  508. package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +8 -2
  509. package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +12 -6
  510. package/src/resources/extensions/gsd/tests/steer-worktree-path.test.ts +17 -1
  511. package/src/resources/extensions/gsd/tests/system-context-message-routing.test.ts +101 -0
  512. package/src/resources/extensions/gsd/tests/token-profile.test.ts +51 -4
  513. package/src/resources/extensions/gsd/tests/turn-epoch.test.ts +7 -16
  514. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +38 -3
  515. package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +5 -7
  516. package/src/resources/extensions/gsd/tests/uok-gitops-turn-action.test.ts +15 -1
  517. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +6 -6
  518. package/src/resources/extensions/gsd/tests/worktree-path-injection.test.ts +235 -0
  519. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +85 -0
  520. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +34 -33
  521. package/src/resources/extensions/gsd/tests/worktree.test.ts +8 -0
  522. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +131 -1
  523. package/src/resources/extensions/gsd/tools/memory-tools.ts +17 -1
  524. package/src/resources/extensions/gsd/unit-context-manifest.ts +44 -12
  525. package/src/resources/extensions/gsd/visualizer-overlay.ts +1 -1
  526. package/src/resources/extensions/gsd/watch/header-renderer.ts +3 -1
  527. package/src/resources/extensions/gsd/workflow-logger.ts +1 -0
  528. package/src/resources/extensions/gsd/worktree-command.ts +31 -44
  529. package/src/resources/extensions/gsd/worktree-manager.ts +40 -1
  530. package/src/resources/extensions/gsd/worktree-resolver.ts +28 -14
  531. package/src/resources/extensions/gsd/worktree-root.ts +144 -0
  532. package/src/resources/extensions/gsd/worktree-session-state.ts +35 -0
  533. package/src/resources/extensions/gsd/worktree.ts +8 -119
  534. package/src/resources/extensions/mcp-client/index.ts +6 -10
  535. package/src/resources/extensions/mcp-client/tests/global-config.test.ts +91 -0
  536. package/src/resources/extensions/ollama/index.ts +16 -2
  537. package/src/resources/extensions/ollama/model-capabilities.ts +34 -0
  538. package/src/resources/extensions/ollama/ollama-client.ts +41 -4
  539. package/src/resources/extensions/ollama/tests/model-capabilities.test.ts +96 -0
  540. package/src/resources/extensions/ollama/tests/ollama-client-timeout-env.test.ts +147 -0
  541. package/src/resources/extensions/slash-commands/create-extension.ts +38 -24
  542. package/src/resources/extensions/subagent/index.ts +165 -7
  543. package/src/resources/skills/create-gsd-extension/SKILL.md +9 -5
  544. package/src/resources/skills/create-gsd-extension/references/custom-commands.md +1 -1
  545. package/src/resources/skills/create-gsd-extension/references/custom-rendering.md +5 -5
  546. package/src/resources/skills/create-gsd-extension/references/custom-tools.md +4 -4
  547. package/src/resources/skills/create-gsd-extension/references/custom-ui.md +6 -6
  548. package/src/resources/skills/create-gsd-extension/references/events-reference.md +3 -3
  549. package/src/resources/skills/create-gsd-extension/references/packaging-distribution.md +1 -1
  550. package/src/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +3 -3
  551. package/src/resources/skills/create-gsd-extension/templates/extension-skeleton.ts +2 -2
  552. package/src/resources/skills/create-gsd-extension/templates/stateful-tool-skeleton.ts +3 -3
  553. package/src/resources/skills/create-gsd-extension/templates/templates.test.ts +58 -0
  554. package/src/resources/skills/create-gsd-extension/workflows/create-extension.md +32 -12
  555. package/src/resources/skills/lint/SKILL.md +4 -0
  556. package/src/resources/skills/review/SKILL.md +4 -0
  557. package/src/resources/skills/test/SKILL.md +3 -0
  558. package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +0 -601
  559. package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +0 -651
  560. package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +0 -91
  561. package/dist/resources/extensions/gsd/tests/auto-supervisor.test.mjs +0 -53
  562. package/dist/resources/extensions/gsd/tests/dist-redirect.mjs +0 -112
  563. package/dist/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +0 -23
  564. package/dist/resources/extensions/gsd/tests/resolve-ts.mjs +0 -5
  565. package/dist/resources/skills/github-workflows/references/gh/tests/__init__.py +0 -0
  566. package/dist/resources/skills/github-workflows/references/gh/tests/test_github_project_setup.py +0 -608
  567. package/dist/web/standalone/.next/static/chunks/2826.e9f5195e91f9cad2.js +0 -11
  568. package/dist/web/standalone/.next/static/chunks/3621.fc7480022c972438.js +0 -20
  569. package/dist/web/standalone/.next/static/chunks/app/page-151349214571e2b6.js +0 -1
  570. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  571. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  572. package/dist/web/standalone/.next/static/chunks/webpack-2e68521d7c82f7c2.js +0 -1
  573. package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +0 -22
  574. package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +0 -47
  575. package/src/resources/extensions/gsd/tests/empty-content-abort-loop.test.ts +0 -75
  576. /package/dist/web/standalone/.next/static/{C1zT2kEfoLhDdbWPWKrXd → hcvW7f3yv1JHzlWe7tIc6}/_buildManifest.js +0 -0
  577. /package/dist/web/standalone/.next/static/{C1zT2kEfoLhDdbWPWKrXd → hcvW7f3yv1JHzlWe7tIc6}/_ssgManifest.js +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"repair-tool-json.test.js","sourceRoot":"","sources":["../../../src/utils/tests/repair-tool-json.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, test } from \"node:test\";\nimport assert from \"node:assert/strict\";\nimport { repairToolJson, hasYamlBulletLists, hasXmlParameterTags, hasTruncatedNumbers } from \"../repair-tool-json.js\";\n\ndescribe(\"repairToolJson — YAML bullet list repair (#2660)\", () => {\n\t// ── Detection ──────────────────────────────────────────────────────────\n\n\ttest(\"hasYamlBulletLists detects YAML-style bullets\", () => {\n\t\tassert.equal(\n\t\t\thasYamlBulletLists('\"keyDecisions\": - Used Web Notification API'),\n\t\t\ttrue,\n\t\t);\n\t});\n\n\ttest(\"hasYamlBulletLists ignores negative numbers\", () => {\n\t\tassert.equal(\n\t\t\thasYamlBulletLists('\"offset\": -1'),\n\t\t\tfalse,\n\t\t\t\"negative number should not be detected as YAML bullet\",\n\t\t);\n\t});\n\n\ttest(\"hasYamlBulletLists returns false for valid JSON\", () => {\n\t\tassert.equal(\n\t\t\thasYamlBulletLists('{\"keyDecisions\": [\"item1\", \"item2\"]}'),\n\t\t\tfalse,\n\t\t);\n\t});\n\n\t// ── Single bullet item ────────────────────────────────────────────────\n\n\ttest(\"repairs single YAML bullet to JSON array\", () => {\n\t\tconst malformed = '{\"keyDecisions\": - Used Web Notification API}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\t\tassert.deepEqual(parsed.keyDecisions, [\"Used Web Notification API\"]);\n\t});\n\n\t// ── Multiple bullet items (newline-separated) ─────────────────────────\n\n\ttest(\"repairs multiple YAML bullets separated by newlines\", () => {\n\t\tconst malformed =\n\t\t\t'{\"keyDecisions\": - Used Web Notification API\\n - Chose Tauri over Electron\\n - Adopted SQLite for storage, \"title\": \"M005\"}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\t\tassert.deepEqual(parsed.keyDecisions, [\n\t\t\t\"Used Web Notification API\",\n\t\t\t\"Chose Tauri over Electron\",\n\t\t\t\"Adopted SQLite for storage\",\n\t\t]);\n\t\tassert.equal(parsed.title, \"M005\");\n\t});\n\n\t// ── Multiple fields with YAML bullets ─────────────────────────────────\n\n\ttest(\"repairs multiple fields each with YAML bullet lists\", () => {\n\t\tconst malformed =\n\t\t\t'{\"keyDecisions\": - decision one\\n - decision two, \"keyFiles\": - src/lib.rs — Extended menu\\n - src/main.ts — Entry point, \"title\": \"done\"}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\t\tassert.deepEqual(parsed.keyDecisions, [\"decision one\", \"decision two\"]);\n\t\tassert.deepEqual(parsed.keyFiles, [\n\t\t\t\"src/lib.rs \\u2014 Extended menu\",\n\t\t\t\"src/main.ts \\u2014 Entry point\",\n\t\t]);\n\t\tassert.equal(parsed.title, \"done\");\n\t});\n\n\t// ── Exact reproduction from issue #2660 ───────────────────────────────\n\n\ttest(\"repairs the exact malformed JSON from issue #2660\", () => {\n\t\tconst malformed = `{\"milestoneId\": \"M005\", \"title\": \"Native Desktop Polish\", \"oneLiner\": \"summary\", \"narrative\": \"details\", \"successCriteriaResults\": \"all pass\", \"definitionOfDoneResults\": \"all done\", \"requirementOutcomes\": \"met\", \"keyDecisions\": - Used Web Notification API (new window.Notification()) instead of Tauri sendNotification wrapper, \"keyFiles\": - src-tauri/src/lib.rs \\u2014 Extended menu builder with notification toggle, \"lessonsLearned\": - Always test notification permissions before sending, \"followUps\": \"none\", \"deviations\": \"none\", \"verificationPassed\": true}`;\n\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\n\t\tassert.equal(parsed.milestoneId, \"M005\");\n\t\tassert.equal(parsed.title, \"Native Desktop Polish\");\n\t\tassert.ok(Array.isArray(parsed.keyDecisions), \"keyDecisions should be an array\");\n\t\tassert.ok(parsed.keyDecisions[0].includes(\"Web Notification API\"));\n\t\tassert.ok(Array.isArray(parsed.keyFiles), \"keyFiles should be an array\");\n\t\tassert.ok(parsed.keyFiles[0].includes(\"src-tauri/src/lib.rs\"));\n\t\tassert.ok(Array.isArray(parsed.lessonsLearned), \"lessonsLearned should be an array\");\n\t\tassert.equal(parsed.verificationPassed, true);\n\t});\n\n\t// ── Passthrough for valid JSON ────────────────────────────────────────\n\n\ttest(\"returns valid JSON unchanged\", () => {\n\t\tconst valid = '{\"keyDecisions\": [\"item1\", \"item2\"], \"count\": -5}';\n\t\tconst result = repairToolJson(valid);\n\t\tassert.equal(result, valid, \"valid JSON should be returned unchanged\");\n\t});\n\n\t// ── Negative numbers are preserved ────────────────────────────────────\n\n\ttest(\"does not mangle negative numbers\", () => {\n\t\tconst valid = '{\"offset\": -1, \"limit\": -100}';\n\t\tconst result = repairToolJson(valid);\n\t\tassert.equal(result, valid);\n\t});\n});\n\n// ═══════════════════════════════════════════════════════════════════════════\n// XML parameter tag repair (#3403)\n// ═══════════════════════════════════════════════════════════════════════════\n\ndescribe(\"repairToolJson — XML parameter tag stripping (#3403)\", () => {\n\ttest(\"hasXmlParameterTags detects opening tags\", () => {\n\t\tassert.equal(\n\t\t\thasXmlParameterTags('<parameter name=\"narrative\">some text</parameter>'),\n\t\t\ttrue,\n\t\t);\n\t});\n\n\ttest(\"hasXmlParameterTags returns false for clean JSON\", () => {\n\t\tassert.equal(\n\t\t\thasXmlParameterTags('{\"narrative\": \"some text\"}'),\n\t\t\tfalse,\n\t\t);\n\t});\n\n\ttest(\"strips XML parameter tags from JSON values\", () => {\n\t\tconst malformed = '{\"sliceId\": \"S03\", \"narrative\": <parameter name=\"narrative\">The slice work</parameter>}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\t// After stripping tags, the content should be parseable or at least tag-free\n\t\tassert.ok(!repaired.includes(\"<parameter\"), \"should not contain <parameter tags\");\n\t\tassert.ok(!repaired.includes(\"</parameter>\"), \"should not contain </parameter> tags\");\n\t});\n\n\ttest(\"handles mixed XML and JSON content\", () => {\n\t\tconst malformed = '{\"oneLiner\": \"done\", \"verification\": <parameter name=\"verification\">all tests pass</parameter>}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tassert.ok(!repaired.includes(\"<parameter\"), \"XML tags should be stripped\");\n\t\tassert.ok(repaired.includes(\"all tests pass\"), \"content should be preserved\");\n\t});\n\n\ttest(\"promotes XML parameters trapped inside valid JSON string values\", () => {\n\t\tconst malformed =\n\t\t\t'{\"narrative\":\"text.</narrative>\\\\n<parameter name=\\\\\"verification\\\\\">all tests pass</parameter>\\\\n<parameter name=\\\\\"verificationEvidence\\\\\">[\\\\\"npm test\\\\\"]</parameter>\",\"oneLiner\":\"done\"}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\n\t\tassert.equal(parsed.narrative, \"text.\");\n\t\tassert.equal(parsed.verification, \"all tests pass\");\n\t\tassert.deepEqual(parsed.verificationEvidence, [\"npm test\"]);\n\t\tassert.equal(parsed.oneLiner, \"done\");\n\t\tassert.ok(!parsed.narrative.includes(\"<parameter\"), \"narrative should not retain leaked XML\");\n\t});\n});\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Truncated number repair (#3464)\n// ═══════════════════════════════════════════════════════════════════════════\n\ndescribe(\"repairToolJson — truncated number repair (#3464)\", () => {\n\ttest(\"hasTruncatedNumbers detects bare comma after colon\", () => {\n\t\tassert.equal(hasTruncatedNumbers('\"exitCode\": ,'), true);\n\t});\n\n\ttest(\"hasTruncatedNumbers detects bare minus before comma\", () => {\n\t\tassert.equal(hasTruncatedNumbers('\"exitCode\": -,'), true);\n\t});\n\n\ttest(\"hasTruncatedNumbers detects bare minus before closing brace\", () => {\n\t\tassert.equal(hasTruncatedNumbers('\"durationMs\": -}'), true);\n\t});\n\n\ttest(\"hasTruncatedNumbers returns false for valid numbers\", () => {\n\t\tassert.equal(hasTruncatedNumbers('\"exitCode\": 0, \"durationMs\": 1234'), false);\n\t});\n\n\ttest(\"hasTruncatedNumbers returns false for negative numbers\", () => {\n\t\tassert.equal(hasTruncatedNumbers('\"exitCode\": -1, \"offset\": -100'), false);\n\t});\n\n\ttest(\"repairs truncated exitCode with bare comma\", () => {\n\t\tconst malformed = '{\"command\": \"npm test\", \"exitCode\": , \"verdict\": \"pass\", \"durationMs\": 500}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\t\tassert.equal(parsed.exitCode, 0);\n\t\tassert.equal(parsed.durationMs, 500);\n\t});\n\n\ttest(\"repairs truncated exitCode with bare minus\", () => {\n\t\tconst malformed = '{\"command\": \"npm test\", \"exitCode\": -, \"verdict\": \"pass\", \"durationMs\": 1234}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\t\tassert.equal(parsed.exitCode, 0);\n\t\tassert.equal(parsed.verdict, \"pass\");\n\t});\n\n\ttest(\"repairs truncated durationMs at end of object\", () => {\n\t\tconst malformed = '{\"command\": \"npm test\", \"exitCode\": 0, \"verdict\": \"pass\", \"durationMs\": -}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\t\tassert.equal(parsed.durationMs, 0);\n\t\tassert.equal(parsed.exitCode, 0);\n\t});\n\n\ttest(\"does not mangle valid negative numbers\", () => {\n\t\tconst valid = '{\"exitCode\": -1, \"offset\": -100}';\n\t\tconst repaired = repairToolJson(valid);\n\t\tconst parsed = JSON.parse(repaired);\n\t\tassert.equal(parsed.exitCode, -1);\n\t\tassert.equal(parsed.offset, -100);\n\t});\n});\n"]}
1
+ {"version":3,"file":"repair-tool-json.test.js","sourceRoot":"","sources":["../../../src/utils/tests/repair-tool-json.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, test } from \"node:test\";\nimport assert from \"node:assert/strict\";\nimport { repairToolJson, hasYamlBulletLists, hasXmlParameterTags, hasTruncatedNumbers } from \"../repair-tool-json.js\";\n\ndescribe(\"repairToolJson — YAML bullet list repair (#2660)\", () => {\n\t// ── Detection ──────────────────────────────────────────────────────────\n\n\ttest(\"hasYamlBulletLists detects YAML-style bullets\", () => {\n\t\tassert.equal(\n\t\t\thasYamlBulletLists('\"keyDecisions\": - Used Web Notification API'),\n\t\t\ttrue,\n\t\t);\n\t});\n\n\ttest(\"hasYamlBulletLists ignores negative numbers\", () => {\n\t\tassert.equal(\n\t\t\thasYamlBulletLists('\"offset\": -1'),\n\t\t\tfalse,\n\t\t\t\"negative number should not be detected as YAML bullet\",\n\t\t);\n\t});\n\n\ttest(\"hasYamlBulletLists returns false for valid JSON\", () => {\n\t\tassert.equal(\n\t\t\thasYamlBulletLists('{\"keyDecisions\": [\"item1\", \"item2\"]}'),\n\t\t\tfalse,\n\t\t);\n\t});\n\n\t// ── Single bullet item ────────────────────────────────────────────────\n\n\ttest(\"repairs single YAML bullet to JSON array\", () => {\n\t\tconst malformed = '{\"keyDecisions\": - Used Web Notification API}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\t\tassert.deepEqual(parsed.keyDecisions, [\"Used Web Notification API\"]);\n\t});\n\n\t// ── Multiple bullet items (newline-separated) ─────────────────────────\n\n\ttest(\"repairs multiple YAML bullets separated by newlines\", () => {\n\t\tconst malformed =\n\t\t\t'{\"keyDecisions\": - Used Web Notification API\\n - Chose Tauri over Electron\\n - Adopted SQLite for storage, \"title\": \"M005\"}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\t\tassert.deepEqual(parsed.keyDecisions, [\n\t\t\t\"Used Web Notification API\",\n\t\t\t\"Chose Tauri over Electron\",\n\t\t\t\"Adopted SQLite for storage\",\n\t\t]);\n\t\tassert.equal(parsed.title, \"M005\");\n\t});\n\n\t// ── Multiple fields with YAML bullets ─────────────────────────────────\n\n\ttest(\"repairs multiple fields each with YAML bullet lists\", () => {\n\t\tconst malformed =\n\t\t\t'{\"keyDecisions\": - decision one\\n - decision two, \"keyFiles\": - src/lib.rs — Extended menu\\n - src/main.ts — Entry point, \"title\": \"done\"}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\t\tassert.deepEqual(parsed.keyDecisions, [\"decision one\", \"decision two\"]);\n\t\tassert.deepEqual(parsed.keyFiles, [\n\t\t\t\"src/lib.rs \\u2014 Extended menu\",\n\t\t\t\"src/main.ts \\u2014 Entry point\",\n\t\t]);\n\t\tassert.equal(parsed.title, \"done\");\n\t});\n\n\t// ── Exact reproduction from issue #2660 ───────────────────────────────\n\n\ttest(\"repairs the exact malformed JSON from issue #2660\", () => {\n\t\tconst malformed = `{\"milestoneId\": \"M005\", \"title\": \"Native Desktop Polish\", \"oneLiner\": \"summary\", \"narrative\": \"details\", \"successCriteriaResults\": \"all pass\", \"definitionOfDoneResults\": \"all done\", \"requirementOutcomes\": \"met\", \"keyDecisions\": - Used Web Notification API (new window.Notification()) instead of Tauri sendNotification wrapper, \"keyFiles\": - src-tauri/src/lib.rs \\u2014 Extended menu builder with notification toggle, \"lessonsLearned\": - Always test notification permissions before sending, \"followUps\": \"none\", \"deviations\": \"none\", \"verificationPassed\": true}`;\n\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\n\t\tassert.equal(parsed.milestoneId, \"M005\");\n\t\tassert.equal(parsed.title, \"Native Desktop Polish\");\n\t\tassert.ok(Array.isArray(parsed.keyDecisions), \"keyDecisions should be an array\");\n\t\tassert.ok(parsed.keyDecisions[0].includes(\"Web Notification API\"));\n\t\tassert.ok(Array.isArray(parsed.keyFiles), \"keyFiles should be an array\");\n\t\tassert.ok(parsed.keyFiles[0].includes(\"src-tauri/src/lib.rs\"));\n\t\tassert.ok(Array.isArray(parsed.lessonsLearned), \"lessonsLearned should be an array\");\n\t\tassert.equal(parsed.verificationPassed, true);\n\t});\n\n\t// ── Passthrough for valid JSON ────────────────────────────────────────\n\n\ttest(\"returns valid JSON unchanged\", () => {\n\t\tconst valid = '{\"keyDecisions\": [\"item1\", \"item2\"], \"count\": -5}';\n\t\tconst result = repairToolJson(valid);\n\t\tassert.equal(result, valid, \"valid JSON should be returned unchanged\");\n\t});\n\n\t// ── Negative numbers are preserved ────────────────────────────────────\n\n\ttest(\"does not mangle negative numbers\", () => {\n\t\tconst valid = '{\"offset\": -1, \"limit\": -100}';\n\t\tconst result = repairToolJson(valid);\n\t\tassert.equal(result, valid);\n\t});\n});\n\n// ═══════════════════════════════════════════════════════════════════════════\n// XML parameter tag repair (#3403)\n// ═══════════════════════════════════════════════════════════════════════════\n\ndescribe(\"repairToolJson — XML parameter tag stripping (#3403)\", () => {\n\ttest(\"hasXmlParameterTags detects opening tags\", () => {\n\t\tassert.equal(\n\t\t\thasXmlParameterTags('<parameter name=\"narrative\">some text</parameter>'),\n\t\t\ttrue,\n\t\t);\n\t});\n\n\ttest(\"hasXmlParameterTags returns false for clean JSON\", () => {\n\t\tassert.equal(\n\t\t\thasXmlParameterTags('{\"narrative\": \"some text\"}'),\n\t\t\tfalse,\n\t\t);\n\t});\n\n\ttest(\"strips XML parameter tags from JSON values\", () => {\n\t\tconst malformed = '{\"sliceId\": \"S03\", \"narrative\": <parameter name=\"narrative\">The slice work</parameter>}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\t// After stripping tags, the content should be parseable or at least tag-free\n\t\tassert.ok(!repaired.includes(\"<parameter\"), \"should not contain <parameter tags\");\n\t\tassert.ok(!repaired.includes(\"</parameter>\"), \"should not contain </parameter> tags\");\n\t});\n\n\ttest(\"handles mixed XML and JSON content\", () => {\n\t\tconst malformed = '{\"oneLiner\": \"done\", \"verification\": <parameter name=\"verification\">all tests pass</parameter>}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tassert.ok(!repaired.includes(\"<parameter\"), \"XML tags should be stripped\");\n\t\tassert.ok(repaired.includes(\"all tests pass\"), \"content should be preserved\");\n\t});\n\n\ttest(\"promotes XML parameters trapped inside valid JSON string values\", () => {\n\t\tconst malformed =\n\t\t\t'{\"narrative\":\"text.</narrative>\\\\n<parameter name=\\\\\"verification\\\\\">all tests pass</parameter>\\\\n<parameter name=\\\\\"verificationEvidence\\\\\">[\\\\\"npm test\\\\\"]</parameter>\",\"oneLiner\":\"done\"}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\n\t\tassert.equal(parsed.narrative, \"text.\");\n\t\tassert.equal(parsed.verification, \"all tests pass\");\n\t\tassert.deepEqual(parsed.verificationEvidence, [\"npm test\"]);\n\t\tassert.equal(parsed.oneLiner, \"done\");\n\t\tassert.ok(!parsed.narrative.includes(\"<parameter\"), \"narrative should not retain leaked XML\");\n\t});\n\n\ttest(\"promotes dangling XML parameters trapped inside valid JSON string values\", () => {\n\t\tconst malformed = JSON.stringify({\n\t\t\tnarrative:\n\t\t\t\t'text.\\n<parameter name=\"verification\">all tests pass\\n<parameter name=\"verificationEvidence\">[\"npm test\"]',\n\t\t\toneLiner: \"done\",\n\t\t});\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\n\t\tassert.equal(parsed.narrative, \"text.\");\n\t\tassert.equal(parsed.verification, \"all tests pass\");\n\t\tassert.deepEqual(parsed.verificationEvidence, [\"npm test\"]);\n\t\tassert.equal(parsed.oneLiner, \"done\");\n\t\tassert.ok(!parsed.narrative.includes(\"<parameter\"), \"narrative should not retain leaked XML\");\n\t});\n\n\ttest(\"promotes mixed dangling and closed XML parameters from valid JSON string values\", () => {\n\t\tconst malformed = JSON.stringify({\n\t\t\tnarrative:\n\t\t\t\t'text.\\n<parameter name=\"verification\">all tests pass\\n<parameter name=\"verificationEvidence\">[\"npm test\"]</parameter>',\n\t\t\toneLiner: \"done\",\n\t\t});\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\n\t\tassert.equal(parsed.narrative, \"text.\");\n\t\tassert.equal(parsed.verification, \"all tests pass\");\n\t\tassert.deepEqual(parsed.verificationEvidence, [\"npm test\"]);\n\t\tassert.equal(parsed.oneLiner, \"done\");\n\t\tassert.ok(!parsed.narrative.includes(\"<parameter\"), \"narrative should not retain leaked XML\");\n\t});\n});\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Truncated number repair (#3464)\n// ═══════════════════════════════════════════════════════════════════════════\n\ndescribe(\"repairToolJson — truncated number repair (#3464)\", () => {\n\ttest(\"hasTruncatedNumbers detects bare comma after colon\", () => {\n\t\tassert.equal(hasTruncatedNumbers('\"exitCode\": ,'), true);\n\t});\n\n\ttest(\"hasTruncatedNumbers detects bare minus before comma\", () => {\n\t\tassert.equal(hasTruncatedNumbers('\"exitCode\": -,'), true);\n\t});\n\n\ttest(\"hasTruncatedNumbers detects bare minus before closing brace\", () => {\n\t\tassert.equal(hasTruncatedNumbers('\"durationMs\": -}'), true);\n\t});\n\n\ttest(\"hasTruncatedNumbers returns false for valid numbers\", () => {\n\t\tassert.equal(hasTruncatedNumbers('\"exitCode\": 0, \"durationMs\": 1234'), false);\n\t});\n\n\ttest(\"hasTruncatedNumbers returns false for negative numbers\", () => {\n\t\tassert.equal(hasTruncatedNumbers('\"exitCode\": -1, \"offset\": -100'), false);\n\t});\n\n\ttest(\"repairs truncated exitCode with bare comma\", () => {\n\t\tconst malformed = '{\"command\": \"npm test\", \"exitCode\": , \"verdict\": \"pass\", \"durationMs\": 500}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\t\tassert.equal(parsed.exitCode, 0);\n\t\tassert.equal(parsed.durationMs, 500);\n\t});\n\n\ttest(\"repairs truncated exitCode with bare minus\", () => {\n\t\tconst malformed = '{\"command\": \"npm test\", \"exitCode\": -, \"verdict\": \"pass\", \"durationMs\": 1234}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\t\tassert.equal(parsed.exitCode, 0);\n\t\tassert.equal(parsed.verdict, \"pass\");\n\t});\n\n\ttest(\"repairs truncated durationMs at end of object\", () => {\n\t\tconst malformed = '{\"command\": \"npm test\", \"exitCode\": 0, \"verdict\": \"pass\", \"durationMs\": -}';\n\t\tconst repaired = repairToolJson(malformed);\n\t\tconst parsed = JSON.parse(repaired);\n\t\tassert.equal(parsed.durationMs, 0);\n\t\tassert.equal(parsed.exitCode, 0);\n\t});\n\n\ttest(\"does not mangle valid negative numbers\", () => {\n\t\tconst valid = '{\"exitCode\": -1, \"offset\": -100}';\n\t\tconst repaired = repairToolJson(valid);\n\t\tconst parsed = JSON.parse(repaired);\n\t\tassert.equal(parsed.exitCode, -1);\n\t\tassert.equal(parsed.offset, -100);\n\t});\n});\n"]}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gsd/pi-ai",
3
- "version": "2.78.0",
3
+ "version": "2.78.1",
4
4
  "description": "Unified LLM API (vendored from pi-mono)",
5
5
  "type": "module",
6
6
  "gsd": {
@@ -0,0 +1,289 @@
1
+ // @gsd/pi-ai + anthropic-shared.cache-breakpoint.test — coverage for #5027.
2
+ // `convertMessages` must apply Anthropic `cache_control` to:
3
+ // - the last message (existing volatile-suffix anchor — preserved)
4
+ // - the most recent message flagged with `cacheBreakpoint: true`
5
+ // (new compaction-boundary anchor)
6
+ // And it must NOT exceed the 4-breakpoint limit by treating multiple
7
+ // breakpoints as one — only the most recent earns the marker.
8
+
9
+ import { describe, test } from "node:test";
10
+ import assert from "node:assert/strict";
11
+
12
+ import { buildParams, convertMessages } from "./anthropic-shared.js";
13
+ import type { Context, Message, Model, Tool } from "../types.js";
14
+ import type { AnthropicApi } from "./anthropic-shared.js";
15
+
16
+ // Minimal model stub — convertMessages only reads `input` to decide whether to
17
+ // drop image blocks. Returning ["image"] keeps the conversion paths exercised.
18
+ const model = { input: ["text", "image"] } as unknown as Model<AnthropicApi>;
19
+ const cacheControl = { type: "ephemeral" as const };
20
+
21
+ function userMsg(text: string, opts: { cacheBreakpoint?: boolean } = {}): Message {
22
+ return {
23
+ role: "user",
24
+ content: text,
25
+ timestamp: 0,
26
+ ...(opts.cacheBreakpoint ? { cacheBreakpoint: true } : {}),
27
+ } as Message;
28
+ }
29
+
30
+ /** Produces a UserMessage whose content is an array of text blocks —
31
+ * the production shape emitted by `convertToLlm()` for compaction summaries. */
32
+ function userMsgArray(text: string, opts: { cacheBreakpoint?: boolean } = {}): Message {
33
+ return {
34
+ role: "user",
35
+ content: [{ type: "text", text }],
36
+ timestamp: 0,
37
+ ...(opts.cacheBreakpoint ? { cacheBreakpoint: true } : {}),
38
+ } as Message;
39
+ }
40
+
41
+ function assistantMsg(text: string): Message {
42
+ return {
43
+ role: "assistant",
44
+ content: [{ type: "text", text }],
45
+ api: "anthropic-messages",
46
+ provider: "anthropic",
47
+ model: "claude-sonnet-4-6",
48
+ usage: {
49
+ input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0,
50
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
51
+ },
52
+ stopReason: "stop",
53
+ timestamp: 0,
54
+ } as Message;
55
+ }
56
+
57
+ /** Returns whether the message at the given index has cache_control on its last block. */
58
+ function hasCacheControl(params: ReturnType<typeof convertMessages>, index: number): boolean {
59
+ const param = params[index];
60
+ if (!param || param.role !== "user") return false;
61
+ if (!Array.isArray(param.content)) return false;
62
+ const lastBlock = param.content[param.content.length - 1];
63
+ return Boolean(lastBlock && (lastBlock as any).cache_control);
64
+ }
65
+
66
+ describe("convertMessages — cache breakpoints (#5027)", () => {
67
+ test("with no cacheControl option: no breakpoints are placed", () => {
68
+ const result = convertMessages([userMsg("hello"), assistantMsg("hi"), userMsg("again")], model, false);
69
+ for (let i = 0; i < result.length; i++) {
70
+ assert.equal(hasCacheControl(result, i), false, `index ${i} should have no cache_control`);
71
+ }
72
+ });
73
+
74
+ test("no cacheBreakpoint anywhere: only the last message gets cache_control (existing behavior preserved)", () => {
75
+ const result = convertMessages(
76
+ [userMsg("first"), assistantMsg("response"), userMsg("second")],
77
+ model,
78
+ false,
79
+ cacheControl,
80
+ );
81
+ assert.equal(hasCacheControl(result, 0), false, "first user msg has no breakpoint");
82
+ assert.equal(hasCacheControl(result, result.length - 1), true, "last msg gets the volatile-suffix anchor");
83
+ });
84
+
85
+ test("one cacheBreakpoint message: both that message AND the last message get breakpoints", () => {
86
+ const result = convertMessages(
87
+ [
88
+ userMsg("ancient"),
89
+ assistantMsg("ancient response"),
90
+ userMsg("[COMPACTION SUMMARY]", { cacheBreakpoint: true }),
91
+ assistantMsg("post-compaction response"),
92
+ userMsg("new turn"),
93
+ ],
94
+ model,
95
+ false,
96
+ cacheControl,
97
+ );
98
+ // Find the compaction-summary index (it's the third user-shaped param)
99
+ const compactionIdx = result.findIndex(
100
+ (p) => p.role === "user" && Array.isArray(p.content) && (p.content as any)[0]?.text?.includes("COMPACTION SUMMARY"),
101
+ );
102
+ assert.ok(compactionIdx >= 0, "compaction summary should be in the params");
103
+ assert.equal(hasCacheControl(result, compactionIdx), true, "compaction boundary gets a breakpoint");
104
+ assert.equal(hasCacheControl(result, result.length - 1), true, "last msg still gets the volatile-suffix anchor");
105
+ });
106
+
107
+ test("array-content cacheBreakpoint message: breakpoint is applied (production shape for compaction summary)", () => {
108
+ // convertToLlm() emits compaction summaries as content:[{type:"text",...}];
109
+ // this exercises the array-backed branch in anthropic-shared.ts.
110
+ const result = convertMessages(
111
+ [
112
+ userMsgArray("[COMPACTION SUMMARY]", { cacheBreakpoint: true }),
113
+ assistantMsg("post-compaction response"),
114
+ userMsg("post-compaction turn"),
115
+ ],
116
+ model,
117
+ false,
118
+ cacheControl,
119
+ );
120
+ const compactionIdx = result.findIndex(
121
+ (p) =>
122
+ p.role === "user" &&
123
+ Array.isArray(p.content) &&
124
+ (p.content as any)[0]?.text?.includes("COMPACTION SUMMARY"),
125
+ );
126
+ assert.ok(compactionIdx >= 0, "compaction summary param should be present");
127
+ assert.equal(hasCacheControl(result, compactionIdx), true, "array-content boundary gets cache_control");
128
+ assert.equal(hasCacheControl(result, result.length - 1), true, "last msg still gets the volatile-suffix anchor");
129
+ });
130
+
131
+ test("multiple cacheBreakpoint messages: only the most recent one earns a breakpoint (4-limit safety)", () => {
132
+ const result = convertMessages(
133
+ [
134
+ userMsg("[OLD COMPACTION]", { cacheBreakpoint: true }),
135
+ assistantMsg("post-old response"),
136
+ userMsg("[NEW COMPACTION]", { cacheBreakpoint: true }),
137
+ assistantMsg("post-new response"),
138
+ userMsg("latest turn"),
139
+ ],
140
+ model,
141
+ false,
142
+ cacheControl,
143
+ );
144
+ const oldIdx = result.findIndex(
145
+ (p) => p.role === "user" && Array.isArray(p.content) && (p.content as any)[0]?.text?.includes("OLD COMPACTION"),
146
+ );
147
+ const newIdx = result.findIndex(
148
+ (p) => p.role === "user" && Array.isArray(p.content) && (p.content as any)[0]?.text?.includes("NEW COMPACTION"),
149
+ );
150
+ assert.equal(hasCacheControl(result, oldIdx), false, "older boundary should not earn a breakpoint");
151
+ assert.equal(hasCacheControl(result, newIdx), true, "most recent boundary earns the breakpoint");
152
+ assert.equal(hasCacheControl(result, result.length - 1), true, "last msg still gets the volatile-suffix anchor");
153
+ });
154
+
155
+ test("cacheBreakpoint on the LAST message: only one breakpoint applied (deduplication)", () => {
156
+ // When the boundary message IS the last message, applying twice would be
157
+ // a no-op overwrite but the deduplication guard avoids the double-call.
158
+ const result = convertMessages(
159
+ [userMsg("hello"), userMsg("[BOUNDARY AS LAST]", { cacheBreakpoint: true })],
160
+ model,
161
+ false,
162
+ cacheControl,
163
+ );
164
+ assert.equal(hasCacheControl(result, result.length - 1), true);
165
+ // Only one user message besides the last, with no breakpoint
166
+ assert.equal(hasCacheControl(result, 0), false);
167
+ });
168
+
169
+ test("cacheBreakpoint flag is ignored when no cacheControl option is provided", () => {
170
+ const result = convertMessages(
171
+ [userMsg("[COMPACTION]", { cacheBreakpoint: true }), userMsg("turn")],
172
+ model,
173
+ false,
174
+ );
175
+ for (let i = 0; i < result.length; i++) {
176
+ assert.equal(hasCacheControl(result, i), false, `index ${i} should have no cache_control`);
177
+ }
178
+ });
179
+
180
+ test("array-content cacheBreakpoint on last message: deduplication guard prevents double application", () => {
181
+ // The boundary IS the last message — both anchors target the same param,
182
+ // so cache_control should appear exactly once.
183
+ const result = convertMessages(
184
+ [userMsg("prior turn"), userMsgArray("[BOUNDARY AS LAST]", { cacheBreakpoint: true })],
185
+ model,
186
+ false,
187
+ cacheControl,
188
+ );
189
+ const lastParam = result[result.length - 1];
190
+ assert.ok(lastParam && Array.isArray(lastParam.content), "last param has array content");
191
+ const cacheBlocks = (lastParam!.content as any[]).filter((b) => b.cache_control);
192
+ assert.equal(cacheBlocks.length, 1, "cache_control applied exactly once");
193
+ assert.equal(hasCacheControl(result, 0), false, "prior turn has no cache_control");
194
+ });
195
+ });
196
+
197
+ // ─── 4-breakpoint-limit safety at buildParams level (OAuth path) ──────────
198
+
199
+ /** Count cache_control occurrences across system + tools + messages params. */
200
+ function countBreakpoints(params: { system?: any; tools?: any[]; messages: any[] }): number {
201
+ let n = 0;
202
+ if (Array.isArray(params.system)) {
203
+ for (const block of params.system) if (block.cache_control) n++;
204
+ }
205
+ if (Array.isArray(params.tools)) {
206
+ for (const tool of params.tools) if ((tool as any).cache_control) n++;
207
+ }
208
+ for (const m of params.messages) {
209
+ if (m.role === "user" && Array.isArray(m.content)) {
210
+ for (const block of m.content) if (block.cache_control) n++;
211
+ }
212
+ }
213
+ return n;
214
+ }
215
+
216
+ const buildParamsModel = {
217
+ id: "claude-sonnet-4-6",
218
+ baseUrl: "https://api.anthropic.com",
219
+ api: "anthropic-messages",
220
+ input: ["text", "image"],
221
+ maxTokens: 64000,
222
+ } as unknown as Model<AnthropicApi>;
223
+
224
+ describe("buildParams — 4-breakpoint limit safety in OAuth + boundary scenario (#5027)", () => {
225
+ test("OAuth + system prompt + last user: ≤2 breakpoints (no boundary, no tools)", () => {
226
+ const ctx: Context = {
227
+ messages: [userMsg("hello")],
228
+ systemPrompt: "You are a helpful coding assistant.",
229
+ } as Context;
230
+ const params = buildParams(buildParamsModel, ctx, true) as any;
231
+ assert.ok(countBreakpoints(params) <= 4, `expected ≤4 breakpoints, got ${countBreakpoints(params)}`);
232
+ // One on user system block, one on last user msg.
233
+ assert.equal(countBreakpoints(params), 2);
234
+ });
235
+
236
+ test("OAuth + system prompt + boundary + last user: ≤3 breakpoints (system de-duplicated)", () => {
237
+ const ctx: Context = {
238
+ messages: [
239
+ userMsg("[COMPACTION SUMMARY]", { cacheBreakpoint: true }),
240
+ userMsg("post-compaction turn"),
241
+ ],
242
+ systemPrompt: "You are a helpful coding assistant.",
243
+ } as Context;
244
+ const params = buildParams(buildParamsModel, ctx, true) as any;
245
+ const count = countBreakpoints(params);
246
+ assert.ok(count <= 4, `must stay under Anthropic's 4-breakpoint limit, got ${count}`);
247
+ // system(1, the user's prompt — Claude Code header skipped) + boundary(1) + last(1) = 3.
248
+ assert.equal(count, 3);
249
+ });
250
+
251
+ test("OAuth + system prompt + tools + boundary + last user: exactly 4 breakpoints (ceiling)", () => {
252
+ // Worst-case breakpoint budget:
253
+ // system(user prompt, 1) + last tool(1) + boundary(1) + last user(1) = 4.
254
+ // The "You are Claude Code" header intentionally carries NO cache_control
255
+ // when a user systemPrompt is present (#5027), which keeps us at 4 rather than 5.
256
+ const tool: Tool = {
257
+ name: "Read",
258
+ description: "Read a file from disk.",
259
+ parameters: {
260
+ type: "object" as const,
261
+ properties: {
262
+ path: { type: "string" },
263
+ },
264
+ required: ["path"],
265
+ } as any,
266
+ };
267
+ const ctx: Context = {
268
+ messages: [
269
+ userMsg("[COMPACTION SUMMARY]", { cacheBreakpoint: true }),
270
+ userMsg("post-compaction turn"),
271
+ ],
272
+ systemPrompt: "You are a helpful coding assistant.",
273
+ tools: [tool],
274
+ } as Context;
275
+ const params = buildParams(buildParamsModel, ctx, true) as any;
276
+ const count = countBreakpoints(params);
277
+ assert.ok(count <= 4, `must stay under Anthropic's 4-breakpoint limit, got ${count}`);
278
+ // system(1) + tool(1) + boundary(1) + last-user(1) = 4 exactly.
279
+ assert.equal(count, 4);
280
+ });
281
+
282
+ test("OAuth header WITHOUT user systemPrompt still cache-marks the header", () => {
283
+ // When there's no user systemPrompt, the Claude Code header IS the
284
+ // last system block, so it correctly carries cache_control.
285
+ const ctx: Context = { messages: [userMsg("hello")] } as Context;
286
+ const params = buildParams(buildParamsModel, ctx, true) as any;
287
+ assert.equal(countBreakpoints(params), 2, "header(1) + last user(1) = 2");
288
+ });
289
+ });
@@ -262,6 +262,11 @@ export function convertMessages(
262
262
  cacheControl?: { type: "ephemeral"; ttl?: "1h" },
263
263
  ): MessageParam[] {
264
264
  const params: MessageParam[] = [];
265
+ // Indices into `params` for messages flagged with `cacheBreakpoint: true` —
266
+ // e.g. compaction summaries. We apply cache_control to the most recent one
267
+ // (in addition to the last message) so the stable summary + kept-history
268
+ // block can earn cache reads on every post-compaction turn. (#5027)
269
+ const breakpointIndices: number[] = [];
265
270
 
266
271
  const transformedMessages = transformMessagesWithReport(messages, model, normalizeToolCallId, "anthropic-messages");
267
272
 
@@ -275,6 +280,7 @@ export function convertMessages(
275
280
  role: "user",
276
281
  content: sanitizeSurrogates(msg.content),
277
282
  });
283
+ if (msg.cacheBreakpoint) breakpointIndices.push(params.length - 1);
278
284
  }
279
285
  } else {
280
286
  const blocks: ContentBlockParam[] = msg.content.map((item) => {
@@ -306,6 +312,7 @@ export function convertMessages(
306
312
  role: "user",
307
313
  content: filteredBlocks,
308
314
  });
315
+ if (msg.cacheBreakpoint) breakpointIndices.push(params.length - 1);
309
316
  }
310
317
  } else if (msg.role === "assistant") {
311
318
  const blocks: ContentBlockParam[] = [];
@@ -403,31 +410,50 @@ export function convertMessages(
403
410
  }
404
411
 
405
412
  if (cacheControl && params.length > 0) {
406
- const lastMessage = params[params.length - 1];
407
- if (lastMessage.role === "user") {
408
- if (Array.isArray(lastMessage.content)) {
409
- const lastBlock = lastMessage.content[lastMessage.content.length - 1];
410
- if (
411
- lastBlock &&
412
- (lastBlock.type === "text" || lastBlock.type === "image" || lastBlock.type === "tool_result")
413
- ) {
414
- (lastBlock as any).cache_control = cacheControl;
415
- }
416
- } else if (typeof lastMessage.content === "string") {
417
- lastMessage.content = [
418
- {
419
- type: "text",
420
- text: lastMessage.content,
421
- cache_control: cacheControl,
422
- },
423
- ] as any;
424
- }
413
+ // Apply to the volatile suffix anchor (last user message) — existing behavior.
414
+ applyCacheControlToParam(params, params.length - 1, cacheControl);
415
+
416
+ // Apply to the most recent compaction-boundary message, if any. Capping at
417
+ // one boundary keeps us safely under Anthropic's 4-breakpoint limit
418
+ // (system + tools + boundary + last user = 4). If multiple
419
+ // cacheBreakpoint messages are present, only the most recent one — the
420
+ // freshest stable boundary — earns the breakpoint. (#5027)
421
+ const mostRecentBreakpoint = breakpointIndices[breakpointIndices.length - 1];
422
+ if (mostRecentBreakpoint !== undefined && mostRecentBreakpoint !== params.length - 1) {
423
+ applyCacheControlToParam(params, mostRecentBreakpoint, cacheControl);
425
424
  }
426
425
  }
427
426
 
428
427
  return params;
429
428
  }
430
429
 
430
+ /** Apply `cache_control` to the last cacheable block of the user-role param at `index`. No-op for non-user roles. */
431
+ function applyCacheControlToParam(
432
+ params: MessageParam[],
433
+ index: number,
434
+ cacheControl: { type: "ephemeral"; ttl?: "1h" },
435
+ ): void {
436
+ const param = params[index];
437
+ if (!param || param.role !== "user") return;
438
+ if (Array.isArray(param.content)) {
439
+ const lastBlock = param.content[param.content.length - 1];
440
+ if (
441
+ lastBlock &&
442
+ (lastBlock.type === "text" || lastBlock.type === "image" || lastBlock.type === "tool_result")
443
+ ) {
444
+ (lastBlock as any).cache_control = cacheControl;
445
+ }
446
+ } else if (typeof param.content === "string") {
447
+ param.content = [
448
+ {
449
+ type: "text",
450
+ text: param.content,
451
+ cache_control: cacheControl,
452
+ },
453
+ ] as any;
454
+ }
455
+ }
456
+
431
457
  /** Convert GSD tools to Anthropic SDK tool definitions, applying cache control to the last entry. */
432
458
  export function convertTools(
433
459
  tools: Tool[],
@@ -475,11 +501,17 @@ export function buildParams(
475
501
  };
476
502
 
477
503
  if (isOAuthToken) {
504
+ // Only the LAST system block carries `cache_control` — the boundary
505
+ // covers the entire system prefix up to that point. Putting cache_control
506
+ // on the short "You are Claude Code" header AND the user systemPrompt
507
+ // would consume two of Anthropic's 4 breakpoint slots for redundant
508
+ // coverage, leaving no room for a compaction-summary breakpoint. (#5027)
509
+ const hasUserSystemPrompt = Boolean(context.systemPrompt);
478
510
  params.system = [
479
511
  {
480
512
  type: "text",
481
513
  text: "You are Claude Code, Anthropic's official CLI for Claude.",
482
- ...(cacheControl ? { cache_control: cacheControl } : {}),
514
+ ...(cacheControl && !hasUserSystemPrompt ? { cache_control: cacheControl } : {}),
483
515
  },
484
516
  ];
485
517
  if (context.systemPrompt) {
@@ -201,6 +201,19 @@ export interface UserMessage {
201
201
  role: "user";
202
202
  content: string | (TextContent | ImageContent)[];
203
203
  timestamp: number; // Unix timestamp in milliseconds
204
+ /**
205
+ * Hint to provider adapters that this message marks a stable point in the
206
+ * conversation suitable as a prompt-cache anchor. Providers that support
207
+ * prompt caching (e.g. Anthropic) may apply a `cache_control` breakpoint
208
+ * here so the prefix up to and including this message can earn cache
209
+ * reads on subsequent turns.
210
+ *
211
+ * Optional and additive — providers that do not support caching, or
212
+ * messages without the hint, behave exactly as before. Set by upstream
213
+ * code that knows the message represents a stable boundary (e.g. a
214
+ * compaction summary). (#5027)
215
+ */
216
+ cacheBreakpoint?: boolean;
204
217
  }
205
218
 
206
219
  export interface AssistantMessage {
@@ -61,6 +61,7 @@ type XmlParameterBlock = {
61
61
  };
62
62
 
63
63
  const xmlParameterBlockPattern = /<parameter\s+name="([^"]+)"\s*>([\s\S]*?)<\/parameter>/g;
64
+ const xmlParameterOpenPattern = /<parameter\s+name="([^"]+)"\s*>/g;
64
65
 
65
66
  function parseXmlParameterValue(raw: string): unknown {
66
67
  const trimmed = raw.trim();
@@ -73,11 +74,31 @@ function parseXmlParameterValue(raw: string): unknown {
73
74
  }
74
75
 
75
76
  function extractXmlParameterBlocks(text: string): XmlParameterBlock[] {
76
- const blocks: XmlParameterBlock[] = [];
77
+ const strictBlocks: XmlParameterBlock[] = [];
78
+ let hasNestedParameterOpening = false;
77
79
  for (const match of text.matchAll(xmlParameterBlockPattern)) {
78
- blocks.push({
80
+ const rawValue = match[2] ?? "";
81
+ hasNestedParameterOpening ||= rawValue.includes("<parameter");
82
+ strictBlocks.push({
79
83
  name: match[1],
80
- value: parseXmlParameterValue(match[2] ?? ""),
84
+ value: parseXmlParameterValue(rawValue),
85
+ });
86
+ }
87
+ if (strictBlocks.length > 0 && !hasNestedParameterOpening) return strictBlocks;
88
+
89
+ const blocks: XmlParameterBlock[] = [];
90
+ const openings = [...text.matchAll(xmlParameterOpenPattern)];
91
+ for (let i = 0; i < openings.length; i++) {
92
+ const current = openings[i];
93
+ const next = openings[i + 1];
94
+ if (current.index === undefined) continue;
95
+
96
+ const start = current.index + current[0].length;
97
+ const end = next?.index ?? text.length;
98
+ const rawValue = text.slice(start, end).replace(/\s*<\/parameter>\s*$/, "");
99
+ blocks.push({
100
+ name: current[1],
101
+ value: parseXmlParameterValue(rawValue),
81
102
  });
82
103
  }
83
104
  return blocks;
@@ -147,6 +147,38 @@ describe("repairToolJson — XML parameter tag stripping (#3403)", () => {
147
147
  assert.equal(parsed.oneLiner, "done");
148
148
  assert.ok(!parsed.narrative.includes("<parameter"), "narrative should not retain leaked XML");
149
149
  });
150
+
151
+ test("promotes dangling XML parameters trapped inside valid JSON string values", () => {
152
+ const malformed = JSON.stringify({
153
+ narrative:
154
+ 'text.\n<parameter name="verification">all tests pass\n<parameter name="verificationEvidence">["npm test"]',
155
+ oneLiner: "done",
156
+ });
157
+ const repaired = repairToolJson(malformed);
158
+ const parsed = JSON.parse(repaired);
159
+
160
+ assert.equal(parsed.narrative, "text.");
161
+ assert.equal(parsed.verification, "all tests pass");
162
+ assert.deepEqual(parsed.verificationEvidence, ["npm test"]);
163
+ assert.equal(parsed.oneLiner, "done");
164
+ assert.ok(!parsed.narrative.includes("<parameter"), "narrative should not retain leaked XML");
165
+ });
166
+
167
+ test("promotes mixed dangling and closed XML parameters from valid JSON string values", () => {
168
+ const malformed = JSON.stringify({
169
+ narrative:
170
+ 'text.\n<parameter name="verification">all tests pass\n<parameter name="verificationEvidence">["npm test"]</parameter>',
171
+ oneLiner: "done",
172
+ });
173
+ const repaired = repairToolJson(malformed);
174
+ const parsed = JSON.parse(repaired);
175
+
176
+ assert.equal(parsed.narrative, "text.");
177
+ assert.equal(parsed.verification, "all tests pass");
178
+ assert.deepEqual(parsed.verificationEvidence, ["npm test"]);
179
+ assert.equal(parsed.oneLiner, "done");
180
+ assert.ok(!parsed.narrative.includes("<parameter"), "narrative should not retain leaked XML");
181
+ });
150
182
  });
151
183
 
152
184
  // ═══════════════════════════════════════════════════════════════════════════