gsd-pi 2.70.1 → 2.71.0-dev.72557e1

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 (335) hide show
  1. package/README.md +24 -17
  2. package/dist/cli.js +12 -3
  3. package/dist/mcp-server.js +6 -6
  4. package/dist/provider-migrations.d.ts +10 -0
  5. package/dist/provider-migrations.js +12 -0
  6. package/dist/resource-loader.js +139 -13
  7. package/dist/resources/GSD-WORKFLOW.md +1 -1
  8. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +129 -30
  9. package/dist/resources/extensions/get-secrets-from-user.js +17 -1
  10. package/dist/resources/extensions/gsd/auto-start.js +4 -12
  11. package/dist/resources/extensions/gsd/auto-tool-tracking.js +1 -1
  12. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +6 -0
  13. package/dist/resources/extensions/gsd/bootstrap/system-context.js +6 -0
  14. package/dist/resources/extensions/gsd/commands/context.js +15 -6
  15. package/dist/resources/extensions/gsd/commands/dispatcher.js +12 -2
  16. package/dist/resources/extensions/gsd/custom-workflow-engine.js +16 -12
  17. package/dist/resources/extensions/gsd/dispatch-guard.js +18 -1
  18. package/dist/resources/extensions/gsd/error-classifier.js +1 -1
  19. package/dist/resources/extensions/gsd/file-lock.js +60 -0
  20. package/dist/resources/extensions/gsd/guided-flow.js +12 -10
  21. package/dist/resources/extensions/gsd/init-wizard.js +3 -11
  22. package/dist/resources/extensions/gsd/notification-store.js +21 -1
  23. package/dist/resources/extensions/gsd/notification-widget.js +1 -1
  24. package/dist/resources/extensions/gsd/pre-execution-checks.js +35 -2
  25. package/dist/resources/extensions/gsd/prompts/complete-slice.md +2 -2
  26. package/dist/resources/extensions/gsd/prompts/discuss.md +33 -13
  27. package/dist/resources/extensions/gsd/prompts/execute-task.md +20 -19
  28. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  29. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +2 -0
  30. package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  31. package/dist/resources/extensions/gsd/prompts/queue.md +3 -2
  32. package/dist/resources/extensions/gsd/prompts/system.md +1 -0
  33. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -1
  34. package/dist/resources/extensions/gsd/state.js +234 -332
  35. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +34 -0
  36. package/dist/resources/extensions/gsd/workflow-events.js +25 -13
  37. package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +56 -0
  38. package/dist/resources/extensions/gsd/workflow-mcp.js +1 -1
  39. package/dist/resources/skills/create-skill/SKILL.md +2 -0
  40. package/dist/web/standalone/.next/BUILD_ID +1 -1
  41. package/dist/web/standalone/.next/app-path-routes-manifest.json +13 -13
  42. package/dist/web/standalone/.next/build-manifest.json +4 -4
  43. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  44. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  45. package/dist/web/standalone/.next/required-server-files.json +3 -3
  46. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  47. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  48. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  49. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  57. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  58. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  59. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  60. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  61. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  63. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  67. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  68. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  69. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  70. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  71. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  72. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  73. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  74. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  75. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  76. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  77. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  78. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  79. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  80. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  81. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  82. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  83. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  84. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  85. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  86. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  87. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  88. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  89. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  90. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  92. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  93. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  105. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  115. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  121. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  137. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +2 -2
  141. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  150. package/dist/web/standalone/.next/server/app/index.html +1 -1
  151. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  152. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  153. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  154. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  155. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  156. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  157. package/dist/web/standalone/.next/server/app/page.js +2 -2
  158. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/app-paths-manifest.json +13 -13
  160. package/dist/web/standalone/.next/server/chunks/63.js +3 -3
  161. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  162. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  163. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  164. package/dist/web/standalone/.next/server/middleware.js +2 -2
  165. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  166. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  167. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  168. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  169. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  170. package/dist/web/standalone/.next/static/chunks/2826.dd3dc8bbd3025fa5.js +9 -0
  171. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  172. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  173. package/dist/web/standalone/.next/static/chunks/app/page-f1e30ab6bb269149.js +1 -0
  174. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  175. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  176. package/dist/web/standalone/.next/static/chunks/{webpack-6e4d7e9a4f57bed4.js → webpack-b868033a5834586d.js} +1 -1
  177. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  178. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  179. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  180. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  181. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  182. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  183. package/dist/web/standalone/server.js +1 -1
  184. package/package.json +1 -1
  185. package/packages/mcp-server/dist/env-writer.d.ts +39 -0
  186. package/packages/mcp-server/dist/env-writer.d.ts.map +1 -0
  187. package/packages/mcp-server/dist/env-writer.js +158 -0
  188. package/packages/mcp-server/dist/env-writer.js.map +1 -0
  189. package/packages/mcp-server/dist/server.d.ts +11 -2
  190. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  191. package/packages/mcp-server/dist/server.js +102 -2
  192. package/packages/mcp-server/dist/server.js.map +1 -1
  193. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  194. package/packages/mcp-server/dist/workflow-tools.js +21 -11
  195. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  196. package/packages/mcp-server/src/env-writer.test.ts +280 -0
  197. package/packages/mcp-server/src/env-writer.ts +183 -0
  198. package/packages/mcp-server/src/secure-env-collect.test.ts +265 -0
  199. package/packages/mcp-server/src/server.ts +137 -3
  200. package/packages/mcp-server/src/workflow-tools.test.ts +110 -0
  201. package/packages/mcp-server/src/workflow-tools.ts +31 -11
  202. package/packages/pi-ai/dist/providers/amazon-bedrock.js +11 -2
  203. package/packages/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
  204. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts +4 -1
  205. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
  206. package/packages/pi-ai/dist/providers/anthropic-shared.js +8 -3
  207. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
  208. package/packages/pi-ai/dist/providers/anthropic-shared.test.js +44 -1
  209. package/packages/pi-ai/dist/providers/anthropic-shared.test.js.map +1 -1
  210. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  211. package/packages/pi-ai/dist/providers/openai-completions.js +11 -0
  212. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  213. package/packages/pi-ai/src/providers/amazon-bedrock.ts +13 -1
  214. package/packages/pi-ai/src/providers/anthropic-shared.test.ts +55 -1
  215. package/packages/pi-ai/src/providers/anthropic-shared.ts +14 -3
  216. package/packages/pi-ai/src/providers/openai-completions.ts +14 -0
  217. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.d.ts +2 -0
  218. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.d.ts.map +1 -0
  219. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +388 -0
  220. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -0
  221. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -0
  222. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  223. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  224. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts +19 -2
  225. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  226. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js +50 -1
  227. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  228. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.d.ts +1 -0
  229. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  230. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.js +1 -0
  231. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.js.map +1 -1
  232. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  233. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +168 -23
  234. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  235. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +1 -0
  236. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  237. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  238. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +6 -0
  239. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  240. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +58 -2
  241. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  242. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +1 -1
  243. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  244. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +1 -0
  245. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  246. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
  247. package/packages/pi-coding-agent/package.json +1 -1
  248. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +468 -0
  249. package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -0
  250. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.ts +58 -2
  251. package/packages/pi-coding-agent/src/modes/interactive/components/extension-input.ts +2 -0
  252. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +198 -29
  253. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -0
  254. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +66 -2
  255. package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +1 -1
  256. package/packages/pi-coding-agent/src/modes/rpc/rpc-types.ts +1 -0
  257. package/packages/pi-tui/dist/components/__tests__/input.test.js +9 -0
  258. package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
  259. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.d.ts +2 -0
  260. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.d.ts.map +1 -0
  261. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js +66 -0
  262. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js.map +1 -0
  263. package/packages/pi-tui/dist/components/input.d.ts +2 -0
  264. package/packages/pi-tui/dist/components/input.d.ts.map +1 -1
  265. package/packages/pi-tui/dist/components/input.js +7 -4
  266. package/packages/pi-tui/dist/components/input.js.map +1 -1
  267. package/packages/pi-tui/dist/components/markdown.d.ts +3 -0
  268. package/packages/pi-tui/dist/components/markdown.d.ts.map +1 -1
  269. package/packages/pi-tui/dist/components/markdown.js +17 -1
  270. package/packages/pi-tui/dist/components/markdown.js.map +1 -1
  271. package/packages/pi-tui/src/components/__tests__/input.test.ts +11 -0
  272. package/packages/pi-tui/src/components/__tests__/markdown-maxlines.test.ts +75 -0
  273. package/packages/pi-tui/src/components/input.ts +7 -4
  274. package/packages/pi-tui/src/components/markdown.ts +22 -1
  275. package/pkg/package.json +1 -1
  276. package/src/resources/GSD-WORKFLOW.md +1 -1
  277. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +166 -31
  278. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +145 -0
  279. package/src/resources/extensions/get-secrets-from-user.ts +24 -1
  280. package/src/resources/extensions/gsd/auto-start.ts +4 -14
  281. package/src/resources/extensions/gsd/auto-tool-tracking.ts +1 -1
  282. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +6 -0
  283. package/src/resources/extensions/gsd/bootstrap/system-context.ts +7 -0
  284. package/src/resources/extensions/gsd/commands/context.ts +16 -5
  285. package/src/resources/extensions/gsd/commands/dispatcher.ts +14 -2
  286. package/src/resources/extensions/gsd/custom-workflow-engine.ts +19 -14
  287. package/src/resources/extensions/gsd/dispatch-guard.ts +18 -1
  288. package/src/resources/extensions/gsd/error-classifier.ts +1 -1
  289. package/src/resources/extensions/gsd/file-lock.ts +59 -0
  290. package/src/resources/extensions/gsd/guided-flow.ts +12 -9
  291. package/src/resources/extensions/gsd/init-wizard.ts +3 -13
  292. package/src/resources/extensions/gsd/notification-store.ts +19 -1
  293. package/src/resources/extensions/gsd/notification-widget.ts +1 -1
  294. package/src/resources/extensions/gsd/pre-execution-checks.ts +39 -2
  295. package/src/resources/extensions/gsd/prompts/complete-slice.md +2 -2
  296. package/src/resources/extensions/gsd/prompts/discuss.md +33 -13
  297. package/src/resources/extensions/gsd/prompts/execute-task.md +20 -19
  298. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  299. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +2 -0
  300. package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  301. package/src/resources/extensions/gsd/prompts/queue.md +3 -2
  302. package/src/resources/extensions/gsd/prompts/system.md +1 -0
  303. package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -1
  304. package/src/resources/extensions/gsd/state.ts +274 -344
  305. package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +28 -0
  306. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +39 -0
  307. package/src/resources/extensions/gsd/tests/complete-slice-prompt-task-summary-layout.test.ts +18 -0
  308. package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +436 -0
  309. package/src/resources/extensions/gsd/tests/discuss-incremental-persistence.test.ts +9 -0
  310. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +27 -0
  311. package/src/resources/extensions/gsd/tests/execute-task-prompt-existing-artifact-guard.test.ts +33 -0
  312. package/src/resources/extensions/gsd/tests/file-lock.test.ts +103 -0
  313. package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +73 -0
  314. package/src/resources/extensions/gsd/tests/notification-store.test.ts +17 -0
  315. package/src/resources/extensions/gsd/tests/notification-widget.test.ts +25 -0
  316. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +49 -0
  317. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +19 -0
  318. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +7 -0
  319. package/src/resources/extensions/gsd/tests/secure-env-collect.test.ts +45 -0
  320. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +7 -0
  321. package/src/resources/extensions/gsd/tests/validate-milestone-prompt-verification-classes.test.ts +18 -0
  322. package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +76 -0
  323. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +155 -1
  324. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +22 -0
  325. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +60 -25
  326. package/src/resources/extensions/gsd/workflow-events.ts +34 -25
  327. package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +76 -0
  328. package/src/resources/extensions/gsd/workflow-mcp.ts +1 -1
  329. package/src/resources/skills/create-skill/SKILL.md +2 -0
  330. package/dist/web/standalone/.next/static/chunks/2826.821e01b07d92e948.js +0 -9
  331. package/dist/web/standalone/.next/static/chunks/app/page-7115e62689b5fd84.js +0 -1
  332. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  333. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  334. /package/dist/web/standalone/.next/static/{9pw9EXtXjdM7EFrCXUEPf → DpE_M0QWUMtGDPkq_Dvfr}/_buildManifest.js +0 -0
  335. /package/dist/web/standalone/.next/static/{9pw9EXtXjdM7EFrCXUEPf → DpE_M0QWUMtGDPkq_Dvfr}/_ssgManifest.js +0 -0
