gsd-pi 2.38.0 → 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
package/README.md CHANGED
@@ -24,21 +24,25 @@ One command. Walk away. Come back to a built project with clean git history.
24
24
 
25
25
  ---
26
26
 
27
- ## What's New in v2.37
27
+ ## What's New in v2.38
28
28
 
29
- - **cmux integration** — sidebar status, progress bars, and notifications for [cmux](https://cmux.com) terminal multiplexer users
30
- - **Redesigned dashboard** — two-column layout with redesigned widget
31
- - **Search budget enforcement** — session-level search budget prevents unbounded native web search
29
+ - **Reactive task execution (ADR-004)** — graph-derived parallel task dispatch within slices. When enabled, GSD derives a dependency graph from IO annotations in task plans and dispatches multiple non-conflicting tasks in parallel via subagents. Backward compatible — disabled by default. Enable with `reactive_execution: true` in preferences.
30
+ - **Anthropic Vertex AI provider** — run Claude models (Opus 4.6, Sonnet 4.6, Haiku 4.5) through Google Vertex AI. Set `ANTHROPIC_VERTEX_PROJECT_ID` to activate.
31
+ - **CI optimization** — GitHub Actions minutes reduced ~60-70% (~10k ~3-4k/month)
32
+ - **Reactive batch verification** — dependency-based carry-forward for verification results across parallel task batches
33
+ - **Backtick file path enforcement** — task plan IO sections now require backtick-wrapped paths for reliable parsing
34
+
35
+ See the full [Changelog](./CHANGELOG.md) for details.
36
+
37
+ ### Previous highlights (v2.34–v2.37)
38
+
39
+ - **cmux integration** — sidebar status, progress bars, and notifications for cmux terminal multiplexer users
40
+ - **Redesigned dashboard** — two-column layout with 4 widget modes (full → small → min → off)
32
41
  - **AGENTS.md support** — deprecated `agent-instructions.md` in favor of standard `AGENTS.md` / `CLAUDE.md`
33
42
  - **AI-powered triage** — automated issue and PR triage via Claude Haiku
34
- - **Auto-generated OpenRouter registry** — model registry built from OpenRouter API for always-current model support
35
- - **Extension manifest system** — user-managed enable/disable for bundled extensions
36
- - **Pipeline simplification (ADR-003)** — merged research into planning, mechanical completion
37
- - **Workflow templates** — right-sized workflows for every task type
38
- - **Health widget** — always-on environment health checks with progress scoring
43
+ - **Auto-generated OpenRouter registry** — model registry built from OpenRouter API
39
44
  - **`/gsd changelog`** — LLM-summarized release notes for any version
40
-
41
- See the full [Changelog](./CHANGELOG.md) for details.
45
+ - **Search budget enforcement** — session-level cap prevents unbounded web search
42
46
 
43
47
  ---
44
48
 
package/dist/app-paths.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { homedir } from 'os';
2
2
  import { join } from 'path';
3
- export const appRoot = join(homedir(), '.gsd');
3
+ export const appRoot = process.env.GSD_HOME || join(homedir(), '.gsd');
4
4
  export const agentDir = join(appRoot, 'agent');
5
5
  export const sessionsDir = join(appRoot, 'sessions');
6
6
  export const authFilePath = join(agentDir, 'auth.json');
package/dist/cli.js CHANGED
@@ -505,6 +505,15 @@ if (enabledModelPatterns && enabledModelPatterns.length > 0) {
505
505
  session.setScopedModels(scopedModels);
506
506
  }
507
507
  }
508
+ // Welcome screen — shown on every fresh interactive session before TUI takes over
509
+ {
510
+ const { printWelcomeScreen } = await import('./welcome-screen.js');
511
+ printWelcomeScreen({
512
+ version: process.env.GSD_VERSION || '0.0.0',
513
+ modelName: settingsManager.getDefaultModel() || undefined,
514
+ provider: settingsManager.getDefaultProvider() || undefined,
515
+ });
516
+ }
508
517
  const interactiveMode = new InteractiveMode(session);
509
518
  markStartup('InteractiveMode');
