gsd-pi 2.77.0 → 2.78.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (798) hide show
  1. package/README.md +18 -36
  2. package/dist/claude-cli-check.js +5 -1
  3. package/dist/cli-web-branch.d.ts +1 -0
  4. package/dist/cli-web-branch.js +3 -0
  5. package/dist/cli.js +38 -2
  6. package/dist/extension-discovery.d.ts +6 -0
  7. package/dist/extension-discovery.js +37 -0
  8. package/dist/extension-registry.d.ts +3 -0
  9. package/dist/extension-sort.d.ts +18 -0
  10. package/dist/extension-sort.js +114 -0
  11. package/dist/extension-validator.d.ts +47 -0
  12. package/dist/extension-validator.js +127 -0
  13. package/dist/headless.js +49 -4
  14. package/dist/loader.js +35 -7
  15. package/dist/provider-migrations.d.ts +18 -0
  16. package/dist/provider-migrations.js +14 -0
  17. package/dist/resource-loader.d.ts +40 -0
  18. package/dist/resource-loader.js +32 -13
  19. package/dist/resources/extensions/browser-tools/capture.js +9 -0
  20. package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
  21. package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
  22. package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
  23. package/dist/resources/extensions/browser-tools/tools/forms.js +5 -1
  24. package/dist/resources/extensions/browser-tools/tools/intent.js +5 -1
  25. package/dist/resources/extensions/claude-code-cli/readiness.js +5 -1
  26. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +552 -67
  27. package/dist/resources/extensions/cmux/index.js +20 -0
  28. package/dist/resources/extensions/github-sync/templates.js +103 -0
  29. package/dist/resources/extensions/google-search/extension-manifest.json +5 -4
  30. package/dist/resources/extensions/google-search/index.js +3 -375
  31. package/dist/resources/extensions/gsd/abandon-detect.js +44 -0
  32. package/dist/resources/extensions/gsd/auto/loop.js +124 -2
  33. package/dist/resources/extensions/gsd/auto/phases.js +57 -39
  34. package/dist/resources/extensions/gsd/auto/resolve.js +24 -0
  35. package/dist/resources/extensions/gsd/auto/run-unit.js +10 -2
  36. package/dist/resources/extensions/gsd/auto/session.js +6 -2
  37. package/dist/resources/extensions/gsd/auto/turn-epoch.js +95 -0
  38. package/dist/resources/extensions/gsd/auto-dispatch.js +201 -38
  39. package/dist/resources/extensions/gsd/auto-loop.js +1 -1
  40. package/dist/resources/extensions/gsd/auto-model-selection.js +124 -4
  41. package/dist/resources/extensions/gsd/auto-post-unit.js +215 -64
  42. package/dist/resources/extensions/gsd/auto-prompts.js +372 -104
  43. package/dist/resources/extensions/gsd/auto-recovery.js +210 -24
  44. package/dist/resources/extensions/gsd/auto-start.js +122 -30
  45. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +11 -5
  46. package/dist/resources/extensions/gsd/auto-tool-tracking.js +47 -7
  47. package/dist/resources/extensions/gsd/auto-unit-closeout.js +11 -2
  48. package/dist/resources/extensions/gsd/auto-worktree.js +180 -34
  49. package/dist/resources/extensions/gsd/auto.js +107 -35
  50. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +19 -1
  51. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +209 -0
  52. package/dist/resources/extensions/gsd/bootstrap/provider-error-resume.js +5 -6
  53. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +11 -0
  54. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +7 -3
  55. package/dist/resources/extensions/gsd/bootstrap/system-context.js +11 -6
  56. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +127 -9
  57. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +31 -4
  58. package/dist/resources/extensions/gsd/commands-cmux.js +9 -6
  59. package/dist/resources/extensions/gsd/commands-extensions.js +634 -43
  60. package/dist/resources/extensions/gsd/component-loader.js +447 -0
  61. package/dist/resources/extensions/gsd/component-types.js +69 -0
  62. package/dist/resources/extensions/gsd/context-store.js +23 -7
  63. package/dist/resources/extensions/gsd/detection.js +49 -1
  64. package/dist/resources/extensions/gsd/dispatch-guard.js +29 -3
  65. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  66. package/dist/resources/extensions/gsd/file-lock.js +49 -9
  67. package/dist/resources/extensions/gsd/forensics.js +106 -0
  68. package/dist/resources/extensions/gsd/gate-registry.js +2 -2
  69. package/dist/resources/extensions/gsd/git-constants.js +28 -1
  70. package/dist/resources/extensions/gsd/git-self-heal.js +27 -0
  71. package/dist/resources/extensions/gsd/git-service.js +127 -2
  72. package/dist/resources/extensions/gsd/gitignore.js +1 -0
  73. package/dist/resources/extensions/gsd/gsd-db.js +6 -3
  74. package/dist/resources/extensions/gsd/guided-flow-queue.js +4 -1
  75. package/dist/resources/extensions/gsd/guided-flow.js +39 -13
  76. package/dist/resources/extensions/gsd/journal.js +17 -2
  77. package/dist/resources/extensions/gsd/memory-extractor.js +7 -1
  78. package/dist/resources/extensions/gsd/milestone-actions.js +15 -0
  79. package/dist/resources/extensions/gsd/milestone-scope-classifier.js +299 -0
  80. package/dist/resources/extensions/gsd/milestone-summary-classifier.js +37 -0
  81. package/dist/resources/extensions/gsd/model-cost-table.js +3 -0
  82. package/dist/resources/extensions/gsd/model-router.js +6 -0
  83. package/dist/resources/extensions/gsd/native-git-bridge.js +34 -4
  84. package/dist/resources/extensions/gsd/notifications.js +30 -16
  85. package/dist/resources/extensions/gsd/preferences-validation.js +23 -0
  86. package/dist/resources/extensions/gsd/prompt-cache-optimizer.js +4 -0
  87. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +6 -2
  88. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +23 -4
  89. package/dist/resources/extensions/gsd/prompts/doctor-heal.md +5 -4
  90. package/dist/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  91. package/dist/resources/extensions/gsd/prompts/system.md +1 -0
  92. package/dist/resources/extensions/gsd/reports.js +5 -4
  93. package/dist/resources/extensions/gsd/safety/git-checkpoint.js +11 -0
  94. package/dist/resources/extensions/gsd/service-tier.js +5 -2
  95. package/dist/resources/extensions/gsd/session-lock.js +19 -10
  96. package/dist/resources/extensions/gsd/skill-manifest.js +168 -0
  97. package/dist/resources/extensions/gsd/slice-cadence.js +238 -0
  98. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +278 -8
  99. package/dist/resources/extensions/gsd/state-transition-matrix.js +118 -0
  100. package/dist/resources/extensions/gsd/state.js +69 -58
  101. package/dist/resources/extensions/gsd/sync-lock.js +98 -42
  102. package/dist/resources/extensions/gsd/tools/complete-slice.js +21 -0
  103. package/dist/resources/extensions/gsd/tools/complete-task.js +31 -0
  104. package/dist/resources/extensions/gsd/tools/validate-milestone.js +7 -2
  105. package/dist/resources/extensions/gsd/unit-context-composer.js +147 -0
  106. package/dist/resources/extensions/gsd/unit-context-manifest.js +370 -0
  107. package/dist/resources/extensions/gsd/uok/audit.js +18 -2
  108. package/dist/resources/extensions/gsd/uok/dispatch-envelope.js +33 -0
  109. package/dist/resources/extensions/gsd/uok/execution-graph.js +10 -0
  110. package/dist/resources/extensions/gsd/uok/gate-runner.js +53 -5
  111. package/dist/resources/extensions/gsd/uok/gitops.js +2 -1
  112. package/dist/resources/extensions/gsd/uok/loop-adapter.js +37 -10
  113. package/dist/resources/extensions/gsd/uok/parity-report.js +58 -0
  114. package/dist/resources/extensions/gsd/uok/plan-v2.js +10 -4
  115. package/dist/resources/extensions/gsd/uok/writer.js +82 -0
  116. package/dist/resources/extensions/gsd/workflow-logger.js +10 -2
  117. package/dist/resources/extensions/gsd/workflow-mcp.js +6 -0
  118. package/dist/resources/extensions/gsd/worktree-manager.js +86 -8
  119. package/dist/resources/extensions/gsd/worktree-resolver.js +86 -7
  120. package/dist/resources/extensions/gsd/worktree-telemetry.js +198 -0
  121. package/dist/resources/extensions/mcp-client/auth.js +10 -1
  122. package/dist/resources/extensions/mcp-client/index.js +121 -10
  123. package/dist/resources/extensions/ollama/index.js +5 -1
  124. package/dist/resources/extensions/remote-questions/manager.js +11 -5
  125. package/dist/resources/extensions/shared/cmux-events.js +12 -0
  126. package/dist/resources/extensions/shared/rtk-session-stats.js +1 -2
  127. package/dist/resources/skills/create-skill/SKILL.md +2 -2
  128. package/dist/resources/skills/create-skill/references/gsd-skill-ecosystem.md +4 -4
  129. package/dist/resources/skills/create-skill/workflows/audit-skill.md +4 -4
  130. package/dist/resources/skills/create-skill/workflows/create-new-skill.md +5 -5
  131. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  132. package/dist/web/standalone/.next/BUILD_ID +1 -1
  133. package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
  134. package/dist/web/standalone/.next/build-manifest.json +3 -3
  135. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  136. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  137. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  138. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  139. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  140. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  141. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  142. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  143. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  144. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  145. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  146. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  147. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  148. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  149. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  150. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  151. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  152. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  154. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  156. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  158. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  160. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
  162. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
  164. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  166. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  168. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  170. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  172. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  174. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  175. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  176. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  178. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  180. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  181. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  182. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  183. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  184. package/dist/web/standalone/.next/server/app/api/notifications/route.js +1 -1
  185. package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  186. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  187. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  188. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  189. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  190. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  191. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  192. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  193. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  194. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  195. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  196. package/dist/web/standalone/.next/server/app/api/session/events/route.js +1 -1
  197. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  198. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  199. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  200. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  201. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  202. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  203. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  204. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  205. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  206. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  207. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  208. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
  209. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  210. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +1 -1
  211. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  212. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  213. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  214. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  215. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  216. package/dist/web/standalone/.next/server/app/index.html +1 -1
  217. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  218. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  219. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  220. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  221. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  222. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  223. package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
  224. package/dist/web/standalone/.next/server/chunks/1926.js +1 -0
  225. package/dist/web/standalone/.next/server/chunks/6897.js +2 -2
  226. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  227. package/dist/web/standalone/.next/server/middleware-manifest.json +1 -1
  228. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  229. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  230. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  231. package/dist/web/standalone/.next/static/chunks/2826.e9f5195e91f9cad2.js +11 -0
  232. package/dist/web/standalone/.next/static/chunks/{webpack-5fc74f13a25fa1bb.js → webpack-2e68521d7c82f7c2.js} +1 -1
  233. package/package.json +17 -16
  234. package/packages/daemon/package.json +2 -2
  235. package/packages/daemon/src/logger.ts +4 -3
  236. package/packages/mcp-server/README.md +3 -3
  237. package/packages/mcp-server/dist/env-writer.d.ts +1 -0
  238. package/packages/mcp-server/dist/env-writer.d.ts.map +1 -1
  239. package/packages/mcp-server/dist/env-writer.js +74 -6
  240. package/packages/mcp-server/dist/env-writer.js.map +1 -1
  241. package/packages/mcp-server/dist/server.d.ts +24 -0
  242. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  243. package/packages/mcp-server/dist/server.js +111 -87
  244. package/packages/mcp-server/dist/server.js.map +1 -1
  245. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  246. package/packages/mcp-server/dist/workflow-tools.js +15 -6
  247. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  248. package/packages/mcp-server/package.json +7 -2
  249. package/packages/mcp-server/src/env-writer.test.ts +79 -1
  250. package/packages/mcp-server/src/env-writer.ts +76 -6
  251. package/packages/mcp-server/src/mcp-server.test.ts +25 -3
  252. package/packages/mcp-server/src/readers/graph.test.ts +87 -15
  253. package/packages/mcp-server/src/readers/readers.test.ts +5 -1
  254. package/packages/mcp-server/src/secure-env-collect.test.ts +232 -237
  255. package/packages/mcp-server/src/server.ts +158 -105
  256. package/packages/mcp-server/src/workflow-tools.test.ts +85 -0
  257. package/packages/mcp-server/src/workflow-tools.ts +19 -6
  258. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  259. package/packages/native/package.json +7 -2
  260. package/packages/native/src/__tests__/_test-coverage-guard.test.mjs +98 -0
  261. package/packages/native/src/__tests__/clipboard.test.mjs +69 -23
  262. package/packages/native/src/__tests__/module-compat.test.mjs +59 -27
  263. package/packages/native/src/__tests__/ps.test.mjs +14 -8
  264. package/packages/native/src/__tests__/stream-process.test.mjs +23 -2
  265. package/packages/native/src/__tests__/truncate.test.mjs +17 -2
  266. package/packages/native/tsconfig.tsbuildinfo +1 -1
  267. package/packages/pi-agent-core/package.json +6 -1
  268. package/packages/pi-agent-core/src/agent-loop.test.ts +226 -31
  269. package/packages/pi-agent-core/src/agent.test.ts +96 -102
  270. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  271. package/packages/pi-ai/dist/models/capability-patches.d.ts.map +1 -1
  272. package/packages/pi-ai/dist/models/capability-patches.js +9 -2
  273. package/packages/pi-ai/dist/models/capability-patches.js.map +1 -1
  274. package/packages/pi-ai/dist/models/generated/index.d.ts +34 -0
  275. package/packages/pi-ai/dist/models/generated/index.d.ts.map +1 -1
  276. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts +17 -0
  277. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts.map +1 -1
  278. package/packages/pi-ai/dist/models/generated/openai-codex.js +17 -0
  279. package/packages/pi-ai/dist/models/generated/openai-codex.js.map +1 -1
  280. package/packages/pi-ai/dist/models/generated/openai.d.ts +17 -0
  281. package/packages/pi-ai/dist/models/generated/openai.d.ts.map +1 -1
  282. package/packages/pi-ai/dist/models/generated/openai.js +17 -0
  283. package/packages/pi-ai/dist/models/generated/openai.js.map +1 -1
  284. package/packages/pi-ai/dist/models.generated.test.js +43 -70
  285. package/packages/pi-ai/dist/models.generated.test.js.map +1 -1
  286. package/packages/pi-ai/dist/models.test.js +36 -11
  287. package/packages/pi-ai/dist/models.test.js.map +1 -1
  288. package/packages/pi-ai/package.json +6 -1
  289. package/packages/pi-ai/scripts/generate-models.ts +44 -0
  290. package/packages/pi-ai/src/models/capability-patches.ts +10 -2
  291. package/packages/pi-ai/src/models/generated/openai-codex.ts +17 -0
  292. package/packages/pi-ai/src/models/generated/openai.ts +17 -0
  293. package/packages/pi-ai/src/models.generated.test.ts +46 -73
  294. package/packages/pi-ai/src/models.test.ts +48 -11
  295. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  296. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +96 -32
  297. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  298. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js +75 -12
  299. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js.map +1 -1
  300. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +99 -31
  301. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -1
  302. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts +25 -0
  303. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
  304. package/packages/pi-coding-agent/dist/core/compaction/compaction.js +105 -6
  305. package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
  306. package/packages/pi-coding-agent/dist/core/compaction/compaction.test.js +230 -28
  307. package/packages/pi-coding-agent/dist/core/compaction/compaction.test.js.map +1 -1
  308. package/packages/pi-coding-agent/dist/core/compaction/utils.d.ts +30 -2
  309. package/packages/pi-coding-agent/dist/core/compaction/utils.d.ts.map +1 -1
  310. package/packages/pi-coding-agent/dist/core/compaction/utils.js +113 -12
  311. package/packages/pi-coding-agent/dist/core/compaction/utils.js.map +1 -1
  312. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts +1 -0
  313. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts.map +1 -1
  314. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +29 -18
  315. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -1
  316. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.test.d.ts +2 -0
  317. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.test.d.ts.map +1 -0
  318. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.test.js +130 -0
  319. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.test.js.map +1 -0
  320. package/packages/pi-coding-agent/dist/core/compaction-utils.test.js +56 -1
  321. package/packages/pi-coding-agent/dist/core/compaction-utils.test.js.map +1 -1
  322. package/packages/pi-coding-agent/dist/core/discovery-cache.test.js +8 -15
  323. package/packages/pi-coding-agent/dist/core/discovery-cache.test.js.map +1 -1
  324. package/packages/pi-coding-agent/dist/core/extensions/extension-discovery.d.ts +25 -0
  325. package/packages/pi-coding-agent/dist/core/extensions/extension-discovery.d.ts.map +1 -0
  326. package/packages/pi-coding-agent/dist/core/extensions/extension-discovery.js +109 -0
  327. package/packages/pi-coding-agent/dist/core/extensions/extension-discovery.js.map +1 -0
  328. package/packages/pi-coding-agent/dist/core/extensions/extension-registry.d.ts +67 -0
  329. package/packages/pi-coding-agent/dist/core/extensions/extension-registry.d.ts.map +1 -0
  330. package/packages/pi-coding-agent/dist/core/extensions/extension-registry.js +167 -0
  331. package/packages/pi-coding-agent/dist/core/extensions/extension-registry.js.map +1 -0
  332. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +8 -2
  333. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  334. package/packages/pi-coding-agent/dist/core/extensions/loader.js +85 -8
  335. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  336. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +7 -0
  337. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  338. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  339. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js +41 -4
  340. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js.map +1 -1
  341. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +19 -2
  342. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  343. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js +76 -18
  344. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js.map +1 -1
  345. package/packages/pi-coding-agent/dist/core/resource-loader.js +1 -1
  346. package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
  347. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  348. package/packages/pi-coding-agent/dist/core/retry-handler.js +2 -6
  349. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  350. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +5 -1
  351. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  352. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts +18 -0
  353. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts.map +1 -0
  354. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js +18 -0
  355. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js.map +1 -0
  356. package/packages/pi-coding-agent/dist/core/sdk.d.ts +1 -0
  357. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  358. package/packages/pi-coding-agent/dist/core/sdk.js +4 -1
  359. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  360. package/packages/pi-coding-agent/dist/core/sdk.test.js +19 -1
  361. package/packages/pi-coding-agent/dist/core/sdk.test.js.map +1 -1
  362. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +20 -0
  363. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  364. package/packages/pi-coding-agent/dist/core/system-prompt.js +19 -5
  365. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  366. package/packages/pi-coding-agent/dist/core/tools/path-utils.test.js +2 -1
  367. package/packages/pi-coding-agent/dist/core/tools/path-utils.test.js.map +1 -1
  368. package/packages/pi-coding-agent/dist/index.d.ts +1 -0
  369. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  370. package/packages/pi-coding-agent/dist/index.js +1 -0
  371. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  372. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.js +15 -6
  373. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.js.map +1 -1
  374. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +36 -5
  375. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  376. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js +20 -13
  377. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js.map +1 -1
  378. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  379. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +14 -5
  380. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  381. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts +7 -1
  382. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  383. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +31 -9
  384. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
  385. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  386. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +30 -12
  387. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  388. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  389. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +18 -3
  390. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  391. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +139 -0
  392. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  393. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +2 -0
  394. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  395. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  396. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +4 -0
  397. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  398. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +105 -13
  399. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  400. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts +2 -0
  401. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts.map +1 -0
  402. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js +130 -0
  403. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js.map +1 -0
  404. package/packages/pi-coding-agent/package.json +6 -1
  405. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +113 -37
  406. package/packages/pi-coding-agent/src/core/agent-session-model-switch.test.ts +89 -17
  407. package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +112 -43
  408. package/packages/pi-coding-agent/src/core/compaction/compaction.test.ts +368 -28
  409. package/packages/pi-coding-agent/src/core/compaction/compaction.ts +122 -6
  410. package/packages/pi-coding-agent/src/core/compaction/utils.ts +111 -13
  411. package/packages/pi-coding-agent/src/core/compaction-orchestrator.test.ts +154 -0
  412. package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +32 -18
  413. package/packages/pi-coding-agent/src/core/compaction-utils.test.ts +68 -1
  414. package/packages/pi-coding-agent/src/core/discovery-cache.test.ts +9 -18
  415. package/packages/pi-coding-agent/src/core/extensions/extension-discovery.ts +119 -0
  416. package/packages/pi-coding-agent/src/core/extensions/extension-registry.ts +222 -0
  417. package/packages/pi-coding-agent/src/core/extensions/loader.ts +82 -11
  418. package/packages/pi-coding-agent/src/core/extensions/types.ts +8 -0
  419. package/packages/pi-coding-agent/src/core/lsp/lsp-integration.test.ts +48 -4
  420. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +22 -2
  421. package/packages/pi-coding-agent/src/core/resource-loader-cache-reset.test.ts +93 -28
  422. package/packages/pi-coding-agent/src/core/resource-loader.ts +1 -1
  423. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +5 -1
  424. package/packages/pi-coding-agent/src/core/retry-handler.ts +2 -8
  425. package/packages/pi-coding-agent/src/core/retryable-error-regex.ts +18 -0
  426. package/packages/pi-coding-agent/src/core/sdk.test.ts +25 -1
  427. package/packages/pi-coding-agent/src/core/sdk.ts +10 -3
  428. package/packages/pi-coding-agent/src/core/system-prompt.ts +38 -4
  429. package/packages/pi-coding-agent/src/core/tools/path-utils.test.ts +2 -1
  430. package/packages/pi-coding-agent/src/index.ts +1 -0
  431. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/provider-display-name.test.ts +17 -7
  432. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +49 -3
  433. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.test.ts +26 -20
  434. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +14 -5
  435. package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +45 -11
  436. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +48 -9
  437. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +160 -1
  438. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +20 -3
  439. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +2 -0
  440. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +119 -13
  441. package/packages/pi-coding-agent/src/tests/system-prompt-skill-filter.test.ts +157 -0
  442. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  443. package/packages/pi-tui/dist/__tests__/autocomplete.test.js +31 -14
  444. package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
  445. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +128 -17
  446. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -1
  447. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js +51 -6
  448. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js.map +1 -1
  449. package/packages/pi-tui/dist/__tests__/tui.test.js +18 -30
  450. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  451. package/packages/pi-tui/dist/components/__tests__/input.test.js +10 -3
  452. package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
  453. package/packages/pi-tui/dist/components/__tests__/loader.test.js +53 -9
  454. package/packages/pi-tui/dist/components/__tests__/loader.test.js.map +1 -1
  455. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js +6 -2
  456. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js.map +1 -1
  457. package/packages/pi-tui/dist/components/editor.d.ts +14 -0
  458. package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
  459. package/packages/pi-tui/dist/components/editor.js +19 -0
  460. package/packages/pi-tui/dist/components/editor.js.map +1 -1
  461. package/packages/pi-tui/dist/components/image.test.js +6 -5
  462. package/packages/pi-tui/dist/components/image.test.js.map +1 -1
  463. package/packages/pi-tui/dist/editor-component.d.ts +2 -0
  464. package/packages/pi-tui/dist/editor-component.d.ts.map +1 -1
  465. package/packages/pi-tui/dist/editor-component.js.map +1 -1
  466. package/packages/pi-tui/dist/stdin-buffer.d.ts +7 -0
  467. package/packages/pi-tui/dist/stdin-buffer.d.ts.map +1 -1
  468. package/packages/pi-tui/dist/stdin-buffer.js +20 -0
  469. package/packages/pi-tui/dist/stdin-buffer.js.map +1 -1
  470. package/packages/pi-tui/package.json +6 -1
  471. package/packages/pi-tui/src/__tests__/autocomplete.test.ts +46 -15
  472. package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +140 -17
  473. package/packages/pi-tui/src/__tests__/stdin-buffer.test.ts +62 -6
  474. package/packages/pi-tui/src/__tests__/tui.test.ts +18 -37
  475. package/packages/pi-tui/src/components/__tests__/input.test.ts +19 -3
  476. package/packages/pi-tui/src/components/__tests__/loader.test.ts +112 -35
  477. package/packages/pi-tui/src/components/__tests__/markdown-maxlines.test.ts +9 -2
  478. package/packages/pi-tui/src/components/editor.ts +22 -0
  479. package/packages/pi-tui/src/components/image.test.ts +10 -5
  480. package/packages/pi-tui/src/editor-component.ts +3 -0
  481. package/packages/pi-tui/src/stdin-buffer.ts +26 -0
  482. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  483. package/packages/rpc-client/dist/rpc-client.test.js +101 -51
  484. package/packages/rpc-client/dist/rpc-client.test.js.map +1 -1
  485. package/packages/rpc-client/package.json +6 -1
  486. package/packages/rpc-client/src/rpc-client.test.ts +109 -52
  487. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
  488. package/pkg/package.json +1 -1
  489. package/scripts/install.js +526 -0
  490. package/scripts/lib/workspace-manifest.cjs +86 -0
  491. package/scripts/link-workspace-packages.cjs +5 -17
  492. package/scripts/postinstall.js +9 -178
  493. package/src/resources/extensions/browser-tools/capture.ts +12 -0
  494. package/src/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
  495. package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
  496. package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
  497. package/src/resources/extensions/browser-tools/tools/forms.ts +5 -1
  498. package/src/resources/extensions/browser-tools/tools/intent.ts +5 -1
  499. package/src/resources/extensions/claude-code-cli/readiness.ts +5 -1
  500. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +602 -73
  501. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +1028 -91
  502. package/src/resources/extensions/cmux/index.ts +35 -10
  503. package/src/resources/extensions/github-sync/templates.ts +151 -0
  504. package/src/resources/extensions/github-sync/tests/cli.test.ts +76 -7
  505. package/src/resources/extensions/github-sync/tests/templates.test.ts +92 -1
  506. package/src/resources/extensions/google-search/extension-manifest.json +5 -4
  507. package/src/resources/extensions/google-search/index.ts +9 -470
  508. package/src/resources/extensions/gsd/abandon-detect.ts +62 -0
  509. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -1
  510. package/src/resources/extensions/gsd/auto/loop.ts +142 -2
  511. package/src/resources/extensions/gsd/auto/phases.ts +62 -38
  512. package/src/resources/extensions/gsd/auto/resolve.ts +29 -0
  513. package/src/resources/extensions/gsd/auto/run-unit.ts +16 -2
  514. package/src/resources/extensions/gsd/auto/session.ts +7 -2
  515. package/src/resources/extensions/gsd/auto/turn-epoch.ts +108 -0
  516. package/src/resources/extensions/gsd/auto/types.ts +1 -1
  517. package/src/resources/extensions/gsd/auto-dispatch.ts +214 -37
  518. package/src/resources/extensions/gsd/auto-loop.ts +1 -1
  519. package/src/resources/extensions/gsd/auto-model-selection.ts +131 -4
  520. package/src/resources/extensions/gsd/auto-post-unit.ts +226 -73
  521. package/src/resources/extensions/gsd/auto-prompts.ts +385 -93
  522. package/src/resources/extensions/gsd/auto-recovery.ts +240 -25
  523. package/src/resources/extensions/gsd/auto-start.ts +146 -14
  524. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +12 -5
  525. package/src/resources/extensions/gsd/auto-tool-tracking.ts +51 -7
  526. package/src/resources/extensions/gsd/auto-unit-closeout.ts +14 -3
  527. package/src/resources/extensions/gsd/auto-worktree.ts +190 -31
  528. package/src/resources/extensions/gsd/auto.ts +127 -41
  529. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +20 -1
  530. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +221 -0
  531. package/src/resources/extensions/gsd/bootstrap/provider-error-resume.ts +6 -6
  532. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +11 -0
  533. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +7 -3
  534. package/src/resources/extensions/gsd/bootstrap/system-context.ts +13 -9
  535. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +158 -9
  536. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +27 -8
  537. package/src/resources/extensions/gsd/commands-cmux.ts +10 -6
  538. package/src/resources/extensions/gsd/commands-extensions.ts +747 -41
  539. package/src/resources/extensions/gsd/component-loader.ts +598 -0
  540. package/src/resources/extensions/gsd/component-types.ts +362 -0
  541. package/src/resources/extensions/gsd/context-store.ts +25 -8
  542. package/src/resources/extensions/gsd/detection.ts +58 -1
  543. package/src/resources/extensions/gsd/dispatch-guard.ts +26 -2
  544. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  545. package/src/resources/extensions/gsd/file-lock.ts +84 -11
  546. package/src/resources/extensions/gsd/forensics.ts +118 -1
  547. package/src/resources/extensions/gsd/gate-registry.ts +2 -2
  548. package/src/resources/extensions/gsd/git-constants.ts +30 -1
  549. package/src/resources/extensions/gsd/git-self-heal.ts +31 -0
  550. package/src/resources/extensions/gsd/git-service.ts +150 -2
  551. package/src/resources/extensions/gsd/gitignore.ts +1 -0
  552. package/src/resources/extensions/gsd/gsd-db.ts +6 -3
  553. package/src/resources/extensions/gsd/guided-flow-queue.ts +4 -1
  554. package/src/resources/extensions/gsd/guided-flow.ts +57 -14
  555. package/src/resources/extensions/gsd/journal.ts +38 -3
  556. package/src/resources/extensions/gsd/memory-extractor.ts +11 -3
  557. package/src/resources/extensions/gsd/milestone-actions.ts +18 -0
  558. package/src/resources/extensions/gsd/milestone-scope-classifier.ts +366 -0
  559. package/src/resources/extensions/gsd/milestone-summary-classifier.ts +42 -0
  560. package/src/resources/extensions/gsd/model-cost-table.ts +3 -0
  561. package/src/resources/extensions/gsd/model-router.ts +6 -0
  562. package/src/resources/extensions/gsd/native-git-bridge.ts +34 -4
  563. package/src/resources/extensions/gsd/notifications.ts +27 -15
  564. package/src/resources/extensions/gsd/preferences-validation.ts +21 -0
  565. package/src/resources/extensions/gsd/prompt-cache-optimizer.ts +4 -0
  566. package/src/resources/extensions/gsd/prompts/complete-milestone.md +6 -2
  567. package/src/resources/extensions/gsd/prompts/discuss-headless.md +23 -4
  568. package/src/resources/extensions/gsd/prompts/doctor-heal.md +5 -4
  569. package/src/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  570. package/src/resources/extensions/gsd/prompts/system.md +1 -0
  571. package/src/resources/extensions/gsd/reports.ts +5 -4
  572. package/src/resources/extensions/gsd/safety/git-checkpoint.ts +15 -0
  573. package/src/resources/extensions/gsd/service-tier.ts +5 -2
  574. package/src/resources/extensions/gsd/session-lock.ts +20 -10
  575. package/src/resources/extensions/gsd/skill-manifest.ts +175 -0
  576. package/src/resources/extensions/gsd/slice-cadence.ts +299 -0
  577. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +309 -8
  578. package/src/resources/extensions/gsd/state-transition-matrix.ts +152 -0
  579. package/src/resources/extensions/gsd/state.ts +76 -66
  580. package/src/resources/extensions/gsd/sync-lock.ts +97 -39
  581. package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +270 -0
  582. package/src/resources/extensions/gsd/tests/artifacts-table-preserved-on-cache-invalidate.test.ts +2 -1
  583. package/src/resources/extensions/gsd/tests/auto-deterministic-error-classification-4973.test.ts +341 -0
  584. package/src/resources/extensions/gsd/tests/auto-discuss-milestone-deadlock-4973.test.ts +264 -0
  585. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +135 -285
  586. package/src/resources/extensions/gsd/tests/auto-mode-guards.test.ts +79 -0
  587. package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +742 -0
  588. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +78 -0
  589. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +61 -0
  590. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +166 -0
  591. package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +4 -1
  592. package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +8 -194
  593. package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +64 -0
  594. package/src/resources/extensions/gsd/tests/auto-start-cold-db-bootstrap.test.ts +2 -2
  595. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +15 -58
  596. package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +2 -2
  597. package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +3 -2
  598. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +3 -2
  599. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +2 -1
  600. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +17 -21
  601. package/src/resources/extensions/gsd/tests/canonical-milestone-root.test.ts +108 -0
  602. package/src/resources/extensions/gsd/tests/cmux.test.ts +5 -9
  603. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +263 -0
  604. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +25 -0
  605. package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +192 -0
  606. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +2 -1
  607. package/src/resources/extensions/gsd/tests/complete-task.test.ts +16 -8
  608. package/src/resources/extensions/gsd/tests/component-loader.test.ts +589 -0
  609. package/src/resources/extensions/gsd/tests/component-types.test.ts +127 -0
  610. package/src/resources/extensions/gsd/tests/context-store.test.ts +79 -0
  611. package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +2 -1
  612. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +50 -1
  613. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +159 -0
  614. package/src/resources/extensions/gsd/tests/db-access-guardrails.test.ts +1 -0
  615. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +3 -3
  616. package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +40 -0
  617. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +91 -3
  618. package/src/resources/extensions/gsd/tests/derive-state.test.ts +4 -4
  619. package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +2 -1
  620. package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +2 -1
  621. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +14 -9
  622. package/src/resources/extensions/gsd/tests/dispatch-guard-summary-db-mismatch.test.ts +77 -0
  623. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +25 -0
  624. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +14 -0
  625. package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +3 -2
  626. package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +4 -3
  627. package/src/resources/extensions/gsd/tests/empty-content-abort-loop.test.ts +4 -3
  628. package/src/resources/extensions/gsd/tests/execution-entry-missing-context-4671.test.ts +173 -0
  629. package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +139 -129
  630. package/src/resources/extensions/gsd/tests/file-lock.test.ts +86 -12
  631. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +8 -104
  632. package/src/resources/extensions/gsd/tests/gate-state-canonicalization.test.ts +102 -0
  633. package/src/resources/extensions/gsd/tests/gate-storage.test.ts +1 -1
  634. package/src/resources/extensions/gsd/tests/google-search-stub.test.ts +131 -0
  635. package/src/resources/extensions/gsd/tests/headless-milestone-parity.test.ts +117 -0
  636. package/src/resources/extensions/gsd/tests/hook-key-parsing.test.ts +4 -55
  637. package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +7 -56
  638. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +20 -0
  639. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +30 -0
  640. package/src/resources/extensions/gsd/tests/integration/doctor-proactive.test.ts +18 -2
  641. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +3 -2
  642. package/src/resources/extensions/gsd/tests/integration/queue-completed-milestone-perf.test.ts +10 -4
  643. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +144 -7
  644. package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +4 -0
  645. package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +2 -16
  646. package/src/resources/extensions/gsd/tests/integration/worktree-e2e.test.ts +11 -0
  647. package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +9 -3
  648. package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +6 -9
  649. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +64 -0
  650. package/src/resources/extensions/gsd/tests/knowledge.test.ts +93 -1
  651. package/src/resources/extensions/gsd/tests/mcp-client-security.test.ts +47 -0
  652. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +5 -15
  653. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +227 -55
  654. package/src/resources/extensions/gsd/tests/milestone-scope-classifier.test.ts +187 -0
  655. package/src/resources/extensions/gsd/tests/milestone-status-authoritative.test.ts +3 -3
  656. package/src/resources/extensions/gsd/tests/milestone-summary-classifier.test.ts +30 -0
  657. package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +4 -2
  658. package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +9 -1
  659. package/src/resources/extensions/gsd/tests/model-router.test.ts +1 -1
  660. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +6 -48
  661. package/src/resources/extensions/gsd/tests/notification-widget.test.ts +6 -3
  662. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +59 -2
  663. package/src/resources/extensions/gsd/tests/parallel-commit-scope.test.ts +5 -0
  664. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +273 -130
  665. package/src/resources/extensions/gsd/tests/parallel-skill-prompt-integration.test.ts +150 -0
  666. package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +301 -0
  667. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +32 -1
  668. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +2 -1
  669. package/src/resources/extensions/gsd/tests/prompt-cache-optimizer.test.ts +12 -0
  670. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +15 -4
  671. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +54 -41
  672. package/src/resources/extensions/gsd/tests/queue-auto-guard.test.ts +213 -0
  673. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +3 -2
  674. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +4 -5
  675. package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +13 -7
  676. package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +75 -2
  677. package/src/resources/extensions/gsd/tests/reassess-default-optin.test.ts +132 -0
  678. package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +8 -40
  679. package/src/resources/extensions/gsd/tests/regex-hardening.test.ts +136 -256
  680. package/src/resources/extensions/gsd/tests/require-slice-discussion-dispatch.test.ts +170 -0
  681. package/src/resources/extensions/gsd/tests/research-milestone-composer.test.ts +114 -0
  682. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +6 -3
  683. package/src/resources/extensions/gsd/tests/rewrite-docs-abandon-detect.test.ts +195 -0
  684. package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +148 -0
  685. package/src/resources/extensions/gsd/tests/service-tier.test.ts +4 -0
  686. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +29 -0
  687. package/src/resources/extensions/gsd/tests/sidecar-queue.test.ts +3 -2
  688. package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +55 -95
  689. package/src/resources/extensions/gsd/tests/single-writer-v3-tool-surface.test.ts +158 -0
  690. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +120 -1
  691. package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +112 -0
  692. package/src/resources/extensions/gsd/tests/slice-cadence.test.ts +242 -0
  693. package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +3 -2
  694. package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +164 -1
  695. package/src/resources/extensions/gsd/tests/smart-entry-draft.test.ts +2 -1
  696. package/src/resources/extensions/gsd/tests/stale-dirlistcache-4648.test.ts +112 -0
  697. package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +29 -5
  698. package/src/resources/extensions/gsd/tests/state-transition-matrix.test.ts +44 -0
  699. package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +3 -3
  700. package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +11 -92
  701. package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +2 -2
  702. package/src/resources/extensions/gsd/tests/subagent-model-dispatch.test.ts +7 -6
  703. package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +102 -101
  704. package/src/resources/extensions/gsd/tests/sync-lock.test.ts +31 -0
  705. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +4 -3
  706. package/src/resources/extensions/gsd/tests/test-helpers.test.ts +98 -0
  707. package/src/resources/extensions/gsd/tests/test-helpers.ts +153 -0
  708. package/src/resources/extensions/gsd/tests/token-profile.test.ts +8 -1
  709. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +61 -1
  710. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +8 -1
  711. package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +50 -2
  712. package/src/resources/extensions/gsd/tests/turn-epoch.test.ts +162 -0
  713. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +355 -0
  714. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +258 -0
  715. package/src/resources/extensions/gsd/tests/uok-contracts.test.ts +51 -0
  716. package/src/resources/extensions/gsd/tests/uok-execution-graph.test.ts +16 -0
  717. package/src/resources/extensions/gsd/tests/uok-gate-runner.test.ts +75 -0
  718. package/src/resources/extensions/gsd/tests/uok-gitops-wiring.test.ts +49 -26
  719. package/src/resources/extensions/gsd/tests/uok-loop-adapter-writer.test.ts +65 -0
  720. package/src/resources/extensions/gsd/tests/uok-parity-report.test.ts +42 -0
  721. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +19 -2
  722. package/src/resources/extensions/gsd/tests/uok-writer.test.ts +75 -0
  723. package/src/resources/extensions/gsd/tests/validate-extension-package.test.ts +168 -0
  724. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +139 -5
  725. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +144 -80
  726. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -54
  727. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +342 -277
  728. package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +37 -29
  729. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +25 -2
  730. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +226 -266
  731. package/src/resources/extensions/gsd/tests/worktree-health-monorepo.test.ts +103 -67
  732. package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +92 -90
  733. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +238 -59
  734. package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +113 -161
  735. package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +210 -0
  736. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +262 -0
  737. package/src/resources/extensions/gsd/tests/write-gate-predicates.test.ts +186 -0
  738. package/src/resources/extensions/gsd/tests/write-gate.test.ts +7 -5
  739. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +80 -96
  740. package/src/resources/extensions/gsd/tools/complete-slice.ts +38 -0
  741. package/src/resources/extensions/gsd/tools/complete-task.ts +49 -0
  742. package/src/resources/extensions/gsd/tools/validate-milestone.ts +8 -2
  743. package/src/resources/extensions/gsd/types.ts +3 -3
  744. package/src/resources/extensions/gsd/unit-context-composer.ts +218 -0
  745. package/src/resources/extensions/gsd/unit-context-manifest.ts +574 -0
  746. package/src/resources/extensions/gsd/uok/audit.ts +20 -2
  747. package/src/resources/extensions/gsd/uok/contracts.ts +65 -0
  748. package/src/resources/extensions/gsd/uok/dispatch-envelope.ts +56 -0
  749. package/src/resources/extensions/gsd/uok/execution-graph.ts +22 -0
  750. package/src/resources/extensions/gsd/uok/gate-runner.ts +65 -5
  751. package/src/resources/extensions/gsd/uok/gitops.ts +6 -1
  752. package/src/resources/extensions/gsd/uok/loop-adapter.ts +45 -10
  753. package/src/resources/extensions/gsd/uok/parity-report.ts +84 -0
  754. package/src/resources/extensions/gsd/uok/plan-v2.ts +13 -5
  755. package/src/resources/extensions/gsd/uok/writer.ts +113 -0
  756. package/src/resources/extensions/gsd/workflow-logger.ts +22 -3
  757. package/src/resources/extensions/gsd/workflow-mcp.ts +6 -0
  758. package/src/resources/extensions/gsd/worktree-manager.ts +109 -7
  759. package/src/resources/extensions/gsd/worktree-resolver.ts +96 -9
  760. package/src/resources/extensions/gsd/worktree-telemetry.ts +322 -0
  761. package/src/resources/extensions/mcp-client/auth.ts +12 -1
  762. package/src/resources/extensions/mcp-client/index.ts +132 -11
  763. package/src/resources/extensions/mcp-client/tests/server-name-spaces.test.ts +70 -36
  764. package/src/resources/extensions/ollama/index.ts +5 -1
  765. package/src/resources/extensions/ollama/ollama-auth-mode.test.ts +123 -15
  766. package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +206 -19
  767. package/src/resources/extensions/remote-questions/manager.ts +36 -4
  768. package/src/resources/extensions/remote-questions/tests/command-polling.test.ts +200 -190
  769. package/src/resources/extensions/shared/cmux-events.ts +59 -0
  770. package/src/resources/extensions/shared/rtk-session-stats.ts +1 -2
  771. package/src/resources/extensions/shared/tests/interview-preview.test.ts +11 -3
  772. package/src/resources/extensions/voice/tests/linux-ready.test.ts +129 -113
  773. package/src/resources/skills/create-skill/SKILL.md +2 -2
  774. package/src/resources/skills/create-skill/references/gsd-skill-ecosystem.md +4 -4
  775. package/src/resources/skills/create-skill/workflows/audit-skill.md +4 -4
  776. package/src/resources/skills/create-skill/workflows/create-new-skill.md +5 -5
  777. package/dist/web/standalone/.next/server/chunks/7461.js +0 -1
  778. package/dist/web/standalone/.next/static/chunks/2826.e59e8578e2e28639.js +0 -9
  779. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts +0 -2
  780. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts.map +0 -1
  781. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js +0 -289
  782. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js.map +0 -1
  783. package/packages/pi-ai/src/utils/oauth/oauth-providers.test.ts +0 -363
  784. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +0 -143
  785. package/src/resources/extensions/gsd/tests/complete-milestone-false-merge.test.ts +0 -142
  786. package/src/resources/extensions/gsd/tests/dashboard-model-label-ordering.test.ts +0 -107
  787. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +0 -48
  788. package/src/resources/extensions/gsd/tests/forensics-context-persist.test.ts +0 -159
  789. package/src/resources/extensions/gsd/tests/forensics-db-completion.test.ts +0 -96
  790. package/src/resources/extensions/gsd/tests/forensics-dedup.test.ts +0 -79
  791. package/src/resources/extensions/gsd/tests/forensics-hook-key-parse.test.ts +0 -74
  792. package/src/resources/extensions/gsd/tests/forensics-journal.test.ts +0 -162
  793. package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +0 -38
  794. package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +0 -73
  795. package/src/resources/extensions/gsd/tests/idle-watchdog-stall-override.test.ts +0 -125
  796. package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +0 -42
  797. /package/dist/web/standalone/.next/static/{pV-mPo7rYGb5JBC09C8GG → C1zT2kEfoLhDdbWPWKrXd}/_buildManifest.js +0 -0
  798. /package/dist/web/standalone/.next/static/{pV-mPo7rYGb5JBC09C8GG → C1zT2kEfoLhDdbWPWKrXd}/_ssgManifest.js +0 -0
