gsd-pi 2.34.0-dev.ed0bfbf → 2.35.0-dev.30eec3f

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 (334) hide show
  1. package/dist/cli.js +7 -2
  2. package/dist/resource-loader.d.ts +1 -1
  3. package/dist/resource-loader.js +13 -1
  4. package/dist/resources/extensions/async-jobs/await-tool.js +0 -2
  5. package/dist/resources/extensions/async-jobs/job-manager.js +0 -6
  6. package/dist/resources/extensions/bg-shell/output-formatter.js +1 -19
  7. package/dist/resources/extensions/bg-shell/process-manager.js +0 -4
  8. package/dist/resources/extensions/bg-shell/types.js +0 -2
  9. package/dist/resources/extensions/context7/index.js +5 -0
  10. package/dist/resources/extensions/get-secrets-from-user.js +2 -30
  11. package/dist/resources/extensions/google-search/index.js +5 -0
  12. package/dist/resources/extensions/gsd/auto-dispatch.js +43 -1
  13. package/dist/resources/extensions/gsd/auto-loop.js +10 -1
  14. package/dist/resources/extensions/gsd/auto-recovery.js +35 -0
  15. package/dist/resources/extensions/gsd/auto-start.js +35 -2
  16. package/dist/resources/extensions/gsd/auto.js +59 -4
  17. package/dist/resources/extensions/gsd/changelog.js +162 -0
  18. package/dist/resources/extensions/gsd/commands-bootstrap.js +1 -0
  19. package/dist/resources/extensions/gsd/commands-handlers.js +2 -2
  20. package/dist/resources/extensions/gsd/commands-inspect.js +10 -3
  21. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +5 -1
  22. package/dist/resources/extensions/gsd/commands.js +8 -1
  23. package/dist/resources/extensions/gsd/docs/preferences-reference.md +10 -0
  24. package/dist/resources/extensions/gsd/doctor-checks.js +113 -5
  25. package/dist/resources/extensions/gsd/doctor-environment.js +26 -17
  26. package/dist/resources/extensions/gsd/doctor-proactive.js +22 -0
  27. package/dist/resources/extensions/gsd/doctor.js +36 -0
  28. package/dist/resources/extensions/gsd/files.js +11 -2
  29. package/dist/resources/extensions/gsd/gitignore.js +54 -7
  30. package/dist/resources/extensions/gsd/guided-flow.js +5 -3
  31. package/dist/resources/extensions/gsd/health-widget-core.js +96 -0
  32. package/dist/resources/extensions/gsd/health-widget.js +97 -46
  33. package/dist/resources/extensions/gsd/index.js +10 -1
  34. package/dist/resources/extensions/gsd/migrate-external.js +55 -2
  35. package/dist/resources/extensions/gsd/paths.js +74 -7
  36. package/dist/resources/extensions/gsd/post-unit-hooks.js +4 -1
  37. package/dist/resources/extensions/gsd/preferences-validation.js +54 -1
  38. package/dist/resources/extensions/gsd/preferences.js +2 -0
  39. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -0
  40. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
  41. package/dist/resources/extensions/gsd/roadmap-mutations.js +55 -0
  42. package/dist/resources/extensions/gsd/session-lock.js +26 -2
  43. package/dist/resources/extensions/gsd/templates/plan.md +8 -0
  44. package/dist/resources/extensions/gsd/worktree-resolver.js +12 -0
  45. package/dist/resources/extensions/remote-questions/remote-command.js +2 -22
  46. package/dist/resources/extensions/shared/mod.js +1 -1
  47. package/dist/resources/extensions/shared/sanitize.js +30 -0
  48. package/dist/resources/extensions/subagent/index.js +6 -14
  49. package/dist/resources/skills/create-gsd-extension/references/events-reference.md +4 -4
  50. package/package.json +2 -1
  51. package/packages/pi-agent-core/dist/agent-loop.d.ts +14 -0
  52. package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  53. package/packages/pi-agent-core/dist/agent-loop.js +24 -27
  54. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  55. package/packages/pi-agent-core/dist/agent.d.ts +1 -0
  56. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  57. package/packages/pi-agent-core/dist/agent.js +11 -22
  58. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  59. package/packages/pi-agent-core/dist/proxy.d.ts.map +1 -1
  60. package/packages/pi-agent-core/dist/proxy.js +2 -8
  61. package/packages/pi-agent-core/dist/proxy.js.map +1 -1
  62. package/packages/pi-agent-core/src/agent-loop.ts +30 -27
  63. package/packages/pi-agent-core/src/agent.ts +12 -23
  64. package/packages/pi-agent-core/src/proxy.ts +2 -8
  65. package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
  66. package/packages/pi-ai/dist/providers/azure-openai-responses.js +5 -41
  67. package/packages/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
  68. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  69. package/packages/pi-ai/dist/providers/openai-completions.js +10 -73
  70. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  71. package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
  72. package/packages/pi-ai/dist/providers/openai-responses.js +9 -80
  73. package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
  74. package/packages/pi-ai/dist/providers/openai-shared.d.ts +65 -0
  75. package/packages/pi-ai/dist/providers/openai-shared.d.ts.map +1 -0
  76. package/packages/pi-ai/dist/providers/openai-shared.js +146 -0
  77. package/packages/pi-ai/dist/providers/openai-shared.js.map +1 -0
  78. package/packages/pi-ai/dist/utils/oauth/google-antigravity.d.ts.map +1 -1
  79. package/packages/pi-ai/dist/utils/oauth/google-antigravity.js +7 -135
  80. package/packages/pi-ai/dist/utils/oauth/google-antigravity.js.map +1 -1
  81. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.d.ts.map +1 -1
  82. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.js +7 -135
  83. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.js.map +1 -1
  84. package/packages/pi-ai/dist/utils/oauth/google-oauth-utils.d.ts +46 -0
  85. package/packages/pi-ai/dist/utils/oauth/google-oauth-utils.d.ts.map +1 -0
  86. package/packages/pi-ai/dist/utils/oauth/google-oauth-utils.js +160 -0
  87. package/packages/pi-ai/dist/utils/oauth/google-oauth-utils.js.map +1 -0
  88. package/packages/pi-ai/src/providers/azure-openai-responses.ts +11 -45
  89. package/packages/pi-ai/src/providers/openai-completions.ts +16 -86
  90. package/packages/pi-ai/src/providers/openai-responses.ts +16 -96
  91. package/packages/pi-ai/src/providers/openai-shared.ts +193 -0
  92. package/packages/pi-ai/src/utils/oauth/google-antigravity.ts +14 -162
  93. package/packages/pi-ai/src/utils/oauth/google-gemini-cli.ts +13 -161
  94. package/packages/pi-ai/src/utils/oauth/google-oauth-utils.ts +201 -0
  95. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +16 -63
  96. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  97. package/packages/pi-coding-agent/dist/core/agent-session.js +104 -641
  98. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  99. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +0 -1
  100. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  101. package/packages/pi-coding-agent/dist/core/auth-storage.js +4 -35
  102. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  103. package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  104. package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.js +5 -43
  105. package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.js.map +1 -1
  106. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
  107. package/packages/pi-coding-agent/dist/core/compaction/compaction.js +11 -69
  108. package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
  109. package/packages/pi-coding-agent/dist/core/compaction/utils.d.ts +40 -0
  110. package/packages/pi-coding-agent/dist/core/compaction/utils.d.ts.map +1 -1
  111. package/packages/pi-coding-agent/dist/core/compaction/utils.js +78 -0
  112. package/packages/pi-coding-agent/dist/core/compaction/utils.js.map +1 -1
  113. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts +77 -0
  114. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts.map +1 -0
  115. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +331 -0
  116. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -0
  117. package/packages/pi-coding-agent/dist/core/extensions/index.d.ts +2 -2
  118. package/packages/pi-coding-agent/dist/core/extensions/index.d.ts.map +1 -1
  119. package/packages/pi-coding-agent/dist/core/extensions/index.js +1 -1
  120. package/packages/pi-coding-agent/dist/core/extensions/index.js.map +1 -1
  121. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +15 -0
  122. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  123. package/packages/pi-coding-agent/dist/core/extensions/runner.js +129 -243
  124. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  125. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +49 -42
  126. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  127. package/packages/pi-coding-agent/dist/core/extensions/types.js +2 -21
  128. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  129. package/packages/pi-coding-agent/dist/core/lock-utils.d.ts +39 -0
  130. package/packages/pi-coding-agent/dist/core/lock-utils.d.ts.map +1 -0
  131. package/packages/pi-coding-agent/dist/core/lock-utils.js +89 -0
  132. package/packages/pi-coding-agent/dist/core/lock-utils.js.map +1 -0
  133. package/packages/pi-coding-agent/dist/core/lsp/config.d.ts +2 -0
  134. package/packages/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -1
  135. package/packages/pi-coding-agent/dist/core/lsp/config.js +4 -1
  136. package/packages/pi-coding-agent/dist/core/lsp/config.js.map +1 -1
  137. package/packages/pi-coding-agent/dist/core/lsp/index.d.ts.map +1 -1
  138. package/packages/pi-coding-agent/dist/core/lsp/index.js +52 -107
  139. package/packages/pi-coding-agent/dist/core/lsp/index.js.map +1 -1
  140. package/packages/pi-coding-agent/dist/core/lsp/lspmux.d.ts.map +1 -1
  141. package/packages/pi-coding-agent/dist/core/lsp/lspmux.js +2 -21
  142. package/packages/pi-coding-agent/dist/core/lsp/lspmux.js.map +1 -1
  143. package/packages/pi-coding-agent/dist/core/lsp/types.d.ts +0 -1
  144. package/packages/pi-coding-agent/dist/core/lsp/types.d.ts.map +1 -1
  145. package/packages/pi-coding-agent/dist/core/lsp/types.js +0 -28
  146. package/packages/pi-coding-agent/dist/core/lsp/types.js.map +1 -1
  147. package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
  148. package/packages/pi-coding-agent/dist/core/package-manager.js +2 -4
  149. package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
  150. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +2 -4
  151. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
  152. package/packages/pi-coding-agent/dist/core/resource-loader.js +46 -60
  153. package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
  154. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts +87 -0
  155. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -0
  156. package/packages/pi-coding-agent/dist/core/retry-handler.js +295 -0
  157. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -0
  158. package/packages/pi-coding-agent/dist/core/session-manager.d.ts +0 -1
  159. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  160. package/packages/pi-coding-agent/dist/core/session-manager.js +3 -28
  161. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  162. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +8 -0
  163. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  164. package/packages/pi-coding-agent/dist/core/settings-manager.js +76 -166
  165. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  166. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  167. package/packages/pi-coding-agent/dist/core/skills.js +1 -3
  168. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  169. package/packages/pi-coding-agent/dist/index.d.ts +1 -1
  170. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  171. package/packages/pi-coding-agent/dist/index.js +1 -1
  172. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  173. package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector.d.ts +1 -1
  174. package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  175. package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector.js +9 -26
  176. package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector.js.map +1 -1
  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 +1 -13
  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/components/tree-render-utils.d.ts +44 -0
  181. package/packages/pi-coding-agent/dist/modes/interactive/components/tree-render-utils.d.ts.map +1 -0
  182. package/packages/pi-coding-agent/dist/modes/interactive/components/tree-render-utils.js +61 -0
  183. package/packages/pi-coding-agent/dist/modes/interactive/components/tree-render-utils.js.map +1 -0
  184. package/packages/pi-coding-agent/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  185. package/packages/pi-coding-agent/dist/modes/interactive/components/tree-selector.js +6 -9
  186. package/packages/pi-coding-agent/dist/modes/interactive/components/tree-selector.js.map +1 -1
  187. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +65 -0
  188. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  189. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +6 -16
  190. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  191. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts +12 -0
  192. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -0
  193. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +175 -0
  194. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -0
  195. package/packages/pi-coding-agent/dist/modes/interactive/utils/shorten-path.d.ts +6 -0
  196. package/packages/pi-coding-agent/dist/modes/interactive/utils/shorten-path.d.ts.map +1 -0
  197. package/packages/pi-coding-agent/dist/modes/interactive/utils/shorten-path.js +15 -0
  198. package/packages/pi-coding-agent/dist/modes/interactive/utils/shorten-path.js.map +1 -0
  199. package/packages/pi-coding-agent/dist/modes/print-mode.d.ts.map +1 -1
  200. package/packages/pi-coding-agent/dist/modes/print-mode.js +2 -30
  201. package/packages/pi-coding-agent/dist/modes/print-mode.js.map +1 -1
  202. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  203. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +2 -28
  204. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  205. package/packages/pi-coding-agent/dist/modes/shared/command-context-actions.d.ts +19 -0
  206. package/packages/pi-coding-agent/dist/modes/shared/command-context-actions.d.ts.map +1 -0
  207. package/packages/pi-coding-agent/dist/modes/shared/command-context-actions.js +45 -0
  208. package/packages/pi-coding-agent/dist/modes/shared/command-context-actions.js.map +1 -0
  209. package/packages/pi-coding-agent/dist/utils/error.d.ts +5 -0
  210. package/packages/pi-coding-agent/dist/utils/error.d.ts.map +1 -0
  211. package/packages/pi-coding-agent/dist/utils/error.js +7 -0
  212. package/packages/pi-coding-agent/dist/utils/error.js.map +1 -0
  213. package/packages/pi-coding-agent/package.json +1 -1
  214. package/packages/pi-coding-agent/src/core/agent-session.ts +117 -745
  215. package/packages/pi-coding-agent/src/core/auth-storage.ts +4 -38
  216. package/packages/pi-coding-agent/src/core/compaction/branch-summarization.ts +7 -53
  217. package/packages/pi-coding-agent/src/core/compaction/compaction.ts +14 -74
  218. package/packages/pi-coding-agent/src/core/compaction/utils.ts +100 -0
  219. package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +424 -0
  220. package/packages/pi-coding-agent/src/core/extensions/index.ts +1 -21
  221. package/packages/pi-coding-agent/src/core/extensions/runner.ts +119 -243
  222. package/packages/pi-coding-agent/src/core/extensions/types.ts +50 -69
  223. package/packages/pi-coding-agent/src/core/lock-utils.ts +113 -0
  224. package/packages/pi-coding-agent/src/core/lsp/config.ts +4 -1
  225. package/packages/pi-coding-agent/src/core/lsp/index.ts +83 -152
  226. package/packages/pi-coding-agent/src/core/lsp/lspmux.ts +2 -22
  227. package/packages/pi-coding-agent/src/core/lsp/types.ts +0 -29
  228. package/packages/pi-coding-agent/src/core/package-manager.ts +1 -4
  229. package/packages/pi-coding-agent/src/core/resource-loader.ts +56 -69
  230. package/packages/pi-coding-agent/src/core/retry-handler.ts +359 -0
  231. package/packages/pi-coding-agent/src/core/session-manager.ts +3 -30
  232. package/packages/pi-coding-agent/src/core/settings-manager.ts +85 -164
  233. package/packages/pi-coding-agent/src/core/skills.ts +1 -4
  234. package/packages/pi-coding-agent/src/index.ts +1 -7
  235. package/packages/pi-coding-agent/src/modes/interactive/components/session-selector.ts +17 -29
  236. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +1 -13
  237. package/packages/pi-coding-agent/src/modes/interactive/components/tree-render-utils.ts +81 -0
  238. package/packages/pi-coding-agent/src/modes/interactive/components/tree-selector.ts +14 -19
  239. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +7 -18
  240. package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +196 -0
  241. package/packages/pi-coding-agent/src/modes/interactive/utils/shorten-path.ts +14 -0
  242. package/packages/pi-coding-agent/src/modes/print-mode.ts +2 -30
  243. package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +2 -28
  244. package/packages/pi-coding-agent/src/modes/shared/command-context-actions.ts +53 -0
  245. package/packages/pi-coding-agent/src/utils/error.ts +6 -0
  246. package/packages/pi-tui/dist/components/markdown.d.ts +5 -0
  247. package/packages/pi-tui/dist/components/markdown.d.ts.map +1 -1
  248. package/packages/pi-tui/dist/components/markdown.js +25 -31
  249. package/packages/pi-tui/dist/components/markdown.js.map +1 -1
  250. package/packages/pi-tui/dist/keys.d.ts +0 -4
  251. package/packages/pi-tui/dist/keys.d.ts.map +1 -1
  252. package/packages/pi-tui/dist/keys.js +94 -162
  253. package/packages/pi-tui/dist/keys.js.map +1 -1
  254. package/packages/pi-tui/src/components/markdown.ts +25 -29
  255. package/packages/pi-tui/src/keys.ts +94 -173
  256. package/pkg/dist/modes/interactive/theme/theme.d.ts +65 -0
  257. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  258. package/pkg/dist/modes/interactive/theme/theme.js +6 -16
  259. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  260. package/pkg/dist/modes/interactive/theme/themes.d.ts +12 -0
  261. package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -0
  262. package/pkg/dist/modes/interactive/theme/themes.js +175 -0
  263. package/pkg/dist/modes/interactive/theme/themes.js.map +1 -0
  264. package/pkg/package.json +1 -1
  265. package/src/resources/extensions/async-jobs/await-tool.ts +0 -2
  266. package/src/resources/extensions/async-jobs/job-manager.ts +0 -7
  267. package/src/resources/extensions/bg-shell/output-formatter.ts +0 -17
  268. package/src/resources/extensions/bg-shell/process-manager.ts +0 -4
  269. package/src/resources/extensions/bg-shell/types.ts +0 -12
  270. package/src/resources/extensions/context7/index.ts +7 -0
  271. package/src/resources/extensions/get-secrets-from-user.ts +2 -35
  272. package/src/resources/extensions/google-search/index.ts +7 -0
  273. package/src/resources/extensions/gsd/auto-dispatch.ts +49 -1
  274. package/src/resources/extensions/gsd/auto-loop.ts +11 -1
  275. package/src/resources/extensions/gsd/auto-recovery.ts +39 -0
  276. package/src/resources/extensions/gsd/auto-start.ts +42 -2
  277. package/src/resources/extensions/gsd/auto.ts +61 -3
  278. package/src/resources/extensions/gsd/changelog.ts +213 -0
  279. package/src/resources/extensions/gsd/commands-bootstrap.ts +1 -0
  280. package/src/resources/extensions/gsd/commands-handlers.ts +2 -2
  281. package/src/resources/extensions/gsd/commands-inspect.ts +10 -3
  282. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +5 -1
  283. package/src/resources/extensions/gsd/commands.ts +9 -1
  284. package/src/resources/extensions/gsd/docs/preferences-reference.md +10 -0
  285. package/src/resources/extensions/gsd/doctor-checks.ts +107 -5
  286. package/src/resources/extensions/gsd/doctor-environment.ts +26 -16
  287. package/src/resources/extensions/gsd/doctor-proactive.ts +24 -0
  288. package/src/resources/extensions/gsd/doctor-types.ts +9 -1
  289. package/src/resources/extensions/gsd/doctor.ts +35 -0
  290. package/src/resources/extensions/gsd/files.ts +12 -2
  291. package/src/resources/extensions/gsd/gitignore.ts +54 -7
  292. package/src/resources/extensions/gsd/guided-flow.ts +5 -3
  293. package/src/resources/extensions/gsd/health-widget-core.ts +129 -0
  294. package/src/resources/extensions/gsd/health-widget.ts +103 -59
  295. package/src/resources/extensions/gsd/index.ts +10 -1
  296. package/src/resources/extensions/gsd/migrate-external.ts +47 -2
  297. package/src/resources/extensions/gsd/paths.ts +73 -7
  298. package/src/resources/extensions/gsd/post-unit-hooks.ts +5 -1
  299. package/src/resources/extensions/gsd/preferences-validation.ts +54 -1
  300. package/src/resources/extensions/gsd/preferences.ts +2 -0
  301. package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -0
  302. package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
  303. package/src/resources/extensions/gsd/roadmap-mutations.ts +66 -0
  304. package/src/resources/extensions/gsd/session-lock.ts +29 -2
  305. package/src/resources/extensions/gsd/templates/plan.md +8 -0
  306. package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +46 -0
  307. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +98 -2
  308. package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +59 -3
  309. package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +20 -0
  310. package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +214 -0
  311. package/src/resources/extensions/gsd/tests/health-widget.test.ts +158 -0
  312. package/src/resources/extensions/gsd/tests/paths.test.ts +113 -0
  313. package/src/resources/extensions/gsd/tests/preferences.test.ts +40 -2
  314. package/src/resources/extensions/gsd/tests/test-utils.ts +165 -0
  315. package/src/resources/extensions/gsd/tests/validate-directory.test.ts +15 -0
  316. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +2 -0
  317. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +32 -0
  318. package/src/resources/extensions/gsd/worktree-resolver.ts +11 -0
  319. package/src/resources/extensions/remote-questions/remote-command.ts +2 -23
  320. package/src/resources/extensions/shared/mod.ts +1 -1
  321. package/src/resources/extensions/shared/sanitize.ts +36 -0
  322. package/src/resources/extensions/subagent/index.ts +6 -12
  323. package/src/resources/skills/create-gsd-extension/references/events-reference.md +4 -4
  324. package/dist/resources/extensions/shared/wizard-ui.js +0 -478
  325. package/packages/pi-coding-agent/dist/modes/interactive/theme/dark.json +0 -85
  326. package/packages/pi-coding-agent/dist/modes/interactive/theme/light.json +0 -84
  327. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.json +0 -335
  328. package/packages/pi-coding-agent/src/modes/interactive/theme/dark.json +0 -85
  329. package/packages/pi-coding-agent/src/modes/interactive/theme/light.json +0 -84
  330. package/packages/pi-coding-agent/src/modes/interactive/theme/theme-schema.json +0 -335
  331. package/pkg/dist/modes/interactive/theme/dark.json +0 -85
  332. package/pkg/dist/modes/interactive/theme/light.json +0 -84
  333. package/pkg/dist/modes/interactive/theme/theme-schema.json +0 -335
  334. package/src/resources/extensions/shared/wizard-ui.ts +0 -551
