gsd-pi 2.21.0 → 2.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (317) hide show
  1. package/README.md +37 -1
  2. package/dist/cli.js +82 -7
  3. package/dist/headless.d.ts +21 -0
  4. package/dist/headless.js +346 -0
  5. package/dist/help-text.d.ts +1 -0
  6. package/dist/help-text.js +62 -1
  7. package/dist/mcp-server.d.ts +35 -0
  8. package/dist/mcp-server.js +73 -0
  9. package/dist/models-resolver.d.ts +32 -0
  10. package/dist/models-resolver.js +50 -0
  11. package/dist/resources/agents/javascript-pro.md +280 -0
  12. package/dist/resources/agents/typescript-pro.md +255 -0
  13. package/dist/resources/extensions/bg-shell/index.ts +14 -2
  14. package/dist/resources/extensions/bg-shell/output-formatter.ts +36 -16
  15. package/dist/resources/extensions/bg-shell/process-manager.ts +6 -4
  16. package/dist/resources/extensions/bg-shell/types.ts +33 -1
  17. package/dist/resources/extensions/bg-shell/utilities.ts +14 -0
  18. package/dist/resources/extensions/browser-tools/capture.ts +18 -16
  19. package/dist/resources/extensions/browser-tools/index.ts +20 -0
  20. package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +25 -0
  21. package/dist/resources/extensions/browser-tools/tools/action-cache.ts +216 -0
  22. package/dist/resources/extensions/browser-tools/tools/codegen.ts +274 -0
  23. package/dist/resources/extensions/browser-tools/tools/device.ts +183 -0
  24. package/dist/resources/extensions/browser-tools/tools/extract.ts +229 -0
  25. package/dist/resources/extensions/browser-tools/tools/injection-detect.ts +221 -0
  26. package/dist/resources/extensions/browser-tools/tools/network-mock.ts +244 -0
  27. package/dist/resources/extensions/browser-tools/tools/pdf.ts +92 -0
  28. package/dist/resources/extensions/browser-tools/tools/state-persistence.ts +202 -0
  29. package/dist/resources/extensions/browser-tools/tools/visual-diff.ts +209 -0
  30. package/dist/resources/extensions/browser-tools/tools/zoom.ts +104 -0
  31. package/dist/resources/extensions/gsd/auto-dashboard.ts +2 -0
  32. package/dist/resources/extensions/gsd/auto-prompts.ts +42 -1
  33. package/dist/resources/extensions/gsd/auto-recovery.ts +10 -0
  34. package/dist/resources/extensions/gsd/auto-worktree.ts +35 -2
  35. package/dist/resources/extensions/gsd/auto.ts +522 -21
  36. package/dist/resources/extensions/gsd/captures.ts +49 -0
  37. package/dist/resources/extensions/gsd/claude-import.ts +656 -0
  38. package/dist/resources/extensions/gsd/collision-diagnostics.ts +332 -0
  39. package/dist/resources/extensions/gsd/commands.ts +78 -8
  40. package/dist/resources/extensions/gsd/dashboard-overlay.ts +16 -2
  41. package/dist/resources/extensions/gsd/diff-context.ts +213 -0
  42. package/dist/resources/extensions/gsd/docs/claude-marketplace-import.md +214 -0
  43. package/dist/resources/extensions/gsd/docs/preferences-reference.md +2 -1
  44. package/dist/resources/extensions/gsd/doctor.ts +32 -5
  45. package/dist/resources/extensions/gsd/file-watcher.ts +97 -0
  46. package/dist/resources/extensions/gsd/files.ts +1 -1
  47. package/dist/resources/extensions/gsd/forensics.ts +639 -0
  48. package/dist/resources/extensions/gsd/git-service.ts +2 -1
  49. package/dist/resources/extensions/gsd/github-client.ts +235 -0
  50. package/dist/resources/extensions/gsd/gitignore.ts +1 -0
  51. package/dist/resources/extensions/gsd/guided-flow.ts +10 -5
  52. package/dist/resources/extensions/gsd/marketplace-discovery.ts +507 -0
  53. package/dist/resources/extensions/gsd/mcp-server.ts +108 -0
  54. package/dist/resources/extensions/gsd/namespaced-registry.ts +467 -0
  55. package/dist/resources/extensions/gsd/namespaced-resolver.ts +307 -0
  56. package/dist/resources/extensions/gsd/plugin-importer.ts +410 -0
  57. package/dist/resources/extensions/gsd/post-unit-hooks.ts +2 -1
  58. package/dist/resources/extensions/gsd/preferences.ts +14 -3
  59. package/dist/resources/extensions/gsd/prompts/execute-task.md +6 -1
  60. package/dist/resources/extensions/gsd/prompts/forensics.md +71 -0
  61. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +104 -1
  62. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -0
  63. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  64. package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
  65. package/dist/resources/extensions/gsd/prompts/system.md +11 -4
  66. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +91 -0
  67. package/dist/resources/extensions/gsd/roadmap-slices.ts +42 -2
  68. package/dist/resources/extensions/gsd/session-forensics.ts +36 -2
  69. package/dist/resources/extensions/gsd/templates/milestone-validation.md +62 -0
  70. package/dist/resources/extensions/gsd/tests/auto-lock-creation.test.ts +186 -0
  71. package/dist/resources/extensions/gsd/tests/auto-recovery.test.ts +64 -0
  72. package/dist/resources/extensions/gsd/tests/auto-skip-loop.test.ts +123 -0
  73. package/dist/resources/extensions/gsd/tests/claude-import-tui.test.ts +351 -0
  74. package/dist/resources/extensions/gsd/tests/collision-diagnostics.test.ts +705 -0
  75. package/dist/resources/extensions/gsd/tests/context-compression.test.ts +13 -0
  76. package/dist/resources/extensions/gsd/tests/diff-context.test.ts +136 -0
  77. package/dist/resources/extensions/gsd/tests/doctor-git.test.ts +118 -0
  78. package/dist/resources/extensions/gsd/tests/doctor.test.ts +58 -0
  79. package/dist/resources/extensions/gsd/tests/in-flight-tool-tracking.test.ts +17 -6
  80. package/dist/resources/extensions/gsd/tests/integration/headless-command.ts +534 -0
  81. package/dist/resources/extensions/gsd/tests/marketplace-discovery.test.ts +202 -0
  82. package/dist/resources/extensions/gsd/tests/marketplace-test-fixtures.ts +91 -0
  83. package/dist/resources/extensions/gsd/tests/namespaced-registry.test.ts +1027 -0
  84. package/dist/resources/extensions/gsd/tests/namespaced-resolver.test.ts +671 -0
  85. package/dist/resources/extensions/gsd/tests/none-mode-gates.test.ts +105 -0
  86. package/dist/resources/extensions/gsd/tests/plugin-importer-live.test.ts +481 -0
  87. package/dist/resources/extensions/gsd/tests/plugin-importer.test.ts +1383 -0
  88. package/dist/resources/extensions/gsd/tests/preferences-git.test.ts +21 -2
  89. package/dist/resources/extensions/gsd/tests/preferences-schema-validation.test.ts +8 -1
  90. package/dist/resources/extensions/gsd/tests/roadmap-slices.test.ts +43 -1
  91. package/dist/resources/extensions/gsd/tests/triage-dispatch.test.ts +120 -0
  92. package/dist/resources/extensions/gsd/tests/triage-resolution.test.ts +203 -2
  93. package/dist/resources/extensions/gsd/tests/visualizer-data.test.ts +11 -0
  94. package/dist/resources/extensions/gsd/tests/visualizer-overlay.test.ts +8 -3
  95. package/dist/resources/extensions/gsd/tests/worktree-bugfix.test.ts +120 -0
  96. package/dist/resources/extensions/gsd/token-counter.ts +45 -0
  97. package/dist/resources/extensions/gsd/triage-resolution.ts +83 -0
  98. package/dist/resources/extensions/gsd/visualizer-data.ts +2 -2
  99. package/dist/resources/extensions/gsd/visualizer-overlay.ts +8 -1
  100. package/dist/resources/extensions/gsd/workspace-index.ts +34 -6
  101. package/dist/resources/extensions/gsd/worktree-manager.ts +29 -1
  102. package/dist/resources/extensions/gsd/worktree.ts +3 -0
  103. package/dist/resources/extensions/mcporter/index.ts +73 -2
  104. package/dist/resources/extensions/shared/terminal.ts +1 -1
  105. package/dist/resources/extensions/universal-config/discovery.ts +4 -0
  106. package/dist/resources/extensions/universal-config/format.ts +35 -4
  107. package/dist/resources/extensions/universal-config/index.ts +5 -3
  108. package/dist/resources/extensions/universal-config/scanners.ts +65 -2
  109. package/dist/resources/extensions/universal-config/tests/discovery.test.ts +9 -1
  110. package/dist/resources/extensions/universal-config/tests/format.test.ts +22 -6
  111. package/dist/resources/extensions/universal-config/tests/scanners.test.ts +18 -0
  112. package/dist/resources/extensions/universal-config/types.ts +20 -1
  113. package/dist/resources/skills/lint/SKILL.md +141 -0
  114. package/dist/resources/skills/review/SKILL.md +214 -0
  115. package/dist/resources/skills/test/SKILL.md +201 -0
  116. package/package.json +4 -1
  117. package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
  118. package/packages/pi-ai/dist/providers/azure-openai-responses.js +12 -1
  119. package/packages/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
  120. package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
  121. package/packages/pi-ai/dist/providers/openai-responses.js +12 -1
  122. package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
  123. package/packages/pi-ai/node_modules/@smithy/node-http-handler/LICENSE +201 -0
  124. package/packages/pi-ai/node_modules/@smithy/node-http-handler/README.md +9 -0
  125. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-cjs/index.js +762 -0
  126. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/build-abort-error.js +19 -0
  127. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/constants.js +1 -0
  128. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/get-transformed-headers.js +9 -0
  129. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/index.js +3 -0
  130. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/node-http-handler.js +230 -0
  131. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/node-http2-connection-manager.js +87 -0
  132. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/node-http2-connection-pool.js +32 -0
  133. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/node-http2-handler.js +169 -0
  134. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/readable.mock.js +21 -0
  135. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/server.mock.js +88 -0
  136. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/set-connection-timeout.js +36 -0
  137. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/set-request-timeout.js +21 -0
  138. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/set-socket-keep-alive.js +22 -0
  139. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/set-socket-timeout.js +23 -0
  140. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/stream-collector/collector.js +8 -0
  141. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/stream-collector/index.js +41 -0
  142. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/stream-collector/readable.mock.js +21 -0
  143. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/timing.js +4 -0
  144. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-es/write-request-body.js +63 -0
  145. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/build-abort-error.d.ts +10 -0
  146. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/constants.d.ts +5 -0
  147. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/get-transformed-headers.d.ts +4 -0
  148. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/index.d.ts +3 -0
  149. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/node-http-handler.d.ts +46 -0
  150. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/node-http2-connection-manager.d.ts +24 -0
  151. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/node-http2-connection-pool.d.ts +12 -0
  152. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/node-http2-handler.d.ts +63 -0
  153. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/readable.mock.d.ts +13 -0
  154. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/server.mock.d.ts +12 -0
  155. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/set-connection-timeout.d.ts +2 -0
  156. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/set-request-timeout.d.ts +6 -0
  157. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/set-socket-keep-alive.d.ts +6 -0
  158. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/set-socket-timeout.d.ts +2 -0
  159. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/stream-collector/collector.d.ts +5 -0
  160. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/stream-collector/index.d.ts +6 -0
  161. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/stream-collector/readable.mock.d.ts +13 -0
  162. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/timing.d.ts +8 -0
  163. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/build-abort-error.d.ts +10 -0
  164. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/constants.d.ts +5 -0
  165. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/get-transformed-headers.d.ts +4 -0
  166. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/index.d.ts +3 -0
  167. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/node-http-handler.d.ts +46 -0
  168. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/node-http2-connection-manager.d.ts +24 -0
  169. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/node-http2-connection-pool.d.ts +12 -0
  170. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/node-http2-handler.d.ts +63 -0
  171. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/readable.mock.d.ts +13 -0
  172. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/server.mock.d.ts +12 -0
  173. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/set-connection-timeout.d.ts +2 -0
  174. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/set-request-timeout.d.ts +6 -0
  175. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/set-socket-keep-alive.d.ts +6 -0
  176. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/set-socket-timeout.d.ts +2 -0
  177. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/stream-collector/collector.d.ts +5 -0
  178. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/stream-collector/index.d.ts +6 -0
  179. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/stream-collector/readable.mock.d.ts +13 -0
  180. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/timing.d.ts +8 -0
  181. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/ts3.4/write-request-body.d.ts +12 -0
  182. package/packages/pi-ai/node_modules/@smithy/node-http-handler/dist-types/write-request-body.d.ts +12 -0
  183. package/packages/pi-ai/node_modules/@smithy/node-http-handler/package.json +68 -0
  184. package/packages/pi-ai/package.json +3 -0
  185. package/packages/pi-ai/pnpm-lock.yaml +2022 -0
  186. package/packages/pi-ai/src/providers/azure-openai-responses.ts +12 -1
  187. package/packages/pi-ai/src/providers/openai-responses.ts +12 -1
  188. package/packages/pi-coding-agent/dist/core/tools/bash-background.test.d.ts +10 -0
  189. package/packages/pi-coding-agent/dist/core/tools/bash-background.test.d.ts.map +1 -0
  190. package/packages/pi-coding-agent/dist/core/tools/bash-background.test.js +79 -0
  191. package/packages/pi-coding-agent/dist/core/tools/bash-background.test.js.map +1 -0
  192. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts +18 -0
  193. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
  194. package/packages/pi-coding-agent/dist/core/tools/bash.js +77 -1
  195. package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
  196. package/packages/pi-coding-agent/dist/core/tools/index.d.ts +1 -1
  197. package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
  198. package/packages/pi-coding-agent/dist/core/tools/index.js +1 -1
  199. package/packages/pi-coding-agent/dist/core/tools/index.js.map +1 -1
  200. package/packages/pi-coding-agent/dist/index.d.ts +1 -1
  201. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  202. package/packages/pi-coding-agent/dist/index.js +1 -1
  203. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  204. package/packages/pi-coding-agent/pnpm-lock.yaml +454 -0
  205. package/packages/pi-coding-agent/src/core/tools/bash-background.test.ts +91 -0
  206. package/packages/pi-coding-agent/src/core/tools/bash.ts +83 -1
  207. package/packages/pi-coding-agent/src/core/tools/index.ts +1 -0
  208. package/packages/pi-coding-agent/src/index.ts +1 -0
  209. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  210. package/packages/pi-tui/dist/tui.js +2 -2
  211. package/packages/pi-tui/dist/tui.js.map +1 -1
  212. package/packages/pi-tui/src/tui.ts +2 -2
  213. package/src/resources/agents/javascript-pro.md +280 -0
  214. package/src/resources/agents/typescript-pro.md +255 -0
  215. package/src/resources/extensions/bg-shell/index.ts +14 -2
  216. package/src/resources/extensions/bg-shell/output-formatter.ts +36 -16
  217. package/src/resources/extensions/bg-shell/process-manager.ts +6 -4
  218. package/src/resources/extensions/bg-shell/types.ts +33 -1
  219. package/src/resources/extensions/bg-shell/utilities.ts +14 -0
  220. package/src/resources/extensions/browser-tools/capture.ts +18 -16
  221. package/src/resources/extensions/browser-tools/index.ts +20 -0
  222. package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +25 -0
  223. package/src/resources/extensions/browser-tools/tools/action-cache.ts +216 -0
  224. package/src/resources/extensions/browser-tools/tools/codegen.ts +274 -0
  225. package/src/resources/extensions/browser-tools/tools/device.ts +183 -0
  226. package/src/resources/extensions/browser-tools/tools/extract.ts +229 -0
  227. package/src/resources/extensions/browser-tools/tools/injection-detect.ts +221 -0
  228. package/src/resources/extensions/browser-tools/tools/network-mock.ts +244 -0
  229. package/src/resources/extensions/browser-tools/tools/pdf.ts +92 -0
  230. package/src/resources/extensions/browser-tools/tools/state-persistence.ts +202 -0
  231. package/src/resources/extensions/browser-tools/tools/visual-diff.ts +209 -0
  232. package/src/resources/extensions/browser-tools/tools/zoom.ts +104 -0
  233. package/src/resources/extensions/gsd/auto-dashboard.ts +2 -0
  234. package/src/resources/extensions/gsd/auto-prompts.ts +42 -1
  235. package/src/resources/extensions/gsd/auto-recovery.ts +10 -0
  236. package/src/resources/extensions/gsd/auto-worktree.ts +35 -2
  237. package/src/resources/extensions/gsd/auto.ts +522 -21
  238. package/src/resources/extensions/gsd/captures.ts +49 -0
  239. package/src/resources/extensions/gsd/claude-import.ts +656 -0
  240. package/src/resources/extensions/gsd/collision-diagnostics.ts +332 -0
  241. package/src/resources/extensions/gsd/commands.ts +78 -8
  242. package/src/resources/extensions/gsd/dashboard-overlay.ts +16 -2
  243. package/src/resources/extensions/gsd/diff-context.ts +213 -0
  244. package/src/resources/extensions/gsd/docs/claude-marketplace-import.md +214 -0
  245. package/src/resources/extensions/gsd/docs/preferences-reference.md +2 -1
  246. package/src/resources/extensions/gsd/doctor.ts +32 -5
  247. package/src/resources/extensions/gsd/file-watcher.ts +97 -0
  248. package/src/resources/extensions/gsd/files.ts +1 -1
  249. package/src/resources/extensions/gsd/forensics.ts +639 -0
  250. package/src/resources/extensions/gsd/git-service.ts +2 -1
  251. package/src/resources/extensions/gsd/github-client.ts +235 -0
  252. package/src/resources/extensions/gsd/gitignore.ts +1 -0
  253. package/src/resources/extensions/gsd/guided-flow.ts +10 -5
  254. package/src/resources/extensions/gsd/marketplace-discovery.ts +507 -0
  255. package/src/resources/extensions/gsd/mcp-server.ts +108 -0
  256. package/src/resources/extensions/gsd/namespaced-registry.ts +467 -0
  257. package/src/resources/extensions/gsd/namespaced-resolver.ts +307 -0
  258. package/src/resources/extensions/gsd/plugin-importer.ts +410 -0
  259. package/src/resources/extensions/gsd/post-unit-hooks.ts +2 -1
  260. package/src/resources/extensions/gsd/preferences.ts +14 -3
  261. package/src/resources/extensions/gsd/prompts/execute-task.md +6 -1
  262. package/src/resources/extensions/gsd/prompts/forensics.md +71 -0
  263. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +104 -1
  264. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -0
  265. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  266. package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
  267. package/src/resources/extensions/gsd/prompts/system.md +11 -4
  268. package/src/resources/extensions/gsd/prompts/validate-milestone.md +91 -0
  269. package/src/resources/extensions/gsd/roadmap-slices.ts +42 -2
  270. package/src/resources/extensions/gsd/session-forensics.ts +36 -2
  271. package/src/resources/extensions/gsd/templates/milestone-validation.md +62 -0
  272. package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +186 -0
  273. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +64 -0
  274. package/src/resources/extensions/gsd/tests/auto-skip-loop.test.ts +123 -0
  275. package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +351 -0
  276. package/src/resources/extensions/gsd/tests/collision-diagnostics.test.ts +705 -0
  277. package/src/resources/extensions/gsd/tests/context-compression.test.ts +13 -0
  278. package/src/resources/extensions/gsd/tests/diff-context.test.ts +136 -0
  279. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +118 -0
  280. package/src/resources/extensions/gsd/tests/doctor.test.ts +58 -0
  281. package/src/resources/extensions/gsd/tests/in-flight-tool-tracking.test.ts +17 -6
  282. package/src/resources/extensions/gsd/tests/integration/headless-command.ts +534 -0
  283. package/src/resources/extensions/gsd/tests/marketplace-discovery.test.ts +202 -0
  284. package/src/resources/extensions/gsd/tests/marketplace-test-fixtures.ts +91 -0
  285. package/src/resources/extensions/gsd/tests/namespaced-registry.test.ts +1027 -0
  286. package/src/resources/extensions/gsd/tests/namespaced-resolver.test.ts +671 -0
  287. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +105 -0
  288. package/src/resources/extensions/gsd/tests/plugin-importer-live.test.ts +481 -0
  289. package/src/resources/extensions/gsd/tests/plugin-importer.test.ts +1383 -0
  290. package/src/resources/extensions/gsd/tests/preferences-git.test.ts +21 -2
  291. package/src/resources/extensions/gsd/tests/preferences-schema-validation.test.ts +8 -1
  292. package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +43 -1
  293. package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +120 -0
  294. package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +203 -2
  295. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +11 -0
  296. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +8 -3
  297. package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +120 -0
  298. package/src/resources/extensions/gsd/token-counter.ts +45 -0
  299. package/src/resources/extensions/gsd/triage-resolution.ts +83 -0
  300. package/src/resources/extensions/gsd/visualizer-data.ts +2 -2
  301. package/src/resources/extensions/gsd/visualizer-overlay.ts +8 -1
  302. package/src/resources/extensions/gsd/workspace-index.ts +34 -6
  303. package/src/resources/extensions/gsd/worktree-manager.ts +29 -1
  304. package/src/resources/extensions/gsd/worktree.ts +3 -0
  305. package/src/resources/extensions/mcporter/index.ts +73 -2
  306. package/src/resources/extensions/shared/terminal.ts +1 -1
  307. package/src/resources/extensions/universal-config/discovery.ts +4 -0
  308. package/src/resources/extensions/universal-config/format.ts +35 -4
  309. package/src/resources/extensions/universal-config/index.ts +5 -3
  310. package/src/resources/extensions/universal-config/scanners.ts +65 -2
  311. package/src/resources/extensions/universal-config/tests/discovery.test.ts +9 -1
  312. package/src/resources/extensions/universal-config/tests/format.test.ts +22 -6
  313. package/src/resources/extensions/universal-config/tests/scanners.test.ts +18 -0
  314. package/src/resources/extensions/universal-config/types.ts +20 -1
  315. package/src/resources/skills/lint/SKILL.md +141 -0
  316. package/src/resources/skills/review/SKILL.md +214 -0
  317. package/src/resources/skills/test/SKILL.md +201 -0
