gsd-pi 2.80.0-dev.3065f7bfa → 2.80.0-dev.488219eb6

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 (376) hide show
  1. package/dist/claude-cli-check.d.ts +30 -0
  2. package/dist/claude-cli-check.js +18 -7
  3. package/dist/cli.js +0 -19
  4. package/dist/headless-query.d.ts +10 -0
  5. package/dist/headless-query.js +6 -4
  6. package/dist/loader-entrypoint.d.ts +8 -0
  7. package/dist/loader-entrypoint.js +27 -0
  8. package/dist/loader.js +2 -11
  9. package/dist/mcp-server.d.ts +1 -0
  10. package/dist/mcp-server.js +6 -3
  11. package/dist/resources/.managed-resources-content-hash +1 -1
  12. package/dist/resources/extensions/claude-code-cli/readiness.js +18 -7
  13. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +29 -0
  14. package/dist/resources/extensions/github-sync/sync.js +4 -1
  15. package/dist/resources/extensions/gsd/auto/loop.js +71 -8
  16. package/dist/resources/extensions/gsd/auto/phases.js +32 -8
  17. package/dist/resources/extensions/gsd/auto/resolve.js +12 -0
  18. package/dist/resources/extensions/gsd/auto/run-unit.js +3 -1
  19. package/dist/resources/extensions/gsd/auto/workflow-dispatch-claim.js +33 -1
  20. package/dist/resources/extensions/gsd/auto/workflow-worker-heartbeat.js +9 -1
  21. package/dist/resources/extensions/gsd/auto-post-unit.js +81 -76
  22. package/dist/resources/extensions/gsd/auto-start.js +41 -6
  23. package/dist/resources/extensions/gsd/auto-worktree.js +98 -80
  24. package/dist/resources/extensions/gsd/auto.js +84 -29
  25. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +62 -13
  26. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +20 -26
  27. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +29 -12
  28. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +6 -9
  29. package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +4 -7
  30. package/dist/resources/extensions/gsd/bootstrap/memory-tools.js +6 -10
  31. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +5 -9
  32. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +1 -1
  33. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +60 -26
  34. package/dist/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.js +4 -0
  35. package/dist/resources/extensions/gsd/commands/handlers/ops.js +2 -2
  36. package/dist/resources/extensions/gsd/commands-config.js +1 -1
  37. package/dist/resources/extensions/gsd/commands-eval-review.js +2 -2
  38. package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -1
  39. package/dist/resources/extensions/gsd/db/unit-dispatches.js +53 -0
  40. package/dist/resources/extensions/gsd/git-service.js +37 -1
  41. package/dist/resources/extensions/gsd/guided-flow.js +14 -4
  42. package/dist/resources/extensions/gsd/init-wizard.js +4 -1
  43. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +6 -4
  44. package/dist/resources/extensions/gsd/slice-cadence.js +45 -2
  45. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +15 -9
  46. package/dist/resources/extensions/gsd/tools/complete-milestone.js +6 -7
  47. package/dist/resources/extensions/gsd/tools/complete-task.js +1 -1
  48. package/dist/resources/extensions/gsd/worktree-resolver.js +22 -1
  49. package/dist/resources/skills/web-quality-audit/scripts/analyze.sh +0 -0
  50. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  51. package/dist/web/standalone/.next/BUILD_ID +1 -1
  52. package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
  53. package/dist/web/standalone/.next/build-manifest.json +3 -3
  54. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  55. package/dist/web/standalone/.next/react-loadable-manifest.json +2 -2
  56. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  57. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  58. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  66. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  67. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/index.html +1 -1
  75. package/dist/web/standalone/.next/server/app/index.rsc +2 -2
  76. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  77. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
  78. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  82. package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
  83. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  84. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/middleware.js +3 -3
  86. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  87. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  88. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  89. package/dist/web/standalone/.next/static/chunks/8359.e059d86b255fce1c.js +10 -0
  90. package/dist/web/standalone/.next/static/chunks/app/{page-fab3ebb85b006001.js → page-752f1e2ebdaa3e45.js} +1 -1
  91. package/dist/web/standalone/.next/static/chunks/{webpack-0481f1221120a7c6.js → webpack-de742b64187e13fe.js} +1 -1
  92. package/dist/welcome-screen.d.ts +2 -0
  93. package/dist/welcome-screen.js +9 -7
  94. package/package.json +1 -1
  95. package/packages/pi-ai/dist/providers/anthropic-auth.test.js +35 -13
  96. package/packages/pi-ai/dist/providers/anthropic-auth.test.js.map +1 -1
  97. package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.js +21 -11
  98. package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.js.map +1 -1
  99. package/packages/pi-ai/dist/providers/anthropic.d.ts +7 -0
  100. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  101. package/packages/pi-ai/dist/providers/anthropic.js +9 -7
  102. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  103. package/packages/pi-ai/dist/providers/minimax-tool-name.test.js +23 -14
  104. package/packages/pi-ai/dist/providers/minimax-tool-name.test.js.map +1 -1
  105. package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js +48 -21
  106. package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js.map +1 -1
  107. package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.js +22 -21
  108. package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.js.map +1 -1
  109. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.js +22 -21
  110. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.js.map +1 -1
  111. package/packages/pi-ai/src/providers/anthropic-auth.test.ts +39 -25
  112. package/packages/pi-ai/src/providers/anthropic-bearer-auth.test.ts +26 -22
  113. package/packages/pi-ai/src/providers/anthropic.ts +22 -9
  114. package/packages/pi-ai/src/providers/minimax-tool-name.test.ts +34 -21
  115. package/packages/pi-ai/src/utils/oauth/github-copilot.test.ts +56 -22
  116. package/packages/pi-ai/src/utils/oauth/google-antigravity.test.ts +24 -28
  117. package/packages/pi-ai/src/utils/oauth/google-gemini-cli.test.ts +24 -28
  118. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  119. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +3 -1
  120. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
  121. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -2
  122. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  123. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  124. package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.js +22 -56
  125. package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.js.map +1 -1
  126. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts +1 -0
  127. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
  128. package/packages/pi-coding-agent/dist/core/tools/bash.js +3 -1
  129. package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
  130. package/packages/pi-coding-agent/dist/core/tools/edit.d.ts +2 -0
  131. package/packages/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
  132. package/packages/pi-coding-agent/dist/core/tools/edit.js +12 -1
  133. package/packages/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
  134. package/packages/pi-coding-agent/dist/core/tools/find.d.ts +2 -0
  135. package/packages/pi-coding-agent/dist/core/tools/find.d.ts.map +1 -1
  136. package/packages/pi-coding-agent/dist/core/tools/find.js +14 -6
  137. package/packages/pi-coding-agent/dist/core/tools/find.js.map +1 -1
  138. package/packages/pi-coding-agent/dist/core/tools/grep.d.ts +2 -0
  139. package/packages/pi-coding-agent/dist/core/tools/grep.d.ts.map +1 -1
  140. package/packages/pi-coding-agent/dist/core/tools/grep.js +12 -3
  141. package/packages/pi-coding-agent/dist/core/tools/grep.js.map +1 -1
  142. package/packages/pi-coding-agent/dist/core/tools/hashline-read.d.ts +2 -0
  143. package/packages/pi-coding-agent/dist/core/tools/hashline-read.d.ts.map +1 -1
  144. package/packages/pi-coding-agent/dist/core/tools/hashline-read.js +3 -1
  145. package/packages/pi-coding-agent/dist/core/tools/hashline-read.js.map +1 -1
  146. package/packages/pi-coding-agent/dist/core/tools/index.d.ts +2 -1
  147. package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
  148. package/packages/pi-coding-agent/dist/core/tools/index.js +1 -0
  149. package/packages/pi-coding-agent/dist/core/tools/index.js.map +1 -1
  150. package/packages/pi-coding-agent/dist/core/tools/ls.d.ts +2 -0
  151. package/packages/pi-coding-agent/dist/core/tools/ls.d.ts.map +1 -1
  152. package/packages/pi-coding-agent/dist/core/tools/ls.js +10 -3
  153. package/packages/pi-coding-agent/dist/core/tools/ls.js.map +1 -1
  154. package/packages/pi-coding-agent/dist/core/tools/read.d.ts +2 -0
  155. package/packages/pi-coding-agent/dist/core/tools/read.d.ts.map +1 -1
  156. package/packages/pi-coding-agent/dist/core/tools/read.js +3 -1
  157. package/packages/pi-coding-agent/dist/core/tools/read.js.map +1 -1
  158. package/packages/pi-coding-agent/dist/core/tools/spawn-shell-windows.test.js +7 -62
  159. package/packages/pi-coding-agent/dist/core/tools/spawn-shell-windows.test.js.map +1 -1
  160. package/packages/pi-coding-agent/dist/core/tools/tool-target-metadata.test.d.ts +2 -0
  161. package/packages/pi-coding-agent/dist/core/tools/tool-target-metadata.test.d.ts.map +1 -0
  162. package/packages/pi-coding-agent/dist/core/tools/tool-target-metadata.test.js +115 -0
  163. package/packages/pi-coding-agent/dist/core/tools/tool-target-metadata.test.js.map +1 -0
  164. package/packages/pi-coding-agent/dist/core/tools/tool-target.d.ts +19 -0
  165. package/packages/pi-coding-agent/dist/core/tools/tool-target.d.ts.map +1 -0
  166. package/packages/pi-coding-agent/dist/core/tools/tool-target.js +20 -0
  167. package/packages/pi-coding-agent/dist/core/tools/tool-target.js.map +1 -0
  168. package/packages/pi-coding-agent/dist/core/tools/write.d.ts +4 -0
  169. package/packages/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
  170. package/packages/pi-coding-agent/dist/core/tools/write.js +9 -1
  171. package/packages/pi-coding-agent/dist/core/tools/write.js.map +1 -1
  172. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +89 -2
  173. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  174. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +25 -1
  175. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -1
  176. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +5 -0
  177. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  178. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +153 -8
  179. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  180. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  181. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +18 -1
  182. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  183. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +23 -5
  184. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -1
  185. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +2 -0
  186. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  187. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +19 -0
  188. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  189. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js +23 -5
  190. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js.map +1 -1
  191. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +3 -1
  192. package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -1
  193. package/packages/pi-coding-agent/src/core/tools/bash-spawn-windows.test.ts +22 -66
  194. package/packages/pi-coding-agent/src/core/tools/bash.ts +4 -1
  195. package/packages/pi-coding-agent/src/core/tools/edit.ts +13 -1
  196. package/packages/pi-coding-agent/src/core/tools/find.ts +15 -6
  197. package/packages/pi-coding-agent/src/core/tools/grep.ts +13 -3
  198. package/packages/pi-coding-agent/src/core/tools/hashline-read.ts +4 -1
  199. package/packages/pi-coding-agent/src/core/tools/index.ts +8 -0
  200. package/packages/pi-coding-agent/src/core/tools/ls.ts +11 -3
  201. package/packages/pi-coding-agent/src/core/tools/read.ts +4 -1
  202. package/packages/pi-coding-agent/src/core/tools/spawn-shell-windows.test.ts +11 -72
  203. package/packages/pi-coding-agent/src/core/tools/tool-target-metadata.test.ts +127 -0
  204. package/packages/pi-coding-agent/src/core/tools/tool-target.ts +48 -0
  205. package/packages/pi-coding-agent/src/core/tools/write.ts +14 -2
  206. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +119 -2
  207. package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +31 -1
  208. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +177 -8
  209. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +24 -5
  210. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +17 -1
  211. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +21 -0
  212. package/packages/pi-coding-agent/src/resources/extensions/memory/storage-safety-guard.test.ts +25 -8
  213. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  214. package/src/resources/extensions/claude-code-cli/readiness.ts +25 -7
  215. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +30 -0
  216. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +26 -0
  217. package/src/resources/extensions/github-sync/sync.ts +8 -1
  218. package/src/resources/extensions/github-sync/tests/sync-source.test.ts +6 -18
  219. package/src/resources/extensions/gsd/auto/loop.ts +84 -8
  220. package/src/resources/extensions/gsd/auto/phases.ts +43 -8
  221. package/src/resources/extensions/gsd/auto/resolve.ts +19 -0
  222. package/src/resources/extensions/gsd/auto/run-unit.ts +3 -0
  223. package/src/resources/extensions/gsd/auto/workflow-dispatch-claim.ts +63 -1
  224. package/src/resources/extensions/gsd/auto/workflow-worker-heartbeat.ts +14 -1
  225. package/src/resources/extensions/gsd/auto-post-unit.ts +93 -78
  226. package/src/resources/extensions/gsd/auto-start.ts +56 -6
  227. package/src/resources/extensions/gsd/auto-worktree.ts +118 -72
  228. package/src/resources/extensions/gsd/auto.ts +121 -29
  229. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +76 -12
  230. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +20 -26
  231. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +27 -12
  232. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +7 -9
  233. package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +4 -7
  234. package/src/resources/extensions/gsd/bootstrap/memory-tools.ts +6 -10
  235. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +5 -9
  236. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +1 -1
  237. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +64 -26
  238. package/src/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.ts +4 -0
  239. package/src/resources/extensions/gsd/commands/handlers/ops.ts +4 -2
  240. package/src/resources/extensions/gsd/commands-config.ts +1 -1
  241. package/src/resources/extensions/gsd/commands-eval-review.ts +2 -2
  242. package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -1
  243. package/src/resources/extensions/gsd/db/unit-dispatches.ts +66 -0
  244. package/src/resources/extensions/gsd/git-service.ts +41 -2
  245. package/src/resources/extensions/gsd/guided-flow.ts +19 -4
  246. package/src/resources/extensions/gsd/init-wizard.ts +5 -1
  247. package/src/resources/extensions/gsd/prompts/complete-milestone.md +6 -4
  248. package/src/resources/extensions/gsd/slice-cadence.ts +49 -2
  249. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +23 -9
  250. package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +59 -89
  251. package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +47 -172
  252. package/src/resources/extensions/gsd/tests/artifacts-table-preserved-on-cache-invalidate.test.ts +0 -35
  253. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +35 -9
  254. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +15 -0
  255. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +80 -59
  256. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +3 -47
  257. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +76 -18
  258. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +106 -0
  259. package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +54 -95
  260. package/src/resources/extensions/gsd/tests/auto-project-root-env.test.ts +67 -26
  261. package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +32 -30
  262. package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +32 -128
  263. package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +20 -54
  264. package/src/resources/extensions/gsd/tests/auto-start-cold-db-bootstrap.test.ts +20 -30
  265. package/src/resources/extensions/gsd/tests/auto-start-index-lock.test.ts +17 -29
  266. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +138 -0
  267. package/src/resources/extensions/gsd/tests/auto-start-time-persistence.test.ts +21 -39
  268. package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +15 -24
  269. package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +44 -29
  270. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +39 -51
  271. package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +1 -217
  272. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +15 -32
  273. package/src/resources/extensions/gsd/tests/browser-teardown.test.ts +0 -41
  274. package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +34 -27
  275. package/src/resources/extensions/gsd/tests/cmux.test.ts +51 -53
  276. package/src/resources/extensions/gsd/tests/cold-resume-db-reopen.test.ts +39 -61
  277. package/src/resources/extensions/gsd/tests/commands-config.test.ts +26 -19
  278. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +140 -0
  279. package/src/resources/extensions/gsd/tests/complete-task-normalize-lists.test.ts +29 -33
  280. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +45 -108
  281. package/src/resources/extensions/gsd/tests/crash-handler-secondary.test.ts +35 -31
  282. package/src/resources/extensions/gsd/tests/cwd-fallback-hardening.test.ts +138 -0
  283. package/src/resources/extensions/gsd/tests/dashboard-custom-engine.test.ts +4 -68
  284. package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +4 -9
  285. package/src/resources/extensions/gsd/tests/deferred-milestone-dir-4996.test.ts +14 -65
  286. package/src/resources/extensions/gsd/tests/discuss-tool-scoping.test.ts +44 -37
  287. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +58 -40
  288. package/src/resources/extensions/gsd/tests/dispatch-guard-closed-status.test.ts +25 -15
  289. package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +35 -17
  290. package/src/resources/extensions/gsd/tests/error-success-mask.test.ts +16 -21
  291. package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +15 -82
  292. package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +2 -20
  293. package/src/resources/extensions/gsd/tests/frontmatter-parse-noise.test.ts +18 -26
  294. package/src/resources/extensions/gsd/tests/init-skip-git.test.ts +9 -12
  295. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +40 -0
  296. package/src/resources/extensions/gsd/tests/integration/commands-eval-review.integration.test.ts +4 -2
  297. package/src/resources/extensions/gsd/tests/integration/git-locale.test.ts +31 -20
  298. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +12 -0
  299. package/src/resources/extensions/gsd/tests/integration/milestone-transition-worktree.test.ts +0 -47
  300. package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +60 -202
  301. package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +13 -59
  302. package/src/resources/extensions/gsd/tests/lazy-pi-tui-import.test.ts +44 -6
  303. package/src/resources/extensions/gsd/tests/memory-pressure-stuck-state.test.ts +21 -35
  304. package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +79 -98
  305. package/src/resources/extensions/gsd/tests/model-unittype-mapping.test.ts +70 -278
  306. package/src/resources/extensions/gsd/tests/needs-remediation-revalidation.test.ts +37 -30
  307. package/src/resources/extensions/gsd/tests/note-captures-executed.test.ts +32 -28
  308. package/src/resources/extensions/gsd/tests/phantom-ghost-detection.test.ts +24 -37
  309. package/src/resources/extensions/gsd/tests/phantom-milestone-default-queued.test.ts +9 -24
  310. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +113 -0
  311. package/src/resources/extensions/gsd/tests/post-unit-state-rebuild.test.ts +36 -22
  312. package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +36 -30
  313. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +74 -4
  314. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +20 -22
  315. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +130 -32
  316. package/src/resources/extensions/gsd/tests/project-root-cwd-crash.test.ts +18 -36
  317. package/src/resources/extensions/gsd/tests/projection-no-plan-overwrite.test.ts +35 -73
  318. package/src/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +76 -138
  319. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +70 -125
  320. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +59 -161
  321. package/src/resources/extensions/gsd/tests/query-tools-db-open.test.ts +33 -29
  322. package/src/resources/extensions/gsd/tests/queue-auto-guard.test.ts +22 -196
  323. package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +23 -93
  324. package/src/resources/extensions/gsd/tests/quick-turn-end-cleanup.test.ts +50 -79
  325. package/src/resources/extensions/gsd/tests/reassess-default-optin.test.ts +27 -13
  326. package/src/resources/extensions/gsd/tests/remote-questions.test.ts +151 -251
  327. package/src/resources/extensions/gsd/tests/resource-loader-import-path.test.ts +41 -29
  328. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +58 -90
  329. package/src/resources/extensions/gsd/tests/resume-dispatch-worktree.test.ts +32 -164
  330. package/src/resources/extensions/gsd/tests/run-uat-replay-cap.test.ts +57 -41
  331. package/src/resources/extensions/gsd/tests/session-model-override.test.ts +14 -9
  332. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +77 -0
  333. package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +61 -1
  334. package/src/resources/extensions/gsd/tests/show-config-command.test.ts +44 -42
  335. package/src/resources/extensions/gsd/tests/skip-slice-state-rebuild.test.ts +56 -24
  336. package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +51 -11
  337. package/src/resources/extensions/gsd/tests/slice-cadence.test.ts +23 -0
  338. package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +66 -50
  339. package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +68 -107
  340. package/src/resources/extensions/gsd/tests/slice-sequence-insert.test.ts +115 -42
  341. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +21 -97
  342. package/src/resources/extensions/gsd/tests/smart-entry-draft.test.ts +25 -116
  343. package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +21 -57
  344. package/src/resources/extensions/gsd/tests/stale-dirlistcache-4648.test.ts +29 -76
  345. package/src/resources/extensions/gsd/tests/stale-lockfile-recovery.test.ts +33 -24
  346. package/src/resources/extensions/gsd/tests/stale-slice-rows.test.ts +39 -30
  347. package/src/resources/extensions/gsd/tests/status-db-open.test.ts +33 -47
  348. package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +48 -46
  349. package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +14 -102
  350. package/src/resources/extensions/gsd/tests/subagent-model-dispatch.test.ts +78 -232
  351. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +32 -35
  352. package/src/resources/extensions/gsd/tests/token-profile.test.ts +84 -309
  353. package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +134 -341
  354. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +8 -25
  355. package/src/resources/extensions/gsd/tests/unit-dispatches.test.ts +50 -1
  356. package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +6 -101
  357. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +43 -36
  358. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -444
  359. package/src/resources/extensions/gsd/tests/workflow-dispatch-claim.test.ts +142 -0
  360. package/src/resources/extensions/gsd/tests/workflow-logger-wiring.test.ts +44 -189
  361. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +2 -57
  362. package/src/resources/extensions/gsd/tests/workflow-worker-heartbeat.test.ts +32 -1
  363. package/src/resources/extensions/gsd/tests/worktree-db-same-file.test.ts +21 -44
  364. package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +27 -26
  365. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +14 -13
  366. package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +20 -18
  367. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +36 -0
  368. package/src/resources/extensions/gsd/tests/zero-slice-roadmap-guided.test.ts +19 -13
  369. package/src/resources/extensions/gsd/tools/complete-milestone.ts +14 -15
  370. package/src/resources/extensions/gsd/tools/complete-task.ts +1 -1
  371. package/src/resources/extensions/gsd/worktree-resolver.ts +34 -1
  372. package/src/resources/skills/create-gsd-extension/templates/templates.test.ts +86 -40
  373. package/src/resources/skills/web-quality-audit/scripts/analyze.sh +0 -0
  374. package/dist/web/standalone/.next/static/chunks/8336.631939fb583761fa.js +0 -10
  375. /package/dist/web/standalone/.next/static/{O2WvnXROue6l7nAp_4dhC → G68KoLv5claox_Dyd4pZn}/_buildManifest.js +0 -0
  376. /package/dist/web/standalone/.next/static/{O2WvnXROue6l7nAp_4dhC → G68KoLv5claox_Dyd4pZn}/_ssgManifest.js +0 -0