@@ -14,9 +14,24 @@ export function validatePreferences(preferences) {
14
14
  const warnings = [];
15
15
  const validated = {};
16
16
  // ─── Unknown Key Detection ──────────────────────────────────────────
17
+ // Common key migration hints for pi-level settings that don't map to GSD prefs
18
+ const KEY_MIGRATION_HINTS = {
19
+ taskIsolation: 'use "git.isolation" instead (values: worktree, branch, none)',
20
+ task_isolation: 'use "git.isolation" instead (values: worktree, branch, none)',
21
+ isolation: 'use "git.isolation" instead (values: worktree, branch, none)',
22
+ manage_gitignore: 'use "git.manage_gitignore" instead',
23
+ auto_push: 'use "git.auto_push" instead',
24
+ main_branch: 'use "git.main_branch" instead',
25
+ };
17
26
  for (const key of Object.keys(preferences)) {
18
27
  if (!KNOWN_PREFERENCE_KEYS.has(key)) {
19
- warnings.push(`unknown preference key "${key}" — ignored`);
28
+ const hint = KEY_MIGRATION_HINTS[key];
29
+ if (hint) {
30
+ warnings.push(`unknown preference key "${key}" — ${hint}`);
31
+ }
32
+ else {
33
+ warnings.push(`unknown preference key "${key}" — ignored`);
34
+ }
20
35
  }
21
36
  }
22
37
  if (preferences.version !== undefined) {
@@ -603,5 +618,43 @@ export function validatePreferences(preferences) {
603
618
  validated.git = git;
604
619
  }
605
620
  }
621
+ // ─── Auto Visualize ─────────────────────────────────────────────────
622
+ if (preferences.auto_visualize !== undefined) {
623
+ if (typeof preferences.auto_visualize === "boolean") {
624
+ validated.auto_visualize = preferences.auto_visualize;
625
+ }
626
+ else {
627
+ errors.push("auto_visualize must be a boolean");
628
+ }
629
+ }
630
+ // ─── Auto Report ────────────────────────────────────────────────────
631
+ if (preferences.auto_report !== undefined) {
632
+ if (typeof preferences.auto_report === "boolean") {
633
+ validated.auto_report = preferences.auto_report;
634
+ }
635
+ else {
636
+ errors.push("auto_report must be a boolean");
637
+ }
638
+ }
639
+ // ─── Compression Strategy ───────────────────────────────────────────
640
+ if (preferences.compression_strategy !== undefined) {
641
+ const validStrategies = new Set(["truncate", "compress"]);
642
+ if (typeof preferences.compression_strategy === "string" && validStrategies.has(preferences.compression_strategy)) {
643
+ validated.compression_strategy = preferences.compression_strategy;
644
+ }
645
+ else {
646
+ errors.push(`compression_strategy must be one of: truncate, compress`);
647
+ }
648
+ }
649
+ // ─── Context Selection ──────────────────────────────────────────────
650
+ if (preferences.context_selection !== undefined) {
651
+ const validModes = new Set(["full", "smart"]);
652
+ if (typeof preferences.context_selection === "string" && validModes.has(preferences.context_selection)) {
653
+ validated.context_selection = preferences.context_selection;
654
+ }
655
+ else {
656
+ errors.push(`context_selection must be one of: full, smart`);
657
+ }
658
+ }
606
659
  return { preferences: validated, errors, warnings };
607
660
  }