package/README.md CHANGED
@@ -221,6 +221,26 @@ gsd
221
221
 
222
222
  Both terminals read and write the same `.gsd/` files on disk. Your decisions in terminal 2 are picked up automatically at the next phase boundary — no need to stop auto mode.
223
223
 
224
+ ### Headless mode — CI and scripts
225
+
226
+ `gsd headless` runs any `/gsd` command without a TUI. Designed for CI pipelines, cron jobs, and scripted automation.
227
+
228
+ ```bash
229
+ # Run auto mode in CI
230
+ gsd headless --timeout 600000
231
+
232
+ # One unit at a time (cron-friendly)
233
+ gsd headless next
234
+
235
+ # Machine-readable status
236
+ gsd headless --json status
237
+
238
+ # Force a specific pipeline phase
239
+ gsd headless dispatch plan
240
+ ```
241
+
242
+ Headless auto-responds to interactive prompts, detects completion, and exits with structured codes: `0` complete, `1` error/timeout, `2` blocked. Pair with [remote questions](./docs/remote-questions.md) to route decisions to Slack or Discord when human input is needed.
243
+
224
244
  ### First launch
225
245
 
226
246
  On first run, GSD launches a branded setup wizard that walks you through LLM provider selection (OAuth or API key), then optional tool API keys (Brave Search, Context7, Jina, Slack, Discord). Every step is skippable — press Enter to skip any. If you have an existing Pi installation, your provider credentials (LLM and tool keys) are imported automatically. Run `gsd config` anytime to re-run the wizard.
