gsd-pi 2.38.0-dev.eeb3520 → 2.39.0-dev.20aba06

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 (346) hide show
  1. package/README.md +15 -11
  2. package/dist/app-paths.js +1 -1
  3. package/dist/cli.js +9 -0
  4. package/dist/extension-discovery.d.ts +5 -3
  5. package/dist/extension-discovery.js +14 -9
  6. package/dist/extension-registry.js +2 -2
  7. package/dist/remote-questions-config.js +2 -2
  8. package/dist/resource-loader.js +100 -3
  9. package/dist/resources/extensions/async-jobs/index.js +10 -0
  10. package/dist/resources/extensions/browser-tools/index.js +3 -1
  11. package/dist/resources/extensions/browser-tools/package.json +3 -1
  12. package/dist/resources/extensions/browser-tools/tools/verify.js +97 -0
  13. package/dist/resources/extensions/cmux/index.js +55 -1
  14. package/dist/resources/extensions/context7/package.json +1 -1
  15. package/dist/resources/extensions/get-secrets-from-user.js +5 -24
  16. package/dist/resources/extensions/github-sync/cli.js +284 -0
  17. package/dist/resources/extensions/github-sync/index.js +73 -0
  18. package/dist/resources/extensions/github-sync/mapping.js +67 -0
  19. package/dist/resources/extensions/github-sync/sync.js +424 -0
  20. package/dist/resources/extensions/github-sync/templates.js +118 -0
  21. package/dist/resources/extensions/github-sync/types.js +7 -0
  22. package/dist/resources/extensions/google-search/package.json +3 -1
  23. package/dist/resources/extensions/gsd/auto/session.js +6 -23
  24. package/dist/resources/extensions/gsd/auto-dashboard.js +7 -0
  25. package/dist/resources/extensions/gsd/auto-dispatch.js +8 -9
  26. package/dist/resources/extensions/gsd/auto-loop.js +923 -787
  27. package/dist/resources/extensions/gsd/auto-post-unit.js +107 -70
  28. package/dist/resources/extensions/gsd/auto-prompts.js +205 -51
  29. package/dist/resources/extensions/gsd/auto-start.js +19 -3
  30. package/dist/resources/extensions/gsd/auto-worktree-sync.js +13 -5
  31. package/dist/resources/extensions/gsd/auto-worktree.js +3 -3
  32. package/dist/resources/extensions/gsd/auto.js +149 -100
  33. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +126 -0
  34. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +233 -0
  35. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +59 -0
  36. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +38 -0
  37. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +156 -0
  38. package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +46 -0
  39. package/dist/resources/extensions/gsd/bootstrap/system-context.js +300 -0
  40. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +38 -0
  41. package/dist/resources/extensions/gsd/captures.js +9 -1
  42. package/dist/resources/extensions/gsd/commands/catalog.js +278 -0
  43. package/dist/resources/extensions/gsd/commands/context.js +84 -0
  44. package/dist/resources/extensions/gsd/commands/dispatcher.js +21 -0
  45. package/dist/resources/extensions/gsd/commands/handlers/auto.js +72 -0
  46. package/dist/resources/extensions/gsd/commands/handlers/core.js +246 -0
  47. package/dist/resources/extensions/gsd/commands/handlers/ops.js +166 -0
  48. package/dist/resources/extensions/gsd/commands/handlers/parallel.js +94 -0
  49. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +102 -0
  50. package/dist/resources/extensions/gsd/commands/index.js +11 -0
  51. package/dist/resources/extensions/gsd/commands-extensions.js +3 -2
  52. package/dist/resources/extensions/gsd/commands-handlers.js +17 -4
  53. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +1 -1
  54. package/dist/resources/extensions/gsd/commands.js +8 -1169
  55. package/dist/resources/extensions/gsd/context-budget.js +2 -10
  56. package/dist/resources/extensions/gsd/dashboard-overlay.js +9 -0
  57. package/dist/resources/extensions/gsd/detection.js +1 -2
  58. package/dist/resources/extensions/gsd/docs/preferences-reference.md +0 -2
  59. package/dist/resources/extensions/gsd/doctor-checks.js +82 -0
  60. package/dist/resources/extensions/gsd/doctor-environment.js +78 -0
  61. package/dist/resources/extensions/gsd/doctor-format.js +15 -0
  62. package/dist/resources/extensions/gsd/doctor-proactive.js +80 -10
  63. package/dist/resources/extensions/gsd/doctor-providers.js +30 -11
  64. package/dist/resources/extensions/gsd/doctor.js +234 -12
  65. package/dist/resources/extensions/gsd/env-utils.js +29 -0
  66. package/dist/resources/extensions/gsd/exit-command.js +2 -1
  67. package/dist/resources/extensions/gsd/export-html.js +46 -0
  68. package/dist/resources/extensions/gsd/export.js +1 -1
  69. package/dist/resources/extensions/gsd/files.js +48 -9
  70. package/dist/resources/extensions/gsd/forensics.js +1 -1
  71. package/dist/resources/extensions/gsd/git-service.js +30 -12
  72. package/dist/resources/extensions/gsd/gitignore.js +16 -3
  73. package/dist/resources/extensions/gsd/guided-flow.js +149 -38
  74. package/dist/resources/extensions/gsd/health-widget-core.js +32 -70
  75. package/dist/resources/extensions/gsd/health-widget.js +4 -87
  76. package/dist/resources/extensions/gsd/index.js +4 -1111
  77. package/dist/resources/extensions/gsd/migrate/parsers.js +1 -1
  78. package/dist/resources/extensions/gsd/migrate-external.js +18 -1
  79. package/dist/resources/extensions/gsd/native-git-bridge.js +37 -0
  80. package/dist/resources/extensions/gsd/package.json +1 -1
  81. package/dist/resources/extensions/gsd/paths.js +3 -0
  82. package/dist/resources/extensions/gsd/preferences-models.js +0 -12
  83. package/dist/resources/extensions/gsd/preferences-types.js +1 -1
  84. package/dist/resources/extensions/gsd/preferences-validation.js +59 -11
  85. package/dist/resources/extensions/gsd/preferences.js +22 -11
  86. package/dist/resources/extensions/gsd/progress-score.js +20 -1
  87. package/dist/resources/extensions/gsd/prompt-loader.js +6 -2
  88. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  89. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  90. package/dist/resources/extensions/gsd/prompts/discuss.md +11 -14
  91. package/dist/resources/extensions/gsd/prompts/execute-task.md +5 -3
  92. package/dist/resources/extensions/gsd/prompts/forensics.md +121 -46
  93. package/dist/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
  94. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
  95. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
  96. package/dist/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
  97. package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
  98. package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
  99. package/dist/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
  100. package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  101. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  102. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  103. package/dist/resources/extensions/gsd/prompts/queue.md +4 -8
  104. package/dist/resources/extensions/gsd/prompts/reactive-execute.md +11 -8
  105. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
  106. package/dist/resources/extensions/gsd/prompts/research-milestone.md +1 -1
  107. package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
  108. package/dist/resources/extensions/gsd/prompts/run-uat.md +28 -11
  109. package/dist/resources/extensions/gsd/prompts/workflow-start.md +2 -2
  110. package/dist/resources/extensions/gsd/repo-identity.js +21 -4
  111. package/dist/resources/extensions/gsd/resource-version.js +2 -1
  112. package/dist/resources/extensions/gsd/roadmap-mutations.js +24 -0
  113. package/dist/resources/extensions/gsd/state.js +42 -23
  114. package/dist/resources/extensions/gsd/templates/runtime.md +21 -0
  115. package/dist/resources/extensions/gsd/templates/task-plan.md +3 -0
  116. package/dist/resources/extensions/gsd/visualizer-data.js +27 -2
  117. package/dist/resources/extensions/gsd/visualizer-views.js +52 -0
  118. package/dist/resources/extensions/gsd/worktree.js +35 -16
  119. package/dist/resources/extensions/mcp-client/index.js +14 -1
  120. package/dist/resources/extensions/remote-questions/status.js +4 -1
  121. package/dist/resources/extensions/remote-questions/store.js +4 -1
  122. package/dist/resources/extensions/search-the-web/provider.js +2 -1
  123. package/dist/resources/extensions/shared/frontmatter.js +1 -1
  124. package/dist/resources/extensions/subagent/index.js +12 -3
  125. package/dist/resources/extensions/subagent/isolation.js +2 -1
  126. package/dist/resources/extensions/ttsr/rule-loader.js +2 -1
  127. package/dist/resources/extensions/universal-config/package.json +1 -1
  128. package/dist/welcome-screen.d.ts +13 -0
  129. package/dist/welcome-screen.js +97 -0
  130. package/package.json +1 -1
  131. package/packages/pi-ai/dist/utils/oauth/anthropic.js +2 -2
  132. package/packages/pi-ai/dist/utils/oauth/anthropic.js.map +1 -1
  133. package/packages/pi-ai/src/utils/oauth/anthropic.ts +2 -2
  134. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +12 -0
  135. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  136. package/packages/pi-coding-agent/dist/core/agent-session.js +107 -24
  137. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  138. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  139. package/packages/pi-coding-agent/dist/core/extensions/loader.js +205 -7
  140. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  141. package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
  142. package/packages/pi-coding-agent/dist/core/package-manager.js +8 -4
  143. package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
  144. package/packages/pi-coding-agent/dist/core/skill-tool.test.d.ts +2 -0
  145. package/packages/pi-coding-agent/dist/core/skill-tool.test.d.ts.map +1 -0
  146. package/packages/pi-coding-agent/dist/core/skill-tool.test.js +70 -0
  147. package/packages/pi-coding-agent/dist/core/skill-tool.test.js.map +1 -0
  148. package/packages/pi-coding-agent/dist/core/skills.d.ts +1 -0
  149. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  150. package/packages/pi-coding-agent/dist/core/skills.js +8 -2
  151. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  152. package/packages/pi-coding-agent/dist/index.d.ts +1 -1
  153. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  154. package/packages/pi-coding-agent/dist/index.js +1 -1
  155. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  156. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts +17 -0
  157. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -0
  158. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +244 -0
  159. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -0
  160. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.d.ts +3 -0
  161. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.d.ts.map +1 -0
  162. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js +58 -0
  163. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js.map +1 -0
  164. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts +12 -0
  165. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -0
  166. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +54 -0
  167. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -0
  168. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.d.ts +6 -0
  169. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.d.ts.map +1 -0
  170. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js +63 -0
  171. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js.map +1 -0
  172. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +38 -0
  173. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -0
  174. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js +2 -0
  175. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -0
  176. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +1 -1
  177. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  178. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +15 -457
  179. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  180. package/packages/pi-coding-agent/package.json +1 -1
  181. package/packages/pi-coding-agent/src/core/agent-session.ts +122 -23
  182. package/packages/pi-coding-agent/src/core/extensions/loader.ts +223 -7
  183. package/packages/pi-coding-agent/src/core/package-manager.ts +8 -4
  184. package/packages/pi-coding-agent/src/core/skill-tool.test.ts +89 -0
  185. package/packages/pi-coding-agent/src/core/skills.ts +11 -2
  186. package/packages/pi-coding-agent/src/index.ts +1 -0
  187. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +302 -0
  188. package/packages/pi-coding-agent/src/modes/interactive/controllers/extension-ui-controller.ts +59 -0
  189. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +68 -0
  190. package/packages/pi-coding-agent/src/modes/interactive/controllers/model-controller.ts +71 -0
  191. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +37 -0
  192. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +18 -510
  193. package/pkg/package.json +1 -1
  194. package/src/resources/extensions/async-jobs/index.ts +11 -0
  195. package/src/resources/extensions/browser-tools/index.ts +3 -0
  196. package/src/resources/extensions/browser-tools/tools/verify.ts +117 -0
  197. package/src/resources/extensions/cmux/index.ts +57 -1
  198. package/src/resources/extensions/get-secrets-from-user.ts +5 -24
  199. package/src/resources/extensions/github-sync/cli.ts +364 -0
  200. package/src/resources/extensions/github-sync/index.ts +93 -0
  201. package/src/resources/extensions/github-sync/mapping.ts +81 -0
  202. package/src/resources/extensions/github-sync/sync.ts +556 -0
  203. package/src/resources/extensions/github-sync/templates.ts +183 -0
  204. package/src/resources/extensions/github-sync/tests/cli.test.ts +20 -0
  205. package/src/resources/extensions/github-sync/tests/commit-linking.test.ts +39 -0
  206. package/src/resources/extensions/github-sync/tests/mapping.test.ts +104 -0
  207. package/src/resources/extensions/github-sync/tests/templates.test.ts +110 -0
  208. package/src/resources/extensions/github-sync/types.ts +47 -0
  209. package/src/resources/extensions/gsd/auto/session.ts +7 -25
  210. package/src/resources/extensions/gsd/auto-dashboard.ts +10 -0
  211. package/src/resources/extensions/gsd/auto-dispatch.ts +7 -9
  212. package/src/resources/extensions/gsd/auto-loop.ts +1285 -1138
  213. package/src/resources/extensions/gsd/auto-post-unit.ts +90 -46
  214. package/src/resources/extensions/gsd/auto-prompts.ts +250 -53
  215. package/src/resources/extensions/gsd/auto-start.ts +24 -3
  216. package/src/resources/extensions/gsd/auto-worktree-sync.ts +15 -4
  217. package/src/resources/extensions/gsd/auto-worktree.ts +3 -3
  218. package/src/resources/extensions/gsd/auto.ts +152 -111
  219. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +142 -0
  220. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +238 -0
  221. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +90 -0
  222. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +46 -0
  223. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +167 -0
  224. package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +55 -0
  225. package/src/resources/extensions/gsd/bootstrap/system-context.ts +340 -0
  226. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +51 -0
  227. package/src/resources/extensions/gsd/captures.ts +10 -1
  228. package/src/resources/extensions/gsd/commands/catalog.ts +301 -0
  229. package/src/resources/extensions/gsd/commands/context.ts +101 -0
  230. package/src/resources/extensions/gsd/commands/dispatcher.ts +32 -0
  231. package/src/resources/extensions/gsd/commands/handlers/auto.ts +74 -0
  232. package/src/resources/extensions/gsd/commands/handlers/core.ts +274 -0
  233. package/src/resources/extensions/gsd/commands/handlers/ops.ts +169 -0
  234. package/src/resources/extensions/gsd/commands/handlers/parallel.ts +118 -0
  235. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +109 -0
  236. package/src/resources/extensions/gsd/commands/index.ts +14 -0
  237. package/src/resources/extensions/gsd/commands-extensions.ts +4 -2
  238. package/src/resources/extensions/gsd/commands-handlers.ts +18 -3
  239. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +1 -1
  240. package/src/resources/extensions/gsd/commands.ts +10 -1307
  241. package/src/resources/extensions/gsd/context-budget.ts +2 -12
  242. package/src/resources/extensions/gsd/dashboard-overlay.ts +10 -0
  243. package/src/resources/extensions/gsd/detection.ts +2 -2
  244. package/src/resources/extensions/gsd/docs/preferences-reference.md +0 -2
  245. package/src/resources/extensions/gsd/doctor-checks.ts +75 -0
  246. package/src/resources/extensions/gsd/doctor-environment.ts +82 -1
  247. package/src/resources/extensions/gsd/doctor-format.ts +20 -0
  248. package/src/resources/extensions/gsd/doctor-proactive.ts +106 -10
  249. package/src/resources/extensions/gsd/doctor-providers.ts +30 -9
  250. package/src/resources/extensions/gsd/doctor-types.ts +16 -1
  251. package/src/resources/extensions/gsd/doctor.ts +243 -14
  252. package/src/resources/extensions/gsd/env-utils.ts +31 -0
  253. package/src/resources/extensions/gsd/exit-command.ts +2 -2
  254. package/src/resources/extensions/gsd/export-html.ts +51 -0
  255. package/src/resources/extensions/gsd/export.ts +1 -1
  256. package/src/resources/extensions/gsd/files.ts +51 -11
  257. package/src/resources/extensions/gsd/forensics.ts +1 -1
  258. package/src/resources/extensions/gsd/git-service.ts +44 -10
  259. package/src/resources/extensions/gsd/gitignore.ts +17 -3
  260. package/src/resources/extensions/gsd/guided-flow.ts +177 -44
  261. package/src/resources/extensions/gsd/health-widget-core.ts +28 -80
  262. package/src/resources/extensions/gsd/health-widget.ts +4 -89
  263. package/src/resources/extensions/gsd/index.ts +12 -1307
  264. package/src/resources/extensions/gsd/migrate/parsers.ts +1 -1
  265. package/src/resources/extensions/gsd/migrate-external.ts +18 -1
  266. package/src/resources/extensions/gsd/native-git-bridge.ts +37 -0
  267. package/src/resources/extensions/gsd/paths.ts +4 -0
  268. package/src/resources/extensions/gsd/preferences-models.ts +0 -12
  269. package/src/resources/extensions/gsd/preferences-types.ts +4 -4
  270. package/src/resources/extensions/gsd/preferences-validation.ts +51 -11
  271. package/src/resources/extensions/gsd/preferences.ts +25 -11
  272. package/src/resources/extensions/gsd/progress-score.ts +23 -0
  273. package/src/resources/extensions/gsd/prompt-loader.ts +7 -2
  274. package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  275. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  276. package/src/resources/extensions/gsd/prompts/discuss.md +11 -14
  277. package/src/resources/extensions/gsd/prompts/execute-task.md +5 -3
  278. package/src/resources/extensions/gsd/prompts/forensics.md +121 -46
  279. package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
  280. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
  281. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
  282. package/src/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
  283. package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
  284. package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
  285. package/src/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
  286. package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  287. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  288. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  289. package/src/resources/extensions/gsd/prompts/queue.md +4 -8
  290. package/src/resources/extensions/gsd/prompts/reactive-execute.md +11 -8
  291. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
  292. package/src/resources/extensions/gsd/prompts/research-milestone.md +1 -1
  293. package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
  294. package/src/resources/extensions/gsd/prompts/run-uat.md +28 -11
  295. package/src/resources/extensions/gsd/prompts/workflow-start.md +2 -2
  296. package/src/resources/extensions/gsd/repo-identity.ts +23 -4
  297. package/src/resources/extensions/gsd/resource-version.ts +3 -1
  298. package/src/resources/extensions/gsd/roadmap-mutations.ts +29 -0
  299. package/src/resources/extensions/gsd/state.ts +39 -21
  300. package/src/resources/extensions/gsd/templates/runtime.md +21 -0
  301. package/src/resources/extensions/gsd/templates/task-plan.md +3 -0
  302. package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +21 -18
  303. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +135 -77
  304. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +4 -3
  305. package/src/resources/extensions/gsd/tests/cmux.test.ts +93 -0
  306. package/src/resources/extensions/gsd/tests/derive-state.test.ts +43 -0
  307. package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +266 -0
  308. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +3 -3
  309. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +86 -3
  310. package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +50 -0
  311. package/src/resources/extensions/gsd/tests/health-widget.test.ts +16 -54
  312. package/src/resources/extensions/gsd/tests/parsers.test.ts +131 -14
  313. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +209 -0
  314. package/src/resources/extensions/gsd/tests/preferences.test.ts +2 -7
  315. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +59 -0
  316. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +16 -16
  317. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +21 -1
  318. package/src/resources/extensions/gsd/tests/run-uat.test.ts +16 -4
  319. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +140 -0
  320. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +10 -10
  321. package/src/resources/extensions/gsd/tests/worktree.test.ts +47 -0
  322. package/src/resources/extensions/gsd/types.ts +18 -1
  323. package/src/resources/extensions/gsd/verification-evidence.ts +16 -0
  324. package/src/resources/extensions/gsd/visualizer-data.ts +52 -2
  325. package/src/resources/extensions/gsd/visualizer-views.ts +58 -0
  326. package/src/resources/extensions/gsd/worktree.ts +35 -15
  327. package/src/resources/extensions/mcp-client/index.ts +17 -1
  328. package/src/resources/extensions/remote-questions/status.ts +5 -1
  329. package/src/resources/extensions/remote-questions/store.ts +5 -1
  330. package/src/resources/extensions/search-the-web/provider.ts +2 -1
  331. package/src/resources/extensions/shared/frontmatter.ts +1 -1
  332. package/src/resources/extensions/subagent/index.ts +12 -3
  333. package/src/resources/extensions/subagent/isolation.ts +3 -1
  334. package/src/resources/extensions/ttsr/rule-loader.ts +3 -1
  335. package/dist/resources/extensions/gsd/prompt-compressor.js +0 -393
  336. package/dist/resources/extensions/gsd/semantic-chunker.js +0 -254
  337. package/dist/resources/extensions/gsd/summary-distiller.js +0 -212
  338. package/src/resources/extensions/gsd/prompt-compressor.ts +0 -508
  339. package/src/resources/extensions/gsd/semantic-chunker.ts +0 -336
  340. package/src/resources/extensions/gsd/summary-distiller.ts +0 -258
  341. package/src/resources/extensions/gsd/tests/context-compression.test.ts +0 -193
  342. package/src/resources/extensions/gsd/tests/prompt-compressor.test.ts +0 -529
  343. package/src/resources/extensions/gsd/tests/semantic-chunker.test.ts +0 -426
  344. package/src/resources/extensions/gsd/tests/summary-distiller.test.ts +0 -323
  345. package/src/resources/extensions/gsd/tests/token-optimization-benchmark.test.ts +0 -1272
  346. package/src/resources/extensions/gsd/tests/token-optimization-prefs.test.ts +0 -164