@@ -186,6 +186,8 @@ function mergePreferences(base, override) {
186
186
  search_provider: override.search_provider ?? base.search_provider,
187
187
  compression_strategy: override.compression_strategy ?? base.compression_strategy,
188
188
  context_selection: override.context_selection ?? base.context_selection,
189
+ auto_visualize: override.auto_visualize ?? base.auto_visualize,
190
+ auto_report: override.auto_report ?? base.auto_report,
189
191
  };
190
192
  }
191
193
  function mergeStringLists(base, override) {
@@ -28,6 +28,8 @@ Then:
28
28
 
29
29
  **Important:** Do NOT skip the success criteria and definition of done verification (steps 3-4). The milestone summary must reflect actual verified outcomes, not assumed success. If any criterion was not met, document it clearly in the summary and do not mark the milestone as passing verification.
30
30
 
31
+ **File system safety:** When scanning milestone directories for evidence, use `ls` or `find` to list directory contents first — never pass a directory path (e.g. `tasks/`, `slices/`) directly to the `read` tool. The `read` tool only accepts file paths, not directories.
32
+
31
33
  **You MUST write `{{milestoneSummaryPath}}` AND update PROJECT.md before finishing.**
32
34
 
33
35
  When done, say: "Milestone {{milestoneId}} complete."
@@ -67,4 +67,6 @@ If verdict is `needs-remediation`:
67
67
 
68
68
  **You MUST write `{{validationPath}}` before finishing.**
69
69
 
70
+ **File system safety:** When scanning milestone directories for evidence, use `ls` or `find` to list directory contents first — never pass a directory path (e.g. `tasks/`, `slices/`) directly to the `read` tool. The `read` tool only accepts file paths, not directories.
71
+
70
72
  When done, say: "Milestone {{milestoneId}} validation complete — verdict: <verdict>."
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Roadmap Mutations — shared utilities for modifying roadmap checkbox state.
3
+ *
4
+ * Extracts the duplicated "flip slice checkbox" pattern that existed in
5
+ * doctor.ts, mechanical-completion.ts, and auto-recovery.ts.
6
+ */
7
+ import { readFileSync } from "node:fs";
8
+ import { atomicWriteSync } from "./atomic-write.js";
9
+ import { resolveMilestoneFile } from "./paths.js";
10
+ import { clearParseCache } from "./files.js";
11
+ /**
12
+ * Mark a slice as done ([x]) in the milestone roadmap.
13
+ * Idempotent — no-op if already checked or if the slice isn't found.
14
+ *
15
+ * @returns true if the roadmap was modified, false if no change was needed
16
+ */
17
+ export function markSliceDoneInRoadmap(basePath, mid, sid) {
18
+ const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP");
19
+ if (!roadmapFile)
20
+ return false;
21
+ let content;
22
+ try {
23
+ content = readFileSync(roadmapFile, "utf-8");
24
+ }
25
+ catch {
26
+ return false;
27
+ }
28
+ const updated = content.replace(new RegExp(`^(\\s*-\\s+)\\[ \\]\\s+\\*\\*${sid}:`, "m"), `$1[x] **${sid}:`);
29
+ if (updated === content)
30
+ return false;
31
+ atomicWriteSync(roadmapFile, updated);
32
+ clearParseCache();
33
+ return true;
34
+ }
35
+ /**
36
+ * Mark a task as done ([x]) in the slice plan.
37
+ * Idempotent — no-op if already checked or if the task isn't found.
38
+ *
39
+ * @returns true if the plan was modified, false if no change was needed
40
+ */
41
+ export function markTaskDoneInPlan(basePath, planPath, tid) {
42
+ let content;
43
+ try {
44
+ content = readFileSync(planPath, "utf-8");
45
+ }
46
+ catch {
47
+ return false;
48
+ }
49
+ const updated = content.replace(new RegExp(`^(\\s*-\\s+)\\[ \\]\\s+\\*\\*${tid}:`, "m"), `$1[x] **${tid}:`);
50
+ if (updated === content)
51
+ return false;
52
+ atomicWriteSync(planPath, updated);
53
+ clearParseCache();
54
+ return true;
55
+ }
@@ -32,8 +32,17 @@ let _lockPid = 0;
32
32
  let _lockCompromised = false;
33
33
  /** Whether we've already registered a process.on('exit') handler. */
34
34
  let _exitHandlerRegistered = false;
35
+ /** Snapshotted lock file path — captured at acquireSessionLock time to avoid
36
+ * gsdRoot() resolving differently in worktree vs project root contexts (#1363). */
37
+ let _snapshotLockPath = null;
38
+ /** Timestamp when the session lock was acquired — used to detect false-positive
39
+ * onCompromised events from event loop stalls within the stale window (#1362). */
40
+ let _lockAcquiredAt = 0;
35
41
  const LOCK_FILE = "auto.lock";
36
42
  function lockPath(basePath) {
43
+ // If we have a snapshotted path from acquisition, use it for consistency
44
+ if (_snapshotLockPath)
45
+ return _snapshotLockPath;
37
46
  return join(gsdRoot(basePath), LOCK_FILE);
38
47
  }
39
48
  // ─── Stray Lock Cleanup ─────────────────────────────────────────────────────
@@ -175,8 +184,17 @@ export function acquireSessionLock(basePath) {
175
184
  onCompromised: () => {
176
185
  // proper-lockfile detected mtime drift (system sleep, event loop stall, etc.).
177
186
  // Default handler throws inside setTimeout — an uncaught exception that crashes
178
- // or corrupts process state. Instead, set a flag so validateSessionLock() can
179
- // detect the compromise gracefully on the next dispatch cycle.
187
+ // or corrupts process state.
188
+ //
189
+ // False-positive suppression (#1362): If we're still within the stale window
190
+ // (30 min since acquisition), the mtime mismatch is from an event loop stall
191
+ // during a long LLM call — not a real takeover. Log and continue.
192
+ const elapsed = Date.now() - _lockAcquiredAt;
193
+ if (elapsed < 1_800_000) {
194
+ process.stderr.write(`[gsd] Lock heartbeat mismatch after ${Math.round(elapsed / 1000)}s — event loop stall, continuing.\n`);
195
+ return; // Suppress false positive
196
+ }
197
+ // Past the stale window — this is a real compromise
180
198
  _lockCompromised = true;
181
199
  _releaseFunction = null;
182
200
  },
@@ -185,6 +203,8 @@ export function acquireSessionLock(basePath) {
185
203
  _lockedPath = basePath;
186
204
  _lockPid = process.pid;
187
205
  _lockCompromised = false;
206
+ _lockAcquiredAt = Date.now();
207
+ _snapshotLockPath = lp; // Snapshot the resolved path for consistent access (#1363)
188
208
  // Safety net: clean up lock dir on process exit if _releaseFunction
189
209
  // wasn't called (e.g., normal exit after clean completion) (#1245).
190
210
  ensureExitHandler(gsdDir);
@@ -219,6 +239,8 @@ export function acquireSessionLock(basePath) {
219
239
  _lockedPath = basePath;
220
240
  _lockPid = process.pid;
221
241
  _lockCompromised = false;
242
+ _lockAcquiredAt = Date.now();
243
+ _snapshotLockPath = lp; // Snapshot for retry path too (#1363)
222
244
  // Safety net — uses centralized handler to avoid double-registration
223
245
  ensureExitHandler(gsdDir);
224
246
  atomicWriteSync(lp, JSON.stringify(lockData, null, 2));
@@ -348,6 +370,8 @@ export function releaseSessionLock(basePath) {
348
370
  _lockedPath = null;
349
371
  _lockPid = 0;
350
372
  _lockCompromised = false;
373
+ _lockAcquiredAt = 0;
374
+ _snapshotLockPath = null;
351
375
  }
352
376
  /**
353
377
  * Check if a session lock exists and return its data (for crash recovery).
@@ -113,6 +113,14 @@
113
113
  - Tasks execute sequentially in order (T01, T02, T03, ...)
114
114
  - est: is informational (e.g. 30m, 1h, 2h) and optional
115
115
 
116
+ Verify field rules:
117
+ - MUST be a mechanically executable command: `npm test`, `grep -q "pattern" file`, `test -f path`
118
+ - For content/document tasks: verify file existence, section count, YAML validity, or word count
119
+ NOT exact phrasing, specific formulas, or "zero TBD" aspirational criteria
120
+ - If no command can verify the output, write: "Manual review — file exists and is non-empty"
121
+ - BAD: "Sections 3.1 and 3.2 exist with exact formulas. Zero TBD/TODO."
122
+ - GOOD: `grep -c "^## " doc.md` returns >= 4 (4+ sections), `! grep -q "TBD\|TODO" doc.md`
123
+
116
124
  Integration closure rule:
117
125
  - At least one slice in any multi-boundary milestone should perform real composition/wiring, not just contract hardening
118
126
  - For the final assembly slice, verification must exercise the real entrypoint or runtime path
@@ -12,6 +12,8 @@
12
12
  * Key invariant: `createAutoWorktree()` and `enterAutoWorktree()` call
13
13
  * `process.chdir()` internally — this class MUST NOT double-chdir.
14
14
  */
15
+ import { existsSync, unlinkSync } from "node:fs";
16
+ import { join } from "node:path";
15
17
  import { debugLog } from "./debug-logger.js";
16
18
  // ─── WorktreeResolver ──────────────────────────────────────────────────────
17
19
  export class WorktreeResolver {
@@ -253,6 +255,16 @@ export class WorktreeResolver {
253
255
  fallback: "chdir-to-project-root",
254
256
  });
255
257
  ctx.notify(`Milestone merge failed: ${msg}`, "warning");
258
+ // Clean up stale merge state left by failed squash-merge (#1389)
259
+ try {
260
+ const gitDir = join(originalBase || this.s.basePath, ".git");
261
+ for (const f of ["SQUASH_MSG", "MERGE_HEAD", "MERGE_MSG"]) {
262
+ const p = join(gitDir, f);
263
+ if (existsSync(p))
264
+ unlinkSync(p);
265
+ }
266
+ }
267
+ catch { /* best-effort */ }
256
268
  // Error recovery: always restore to project root
257
269
  if (originalBase) {
258
270
  try {
@@ -2,12 +2,12 @@
2
2
  * Remote Questions — /gsd remote command
3
3
  */
4
4
  import { AuthStorage } from "@gsd/pi-coding-agent";
5
- import { CURSOR_MARKER, Editor, Key, matchesKey, truncateToWidth } from "@gsd/pi-tui";
5
+ import { Editor, Key, matchesKey, truncateToWidth } from "@gsd/pi-tui";
6
6
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
7
7
  import { dirname, join } from "node:path";
8
8
  import { getGlobalGSDPreferencesPath, loadEffectiveGSDPreferences } from "../gsd/preferences.js";
9
9
  import { getRemoteConfigStatus, isValidChannelId, resolveRemoteConfig } from "./config.js";
10
- import { sanitizeError } from "../shared/sanitize.js";
10
+ import { maskEditorLine, sanitizeError } from "../shared/mod.js";
11
11
  import { getLatestPromptSummary } from "./status.js";
12
12
  export async function handleRemote(subcommand, ctx, _pi) {
13
13
  const trimmed = subcommand.trim();
@@ -339,26 +339,6 @@ function removeRemoteQuestionsConfig() {
339
339
  const next = frontmatter ? `---\n${frontmatter}\n---${content.slice(fmMatch[0].length)}` : content.slice(fmMatch[0].length).replace(/^\n+/, "");
340
340
  writeFileSync(prefsPath, next, "utf-8");
341
341
  }
342
- function maskEditorLine(line) {
343
- let output = "";
344
- let i = 0;
345
- while (i < line.length) {
346
- if (line.startsWith(CURSOR_MARKER, i)) {
347
- output += CURSOR_MARKER;
348
- i += CURSOR_MARKER.length;
349
- continue;
350
- }
351
- const ansiMatch = /^\x1b\[[0-9;]*m/.exec(line.slice(i));
352
- if (ansiMatch) {
353
- output += ansiMatch[0];
354
- i += ansiMatch[0].length;
355
- continue;
356
- }
357
- output += line[i] === " " ? " " : "*";
358
- i += 1;
359
- }
360
- return output;
361
- }
362
342
  async function promptMaskedInput(ctx, label, hint) {
363
343
  if (!ctx.hasUI)
364
344
  return null;
@@ -6,6 +6,6 @@ export { toPosixPath } from "./path-display.js";
6
6
  export { showInterviewRound } from "./interview-ui.js";
7
7
  export { showNextAction } from "./next-action-ui.js";
8
8
  export { showConfirm } from "./confirm-ui.js";
9
- export { sanitizeError } from "./sanitize.js";
9
+ export { sanitizeError, maskEditorLine } from "./sanitize.js";
10
10
  export { formatDateShort, truncateWithEllipsis } from "./format-utils.js";
11
11
  export { splitFrontmatter, parseFrontmatterMap } from "./frontmatter.js";
@@ -1,6 +1,8 @@
1
1
  /**
2
2
  * Sanitize error messages by redacting token-like strings before surfacing.
3
+ * Also provides maskEditorLine for masking sensitive TUI editor input.
3
4
  */
5
+ import { CURSOR_MARKER } from "@gsd/pi-tui";
4
6
  const TOKEN_PATTERNS = [
5
7
  /xoxb-[A-Za-z0-9\-]+/g, // Slack bot tokens
6
8
  /xoxp-[A-Za-z0-9\-]+/g, // Slack user tokens
@@ -15,3 +17,31 @@ export function sanitizeError(msg) {
15
17
  }
16
18
  return sanitized;
17
19
  }
20
+ /**
21
+ * Replace editor visible text with masked characters while preserving
22
+ * ANSI cursor/sequencer codes. Keeps border/metadata lines readable.
23
+ */
24
+ export function maskEditorLine(line) {
25
+ if (line.startsWith("─")) {
26
+ return line;
27
+ }
28
+ let output = "";
29
+ let i = 0;
30
+ while (i < line.length) {
31
+ if (line.startsWith(CURSOR_MARKER, i)) {
32
+ output += CURSOR_MARKER;
33
+ i += CURSOR_MARKER.length;
34
+ continue;
35
+ }
36
+ const ansiMatch = /^\x1b\[[0-9;]*m/.exec(line.slice(i));
37
+ if (ansiMatch) {
38
+ output += ansiMatch[0];
39
+ i += ansiMatch[0].length;
40
+ continue;
41
+ }
42
+ const ch = line[i];
43
+ output += ch === " " ? " " : "*";
44
+ i += 1;
45
+ }
46
+ return output;
47
+ }
@@ -20,6 +20,7 @@ import { StringEnum } from "@gsd/pi-ai";
20
20
  import { getMarkdownTheme } from "@gsd/pi-coding-agent";
21
21
  import { Container, Markdown, Spacer, Text } from "@gsd/pi-tui";
22
22
  import { Type } from "@sinclair/typebox";
23
+ import { formatTokenCount } from "../shared/mod.js";
23
24
  import { discoverAgents } from "./agents.js";
24
25
  import { createIsolation, mergeDeltaPatches, readIsolationMode, } from "./isolation.js";
25
26
  import { registerWorker, updateWorker } from "./worker-registry.js";
@@ -58,31 +59,22 @@ async function stopLiveSubagents() {
58
59
  }
59
60
  }
60
61
  }
61
- function formatTokens(count) {
62
- if (count < 1000)
63
- return count.toString();
64
- if (count < 10000)
65
- return `${(count / 1000).toFixed(1)}k`;
66
- if (count < 1000000)
67
- return `${Math.round(count / 1000)}k`;
68
- return `${(count / 1000000).toFixed(1)}M`;
69
- }
70
62
  function formatUsageStats(usage, model) {
71
63
  const parts = [];
72
64
  if (usage.turns)
73
65
  parts.push(`${usage.turns} turn${usage.turns > 1 ? "s" : ""}`);
74
66
  if (usage.input)
75
- parts.push(`↑${formatTokens(usage.input)}`);
67
+ parts.push(`↑${formatTokenCount(usage.input)}`);
76
68
  if (usage.output)
77
- parts.push(`↓${formatTokens(usage.output)}`);
69
+ parts.push(`↓${formatTokenCount(usage.output)}`);
78
70
  if (usage.cacheRead)
79
- parts.push(`R${formatTokens(usage.cacheRead)}`);
71
+ parts.push(`R${formatTokenCount(usage.cacheRead)}`);
80
72
  if (usage.cacheWrite)
81
- parts.push(`W${formatTokens(usage.cacheWrite)}`);
73
+ parts.push(`W${formatTokenCount(usage.cacheWrite)}`);
82
74
  if (usage.cost)
83
75
  parts.push(`$${(Number(usage.cost) || 0).toFixed(4)}`);
84
76
  if (usage.contextTokens && usage.contextTokens > 0) {
85
- parts.push(`ctx:${formatTokens(usage.contextTokens)}`);
77
+ parts.push(`ctx:${formatTokenCount(usage.contextTokens)}`);
86
78
  }
87
79
  if (model)
88
80
  parts.push(model);
@@ -61,10 +61,10 @@ pi.on("tool_call", async (event, ctx) => {
61
61
 
62
62
  **tool_result** — Fired after tool executes. Can modify result. Handlers chain like middleware.
63
63
  ```typescript
64
- import { isBashToolResult } from "@mariozechner/pi-coding-agent";
64
+ import { isToolResultEventType } from "@mariozechner/pi-coding-agent";
65
65
 
66
66
  pi.on("tool_result", async (event, ctx) => {
67
- if (isBashToolResult(event)) {
67
+ if (isToolResultEventType("bash", event)) {
68
68
  // event.details is typed as BashToolDetails
69
69
  }
70
70
  // Return partial patch: { content, details, isError }
@@ -105,7 +105,7 @@ pi.on("model_select", async (event, ctx) => {
105
105
  Built-in type guards for tool events:
106
106
 
107
107
  ```typescript
108
- import { isToolCallEventType, isBashToolResult } from "@mariozechner/pi-coding-agent";
108
+ import { isToolCallEventType, isToolResultEventType } from "@mariozechner/pi-coding-agent";
109
109
 
110
110
  // Tool calls — narrows event.input type
111
111
  if (isToolCallEventType("bash", event)) { /* event.input: { command, timeout? } */ }
@@ -114,7 +114,7 @@ if (isToolCallEventType("write", event)) { /* event.input: { path, content } */
114
114
  if (isToolCallEventType("edit", event)) { /* event.input: { path, oldText, newText } */ }
115
115
 
116
116
  // Tool results — narrows event.details type
117
- if (isBashToolResult(event)) { /* event.details: BashToolDetails */ }
117
+ if (isToolResultEventType("bash", event)) { /* event.details: BashToolDetails */ }
118
118
  ```
119
119
 
120
120
  For custom tools, export your input type and use explicit type params:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gsd-pi",
3
- "version": "2.34.0-dev.ed0bfbf",
3
+ "version": "2.35.0-dev.30eec3f",
4
4
  "description": "GSD — Get Shit Done coding agent",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -50,6 +50,7 @@
50
50
  "copy-themes": "node scripts/copy-themes.cjs",
51
51
  "copy-export-html": "node scripts/copy-export-html.cjs",
52
52
  "test:unit": "node --import ./src/resources/extensions/gsd/tests/resolve-ts.mjs --experimental-strip-types --test src/resources/extensions/gsd/tests/*.test.ts src/resources/extensions/gsd/tests/*.test.mjs src/tests/*.test.ts",
53
+ "test:marketplace": "GSD_TEST_CLONE_MARKETPLACES=1 node --import ./src/resources/extensions/gsd/tests/resolve-ts.mjs --experimental-strip-types --test src/resources/extensions/gsd/tests/claude-import-tui.test.ts src/resources/extensions/gsd/tests/plugin-importer-live.test.ts src/tests/marketplace-discovery.test.ts",
53
54
  "test:coverage": "c8 --reporter=text --reporter=lcov --exclude='src/resources/extensions/gsd/tests/**' --exclude='src/tests/**' --exclude='scripts/**' --exclude='native/**' --exclude='node_modules/**' --check-coverage --statements=50 --lines=50 --branches=20 --functions=20 node --import ./src/resources/extensions/gsd/tests/resolve-ts.mjs --experimental-strip-types --test src/resources/extensions/gsd/tests/*.test.ts src/resources/extensions/gsd/tests/*.test.mjs src/tests/*.test.ts",
54
55
  "test:integration": "node --import ./src/resources/extensions/gsd/tests/resolve-ts.mjs --experimental-strip-types --test src/resources/extensions/gsd/tests/*integration*.test.ts src/tests/integration/*.test.ts",
55
56
  "test": "npm run test:unit && npm run test:integration",
@@ -4,6 +4,20 @@
4
4
  */
5
5
  import { EventStream } from "@gsd/pi-ai";
6
6
  import type { AgentContext, AgentEvent, AgentLoopConfig, AgentMessage, StreamFn } from "./types.js";
7
+ export declare const ZERO_USAGE: {
8
+ readonly input: 0;
9
+ readonly output: 0;
10
+ readonly cacheRead: 0;
11
+ readonly cacheWrite: 0;
12
+ readonly totalTokens: 0;
13
+ readonly cost: {
14
+ readonly input: 0;
15
+ readonly output: 0;
16
+ readonly cacheRead: 0;
17
+ readonly cacheWrite: 0;
18
+ readonly total: 0;
19
+ };
20
+ };
7
21
  /**
8
22
  * Start an agent loop with a new prompt message.
9
23
  * The prompt is added to the context and events are emitted for it.
@@ -1 +1 @@
1
- {"version":3,"file":"agent-loop.d.ts","sourceRoot":"","sources":["../src/agent-loop.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAGN,WAAW,EAIX,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EACX,YAAY,EACZ,UAAU,EACV,eAAe,EACf,YAAY,EAIZ,QAAQ,EACR,MAAM,YAAY,CAAC;AA8BpB;;;GAGG;AACH,wBAAgB,SAAS,CACxB,OAAO,EAAE,YAAY,EAAE,EACvB,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe,EACvB,MAAM,CAAC,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,QAAQ,GACjB,WAAW,CAAC,UAAU,EAAE,YAAY,EAAE,CAAC,CA8BzC;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAChC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe,EACvB,MAAM,CAAC,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,QAAQ,GACjB,WAAW,CAAC,UAAU,EAAE,YAAY,EAAE,CAAC,CA+BzC"}
1
+ {"version":3,"file":"agent-loop.d.ts","sourceRoot":"","sources":["../src/agent-loop.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAGN,WAAW,EAIX,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EACX,YAAY,EACZ,UAAU,EACV,eAAe,EACf,YAAY,EAIZ,QAAQ,EACR,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,UAAU;;;;;;;;;;;;;CAOb,CAAC;AA4CX;;;GAGG;AACH,wBAAgB,SAAS,CACxB,OAAO,EAAE,YAAY,EAAE,EACvB,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe,EACvB,MAAM,CAAC,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,QAAQ,GACjB,WAAW,CAAC,UAAU,EAAE,YAAY,EAAE,CAAC,CAwBzC;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAChC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe,EACvB,MAAM,CAAC,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,QAAQ,GACjB,WAAW,CAAC,UAAU,EAAE,YAAY,EAAE,CAAC,CA0BzC"}
@@ -3,7 +3,7 @@
3
3
  * Transforms to Message[] only at the LLM call boundary.
4
4
  */
5
5
  import { EventStream, streamSimple, validateToolArguments, } from "@gsd/pi-ai";
6
- const ZERO_USAGE = {
6
+ export const ZERO_USAGE = {
7
7
  input: 0,
8
8
  output: 0,
9
9
  cacheRead: 0,
@@ -29,6 +29,23 @@ function createErrorMessage(error, config) {
29
29
  timestamp: Date.now(),
30
30
  };
31
31
  }
32
+ /**
33
+ * Emit a message_start + message_end pair for a single message.
34
+ */
35
+ function emitMessagePair(stream, message) {
36
+ stream.push({ type: "message_start", message });
37
+ stream.push({ type: "message_end", message });
38
+ }
39
+ /**
40
+ * Emit the standard error sequence when the outer agent loop catches an error.
41
+ * Pushes message_start/end, turn_end, agent_end, then closes the stream.
42
+ */
43
+ function emitErrorSequence(stream, errMsg, newMessages) {
44
+ emitMessagePair(stream, errMsg);
45
+ stream.push({ type: "turn_end", message: errMsg, toolResults: [] });
46
+ stream.push({ type: "agent_end", messages: [...newMessages, errMsg] });
47
+ stream.end([...newMessages, errMsg]);
48
+ }
32
49
  /**
33
50
  * Start an agent loop with a new prompt message.
34
51
  * The prompt is added to the context and events are emitted for it.
@@ -44,19 +61,13 @@ export function agentLoop(prompts, context, config, signal, streamFn) {
44
61
  stream.push({ type: "agent_start" });
45
62
  stream.push({ type: "turn_start" });
46
63
  for (const prompt of prompts) {
47
- stream.push({ type: "message_start", message: prompt });
48
- stream.push({ type: "message_end", message: prompt });
64
+ emitMessagePair(stream, prompt);
49
65
  }
50
66
  try {
51
67
  await runLoop(currentContext, newMessages, config, signal, stream, streamFn);
52
68
  }
53
69
  catch (error) {
54
- const errMsg = createErrorMessage(error, config);
55
- stream.push({ type: "message_start", message: errMsg });
56
- stream.push({ type: "message_end", message: errMsg });
57
- stream.push({ type: "turn_end", message: errMsg, toolResults: [] });
58
- stream.push({ type: "agent_end", messages: [...newMessages, errMsg] });
59
- stream.end([...newMessages, errMsg]);
70
+ emitErrorSequence(stream, createErrorMessage(error, config), newMessages);
60
71
  }
61
72
  })();
62
73
  return stream;
@@ -86,12 +97,7 @@ export function agentLoopContinue(context, config, signal, streamFn) {
86
97
  await runLoop(currentContext, newMessages, config, signal, stream, streamFn);
87
98
  }
88
99
  catch (error) {
89
- const errMsg = createErrorMessage(error, config);
90
- stream.push({ type: "message_start", message: errMsg });
91
- stream.push({ type: "message_end", message: errMsg });
92
- stream.push({ type: "turn_end", message: errMsg, toolResults: [] });
93
- stream.push({ type: "agent_end", messages: [...newMessages, errMsg] });
94
- stream.end([...newMessages, errMsg]);
100
+ emitErrorSequence(stream, createErrorMessage(error, config), newMessages);
95
101
  }
96
102
  })();
97
103
  return stream;
@@ -121,8 +127,7 @@ async function runLoop(currentContext, newMessages, config, signal, stream, stre
121
127
  // Process pending messages (inject before next assistant response)
122
128
  if (pendingMessages.length > 0) {
123
129
  for (const message of pendingMessages) {
124
- stream.push({ type: "message_start", message });
125
- stream.push({ type: "message_end", message });
130
+ emitMessagePair(stream, message);
126
131
  currentContext.messages.push(message);
127
132
  newMessages.push(message);
128
133
  }
@@ -144,14 +149,7 @@ async function runLoop(currentContext, newMessages, config, signal, stream, stre
144
149
  api: config.model.api,
145
150
  provider: config.model.provider,
146
151
  model: config.model.id,
147
- usage: {
148
- input: 0,
149
- output: 0,
150
- cacheRead: 0,
151
- cacheWrite: 0,
152
- totalTokens: 0,
153
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
154
- },
152
+ usage: ZERO_USAGE,
155
153
  stopReason: signal?.aborted ? "aborted" : "error",
156
154
  errorMessage: errorText,
157
155
  timestamp: Date.now(),
@@ -478,8 +476,7 @@ function emitToolCallOutcome(toolCall, result, isError, stream) {
478
476
  isError,
479
477
  timestamp: Date.now(),
480
478
  };
481
- stream.push({ type: "message_start", message: toolResultMessage });
482
- stream.push({ type: "message_end", message: toolResultMessage });
479
+ emitMessagePair(stream, toolResultMessage);
483
480
  return toolResultMessage;
484
481
  }
485
482
  function skipToolCall(toolCall, stream, options) {