@@ -242,6 +262,8 @@ On first run, GSD launches a branded setup wizard that walks you through LLM pro
242
262
  | `/gsd migrate` | Migrate a v1 `.planning` directory to `.gsd` format |
243
263
  | `/gsd help` | Categorized command reference for all GSD subcommands |
244
264
  | `/gsd mode` | Switch workflow mode (solo/team) with coordinated defaults |
265
+ | `/gsd forensics` | Post-mortem investigation of auto-mode failures |
266
+ | `/gsd cleanup` | Archive phase directories from completed milestones |
245
267
  | `/gsd doctor` | Runtime health checks with auto-fix for common issues |
246
268
  | `/worktree` (`/wt`) | Git worktree lifecycle — create, switch, merge, remove |
247
269
  | `/voice` | Toggle real-time speech-to-text (macOS, Linux) |
@@ -252,7 +274,10 @@ On first run, GSD launches a branded setup wizard that walks you through LLM pro
252
274
  | `Ctrl+Alt+V` | Toggle voice transcription |
253
275
  | `Ctrl+Alt+B` | Show background shell processes |
254
276
  | `gsd config` | Re-run the setup wizard (LLM provider + tool keys) |
277
+ | `gsd update` | Update GSD to the latest version |
278
+ | `gsd headless [cmd]` | Run `/gsd` commands without TUI (CI, cron, scripts) |
255
279
  | `gsd --continue` (`-c`) | Resume the most recent session for the current directory |