510
519
  printStartupTimings();
@@ -1,9 +1,11 @@
1
1
  /**
2
2
  * Resolves the entry-point file(s) for a single extension directory.
3
3
  *
4
- * 1. If the directory contains a package.json with a `pi.extensions` array,
5
- * each entry is resolved relative to the directory and returned (if it exists).
6
- * 2. Otherwise falls back to `index.ts` → `index.js`.
4
+ * 1. If the directory contains a package.json with a `pi` manifest object,
5
+ * the manifest is authoritative:
6
+ * - `pi.extensions` array resolve each entry relative to the directory.
7
+ * - `pi: {}` (no extensions) → return empty (library opt-out, e.g. cmux).
8
+ * 2. Only when no `pi` manifest exists does it fall back to `index.ts` → `index.js`.
7
9
  */
8
10
  export declare function resolveExtensionEntries(dir: string): string[];
9
11
  /**
@@ -6,24 +6,29 @@ function isExtensionFile(name) {
6
6
  /**
7
7
  * Resolves the entry-point file(s) for a single extension directory.
8
8
  *
9
- * 1. If the directory contains a package.json with a `pi.extensions` array,
10
- * each entry is resolved relative to the directory and returned (if it exists).
11
- * 2. Otherwise falls back to `index.ts` → `index.js`.
9
+ * 1. If the directory contains a package.json with a `pi` manifest object,
10
+ * the manifest is authoritative:
11
+ * - `pi.extensions` array resolve each entry relative to the directory.
12
+ * - `pi: {}` (no extensions) → return empty (library opt-out, e.g. cmux).
13
+ * 2. Only when no `pi` manifest exists does it fall back to `index.ts` → `index.js`.
12
14
  */
