gsd-pi 2.29.0-dev.953d788 → 2.29.0-dev.f08b4fe

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 (316) hide show
  1. package/README.md +24 -17
  2. package/dist/extension-registry.d.ts +63 -0
  3. package/dist/extension-registry.js +166 -0
  4. package/dist/headless.js +4 -0
  5. package/dist/loader.js +10 -1
  6. package/dist/resource-loader.js +11 -1
  7. package/dist/resources/extensions/async-jobs/extension-manifest.json +13 -0
  8. package/dist/resources/extensions/bg-shell/extension-manifest.json +14 -0
  9. package/dist/resources/extensions/bg-shell/process-manager.ts +13 -0
  10. package/dist/resources/extensions/browser-tools/extension-manifest.json +37 -0
  11. package/dist/resources/extensions/context7/extension-manifest.json +12 -0
  12. package/dist/resources/extensions/google-search/extension-manifest.json +12 -0
  13. package/dist/resources/extensions/gsd/auto-dashboard.ts +217 -65
  14. package/dist/resources/extensions/gsd/auto-dispatch.ts +32 -3
  15. package/dist/resources/extensions/gsd/auto-post-unit.ts +45 -13
  16. package/dist/resources/extensions/gsd/auto-prompts.ts +40 -17
  17. package/dist/resources/extensions/gsd/auto-recovery.ts +18 -23
  18. package/dist/resources/extensions/gsd/auto-start.ts +18 -32
  19. package/dist/resources/extensions/gsd/auto-worktree.ts +21 -182
  20. package/dist/resources/extensions/gsd/auto.ts +2 -24
  21. package/dist/resources/extensions/gsd/captures.ts +4 -10
  22. package/dist/resources/extensions/gsd/commands-extensions.ts +328 -0
  23. package/dist/resources/extensions/gsd/commands-handlers.ts +22 -2
  24. package/dist/resources/extensions/gsd/commands-logs.ts +13 -14
  25. package/dist/resources/extensions/gsd/commands-prefs-wizard.ts +44 -14
  26. package/dist/resources/extensions/gsd/commands-workflow-templates.ts +544 -0
  27. package/dist/resources/extensions/gsd/commands.ts +108 -24
  28. package/dist/resources/extensions/gsd/dashboard-overlay.ts +2 -1
  29. package/dist/resources/extensions/gsd/detection.ts +2 -1
  30. package/dist/resources/extensions/gsd/doctor-checks.ts +49 -1
  31. package/dist/resources/extensions/gsd/doctor-types.ts +3 -1
  32. package/dist/resources/extensions/gsd/extension-manifest.json +18 -0
  33. package/dist/resources/extensions/gsd/forensics.ts +2 -2
  34. package/dist/resources/extensions/gsd/git-service.ts +3 -2
  35. package/dist/resources/extensions/gsd/gitignore.ts +9 -63
  36. package/dist/resources/extensions/gsd/gsd-db.ts +1 -165
  37. package/dist/resources/extensions/gsd/guided-flow.ts +8 -5
  38. package/dist/resources/extensions/gsd/index.ts +3 -3
  39. package/dist/resources/extensions/gsd/json-persistence.ts +16 -1
  40. package/dist/resources/extensions/gsd/md-importer.ts +3 -2
  41. package/dist/resources/extensions/gsd/mechanical-completion.ts +430 -0
  42. package/dist/resources/extensions/gsd/migrate/command.ts +3 -2
  43. package/dist/resources/extensions/gsd/migrate/writer.ts +2 -1
  44. package/dist/resources/extensions/gsd/migrate-external.ts +123 -0
  45. package/dist/resources/extensions/gsd/paths.ts +24 -2
  46. package/dist/resources/extensions/gsd/post-unit-hooks.ts +6 -5
  47. package/dist/resources/extensions/gsd/preferences-models.ts +7 -1
  48. package/dist/resources/extensions/gsd/preferences-validation.ts +2 -1
  49. package/dist/resources/extensions/gsd/preferences.ts +10 -5
  50. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +4 -2
  51. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
  52. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +26 -2
  53. package/dist/resources/extensions/gsd/prompts/plan-slice.md +15 -1
  54. package/dist/resources/extensions/gsd/prompts/workflow-start.md +28 -0
  55. package/dist/resources/extensions/gsd/queue-order.ts +10 -11
  56. package/dist/resources/extensions/gsd/repo-identity.ts +148 -0
  57. package/dist/resources/extensions/gsd/resource-version.ts +99 -0
  58. package/dist/resources/extensions/gsd/session-forensics.ts +4 -3
  59. package/dist/resources/extensions/gsd/session-status-io.ts +23 -41
  60. package/dist/resources/extensions/gsd/tests/activity-log.test.ts +2 -2
  61. package/dist/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +1 -1
  62. package/dist/resources/extensions/gsd/tests/auto-recovery.test.ts +3 -3
  63. package/dist/resources/extensions/gsd/tests/auto-skip-loop.test.ts +1 -1
  64. package/dist/resources/extensions/gsd/tests/auto-worktree.test.ts +0 -58
  65. package/dist/resources/extensions/gsd/tests/doctor-runtime.test.ts +3 -4
  66. package/dist/resources/extensions/gsd/tests/extension-selector-separator.test.ts +60 -38
  67. package/dist/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +5 -18
  68. package/dist/resources/extensions/gsd/tests/git-service.test.ts +10 -37
  69. package/dist/resources/extensions/gsd/tests/knowledge.test.ts +4 -4
  70. package/dist/resources/extensions/gsd/tests/mechanical-completion.test.ts +356 -0
  71. package/dist/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +1 -1
  72. package/dist/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +1 -0
  73. package/dist/resources/extensions/gsd/tests/token-profile.test.ts +14 -16
  74. package/dist/resources/extensions/gsd/tests/workflow-templates.test.ts +173 -0
  75. package/dist/resources/extensions/gsd/triage-resolution.ts +2 -1
  76. package/dist/resources/extensions/gsd/types.ts +2 -0
  77. package/dist/resources/extensions/gsd/workflow-templates/bugfix.md +87 -0
  78. package/dist/resources/extensions/gsd/workflow-templates/dep-upgrade.md +74 -0
  79. package/dist/resources/extensions/gsd/workflow-templates/full-project.md +41 -0
  80. package/dist/resources/extensions/gsd/workflow-templates/hotfix.md +45 -0
  81. package/dist/resources/extensions/gsd/workflow-templates/refactor.md +83 -0
  82. package/dist/resources/extensions/gsd/workflow-templates/registry.json +85 -0
  83. package/dist/resources/extensions/gsd/workflow-templates/security-audit.md +73 -0
  84. package/dist/resources/extensions/gsd/workflow-templates/small-feature.md +81 -0
  85. package/dist/resources/extensions/gsd/workflow-templates/spike.md +69 -0
  86. package/dist/resources/extensions/gsd/workflow-templates.ts +241 -0
  87. package/dist/resources/extensions/gsd/worktree-command.ts +1 -11
  88. package/dist/resources/extensions/gsd/worktree-manager.ts +3 -2
  89. package/dist/resources/extensions/gsd/worktree.ts +42 -5
  90. package/dist/resources/extensions/mac-tools/extension-manifest.json +16 -0
  91. package/dist/resources/extensions/mcp-client/index.ts +459 -0
  92. package/dist/resources/extensions/mcporter/extension-manifest.json +12 -0
  93. package/dist/resources/extensions/remote-questions/discord-adapter.ts +8 -19
  94. package/dist/resources/extensions/remote-questions/extension-manifest.json +11 -0
  95. package/dist/resources/extensions/remote-questions/http-client.ts +76 -0
  96. package/dist/resources/extensions/remote-questions/slack-adapter.ts +11 -17
  97. package/dist/resources/extensions/remote-questions/telegram-adapter.ts +8 -19
  98. package/dist/resources/extensions/search-the-web/extension-manifest.json +13 -0
  99. package/dist/resources/extensions/slash-commands/extension-manifest.json +11 -0
  100. package/dist/resources/extensions/subagent/extension-manifest.json +13 -0
  101. package/dist/resources/extensions/ttsr/extension-manifest.json +11 -0
  102. package/dist/resources/extensions/universal-config/extension-manifest.json +13 -0
  103. package/dist/resources/extensions/voice/extension-manifest.json +12 -0
  104. package/dist/resources/skills/create-gsd-extension/SKILL.md +87 -0
  105. package/dist/resources/skills/create-gsd-extension/references/compaction-session-control.md +77 -0
  106. package/dist/resources/skills/create-gsd-extension/references/custom-commands.md +139 -0
  107. package/dist/resources/skills/create-gsd-extension/references/custom-rendering.md +108 -0
  108. package/dist/resources/skills/create-gsd-extension/references/custom-tools.md +183 -0
  109. package/dist/resources/skills/create-gsd-extension/references/custom-ui.md +490 -0
  110. package/dist/resources/skills/create-gsd-extension/references/events-reference.md +126 -0
  111. package/dist/resources/skills/create-gsd-extension/references/extension-lifecycle.md +64 -0
  112. package/dist/resources/skills/create-gsd-extension/references/extensionapi-reference.md +75 -0
  113. package/dist/resources/skills/create-gsd-extension/references/extensioncontext-reference.md +53 -0
  114. package/dist/resources/skills/create-gsd-extension/references/key-rules-gotchas.md +36 -0
  115. package/dist/resources/skills/create-gsd-extension/references/mode-behavior.md +32 -0
  116. package/dist/resources/skills/create-gsd-extension/references/model-provider-management.md +89 -0
  117. package/dist/resources/skills/create-gsd-extension/references/packaging-distribution.md +55 -0
  118. package/dist/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +90 -0
  119. package/dist/resources/skills/create-gsd-extension/references/state-management.md +70 -0
  120. package/dist/resources/skills/create-gsd-extension/references/system-prompt-modification.md +52 -0
  121. package/dist/resources/skills/create-gsd-extension/templates/extension-skeleton.ts +51 -0
  122. package/dist/resources/skills/create-gsd-extension/templates/stateful-tool-skeleton.ts +143 -0
  123. package/dist/resources/skills/create-gsd-extension/workflows/add-capability.md +57 -0
  124. package/dist/resources/skills/create-gsd-extension/workflows/create-extension.md +156 -0
  125. package/dist/resources/skills/create-gsd-extension/workflows/debug-extension.md +74 -0
  126. package/dist/resources/skills/create-skill/SKILL.md +184 -0
  127. package/dist/resources/skills/create-skill/references/api-security.md +226 -0
  128. package/dist/resources/skills/create-skill/references/be-clear-and-direct.md +531 -0
  129. package/dist/resources/skills/create-skill/references/common-patterns.md +595 -0
  130. package/dist/resources/skills/create-skill/references/core-principles.md +437 -0
  131. package/dist/resources/skills/create-skill/references/executable-code.md +175 -0
  132. package/dist/resources/skills/create-skill/references/gsd-skill-ecosystem.md +68 -0
  133. package/dist/resources/skills/create-skill/references/iteration-and-testing.md +474 -0
  134. package/dist/resources/skills/create-skill/references/recommended-structure.md +168 -0
  135. package/dist/resources/skills/create-skill/references/skill-structure.md +372 -0
  136. package/dist/resources/skills/create-skill/references/use-xml-tags.md +466 -0
  137. package/dist/resources/skills/create-skill/references/using-scripts.md +113 -0
  138. package/dist/resources/skills/create-skill/references/using-templates.md +112 -0
  139. package/dist/resources/skills/create-skill/references/workflows-and-validation.md +510 -0
  140. package/dist/resources/skills/create-skill/templates/router-skill.md +73 -0
  141. package/dist/resources/skills/create-skill/templates/simple-skill.md +33 -0
  142. package/dist/resources/skills/create-skill/workflows/add-reference.md +96 -0
  143. package/dist/resources/skills/create-skill/workflows/add-script.md +93 -0
  144. package/dist/resources/skills/create-skill/workflows/add-template.md +74 -0
  145. package/dist/resources/skills/create-skill/workflows/add-workflow.md +120 -0
  146. package/dist/resources/skills/create-skill/workflows/audit-skill.md +148 -0
  147. package/dist/resources/skills/create-skill/workflows/create-new-skill.md +196 -0
  148. package/dist/resources/skills/create-skill/workflows/get-guidance.md +121 -0
  149. package/dist/resources/skills/create-skill/workflows/upgrade-to-router.md +161 -0
  150. package/dist/resources/skills/create-skill/workflows/verify-skill.md +204 -0
  151. package/dist/resources/skills/react-best-practices/SKILL.md +1 -1
  152. package/package.json +1 -1
  153. package/packages/native/dist/native.d.ts +2 -0
  154. package/packages/native/dist/native.js +19 -5
  155. package/packages/native/src/native.ts +23 -9
  156. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  157. package/packages/pi-coding-agent/dist/core/extensions/loader.js +13 -0
  158. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  159. package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
  160. package/packages/pi-coding-agent/dist/core/lsp/client.js +3 -0
  161. package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
  162. package/packages/pi-coding-agent/src/core/extensions/loader.ts +13 -0
  163. package/packages/pi-coding-agent/src/core/lsp/client.ts +3 -0
  164. package/src/resources/extensions/async-jobs/extension-manifest.json +13 -0
  165. package/src/resources/extensions/bg-shell/extension-manifest.json +14 -0
  166. package/src/resources/extensions/bg-shell/process-manager.ts +13 -0
  167. package/src/resources/extensions/browser-tools/extension-manifest.json +37 -0
  168. package/src/resources/extensions/context7/extension-manifest.json +12 -0
  169. package/src/resources/extensions/google-search/extension-manifest.json +12 -0
  170. package/src/resources/extensions/gsd/auto-dashboard.ts +217 -65
  171. package/src/resources/extensions/gsd/auto-dispatch.ts +32 -3
  172. package/src/resources/extensions/gsd/auto-post-unit.ts +45 -13
  173. package/src/resources/extensions/gsd/auto-prompts.ts +40 -17
  174. package/src/resources/extensions/gsd/auto-recovery.ts +18 -23
  175. package/src/resources/extensions/gsd/auto-start.ts +18 -32
  176. package/src/resources/extensions/gsd/auto-worktree.ts +21 -182
  177. package/src/resources/extensions/gsd/auto.ts +2 -24
  178. package/src/resources/extensions/gsd/captures.ts +4 -10
  179. package/src/resources/extensions/gsd/commands-extensions.ts +328 -0
  180. package/src/resources/extensions/gsd/commands-handlers.ts +22 -2
  181. package/src/resources/extensions/gsd/commands-logs.ts +13 -14
  182. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +44 -14
  183. package/src/resources/extensions/gsd/commands-workflow-templates.ts +544 -0
  184. package/src/resources/extensions/gsd/commands.ts +108 -24
  185. package/src/resources/extensions/gsd/dashboard-overlay.ts +2 -1
  186. package/src/resources/extensions/gsd/detection.ts +2 -1
  187. package/src/resources/extensions/gsd/doctor-checks.ts +49 -1
  188. package/src/resources/extensions/gsd/doctor-types.ts +3 -1
  189. package/src/resources/extensions/gsd/extension-manifest.json +18 -0
  190. package/src/resources/extensions/gsd/forensics.ts +2 -2
  191. package/src/resources/extensions/gsd/git-service.ts +3 -2
  192. package/src/resources/extensions/gsd/gitignore.ts +9 -63
  193. package/src/resources/extensions/gsd/gsd-db.ts +1 -165
  194. package/src/resources/extensions/gsd/guided-flow.ts +8 -5
  195. package/src/resources/extensions/gsd/index.ts +3 -3
  196. package/src/resources/extensions/gsd/json-persistence.ts +16 -1
  197. package/src/resources/extensions/gsd/md-importer.ts +3 -2
  198. package/src/resources/extensions/gsd/mechanical-completion.ts +430 -0
  199. package/src/resources/extensions/gsd/migrate/command.ts +3 -2
  200. package/src/resources/extensions/gsd/migrate/writer.ts +2 -1
  201. package/src/resources/extensions/gsd/migrate-external.ts +123 -0
  202. package/src/resources/extensions/gsd/paths.ts +24 -2
  203. package/src/resources/extensions/gsd/post-unit-hooks.ts +6 -5
  204. package/src/resources/extensions/gsd/preferences-models.ts +7 -1
  205. package/src/resources/extensions/gsd/preferences-validation.ts +2 -1
  206. package/src/resources/extensions/gsd/preferences.ts +10 -5
  207. package/src/resources/extensions/gsd/prompts/discuss-headless.md +4 -2
  208. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
  209. package/src/resources/extensions/gsd/prompts/plan-milestone.md +26 -2
  210. package/src/resources/extensions/gsd/prompts/plan-slice.md +15 -1
  211. package/src/resources/extensions/gsd/prompts/workflow-start.md +28 -0
  212. package/src/resources/extensions/gsd/queue-order.ts +10 -11
  213. package/src/resources/extensions/gsd/repo-identity.ts +148 -0
  214. package/src/resources/extensions/gsd/resource-version.ts +99 -0
  215. package/src/resources/extensions/gsd/session-forensics.ts +4 -3
  216. package/src/resources/extensions/gsd/session-status-io.ts +23 -41
  217. package/src/resources/extensions/gsd/tests/activity-log.test.ts +2 -2
  218. package/src/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +1 -1
  219. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +3 -3
  220. package/src/resources/extensions/gsd/tests/auto-skip-loop.test.ts +1 -1
  221. package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +0 -58
  222. package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +3 -4
  223. package/src/resources/extensions/gsd/tests/extension-selector-separator.test.ts +60 -38
  224. package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +5 -18
  225. package/src/resources/extensions/gsd/tests/git-service.test.ts +10 -37
  226. package/src/resources/extensions/gsd/tests/knowledge.test.ts +4 -4
  227. package/src/resources/extensions/gsd/tests/mechanical-completion.test.ts +356 -0
  228. package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +1 -1
  229. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +1 -0
  230. package/src/resources/extensions/gsd/tests/token-profile.test.ts +14 -16
  231. package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +173 -0
  232. package/src/resources/extensions/gsd/triage-resolution.ts +2 -1
  233. package/src/resources/extensions/gsd/types.ts +2 -0
  234. package/src/resources/extensions/gsd/workflow-templates/bugfix.md +87 -0
  235. package/src/resources/extensions/gsd/workflow-templates/dep-upgrade.md +74 -0
  236. package/src/resources/extensions/gsd/workflow-templates/full-project.md +41 -0
  237. package/src/resources/extensions/gsd/workflow-templates/hotfix.md +45 -0
  238. package/src/resources/extensions/gsd/workflow-templates/refactor.md +83 -0
  239. package/src/resources/extensions/gsd/workflow-templates/registry.json +85 -0
  240. package/src/resources/extensions/gsd/workflow-templates/security-audit.md +73 -0
  241. package/src/resources/extensions/gsd/workflow-templates/small-feature.md +81 -0
  242. package/src/resources/extensions/gsd/workflow-templates/spike.md +69 -0
  243. package/src/resources/extensions/gsd/workflow-templates.ts +241 -0
  244. package/src/resources/extensions/gsd/worktree-command.ts +1 -11
  245. package/src/resources/extensions/gsd/worktree-manager.ts +3 -2
  246. package/src/resources/extensions/gsd/worktree.ts +42 -5
  247. package/src/resources/extensions/mac-tools/extension-manifest.json +16 -0
  248. package/src/resources/extensions/mcp-client/index.ts +459 -0
  249. package/src/resources/extensions/mcporter/extension-manifest.json +12 -0
  250. package/src/resources/extensions/remote-questions/discord-adapter.ts +8 -19
  251. package/src/resources/extensions/remote-questions/extension-manifest.json +11 -0
  252. package/src/resources/extensions/remote-questions/http-client.ts +76 -0
  253. package/src/resources/extensions/remote-questions/slack-adapter.ts +11 -17
  254. package/src/resources/extensions/remote-questions/telegram-adapter.ts +8 -19
  255. package/src/resources/extensions/search-the-web/extension-manifest.json +13 -0
  256. package/src/resources/extensions/slash-commands/extension-manifest.json +11 -0
  257. package/src/resources/extensions/subagent/extension-manifest.json +13 -0
  258. package/src/resources/extensions/ttsr/extension-manifest.json +11 -0
  259. package/src/resources/extensions/universal-config/extension-manifest.json +13 -0
  260. package/src/resources/extensions/voice/extension-manifest.json +12 -0
  261. package/src/resources/skills/create-gsd-extension/SKILL.md +87 -0
  262. package/src/resources/skills/create-gsd-extension/references/compaction-session-control.md +77 -0
  263. package/src/resources/skills/create-gsd-extension/references/custom-commands.md +139 -0
  264. package/src/resources/skills/create-gsd-extension/references/custom-rendering.md +108 -0
  265. package/src/resources/skills/create-gsd-extension/references/custom-tools.md +183 -0
  266. package/src/resources/skills/create-gsd-extension/references/custom-ui.md +490 -0
  267. package/src/resources/skills/create-gsd-extension/references/events-reference.md +126 -0
  268. package/src/resources/skills/create-gsd-extension/references/extension-lifecycle.md +64 -0
  269. package/src/resources/skills/create-gsd-extension/references/extensionapi-reference.md +75 -0
  270. package/src/resources/skills/create-gsd-extension/references/extensioncontext-reference.md +53 -0
  271. package/src/resources/skills/create-gsd-extension/references/key-rules-gotchas.md +36 -0
  272. package/src/resources/skills/create-gsd-extension/references/mode-behavior.md +32 -0
  273. package/src/resources/skills/create-gsd-extension/references/model-provider-management.md +89 -0
  274. package/src/resources/skills/create-gsd-extension/references/packaging-distribution.md +55 -0
  275. package/src/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +90 -0
  276. package/src/resources/skills/create-gsd-extension/references/state-management.md +70 -0
  277. package/src/resources/skills/create-gsd-extension/references/system-prompt-modification.md +52 -0
  278. package/src/resources/skills/create-gsd-extension/templates/extension-skeleton.ts +51 -0
  279. package/src/resources/skills/create-gsd-extension/templates/stateful-tool-skeleton.ts +143 -0
  280. package/src/resources/skills/create-gsd-extension/workflows/add-capability.md +57 -0
  281. package/src/resources/skills/create-gsd-extension/workflows/create-extension.md +156 -0
  282. package/src/resources/skills/create-gsd-extension/workflows/debug-extension.md +74 -0
  283. package/src/resources/skills/create-skill/SKILL.md +184 -0
  284. package/src/resources/skills/create-skill/references/api-security.md +226 -0
  285. package/src/resources/skills/create-skill/references/be-clear-and-direct.md +531 -0
  286. package/src/resources/skills/create-skill/references/common-patterns.md +595 -0
  287. package/src/resources/skills/create-skill/references/core-principles.md +437 -0
  288. package/src/resources/skills/create-skill/references/executable-code.md +175 -0
  289. package/src/resources/skills/create-skill/references/gsd-skill-ecosystem.md +68 -0
  290. package/src/resources/skills/create-skill/references/iteration-and-testing.md +474 -0
  291. package/src/resources/skills/create-skill/references/recommended-structure.md +168 -0
  292. package/src/resources/skills/create-skill/references/skill-structure.md +372 -0
  293. package/src/resources/skills/create-skill/references/use-xml-tags.md +466 -0
  294. package/src/resources/skills/create-skill/references/using-scripts.md +113 -0
  295. package/src/resources/skills/create-skill/references/using-templates.md +112 -0
  296. package/src/resources/skills/create-skill/references/workflows-and-validation.md +510 -0
  297. package/src/resources/skills/create-skill/templates/router-skill.md +73 -0
  298. package/src/resources/skills/create-skill/templates/simple-skill.md +33 -0
  299. package/src/resources/skills/create-skill/workflows/add-reference.md +96 -0
  300. package/src/resources/skills/create-skill/workflows/add-script.md +93 -0
  301. package/src/resources/skills/create-skill/workflows/add-template.md +74 -0
  302. package/src/resources/skills/create-skill/workflows/add-workflow.md +120 -0
  303. package/src/resources/skills/create-skill/workflows/audit-skill.md +148 -0
  304. package/src/resources/skills/create-skill/workflows/create-new-skill.md +196 -0
  305. package/src/resources/skills/create-skill/workflows/get-guidance.md +121 -0
  306. package/src/resources/skills/create-skill/workflows/upgrade-to-router.md +161 -0
  307. package/src/resources/skills/create-skill/workflows/verify-skill.md +204 -0
  308. package/src/resources/skills/react-best-practices/SKILL.md +1 -1
  309. package/dist/resources/extensions/gsd/auto-worktree-sync.ts +0 -198
  310. package/dist/resources/extensions/gsd/tests/worktree-db-integration.test.ts +0 -205
  311. package/dist/resources/extensions/gsd/tests/worktree-db.test.ts +0 -442
  312. package/dist/resources/extensions/mcporter/index.ts +0 -525
  313. package/src/resources/extensions/gsd/auto-worktree-sync.ts +0 -198
  314. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +0 -205
  315. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +0 -442
  316. package/src/resources/extensions/mcporter/index.ts +0 -525