@@ -10,6 +10,7 @@ import { execFileSync } from "node:child_process";
10
10
  import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, rmSync, symlinkSync } from "node:fs";
11
11
  import { homedir } from "node:os";
12
12
  import { join, resolve } from "node:path";
13
+ const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
13
14
  // ─── Repo Identity ──────────────────────────────────────────────────────────
14
15
  /**
15
16
  * Get the git remote URL for "origin", or "" if no remote is configured.
@@ -84,14 +85,30 @@ function resolveGitRoot(basePath) {
84
85
  return resolve(basePath);
85
86
  }
86
87
  }
88
+ /**
89
+ * Validate a GSD_PROJECT_ID value.
90
+ *
91
+ * Must contain only alphanumeric characters, hyphens, and underscores.
92
+ * Call this once at startup so the user gets immediate feedback on bad values.
93
+ */
94
+ export function validateProjectId(id) {
95
+ return /^[a-zA-Z0-9_-]+$/.test(id);
96
+ }
87
97
  /**
88
98
  * Compute a stable identity for a repository.
89
99
  *
90
- * SHA-256 of `${remoteUrl}\n${resolvedRoot}`, truncated to 12 hex chars.
91
- * Deterministic: same repo always produces the same hash regardless of
92
- * which worktree the caller is inside.
100
+ * If `GSD_PROJECT_ID` is set, returns it directly (validation is expected
101
+ * to have already happened at startup via `validateProjectId`).
102
+ *
103
+ * Otherwise returns SHA-256 of `${remoteUrl}\n${resolvedRoot}`, truncated
104
+ * to 12 hex chars. Deterministic: same repo always produces the same hash
105
+ * regardless of which worktree the caller is inside.
93
106
  */