@@ -248,17 +248,9 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
248
248
  logWarning("engine", `mkdir failed: ${err instanceof Error ? err.message : String(err)}`);
249
249
  }
250
250
  }
251
- if (ctx.model?.provider === "claude-code") {
252
- try {
253
- const { ensureProjectWorkflowMcpConfig } = await import("./mcp-project-config.js");
254
- const result = ensureProjectWorkflowMcpConfig(base);
255
- if (result.status !== "unchanged") {
256
- ctx.ui.notify(`Claude Code MCP prepared at ${result.configPath}`, "info");
257
- }
258
- }
259
- catch (err) {
260
- ctx.ui.notify(`Claude Code MCP prep failed: ${err instanceof Error ? err.message : String(err)}`, "warning");
261
- }
251
+ {
252
+ const { prepareWorkflowMcpForProject } = await import("./workflow-mcp-auto-prep.js");
253
+ prepareWorkflowMcpForProject(ctx, base);
262
254
  }
263
255
  // Initialize GitServiceImpl
264
256
  s.gitService = new GitServiceImpl(s.basePath, loadEffectiveGSDPreferences()?.preferences?.git ?? {});
@@ -531,7 +523,7 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
531
523
  }
532
524
  }
533
525
  // ── DB lifecycle ──
534
- const gsdDbPath = join(s.basePath, ".gsd", "gsd.db");
526
+ const gsdDbPath = resolveProjectRootDbPath(s.basePath);
535
527
  const gsdDirPath = join(s.basePath, ".gsd");