@@ -22,7 +22,6 @@ import { loadFile, getManifestStatus, resolveAllOverrides, parsePlan, parseSumma
22
22
  import { loadPrompt } from "./prompt-loader.js";
23
23
  import { runVerificationGate, formatFailureContext, captureRuntimeErrors, runDependencyAudit } from "./verification-gate.js";
24
24
  import { writeVerificationJSON } from "./verification-evidence.js";
25
- export { inlinePriorMilestoneSummary } from "./files.js";
26
25
  import { collectSecretsFromManifest } from "../get-secrets-from-user.js";
27
26
  import {
28
27
  gsdRoot, resolveMilestoneFile, resolveSliceFile, resolveSlicePath,
@@ -70,12 +69,10 @@ import { closeoutUnit } from "./auto-unit-closeout.js";
70
69
  import { recoverTimedOutUnit } from "./auto-timeout-recovery.js";
71
70
  import { selectAndApplyModel } from "./auto-model-selection.js";
72
71
  import {
73
- syncProjectRootToWorktree,
74
- syncStateToProjectRoot,
75
72
  readResourceVersion,
76
73
  checkResourcesStale,
77
74
  escapeStaleWorktree,
78
- } from "./auto-worktree-sync.js";
75
+ } from "./resource-version.js";
79
76
  import { initRoutingHistory, resetRoutingHistory, recordOutcome } from "./routing-history.js";
80
77
  import {
81
78
  checkPostUnitHooks,
@@ -181,7 +178,7 @@ import { runPostUnitVerification, type VerificationContext } from "./auto-verifi
181
178
  import { postUnitPreVerification, postUnitPostVerification, type PostUnitContext } from "./auto-post-unit.js";
182
179
  import { bootstrapAutoSession, type BootstrapDeps } from "./auto-start.js";
183
180
 
184
- // Worktree sync, resource staleness, stale worktree escape → auto-worktree-sync.ts
181
+ // Resource staleness, stale worktree escape → resource-version.ts
185
182
 
186
183
  // ─── Session State ─────────────────────────────────────────────────────────
187
184
 
@@ -192,12 +189,6 @@ import {
192
189
  NEW_SESSION_TIMEOUT_MS, DISPATCH_HANG_TIMEOUT_MS,
193
190
  } from "./auto/session.js";
194
191
  import type { CompletedUnit, CurrentUnit, UnitRouting, StartModel, PendingVerificationRetry } from "./auto/session.js";
195
- export {
196
- MAX_UNIT_DISPATCHES, STUB_RECOVERY_THRESHOLD, MAX_LIFETIME_DISPATCHES,
197
- MAX_CONSECUTIVE_SKIPS, DISPATCH_GAP_TIMEOUT_MS, MAX_SKIP_DEPTH,
198
- NEW_SESSION_TIMEOUT_MS, DISPATCH_HANG_TIMEOUT_MS,
199
- } from "./auto/session.js";
200
- export type { CompletedUnit, CurrentUnit, UnitRouting, StartModel } from "./auto/session.js";
201
192
 
202
193
  // ── ENCAPSULATION INVARIANT ─────────────────────────────────────────────────
203
194
  // ALL mutable auto-mode state lives in the AutoSession class (auto/session.ts).
@@ -268,8 +259,6 @@ export function shouldUseWorktreeIsolation(): boolean {
268
259
  * Maps toolCallId → start timestamp (ms) so the idle watchdog can detect tools that have been
269
260
  * running suspiciously long (e.g., a Bash command hung because `&` kept stdout open).
270
261
  */
271
- // Re-export budget utilities for external consumers
272
- export { getBudgetAlertLevel, getNewBudgetAlertLevel, getBudgetEnforcementAction } from "./auto-budget.js";
273
262
 
274
263
  /** Wrapper: register SIGTERM handler and store reference. */
275
264
  function registerSigtermHandler(currentBasePath: string): void {
@@ -282,8 +271,6 @@ function deregisterSigtermHandler(): void {
282
271
  s.sigtermHandler = null;
283
272
  }
284
273
 
285
- export { type AutoDashboardData } from "./auto-dashboard.js";
286
-
287
274
  export function getAutoDashboardData(): AutoDashboardData {
288
275
  const ledger = getLedger();
289
276
  const totals = ledger ? getProjectTotals(ledger.units) : null;
@@ -934,8 +921,6 @@ async function showStepWizard(
934
921
  }
935
922
  }
936
923
 
937
- // describeNextUnit is imported from auto-dashboard.ts and re-exported
938
- export { describeNextUnit } from "./auto-dashboard.js";
939
924
 
940
925
  /** Thin wrapper: delegates to auto-dashboard.ts, passing state accessors. */
941
926
  function updateProgressWidget(
@@ -1031,11 +1016,6 @@ async function dispatchNextUnit(
1031
1016
  // Non-fatal
1032
1017
  }
1033
1018
 
1034
- // ── Sync project root artifacts into worktree ──
1035
- if (s.originalBasePath && s.basePath !== s.originalBasePath && s.currentMilestoneId) {
1036
- syncProjectRootToWorktree(s.originalBasePath, s.basePath, s.currentMilestoneId);
1037
- }
1038
-
1039
1019
  const stopDeriveTimer = debugTime("derive-state");
1040
1020
  let state = await deriveState(s.basePath);
1041
1021
  stopDeriveTimer({
@@ -1905,5 +1885,3 @@ export async function dispatchHookUnit(
1905
1885
  }
1906
1886
 
1907
1887
 
1908
- // Direct phase dispatch → auto-direct-dispatch.ts
1909
- export { dispatchDirectPhase } from "./auto-direct-dispatch.js";
@@ -9,9 +9,10 @@
9
9
  */
10
10
 
11
11
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
12
- import { join, resolve, sep } from "node:path";
12
+ import { join, resolve } from "node:path";
13
13
  import { randomUUID } from "node:crypto";
14
14
  import { gsdRoot } from "./paths.js";
15
+ import { resolveProjectRoot } from "./worktree.js";
15
16
 
16
17
  // ─── Types ────────────────────────────────────────────────────────────────────
17
18
 
@@ -58,15 +59,8 @@ const VALID_CLASSIFICATIONS: readonly string[] = [
58
59
  * directory that contains `.gsd/worktrees/` — that's the project root.
59
60
  */
60
61
  export function resolveCapturesPath(basePath: string): string {
61
- const resolved = resolve(basePath);
62
- const worktreeMarker = `${sep}.gsd${sep}worktrees${sep}`;
63
- const idx = resolved.indexOf(worktreeMarker);
64
- if (idx !== -1) {
65
- // basePath is inside a worktree — resolve to project root
66
- const projectRoot = resolved.slice(0, idx);
67
- return join(projectRoot, ".gsd", CAPTURES_FILENAME);
68
- }
69
- return join(gsdRoot(basePath), CAPTURES_FILENAME);
62
+ const projectRoot = resolveProjectRoot(resolve(basePath));
63
+ return join(gsdRoot(projectRoot), CAPTURES_FILENAME);
70
64
  }
71
65
 
72
66
  // ─── File I/O ─────────────────────────────────────────────────────────────────
@@ -0,0 +1,328 @@
1
+ /**
2
+ * GSD Extensions Command — /gsd extensions
3
+ *
4
+ * Manage the extension registry: list, enable, disable, info.
5
+ * Self-contained — no imports outside the extensions tree (extensions are loaded
6
+ * via jiti at runtime from ~/.gsd/agent/, not compiled by tsc).
7
+ */
8
+
9
+ import type { ExtensionCommandContext } from "@gsd/pi-coding-agent";
10
+ import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, writeFileSync } from "node:fs";
11
+ import { dirname, join } from "node:path";
12
+ import { homedir } from "node:os";
13
+
14
+ // ─── Types (mirrored from extension-registry.ts) ────────────────────────────
15
+
16
+ interface ExtensionManifest {
17
+ id: string;
18
+ name: string;
19
+ version: string;
20
+ description: string;
21
+ tier: "core" | "bundled" | "community";
22
+ requires: { platform: string };
23
+ provides?: {
24
+ tools?: string[];
25
+ commands?: string[];
26
+ hooks?: string[];
27
+ shortcuts?: string[];
28
+ };
29
+ dependencies?: {
30
+ extensions?: string[];
31
+ runtime?: string[];
32
+ };
33
+ }
34
+
35
+ interface ExtensionRegistryEntry {
36
+ id: string;
37
+ enabled: boolean;
38
+ source: "bundled" | "user" | "project";
39
+ disabledAt?: string;
40
+ disabledReason?: string;
41
+ }
42
+
43
+ interface ExtensionRegistry {
44
+ version: 1;
45
+ entries: Record<string, ExtensionRegistryEntry>;
46
+ }
47
+
48
+ // ─── Registry I/O ───────────────────────────────────────────────────────────
49
+
50
+ function getRegistryPath(): string {
51
+ return join(homedir(), ".gsd", "extensions", "registry.json");
52
+ }
53
+
54
+ function getAgentExtensionsDir(): string {
55
+ return join(homedir(), ".gsd", "agent", "extensions");
56
+ }
57
+
58
+ function loadRegistry(): ExtensionRegistry {
59
+ const filePath = getRegistryPath();
60
+ try {
61
+ if (!existsSync(filePath)) return { version: 1, entries: {} };
62
+ const raw = readFileSync(filePath, "utf-8");
63
+ const parsed = JSON.parse(raw);
64
+ if (typeof parsed === "object" && parsed !== null && parsed.version === 1 && typeof parsed.entries === "object") {
65
+ return parsed as ExtensionRegistry;
66
+ }
67
+ return { version: 1, entries: {} };
68
+ } catch {
69
+ return { version: 1, entries: {} };
70
+ }
71
+ }
72
+
73
+ function saveRegistry(registry: ExtensionRegistry): void {
74
+ const filePath = getRegistryPath();
75
+ try {
76
+ mkdirSync(dirname(filePath), { recursive: true });
77
+ const tmp = filePath + ".tmp";
78
+ writeFileSync(tmp, JSON.stringify(registry, null, 2), "utf-8");
79
+ renameSync(tmp, filePath);
80
+ } catch { /* non-fatal */ }
81
+ }
82
+
83
+ function isEnabled(registry: ExtensionRegistry, id: string): boolean {
84
+ const entry = registry.entries[id];
85
+ if (!entry) return true;
86
+ return entry.enabled;
87
+ }
88
+
89
+ function readManifest(dir: string): ExtensionManifest | null {
90
+ const mPath = join(dir, "extension-manifest.json");
91
+ if (!existsSync(mPath)) return null;
92
+ try {
93
+ const raw = JSON.parse(readFileSync(mPath, "utf-8"));
94
+ if (typeof raw?.id === "string" && typeof raw?.name === "string") return raw as ExtensionManifest;
95
+ return null;
96
+ } catch {
97
+ return null;
98
+ }
99
+ }
100
+
101
+ function discoverManifests(): Map<string, ExtensionManifest> {
102
+ const extDir = getAgentExtensionsDir();
103
+ const manifests = new Map<string, ExtensionManifest>();
104
+ if (!existsSync(extDir)) return manifests;
105
+ for (const entry of readdirSync(extDir, { withFileTypes: true })) {
106
+ if (!entry.isDirectory()) continue;
107
+ const m = readManifest(join(extDir, entry.name));
108
+ if (m) manifests.set(m.id, m);
109
+ }
110
+ return manifests;
111
+ }
112
+
113
+ // ─── Command Handler ────────────────────────────────────────────────────────
114
+
115
+ export async function handleExtensions(args: string, ctx: ExtensionCommandContext): Promise<void> {
116
+ const parts = args.split(/\s+/).filter(Boolean);
117
+ const subCmd = parts[0] ?? "list";
118
+
119
+ if (subCmd === "list") {
120
+ handleList(ctx);
121
+ return;
122
+ }
123
+
124
+ if (subCmd === "enable") {
125
+ handleEnable(parts[1], ctx);
126
+ return;
127
+ }
128
+
129
+ if (subCmd === "disable") {
130
+ handleDisable(parts[1], parts.slice(2).join(" "), ctx);
131
+ return;
132
+ }
133
+
134
+ if (subCmd === "info") {
135
+ handleInfo(parts[1], ctx);
136
+ return;
137
+ }
138
+
139
+ ctx.ui.notify(
140
+ `Unknown: /gsd extensions ${subCmd}. Usage: /gsd extensions [list|enable|disable|info]`,
141
+ "warning",
142
+ );
143
+ }
144
+
145
+ function handleList(ctx: ExtensionCommandContext): void {
146
+ const manifests = discoverManifests();
147
+ const registry = loadRegistry();
148
+
149
+ if (manifests.size === 0) {
150
+ ctx.ui.notify("No extension manifests found.", "warning");
151
+ return;
152
+ }
153
+
154
+ // Sort: core first, then alphabetical
155
+ const sorted = [...manifests.values()].sort((a, b) => {
156
+ if (a.tier === "core" && b.tier !== "core") return -1;
157
+ if (b.tier === "core" && a.tier !== "core") return 1;
158
+ return a.id.localeCompare(b.id);
159
+ });
160
+
161
+ const lines: string[] = [];
162
+ const hdr = padRight("Extensions", 38) + padRight("Status", 10) + padRight("Tier", 10) + padRight("Tools", 7) + "Commands";
163
+ lines.push(hdr);
164
+ lines.push("─".repeat(hdr.length));
165
+
166
+ for (const m of sorted) {
167
+ const enabled = isEnabled(registry, m.id);
168
+ const status = enabled ? "enabled" : "disabled";
169
+ const toolCount = m.provides?.tools?.length ?? 0;
170
+ const cmdCount = m.provides?.commands?.length ?? 0;
171
+ const label = `${m.id} (${m.name})`;
172
+
173
+ lines.push(
174
+ padRight(label, 38) +
175
+ padRight(status, 10) +
176
+ padRight(m.tier, 10) +
177
+ padRight(String(toolCount), 7) +
178
+ String(cmdCount),
179
+ );
180
+
181
+ if (!enabled) {
182
+ lines.push(` ↳ gsd extensions enable ${m.id}`);
183
+ }
184
+ }
185
+
186
+ ctx.ui.notify(lines.join("\n"), "info");
187
+ }
188
+
189
+ function handleEnable(id: string | undefined, ctx: ExtensionCommandContext): void {
190
+ if (!id) {
191
+ ctx.ui.notify("Usage: /gsd extensions enable <id>", "warning");
192
+ return;
193
+ }
194
+
195
+ const manifests = discoverManifests();
196
+ if (!manifests.has(id)) {
197
+ ctx.ui.notify(`Extension "${id}" not found. Run /gsd extensions list to see available extensions.`, "warning");
198
+ return;
199
+ }
200
+
201
+ const registry = loadRegistry();
202
+ if (isEnabled(registry, id)) {
203
+ ctx.ui.notify(`Extension "${id}" is already enabled.`, "info");
204
+ return;
205
+ }
206
+
207
+ const entry = registry.entries[id];
208
+ if (entry) {
209
+ entry.enabled = true;
210
+ delete entry.disabledAt;
211
+ delete entry.disabledReason;
212
+ } else {
213
+ registry.entries[id] = { id, enabled: true, source: "bundled" };
214
+ }
215
+ saveRegistry(registry);
216
+ ctx.ui.notify(`Enabled "${id}". Restart GSD to activate.`, "info");
217
+ }
218
+
219
+ function handleDisable(id: string | undefined, reason: string, ctx: ExtensionCommandContext): void {
220
+ if (!id) {
221
+ ctx.ui.notify("Usage: /gsd extensions disable <id>", "warning");
222
+ return;
223
+ }
224
+
225
+ const manifests = discoverManifests();
226
+ const manifest = manifests.get(id) ?? null;
227
+
228
+ if (!manifests.has(id)) {
229
+ ctx.ui.notify(`Extension "${id}" not found. Run /gsd extensions list to see available extensions.`, "warning");
230
+ return;
231
+ }
232
+
233
+ if (manifest?.tier === "core") {
234
+ ctx.ui.notify(`Cannot disable "${id}" — it is a core extension.`, "warning");
235
+ return;
236
+ }
237
+
238
+ const registry = loadRegistry();
239
+ if (!isEnabled(registry, id)) {
240
+ ctx.ui.notify(`Extension "${id}" is already disabled.`, "info");
241
+ return;
242
+ }
243
+
244
+ const entry = registry.entries[id];
245
+ if (entry) {
246
+ entry.enabled = false;
247
+ entry.disabledAt = new Date().toISOString();
248
+ entry.disabledReason = reason || undefined;
249
+ } else {
250
+ registry.entries[id] = {
251
+ id,
252
+ enabled: false,
253
+ source: "bundled",
254
+ disabledAt: new Date().toISOString(),
255
+ disabledReason: reason || undefined,
256
+ };
257
+ }
258
+ saveRegistry(registry);
259
+ ctx.ui.notify(`Disabled "${id}". Restart GSD to deactivate.`, "info");
260
+ }
261
+
262
+ function handleInfo(id: string | undefined, ctx: ExtensionCommandContext): void {
263
+ if (!id) {
264
+ ctx.ui.notify("Usage: /gsd extensions info <id>", "warning");
265
+ return;
266
+ }
267
+
268
+ const manifests = discoverManifests();
269
+ const manifest = manifests.get(id);
270
+ if (!manifest) {
271
+ ctx.ui.notify(`Extension "${id}" not found.`, "warning");
272
+ return;
273
+ }
274
+
275
+ const registry = loadRegistry();
276
+ const enabled = isEnabled(registry, id);
277
+ const entry = registry.entries[id];
278
+
279
+ const lines: string[] = [
280
+ `${manifest.name} (${manifest.id})`,
281
+ "",
282
+ ` Version: ${manifest.version}`,
283
+ ` Description: ${manifest.description}`,
284
+ ` Tier: ${manifest.tier}`,
285
+ ` Status: ${enabled ? "enabled" : "disabled"}`,
286
+ ];
287
+
288
+ if (entry?.disabledAt) {
289
+ lines.push(` Disabled at: ${entry.disabledAt}`);
290
+ }
291
+ if (entry?.disabledReason) {
292
+ lines.push(` Reason: ${entry.disabledReason}`);
293
+ }
294
+
295
+ if (manifest.provides) {
296
+ lines.push("");
297
+ lines.push(" Provides:");
298
+ if (manifest.provides.tools?.length) {
299
+ lines.push(` Tools: ${manifest.provides.tools.join(", ")}`);
300
+ }
301
+ if (manifest.provides.commands?.length) {
302
+ lines.push(` Commands: ${manifest.provides.commands.join(", ")}`);
303
+ }
304
+ if (manifest.provides.hooks?.length) {
305
+ lines.push(` Hooks: ${manifest.provides.hooks.join(", ")}`);
306
+ }
307
+ if (manifest.provides.shortcuts?.length) {
308
+ lines.push(` Shortcuts: ${manifest.provides.shortcuts.join(", ")}`);
309
+ }
310
+ }
311
+
312
+ if (manifest.dependencies) {
313
+ lines.push("");
314
+ lines.push(" Dependencies:");
315
+ if (manifest.dependencies.extensions?.length) {
316
+ lines.push(` Extensions: ${manifest.dependencies.extensions.join(", ")}`);
317
+ }
318
+ if (manifest.dependencies.runtime?.length) {
319
+ lines.push(` Runtime: ${manifest.dependencies.runtime.join(", ")}`);
320
+ }
321
+ }
322
+
323
+ ctx.ui.notify(lines.join("\n"), "info");
324
+ }
325
+
326
+ function padRight(str: string, len: number): string {
327
+ return str.length >= len ? str + " " : str + " ".repeat(len - str.length);
328
+ }
@@ -9,6 +9,7 @@ import type { ExtensionAPI, ExtensionCommandContext } from "@gsd/pi-coding-agent
9
9
  import { existsSync, readFileSync, mkdirSync } from "node:fs";
10
10
  import { join } from "node:path";
11
11
  import { deriveState } from "./state.js";
12
+ import { gsdRoot } from "./paths.js";
12
13
  import { appendCapture, hasPendingCaptures, loadPendingCaptures } from "./captures.js";
13
14
  import { appendOverride, appendKnowledge } from "./files.js";
14
15
  import {
@@ -19,7 +20,26 @@ import {
19
20
  filterDoctorIssues,
20
21
  } from "./doctor.js";
21
22
  import { isAutoActive } from "./auto.js";
22
- import { projectRoot, dispatchDoctorHeal } from "./commands.js";
23
+ import { projectRoot } from "./commands.js";
24
+ import { loadPrompt } from "./prompt-loader.js";
25
+
26
+ export function dispatchDoctorHeal(pi: ExtensionAPI, scope: string | undefined, reportText: string, structuredIssues: string): void {
27
+ const workflowPath = process.env.GSD_WORKFLOW_PATH ?? join(process.env.HOME ?? "~", ".pi", "GSD-WORKFLOW.md");
28
+ const workflow = readFileSync(workflowPath, "utf-8");
29
+ const prompt = loadPrompt("doctor-heal", {
30
+ doctorSummary: reportText,
31
+ structuredIssues,
32
+ scopeLabel: scope ?? "active milestone / blocking scope",
33
+ doctorCommandSuffix: scope ? ` ${scope}` : "",
34
+ });
35
+
36
+ const content = `Read the following GSD workflow protocol and execute exactly.\n\n${workflow}\n\n## Your Task\n\n${prompt}`;
37
+
38
+ pi.sendMessage(
39
+ { customType: "gsd-doctor-heal", content, display: false },
40
+ { triggerTurn: true },
41
+ );
42
+ }
23
43
 
24
44
  export async function handleDoctor(args: string, ctx: ExtensionCommandContext, pi: ExtensionAPI): Promise<void> {
25
45
  const trimmed = args.trim();
@@ -117,7 +137,7 @@ export async function handleCapture(args: string, ctx: ExtensionCommandContext):
117
137
  const basePath = process.cwd();
118
138
 
119
139
  // Ensure .gsd/ exists — capture should work even without a milestone
120
- const gsdDir = join(basePath, ".gsd");
140
+ const gsdDir = gsdRoot(basePath);
121
141
  if (!existsSync(gsdDir)) {
122
142
  mkdirSync(gsdDir, { recursive: true });
123
143
  }
@@ -14,6 +14,7 @@ import type { ExtensionCommandContext } from "@gsd/pi-coding-agent";
14
14
  import { existsSync, readdirSync, readFileSync, statSync, unlinkSync } from "node:fs";
15
15
  import { join } from "node:path";
16
16
  import { gsdRoot } from "./paths.js";
17
+ import { loadJsonFileOrNull } from "./json-persistence.js";
17
18
 
18
19
  // ─── Types ──────────────────────────────────────────────────────────────────
19
20
 
@@ -331,20 +332,18 @@ async function handleLogsList(basePath: string, ctx: ExtensionCommandContext): P
331
332
 
332
333
  // Metrics summary
333
334
  const metricsPath = join(gsdRoot(basePath), "metrics.json");
334
- if (existsSync(metricsPath)) {
335
- try {
336
- const metrics = JSON.parse(readFileSync(metricsPath, "utf-8"));
337
- const units = metrics?.units;
338
- if (Array.isArray(units) && units.length > 0) {
339
- const totalCost = units.reduce((sum: number, u: Record<string, unknown>) => sum + ((u.cost as number) ?? 0), 0);
340
- const totalTokens = units.reduce((sum: number, u: Record<string, unknown>) => {
341
- const t = u.tokens as Record<string, number> | undefined;
342
- return sum + (t?.total ?? 0);
343
- }, 0);
344
- lines.push("");
345
- lines.push(`Metrics: ${units.length} units tracked · $${totalCost.toFixed(2)} · ${(totalTokens / 1000).toFixed(0)}K tokens`);
346
- }
347
- } catch { /* ignore */ }
335
+ const isMetrics = (d: unknown): d is { units: Array<Record<string, unknown>> } =>
336
+ d !== null && typeof d === "object" && "units" in d! && Array.isArray((d as Record<string, unknown>).units);
337
+ const metrics = loadJsonFileOrNull(metricsPath, isMetrics);
338
+ if (metrics && metrics.units.length > 0) {
339
+ const units = metrics.units;
340
+ const totalCost = units.reduce((sum: number, u) => sum + ((u.cost as number) ?? 0), 0);
341
+ const totalTokens = units.reduce((sum: number, u) => {
342
+ const t = u.tokens as Record<string, number> | undefined;
343
+ return sum + (t?.total ?? 0);
344
+ }, 0);
345
+ lines.push("");
346
+ lines.push(`Metrics: ${units.length} units tracked · $${totalCost.toFixed(2)} · ${(totalTokens / 1000).toFixed(0)}K tokens`);
348
347
  }
349
348
 
350
349
  lines.push("");
@@ -260,27 +260,57 @@ async function configureModels(ctx: ExtensionCommandContext, prefs: Record<strin
260
260
  group.push(m);
261
261
  }
262
262
  const providers = Array.from(byProvider.keys()).sort((a, b) => a.localeCompare(b));
263
-
264
- const modelOptions: string[] = [];
265
- for (const provider of providers) {
266
- const group = byProvider.get(provider)!;
267
- modelOptions.push(`─── ${provider} (${group.length}) ───`);
268
- for (const m of group) {
269
- modelOptions.push(`${m.id} · ${m.provider}`);
270
- }
263
+ // Sort models within each provider
264
+ for (const group of byProvider.values()) {
265
+ group.sort((a, b) => a.id.localeCompare(b.id));
271
266
  }
272
- modelOptions.push("(keep current)", "(clear)");
267
+
268
+ // Build provider menu with model counts
269
+ const providerOptions = providers.map(p => {
270
+ const count = byProvider.get(p)!.length;
271
+ return `${p} (${count} models)`;
272
+ });
273
+ providerOptions.push("(keep current)", "(clear)", "(type manually)");
273
274
 
274
275
  for (const phase of modelPhases) {
275
276
  const current = models[phase] ?? "";
276
- const title = `Model for ${phase} phase${current ? ` (current: ${current})` : ""}:`;
277
- const choice = await ctx.ui.select(title, modelOptions);
277
+ const phaseLabel = `Model for ${phase} phase${current ? ` (current: ${current})` : ""}`;
278
+
279
+ // Step 1: pick provider
280
+ const providerChoice = await ctx.ui.select(`${phaseLabel} — choose provider:`, providerOptions);
281
+ if (!providerChoice || typeof providerChoice !== "string" || providerChoice === "(keep current)") continue;
282
+
283
+ if (providerChoice === "(clear)") {
284
+ delete models[phase];
285
+ continue;
286
+ }
287
+
288
+ if (providerChoice === "(type manually)") {
289
+ const input = await ctx.ui.input(
290
+ `${phaseLabel} — enter model ID:`,
291
+ current || "e.g. claude-sonnet-4-20250514",
292
+ );
293
+ if (input !== null && input !== undefined) {
294
+ const val = input.trim();
295
+ if (val) models[phase] = val;
296
+ }
297
+ continue;
298
+ }
299
+
300
+ // Step 2: pick model within provider
301
+ const providerName = providerChoice.replace(/ \(\d+ models?\)$/, "");
302
+ const group = byProvider.get(providerName);
303
+ if (!group) continue;
304
+
305
+ const modelOptions = group.map(m => m.id);
306
+ modelOptions.push("(keep current)", "(clear)");
278
307
 
279
- if (choice && typeof choice === "string" && choice !== "(keep current)") {
280
- if (choice === "(clear)") {
308
+ const modelChoice = await ctx.ui.select(`${phaseLabel} ${providerName}:`, modelOptions);
309
+ if (modelChoice && typeof modelChoice === "string" && modelChoice !== "(keep current)") {
310
+ if (modelChoice === "(clear)") {
281
311
  delete models[phase];
282
312
  } else {
283
- models[phase] = choice.split(" · ")[0];
313
+ models[phase] = modelChoice;
284
314
  }
285
315
  }
286
316
  }