94
107
  export function repoIdentity(basePath) {
108
+ const projectId = process.env.GSD_PROJECT_ID;
109
+ if (projectId) {
110
+ return projectId;
111
+ }
95
112
  const remoteUrl = getRemoteUrl(basePath);
96
113
  const root = resolveGitRoot(basePath);
97
114
  const input = `${remoteUrl}\n${root}`;
@@ -105,7 +122,7 @@ export function repoIdentity(basePath) {
105
122
  * otherwise `~/.gsd/projects/<hash>`.
106
123
  */
107
124
  export function externalGsdRoot(basePath) {
108
- const base = process.env.GSD_STATE_DIR || join(homedir(), ".gsd");
125
+ const base = process.env.GSD_STATE_DIR || gsdHome;
109
126
  return join(base, "projects", repoIdentity(basePath));
110
127
  }
111
128
  // ─── Symlink Management ─────────────────────────────────────────────────────
@@ -9,6 +9,7 @@ import { loadJsonFileOrNull } from "./json-persistence.js";
9
9
  import { join } from "node:path";
10
10
  import { homedir } from "node:os";
11
11
  import { resolveProjectRoot } from "./worktree.js";
12
+ const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
12
13
  // ─── Resource Staleness ───────────────────────────────────────────────────
13
14
  /**
14
15
  * Read the resource version (semver) from the managed-resources manifest.
@@ -19,7 +20,7 @@ function isManifestWithVersion(data) {
19
20
  return data !== null && typeof data === "object" && "gsdVersion" in data && typeof data.gsdVersion === "string";
20
21
  }
21
22
  export function readResourceVersion() {
22
- const agentDir = process.env.GSD_CODING_AGENT_DIR || join(homedir(), ".gsd", "agent");
23
+ const agentDir = process.env.GSD_CODING_AGENT_DIR || join(gsdHome, "agent");
23
24
  const manifestPath = join(agentDir, "managed-resources.json");
24
25
  const manifest = loadJsonFileOrNull(manifestPath, isManifestWithVersion);
25
26
  return manifest?.gsdVersion ?? null;
@@ -32,6 +32,30 @@ export function markSliceDoneInRoadmap(basePath, mid, sid) {
32
32
  clearParseCache();
33
33
  return true;
34
34
  }
35
+ /**
36
+ * Mark a slice as not done ([ ]) in the milestone roadmap.
37
+ * Idempotent — no-op if already unchecked or if the slice isn't found.
38
+ *
39
+ * @returns true if the roadmap was modified, false if no change was needed
40
+ */
41
+ export function markSliceUndoneInRoadmap(basePath, mid, sid) {
42
+ const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP");
43
+ if (!roadmapFile)
44
+ return false;
45
+ let content;
46
+ try {
47
+ content = readFileSync(roadmapFile, "utf-8");
48
+ }
49
+ catch {
50
+ return false;
51
+ }
52
+ const updated = content.replace(new RegExp(`^(\\s*-\\s+)\\[x\\]\\s+\\*\\*${sid}:`, "m"), `$1[ ] **${sid}:`);
53
+ if (updated === content)
54
+ return false;
55
+ atomicWriteSync(roadmapFile, updated);
56
+ clearParseCache();
57
+ return true;
58
+ }
35
59
  /**
36
60
  * Mark a task as done ([x]) in the slice plan.
37
61
  * Idempotent — no-op if already checked or if the task isn't found.
@@ -3,7 +3,7 @@
3
3
  // Pure TypeScript, zero Pi dependencies.
4
4
  import { parseRoadmap, parsePlan, parseSummary, loadFile, parseRequirementCounts, parseContextDependsOn, } from './files.js';
5
5
  import { resolveMilestoneFile, resolveSlicePath, resolveSliceFile, resolveTaskFile, resolveTasksDir, resolveGsdRootFile, gsdRoot, } from './paths.js';
6
- import { findMilestoneIds } from './guided-flow.js';
6
+ import { findMilestoneIds } from './milestone-ids.js';
7
7
  import { nativeBatchParseGsdFiles } from './native-parser-bridge.js';
8
8
  import { join, resolve } from 'path';
9
9
  import { existsSync, readdirSync } from 'node:fs';
@@ -82,8 +82,13 @@ export async function getActiveMilestoneId(basePath) {
82
82
  // A draft milestone is still "active" — this function only determines which milestone is current.
83
83
  }
84
84
  const roadmap = parseRoadmap(content);
85
- if (!isMilestoneComplete(roadmap))
86
- return mid;
85
+ if (!isMilestoneComplete(roadmap)) {
86
+ // Summary is the terminal artifact — if it exists, the milestone is
87
+ // complete even when roadmap checkboxes weren't ticked (#864).
88
+ const summaryFile = resolveMilestoneFile(basePath, mid, "SUMMARY");
89
+ if (!summaryFile)
90
+ return mid;
91
+ }
87
92
  }
88
93
  return null;
89
94
  }
@@ -202,8 +207,14 @@ async function _deriveStateImpl(basePath) {
202
207
  }
203
208
  const rmap = parseRoadmap(rc);
204
209
  roadmapCache.set(mid, rmap);
205
- if (!isMilestoneComplete(rmap))
210
+ if (!isMilestoneComplete(rmap)) {
211
+ // Summary is the terminal artifact — if it exists, the milestone is
212
+ // complete even when roadmap checkboxes weren't ticked (#864).
213
+ const sf = resolveMilestoneFile(basePath, mid, "SUMMARY");
214
+ if (sf)
215
+ completeMilestoneIds.add(mid);
206
216
  continue;
217
+ }
207
218
  const sf = resolveMilestoneFile(basePath, mid, "SUMMARY");
208
219
  if (sf)
209
220
  completeMilestoneIds.add(mid);
@@ -303,29 +314,37 @@ async function _deriveStateImpl(basePath) {
303
314
  registry.push({ id: mid, title, status: 'complete' });
304
315
  }
305
316
  }
306
- else if (!activeMilestoneFound) {
307
- // Check milestone-level dependencies before promoting to active
308
- const contextFile = resolveMilestoneFile(basePath, mid, "CONTEXT");
309
- const contextContent = contextFile ? await cachedLoadFile(contextFile) : null;
310
- const deps = parseContextDependsOn(contextContent);
311
- const depsUnmet = deps.some(dep => !completeMilestoneIds.has(dep));
312
- if (depsUnmet) {
313
- registry.push({ id: mid, title, status: 'pending', dependsOn: deps });
314
- // Do NOT set activeMilestoneFound let the loop continue to the next milestone
317
+ else {
318
+ // Roadmap slices not all checked but if a summary exists, the milestone
319
+ // is still complete. The summary is the terminal artifact (#864).
320
+ const summaryFile = resolveMilestoneFile(basePath, mid, "SUMMARY");
321
+ if (summaryFile) {
322
+ registry.push({ id: mid, title, status: 'complete' });
323
+ }
324
+ else if (!activeMilestoneFound) {
325
+ // Check milestone-level dependencies before promoting to active
326
+ const contextFile = resolveMilestoneFile(basePath, mid, "CONTEXT");
327
+ const contextContent = contextFile ? await cachedLoadFile(contextFile) : null;
328
+ const deps = parseContextDependsOn(contextContent);
329
+ const depsUnmet = deps.some(dep => !completeMilestoneIds.has(dep));
330
+ if (depsUnmet) {
331
+ registry.push({ id: mid, title, status: 'pending', dependsOn: deps });
332
+ // Do NOT set activeMilestoneFound — let the loop continue to the next milestone
333
+ }
334
+ else {
335
+ activeMilestone = { id: mid, title };
336
+ activeRoadmap = roadmap;
337
+ activeMilestoneFound = true;
338
+ registry.push({ id: mid, title, status: 'active', ...(deps.length > 0 ? { dependsOn: deps } : {}) });
339
+ }
315
340
  }
316
341
  else {
317
- activeMilestone = { id: mid, title };
318
- activeRoadmap = roadmap;
319
- activeMilestoneFound = true;
320
- registry.push({ id: mid, title, status: 'active', ...(deps.length > 0 ? { dependsOn: deps } : {}) });
342
+ const contextFile2 = resolveMilestoneFile(basePath, mid, "CONTEXT");
343
+ const contextContent2 = contextFile2 ? await cachedLoadFile(contextFile2) : null;
344
+ const deps2 = parseContextDependsOn(contextContent2);
345
+ registry.push({ id: mid, title, status: 'pending', ...(deps2.length > 0 ? { dependsOn: deps2 } : {}) });
321
346
  }
322
347
  }
323
- else {
324
- const contextFile2 = resolveMilestoneFile(basePath, mid, "CONTEXT");
325
- const contextContent2 = contextFile2 ? await cachedLoadFile(contextFile2) : null;
326
- const deps2 = parseContextDependsOn(contextContent2);
327
- registry.push({ id: mid, title, status: 'pending', ...(deps2.length > 0 ? { dependsOn: deps2 } : {}) });
328
- }
329
348
  }
330
349
  const milestoneProgress = {
331
350
  done: registry.filter(entry => entry.status === 'complete').length,
@@ -0,0 +1,21 @@
1
+ # Runtime Context
2
+
3
+ ## Stack
4
+ - **Language:** (e.g., TypeScript, Python, Go)
5
+ - **Framework:** (e.g., Next.js, FastAPI, Gin)
6
+ - **Build:** (e.g., npm run build, cargo build)
7
+ - **Test:** (e.g., npm run test, pytest)
8
+ - **Lint:** (e.g., npm run lint, ruff check)
9
+
10
+ ## Environment
11
+ - **Node version:** (e.g., 20.x)
12
+ - **Package manager:** (e.g., npm, pnpm, yarn)
13
+ - **Required env vars:** (list any needed for local dev)
14
+
15
+ ## Dev Server
16
+ - **Start command:** (e.g., npm run dev)
17
+ - **Default port:** (e.g., 3000)
18
+ - **Health check:** (e.g., curl http://localhost:3000/health)
19
+
20
+ ## Notes
21
+ (Any runtime-specific context the executor needs to know)
@@ -3,6 +3,9 @@
3
3
  # Tasks with 10+ estimated steps or 12+ estimated files trigger a warning to consider splitting.
4
4
  estimated_steps: {{estimatedSteps}}
5
5
  estimated_files: {{estimatedFiles}}
6
+ # Installed skills the planner expects the executor to load before coding.
7
+ skills_used:
8
+ - {{skillName}}
6
9
  ---
7
10
 
8
11
  # {{taskId}}: {{taskTitle}}
@@ -1,15 +1,18 @@
1
1
  // Data loader for workflow visualizer overlay — aggregates state + metrics.
2
2
  import { existsSync, readFileSync, statSync } from 'node:fs';
3
+ import { join } from 'node:path';
3
4
  import { deriveState } from './state.js';
4
5
  import { parseRoadmap, parsePlan, parseSummary, loadFile } from './files.js';
5
- import { findMilestoneIds } from './guided-flow.js';
6
- import { resolveMilestoneFile, resolveSliceFile, resolveGsdRootFile } from './paths.js';
6
+ import { findMilestoneIds } from './milestone-ids.js';
7
+ import { resolveMilestoneFile, resolveSliceFile, resolveGsdRootFile, gsdRoot } from './paths.js';
7
8
  import { getLedger, getProjectTotals, aggregateByPhase, aggregateBySlice, aggregateByModel, aggregateByTier, formatTierSavings, loadLedgerFromDisk, } from './metrics.js';
8
9
  import { loadAllCaptures, countPendingCaptures } from './captures.js';
9
10
  import { loadEffectiveGSDPreferences } from './preferences.js';
10
11
  import { runProviderChecks } from './doctor-providers.js';
11
12
  import { generateSkillHealthReport } from './skill-health.js';
12
13
  import { runEnvironmentChecks } from './doctor-environment.js';
14
+ import { computeProgressScore } from './progress-score.js';
15
+ import { getHealthHistory } from './doctor-proactive.js';
13
16
  // ─── Critical Path ────────────────────────────────────────────────────────────
14
17
  export function computeCriticalPath(milestones) {
15
18
  const empty = {
@@ -392,6 +395,26 @@ function loadHealth(units, totals, basePath) {
392
395
  environmentIssues = runEnvironmentChecks(basePath).filter(r => r.status !== "ok");
393
396
  }
394
397
  catch { /* non-fatal */ }
398
+ // Doctor run history — persisted across sessions (sync read to keep loadHealth sync)
399
+ let doctorHistory = [];
400
+ try {
401
+ const historyPath = join(gsdRoot(basePath), "doctor-history.jsonl");
402
+ if (existsSync(historyPath)) {
403
+ const lines = readFileSync(historyPath, "utf-8").split("\n").filter(l => l.trim());
404
+ doctorHistory = lines.slice(-20).reverse().map(l => JSON.parse(l));
405
+ }
406
+ }
407
+ catch { /* non-fatal */ }
408
+ // Current progress score — only meaningful when auto-mode has health data
409
+ let progressScore = null;
410
+ try {
411
+ const history = getHealthHistory();
412
+ if (history.length > 0) {
413
+ const score = computeProgressScore();
414
+ progressScore = { level: score.level, summary: score.summary, signals: score.signals };
415
+ }
416
+ }
417
+ catch { /* non-fatal */ }
395
418
  return {
396
419
  budgetCeiling,
397
420
  tokenProfile,
@@ -405,6 +428,8 @@ function loadHealth(units, totals, basePath) {
405
428
  providers,
406
429
  skillSummary,
407
430
  environmentIssues,
431
+ doctorHistory,
432
+ progressScore,
408
433
  };
409
434
  }
410
435
  const RECENT_ENTRY_LIMIT = 3;
@@ -872,6 +872,58 @@ export function renderHealthView(data, th, width) {
872
872
  }
873
873
  }