536
528
  if (existsSync(gsdDirPath) && !existsSync(gsdDbPath)) {
537
529
  const hasDecisions = existsSync(join(gsdDirPath, "DECISIONS.md"));
@@ -82,7 +82,7 @@ export function clearInFlightTools() {
82
82
  * handler. When these errors occur, retrying the same unit will produce the same
83
83
  * failure, so the retry loop must be broken.
84
84
  */
85
- const TOOL_INVOCATION_ERROR_RE = /Validation failed for tool|Expected ',' or '\}' in JSON|Unexpected end of JSON|Unexpected token.*in JSON/i;
85
+ const TOOL_INVOCATION_ERROR_RE = /Validation failed for tool|Expected ',' or '\}'(?: after property value)?(?: in JSON)?|Unexpected end of JSON|Unexpected token.*in JSON/i;
86
86
  /**
87
87
  * Returns true if the error message indicates a tool invocation failure due to
88
88
  * malformed/truncated arguments (as opposed to a normal tool execution error).
@@ -39,6 +39,8 @@ export function registerHooks(pi) {
39
39
  resetToolCallLoopGuard();
40
40
  resetAskUserQuestionsCache();
41
41
  await syncServiceTierStatus(ctx);
42
+ const { prepareWorkflowMcpForProject } = await import("../workflow-mcp-auto-prep.js");
43
+ prepareWorkflowMcpForProject(ctx, process.cwd());
42
44
  // Apply show_token_cost preference (#1515)
43
45
  try {
44
46
  const { loadEffectiveGSDPreferences } = await import("../preferences.js");
@@ -78,6 +80,8 @@ export function registerHooks(pi) {
78
80
  resetAskUserQuestionsCache();
79
81
  clearDiscussionFlowState();
80
82
  await syncServiceTierStatus(ctx);
83
+ const { prepareWorkflowMcpForProject } = await import("../workflow-mcp-auto-prep.js");
84
+ prepareWorkflowMcpForProject(ctx, process.cwd());
81
85
  loadToolApiKeys();
82
86
  });
83
87
  pi.on("before_agent_start", async (event, ctx) => {
@@ -107,6 +111,8 @@ export function registerHooks(pi) {
107
111
  return { cancel: true };
108
112
  }
109
113
  const basePath = process.cwd();
114
+ const { ensureDbOpen } = await import("./dynamic-tools.js");
115
+ await ensureDbOpen();
110
116
  const state = await deriveState(basePath);
111
117
  if (!state.activeMilestone || !state.activeSlice || !state.activeTask)
112
118
  return;
@@ -257,6 +257,10 @@ function buildWorktreeContextBlock() {
257
257
  */
258
258
  const RESUME_INTENT_PATTERNS = /^(continue|resume|ok|go|go ahead|proceed|keep going|carry on|next|yes|yeah|yep|sure|do it|let's go|pick up where you left off)$/;
259
259
  async function buildGuidedExecuteContextInjection(prompt, basePath) {
260
+ const ensureStateDbOpen = async () => {
261
+ const { ensureDbOpen } = await import("./dynamic-tools.js");
262
+ await ensureDbOpen();
263
+ };
260
264
  const executeMatch = prompt.match(/Execute the next task:\s+(T\d+)\s+\("([^"]+)"\)\s+in slice\s+(S\d+)\s+of milestone\s+(M\d+(?:-[a-z0-9]{6})?)/i);
261
265
  if (executeMatch) {
262
266
  const [, taskId, taskTitle, sliceId, milestoneId] = executeMatch;
@@ -265,6 +269,7 @@ async function buildGuidedExecuteContextInjection(prompt, basePath) {
265
269
  const resumeMatch = prompt.match(/Resume interrupted work\.[\s\S]*?slice\s+(S\d+)\s+of milestone\s+(M\d+(?:-[a-z0-9]{6})?)/i);
266
270
  if (resumeMatch) {
267
271
  const [, sliceId, milestoneId] = resumeMatch;
272
+ await ensureStateDbOpen();
268
273
  const state = await deriveState(basePath);
269
274
  if (state.activeMilestone?.id === milestoneId && state.activeSlice?.id === sliceId && state.activeTask) {
270
275
  return buildTaskExecutionContextInjection(basePath, milestoneId, sliceId, state.activeTask.id, state.activeTask.title);
@@ -279,6 +284,7 @@ async function buildGuidedExecuteContextInjection(prompt, basePath) {
279
284
  // replanning, gate evaluation, or other non-execution phases.
280
285
  const trimmed = prompt.trim().toLowerCase().replace(/[.!?,]+$/g, "");
281
286
  if (RESUME_INTENT_PATTERNS.test(trimmed)) {
287
+ await ensureStateDbOpen();
282
288
  const state = await deriveState(basePath);
283
289
  if (state.phase === "executing" && state.activeTask && state.activeMilestone && state.activeSlice) {
284
290
  return buildTaskExecutionContextInjection(basePath, state.activeMilestone.id, state.activeSlice.id, state.activeTask.id, state.activeTask.title);
@@ -1,8 +1,18 @@
1
1
  import { checkRemoteAutoSession, isAutoActive, isAutoPaused, stopAutoRemote } from "../auto.js";
2
- import { assertSafeDirectory } from "../validate-directory.js";
2
+ import { validateDirectory } from "../validate-directory.js";
3
3
  import { resolveProjectRoot } from "../worktree.js";
4
4
  import { showNextAction } from "../../shared/tui.js";
5
5
  import { handleStatus } from "./handlers/core.js";
6
+ /**
7
+ * Typed error for when GSD is run outside a valid project directory.
8
+ * Command handlers catch this to show a friendly message instead of a raw exception.
9
+ */
10
+ export class GSDNoProjectError extends Error {
11
+ constructor(reason) {
12
+ super(reason);
13
+ this.name = "GSDNoProjectError";
14
+ }
15
+ }
6
16
  export function projectRoot() {
7
17
  let cwd;
8
18
  try {
@@ -13,11 +23,10 @@ export function projectRoot() {
13
23
  cwd = process.env.HOME ?? "/";
14
24
  }
15
25
  const root = resolveProjectRoot(cwd);
16
- if (root !== cwd) {
17
- assertSafeDirectory(cwd);
18
- }
19
- else {
20
- assertSafeDirectory(root);
26
+ const pathToCheck = root !== cwd ? cwd : root;
27
+ const result = validateDirectory(pathToCheck);
28
+ if (result.severity === "blocked") {
29
+ throw new GSDNoProjectError(result.reason ?? "GSD must be run inside a project directory.");
21
30
  }
22
31
  return root;
23
32
  }
@@ -1,3 +1,4 @@
1
+ import { GSDNoProjectError } from "./context.js";
1
2
  import { handleAutoCommand } from "./handlers/auto.js";
2
3
  import { handleCoreCommand } from "./handlers/core.js";
3
4
  import { handleOpsCommand } from "./handlers/ops.js";
@@ -12,10 +13,19 @@ export async function handleGSDCommand(args, ctx, pi) {
12
13
  () => handleWorkflowCommand(trimmed, ctx, pi),
13
14
  () => handleOpsCommand(trimmed, ctx, pi),
14
15
  ];
15
- for (const handler of handlers) {
16
- if (await handler()) {
16
+ try {
17
+ for (const handler of handlers) {
18
+ if (await handler()) {
19
+ return;
20
+ }
21
+ }
22
+ }
23
+ catch (err) {
24
+ if (err instanceof GSDNoProjectError) {
25
+ ctx.ui.notify(`${err.message} \`cd\` into a project directory first.`, "warning");
17
26
  return;
18
27
  }
28
+ throw err;
19
29
  }
20
30
  ctx.ui.notify(`Unknown: /gsd ${trimmed}. Run /gsd help for available commands.`, "warning");
21
31
  }
@@ -17,6 +17,7 @@ import { parse } from "yaml";
17
17
  import { readGraph, writeGraph, getNextPendingStep, markStepComplete, expandIteration, } from "./graph.js";
18
18
  import { injectContext } from "./context-injector.js";
19
19
  import { parseUnitId } from "./unit-id.js";
20
+ import { withFileLock } from "./file-lock.js";
20
21
  /** Read and parse the frozen DEFINITION.yaml from a run directory. */
21
22
  export function readFrozenDefinition(runDir) {
22
23
  const defPath = join(runDir, "DEFINITION.yaml");
@@ -135,18 +136,21 @@ export class CustomWorkflowEngine {
135
136
  * Returns "milestone-complete" when all steps are now done, "continue" otherwise.
136
137
  */
137
138
  async reconcile(state, completedStep) {
138
- // Re-read the graph from disk so we do not overwrite concurrent
139
- // workflow edits with a stale in-memory snapshot from deriveState().
140
- const graph = readGraph(this.runDir);
141
- // Extract stepId from "<workflowName>/<stepId>"
142
- const { milestone, slice, task } = parseUnitId(completedStep.unitId);
143
- const stepId = task ?? slice ?? milestone;
144
- const updatedGraph = markStepComplete(graph, stepId);
145
- writeGraph(this.runDir, updatedGraph);
146
- const allDone = updatedGraph.steps.every((s) => s.status === "complete" || s.status === "expanded");
147
- return {
148
- outcome: allDone ? "milestone-complete" : "continue",
149
- };
139
+ const graphPath = join(this.runDir, "GRAPH.yaml");
140
+ return await withFileLock(graphPath, () => {
141
+ // Re-read the graph from disk so we do not overwrite concurrent
142
+ // workflow edits with a stale in-memory snapshot from deriveState().
143
+ const graph = readGraph(this.runDir);
144
+ // Extract stepId from "<workflowName>/<stepId>"
145
+ const { milestone, slice, task } = parseUnitId(completedStep.unitId);
146
+ const stepId = task ?? slice ?? milestone;
147
+ const updatedGraph = markStepComplete(graph, stepId);
148
+ writeGraph(this.runDir, updatedGraph);
149
+ const allDone = updatedGraph.steps.every((s) => s.status === "complete" || s.status === "expanded");
150
+ return {
151
+ outcome: allDone ? "milestone-complete" : "continue",
152
+ };
153
+ });
150
154
  }
151
155
  /**
152
156
  * Return UI-facing metadata for progress display.
@@ -102,10 +102,27 @@ export function getPriorSliceCompletionBlocker(base, _mainBranch, unitType, unit
102
102
  }
103
103
  }
104
104
  else {
105
+ // Positional fallback is only a heuristic for legacy slices with no
106
+ // declared dependencies. Skip any earlier slice that depends on the
107
+ // target, directly or transitively, or we can deadlock a valid zero-dep
108
+ // slice behind its own downstream dependents (#3720).
109
+ const reverseDependents = new Set();
110
+ let changed = true;
111
+ while (changed) {
112
+ changed = false;
113
+ for (const slice of slices) {
114
+ if (reverseDependents.has(slice.id))
115
+ continue;
116
+ if (slice.depends.some((depId) => depId === targetSid || reverseDependents.has(depId))) {
117
+ reverseDependents.add(slice.id);
118
+ changed = true;
119
+ }
120
+ }
121
+ }
105
122
  const targetIndex = slices.findIndex((slice) => slice.id === targetSid);
106
123
  const incomplete = slices
107
124
  .slice(0, targetIndex)
108
- .find((slice) => !slice.done);
125
+ .find((slice) => !slice.done && !reverseDependents.has(slice.id));
109
126
  if (incomplete) {
110
127
  return `Cannot dispatch ${unitType} ${unitId}: earlier slice ${targetMid}/${incomplete.id} is not complete.`;
111
128
  }
@@ -23,7 +23,7 @@ const RATE_LIMIT_RE = /rate.?limit|too many requests|429/i;
23
23
  const NETWORK_RE = /network|ECONNRESET|ETIMEDOUT|ECONNREFUSED|socket hang up|fetch failed|connection.*reset|dns/i;
24
24
  const SERVER_RE = /internal server error|500|502|503|overloaded|server_error|api_error|service.?unavailable/i;
25
25
  // ECONNRESET/ECONNREFUSED are in NETWORK_RE (same-model retry first).
26
- const CONNECTION_RE = /terminated|connection.?refused|other side closed|EPIPE|network.?(?:is\s+)?unavailable|stream_exhausted(?:_without_result)?/i;
26
+ const CONNECTION_RE = /terminated|connection.?(?:refused|error)|other side closed|EPIPE|network.?(?:is\s+)?unavailable|stream_exhausted(?:_without_result)?/i;
27
27
  // Catch-all for V8 JSON.parse errors: all modern variants end with "in JSON at position \d+".
28
28
  // This eliminates the need to enumerate every error message variant individually.
29
29
  const STREAM_RE = /in JSON at position \d+|Unexpected end of JSON|SyntaxError.*JSON/i;
@@ -0,0 +1,60 @@
1
+ import { existsSync } from "node:fs";
2
+ function _require(name) {
3
+ try {
4
+ return require(name);
5
+ }
6
+ catch {
7
+ try {
8
+ const gsdPiRequire = require("module").createRequire(require("path").join(process.cwd(), "node_modules", "gsd-pi", "index.js"));
9
+ return gsdPiRequire(name);
10
+ }
11
+ catch {
12
+ return null;
13
+ }
14
+ }
15
+ }
16
+ export function withFileLockSync(filePath, fn) {
17
+ const lockfile = _require("proper-lockfile");
18
+ if (!lockfile)
19
+ return fn();
20
+ if (!existsSync(filePath))
21
+ return fn();
22
+ try {
23
+ const release = lockfile.lockSync(filePath, { retries: 5, stale: 10000 });
24
+ try {
25
+ return fn();
26
+ }
27
+ finally {
28
+ release();
29
+ }
30
+ }
31
+ catch (err) {
32
+ if (err.code === "ELOCKED") {
33
+ // Could not get lock after retries, let's fallback to un-locked instead of crashing the whole state machine
34
+ return fn();
35
+ }
36
+ throw err;
37
+ }
38
+ }
39
+ export async function withFileLock(filePath, fn) {
40
+ const lockfile = _require("proper-lockfile");
41
+ if (!lockfile)
42
+ return await fn();
43
+ if (!existsSync(filePath))
44
+ return await fn();
45
+ try {
46
+ const release = await lockfile.lock(filePath, { retries: 5, stale: 10000 });
47
+ try {
48
+ return await fn();
49
+ }
50
+ finally {
51
+ await release();
52
+ }
53
+ }
54
+ catch (err) {
55
+ if (err.code === "ELOCKED") {
56
+ return await fn();
57
+ }
58
+ throw err;
59
+ }
60
+ }
@@ -334,8 +334,9 @@ function resolveAvailableModel(modelId, availableModels, currentProvider) {
334
334
  * Build the discuss-and-plan prompt for a new milestone.
335
335
  * Used by all three "new milestone" paths (first ever, no active, all complete).
336
336
  */
337
- function buildDiscussPrompt(nextId, preamble, _basePath, preparationContext) {
337
+ function buildDiscussPrompt(nextId, preamble, _basePath, pi, ctx, preparationContext) {
338
338
  const milestoneRel = `.gsd/milestones/${nextId}`;
339
+ const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
339
340
  const inlinedTemplates = [
340
341
  inlineTemplate("project", "Project"),
341
342
  inlineTemplate("requirements", "Requirements"),
@@ -347,6 +348,7 @@ function buildDiscussPrompt(nextId, preamble, _basePath, preparationContext) {
347
348
  milestoneId: nextId,
348
349
  preamble,
349
350
  preparationContext: preparationContext ?? "",
351
+ structuredQuestionsAvailable,
350
352
  contextPath: `${milestoneRel}/${nextId}-CONTEXT.md`,
351
353
  roadmapPath: `${milestoneRel}/${nextId}-ROADMAP.md`,
352
354
  inlinedTemplates,
@@ -390,7 +392,7 @@ function buildHeadlessDiscussPrompt(nextId, seedContext, _basePath) {
390
392
  * @param basePath - Root directory of the project
391
393
  * @returns The discuss prompt string
392
394
  */
393
- async function prepareAndBuildDiscussPrompt(ctx, nextId, preamble, basePath) {
395
+ async function prepareAndBuildDiscussPrompt(ctx, pi, nextId, preamble, basePath) {
394
396
  const prefs = loadEffectiveGSDPreferences()?.preferences ?? {};
395
397
  // Run preparation if enabled (default: true) — results are injected as
396
398
  // supplementary context into the standard discuss prompt, NOT as a
@@ -421,7 +423,7 @@ async function prepareAndBuildDiscussPrompt(ctx, nextId, preamble, basePath) {
421
423
  logWarning("guided", `preparation failed, proceeding without context: ${err.message}`);
422
424
  }
423
425
  }
424
- return buildDiscussPrompt(nextId, preamble, basePath, preparationContext);
426
+ return buildDiscussPrompt(nextId, preamble, basePath, pi, ctx, preparationContext);
425
427
  }
426
428
  /**
427
429
  * Bootstrap a .gsd/ project from scratch for headless use.
@@ -638,7 +640,7 @@ export async function showDiscuss(ctx, pi, basePath) {
638
640
  const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
639
641
  const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
640
642
  pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: false, createdAt: Date.now() });
641
- await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
643
+ await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
642
644
  }
643
645
  return;
644
646
  }
@@ -994,7 +996,7 @@ async function handleMilestoneActions(ctx, pi, basePath, milestoneId, milestoneT
994
996
  const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
995
997
  const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
996
998
  pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
997
- await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
999
+ await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
998
1000
  return true;
999
1001
  }
1000
1002
  // "back" or null
@@ -1161,7 +1163,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
1161
1163
  if (isFirst) {
1162
1164
  // First ever — skip wizard, just ask directly
1163
1165
  pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
1164
- await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New project, milestone ${nextId}. Do NOT read or explore .gsd/ — it's empty scaffolding.`, basePath), "gsd-run", ctx, "discuss-milestone");
1166
+ await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New project, milestone ${nextId}. Do NOT read or explore .gsd/ — it's empty scaffolding.`, basePath), "gsd-run", ctx, "discuss-milestone");
1165
1167
  }
1166
1168
  else {
1167
1169
  const choice = await showNextAction(ctx, {
@@ -1179,7 +1181,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
1179
1181
  });
1180
1182
  if (choice === "new_milestone") {
1181
1183
  pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
1182
- await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
1184
+ await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
1183
1185
  }
1184
1186
  }
1185
1187
  return;
@@ -1211,7 +1213,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
1211
1213
  const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
1212
1214
  const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
1213
1215
  pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
1214
- await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
1216
+ await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
1215
1217
  }
1216
1218
  else if (choice === "status") {
1217
1219
  const { fireStatusViaCommand } = await import("./commands.js");
@@ -1275,7 +1277,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
1275
1277
  const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
1276
1278
  const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
1277
1279
  pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
1278
- await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
1280
+ await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
1279
1281
  }
1280
1282
  return;
1281
1283
  }
@@ -1365,7 +1367,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
1365
1367
  const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
1366
1368
  const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
1367
1369
  pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
1368
- await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
1370
+ await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
1369
1371
  }
1370
1372
  else if (choice === "discard_milestone") {
1371
1373
  const confirmed = await showConfirm(ctx, {
@@ -225,17 +225,9 @@ export async function showProjectInit(ctx, pi, basePath, detection) {
225
225
  catch {
226
226
  // Non-fatal — STATE.md will be regenerated on next /gsd invocation
227
227
  }
228
- if (ctx.model?.provider === "claude-code") {
229
- try {
230
- const { ensureProjectWorkflowMcpConfig } = await import("./mcp-project-config.js");
231
- const result = ensureProjectWorkflowMcpConfig(basePath);
232
- if (result.status !== "unchanged") {
233
- ctx.ui.notify(`Claude Code MCP prepared at ${result.configPath}`, "info");
234
- }
235
- }
236
- catch (err) {
237
- ctx.ui.notify(`Claude Code MCP prep failed: ${err instanceof Error ? err.message : String(err)}`, "warning");
238
- }
228
+ {
229
+ const { prepareWorkflowMcpForProject } = await import("./workflow-mcp-auto-prep.js");
230
+ prepareWorkflowMcpForProject(ctx, basePath);
239
231
  }
240
232
  ctx.ui.notify("GSD initialized. Starting your first milestone...", "info");
241
233
  return { completed: true, bootstrapped: true };
@@ -9,16 +9,22 @@ import { randomUUID } from "node:crypto";
9
9
  const MAX_ENTRIES = 500;
10
10
  const FILENAME = "notifications.jsonl";
11
11
  const LOCKFILE = "notifications.lock";
12
+ const DEDUP_WINDOW_MS = 30_000;
13
+ const DEDUP_PRUNE_THRESHOLD = 200;
12
14
  // ─── Module State ───────────────────────────────────────────────────────
13
15
  let _basePath = null;
14
16
  let _lineCount = 0; // Hint for rotation — not authoritative for public API
15
17
  let _suppressCount = 0;
18
+ let _recentMessageTimestamps = new Map();
16
19
  // ─── Public API ─────────────────────────────────────────────────────────
17
20
  /**
18
21
  * Initialize the notification store. Call once at session start with the
19
22
  * project root. Seeds in-memory counters from the existing file on disk.
20
23
  */
21
24
  export function initNotificationStore(basePath) {
25
+ if (_basePath !== basePath) {
26
+ _recentMessageTimestamps.clear();
27
+ }
22
28
  _basePath = basePath;
23
29
  // Seed line count hint for rotation — public counters read from disk
24
30
  _lineCount = _readEntriesFromDisk(basePath).length;
@@ -32,11 +38,24 @@ export function appendNotification(message, severity, source = "notify") {
32
38
  return;
33
39
  if (_suppressCount > 0)
34
40
  return;
41
+ const persistedMessage = message.length > 500 ? message.slice(0, 500) + "…" : message;
42
+ const dedupKey = `${_basePath}:${severity}:${source}:${persistedMessage}`;
43
+ const now = Date.now();
44
+ const lastSeen = _recentMessageTimestamps.get(dedupKey);
45
+ if (lastSeen !== undefined && now - lastSeen < DEDUP_WINDOW_MS)
46
+ return;
47
+ _recentMessageTimestamps.set(dedupKey, now);
48
+ if (_recentMessageTimestamps.size > DEDUP_PRUNE_THRESHOLD) {
49
+ for (const [key, ts] of _recentMessageTimestamps) {
50
+ if (now - ts > DEDUP_WINDOW_MS)
51
+ _recentMessageTimestamps.delete(key);
52
+ }
53
+ }
35
54
  const entry = {
36
55
  id: randomUUID(),
37
56
  ts: new Date().toISOString(),
38
57
  severity,
39
- message: message.length > 500 ? message.slice(0, 500) + "…" : message,
58
+ message: persistedMessage,
40
59
  source,
41
60
  read: false,
42
61
  };
@@ -154,6 +173,7 @@ export function _resetNotificationStore() {
154
173
  _basePath = null;
155
174
  _lineCount = 0;
156
175
  _suppressCount = 0;
176
+ _recentMessageTimestamps = new Map();
157
177
  }
158
178
  // ─── Internal ───────────────────────────────────────────────────────────
159
179
  function _readEntriesFromDisk(basePath) {
@@ -19,7 +19,7 @@ export function buildNotificationWidgetLines() {
19
19
  const truncated = latest.message.length > msgMax
20
20
  ? latest.message.slice(0, msgMax - 1) + "…"
21
21
  : latest.message;
22
- return [` ${icon} [${badge}] ${truncated} (${formatShortcut("Ctrl+Alt+N")} to view)`];
22
+ return [` ${icon} [${badge}] ${truncated} (${formatShortcut("Ctrl+Alt+N")} or /gsd notifications)`];
23
23
  }
24
24
  // ─── Widget init ────────────────────────────────────────────────────────
25
25
  const REFRESH_INTERVAL_MS = 5_000;
@@ -229,6 +229,33 @@ function extractPathFromAnnotation(raw) {
229
229
  // Fall back to the original behavior for already-plain paths.
230
230
  return trimmed.replace(/`/g, "");
231
231
  }
232
+ /**
233
+ * Planning units sometimes use task.inputs for prose like "Current enum shape"
234
+ * instead of concrete file paths. Those entries should not fail path checks.
235
+ * Keep validation for anything that still looks like a real file reference:
236
+ * explicit backticks, globs, separators, dot-paths, or single-token basenames
237
+ * like Dockerfile.
238
+ */
239
+ function shouldValidateInputAsPath(raw) {
240
+ const trimmed = raw.trim();
241
+ if (!trimmed)
242
+ return false;
243
+ if (/^`+[^`]+`+/.test(trimmed)) {
244
+ return true;
245
+ }
246
+ const candidate = extractPathFromAnnotation(trimmed);
247
+ if (!candidate)
248
+ return false;
249
+ if (!/\s/.test(candidate)) {
250
+ return true;
251
+ }
252
+ return (candidate.startsWith("/") ||
253
+ candidate.startsWith("./") ||
254
+ candidate.startsWith("../") ||
255
+ candidate.startsWith("~/") ||
256
+ /[\\/]/.test(candidate) ||
257
+ /[*?[\]{}]/.test(candidate));
258
+ }
232
259
  /**
233
260
  * Build a set of files that will be created by tasks up to (but not including) taskIndex.
234
261
  * All paths are normalized for consistent comparison.
@@ -262,6 +289,8 @@ export function checkFilePathConsistency(tasks, basePath) {
262
289
  // Skip empty strings
263
290
  if (!file.trim())
264
291
  continue;
292
+ if (!shouldValidateInputAsPath(file))
293
+ continue;
265
294
  // Normalize path for consistent comparison
266
295
  const normalizedFile = normalizeFilePath(file);
267
296
  // Check if file exists on disk
@@ -289,7 +318,7 @@ export function checkFilePathConsistency(tasks, basePath) {
289
318
  *
290
319
  * All paths are normalized before comparison to ensure ./src/a.ts matches src/a.ts.
291
320
  */
292
- export function checkTaskOrdering(tasks, _basePath) {
321
+ export function checkTaskOrdering(tasks, basePath) {
293
322
  const results = [];
294
323
  // Build map: normalized file → task index that creates it
295
324
  const fileCreators = new Map();
@@ -309,9 +338,13 @@ export function checkTaskOrdering(tasks, _basePath) {
309
338
  const task = tasks[i];
310
339
  const filesToCheck = [...task.inputs];
311
340
  for (const file of filesToCheck) {
341
+ if (!shouldValidateInputAsPath(file))
342
+ continue;
312
343
  const normalizedFile = normalizeFilePath(file);
313
344
  const creator = fileCreators.get(normalizedFile);
314
- if (creator && creator.index > i) {
345
+ const absolutePath = resolve(basePath, normalizedFile);
346
+ const existsOnDisk = existsSync(absolutePath);
347
+ if (creator && creator.index > i && !existsOnDisk) {
315
348
  // Task reads file that is created later — impossible ordering
316
349
  results.push({
317
350
  category: "file",
@@ -21,7 +21,7 @@ All relevant context has been preloaded below — the slice plan, all task summa
21
21
  Then:
22
22
  1. Use the **Slice Summary** and **UAT** output templates from the inlined context above
23
23
  2. {{skillActivation}}
24
- 3. Run all slice-level verification checks defined in the slice plan. All must pass before marking the slice done. If any fail, fix them first.
24
+ 3. Run all slice-level verification checks defined in the slice plan. All must pass before marking the slice done. If any fail, fix them first. Task artifacts use a **flat file layout** directly inside `tasks/` (for example `T01-SUMMARY.md`, `T02-SUMMARY.md`) rather than per-task subdirectories. If you need to count or re-read task summaries during verification, use `find .gsd/milestones/{{milestoneId}}/slices/{{sliceId}}/tasks -name "*-SUMMARY.md"` or `ls .gsd/milestones/{{milestoneId}}/slices/{{sliceId}}/tasks/*-SUMMARY.md`. Never use `tasks/*/SUMMARY.md` — that glob expects subdirectories that do not exist.
25
25
  4. If the slice plan includes observability/diagnostic surfaces, confirm they work. Skip this for simple slices that don't have observability sections.
26
26
  5. If the slice involved runtime behavior, fill the **Operational Readiness** section (Q8) in the slice summary: health signal, failure signal, recovery procedure, and monitoring gaps. Omit entirely for simple slices with no runtime concerns.
27
27
  6. If this slice produced evidence that a requirement changed status (Active → Validated, Active → Deferred, etc.), call `gsd_requirement_update` with the requirement ID, updated `status`, and `validation` evidence. Do NOT write `.gsd/REQUIREMENTS.md` directly — the engine renders it from the database.
@@ -35,7 +35,7 @@ Then:
35
35
 
36
36
  **Autonomous execution:** Do not call `ask_user_questions` or `secure_env_collect`. You are running in auto-mode — there is no human available to answer questions. Make reasonable assumptions and document them in the slice summary. If a decision genuinely requires human input, note it in the summary and proceed with the best available option.
37
37
 
38
- **File system safety:** Task summaries are preloaded in the inlined context above. If you need to re-read any of them, use `find .gsd/milestones/{{milestoneId}}/slices/{{sliceId}}/tasks -name "*-SUMMARY.md"` to list file paths first never pass `{{slicePath}}` or any other directory path directly to the `read` tool. The `read` tool only accepts file paths, not directories.
38
+ **File system safety:** Task summaries are preloaded in the inlined context above. Task artifacts use a **flat file layout** — files such as `T01-SUMMARY.md` and `T02-SUMMARY.md` live directly inside the `tasks/` directory, not inside per-task subdirectories like `tasks/T01/SUMMARY.md`. If you need to re-read any of them, use `find .gsd/milestones/{{milestoneId}}/slices/{{sliceId}}/tasks -name "*-SUMMARY.md"` to list file paths first. Never use `tasks/*/SUMMARY.md`, and never pass `{{slicePath}}` or any other directory path directly to the `read` tool. The `read` tool only accepts file paths, not directories.
39
39
 
40
40
  **You MUST call `gsd_complete_slice` with the slice summary and UAT content before finishing. The tool persists to both DB and disk and renders `{{sliceSummaryPath}}` and `{{sliceUatPath}}` automatically.**
41
41