280
+ | `gsd sessions` | Interactive session picker — browse and resume any saved session |
256
281
 
257
282
  ---
258
283
 
@@ -355,7 +380,17 @@ unique_milestone_ids: true
355
380
  | `uat_dispatch` | Enable automatic UAT runs after slice completion |
356
381
  | `always_use_skills` | Skills to always load when relevant |
357
382
  | `skill_rules` | Situational rules for skill routing |
383
+ | `skill_staleness_days` | Skills unused for N days get deprioritized (default: 60, 0 = disabled) |
358
384
  | `unique_milestone_ids` | Uses unique milestone names to avoid clashes when working in teams of people |
385
+ | `git.isolation` | `worktree` (default) or `none` — disable worktree isolation for projects that don't need it |
386
+
387
+ ### Agent Instructions
388
+
389
+ Create an `agent-instructions.md` file in your project root to inject persistent per-project behavioral guidance into every agent session. This file is loaded automatically and provides project-specific context the LLM should always have — coding standards, architectural decisions, domain terminology, or workflow preferences.
390
+
391
+ ### Debug Mode
392
+
393
+ Start GSD with `gsd --debug` to enable structured JSONL diagnostic logging. Debug logs capture dispatch decisions, state transitions, and timing data for troubleshooting auto-mode issues.
359
394
 
360
395
  ### Token Optimization (v2.17)
361
396
 
@@ -384,7 +419,7 @@ GSD ships with 14 extensions, all loaded automatically:
384
419
  | Extension | What it provides |
385
420
  | ---------------------- | ---------------------------------------------------------------------------------------------------------------------- |
386
421
  | **GSD** | Core workflow engine, auto mode, commands, dashboard |
387
- | **Browser Tools** | Playwright-based browser with form intelligence, intent-ranked element finding, and semantic actions |
422
+ | **Browser Tools** | Playwright-based browser with form intelligence, intent-ranked element finding, semantic actions, PDF export, session state persistence, network mocking, device emulation, structured extraction, visual diffing, region zoom, test code generation, and prompt injection detection |
388
423
  | **Search the Web** | Brave Search, Tavily, or Jina page extraction |
389
424
  | **Google Search** | Gemini-powered web search with AI-synthesized answers |
390
425
  | **Context7** | Up-to-date library/framework documentation |
@@ -470,6 +505,7 @@ GSD is a TypeScript application that embeds the Pi coding agent SDK.
470
505
  gsd (CLI binary)
471
506
  └─ loader.ts Sets PI_PACKAGE_DIR, GSD env vars, dynamic-imports cli.ts
472
507
  └─ cli.ts Wires SDK managers, loads extensions, starts InteractiveMode
508
+ ├─ headless.ts Headless orchestrator (spawns RPC child, auto-responds, detects completion)
473
509
  ├─ onboarding.ts First-run setup wizard (LLM provider + tool keys)