874
874
  }
875
+ // Progress score section — current traffic light status
876
+ if (health.progressScore) {
877
+ lines.push("");
878
+ lines.push(th.fg("accent", th.bold("Progress Score")));
879
+ lines.push("");
880
+ const ps = health.progressScore;
881
+ const scoreColor = ps.level === "green" ? "success" : ps.level === "yellow" ? "warning" : "error";
882
+ const scoreIcon = ps.level === "green" ? "●" : ps.level === "yellow" ? "◐" : "○";
883
+ lines.push(` ${th.fg(scoreColor, scoreIcon)} ${th.fg(scoreColor, ps.summary)}`);
884
+ for (const signal of ps.signals) {
885
+ const prefix = signal.kind === "positive" ? th.fg("success", " ✓")
886
+ : signal.kind === "negative" ? th.fg("error", " ✗")
887
+ : th.fg("dim", " ·");
888
+ lines.push(` ${prefix} ${th.fg("dim", signal.label)}`);
889
+ }
890
+ }
891
+ // Doctor history section — persisted across sessions
892
+ const doctorHistory = health.doctorHistory ?? [];
893
+ if (doctorHistory.length > 0) {
894
+ lines.push("");
895
+ lines.push(th.fg("accent", th.bold("Doctor History")));
896
+ lines.push("");
897
+ for (const entry of doctorHistory.slice(0, 10)) {
898
+ const icon = entry.ok ? th.fg("success", "✓") : th.fg("error", "✗");
899
+ const ts = entry.ts.replace("T", " ").slice(0, 19);
900
+ const scopeTag = entry.scope ? th.fg("accent", ` [${entry.scope}]`) : "";
901
+ // Prefer human-readable summary, fall back to counts
902
+ const detail = entry.summary
903
+ ? th.fg("text", entry.summary)
904
+ : th.fg("text", `${entry.errors} errors, ${entry.warnings} warnings, ${entry.fixes} fixes`);
905
+ lines.push(` ${icon} ${th.fg("dim", ts)}${scopeTag} ${detail}`);
906
+ // Show issue details if available
907
+ if (entry.issues && entry.issues.length > 0) {
908
+ for (const issue of entry.issues.slice(0, 3)) {
909
+ const issuePfx = issue.severity === "error" ? th.fg("error", " ✗") : th.fg("warning", " ⚠");
910
+ lines.push(` ${issuePfx} ${th.fg("dim", truncateToWidth(issue.message, width - 12))}`);
911
+ }
912
+ if (entry.issues.length > 3) {
913
+ lines.push(` ${th.fg("dim", `+${entry.issues.length - 3} more`)}`);
914
+ }
915
+ }
916
+ // Show fixes if available
917
+ if (entry.fixDescriptions && entry.fixDescriptions.length > 0) {
918
+ for (const fix of entry.fixDescriptions.slice(0, 2)) {
919
+ lines.push(` ${th.fg("success", "↳")} ${th.fg("dim", truncateToWidth(fix, width - 12))}`);
920
+ }
921
+ }
922
+ }
923
+ if (doctorHistory.length > 10) {
924
+ lines.push(` ${th.fg("dim", `...${doctorHistory.length - 10} older entries`)}`);
925
+ }
926
+ }
875
927
  // Skills section