13
15
  export function resolveExtensionEntries(dir) {
14
16
  const packageJsonPath = join(dir, 'package.json');
15
17
  if (existsSync(packageJsonPath)) {
16
18
  try {
17
19
  const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
18
- const declared = pkg?.pi?.extensions;
19
- if (Array.isArray(declared)) {
20
- const resolved = declared
20
+ if (pkg?.pi && typeof pkg.pi === 'object') {
21
+ // When a pi manifest exists, it is authoritative — don't fall through
22
+ // to index.ts/index.js auto-detection. This allows library directories
23
+ // (like cmux) to opt out by declaring "pi": {} with no extensions.
24
+ const declared = pkg.pi.extensions;
25
+ if (!Array.isArray(declared) || declared.length === 0) {
26
+ return [];
27
+ }
28
+ return declared
21
29
  .filter((entry) => typeof entry === 'string')
22
30
  .map((entry) => resolve(dir, entry))
23
31
  .filter((entry) => existsSync(entry));
24
- if (resolved.length > 0) {
25
- return resolved;
26
- }
27
32
  }
28
33
  }
29
34
  catch {
@@ -6,7 +6,7 @@
6
6
  * The only way an extension stops loading is an explicit `gsd extensions disable <id>`.
7
7
  */
8
8
  import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, writeFileSync } from "node:fs";
9
- import { homedir } from "node:os";
9
+ import { appRoot } from "./app-paths.js";
10
10
  import { dirname, join } from "node:path";
11
11
  // ─── Validation ─────────────────────────────────────────────────────────────
12
12
  function isRegistry(data) {
@@ -26,7 +26,7 @@ function isManifest(data) {
26
26
  }
27
27
  // ─── Registry Path ──────────────────────────────────────────────────────────
28
28
  export function getRegistryPath() {
29
- return join(homedir(), ".gsd", "extensions", "registry.json");
29
+ return join(appRoot, "extensions", "registry.json");
30
30
  }
31
31
  // ─── Registry I/O ───────────────────────────────────────────────────────────
32
32
  function defaultRegistry() {
@@ -9,12 +9,12 @@
9
9
  */
10
10
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
11
11
  import { dirname, join } from "node:path";
12
- import { homedir } from "node:os";
12
+ import { appRoot } from "./app-paths.js";
13
13
  // Inlined from preferences.ts to avoid crossing the compiled/uncompiled
14
14
  // boundary — this file is compiled by tsc, but preferences.ts is loaded
15
15
  // via jiti at runtime. Importing it as .js fails because no .js exists
16
16
  // in dist/. See #592, #1110.
17
- const GLOBAL_PREFERENCES_PATH = join(homedir(), ".gsd", "preferences.md");
17
+ const GLOBAL_PREFERENCES_PATH = join(appRoot, "preferences.md");
18
18
  export function saveRemoteQuestionsConfig(channel, channelId) {
19
19
  const prefsPath = GLOBAL_PREFERENCES_PATH;
20
20
  const block = [
@@ -1,7 +1,7 @@
1
1
  import { DefaultResourceLoader } from '@gsd/pi-coding-agent';
2
2
  import { createHash } from 'node:crypto';
3
3
  import { homedir } from 'node:os';
4
- import { chmodSync, copyFileSync, cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from 'node:fs';
4
+ import { chmodSync, copyFileSync, cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readlinkSync, readdirSync, rmSync, statSync, symlinkSync, unlinkSync, writeFileSync } from 'node:fs';
5
5
  import { dirname, join, relative, resolve } from 'node:path';
6
6
  import { fileURLToPath } from 'node:url';
7
7
  import { compareSemver } from './update-check.js';
@@ -48,10 +48,22 @@ function getBundledGsdVersion() {
48
48
  }
49
49
  }
50
50
  function writeManagedResourceManifest(agentDir) {
51
+ // Record root-level files currently in the bundled extensions source so that
52
+ // future upgrades can detect and prune any that get removed or moved.
53
+ let installedExtensionRootFiles = [];
54
+ try {
55
+ if (existsSync(bundledExtensionsDir)) {
56
+ installedExtensionRootFiles = readdirSync(bundledExtensionsDir, { withFileTypes: true })
57
+ .filter(e => e.isFile())
58
+ .map(e => e.name);
59
+ }
60
+ }
61
+ catch { /* non-fatal */ }
51
62
  const manifest = {
52
63
  gsdVersion: getBundledGsdVersion(),
53
64
  syncedAt: Date.now(),
54
65
  contentHash: computeResourceFingerprint(),
66
+ installedExtensionRootFiles,
55
67
  };
56
68
  writeFileSync(getManagedResourceManifestPath(agentDir), JSON.stringify(manifest));
57
69
  }
@@ -217,6 +229,82 @@ function copyDirRecursive(src, dest) {
217
229
  }
218
230
  }
219
231
  }
232
+ /**
233
+ * Creates (or updates) a symlink at agentDir/node_modules pointing to GSD's
234
+ * own node_modules directory.
235
+ *
236
+ * Native ESM `import()` ignores NODE_PATH — it resolves packages by walking
237
+ * up the directory tree from the importing file. Extension files synced to
238
+ * ~/.gsd/agent/extensions/ have no ancestor node_modules, so imports of
239
+ * @gsd/* packages fail. The symlink makes Node's standard resolution find
240
+ * them without requiring every call site to use jiti.
241
+ */
242
+ function ensureNodeModulesSymlink(agentDir) {
243
+ const agentNodeModules = join(agentDir, 'node_modules');
244
+ const gsdNodeModules = join(packageRoot, 'node_modules');
245
+ try {
246
+ const existing = readlinkSync(agentNodeModules);
247
+ if (existing === gsdNodeModules)
248
+ return; // already correct
249
+ unlinkSync(agentNodeModules);
250
+ }
251
+ catch {
252
+ // readlinkSync throws if path doesn't exist or isn't a symlink — both are fine
253
+ }
254
+ try {
255
+ symlinkSync(gsdNodeModules, agentNodeModules, 'junction');
256
+ }
257
+ catch {
258
+ // Non-fatal — worst case, extensions fall back to NODE_PATH via jiti
259
+ }
260
+ }
261
+ /**
262
+ * Prune root-level extension files that were installed by a previous GSD version
263
+ * but have since been removed or relocated to a subdirectory.
264
+ *
265
+ * Two strategies:
266
+ * 1. Manifest-based (preferred): the manifest records which root files were installed
267
+ * last time; any that are no longer in the current bundle are deleted.
268
+ * 2. Known-stale fallback: for upgrades from versions before manifest tracking,
269
+ * explicitly delete files known to have been moved (e.g. env-utils.js → gsd/).
270
+ */
271
+ function pruneRemovedBundledExtensions(manifest, agentDir) {
272
+ const extensionsDir = join(agentDir, 'extensions');
273
+ if (!existsSync(extensionsDir))
274
+ return;
275
+ // Current bundled root-level files (what the new version provides)
276
+ const currentSourceFiles = new Set();
277
+ try {
278
+ if (existsSync(bundledExtensionsDir)) {
279
+ for (const e of readdirSync(bundledExtensionsDir, { withFileTypes: true })) {
280
+ if (e.isFile())
281
+ currentSourceFiles.add(e.name);
282
+ }
283
+ }
284
+ }
285
+ catch { /* non-fatal */ }
286
+ const removeIfStale = (fileName) => {
287
+ if (currentSourceFiles.has(fileName))
288
+ return; // still in bundle, not stale
289
+ const stale = join(extensionsDir, fileName);
290
+ try {
291
+ if (existsSync(stale))
292
+ rmSync(stale, { force: true });
293
+ }
294
+ catch { /* non-fatal */ }
295
+ };
296
+ if (manifest?.installedExtensionRootFiles) {
297
+ // Manifest-based: remove previously-installed root files that are no longer bundled
298
+ for (const prevFile of manifest.installedExtensionRootFiles) {
299
+ removeIfStale(prevFile);
300
+ }
301
+ }
302
+ else {
303
+ // Fallback: explicitly remove known stale files from pre-manifest-tracking versions
304
+ // env-utils.js was moved from extensions/ root → gsd/ in v2.39.x (#1634)
305
+ removeIfStale('env-utils.js');
306
+ }
307
+ }
220
308
  /**
221
309
  * Syncs all bundled resources to agentDir (~/.gsd/agent/) on every launch.
222
310
  *
@@ -234,11 +322,16 @@ function copyDirRecursive(src, dest) {
234
322
  */
235
323
  export function initResources(agentDir) {
236
324
  mkdirSync(agentDir, { recursive: true });
325
+ const currentVersion = getBundledGsdVersion();
326
+ const manifest = readManagedResourceManifest(agentDir);
327
+ // Always prune root-level extension files that were removed from the bundle.
328
+ // This is cheap (a few existence checks + at most one rmSync) and must run
329
+ // unconditionally so that stale files left by a previous version are cleaned
330
+ // up even when the version/hash match causes the full sync to be skipped.
331
+ pruneRemovedBundledExtensions(manifest, agentDir);
237
332
  // Skip the full copy when both version AND content fingerprint match.
238
333
  // Version-only checks miss same-version content changes (npm link dev workflow,
239
334
  // hotfixes within a release). The content hash catches those at ~1ms cost.
240
- const currentVersion = getBundledGsdVersion();
241
- const manifest = readManagedResourceManifest(agentDir);
242
335
  if (manifest && manifest.gsdVersion === currentVersion) {
243
336
  // Version matches — check content fingerprint for same-version staleness.
244
337
  const currentHash = computeResourceFingerprint();
@@ -262,6 +355,10 @@ export function initResources(agentDir) {
262
355
  // Ensure all newly copied files are owner-writable so the next run can
263
356
  // overwrite them (covers extensions, agents, and skills in one walk).
264
357
  makeTreeWritable(agentDir);
358
+ // Ensure ~/.gsd/agent/node_modules symlinks to GSD's node_modules so that
359
+ // native ESM import() calls from synced extension files can resolve @gsd/*
360
+ // packages via ancestor directory lookup. NODE_PATH only applies to CJS/jiti.
361
+ ensureNodeModulesSymlink(agentDir);
265
362
  writeManagedResourceManifest(agentDir);
266
363
  ensureRegistryEntries(join(agentDir, 'extensions'));
267
364
  }
@@ -64,6 +64,16 @@ export default function AsyncJobs(pi) {
64
64
  },
65
65
  });
66
66
  });
67
+ pi.on("session_before_switch", async () => {
68
+ if (manager) {
69
+ // Cancel all running background jobs — their results are no longer
70
+ // relevant to the new session and would produce wasteful follow-up
71
+ // notifications that trigger empty LLM turns (#1642).
72
+ for (const job of manager.getRunningJobs()) {
73
+ manager.cancel(job.id);
74
+ }
75
+ }
76
+ });
67
77
  pi.on("session_shutdown", async () => {
68
78
  if (manager) {
69
79
  manager.shutdown();
@@ -4,7 +4,7 @@ let registrationPromise = null;
4
4
  async function registerBrowserTools(pi) {
5
5
  if (!registrationPromise) {
6
6
  registrationPromise = (async () => {
7
- const [lifecycle, capture, settle, refs, utils, navigation, screenshot, interaction, inspection, session, assertions, refTools, wait, pages, forms, intent, pdf, statePersistence, networkMock, device, extract, visualDiff, zoom, codegen, actionCache, injectionDetection,] = await Promise.all([
7
+ const [lifecycle, capture, settle, refs, utils, navigation, screenshot, interaction, inspection, session, assertions, refTools, wait, pages, forms, intent, pdf, statePersistence, networkMock, device, extract, visualDiff, zoom, codegen, actionCache, injectionDetection, verify,] = await Promise.all([
8
8
  importExtensionModule(import.meta.url, "./lifecycle.js"),
9
9
  importExtensionModule(import.meta.url, "./capture.js"),
10
10
  importExtensionModule(import.meta.url, "./settle.js"),
@@ -31,6 +31,7 @@ async function registerBrowserTools(pi) {
31
31
  importExtensionModule(import.meta.url, "./tools/codegen.js"),
32
32
  importExtensionModule(import.meta.url, "./tools/action-cache.js"),
33
33
  importExtensionModule(import.meta.url, "./tools/injection-detect.js"),
34
+ importExtensionModule(import.meta.url, "./tools/verify.js"),
34
35
  ]);
35
36
  const deps = {
36
37
  ensureBrowser: lifecycle.ensureBrowser,
@@ -99,6 +100,7 @@ async function registerBrowserTools(pi) {
99
100
  codegen.registerCodegenTools(pi, deps);
100
101
  actionCache.registerActionCacheTools(pi, deps);
101
102
  injectionDetection.registerInjectionDetectionTools(pi, deps);
103
+ verify.registerVerifyTools(pi, deps);
102
104
  })().catch((error) => {
103
105
  registrationPromise = null;
104
106
  throw error;
@@ -7,7 +7,9 @@
7
7
  "test": "node --test tests/*.test.mjs"
8
8
  },
9
9
  "pi": {
10
- "extensions": ["./index.ts"]
10
+ "extensions": [
11
+ "./index.js"
12
+ ]
11
13
  },
12
14
  "peerDependencies": {
13
15
  "playwright": ">=1.40.0",
@@ -0,0 +1,97 @@
1
+ import { Type } from "@sinclair/typebox";
2
+ export function registerVerifyTools(pi, deps) {
3
+ pi.registerTool({
4
+ name: "browser_verify",
5
+ label: "Browser Verify",
6
+ description: "Run a structured browser verification flow: navigate to a URL, run checks (element visibility, text content), capture screenshots as evidence, and return structured pass/fail results.",
7
+ promptGuidelines: [
8
+ "Use browser_verify for UAT verification flows that need structured evidence.",
9
+ "Each check produces a pass/fail result with captured evidence.",
10
+ "Prefer this over manual navigation + assertion sequences for verification tasks.",
11
+ ],
12
+ parameters: Type.Object({
13
+ url: Type.String({ description: "URL to navigate to" }),
14
+ checks: Type.Array(Type.Object({
15
+ description: Type.String({ description: "What this check verifies" }),
16
+ selector: Type.Optional(Type.String({ description: "CSS selector to check" })),
17
+ expectedText: Type.Optional(Type.String({ description: "Expected text content" })),
18
+ expectedVisible: Type.Optional(Type.Boolean({ description: "Whether element should be visible" })),
19
+ screenshot: Type.Optional(Type.Boolean({ description: "Capture screenshot as evidence" })),
20
+ }), { description: "Verification checks to run" }),
21
+ timeout: Type.Optional(Type.Number({ description: "Navigation timeout in ms", default: 10000 })),
22
+ }),
23
+ async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
24
+ const startTime = Date.now();
25
+ const { page } = await deps.ensureBrowser();
26
+ const timeout = params.timeout ?? 10000;
27
+ try {
28
+ await page.goto(params.url, { waitUntil: "domcontentloaded", timeout });
29
+ }
30
+ catch (navErr) {
31
+ const msg = navErr instanceof Error ? navErr.message : String(navErr);
32
+ return {
33
+ content: [{ type: "text", text: `Navigation failed: ${msg}` }],
34
+ details: {
35
+ url: params.url,
36
+ passed: false,
37
+ checks: params.checks.map((c) => ({ description: c.description, passed: false, error: msg })),
38
+ duration: Date.now() - startTime,
39
+ },
40
+ };
41
+ }
42
+ const results = [];
43
+ for (const check of params.checks) {
44
+ try {
45
+ let passed = true;
46
+ let actual;
47
+ let evidence;
48
+ if (check.selector) {
49
+ const element = await page.$(check.selector);
50
+ if (check.expectedVisible !== undefined) {
51
+ const isVisible = element ? await element.isVisible() : false;
52
+ passed = isVisible === check.expectedVisible;
53
+ actual = `visible=${isVisible}`;
54
+ }
55
+ if (check.expectedText !== undefined && element) {
56
+ const text = await element.textContent();
57
+ passed = passed && (text?.includes(check.expectedText) ?? false);
58
+ actual = `text="${text?.slice(0, 200)}"`;
59
+ }
60
+ if (!element && (check.expectedVisible === true || check.expectedText)) {
61
+ passed = false;
62
+ actual = "element not found";
63
+ }
64
+ }
65
+ if (check.screenshot) {
66
+ try {
67
+ const buf = await page.screenshot({ type: "png" });
68
+ evidence = `screenshot captured (${buf.length} bytes)`;
69
+ }
70
+ catch {
71
+ evidence = "screenshot failed";
72
+ }
73
+ }
74
+ results.push({ description: check.description, passed, actual, evidence });
75
+ }
76
+ catch (checkErr) {
77
+ results.push({
78
+ description: check.description,
79
+ passed: false,
80
+ error: checkErr instanceof Error ? checkErr.message : String(checkErr),
81
+ });
82
+ }
83
+ }
84
+ const allPassed = results.every((r) => r.passed);
85
+ const summary = results.map((r) => `${r.passed ? "PASS" : "FAIL"}: ${r.description}${r.actual ? ` (${r.actual})` : ""}${r.error ? ` — ${r.error}` : ""}`).join("\n");
86
+ return {
87
+ content: [{ type: "text", text: `Verification ${allPassed ? "PASSED" : "FAILED"} (${results.filter(r => r.passed).length}/${results.length})\n\n${summary}` }],
88
+ details: {
89
+ url: params.url,
90
+ passed: allPassed,
91
+ checks: results,
92
+ duration: Date.now() - startTime,
93
+ },
94
+ };
95
+ },
96
+ });
97
+ }
@@ -237,11 +237,14 @@ export class CmuxClient {
237
237
  return extractSurfaceIds(parsed);
238
238
  }
239
239
  async createSplit(direction) {
240
+ return this.createSplitFrom(this.config.surfaceId, direction);
241
+ }
242
+ async createSplitFrom(sourceSurfaceId, direction) {
240
243
  if (!this.config.splits)
241
244
  return null;
242
245
  const before = new Set(await this.listSurfaceIds());
243
246
  const args = ["new-split", direction];
244
- const scopedArgs = this.appendSurface(this.appendWorkspace(args), this.config.surfaceId);
247
+ const scopedArgs = this.appendSurface(this.appendWorkspace(args), sourceSurfaceId);
245
248
  await this.runAsync(scopedArgs);
246
249
  const after = await this.listSurfaceIds();
247
250
  for (const id of after) {
@@ -250,6 +253,57 @@ export class CmuxClient {
250
253
  }
251
254
  return null;
252
255
  }
256
+ /**
257
+ * Create a grid of surfaces for parallel agent execution.
258
+ *
259
+ * Layout strategy (gsd stays in the original surface):
260
+ * 1 agent: [gsd | A]
261
+ * 2 agents: [gsd | A]
262
+ * [ | B]
263
+ * 3 agents: [gsd | A]
264
+ * [ C | B]
265
+ * 4 agents: [gsd | A]
266
+ * [ C | B] (D splits from B downward)
267
+ * [ | D]
268
+ *
269
+ * Returns surface IDs in order, or empty array on failure.
270
+ */
271
+ async createGridLayout(count) {
272
+ if (!this.config.splits || count <= 0)
273
+ return [];
274
+ const surfaces = [];
275
+ // First split: create right column from the gsd surface
276
+ const rightCol = await this.createSplitFrom(this.config.surfaceId, "right");
277
+ if (!rightCol)
278
+ return [];
279
+ surfaces.push(rightCol);
280
+ if (count === 1)
281
+ return surfaces;
282
+ // Second split: split right column down → bottom-right
283
+ const bottomRight = await this.createSplitFrom(rightCol, "down");
284
+ if (!bottomRight)
285
+ return surfaces;
286
+ surfaces.push(bottomRight);
287
+ if (count === 2)
288
+ return surfaces;
289
+ // Third split: split gsd surface down → bottom-left
290
+ const bottomLeft = await this.createSplitFrom(this.config.surfaceId, "down");
291
+ if (!bottomLeft)
292
+ return surfaces;
293
+ surfaces.push(bottomLeft);
294
+ if (count === 3)
295
+ return surfaces;
296
+ // Fourth+: split subsequent surfaces down from the last created
297
+ let lastSurface = bottomRight;
298
+ for (let i = 3; i < count; i++) {
299
+ const next = await this.createSplitFrom(lastSurface, "down");
300
+ if (!next)
301
+ break;
302
+ surfaces.push(next);
303
+ lastSurface = next;
304
+ }
305
+ return surfaces;
306
+ }
253
307
  async sendSurface(surfaceId, text) {
254
308
  const payload = text.endsWith("\n") ? text : `${text}\n`;
255
309
  const stdout = await this.runAsync(["send-surface", "--surface", surfaceId, payload]);
@@ -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
  }
@@ -46,30 +46,11 @@ async function writeEnvKey(filePath, key, value) {
46
46
  await writeFile(filePath, content, "utf8");
47
47
  }
48
48
  // ─── Exported utilities ───────────────────────────────────────────────────────
49
- /**
50
- * Check which keys already exist in the .env file or process.env.
51
- * Returns the subset of `keys` that are already set.
52
- * Handles ENOENT gracefully (still checks process.env).
53
- * Empty-string values count as existing.
54
- */
55
- export async function checkExistingEnvKeys(keys, envFilePath) {
56
- let fileContent = "";
57
- try {
58
- fileContent = await readFile(envFilePath, "utf8");
59
- }
60
- catch {
61
- // ENOENT or other read error — proceed with empty content
62
- }
63
- const existing = [];
64
- for (const key of keys) {
65
- const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
66
- const regex = new RegExp(`^${escaped}\\s*=`, "m");
67
- if (regex.test(fileContent) || key in process.env) {
68
- existing.push(key);
69
- }
70
- }
71
- return existing;
72
- }
49
+ // Re-export from env-utils.ts so existing consumers still work.
50
+ // The implementation lives in env-utils.ts to avoid pulling @gsd/pi-tui
51
+ // into modules that only need env-checking (e.g. files.ts during reports).
52
+ import { checkExistingEnvKeys } from "./gsd/env-utils.js";
53
+ export { checkExistingEnvKeys };
73
54
  /**
74
55
  * Detect the write destination based on project files in basePath.
75
56
  * Priority: vercel.json → convex/ dir → fallback "dotenv".