474
510
  ├─ wizard.ts Env hydration from stored auth.json credentials
475
511
  ├─ app-paths.ts ~/.gsd/agent/, ~/.gsd/sessions/, auth.json
package/dist/cli.js CHANGED
@@ -9,7 +9,7 @@ import { getPiDefaultModelAndProvider, migratePiCredentials } from './pi-migrati
9
9
  import { shouldRunOnboarding, runOnboarding } from './onboarding.js';
10
10
  import chalk from 'chalk';
11
11
  import { checkForUpdates } from './update-check.js';
12
- import { printHelp } from './help-text.js';
12
+ import { printHelp, printSubcommandHelp } from './help-text.js';
13
13
  function exitIfManagedResourcesAreNewer(currentAgentDir) {
14
14
  const currentVersion = process.env.GSD_VERSION || '0.0.0';
15
15
  const managedVersion = getNewerManagedResourceVersion(currentAgentDir, currentVersion);
@@ -28,7 +28,7 @@ function parseCliArgs(argv) {
28
28
  const arg = args[i];
29
29
  if (arg === '--mode' && i + 1 < args.length) {
30
30
  const m = args[++i];
31
- if (m === 'text' || m === 'json' || m === 'rpc')
31
+ if (m === 'text' || m === 'json' || m === 'rpc' || m === 'mcp')
32
32
  flags.mode = m;
33
33
  }
34
34
  else if (arg === '--print' || arg === '-p') {
@@ -71,6 +71,13 @@ function parseCliArgs(argv) {
71
71
  }
72
72
  const cliFlags = parseCliArgs(process.argv);
73
73
  const isPrintMode = cliFlags.print || cliFlags.mode !== undefined;
74
+ // `gsd <subcommand> --help` — show subcommand-specific help
75
+ const subcommand = cliFlags.messages[0];
76
+ if (subcommand && process.argv.includes('--help')) {
77
+ if (printSubcommandHelp(subcommand, process.env.GSD_VERSION || '0.0.0')) {
78
+ process.exit(0);
79
+ }
80
+ }
74
81
  // `gsd config` — replay the setup wizard and exit
75
82
  if (cliFlags.messages[0] === 'config') {
76
83
  const authStorage = AuthStorage.create(authFilePath);
@@ -84,6 +91,59 @@ if (cliFlags.messages[0] === 'update') {
84
91
  await runUpdate();
85
92
  process.exit(0);
86
93
  }
94
+ // `gsd sessions` — list past sessions and pick one to resume
95
+ if (cliFlags.messages[0] === 'sessions') {
96
+ const cwd = process.cwd();
97
+ const safePath = `--${cwd.replace(/^[/\\]/, '').replace(/[/\\:]/g, '-')}--`;
98
+ const projectSessionsDir = join(sessionsDir, safePath);
99
+ process.stderr.write(chalk.dim(`Loading sessions for ${cwd}...\n`));
100
+ const sessions = await SessionManager.list(cwd, projectSessionsDir);
101
+ if (sessions.length === 0) {
102
+ process.stderr.write(chalk.yellow('No sessions found for this directory.\n'));
103
+ process.exit(0);
104
+ }
105
+ process.stderr.write(chalk.bold(`\n Sessions (${sessions.length}):\n\n`));
106
+ const maxShow = 20;
107
+ const toShow = sessions.slice(0, maxShow);
108
+ for (let i = 0; i < toShow.length; i++) {
109
+ const s = toShow[i];
110
+ const date = s.modified.toLocaleString();
111
+ const msgs = s.messageCount;
112
+ const name = s.name ? ` ${chalk.cyan(s.name)}` : '';
113
+ const preview = s.firstMessage
114
+ ? s.firstMessage.replace(/\n/g, ' ').substring(0, 80)
115
+ : chalk.dim('(empty)');
116
+ const num = String(i + 1).padStart(3);
117
+ process.stderr.write(` ${chalk.bold(num)}. ${chalk.green(date)} ${chalk.dim(`(${msgs} msgs)`)}${name}\n`);
118
+ process.stderr.write(` ${chalk.dim(preview)}\n\n`);
119
+ }
120
+ if (sessions.length > maxShow) {
121
+ process.stderr.write(chalk.dim(` ... and ${sessions.length - maxShow} more\n\n`));
122
+ }
123
+ // Interactive selection
124
+ const readline = await import('node:readline');
125
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
126
+ const answer = await new Promise((resolve) => {
127
+ rl.question(chalk.bold(' Enter session number to resume (or q to quit): '), resolve);
128
+ });
129
+ rl.close();
130
+ const choice = parseInt(answer, 10);
131
+ if (isNaN(choice) || choice < 1 || choice > toShow.length) {
132
+ process.stderr.write(chalk.dim('Cancelled.\n'));
133
+ process.exit(0);
134
+ }
135
+ const selected = toShow[choice - 1];
136
+ process.stderr.write(chalk.green(`\nResuming session from ${selected.modified.toLocaleString()}...\n\n`));
137
+ // Mark for the interactive session below to open this specific session
138
+ cliFlags.continue = true;
139
+ cliFlags._selectedSessionPath = selected.path;
140
+ }
141
+ // `gsd headless` — run auto-mode without TUI
142
+ if (cliFlags.messages[0] === 'headless') {
143
+ const { runHeadless, parseHeadlessArgs } = await import('./headless.js');
144
+ await runHeadless(parseHeadlessArgs(process.argv));
145
+ process.exit(0);
146
+ }
87
147
  // Pi's tool bootstrap can mis-detect already-installed fd/rg on some systems
88
148
  // because spawnSync(..., ["--version"]) returns EPERM despite a zero exit code.
89
149
  // Provision local managed binaries first so Pi sees them without probing PATH.
@@ -91,7 +151,10 @@ ensureManagedTools(join(agentDir, 'bin'));
91
151
  const authStorage = AuthStorage.create(authFilePath);
92
152
  loadStoredEnvKeys(authStorage);
93
153
  migratePiCredentials(authStorage);
94
- const modelRegistry = new ModelRegistry(authStorage);
154
+ // Resolve models.json path with fallback to ~/.pi/agent/models.json
155
+ const { resolveModelsJsonPath } = await import('./models-resolver.js');
156
+ const modelsJsonPath = resolveModelsJsonPath();
157
+ const modelRegistry = new ModelRegistry(authStorage, modelsJsonPath);
95
158
  const settingsManager = SettingsManager.create(agentDir);
96
159
  // Run onboarding wizard on first launch (no LLM provider configured)
97
160
  if (!isPrintMode && shouldRunOnboarding(authStorage, settingsManager.getDefaultProvider())) {
@@ -245,8 +308,17 @@ if (isPrintMode) {
245
308
  await runRpcMode(session);
246
309
  process.exit(0);
247
310
  }
311
+ if (mode === 'mcp') {
312
+ const { startMcpServer } = await import('./mcp-server.js');
313
+ await startMcpServer({
314
+ tools: session.agent.state.tools ?? [],
315
+ version: process.env.GSD_VERSION || '0.0.0',
316
+ });
317
+ // MCP server runs until the transport closes; keep alive
318
+ await new Promise(() => { });
319
+ }
248
320
  await runPrintMode(session, {
249
- mode,
321
+ mode: mode,
250
322
  messages: cliFlags.messages,
251
323
  });
252
324
  process.exit(0);
@@ -282,9 +354,11 @@ if (existsSync(sessionsDir)) {
282
354
  // Non-fatal — don't block startup if migration fails
283
355
  }
284
356
  }
285
- const sessionManager = cliFlags.continue
286
- ? SessionManager.continueRecent(cwd, projectSessionsDir)
287
- : SessionManager.create(cwd, projectSessionsDir);
357
+ const sessionManager = cliFlags._selectedSessionPath
358
+ ? SessionManager.open(cliFlags._selectedSessionPath, projectSessionsDir)
359
+ : cliFlags.continue
360
+ ? SessionManager.continueRecent(cwd, projectSessionsDir)
361
+ : SessionManager.create(cwd, projectSessionsDir);
288
362
  exitIfManagedResourcesAreNewer(agentDir);
289
363
  initResources(agentDir);
290
364
  const resourceLoader = buildResourceLoader(agentDir);
@@ -347,6 +421,7 @@ if (!process.stdin.isTTY) {
347
421
  process.stderr.write('[gsd] Non-interactive alternatives:\n');
348
422
  process.stderr.write('[gsd] gsd --print "your message" Single-shot prompt\n');
349
423
  process.stderr.write('[gsd] gsd --mode rpc JSON-RPC over stdin/stdout\n');
424
+ process.stderr.write('[gsd] gsd --mode mcp MCP server over stdin/stdout\n');
350
425
  process.stderr.write('[gsd] gsd --mode text "message" Text output mode\n');
351
426
  process.exit(1);
352
427
  }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Headless Orchestrator — `gsd headless`
3
+ *
4
+ * Runs any /gsd subcommand without a TUI by spawning a child process in
5
+ * RPC mode, auto-responding to extension UI requests, and streaming
6
+ * progress to stderr.
7
+ *
8
+ * Exit codes:
9
+ * 0 — complete (command finished successfully)
10
+ * 1 — error or timeout
11
+ * 2 — blocked (command reported a blocker)
12
+ */
13
+ export interface HeadlessOptions {
14
+ timeout: number;
15
+ json: boolean;
16
+ model?: string;
17
+ command: string;
18
+ commandArgs: string[];
19
+ }
20
+ export declare function parseHeadlessArgs(argv: string[]): HeadlessOptions;
21
+ export declare function runHeadless(options: HeadlessOptions): Promise<void>;
@@ -0,0 +1,346 @@
1
+ /**
2
+ * Headless Orchestrator — `gsd headless`
3
+ *
4
+ * Runs any /gsd subcommand without a TUI by spawning a child process in
5
+ * RPC mode, auto-responding to extension UI requests, and streaming
6
+ * progress to stderr.
7
+ *
8
+ * Exit codes:
9
+ * 0 — complete (command finished successfully)
10
+ * 1 — error or timeout
11
+ * 2 — blocked (command reported a blocker)
12
+ */
13
+ import { existsSync } from 'node:fs';
14
+ import { join } from 'node:path';
15
+ // RpcClient is not in @gsd/pi-coding-agent's public exports — import from dist directly.
16
+ // This relative path resolves correctly from both src/ (via tsx) and dist/ (compiled).
17
+ import { RpcClient } from '../packages/pi-coding-agent/dist/modes/rpc/rpc-client.js';
18
+ // ---------------------------------------------------------------------------
19
+ // CLI Argument Parser
20
+ // ---------------------------------------------------------------------------
21
+ export function parseHeadlessArgs(argv) {
22
+ const options = {
23
+ timeout: 300_000,
24
+ json: false,
25
+ command: 'auto',
26
+ commandArgs: [],
27
+ };
28
+ const args = argv.slice(2);
29
+ let positionalStarted = false;
30
+ for (let i = 0; i < args.length; i++) {
31
+ const arg = args[i];
32
+ if (arg === 'headless')
33
+ continue;
34
+ if (!positionalStarted && arg.startsWith('--')) {
35
+ if (arg === '--timeout' && i + 1 < args.length) {
36
+ options.timeout = parseInt(args[++i], 10);
37
+ if (Number.isNaN(options.timeout) || options.timeout <= 0) {
38
+ process.stderr.write('[headless] Error: --timeout must be a positive integer (milliseconds)\n');
39
+ process.exit(1);
40
+ }
41
+ }
42
+ else if (arg === '--json') {
43
+ options.json = true;
44
+ }
45
+ else if (arg === '--model' && i + 1 < args.length) {
46
+ // --model can also be passed from the main CLI; headless-specific takes precedence
47
+ options.model = args[++i];
48
+ }
49
+ }
50
+ else if (!positionalStarted) {
51
+ positionalStarted = true;
52
+ options.command = arg;
53
+ }
54
+ else {
55
+ options.commandArgs.push(arg);
56
+ }
57
+ }
58
+ return options;
59
+ }
60
+ // ---------------------------------------------------------------------------
61
+ // JSONL Helper
62
+ // ---------------------------------------------------------------------------
63
+ function serializeJsonLine(obj) {
64
+ return JSON.stringify(obj) + '\n';
65
+ }
66
+ // ---------------------------------------------------------------------------
67
+ // Extension UI Auto-Responder
68
+ // ---------------------------------------------------------------------------
69
+ function handleExtensionUIRequest(event, writeToStdin) {
70
+ const { id, method } = event;
71
+ let response;
72
+ switch (method) {
73
+ case 'select':
74
+ response = { type: 'extension_ui_response', id, value: event.options?.[0] ?? '' };
75
+ break;
76
+ case 'confirm':
77
+ response = { type: 'extension_ui_response', id, confirmed: true };
78
+ break;
79
+ case 'input':
80
+ response = { type: 'extension_ui_response', id, value: '' };
81
+ break;
82
+ case 'editor':
83
+ response = { type: 'extension_ui_response', id, value: event.prefill ?? '' };
84
+ break;
85
+ case 'notify':
86
+ case 'setStatus':
87
+ case 'setWidget':
88
+ case 'setTitle':
89
+ case 'set_editor_text':
90
+ response = { type: 'extension_ui_response', id, value: '' };
91
+ break;
92
+ default:
93
+ process.stderr.write(`[headless] Warning: unknown extension_ui_request method "${method}", cancelling\n`);
94
+ response = { type: 'extension_ui_response', id, cancelled: true };
95
+ break;
96
+ }
97
+ writeToStdin(serializeJsonLine(response));
98
+ }
99
+ // ---------------------------------------------------------------------------
100
+ // Progress Formatter
101
+ // ---------------------------------------------------------------------------
102
+ function formatProgress(event) {
103
+ const type = String(event.type ?? '');
104
+ switch (type) {
105
+ case 'tool_execution_start':
106
+ return `[tool] ${event.toolName ?? 'unknown'}`;
107
+ case 'agent_start':
108
+ return '[agent] Session started';
109
+ case 'agent_end':
110
+ return '[agent] Session ended';
111
+ case 'extension_ui_request':
112
+ if (event.method === 'notify') {
113
+ return `[gsd] ${event.message ?? ''}`;
114
+ }
115
+ return null;
116
+ default:
117
+ return null;
118
+ }
119
+ }
120
+ // ---------------------------------------------------------------------------
121
+ // Completion Detection
122
+ // ---------------------------------------------------------------------------
123
+ const TERMINAL_KEYWORDS = ['complete', 'stopped', 'blocked'];
124
+ const IDLE_TIMEOUT_MS = 15_000;
125
+ function isTerminalNotification(event) {
126
+ if (event.type !== 'extension_ui_request' || event.method !== 'notify')
127
+ return false;
128
+ const message = String(event.message ?? '').toLowerCase();
129
+ return TERMINAL_KEYWORDS.some((kw) => message.includes(kw));
130
+ }
131
+ function isBlockedNotification(event) {
132
+ if (event.type !== 'extension_ui_request' || event.method !== 'notify')
133
+ return false;
134
+ return String(event.message ?? '').toLowerCase().includes('blocked');
135
+ }
136
+ // ---------------------------------------------------------------------------
137
+ // Quick Command Detection
138
+ // ---------------------------------------------------------------------------
139
+ const QUICK_COMMANDS = new Set([
140
+ 'status', 'queue', 'history', 'hooks', 'export', 'stop', 'pause',
141
+ 'capture', 'skip', 'undo', 'knowledge', 'config', 'prefs',
142
+ 'cleanup', 'migrate', 'doctor', 'remote', 'help', 'steer',
143
+ 'triage', 'visualize',
144
+ ]);
145
+ function isQuickCommand(command) {
146
+ return QUICK_COMMANDS.has(command);
147
+ }
148
+ // ---------------------------------------------------------------------------
149
+ // Main Orchestrator
150
+ // ---------------------------------------------------------------------------
151
+ export async function runHeadless(options) {
152
+ const startTime = Date.now();
153
+ // Validate .gsd/ directory
154
+ const gsdDir = join(process.cwd(), '.gsd');
155
+ if (!existsSync(gsdDir)) {
156
+ process.stderr.write('[headless] Error: No .gsd/ directory found in current directory.\n');
157
+ process.stderr.write("[headless] Run 'gsd' interactively first to initialize a project.\n");
158
+ process.exit(1);
159
+ }
160
+ // Resolve CLI path for the child process
161
+ const cliPath = process.env.GSD_BIN_PATH || process.argv[1];
162
+ if (!cliPath) {
163
+ process.stderr.write('[headless] Error: Cannot determine CLI path. Set GSD_BIN_PATH or run via gsd.\n');
164
+ process.exit(1);
165
+ }
166
+ // Create RPC client
167
+ const clientOptions = {
168
+ cliPath,
169
+ cwd: process.cwd(),
170
+ };
171
+ if (options.model) {
172
+ clientOptions.model = options.model;
173
+ }
174
+ const client = new RpcClient(clientOptions);
175
+ // Event tracking
176
+ let totalEvents = 0;
177
+ let toolCallCount = 0;
178
+ let blocked = false;
179
+ let completed = false;
180
+ let exitCode = 0;
181
+ const recentEvents = [];
182
+ function trackEvent(event) {
183
+ totalEvents++;
184
+ const type = String(event.type ?? 'unknown');
185
+ if (type === 'tool_execution_start') {
186
+ toolCallCount++;
187
+ }
188
+ // Keep last 20 events for diagnostics
189
+ const detail = type === 'tool_execution_start'
190
+ ? String(event.toolName ?? '')
191
+ : type === 'extension_ui_request'
192
+ ? `${event.method}: ${event.title ?? event.message ?? ''}`
193
+ : undefined;
194
+ recentEvents.push({ type, timestamp: Date.now(), detail });
195
+ if (recentEvents.length > 20)
196
+ recentEvents.shift();
197
+ }
198
+ // Stdin writer for sending extension_ui_response to child
199
+ let stdinWriter = null;
200
+ // Completion promise
201
+ let resolveCompletion;
202
+ const completionPromise = new Promise((resolve) => {
203
+ resolveCompletion = resolve;
204
+ });
205
+ // Idle timeout — fallback completion detection
206
+ let idleTimer = null;
207
+ function resetIdleTimer() {
208
+ if (idleTimer)
209
+ clearTimeout(idleTimer);
210
+ if (toolCallCount > 0) {
211
+ idleTimer = setTimeout(() => {
212
+ completed = true;
213
+ resolveCompletion();
214
+ }, IDLE_TIMEOUT_MS);
215
+ }
216
+ }
217
+ // Overall timeout
218
+ const timeoutTimer = setTimeout(() => {
219
+ process.stderr.write(`[headless] Timeout after ${options.timeout / 1000}s\n`);
220
+ exitCode = 1;
221
+ resolveCompletion();
222
+ }, options.timeout);
223
+ // Event handler
224
+ client.onEvent((event) => {
225
+ const eventObj = event;
226
+ trackEvent(eventObj);
227
+ resetIdleTimer();
228
+ // --json mode: forward all events as JSONL to stdout
229
+ if (options.json) {
230
+ process.stdout.write(JSON.stringify(eventObj) + '\n');
231
+ }
232
+ else {
233
+ // Progress output to stderr
234
+ const line = formatProgress(eventObj);
235
+ if (line)
236
+ process.stderr.write(line + '\n');
237
+ }
238
+ // Handle extension_ui_request
239
+ if (eventObj.type === 'extension_ui_request' && stdinWriter) {
240
+ // Check for terminal notification before auto-responding
241
+ if (isBlockedNotification(eventObj)) {
242
+ blocked = true;
243
+ }
244
+ if (isTerminalNotification(eventObj)) {
245
+ completed = true;
246
+ }
247
+ handleExtensionUIRequest(eventObj, stdinWriter);
248
+ // If we detected a terminal notification, resolve after responding
249
+ if (completed) {
250
+ exitCode = blocked ? 2 : 0;
251
+ resolveCompletion();
252
+ return;
253
+ }
254
+ }
255
+ // Quick commands: resolve on first agent_end
256
+ if (eventObj.type === 'agent_end' && isQuickCommand(options.command) && !completed) {
257
+ completed = true;
258
+ resolveCompletion();
259
+ return;
260
+ }
261
+ // Long-running commands: agent_end after tool execution — possible completion
262
+ // The idle timer + terminal notification handle this case.
263
+ });
264
+ // Signal handling
265
+ const signalHandler = () => {
266
+ process.stderr.write('\n[headless] Interrupted, stopping child process...\n');
267
+ exitCode = 1;
268
+ client.stop().finally(() => {
269
+ clearTimeout(timeoutTimer);
270
+ if (idleTimer)
271
+ clearTimeout(idleTimer);
272
+ process.exit(exitCode);
273
+ });
274
+ };
275
+ process.on('SIGINT', signalHandler);
276
+ process.on('SIGTERM', signalHandler);
277
+ // Start the RPC session
278
+ try {
279
+ await client.start();
280
+ }
281
+ catch (err) {
282
+ process.stderr.write(`[headless] Error: Failed to start RPC session: ${err instanceof Error ? err.message : String(err)}\n`);
283
+ clearTimeout(timeoutTimer);
284
+ process.exit(1);
285
+ }
286
+ // Access stdin writer from the internal process
287
+ const internalProcess = client.process;
288
+ if (!internalProcess?.stdin) {
289
+ process.stderr.write('[headless] Error: Cannot access child process stdin\n');
290
+ await client.stop();
291
+ clearTimeout(timeoutTimer);
292
+ process.exit(1);
293
+ }
294
+ stdinWriter = (data) => {
295
+ internalProcess.stdin.write(data);
296
+ };
297
+ // Detect child process crash
298
+ internalProcess.on('exit', (code) => {
299
+ if (!completed) {
300
+ const msg = `[headless] Child process exited unexpectedly with code ${code ?? 'null'}\n`;
301
+ process.stderr.write(msg);
302
+ exitCode = 1;
303
+ resolveCompletion();
304
+ }
305
+ });
306
+ if (!options.json) {
307
+ process.stderr.write(`[headless] Running /gsd ${options.command}${options.commandArgs.length > 0 ? ' ' + options.commandArgs.join(' ') : ''}...\n`);
308
+ }
309
+ // Send the command
310
+ const command = `/gsd ${options.command}${options.commandArgs.length > 0 ? ' ' + options.commandArgs.join(' ') : ''}`;
311
+ try {
312
+ await client.prompt(command);
313
+ }
314
+ catch (err) {
315
+ process.stderr.write(`[headless] Error: Failed to send prompt: ${err instanceof Error ? err.message : String(err)}\n`);
316
+ exitCode = 1;
317
+ }
318
+ // Wait for completion
319
+ if (exitCode === 0 || exitCode === 2) {
320
+ await completionPromise;
321
+ }
322
+ // Cleanup
323
+ clearTimeout(timeoutTimer);
324
+ if (idleTimer)
325
+ clearTimeout(idleTimer);
326
+ process.removeListener('SIGINT', signalHandler);
327
+ process.removeListener('SIGTERM', signalHandler);
328
+ await client.stop();
329
+ // Summary
330
+ const duration = ((Date.now() - startTime) / 1000).toFixed(1);
331
+ const status = blocked ? 'blocked' : exitCode === 1 ? (totalEvents === 0 ? 'error' : 'timeout') : 'complete';
332
+ process.stderr.write(`[headless] Status: ${status}\n`);
333
+ process.stderr.write(`[headless] Duration: ${duration}s\n`);
334
+ process.stderr.write(`[headless] Events: ${totalEvents} total, ${toolCallCount} tool calls\n`);
335
+ // On failure, print last 5 events for diagnostics
336
+ if (exitCode !== 0) {
337
+ const lastFive = recentEvents.slice(-5);
338
+ if (lastFive.length > 0) {
339
+ process.stderr.write('[headless] Last events:\n');
340
+ for (const e of lastFive) {
341
+ process.stderr.write(` ${e.type}${e.detail ? `: ${e.detail}` : ''}\n`);
342
+ }
343
+ }
344
+ }
345
+ process.exit(exitCode);
346
+ }
@@ -1 +1,2 @@
1
1
  export declare function printHelp(version: string): void;
2
+ export declare function printSubcommandHelp(subcommand: string, version: string): boolean;
package/dist/help-text.js CHANGED
@@ -1,8 +1,58 @@
1
+ const SUBCOMMAND_HELP = {
2
+ config: [
3
+ 'Usage: gsd config',
4
+ '',
5
+ 'Re-run the interactive setup wizard to configure:',
6
+ ' - LLM provider (Anthropic, OpenAI, Google, etc.)',
7
+ ' - Web search provider (Brave, Tavily, built-in)',
8
+ ' - Remote questions (Discord, Slack, Telegram)',
9
+ ' - Tool API keys (Context7, Jina, Groq)',
10
+ '',
11
+ 'All steps are skippable and can be changed later with /login or /search-provider.',
12
+ ].join('\n'),
13
+ update: [
14
+ 'Usage: gsd update',
15
+ '',
16
+ 'Update GSD to the latest version.',
17
+ '',
18
+ 'Equivalent to: npm install -g gsd-pi@latest',
19
+ ].join('\n'),
20
+ sessions: [
21
+ 'Usage: gsd sessions',
22
+ '',
23
+ 'List all saved sessions for the current directory and interactively',
24
+ 'pick one to resume. Shows date, message count, and a preview of the',
25
+ 'first message for each session.',
26
+ '',
27
+ 'Sessions are stored per-directory, so you only see sessions that were',
28
+ 'started from the current working directory.',
29
+ '',
30
+ 'Compare with --continue (-c) which always resumes the most recent session.',
31
+ ].join('\n'),
32
+ headless: [
33
+ 'Usage: gsd headless [flags] [command] [args...]',
34
+ '',
35
+ 'Run /gsd commands without the TUI. Default command: auto',
36
+ '',
37
+ 'Flags:',
38
+ ' --timeout N Overall timeout in ms (default: 300000)',
39
+ ' --json JSONL event stream to stdout',
40
+ ' --model ID Override model',
41
+ '',
42
+ 'Examples:',
43
+ ' gsd headless Run /gsd auto',
44
+ ' gsd headless next Run one unit',
45
+ ' gsd headless --json status Machine-readable status',
46
+ ' gsd headless --timeout 60000 With 1-minute timeout',
47
+ '',
48
+ 'Exit codes: 0 = complete, 1 = error/timeout, 2 = blocked',
49
+ ].join('\n'),
50
+ };
1
51
  export function printHelp(version) {
2
52
  process.stdout.write(`GSD v${version} — Get Shit Done\n\n`);
3
53
  process.stdout.write('Usage: gsd [options] [message...]\n\n');
4
54
  process.stdout.write('Options:\n');
5
- process.stdout.write(' --mode <text|json|rpc> Output mode (default: interactive)\n');
55
+ process.stdout.write(' --mode <text|json|rpc|mcp> Output mode (default: interactive)\n');
6
56
  process.stdout.write(' --print, -p Single-shot print mode\n');
7
57
  process.stdout.write(' --continue, -c Resume the most recent session\n');
8
58
  process.stdout.write(' --model <id> Override model (e.g. claude-opus-4-6)\n');
@@ -15,4 +65,15 @@ export function printHelp(version) {
15
65
  process.stdout.write('\nSubcommands:\n');
16
66
  process.stdout.write(' config Re-run the setup wizard\n');
17
67
  process.stdout.write(' update Update GSD to the latest version\n');
68
+ process.stdout.write(' sessions List and resume a past session\n');
69
+ process.stdout.write(' headless [cmd] [args] Run /gsd commands without TUI (default: auto)\n');
70
+ process.stdout.write('\nRun gsd <subcommand> --help for subcommand-specific help.\n');
71
+ }
72
+ export function printSubcommandHelp(subcommand, version) {
73
+ const help = SUBCOMMAND_HELP[subcommand];
74
+ if (!help)
75
+ return false;
76
+ process.stdout.write(`GSD v${version} — Get Shit Done\n\n`);
77
+ process.stdout.write(help + '\n');
78
+ return true;
18
79
  }