876
928
  if (health.skillSummary?.total > 0) {
877
929
  lines.push("");
@@ -57,42 +57,61 @@ export function captureIntegrationBranch(basePath, milestoneId, options) {
57
57
  writeIntegrationBranch(basePath, milestoneId, current, options);
58
58
  }
59
59
  // ─── Pure Utility Functions (unchanged) ────────────────────────────────────
60
+ /**
61
+ * Find the worktrees segment in a path, supporting both direct
62
+ * (`/.gsd/worktrees/`) and symlink-resolved (`/.gsd/projects/<hash>/worktrees/`)
63
+ * layouts. When `.gsd` is a symlink to `~/.gsd/projects/<hash>`, resolved
64
+ * paths contain the intermediate `projects/<hash>/` segment that the old
65
+ * single-marker check missed.
66
+ */
67
+ function findWorktreeSegment(normalizedPath) {
68
+ // Direct layout: /.gsd/worktrees/<name>
69
+ const directMarker = "/.gsd/worktrees/";
70
+ const idx = normalizedPath.indexOf(directMarker);
71
+ if (idx !== -1) {
72
+ return { gsdIdx: idx, afterWorktrees: idx + directMarker.length };
73
+ }
74
+ // Symlink-resolved layout: /.gsd/projects/<hash>/worktrees/<name>
75
+ const symlinkRe = /\/\.gsd\/projects\/[a-f0-9]+\/worktrees\//;
76
+ const match = normalizedPath.match(symlinkRe);
77
+ if (match && match.index !== undefined) {
78
+ return { gsdIdx: match.index, afterWorktrees: match.index + match[0].length };
79
+ }
80
+ return null;
81
+ }
60
82
  /**
61
83
  * Detect the active worktree name from the current working directory.
62
84
  * Returns null if not inside a GSD worktree (.gsd/worktrees/<name>/).
63
85
  */
64
86
  export function detectWorktreeName(basePath) {
65
87
  const normalizedPath = basePath.replaceAll("\\", "/");
66
- const marker = "/.gsd/worktrees/";
67
- const idx = normalizedPath.indexOf(marker);
68
- if (idx === -1)
88
+ const seg = findWorktreeSegment(normalizedPath);
89
+ if (!seg)
69
90
  return null;
70
- const afterMarker = normalizedPath.slice(idx + marker.length);
91
+ const afterMarker = normalizedPath.slice(seg.afterWorktrees);
71
92
  const name = afterMarker.split("/")[0];
72
93
  return name || null;
73
94
  }
74
95
  /**
75
96
  * Resolve the project root from a path that may be inside a worktree.
76
- * If the path contains `/.gsd/worktrees/<name>/`, returns the portion
77
- * before `/.gsd/`. Otherwise returns the input unchanged.
97
+ * If the path contains a worktrees segment, returns the portion before
98
+ * `/.gsd/`. Otherwise returns the input unchanged.
78
99
  *
79
100
  * Use this in commands that call `process.cwd()` to ensure they always
80
101
  * operate against the real project root, not a worktree subdirectory.
81
102
  */
82
103
  export function resolveProjectRoot(basePath) {
83
104
  const normalizedPath = basePath.replaceAll("\\", "/");
84
- const marker = "/.gsd/worktrees/";
85
- const idx = normalizedPath.indexOf(marker);
86
- if (idx === -1)
105
+ const seg = findWorktreeSegment(normalizedPath);
106
+ if (!seg)
87
107
  return basePath;
88
- // Return the original path up to the .gsd/ marker (un-normalized)
89
- // Account for potential OS-specific separators
108
+ // Return the original path up to the /.gsd/ boundary
90
109
  const sep = basePath.includes("\\") ? "\\" : "/";
91
- const markerOs = `${sep}.gsd${sep}worktrees${sep}`;
92
- const idxOs = basePath.indexOf(markerOs);
93
- if (idxOs !== -1)
94
- return basePath.slice(0, idxOs);
95
- return basePath.slice(0, idx);
110
+ const gsdMarker = `${sep}.gsd${sep}`;
111
+ const gsdIdx = basePath.indexOf(gsdMarker);
112
+ if (gsdIdx !== -1)
113
+ return basePath.slice(0, gsdIdx);
114
+ return basePath.slice(0, seg.gsdIdx);
96
115
  }
97
116
  /**
98
117
  * Get the slice branch name, namespaced by worktree when inside one.
@@ -76,6 +76,19 @@ function readConfigs() {
76
76
  function getServerConfig(name) {
77
77
  return readConfigs().find((s) => s.name === name);
78
78
  }
79
+ /** Resolve ${VAR} references in env values against process.env. */
80
+ function resolveEnv(env) {
81
+ const resolved = {};
82
+ for (const [key, value] of Object.entries(env)) {
83
+ if (typeof value === "string") {
84
+ resolved[key] = value.replace(/\$\{([^}]+)\}/g, (_match, varName) => process.env[varName] ?? "");
85
+ }
86
+ else {
87
+ resolved[key] = value;
88
+ }
89
+ }
90
+ return resolved;
91
+ }
79
92
  async function getOrConnect(name, signal) {
80
93
  const existing = connections.get(name);
81
94
  if (existing)
@@ -89,7 +102,7 @@ async function getOrConnect(name, signal) {
89
102
  transport = new StdioClientTransport({
90
103
  command: config.command,
91
104
  args: config.args,
92
- env: config.env ? { ...process.env, ...config.env } : undefined,
105
+ env: config.env ? { ...process.env, ...resolveEnv(config.env) } : undefined,
93
106
  cwd: config.cwd,
94
107
  stderr: "pipe",
95
108
  });
@@ -5,8 +5,11 @@ import { existsSync, readdirSync } from "node:fs";
5
5
  import { join } from "node:path";
6
6
  import { homedir } from "node:os";
7
7
  import { readPromptRecord } from "./store.js";
8
+ function getGsdHome() {
9
+ return process.env.GSD_HOME || join(homedir(), ".gsd");
10
+ }
8
11
  export function getLatestPromptSummary() {
9
- const runtimeDir = join(homedir(), ".gsd", "runtime", "remote-questions");
12
+ const runtimeDir = join(getGsdHome(), "runtime", "remote-questions");
10
13
  if (!existsSync(runtimeDir))
11
14
  return null;
12
15
  const files = readdirSync(runtimeDir).filter((f) => f.endsWith(".json"));
@@ -4,8 +4,11 @@
4
4
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
5
5
  import { join } from "node:path";
6
6
  import { homedir } from "node:os";
7
+ function getGsdHome() {
8
+ return process.env.GSD_HOME || join(homedir(), ".gsd");
9
+ }
7
10
  function runtimeDir() {
8
- return join(homedir(), ".gsd", "runtime", "remote-questions");
11
+ return join(getGsdHome(), "runtime", "remote-questions");
9
12
  }
10
13
  function recordPath(id) {
11
14
  return join(runtimeDir(), `${id}.json`);
@@ -15,7 +15,8 @@ import { resolveSearchProviderFromPreferences } from '../gsd/preferences.js';
15
15
  // Compute authFilePath locally instead of importing from app-paths.ts,
16
16
  // because extensions are copied to ~/.gsd/agent/extensions/ at runtime
17
17
  // where the relative import '../../../app-paths.ts' doesn't resolve.
18
- const authFilePath = join(homedir(), '.gsd', 'agent', 'auth.json');
18
+ const gsdHome = process.env.GSD_HOME || join(homedir(), '.gsd');
19
+ const authFilePath = join(gsdHome, 'agent', 'auth.json');
19
20
  const VALID_PREFERENCES = new Set(['tavily', 'brave', 'ollama', 'auto']);
20
21
  const PREFERENCE_KEY = 'search_provider';
21
22
  /** Returns the Tavily API key from the environment, or empty string if not set. */
@@ -45,7 +45,7 @@ export function parseFrontmatterMap(lines) {
45
45
  continue;
46
46
  }
47
47
  // Array item (2-space indent)
48
- const arrayMatch = line.match(/^ - (.*)$/);
48
+ const arrayMatch = line.match(/^ - ?(.*)$/);
49
49
  if (arrayMatch && currentKey) {
50
50
  // If there's a pending nested object, push it
51
51
  if (currentObj && Object.keys(currentObj).length > 0) {
@@ -360,7 +360,7 @@ async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, si
360
360
  }
361
361
  }
362
362
  }
363
- async function runSingleAgentInCmuxSplit(cmuxClient, direction, defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails) {
363
+ async function runSingleAgentInCmuxSplit(cmuxClient, directionOrSurfaceId, defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails) {
364
364
  const agent = agents.find((a) => a.name === agentName);
365
365
  if (!agent) {
366
366
  return runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails);
@@ -397,7 +397,12 @@ async function runSingleAgentInCmuxSplit(cmuxClient, direction, defaultCwd, agen
397
397
  const stdoutPath = path.join(tmpOutputDir, "stdout.jsonl");
398
398
  const stderrPath = path.join(tmpOutputDir, "stderr.log");
399
399
  const exitPath = path.join(tmpOutputDir, "exit.code");
400
- const cmuxSurfaceId = await cmuxClient.createSplit(direction);
400
+ // Accept either a pre-created surface ID or a direction to create a new split
401
+ const isDirection = directionOrSurfaceId === "right" || directionOrSurfaceId === "down"
402
+ || directionOrSurfaceId === "left" || directionOrSurfaceId === "up";
403
+ const cmuxSurfaceId = isDirection
404
+ ? await cmuxClient.createSplit(directionOrSurfaceId)
405
+ : directionOrSurfaceId;
401
406
  if (!cmuxSurfaceId) {
402
407
  return runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails);
403
408
  }
@@ -656,10 +661,14 @@ export default function (pi) {
656
661
  const MAX_RETRIES = 1; // Retry failed tasks once
657
662
  const batchId = crypto.randomUUID();
658
663
  const batchSize = params.tasks.length;
664
+ // Pre-create a grid layout for cmux splits so agents get a clean tiled arrangement
665
+ const gridSurfaces = cmuxSplitsEnabled
666
+ ? await cmuxClient.createGridLayout(Math.min(batchSize, MAX_CONCURRENCY))
667
+ : [];
659
668
  const results = await mapWithConcurrencyLimit(params.tasks, MAX_CONCURRENCY, async (t, index) => {
660
669
  const workerId = registerWorker(t.agent, t.task, index, batchSize, batchId);
661
670
  const runTask = () => cmuxSplitsEnabled
662
- ? runSingleAgentInCmuxSplit(cmuxClient, index % 2 === 0 ? "right" : "down", ctx.cwd, agents, t.agent, t.task, t.cwd, undefined, signal, (partial) => {
671
+ ? runSingleAgentInCmuxSplit(cmuxClient, gridSurfaces[index] ?? (index % 2 === 0 ? "right" : "down"), ctx.cwd, agents, t.agent, t.task, t.cwd, undefined, signal, (partial) => {
663
672
  if (partial.details?.results[0]) {
664
673
  allResults[index] = partial.details.results[0];
665
674
  emitParallelUpdate();
@@ -17,8 +17,9 @@ const execFile = promisify(execFileCb);
17
17
  function encodeCwd(cwd) {
18
18
  return cwd.replace(/\//g, "--");
19
19
  }
20
+ const gsdHome = process.env.GSD_HOME || path.join(os.homedir(), ".gsd");
20
21
  function getIsolationBaseDir(cwd, taskId) {
21
- return path.join(os.homedir(), ".gsd", "wt", encodeCwd(cwd), taskId);
22
+ return path.join(gsdHome, "wt", encodeCwd(cwd), taskId);
22
23
  }
23
24
  // Track active isolation dirs for cleanup on exit
24
25
  const activeIsolations = new Set();
@@ -8,6 +8,7 @@
8
8
  import { readdirSync, readFileSync, existsSync } from "node:fs";
9
9
  import { join, basename } from "node:path";
10
10
  import { homedir } from "node:os";
11
+ const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
11
12
  import { splitFrontmatter, parseFrontmatterMap } from "../shared/frontmatter.js";
12
13
  function parseRuleFile(filePath) {
13
14
  let content;
@@ -56,7 +57,7 @@ function scanDir(dir) {
56
57
  * Project rules override global rules with the same name.
57
58
  */
58
59
  export function loadRules(cwd) {
59
- const globalDir = join(homedir(), ".gsd", "agent", "rules");
60
+ const globalDir = join(gsdHome, "agent", "rules");
60
61
  const projectDir = join(cwd, ".gsd", "rules");
61
62
  const globalRules = scanDir(globalDir);
62
63
  const projectRules = scanDir(projectDir);
@@ -5,7 +5,7 @@
5
5
  "type": "module",
6
6
  "pi": {
7
7
  "extensions": [
8
- "./index.ts"
8
+ "./index.js"
9
9
  ]
10
10
  }
11
11
  }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * GSD Welcome Screen
3
+ *
4
+ * Two-panel bar layout: full-width accent bars at top/bottom (matching the
5
+ * auto-mode progress widget style), logo left (fixed width), info right.
6
+ * Falls back to simple text on narrow terminals (<70 cols) or non-TTY.
7
+ */
8
+ export interface WelcomeScreenOptions {
9
+ version: string;
10
+ modelName?: string;
11
+ provider?: string;
12
+ }
13
+ export declare function printWelcomeScreen(opts: WelcomeScreenOptions): void;