@@ -1,13 +1,16 @@
1
1
  /**
2
2
  * GSD Extensions Command — /gsd extensions
3
3
  *
4
- * Manage the extension registry: list, enable, disable, info.
4
+ * Manage the extension registry: list, enable, disable, info, install.
5
5
  * Self-contained — no imports outside the extensions tree (extensions are loaded
6
6
  * via jiti at runtime from ~/.gsd/agent/, not compiled by tsc).
7
7
  */
8
- import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, writeFileSync } from "node:fs";
9
- import { dirname, join } from "node:path";
10
- import { homedir } from "node:os";
8
+ import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
9
+ import { dirname, join, resolve } from "node:path";
10
+ import { homedir, tmpdir } from "node:os";
11
+ import { execFileSync } from "node:child_process";
12
+ import { lockSync, unlockSync } from "proper-lockfile";
13
+ import semver from "semver";
11
14
  const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
12
15
  // ─── Registry I/O ───────────────────────────────────────────────────────────
13
16
  function getRegistryPath() {
@@ -42,6 +45,35 @@ function saveRegistry(registry) {
42
45
  }
43
46
  catch { /* non-fatal */ }
44
47
  }
48
+ /**
49
+ * Run a registry load → mutate → save transaction under a cross-process lock.
50
+ * Prevents two concurrent `gsd extensions install/uninstall/update` invocations
51
+ * from trampling each other's registry mutations.
52
+ *
53
+ * Uses proper-lockfile.lockSync against the registry path. Directory is created
54
+ * first so locking works on fresh installs. Lock is always released via finally.
55
+ */
56
+ function withRegistryLock(mutate) {
57
+ const filePath = getRegistryPath();
58
+ mkdirSync(dirname(filePath), { recursive: true });
59
+ // lockSync requires the file to exist — ensure it does before acquiring.
60
+ if (!existsSync(filePath)) {
61
+ writeFileSync(filePath, JSON.stringify({ version: 1, entries: {} }, null, 2), "utf-8");
62
+ }
63
+ lockSync(filePath, { retries: { retries: 5, minTimeout: 50, maxTimeout: 500 } });
64
+ try {
65
+ const registry = loadRegistry();
66
+ const result = mutate(registry);
67
+ saveRegistry(registry);
68
+ return result;
69
+ }
70
+ finally {
71
+ try {
72
+ unlockSync(filePath);
73
+ }
74
+ catch { /* lock may already be gone */ }
75
+ }
76
+ }
45
77
  function isEnabled(registry, id) {
46
78
  const entry = registry.entries[id];
47
79
  if (!entry)
@@ -62,20 +94,517 @@ function readManifest(dir) {
62
94
  return null;
63
95
  }
64
96
  }