@@ -1,10 +1,28 @@
1
1
  import assert from "node:assert/strict";
2
- import { readFileSync } from "node:fs";
2
+ import { lstatSync, mkdtempSync, readFileSync, rmSync, symlinkSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
3
4
  import { join } from "node:path";
4
5
  import test from "node:test";
5
- test("MemoryStorage persists sql.js snapshots through the atomic DB snapshot writer", () => {
6
- const src = readFileSync(join(process.cwd(), "packages", "pi-coding-agent", "src", "resources", "extensions", "memory", "storage.ts"), "utf-8");
7
- assert.match(src, /atomicWriteDbSnapshotSync\(/, "storage must use atomic DB snapshot writes");
8
- assert.doesNotMatch(src, /writeFileSync\(this\.dbPath/, "direct live DB overwrite is forbidden");
6
+ import { MemoryStorage } from "./storage.js";
7
+ test("MemoryStorage replaces a symlinked DB path instead of overwriting the symlink target", async (t) => {
8
+ const dir = mkdtempSync(join(tmpdir(), "memory-storage-atomic-"));
9
+ t.after(() => rmSync(dir, { recursive: true, force: true }));
10
+ const canaryPath = join(dir, "canary.txt");
11
+ const dbPath = join(dir, "memory.db");
12
+ const canaryStorage = await MemoryStorage.create(canaryPath);
13
+ canaryStorage.close();
14
+ const originalCanary = readFileSync(canaryPath);
15
+ symlinkSync(canaryPath, dbPath);
16
+ const storage = await MemoryStorage.create(dbPath);
17
+ storage.upsertThreads([{
18
+ threadId: "thread-1",
19
+ filePath: join(dir, "thread.jsonl"),
20
+ fileSize: 1,
21
+ fileMtime: 1,
22
+ cwd: dir,
23
+ }]);
24
+ storage.close();
25
+ assert.deepEqual(readFileSync(canaryPath), originalCanary);
26
+ assert.equal(lstatSync(dbPath).isSymbolicLink(), false, "atomic snapshot write should replace the symlink");
9
27
  });
10
28
  //# sourceMappingURL=storage-safety-guard.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"storage-safety-guard.test.js","sourceRoot":"","sources":["../../../../src/resources/extensions/memory/storage-safety-guard.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,IAAI,CAAC,+EAA+E,EAAE,GAAG,EAAE;IAC1F,MAAM,GAAG,GAAG,YAAY,CACvB,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,EAAE,iBAAiB,EAAE,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,YAAY,CAAC,EAC5G,OAAO,CACP,CAAC;IAEF,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,6BAA6B,EAAE,4CAA4C,CAAC,CAAC;IAC/F,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,6BAA6B,EAAE,uCAAuC,CAAC,CAAC;AAClG,CAAC,CAAC,CAAC","sourcesContent":["import assert from \"node:assert/strict\";\nimport { readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport test from \"node:test\";\n\ntest(\"MemoryStorage persists sql.js snapshots through the atomic DB snapshot writer\", () => {\n\tconst src = readFileSync(\n\t\tjoin(process.cwd(), \"packages\", \"pi-coding-agent\", \"src\", \"resources\", \"extensions\", \"memory\", \"storage.ts\"),\n\t\t\"utf-8\",\n\t);\n\n\tassert.match(src, /atomicWriteDbSnapshotSync\\(/, \"storage must use atomic DB snapshot writes\");\n\tassert.doesNotMatch(src, /writeFileSync\\(this\\.dbPath/, \"direct live DB overwrite is forbidden\");\n});\n"]}
1
+ {"version":3,"file":"storage-safety-guard.test.js","sourceRoot":"","sources":["../../../../src/resources/extensions/memory/storage-safety-guard.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACpF,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,IAAI,CAAC,sFAAsF,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;IACxG,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,wBAAwB,CAAC,CAAC,CAAC;IAClE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAE7D,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IACtC,MAAM,aAAa,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC7D,aAAa,CAAC,KAAK,EAAE,CAAC;IACtB,MAAM,cAAc,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;IAChD,WAAW,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAEhC,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACnD,OAAO,CAAC,aAAa,CAAC,CAAC;YACtB,QAAQ,EAAE,UAAU;YACpB,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC;YACnC,QAAQ,EAAE,CAAC;YACX,SAAS,EAAE,CAAC;YACZ,GAAG,EAAE,GAAG;SACR,CAAC,CAAC,CAAC;IACJ,OAAO,CAAC,KAAK,EAAE,CAAC;IAEhB,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC,UAAU,CAAC,EAAE,cAAc,CAAC,CAAC;IAC3D,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,cAAc,EAAE,EAAE,KAAK,EAAE,kDAAkD,CAAC,CAAC;AAC7G,CAAC,CAAC,CAAC","sourcesContent":["import assert from \"node:assert/strict\";\nimport { lstatSync, mkdtempSync, readFileSync, rmSync, symlinkSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport test from \"node:test\";\nimport { MemoryStorage } from \"./storage.js\";\n\ntest(\"MemoryStorage replaces a symlinked DB path instead of overwriting the symlink target\", async (t) => {\n\tconst dir = mkdtempSync(join(tmpdir(), \"memory-storage-atomic-\"));\n\tt.after(() => rmSync(dir, { recursive: true, force: true }));\n\n\tconst canaryPath = join(dir, \"canary.txt\");\n\tconst dbPath = join(dir, \"memory.db\");\n\tconst canaryStorage = await MemoryStorage.create(canaryPath);\n\tcanaryStorage.close();\n\tconst originalCanary = readFileSync(canaryPath);\n\tsymlinkSync(canaryPath, dbPath);\n\n\tconst storage = await MemoryStorage.create(dbPath);\n\tstorage.upsertThreads([{\n\t\tthreadId: \"thread-1\",\n\t\tfilePath: join(dir, \"thread.jsonl\"),\n\t\tfileSize: 1,\n\t\tfileMtime: 1,\n\t\tcwd: dir,\n\t}]);\n\tstorage.close();\n\n\tassert.deepEqual(readFileSync(canaryPath), originalCanary);\n\tassert.equal(lstatSync(dbPath).isSymbolicLink(), false, \"atomic snapshot write should replace the symlink\");\n});\n"]}
@@ -1049,7 +1049,9 @@ test("chat-controller rolls up only contiguous low-signal tool runs on message_e
1049
1049
  assert.equal(host.chatContainer.children[1]?.constructor?.name, "AssistantMessageComponent");
1050
1050
  assert.equal(host.chatContainer.children[2]?.constructor?.name, "ToolPhaseSummaryComponent");
1051
1051
  assert.match(host.chatContainer.children[0].render(120).join("\n"), /Setup \/ shell 2 actions/);
1052
- assert.match(host.chatContainer.children[2].render(120).join("\n"), /Context reads 2 actions/);
1052
+ const readSummary = host.chatContainer.children[2].render(120).join("\n");
1053
+ assert.match(readSummary, /Context reads · 2 files/);
1054
+ assert.match(readSummary, /\/tmp\/a · \/tmp\/b/);
1053
1055
  assert.equal(host.chatContainer._prevRender, null, "summary reposition must invalidate the chat container render cache");
1054
1056
  });
1055
1057
 
@@ -69,6 +69,7 @@ import type {
69
69
  LsToolInput,
70
70
  ReadToolDetails,
71
71
  ReadToolInput,
72
+ WriteToolDetails,
72
73
  WriteToolInput,
73
74
  } from "../tools/index.js";
74
75
 
@@ -1031,7 +1032,7 @@ export interface EditToolResultEvent extends ToolResultEventBase {
1031
1032
 
1032
1033
  export interface WriteToolResultEvent extends ToolResultEventBase {
1033
1034
  toolName: "write";
1034
- details: undefined;
1035
+ details: WriteToolDetails | undefined;
1035
1036
  }
1036
1037
 
1037
1038
  export interface GrepToolResultEvent extends ToolResultEventBase {
@@ -19,76 +19,32 @@
19
19
  import test from "node:test";
20
20
  import assert from "node:assert/strict";
21
21
  import { spawn } from "node:child_process";
22
+ import { createBashTool } from "./bash.js";
22
23
 
23
- // Verify the spawn option pattern used across the codebase.
24
- // This is a static/structural test it reads the source files and asserts
25
- // they use the platform-guarded detached flag.
26
- import { existsSync, readFileSync } from "node:fs";
27
- import { dirname, join, resolve } from "node:path";
28
- import { fileURLToPath } from "node:url";
29
-
30
- const __dirname = dirname(fileURLToPath(import.meta.url));
31
-
32
- function findRepoRoot(): string {
33
- const requiredFiles = [
34
- "packages/pi-coding-agent/src/core/tools/bash.ts",
35
- "packages/pi-coding-agent/src/core/bash-executor.ts",
36
- "packages/pi-coding-agent/src/utils/shell.ts",
37
- ];
38
- const candidates = [
39
- resolve(__dirname, "../../../../../"),
40
- resolve(__dirname, "../../../../../../"),
41
- ];
42
- for (const candidate of candidates) {
43
- if (requiredFiles.every((file) => existsSync(join(candidate, file)))) {
44
- return candidate;
45
- }
46
- }
47
- throw new Error(`Unable to resolve repository root from ${__dirname}`);
48
- }
49
-
50
- const REPO_ROOT = findRepoRoot();
51
-
52
- const SPAWN_FILES = [
53
- join(REPO_ROOT, "packages/pi-coding-agent/src/core/tools/bash.ts"),
54
- join(REPO_ROOT, "packages/pi-coding-agent/src/core/bash-executor.ts"),
55
- join(REPO_ROOT, "packages/pi-coding-agent/src/utils/shell.ts"),
56
- ];
57
-
58
- test("spawn calls use platform-guarded detached flag (no unconditional detached: true)", () => {
59
- for (const file of SPAWN_FILES) {
60
- const content = readFileSync(file, "utf-8");
61
- const lines = content.split("\n");
62
-
63
- for (let i = 0; i < lines.length; i++) {
64
- const line = lines[i]!;
65
- // Skip comments
66
- if (line.trim().startsWith("//") || line.trim().startsWith("*")) continue;
67
- // Check for unconditional `detached: true`
68
- if (/detached:\s*true\b/.test(line)) {
69
- assert.fail(
70
- `${file}:${i + 1} has unconditional 'detached: true' — ` +
71
- `must use 'detached: process.platform !== "win32"' ` +
72
- `to prevent EINVAL on Windows (ConPTY / VSCode terminal)`,
73
- );
74
- }
75
- }
76
- }
77
- });
24
+ test("bash tool delegates command execution through operations without forcing process detachment", async () => {
25
+ const calls: Array<{ command: string; cwd: string; env?: NodeJS.ProcessEnv }> = [];
26
+ const tool = createBashTool(process.cwd(), {
27
+ operations: {
28
+ async exec(command, cwd, options) {
29
+ calls.push({ command, cwd, env: options.env });
30
+ options.onData(Buffer.from("ok\n"));
31
+ return { exitCode: 0 };
32
+ },
33
+ },
34
+ });
78
35
 
79
- test("killProcessTree does not use detached: true for taskkill on Windows", () => {
80
- const shellFile = join(REPO_ROOT, "packages/pi-coding-agent/src/utils/shell.ts");
81
- const content = readFileSync(shellFile, "utf-8");
36
+ const result = await tool.execute("tool-call", { command: "echo ok" });
37
+ const firstContent = result.content[0];
82
38
 
83
- // Find the taskkill spawn call and ensure it doesn't have detached: true
84
- const taskkillRegion = content.match(/spawn\("taskkill"[\s\S]*?\}\)/);
85
- if (taskkillRegion) {
86
- assert.ok(
87
- !/detached:\s*true/.test(taskkillRegion[0]),
88
- "taskkill spawn should not use detached: true — " +
89
- "it can cause EINVAL on Windows and is unnecessary for a utility process",
90
- );
39
+ assert.ok(firstContent);
40
+ if (firstContent.type !== "text") {
41
+ assert.fail(`Expected text content, got ${firstContent.type}`);
91
42
  }
43
+ assert.equal(firstContent.text, "ok\n");
44
+ assert.deepEqual(calls.map((call) => ({ command: call.command, cwd: call.cwd })), [
45
+ { command: "echo ok", cwd: process.cwd() },
46
+ ]);
47
+ assert.ok(calls[0]?.env, "default bash context should provide an execution environment");
92
48
  });
93
49
 
94
50
  // Smoke test: spawn with platform-guarded detached flag actually works
@@ -116,6 +116,7 @@ const bashSchema = Type.Object({
116
116
  export type BashToolInput = Static<typeof bashSchema>;
117
117
 
118
118
  export interface BashToolDetails {
119
+ cwd?: string;
119
120
  truncation?: TruncationResult;
120
121
  fullOutputPath?: string;
121
122
  artifactId?: string;
@@ -384,6 +385,7 @@ export function createBashTool(cwd: string, options?: BashToolOptions): AgentToo
384
385
  onUpdate({
385
386
  content: [{ type: "text", text: truncation.content || "" }],
386
387
  details: {
388
+ cwd: spawnContext.cwd,
387
389
  truncation: truncation.truncated ? truncation : undefined,
388
390
  fullOutputPath: spillFilePath,
389
391
  },
@@ -412,10 +414,11 @@ export function createBashTool(cwd: string, options?: BashToolOptions): AgentToo
412
414
  let outputText = truncation.content || "(no output)";
413
415
 
414
416
  // Build details with truncation info
415
- let details: BashToolDetails | undefined;
417
+ let details: BashToolDetails | undefined = { cwd: spawnContext.cwd };
416
418
 
417
419
  if (truncation.truncated) {
418
420
  details = {
421
+ ...details,
419
422
  truncation,
420
423
  fullOutputPath: spillFilePath,
421
424
  ...(spillArtifactId ? { artifactId: spillArtifactId } : {}),
@@ -13,6 +13,7 @@ import {
13
13
  } from "./edit-diff.js";
14
14
  import { notifyFileChanged } from "../lsp/client.js";
15
15
  import { resolveToCwd } from "./path-utils.js";
16
+ import { createToolTarget, type ToolTargetMetadata } from "./tool-target.js";
16
17
 
17
18
  const editSchema = Type.Object({
18
19
  path: Type.String({ description: "Path to the file to edit (relative or absolute)" }),
@@ -27,6 +28,7 @@ export interface EditToolDetails {
27
28
  diff: string;
28
29
  /** Line number of the first change in the new file (for editor navigation) */
29
30
  firstChangedLine?: number;
31
+ target?: ToolTargetMetadata;
30
32
  }
31
33
 
32
34
  /**
@@ -208,7 +210,17 @@ export function createEditTool(cwd: string, options?: EditToolOptions): AgentToo
208
210
  text: `Successfully replaced text in ${path}.`,
209
211
  },
210
212
  ],
211
- details: { diff: diffResult.diff, firstChangedLine: diffResult.firstChangedLine },
213
+ details: {
214
+ diff: diffResult.diff,
215
+ firstChangedLine: diffResult.firstChangedLine,
216
+ target: createToolTarget({
217
+ kind: "file",
218
+ action: "edit",
219
+ inputPath: path,
220
+ resolvedPath: absolutePath,
221
+ line: diffResult.firstChangedLine,
222
+ }),
223
+ },
212
224
  });
213
225
  } catch (error: any) {
214
226
  // Clean up abort handler
@@ -5,6 +5,7 @@ import { existsSync } from "fs";
5
5
  import path from "path";
6
6
  import { FIND_DEFAULT_LIMIT } from "../constants.js";
7
7
  import { resolveToCwd } from "./path-utils.js";
8
+ import { createToolTarget, type ToolTargetMetadata } from "./tool-target.js";
8
9
  import { DEFAULT_MAX_BYTES, formatSize, type TruncationResult, truncateHead } from "./truncate.js";
9
10
 
10
11
  const findSchema = Type.Object({
@@ -20,6 +21,7 @@ export type FindToolInput = Static<typeof findSchema>;
20
21
  const DEFAULT_LIMIT = FIND_DEFAULT_LIMIT;
21
22
 
22
23
  export interface FindToolDetails {
24
+ target?: ToolTargetMetadata;
23
25
  truncation?: TruncationResult;
24
26
  resultLimitReached?: number;
25
27
  }
@@ -73,6 +75,13 @@ export function createFindTool(cwd: string, options?: FindToolOptions): AgentToo
73
75
  (async () => {
74
76
  try {
75
77
  const searchPath = resolveToCwd(searchDir || ".", cwd);
78
+ const target = createToolTarget({
79
+ kind: "search",
80
+ action: "find",
81
+ inputPath: searchDir || ".",
82
+ resolvedPath: searchPath,
83
+ pattern,
84
+ });
76
85
  const effectiveLimit = limit ?? DEFAULT_LIMIT;
77
86
  const ops = customOps ?? defaultFindOperations;
78
87
 
@@ -93,7 +102,7 @@ export function createFindTool(cwd: string, options?: FindToolOptions): AgentToo
93
102
  if (results.length === 0) {
94
103
  resolve({
95
104
  content: [{ type: "text", text: "No files found matching pattern" }],
96
- details: undefined,
105
+ details: { target },
97
106
  });
98
107
  return;
99
108
  }
@@ -111,7 +120,7 @@ export function createFindTool(cwd: string, options?: FindToolOptions): AgentToo
111
120
  const truncation = truncateHead(rawOutput, { maxLines: Number.MAX_SAFE_INTEGER });
112
121
 
113
122
  let resultOutput = truncation.content;
114
- const details: FindToolDetails = {};
123
+ const details: FindToolDetails = { target };
115
124
  const notices: string[] = [];
116
125
 
117
126
  if (resultLimitReached) {
@@ -130,7 +139,7 @@ export function createFindTool(cwd: string, options?: FindToolOptions): AgentToo
130
139
 
131
140
  resolve({
132
141
  content: [{ type: "text", text: resultOutput }],
133
- details: Object.keys(details).length > 0 ? details : undefined,
142
+ details,
134
143
  });
135
144
  return;
136
145
  }
@@ -150,7 +159,7 @@ export function createFindTool(cwd: string, options?: FindToolOptions): AgentToo
150
159
  if (globResult.matches.length === 0) {
151
160
  resolve({
152
161
  content: [{ type: "text", text: "No files found matching pattern" }],
153
- details: undefined,
162
+ details: { target },
154
163
  });
155
164
  return;
156
165
  }
@@ -163,7 +172,7 @@ export function createFindTool(cwd: string, options?: FindToolOptions): AgentToo
163
172
  const truncation = truncateHead(rawOutput, { maxLines: Number.MAX_SAFE_INTEGER });
164
173
 
165
174
  let resultOutput = truncation.content;
166
- const details: FindToolDetails = {};
175
+ const details: FindToolDetails = { target };
167
176
  const notices: string[] = [];
168
177
 
169
178
  if (resultLimitReached) {
@@ -184,7 +193,7 @@ export function createFindTool(cwd: string, options?: FindToolOptions): AgentToo
184
193
 
185
194
  resolve({
186
195
  content: [{ type: "text", text: resultOutput }],
187
- details: Object.keys(details).length > 0 ? details : undefined,
196
+ details,
188
197
  });
189
198
  } catch (e: any) {
190
199
  signal?.removeEventListener("abort", onAbort);
@@ -6,6 +6,7 @@ import { readFileSync, statSync } from "fs";
6
6
  import path from "path";
7
7
  import { ensureTool } from "../../utils/tools-manager.js";
8
8
  import { resolveToCwd } from "./path-utils.js";
9
+ import { createToolTarget, type ToolTargetMetadata } from "./tool-target.js";
9
10
  import {
10
11
  DEFAULT_MAX_BYTES,
11
12
  formatSize,
@@ -34,6 +35,7 @@ export type GrepToolInput = Static<typeof grepSchema>;
34
35
  const DEFAULT_LIMIT = 100;
35
36
 
36
37
  export interface GrepToolDetails {
38
+ target?: ToolTargetMetadata;
37
39
  truncation?: TruncationResult;
38
40
  matchLimitReached?: number;
39
41
  linesTruncated?: boolean;
@@ -112,6 +114,14 @@ export function createGrepTool(cwd: string, options?: GrepToolOptions): AgentToo
112
114
  }
113
115
 
114
116
  const searchPath = resolveToCwd(searchDir || ".", cwd);
117
+ const target = createToolTarget({
118
+ kind: "search",
119
+ action: "grep",
120
+ inputPath: searchDir || ".",
121
+ resolvedPath: searchPath,
122
+ pattern,
123
+ glob,
124
+ });
115
125
  const ops = customOps ?? defaultGrepOperations;
116
126
 
117
127
  let isDirectory: boolean;
@@ -282,7 +292,7 @@ export function createGrepTool(cwd: string, options?: GrepToolOptions): AgentToo
282
292
 
283
293
  if (matchCount === 0) {
284
294
  settle(() =>
285
- resolve({ content: [{ type: "text", text: "No matches found" }], details: undefined }),
295
+ resolve({ content: [{ type: "text", text: "No matches found" }], details: { target } }),
286
296
  );
287
297
  return;
288
298
  }
@@ -298,7 +308,7 @@ export function createGrepTool(cwd: string, options?: GrepToolOptions): AgentToo
298
308
  const truncation = truncateHead(rawOutput, { maxLines: Number.MAX_SAFE_INTEGER });
299
309
 
300
310
  let output = truncation.content;
301
- const details: GrepToolDetails = {};
311
+ const details: GrepToolDetails = { target };
302
312
 
303
313
  // Build notices
304
314
  const notices: string[] = [];
@@ -329,7 +339,7 @@ export function createGrepTool(cwd: string, options?: GrepToolOptions): AgentToo
329
339
  settle(() =>
330
340
  resolve({
331
341
  content: [{ type: "text", text: output }],
332
- details: Object.keys(details).length > 0 ? details : undefined,
342
+ details,
333
343
  }),
334
344
  );
335
345
  });
@@ -17,6 +17,7 @@ import { formatDimensionNote, resizeImage } from "../../utils/image-resize.js";
17
17
  import { detectSupportedImageMimeTypeFromFile } from "../../utils/mime.js";
18
18
  import { formatHashLines } from "./hashline.js";
19
19
  import { resolveReadPath } from "./path-utils.js";
20
+ import { createReadFileTarget, type ToolTargetMetadata } from "./tool-target.js";
20
21
  import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult, truncateHead } from "./truncate.js";
21
22
 
22
23
  const readSchema = Type.Object({
@@ -28,6 +29,7 @@ const readSchema = Type.Object({
28
29
  export type HashlineReadToolInput = Static<typeof readSchema>;
29
30
 
30
31
  export interface HashlineReadToolDetails {
32
+ target?: ToolTargetMetadata;
31
33
  truncation?: TruncationResult;
32
34
  }
33
35
 
@@ -66,6 +68,7 @@ export function createHashlineReadTool(cwd: string, options?: HashlineReadToolOp
66
68
  signal?: AbortSignal,
67
69
  ) => {
68
70
  const absolutePath = resolveReadPath(path, cwd);
71
+ const target = createReadFileTarget(path, absolutePath, offset, limit);
69
72
 
70
73
  return new Promise<{ content: (TextContent | ImageContent)[]; details: HashlineReadToolDetails | undefined }>(
71
74
  (resolve, reject) => {
@@ -186,7 +189,7 @@ export function createHashlineReadTool(cwd: string, options?: HashlineReadToolOp
186
189
  if (aborted) return;
187
190
 
188
191
  if (signal) signal.removeEventListener("abort", onAbort);
189
- resolve({ content, details });
192
+ resolve({ content, details: { ...details, target } });
190
193
  } catch (error: any) {
191
194
  if (signal) signal.removeEventListener("abort", onAbort);
192
195
  if (!aborted) {
@@ -70,10 +70,18 @@ export {
70
70
  export {
71
71
  createWriteTool,
72
72
  type WriteOperations,
73
+ type WriteToolDetails,
73
74
  type WriteToolInput,
74
75
  type WriteToolOptions,
75
76
  writeTool,
76
77
  } from "./write.js";
78
+ export {
79
+ createToolTarget,
80
+ type ToolTargetAction,
81
+ type ToolTargetKind,
82
+ type ToolTargetMetadata,
83
+ type ToolTargetRange,
84
+ } from "./tool-target.js";
77
85
  export {
78
86
  createHashlineEditTool,
79
87
  type HashlineEditInput,
@@ -3,6 +3,7 @@ import { type Static, Type } from "@sinclair/typebox";
3
3
  import { existsSync, readdirSync, statSync } from "fs";
4
4
  import nodePath from "path";
5
5
  import { resolveToCwd } from "./path-utils.js";
6
+ import { createToolTarget, type ToolTargetMetadata } from "./tool-target.js";
6
7
  import { DEFAULT_MAX_BYTES, formatSize, type TruncationResult, truncateHead } from "./truncate.js";
7
8
 
8
9
  const lsSchema = Type.Object({
@@ -15,6 +16,7 @@ export type LsToolInput = Static<typeof lsSchema>;
15
16
  const DEFAULT_LIMIT = 500;
16
17
 
17
18
  export interface LsToolDetails {
19
+ target?: ToolTargetMetadata;
18
20
  truncation?: TruncationResult;
19
21
  entryLimitReached?: number;
20
22
  }
@@ -68,6 +70,12 @@ export function createLsTool(cwd: string, options?: LsToolOptions): AgentTool<ty
68
70
  (async () => {
69
71
  try {
70
72
  const dirPath = resolveToCwd(path || ".", cwd);
73
+ const target = createToolTarget({
74
+ kind: "directory",
75
+ action: "list",
76
+ inputPath: path || ".",
77
+ resolvedPath: dirPath,
78
+ });
71
79
  const effectiveLimit = limit ?? DEFAULT_LIMIT;
72
80
 
73
81
  // Check if path exists
@@ -124,7 +132,7 @@ export function createLsTool(cwd: string, options?: LsToolOptions): AgentTool<ty
124
132
  signal?.removeEventListener("abort", onAbort);
125
133
 
126
134
  if (results.length === 0) {
127
- resolve({ content: [{ type: "text", text: "(empty directory)" }], details: undefined });
135
+ resolve({ content: [{ type: "text", text: "(empty directory)" }], details: { target } });
128
136
  return;
129
137
  }
130
138
 
@@ -133,7 +141,7 @@ export function createLsTool(cwd: string, options?: LsToolOptions): AgentTool<ty
133
141
  const truncation = truncateHead(rawOutput, { maxLines: Number.MAX_SAFE_INTEGER });
134
142
 
135
143
  let output = truncation.content;
136
- const details: LsToolDetails = {};
144
+ const details: LsToolDetails = { target };
137
145
 
138
146
  // Build notices
139
147
  const notices: string[] = [];
@@ -154,7 +162,7 @@ export function createLsTool(cwd: string, options?: LsToolOptions): AgentTool<ty
154
162
 
155
163
  resolve({
156
164
  content: [{ type: "text", text: output }],
157
- details: Object.keys(details).length > 0 ? details : undefined,
165
+ details,
158
166
  });
159
167
  } catch (e: any) {
160
168
  signal?.removeEventListener("abort", onAbort);
@@ -6,6 +6,7 @@ import { access as fsAccess, readFile as fsReadFile } from "fs/promises";
6
6
  import { formatDimensionNote, resizeImage } from "../../utils/image-resize.js";
7
7
  import { detectSupportedImageMimeTypeFromFile } from "../../utils/mime.js";
8
8
  import { resolveReadPath } from "./path-utils.js";
9
+ import { createReadFileTarget, type ToolTargetMetadata } from "./tool-target.js";
9
10
  import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult, truncateHead } from "./truncate.js";
10
11
 
11
12
  const readSchema = Type.Object({
@@ -17,6 +18,7 @@ const readSchema = Type.Object({
17
18
  export type ReadToolInput = Static<typeof readSchema>;
18
19
 
19
20
  export interface ReadToolDetails {
21
+ target?: ToolTargetMetadata;
20
22
  truncation?: TruncationResult;
21
23
  }
22
24
 
@@ -61,6 +63,7 @@ export function createReadTool(cwd: string, options?: ReadToolOptions): AgentToo
61
63
  signal?: AbortSignal,
62
64
  ) => {
63
65
  const absolutePath = resolveReadPath(path, cwd);
66
+ const target = createReadFileTarget(path, absolutePath, offset, limit);
64
67
 
65
68
  return new Promise<{ content: (TextContent | ImageContent)[]; details: ReadToolDetails | undefined }>(
66
69
  (resolve, reject) => {
@@ -210,7 +213,7 @@ export function createReadTool(cwd: string, options?: ReadToolOptions): AgentToo
210
213
  signal.removeEventListener("abort", onAbort);
211
214
  }
212
215
 
213
- resolve({ content, details });
216
+ resolve({ content, details: { ...details, target } });
214
217
  } catch (error: any) {
215
218
  // Clean up abort handler
216
219
  if (signal) {
@@ -15,78 +15,17 @@
15
15
 
16
16
  import test from "node:test";
17
17
  import assert from "node:assert/strict";
18
- import { readFileSync } from "node:fs";
19
- import { join, dirname, relative } from "node:path";
20
- import { fileURLToPath } from "node:url";
18
+ import { execCommand } from "../exec.js";
21
19
 
22
- const __dirname = dirname(fileURLToPath(import.meta.url));
23
- const coreDir = join(__dirname, "..");
24
-
25
- /**
26
- * Files that call `spawn()` with a user-facing binary (not `node`, `sh`, `bash`,
27
- * or `cmd`) and therefore need the Windows shell guard.
28
- *
29
- * If a file spawns only hardcoded system binaries (like `node` in rpc-client.ts),
30
- * it does not need the guard and should NOT appear here.
31
- */
32
- const SPAWN_FILES_NEEDING_SHELL_GUARD = [
33
- // Extension's GSD client — spawns the `gsd` binary which is a .cmd on Windows
34
- join(coreDir, "..", "..", "..", "vscode-extension", "src", "gsd-client.ts"),
35
- // exec.ts — used by extensions to run arbitrary commands
36
- join(coreDir, "exec.ts"),
37
- // LSP index — spawns project-type commands (tsc, cargo, etc.)
38
- join(coreDir, "lsp", "index.ts"),
39
- // LSP client — spawns LSP server binaries (npx, etc.)
40
- join(coreDir, "lsp", "client.ts"),
41
- // LSP mux — spawns lspmux binary
42
- join(coreDir, "lsp", "lspmux.ts"),
43
- // Package manager — spawns npm/yarn/pnpm
44
- join(coreDir, "package-manager.ts"),
45
- ];
46
-
47
- test("all spawn sites that invoke user-facing binaries include shell: process.platform === 'win32'", () => {
48
- const failures: string[] = [];
49
-
50
- for (const file of SPAWN_FILES_NEEDING_SHELL_GUARD) {
51
- let content: string;
52
- try {
53
- content = readFileSync(file, "utf-8");
54
- } catch {
55
- // File may not exist in this checkout — skip
56
- continue;
57
- }
58
-
59
- const lines = content.split("\n");
60
-
61
- // Find all spawn(..., { ... }) call sites and check each one
62
- // for the presence of `shell: process.platform === "win32"` within
63
- // 5 lines after the spawn call.
64
- for (let i = 0; i < lines.length; i++) {
65
- const line = lines[i]!;
66
- // Skip comments
67
- if (line.trim().startsWith("//") || line.trim().startsWith("*")) continue;
68
-
69
- // Detect a spawn() call
70
- if (/\bspawn\(/.test(line)) {
71
- // Look ahead up to 8 lines for the shell guard
72
- const lookahead = lines.slice(i, i + 8).join("\n");
73
- const hasShellGuard =
74
- /shell:\s*process\.platform\s*===\s*["']win32["']/.test(lookahead);
75
-
76
- if (!hasShellGuard) {
77
- const relPath = relative(join(coreDir, "..", ".."), file);
78
- failures.push(`${relPath}:${i + 1}`);
79
- }
80
- }
81
- }
82
- }
83
-
84
- assert.deepEqual(
85
- failures,
86
- [],
87
- `The following spawn sites are missing 'shell: process.platform === "win32"':\n` +
88
- failures.map(f => ` - ${f}`).join("\n") +
89
- `\nOn Windows, .cmd wrapper scripts (npm, npx, tsc, gsd) require shell ` +
90
- `resolution. Without this guard, spawn fails with ENOENT or EINVAL.`,
20
+ test("execCommand runs a user-facing binary and captures stdout", async () => {
21
+ const result = await execCommand(
22
+ process.execPath,
23
+ ["-e", "process.stdout.write('ok')"],
24
+ process.cwd(),
91
25
  );
26
+
27
+ assert.equal(result.stdout, "ok");
28
+ assert.equal(result.stderr, "");
29
+ assert.equal(result.code, 0);
30
+ assert.equal(result.killed, false);
92
31
  });