97
+ export function validateExtensionPackage(packageDir) {
98
+ const errors = [];
99
+ const warnings = [];
100
+ // Check package.json exists
101
+ const pkgPath = join(packageDir, "package.json");
102
+ if (!existsSync(pkgPath)) {
103
+ return { valid: false, errors: ["package.json not found"], warnings };
104
+ }
105
+ let pkg;
106
+ try {
107
+ pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
108
+ }
109
+ catch {
110
+ return { valid: false, errors: ["package.json is invalid JSON"], warnings };
111
+ }
112
+ // (a) gsd.extension: true marker (D-12a)
113
+ const gsdField = pkg.gsd;
114
+ if (gsdField?.extension !== true) {
115
+ errors.push('package.json missing "gsd": { "extension": true }');
116
+ }
117
+ // (b) pi.extensions entry paths exist and are resolvable (D-12b)
118
+ const piField = pkg.pi;
119
+ const piExtensions = piField?.extensions;
120
+ if (!Array.isArray(piExtensions) || piExtensions.length === 0) {
121
+ errors.push('package.json missing "pi": { "extensions": [...] }');
122
+ }
123
+ else {
124
+ for (const entry of piExtensions) {
125
+ if (typeof entry === "string") {
126
+ const resolved = join(packageDir, entry);
127
+ if (!existsSync(resolved)) {
128
+ errors.push(`pi.extensions entry not found: ${entry}`);
129
+ }
130
+ }
131
+ }
132
+ }
133
+ // (c) @gsd/* packages must be in peerDependencies, not dependencies/devDependencies (D-12c)
134
+ // Mirrors validateExtensionManifest below and extension-validator.ts:checkDependencyPlacement.
135
+ for (const field of ["dependencies", "devDependencies"]) {
136
+ const deps = pkg[field] ?? {};
137
+ for (const dep of Object.keys(deps)) {
138
+ if (dep.startsWith("@gsd/")) {
139
+ errors.push(`"${dep}" must be in peerDependencies, not ${field}`);
140
+ }
141
+ }
142
+ }
143
+ return { valid: errors.length === 0, errors, warnings };
144
+ }
65
145
  function discoverManifests() {
66
- const extDir = getAgentExtensionsDir();
67
146
  const manifests = new Map();
68
- if (!existsSync(extDir))
69
- return manifests;
70
- for (const entry of readdirSync(extDir, { withFileTypes: true })) {
71
- if (!entry.isDirectory() && !entry.isSymbolicLink())
147
+ // Scan both bundled/agent dir and user-installed dir so CLI (list/info/
148
+ // enable/disable) sees the same set the loader will merge at runtime.
149
+ // Bundled entries are scanned first so user-installed IDs override on collision.
150
+ const dirs = [getAgentExtensionsDir(), getInstalledExtDir()];
151
+ for (const extDir of dirs) {
152
+ if (!existsSync(extDir))
72
153
  continue;
73
- const m = readManifest(join(extDir, entry.name));
74
- if (m)
75
- manifests.set(m.id, m);
154
+ for (const entry of readdirSync(extDir, { withFileTypes: true })) {
155
+ if (!entry.isDirectory() && !entry.isSymbolicLink())
156
+ continue;
157
+ const m = readManifest(join(extDir, entry.name));
158
+ if (m)
159
+ manifests.set(m.id, m);
160
+ }
76
161
  }
77
162
  return manifests;
78
163
  }
164
+ function getInstalledExtDir() {
165
+ return join(gsdHome, "extensions");
166
+ }
167
+ // Source: derived from npm/git URL conventions (from RESEARCH.md)
168
+ function detectInstallType(specifier) {
169
+ if (specifier.startsWith("/") ||
170
+ specifier.startsWith("./") ||
171
+ specifier.startsWith("../") ||
172
+ specifier.startsWith("~/"))
173
+ return "local";
174
+ if (specifier.startsWith("git+") ||
175
+ specifier.startsWith("git://") ||
176
+ specifier.startsWith("github:") ||
177
+ specifier.startsWith("gitlab:") ||
178
+ specifier.startsWith("bitbucket:") ||
179
+ (specifier.startsWith("https://") && specifier.endsWith(".git")) ||
180
+ (specifier.startsWith("http://") && specifier.endsWith(".git")))
181
+ return "git";
182
+ return "npm";
183
+ }
184
+ function validateExtensionManifest(pkg, opts = {}) {
185
+ const errors = [];
186
+ // Check gsd.extension === true (strict)
187
+ if (typeof pkg !== "object" || pkg === null) {
188
+ errors.push({ code: "MISSING_GSD_MARKER", message: 'package.json must declare "gsd": { "extension": true } to be recognized as a GSD extension.', field: "gsd.extension" });
189
+ }
190
+ else {
191
+ const obj = pkg;
192
+ const gsd = obj.gsd;
193
+ if (typeof gsd !== "object" || gsd === null || gsd.extension !== true) {
194
+ errors.push({ code: "MISSING_GSD_MARKER", message: 'package.json must declare "gsd": { "extension": true } to be recognized as a GSD extension.', field: "gsd.extension" });
195
+ }
196
+ }
197
+ // Check namespace reservation
198
+ if (opts.extensionId && opts.extensionId.startsWith("gsd.") && opts.allowGsdNamespace !== true) {
199
+ errors.push({ code: "RESERVED_NAMESPACE", message: `Extension ID "${opts.extensionId}" is reserved for GSD core extensions. Use a different namespace for community extensions.`, field: "extensionId" });
200
+ }
201
+ // Check dependency placement
202
+ if (typeof pkg === "object" && pkg !== null) {
203
+ const obj = pkg;
204
+ for (const field of ["dependencies", "devDependencies"]) {
205
+ const deps = obj[field];
206
+ if (typeof deps === "object" && deps !== null) {
207
+ for (const pkgName of Object.keys(deps)) {
208
+ if (pkgName.startsWith("@gsd/")) {
209
+ errors.push({ code: "WRONG_DEP_FIELD", message: `"${pkgName}" must not appear in "${field}". Move it to "peerDependencies".`, field });
210
+ }
211
+ }
212
+ }
213
+ }
214
+ }
215
+ return { valid: errors.length === 0, errors };
216
+ }
217
+ // ─── Post-install convergence ────────────────────────────────────────────────
218
+ /**
219
+ * Allowed characters for an extension id when used as a path segment.
220
+ * Rejects anything that could enable traversal or escape (slashes, "..", backslashes).
221
+ */
222
+ const SAFE_EXTENSION_ID_RE = /^[A-Za-z0-9._-]+$/;
223
+ function isSafeExtensionId(id) {
224
+ if (!id || id === "." || id === "..")
225
+ return false;
226
+ if (id.includes("/") || id.includes("\\") || id.includes(".."))
227
+ return false;
228
+ return SAFE_EXTENSION_ID_RE.test(id);
229
+ }
230
+ /**
231
+ * Post-install convergence: validate package and read manifest.
232
+ * Returns the (validated) extension ID and manifest on success, or null on failure.
233
+ * Caller is responsible for writing the registry entry *after* the final commit
234
+ * rename succeeds so a failed move doesn't leave a dangling registry entry.
235
+ */
236
+ function postInstallValidate(destPath, specifier, ctx) {
237
+ // Read package.json
238
+ const pkgJsonPath = join(destPath, "package.json");
239
+ if (!existsSync(pkgJsonPath)) {
240
+ ctx.ui.notify(`Cannot install "${specifier}": no package.json found.`, "error");
241
+ return null;
242
+ }
243
+ let pkgJson;
244
+ try {
245
+ pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
246
+ }
247
+ catch {
248
+ ctx.ui.notify(`Cannot install "${specifier}": malformed package.json.`, "error");
249
+ return null;
250
+ }
251
+ // Read extension-manifest.json for the ID
252
+ const manifest = readManifest(destPath);
253
+ const extensionId = manifest?.id;
254
+ // Validate
255
+ const validation = validateExtensionManifest(pkgJson, { extensionId });
256
+ if (!validation.valid) {
257
+ const msgs = validation.errors.map(e => e.message).join("\n");
258
+ ctx.ui.notify(`Cannot install "${specifier}": ${msgs}`, "error");
259
+ return null;
260
+ }
261
+ if (!manifest || !extensionId) {
262
+ ctx.ui.notify(`Cannot install "${specifier}": no extension-manifest.json with valid id found.`, "error");
263
+ return null;
264
+ }
265
+ // The id from the manifest is used as a path segment under installedExtDir.
266
+ // Reject unsafe ids before the caller performs any path joins.
267
+ if (!isSafeExtensionId(extensionId)) {
268
+ ctx.ui.notify(`Cannot install "${specifier}": extension id "${extensionId}" contains unsafe characters (allowed: alphanumerics, ".", "-", "_").`, "error");
269
+ return null;
270
+ }
271
+ return { id: extensionId, manifest };
272
+ }
273
+ /**
274
+ * Write the registry entry for a freshly-installed extension. Called after the
275
+ * final destination commit succeeds so a failed rename can't leave a stale entry.
276
+ */
277
+ function writeInstalledRegistryEntry(id, manifest, specifier, installType) {
278
+ withRegistryLock((registry) => {
279
+ registry.entries[id] = {
280
+ id,
281
+ enabled: true,
282
+ source: "user",
283
+ version: manifest.version,
284
+ installedFrom: specifier,
285
+ installType,
286
+ };
287
+ });
288
+ }
289
+ // ─── Uninstall helpers ───────────────────────────────────────────────────────
290
+ /**
291
+ * Scan installed extensions to find which ones depend on the target ID.
292
+ * Used for dependency warning on uninstall (D-06).
293
+ */
294
+ function findDependents(targetId, installedExtDir) {
295
+ const dependents = [];
296
+ if (!existsSync(installedExtDir))
297
+ return dependents;
298
+ for (const entry of readdirSync(installedExtDir, { withFileTypes: true })) {
299
+ if (!entry.isDirectory())
300
+ continue;
301
+ const manifest = readManifest(join(installedExtDir, entry.name));
302
+ if (!manifest)
303
+ continue;
304
+ if (manifest.dependencies?.extensions?.includes(targetId)) {
305
+ dependents.push(manifest.id);
306
+ }
307
+ }
308
+ return dependents;
309
+ }
310
+ function handleUninstall(id, ctx) {
311
+ if (!id) {
312
+ ctx.ui.notify("Usage: /gsd extensions uninstall <id>", "warning");
313
+ return;
314
+ }
315
+ // Hold the registry lock for the entire uninstall transaction so a concurrent
316
+ // install can't add or re-enable `id` while we're in the middle of removing it.
317
+ const result = withRegistryLock((registry) => {
318
+ const entry = registry.entries[id];
319
+ // Check if extension exists and is user-installed
320
+ if (!entry || entry.source !== "user") {
321
+ return { ok: false, reason: "not-found" };
322
+ }
323
+ const installedExtDir = getInstalledExtDir();
324
+ const extDir = join(installedExtDir, id);
325
+ // Check for dependents and warn (D-06: warn-then-proceed)
326
+ const dependents = findDependents(id, installedExtDir);
327
+ // Remove directory first, then registry entry (Pitfall 4 from RESEARCH.md)
328
+ // If rm fails, do NOT remove registry entry — leaves a recoverable state
329
+ try {
330
+ if (existsSync(extDir)) {
331
+ rmSync(extDir, { recursive: true, force: true });
332
+ }
333
+ }
334
+ catch (err) {
335
+ const msg = err instanceof Error ? err.message : String(err);
336
+ return { ok: false, reason: "rm-failed", msg };
337
+ }
338
+ // Remove registry entry (D-07)
339
+ delete registry.entries[id];
340
+ return { ok: true, dependents };
341
+ });
342
+ if (!result.ok) {
343
+ if (result.reason === "not-found") {
344
+ ctx.ui.notify(`Extension "${id}" not found in registry. Run /gsd extensions list to see installed extensions.`, "warning");
345
+ }
346
+ else if (result.reason === "rm-failed") {
347
+ ctx.ui.notify(`Failed to remove extension directory for "${id}": ${result.msg}`, "error");
348
+ }
349
+ return;
350
+ }
351
+ if (result.dependents.length > 0) {
352
+ ctx.ui.notify(`Warning: the following installed extensions depend on "${id}": ${result.dependents.join(", ")}. Removed anyway.`, "warning");
353
+ }
354
+ ctx.ui.notify(`Uninstalled "${id}". Restart GSD to deactivate.`, "info");
355
+ }
356
+ // ─── Update subcommand ───────────────────────────────────────────────────────
357
+ async function getLatestNpmVersion(packageName) {
358
+ try {
359
+ const res = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {
360
+ signal: AbortSignal.timeout(5000),
361
+ });
362
+ if (!res.ok)
363
+ return null;
364
+ const data = await res.json();
365
+ return data.version ?? null;
366
+ }
367
+ catch {
368
+ return null;
369
+ }
370
+ }
371
+ async function handleUpdate(id, ctx) {
372
+ const registry = loadRegistry();
373
+ if (id) {
374
+ // Update single extension (D-12)
375
+ await updateSingleExtension(id, registry, ctx);
376
+ }
377
+ else {
378
+ // Update all installed extensions (D-11)
379
+ await updateAllExtensions(registry, ctx);
380
+ }
381
+ }
382
+ async function updateSingleExtension(id, registry, ctx) {
383
+ const entry = registry.entries[id];
384
+ if (!entry || entry.source !== "user") {
385
+ ctx.ui.notify(`Extension "${id}" not found in registry. Run /gsd extensions list to see installed extensions.`, "warning");
386
+ return;
387
+ }
388
+ // Git and local installs: "reinstall to update" hint (D-10, D-12)
389
+ if (entry.installType !== "npm") {
390
+ const source = entry.installType ?? "unknown";
391
+ const hint = entry.installedFrom ? `gsd extensions install ${entry.installedFrom}` : `gsd extensions install <specifier>`;
392
+ ctx.ui.notify(`"${id}" was installed from ${source}. Reinstall to update: ${hint}`, "warning");
393
+ return;
394
+ }
395
+ // npm extension: check for newer version (D-09)
396
+ const current = entry.version ?? "0.0.0";
397
+ const specifier = entry.installedFrom;
398
+ if (!specifier) {
399
+ ctx.ui.notify(`"${id}" has no recorded install source. Reinstall manually.`, "warning");
400
+ return;
401
+ }
402
+ // Split npm specifier into name + optional pin.
403
+ // Scoped (`@scope/name[@version]`) vs unscoped (`name[@version]`).
404
+ const { name: packageName, pin } = parseNpmSpecifier(specifier);
405
+ // Pinned installs: the user explicitly requested a specific version. Don't
406
+ // silently upgrade past the pin — tell them to re-install with a new pin.
407
+ if (pin) {
408
+ ctx.ui.notify(`"${id}" was installed with a pinned version (${pin}). To update, run: gsd extensions install ${packageName}@<new-version>`, "info");
409
+ return;
410
+ }
411
+ const latest = await getLatestNpmVersion(packageName);
412
+ if (!latest) {
413
+ ctx.ui.notify(`Could not fetch latest version for "${id}".`, "warning");
414
+ return;
415
+ }
416
+ if (semver.gt(latest, current)) {
417
+ ctx.ui.notify(`Updating "${id}": v${current} → v${latest}...`, "info");
418
+ await handleInstall(packageName, ctx);
419
+ }
420
+ else {
421
+ ctx.ui.notify(`"${id}" is already at the latest version (v${current}).`, "info");
422
+ }
423
+ }
424
+ /**
425
+ * Parse an npm specifier into its package name and optional version pin.
426
+ * Handles scoped (`@scope/name[@version]`) and unscoped (`name[@version]`).
427
+ */
428
+ function parseNpmSpecifier(specifier) {
429
+ const isScoped = specifier.startsWith("@");
430
+ const searchFrom = isScoped ? specifier.indexOf("/") + 1 : 0;
431
+ const atIdx = specifier.indexOf("@", searchFrom);
432
+ if (atIdx === -1)
433
+ return { name: specifier, pin: null };
434
+ return { name: specifier.slice(0, atIdx), pin: specifier.slice(atIdx + 1) };
435
+ }
436
+ async function updateAllExtensions(registry, ctx) {
437
+ // Find all user-installed extensions
438
+ const userEntries = Object.values(registry.entries).filter(e => e.source === "user");
439
+ if (userEntries.length === 0) {
440
+ ctx.ui.notify("No user-installed extensions found. Use: gsd extensions install <package> to add one.", "warning");
441
+ return;
442
+ }
443
+ ctx.ui.notify(`Checking ${userEntries.length} installed extension(s) for updates...`, "info");
444
+ let updated = 0;
445
+ let skipped = 0;
446
+ for (const entry of userEntries) {
447
+ // Skip non-npm installs (D-11)
448
+ if (entry.installType !== "npm") {
449
+ const source = entry.installType ?? "unknown";
450
+ ctx.ui.notify(` ${entry.id}: installed from ${source} — reinstall to update`, "info");
451
+ skipped++;
452
+ continue;
453
+ }
454
+ const current = entry.version ?? "0.0.0";
455
+ const packageName = entry.installedFrom;
456
+ if (!packageName) {
457
+ ctx.ui.notify(` ${entry.id}: no recorded install source — skip`, "info");
458
+ skipped++;
459
+ continue;
460
+ }
461
+ const latest = await getLatestNpmVersion(packageName);
462
+ if (!latest) {
463
+ ctx.ui.notify(` ${entry.id}: could not fetch latest version — skip`, "info");
464
+ skipped++;
465
+ continue;
466
+ }
467
+ if (semver.gt(latest, current)) {
468
+ ctx.ui.notify(` ${entry.id}: v${current} → v${latest} (updating)`, "info");
469
+ await handleInstall(packageName, ctx);
470
+ updated++;
471
+ }
472
+ else {
473
+ ctx.ui.notify(` ${entry.id}: v${current} (already up to date)`, "info");
474
+ }
475
+ }
476
+ ctx.ui.notify(`Updated ${updated} extension(s). ${skipped} skipped (git/local — reinstall to update).`, "info");
477
+ }
478
+ // ─── Install subcommand ──────────────────────────────────────────────────────
479
+ async function handleInstall(specifier, ctx) {
480
+ if (!specifier) {
481
+ ctx.ui.notify("Usage: /gsd extensions install <npm-package|git-url|local-path>", "warning");
482
+ return;
483
+ }
484
+ const installType = detectInstallType(specifier);
485
+ const installedExtDir = getInstalledExtDir();
486
+ mkdirSync(installedExtDir, { recursive: true });
487
+ process.stderr.write(`Installing ${specifier}...\n`);
488
+ if (installType === "npm") {
489
+ installFromNpm(specifier, installedExtDir, ctx);
490
+ }
491
+ else if (installType === "git") {
492
+ installFromGit(specifier, installedExtDir, ctx);
493
+ }
494
+ else {
495
+ installFromLocal(specifier, installedExtDir, ctx);
496
+ }
497
+ }
498
+ function installFromNpm(specifier, installedExtDir, ctx) {
499
+ // packDir holds the tarball in tmpdir(). The *extractDir* is staged inside
500
+ // installedExtDir so the final renameSync to destPath stays on a single
501
+ // filesystem (avoids EXDEV when tmpdir() and ~/.gsd live on different mounts).
502
+ const packDir = mkdtempSync(join(tmpdir(), "gsd-install-"));
503
+ let extractDir = null;
504
+ try {
505
+ // Step 1: npm pack to tmpdir (D-01, D-05)
506
+ execFileSync("npm", ["pack", specifier, "--pack-destination", packDir, "--ignore-scripts"], {
507
+ stdio: "pipe",
508
+ encoding: "utf-8",
509
+ });
510
+ // Step 2: Find the tarball
511
+ const tgzFile = readdirSync(packDir).find(f => f.endsWith(".tgz"));
512
+ if (!tgzFile)
513
+ throw new Error("npm pack produced no tarball");
514
+ // Step 3: Extract via tar into a staging dir *inside* installedExtDir
515
+ extractDir = mkdtempSync(join(installedExtDir, "tmp-npm-"));
516
+ execFileSync("tar", ["xzf", join(packDir, tgzFile), "-C", extractDir, "--strip-components=1"], { stdio: "pipe" });
517
+ // Step 4: Validate and get extension ID
518
+ const validated = postInstallValidate(extractDir, specifier, ctx);
519
+ if (!validated) {
520
+ return; // Error already notified
521
+ }
522
+ // Step 5: Move to final destination — same filesystem as extractDir
523
+ const destPath = join(installedExtDir, validated.id);
524
+ if (existsSync(destPath)) {
525
+ rmSync(destPath, { recursive: true, force: true });
526
+ }
527
+ renameSync(extractDir, destPath);
528
+ extractDir = null; // Successfully moved; skip cleanup
529
+ // Step 6: Commit the registry entry only after the rename succeeds.
530
+ writeInstalledRegistryEntry(validated.id, validated.manifest, specifier, "npm");
531
+ ctx.ui.notify(`Installed "${validated.id}" v${validated.manifest.version ?? "unknown"}. Restart GSD to activate.`, "info");
532
+ }
533
+ catch (err) {
534
+ const msg = err instanceof Error ? err.message : String(err);
535
+ ctx.ui.notify(`Failed to install "${specifier}": ${msg}`, "error");
536
+ }
537
+ finally {
538
+ if (extractDir && existsSync(extractDir)) {
539
+ try {
540
+ rmSync(extractDir, { recursive: true, force: true });
541
+ }
542
+ catch { /* best-effort */ }
543
+ }
544
+ rmSync(packDir, { recursive: true, force: true });
545
+ }
546
+ }
547
+ function installFromGit(gitUrl, installedExtDir, ctx) {
548
+ // Clone into temp dir, validate, then rename to real ID (D-02)
549
+ const tmpDir = join(installedExtDir, `__installing-${Date.now()}`);
550
+ try {
551
+ execFileSync("git", ["clone", "--depth=1", gitUrl, tmpDir], { stdio: "pipe" });
552
+ // Remove .git directory — not needed after clone
553
+ const dotGit = join(tmpDir, ".git");
554
+ if (existsSync(dotGit)) {
555
+ rmSync(dotGit, { recursive: true, force: true });
556
+ }
557
+ const validated = postInstallValidate(tmpDir, gitUrl, ctx);
558
+ if (!validated) {
559
+ rmSync(tmpDir, { recursive: true, force: true });
560
+ return;
561
+ }
562
+ const destPath = join(installedExtDir, validated.id);
563
+ if (existsSync(destPath)) {
564
+ rmSync(destPath, { recursive: true, force: true });
565
+ }
566
+ renameSync(tmpDir, destPath);
567
+ writeInstalledRegistryEntry(validated.id, validated.manifest, gitUrl, "git");
568
+ ctx.ui.notify(`Installed "${validated.id}" v${validated.manifest.version ?? "unknown"}. Restart GSD to activate.`, "info");
569
+ }
570
+ catch (err) {
571
+ if (existsSync(tmpDir))
572
+ rmSync(tmpDir, { recursive: true, force: true });
573
+ const msg = err instanceof Error ? err.message : String(err);
574
+ ctx.ui.notify(`Failed to install "${gitUrl}": ${msg}`, "error");
575
+ }
576
+ }
577
+ function installFromLocal(localPath, installedExtDir, ctx) {
578
+ // Resolve path and copy (not symlink) per D-03
579
+ const sourcePath = resolve(localPath.startsWith("~/") ? join(homedir(), localPath.slice(2)) : localPath);
580
+ if (!existsSync(sourcePath)) {
581
+ ctx.ui.notify(`Cannot install "${localPath}": path does not exist.`, "error");
582
+ return;
583
+ }
584
+ // Copy to temp dir first, validate, then rename
585
+ const tmpDir = join(installedExtDir, `__installing-${Date.now()}`);
586
+ try {
587
+ cpSync(sourcePath, tmpDir, { recursive: true });
588
+ const validated = postInstallValidate(tmpDir, localPath, ctx);
589
+ if (!validated) {
590
+ rmSync(tmpDir, { recursive: true, force: true });
591
+ return;
592
+ }
593
+ const destPath = join(installedExtDir, validated.id);
594
+ if (existsSync(destPath)) {
595
+ rmSync(destPath, { recursive: true, force: true });
596
+ }
597
+ renameSync(tmpDir, destPath);
598
+ writeInstalledRegistryEntry(validated.id, validated.manifest, localPath, "local");
599
+ ctx.ui.notify(`Installed "${validated.id}" v${validated.manifest.version ?? "unknown"}. Restart GSD to activate.`, "info");
600
+ }
601
+ catch (err) {
602
+ if (existsSync(tmpDir))
603
+ rmSync(tmpDir, { recursive: true, force: true });
604
+ const msg = err instanceof Error ? err.message : String(err);
605
+ ctx.ui.notify(`Failed to install "${localPath}": ${msg}`, "error");
606
+ }
607
+ }
79
608
  // ─── Command Handler ────────────────────────────────────────────────────────
80
609
  export async function handleExtensions(args, ctx) {
81
610
  const parts = args.split(/\s+/).filter(Boolean);
@@ -96,7 +625,23 @@ export async function handleExtensions(args, ctx) {
96
625
  handleInfo(parts[1], ctx);
97
626
  return;
98
627
  }
99
- ctx.ui.notify(`Unknown: /gsd extensions ${subCmd}. Usage: /gsd extensions [list|enable|disable|info]`, "warning");
628
+ if (subCmd === "install") {
629
+ await handleInstall(parts[1], ctx);
630
+ return;
631
+ }
632
+ if (subCmd === "uninstall") {
633
+ handleUninstall(parts[1], ctx);
634
+ return;
635
+ }
636
+ if (subCmd === "update") {
637
+ await handleUpdate(parts[1], ctx);
638
+ return;
639
+ }
640
+ if (subCmd === "validate") {
641
+ handleValidate(parts[1], ctx);
642
+ return;
643
+ }
644
+ ctx.ui.notify(`Unknown: /gsd extensions ${subCmd}. Usage: /gsd extensions [list|enable|disable|info|install|uninstall|update|validate]`, "warning");
100
645
  }
101
646
  function handleList(ctx) {
102
647
  const manifests = discoverManifests();
@@ -128,6 +673,18 @@ function handleList(ctx) {
128
673
  padRight(m.tier, 10) +
129
674
  padRight(String(toolCount), 7) +
130
675
  String(cmdCount));
676
+ // Show source indicator and install info for user-installed extensions
677
+ const regEntry = registry.entries[m.id];
678
+ if (regEntry?.source === "user") {
679
+ // Append [user] tag to the last line
680
+ const lastLine = lines[lines.length - 1];
681
+ lines[lines.length - 1] = lastLine + " [user]";
682
+ if (regEntry.installedFrom) {
683
+ const typePrefix = regEntry.installType ? `${regEntry.installType}:` : "";
684
+ const versionSuffix = regEntry.version ? `@${regEntry.version}` : "";
685
+ lines.push(` installed from: ${typePrefix}${regEntry.installedFrom}${versionSuffix}`);
686
+ }
687
+ }
131
688
  if (!enabled) {
132
689
  lines.push(` ↳ gsd extensions enable ${m.id}`);
133
690
  }
@@ -144,21 +701,24 @@ function handleEnable(id, ctx) {
144
701
  ctx.ui.notify(`Extension "${id}" not found. Run /gsd extensions list to see available extensions.`, "warning");
145
702
  return;
146
703
  }
147
- const registry = loadRegistry();
148
- if (isEnabled(registry, id)) {
704
+ const alreadyEnabled = withRegistryLock((registry) => {
705
+ if (isEnabled(registry, id))
706
+ return true;
707
+ const entry = registry.entries[id];
708
+ if (entry) {
709
+ entry.enabled = true;
710
+ delete entry.disabledAt;
711
+ delete entry.disabledReason;
712
+ }
713
+ else {
714
+ registry.entries[id] = { id, enabled: true, source: "bundled" };
715
+ }
716
+ return false;
717
+ });
718
+ if (alreadyEnabled) {
149
719
  ctx.ui.notify(`Extension "${id}" is already enabled.`, "info");
150
720
  return;
151
721
  }
152
- const entry = registry.entries[id];
153
- if (entry) {
154
- entry.enabled = true;
155
- delete entry.disabledAt;
156
- delete entry.disabledReason;
157
- }
158
- else {
159
- registry.entries[id] = { id, enabled: true, source: "bundled" };
160
- }
161
- saveRegistry(registry);
162
722
  ctx.ui.notify(`Enabled "${id}". Restart GSD to activate.`, "info");
163
723
  }
164
724
  function handleDisable(id, reason, ctx) {
@@ -176,27 +736,30 @@ function handleDisable(id, reason, ctx) {
176
736
  ctx.ui.notify(`Cannot disable "${id}" — it is a core extension.`, "warning");
177
737
  return;
178
738
  }
179
- const registry = loadRegistry();
180
- if (!isEnabled(registry, id)) {
739
+ const alreadyDisabled = withRegistryLock((registry) => {
740
+ if (!isEnabled(registry, id))
741
+ return true;
742
+ const entry = registry.entries[id];
743
+ if (entry) {
744
+ entry.enabled = false;
745
+ entry.disabledAt = new Date().toISOString();
746
+ entry.disabledReason = reason || undefined;
747
+ }
748
+ else {
749
+ registry.entries[id] = {
750
+ id,
751
+ enabled: false,
752
+ source: "bundled",
753
+ disabledAt: new Date().toISOString(),
754
+ disabledReason: reason || undefined,
755
+ };
756
+ }
757
+ return false;
758
+ });
759
+ if (alreadyDisabled) {
181
760
  ctx.ui.notify(`Extension "${id}" is already disabled.`, "info");
182
761
  return;
183
762
  }
184
- const entry = registry.entries[id];
185
- if (entry) {
186
- entry.enabled = false;
187
- entry.disabledAt = new Date().toISOString();
188
- entry.disabledReason = reason || undefined;
189
- }
190
- else {
191
- registry.entries[id] = {
192
- id,
193
- enabled: false,
194
- source: "bundled",
195
- disabledAt: new Date().toISOString(),
196
- disabledReason: reason || undefined,
197
- };
198
- }
199
- saveRegistry(registry);
200
763
  ctx.ui.notify(`Disabled "${id}". Restart GSD to deactivate.`, "info");
201
764
  }
202
765
  function handleInfo(id, ctx) {
@@ -227,6 +790,15 @@ function handleInfo(id, ctx) {
227
790
  if (entry?.disabledReason) {
228
791
  lines.push(` Reason: ${entry.disabledReason}`);
229
792
  }
793
+ // Phase 8 fields for user-installed extensions (per UI-SPEC)
794
+ if (entry?.source === "user") {
795
+ if (entry.installedFrom) {
796
+ lines.push(` Installed from: ${entry.installedFrom}`);
797
+ }
798
+ if (entry.installType) {
799
+ lines.push(` Install type: ${entry.installType}`);
800
+ }
801
+ }
230
802
  if (manifest.provides) {
231
803
  lines.push("");
232
804
  lines.push(" Provides:");
@@ -255,6 +827,25 @@ function handleInfo(id, ctx) {
255
827
  }
256
828
  ctx.ui.notify(lines.join("\n"), "info");
257
829
  }
830
+ function handleValidate(path, ctx) {
831
+ if (!path) {
832
+ ctx.ui.notify("Usage: /gsd extensions validate <path>", "warning");
833
+ return;
834
+ }
835
+ const resolved = resolve(path);
836
+ if (!existsSync(resolved)) {
837
+ ctx.ui.notify(`Path not found: ${resolved}`, "warning");
838
+ return;
839
+ }
840
+ const result = validateExtensionPackage(resolved);
841
+ if (result.valid) {
842
+ ctx.ui.notify(`Valid extension package: ${resolved}`, "info");
843
+ }
844
+ else {
845
+ ctx.ui.notify(`Invalid extension package: ${resolved}\n` +
846
+ result.errors.map(e => ` - ${e}`).join("\n"), "warning");
847
+ }
848
+ }
258
849
  function padRight(str, len) {
259
850
  return str.length >= len ? str + " " : str + " ".repeat(len - str.length);
260
851
  }