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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (346) hide show
  1. package/README.md +15 -11
  2. package/dist/app-paths.js +1 -1
  3. package/dist/cli.js +9 -0
  4. package/dist/extension-discovery.d.ts +5 -3
  5. package/dist/extension-discovery.js +14 -9
  6. package/dist/extension-registry.js +2 -2
  7. package/dist/remote-questions-config.js +2 -2
  8. package/dist/resource-loader.js +100 -3
  9. package/dist/resources/extensions/async-jobs/index.js +10 -0
  10. package/dist/resources/extensions/browser-tools/index.js +3 -1
  11. package/dist/resources/extensions/browser-tools/package.json +3 -1
  12. package/dist/resources/extensions/browser-tools/tools/verify.js +97 -0
  13. package/dist/resources/extensions/cmux/index.js +55 -1
  14. package/dist/resources/extensions/context7/package.json +1 -1
  15. package/dist/resources/extensions/get-secrets-from-user.js +5 -24
  16. package/dist/resources/extensions/github-sync/cli.js +284 -0
  17. package/dist/resources/extensions/github-sync/index.js +73 -0
  18. package/dist/resources/extensions/github-sync/mapping.js +67 -0
  19. package/dist/resources/extensions/github-sync/sync.js +424 -0
  20. package/dist/resources/extensions/github-sync/templates.js +118 -0
  21. package/dist/resources/extensions/github-sync/types.js +7 -0
  22. package/dist/resources/extensions/google-search/package.json +3 -1
  23. package/dist/resources/extensions/gsd/auto/session.js +6 -23
  24. package/dist/resources/extensions/gsd/auto-dashboard.js +7 -0
  25. package/dist/resources/extensions/gsd/auto-dispatch.js +8 -9
  26. package/dist/resources/extensions/gsd/auto-loop.js +923 -787
  27. package/dist/resources/extensions/gsd/auto-post-unit.js +107 -70
  28. package/dist/resources/extensions/gsd/auto-prompts.js +205 -51
  29. package/dist/resources/extensions/gsd/auto-start.js +19 -3
  30. package/dist/resources/extensions/gsd/auto-worktree-sync.js +13 -5
  31. package/dist/resources/extensions/gsd/auto-worktree.js +3 -3
  32. package/dist/resources/extensions/gsd/auto.js +149 -100
  33. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +126 -0
  34. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +233 -0
  35. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +59 -0
  36. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +38 -0
  37. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +156 -0
  38. package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +46 -0
  39. package/dist/resources/extensions/gsd/bootstrap/system-context.js +300 -0
  40. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +38 -0
  41. package/dist/resources/extensions/gsd/captures.js +9 -1
  42. package/dist/resources/extensions/gsd/commands/catalog.js +278 -0
  43. package/dist/resources/extensions/gsd/commands/context.js +84 -0
  44. package/dist/resources/extensions/gsd/commands/dispatcher.js +21 -0
  45. package/dist/resources/extensions/gsd/commands/handlers/auto.js +72 -0
  46. package/dist/resources/extensions/gsd/commands/handlers/core.js +246 -0
  47. package/dist/resources/extensions/gsd/commands/handlers/ops.js +166 -0
  48. package/dist/resources/extensions/gsd/commands/handlers/parallel.js +94 -0
  49. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +102 -0
  50. package/dist/resources/extensions/gsd/commands/index.js +11 -0
  51. package/dist/resources/extensions/gsd/commands-extensions.js +3 -2
  52. package/dist/resources/extensions/gsd/commands-handlers.js +17 -4
  53. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +1 -1
  54. package/dist/resources/extensions/gsd/commands.js +8 -1169
  55. package/dist/resources/extensions/gsd/context-budget.js +2 -10
  56. package/dist/resources/extensions/gsd/dashboard-overlay.js +9 -0
  57. package/dist/resources/extensions/gsd/detection.js +1 -2
  58. package/dist/resources/extensions/gsd/docs/preferences-reference.md +0 -2
  59. package/dist/resources/extensions/gsd/doctor-checks.js +82 -0
  60. package/dist/resources/extensions/gsd/doctor-environment.js +78 -0
  61. package/dist/resources/extensions/gsd/doctor-format.js +15 -0
  62. package/dist/resources/extensions/gsd/doctor-proactive.js +80 -10
  63. package/dist/resources/extensions/gsd/doctor-providers.js +30 -11
  64. package/dist/resources/extensions/gsd/doctor.js +234 -12
  65. package/dist/resources/extensions/gsd/env-utils.js +29 -0
  66. package/dist/resources/extensions/gsd/exit-command.js +2 -1
  67. package/dist/resources/extensions/gsd/export-html.js +46 -0
  68. package/dist/resources/extensions/gsd/export.js +1 -1
  69. package/dist/resources/extensions/gsd/files.js +48 -9
  70. package/dist/resources/extensions/gsd/forensics.js +1 -1
  71. package/dist/resources/extensions/gsd/git-service.js +30 -12
  72. package/dist/resources/extensions/gsd/gitignore.js +16 -3
  73. package/dist/resources/extensions/gsd/guided-flow.js +149 -38
  74. package/dist/resources/extensions/gsd/health-widget-core.js +32 -70
  75. package/dist/resources/extensions/gsd/health-widget.js +4 -87
  76. package/dist/resources/extensions/gsd/index.js +4 -1111
  77. package/dist/resources/extensions/gsd/migrate/parsers.js +1 -1
  78. package/dist/resources/extensions/gsd/migrate-external.js +18 -1
  79. package/dist/resources/extensions/gsd/native-git-bridge.js +37 -0
  80. package/dist/resources/extensions/gsd/package.json +1 -1
  81. package/dist/resources/extensions/gsd/paths.js +3 -0
  82. package/dist/resources/extensions/gsd/preferences-models.js +0 -12
  83. package/dist/resources/extensions/gsd/preferences-types.js +1 -1
  84. package/dist/resources/extensions/gsd/preferences-validation.js +59 -11
  85. package/dist/resources/extensions/gsd/preferences.js +22 -11
  86. package/dist/resources/extensions/gsd/progress-score.js +20 -1
  87. package/dist/resources/extensions/gsd/prompt-loader.js +6 -2
  88. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  89. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  90. package/dist/resources/extensions/gsd/prompts/discuss.md +11 -14
  91. package/dist/resources/extensions/gsd/prompts/execute-task.md +5 -3
  92. package/dist/resources/extensions/gsd/prompts/forensics.md +121 -46
  93. package/dist/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
  94. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
  95. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
  96. package/dist/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
  97. package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
  98. package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
  99. package/dist/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
  100. package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  101. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  102. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  103. package/dist/resources/extensions/gsd/prompts/queue.md +4 -8
  104. package/dist/resources/extensions/gsd/prompts/reactive-execute.md +11 -8
  105. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
  106. package/dist/resources/extensions/gsd/prompts/research-milestone.md +1 -1
  107. package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
  108. package/dist/resources/extensions/gsd/prompts/run-uat.md +28 -11
  109. package/dist/resources/extensions/gsd/prompts/workflow-start.md +2 -2
  110. package/dist/resources/extensions/gsd/repo-identity.js +21 -4
  111. package/dist/resources/extensions/gsd/resource-version.js +2 -1
  112. package/dist/resources/extensions/gsd/roadmap-mutations.js +24 -0
  113. package/dist/resources/extensions/gsd/state.js +42 -23
  114. package/dist/resources/extensions/gsd/templates/runtime.md +21 -0
  115. package/dist/resources/extensions/gsd/templates/task-plan.md +3 -0
  116. package/dist/resources/extensions/gsd/visualizer-data.js +27 -2
  117. package/dist/resources/extensions/gsd/visualizer-views.js +52 -0
  118. package/dist/resources/extensions/gsd/worktree.js +35 -16
  119. package/dist/resources/extensions/mcp-client/index.js +14 -1
  120. package/dist/resources/extensions/remote-questions/status.js +4 -1
  121. package/dist/resources/extensions/remote-questions/store.js +4 -1
  122. package/dist/resources/extensions/search-the-web/provider.js +2 -1
  123. package/dist/resources/extensions/shared/frontmatter.js +1 -1
  124. package/dist/resources/extensions/subagent/index.js +12 -3
  125. package/dist/resources/extensions/subagent/isolation.js +2 -1
  126. package/dist/resources/extensions/ttsr/rule-loader.js +2 -1
  127. package/dist/resources/extensions/universal-config/package.json +1 -1
  128. package/dist/welcome-screen.d.ts +13 -0
  129. package/dist/welcome-screen.js +97 -0
  130. package/package.json +1 -1
  131. package/packages/pi-ai/dist/utils/oauth/anthropic.js +2 -2
  132. package/packages/pi-ai/dist/utils/oauth/anthropic.js.map +1 -1
  133. package/packages/pi-ai/src/utils/oauth/anthropic.ts +2 -2
  134. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +12 -0
  135. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  136. package/packages/pi-coding-agent/dist/core/agent-session.js +107 -24
  137. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  138. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  139. package/packages/pi-coding-agent/dist/core/extensions/loader.js +205 -7
  140. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  141. package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
  142. package/packages/pi-coding-agent/dist/core/package-manager.js +8 -4
  143. package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
  144. package/packages/pi-coding-agent/dist/core/skill-tool.test.d.ts +2 -0
  145. package/packages/pi-coding-agent/dist/core/skill-tool.test.d.ts.map +1 -0
  146. package/packages/pi-coding-agent/dist/core/skill-tool.test.js +70 -0
  147. package/packages/pi-coding-agent/dist/core/skill-tool.test.js.map +1 -0
  148. package/packages/pi-coding-agent/dist/core/skills.d.ts +1 -0
  149. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  150. package/packages/pi-coding-agent/dist/core/skills.js +8 -2
  151. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  152. package/packages/pi-coding-agent/dist/index.d.ts +1 -1
  153. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  154. package/packages/pi-coding-agent/dist/index.js +1 -1
  155. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  156. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts +17 -0
  157. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -0
  158. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +244 -0
  159. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -0
  160. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.d.ts +3 -0
  161. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.d.ts.map +1 -0
  162. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js +58 -0
  163. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js.map +1 -0
  164. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts +12 -0
  165. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -0
  166. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +54 -0
  167. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -0
  168. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.d.ts +6 -0
  169. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.d.ts.map +1 -0
  170. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js +63 -0
  171. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js.map +1 -0
  172. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +38 -0
  173. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -0
  174. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js +2 -0
  175. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -0
  176. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +1 -1
  177. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  178. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +15 -457
  179. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  180. package/packages/pi-coding-agent/package.json +1 -1
  181. package/packages/pi-coding-agent/src/core/agent-session.ts +122 -23
  182. package/packages/pi-coding-agent/src/core/extensions/loader.ts +223 -7
  183. package/packages/pi-coding-agent/src/core/package-manager.ts +8 -4
  184. package/packages/pi-coding-agent/src/core/skill-tool.test.ts +89 -0
  185. package/packages/pi-coding-agent/src/core/skills.ts +11 -2
  186. package/packages/pi-coding-agent/src/index.ts +1 -0
  187. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +302 -0
  188. package/packages/pi-coding-agent/src/modes/interactive/controllers/extension-ui-controller.ts +59 -0
  189. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +68 -0
  190. package/packages/pi-coding-agent/src/modes/interactive/controllers/model-controller.ts +71 -0
  191. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +37 -0
  192. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +18 -510
  193. package/pkg/package.json +1 -1
  194. package/src/resources/extensions/async-jobs/index.ts +11 -0
  195. package/src/resources/extensions/browser-tools/index.ts +3 -0
  196. package/src/resources/extensions/browser-tools/tools/verify.ts +117 -0
  197. package/src/resources/extensions/cmux/index.ts +57 -1
  198. package/src/resources/extensions/get-secrets-from-user.ts +5 -24
  199. package/src/resources/extensions/github-sync/cli.ts +364 -0
  200. package/src/resources/extensions/github-sync/index.ts +93 -0
  201. package/src/resources/extensions/github-sync/mapping.ts +81 -0
  202. package/src/resources/extensions/github-sync/sync.ts +556 -0
  203. package/src/resources/extensions/github-sync/templates.ts +183 -0
  204. package/src/resources/extensions/github-sync/tests/cli.test.ts +20 -0
  205. package/src/resources/extensions/github-sync/tests/commit-linking.test.ts +39 -0
  206. package/src/resources/extensions/github-sync/tests/mapping.test.ts +104 -0
  207. package/src/resources/extensions/github-sync/tests/templates.test.ts +110 -0
  208. package/src/resources/extensions/github-sync/types.ts +47 -0
  209. package/src/resources/extensions/gsd/auto/session.ts +7 -25
  210. package/src/resources/extensions/gsd/auto-dashboard.ts +10 -0
  211. package/src/resources/extensions/gsd/auto-dispatch.ts +7 -9
  212. package/src/resources/extensions/gsd/auto-loop.ts +1285 -1138
  213. package/src/resources/extensions/gsd/auto-post-unit.ts +90 -46
  214. package/src/resources/extensions/gsd/auto-prompts.ts +250 -53
  215. package/src/resources/extensions/gsd/auto-start.ts +24 -3
  216. package/src/resources/extensions/gsd/auto-worktree-sync.ts +15 -4
  217. package/src/resources/extensions/gsd/auto-worktree.ts +3 -3
  218. package/src/resources/extensions/gsd/auto.ts +152 -111
  219. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +142 -0
  220. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +238 -0
  221. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +90 -0
  222. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +46 -0
  223. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +167 -0
  224. package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +55 -0
  225. package/src/resources/extensions/gsd/bootstrap/system-context.ts +340 -0
  226. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +51 -0
  227. package/src/resources/extensions/gsd/captures.ts +10 -1
  228. package/src/resources/extensions/gsd/commands/catalog.ts +301 -0
  229. package/src/resources/extensions/gsd/commands/context.ts +101 -0
  230. package/src/resources/extensions/gsd/commands/dispatcher.ts +32 -0
  231. package/src/resources/extensions/gsd/commands/handlers/auto.ts +74 -0
  232. package/src/resources/extensions/gsd/commands/handlers/core.ts +274 -0
  233. package/src/resources/extensions/gsd/commands/handlers/ops.ts +169 -0
  234. package/src/resources/extensions/gsd/commands/handlers/parallel.ts +118 -0
  235. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +109 -0
  236. package/src/resources/extensions/gsd/commands/index.ts +14 -0
  237. package/src/resources/extensions/gsd/commands-extensions.ts +4 -2
  238. package/src/resources/extensions/gsd/commands-handlers.ts +18 -3
  239. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +1 -1
  240. package/src/resources/extensions/gsd/commands.ts +10 -1307
  241. package/src/resources/extensions/gsd/context-budget.ts +2 -12
  242. package/src/resources/extensions/gsd/dashboard-overlay.ts +10 -0
  243. package/src/resources/extensions/gsd/detection.ts +2 -2
  244. package/src/resources/extensions/gsd/docs/preferences-reference.md +0 -2
  245. package/src/resources/extensions/gsd/doctor-checks.ts +75 -0
  246. package/src/resources/extensions/gsd/doctor-environment.ts +82 -1
  247. package/src/resources/extensions/gsd/doctor-format.ts +20 -0
  248. package/src/resources/extensions/gsd/doctor-proactive.ts +106 -10
  249. package/src/resources/extensions/gsd/doctor-providers.ts +30 -9
  250. package/src/resources/extensions/gsd/doctor-types.ts +16 -1
  251. package/src/resources/extensions/gsd/doctor.ts +243 -14
  252. package/src/resources/extensions/gsd/env-utils.ts +31 -0
  253. package/src/resources/extensions/gsd/exit-command.ts +2 -2
  254. package/src/resources/extensions/gsd/export-html.ts +51 -0
  255. package/src/resources/extensions/gsd/export.ts +1 -1
  256. package/src/resources/extensions/gsd/files.ts +51 -11
  257. package/src/resources/extensions/gsd/forensics.ts +1 -1
  258. package/src/resources/extensions/gsd/git-service.ts +44 -10
  259. package/src/resources/extensions/gsd/gitignore.ts +17 -3
  260. package/src/resources/extensions/gsd/guided-flow.ts +177 -44
  261. package/src/resources/extensions/gsd/health-widget-core.ts +28 -80
  262. package/src/resources/extensions/gsd/health-widget.ts +4 -89
  263. package/src/resources/extensions/gsd/index.ts +12 -1307
  264. package/src/resources/extensions/gsd/migrate/parsers.ts +1 -1
  265. package/src/resources/extensions/gsd/migrate-external.ts +18 -1
  266. package/src/resources/extensions/gsd/native-git-bridge.ts +37 -0
  267. package/src/resources/extensions/gsd/paths.ts +4 -0
  268. package/src/resources/extensions/gsd/preferences-models.ts +0 -12
  269. package/src/resources/extensions/gsd/preferences-types.ts +4 -4
  270. package/src/resources/extensions/gsd/preferences-validation.ts +51 -11
  271. package/src/resources/extensions/gsd/preferences.ts +25 -11
  272. package/src/resources/extensions/gsd/progress-score.ts +23 -0
  273. package/src/resources/extensions/gsd/prompt-loader.ts +7 -2
  274. package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  275. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  276. package/src/resources/extensions/gsd/prompts/discuss.md +11 -14
  277. package/src/resources/extensions/gsd/prompts/execute-task.md +5 -3
  278. package/src/resources/extensions/gsd/prompts/forensics.md +121 -46
  279. package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
  280. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
  281. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
  282. package/src/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
  283. package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
  284. package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
  285. package/src/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
  286. package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  287. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  288. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  289. package/src/resources/extensions/gsd/prompts/queue.md +4 -8
  290. package/src/resources/extensions/gsd/prompts/reactive-execute.md +11 -8
  291. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
  292. package/src/resources/extensions/gsd/prompts/research-milestone.md +1 -1
  293. package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
  294. package/src/resources/extensions/gsd/prompts/run-uat.md +28 -11
  295. package/src/resources/extensions/gsd/prompts/workflow-start.md +2 -2
  296. package/src/resources/extensions/gsd/repo-identity.ts +23 -4
  297. package/src/resources/extensions/gsd/resource-version.ts +3 -1
  298. package/src/resources/extensions/gsd/roadmap-mutations.ts +29 -0
  299. package/src/resources/extensions/gsd/state.ts +39 -21
  300. package/src/resources/extensions/gsd/templates/runtime.md +21 -0
  301. package/src/resources/extensions/gsd/templates/task-plan.md +3 -0
  302. package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +21 -18
  303. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +135 -77
  304. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +4 -3
  305. package/src/resources/extensions/gsd/tests/cmux.test.ts +93 -0
  306. package/src/resources/extensions/gsd/tests/derive-state.test.ts +43 -0
  307. package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +266 -0
  308. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +3 -3
  309. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +86 -3
  310. package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +50 -0
  311. package/src/resources/extensions/gsd/tests/health-widget.test.ts +16 -54
  312. package/src/resources/extensions/gsd/tests/parsers.test.ts +131 -14
  313. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +209 -0
  314. package/src/resources/extensions/gsd/tests/preferences.test.ts +2 -7
  315. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +59 -0
  316. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +16 -16
  317. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +21 -1
  318. package/src/resources/extensions/gsd/tests/run-uat.test.ts +16 -4
  319. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +140 -0
  320. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +10 -10
  321. package/src/resources/extensions/gsd/tests/worktree.test.ts +47 -0
  322. package/src/resources/extensions/gsd/types.ts +18 -1
  323. package/src/resources/extensions/gsd/verification-evidence.ts +16 -0
  324. package/src/resources/extensions/gsd/visualizer-data.ts +52 -2
  325. package/src/resources/extensions/gsd/visualizer-views.ts +58 -0
  326. package/src/resources/extensions/gsd/worktree.ts +35 -15
  327. package/src/resources/extensions/mcp-client/index.ts +17 -1
  328. package/src/resources/extensions/remote-questions/status.ts +5 -1
  329. package/src/resources/extensions/remote-questions/store.ts +5 -1
  330. package/src/resources/extensions/search-the-web/provider.ts +2 -1
  331. package/src/resources/extensions/shared/frontmatter.ts +1 -1
  332. package/src/resources/extensions/subagent/index.ts +12 -3
  333. package/src/resources/extensions/subagent/isolation.ts +3 -1
  334. package/src/resources/extensions/ttsr/rule-loader.ts +3 -1
  335. package/dist/resources/extensions/gsd/prompt-compressor.js +0 -393
  336. package/dist/resources/extensions/gsd/semantic-chunker.js +0 -254
  337. package/dist/resources/extensions/gsd/summary-distiller.js +0 -212
  338. package/src/resources/extensions/gsd/prompt-compressor.ts +0 -508
  339. package/src/resources/extensions/gsd/semantic-chunker.ts +0 -336
  340. package/src/resources/extensions/gsd/summary-distiller.ts +0 -258
  341. package/src/resources/extensions/gsd/tests/context-compression.test.ts +0 -193
  342. package/src/resources/extensions/gsd/tests/prompt-compressor.test.ts +0 -529
  343. package/src/resources/extensions/gsd/tests/semantic-chunker.test.ts +0 -426
  344. package/src/resources/extensions/gsd/tests/summary-distiller.test.ts +0 -323
  345. package/src/resources/extensions/gsd/tests/token-optimization-benchmark.test.ts +0 -1272
  346. package/src/resources/extensions/gsd/tests/token-optimization-prefs.test.ts +0 -164
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gsd/pi-coding-agent",
3
- "version": "2.38.0",
3
+ "version": "2.39.0",
4
4
  "description": "Coding agent CLI (vendored from pi-mono)",
5
5
  "type": "module",
6
6
  "piConfig": {
@@ -25,6 +25,7 @@ import type {
25
25
  } from "@gsd/pi-agent-core";
26
26
  import type { AssistantMessage, ImageContent, Message, Model, TextContent } from "@gsd/pi-ai";
27
27
  import { modelsAreEqual, resetApiProviders, supportsXhigh } from "@gsd/pi-ai";
28
+ import { Type } from "@sinclair/typebox";
28
29
  import { getDocsPath } from "../config.js";
29
30
  import { getErrorMessage } from "../utils/error.js";
30
31
  import { theme } from "../modes/interactive/theme/theme.js";
@@ -732,9 +733,10 @@ export class AgentSession {
732
733
  * Changes take effect on the next agent turn.
733
734
  */
734
735
  setActiveToolsByName(toolNames: string[]): void {
736
+ const requestedToolNames = [...new Set([...toolNames, ...this._getBuiltinToolNames()])];
735
737
  const tools: AgentTool[] = [];
736
738
  const validToolNames: string[] = [];
737
- for (const name of toolNames) {
739
+ for (const name of requestedToolNames) {
738
740
  const tool = this._toolRegistry.get(name);
739
741
  if (tool) {
740
742
  tools.push(tool);
@@ -743,6 +745,7 @@ export class AgentSession {
743
745
  }
744
746
  this.agent.setTools(tools);
745
747
 
748
+
746
749
  // Rebuild base system prompt with new tool set
747
750
  this._baseSystemPrompt = this._rebuildSystemPrompt(validToolNames);
748
751
  this.agent.setSystemPrompt(this._baseSystemPrompt);
@@ -858,6 +861,48 @@ export class AgentSession {
858
861
  return Array.from(unique);
859
862
  }
860
863
 
864
+ private _findSkillByName(skillName: string) {
865
+ return this.resourceLoader.getSkills().skills.find((skill) => skill.name === skillName);
866
+ }
867
+
868
+ private _formatMissingSkillMessage(skillName: string): string {
869
+ const availableSkills = this.resourceLoader.getSkills().skills.map((skill) => skill.name).join(", ") || "(none)";
870
+ return `Skill "${skillName}" not found. Available skills: ${availableSkills}`;
871
+ }
872
+
873
+ private _emitSkillExpansionError(skillFilePath: string, err: unknown): void {
874
+ this._extensionRunner?.emitError({
875
+ extensionPath: skillFilePath,
876
+ event: "skill_expansion",
877
+ error: getErrorMessage(err),
878
+ });
879
+ }
880
+
881
+ private _renderSkillInvocation(skill: { name: string; filePath: string; baseDir: string }, args?: string): string {
882
+ const content = readFileSync(skill.filePath, "utf-8");
883
+ const body = stripFrontmatter(content).trim();
884
+ const skillBlock = `<skill name="${skill.name}" location="${skill.filePath}">\nReferences are relative to ${skill.baseDir}.\n\n${body}\n</skill>`;
885
+ return args && args.trim() ? `${skillBlock}\n\n${args.trim()}` : skillBlock;
886
+ }
887
+
888
+ private _expandSkillByName(skillName: string, args?: string): string {
889
+ const skill = this._findSkillByName(skillName);
890
+ if (!skill) {
891
+ throw new Error(this._formatMissingSkillMessage(skillName));
892
+ }
893
+
894
+ try {
895
+ return this._renderSkillInvocation(skill, args);
896
+ } catch (err) {
897
+ this._emitSkillExpansionError(skill.filePath, err);
898
+ throw err;
899
+ }
900
+ }
901
+
902
+ private _formatSkillInvocation(skillName: string, args?: string): string {
903
+ return this._expandSkillByName(skillName, args);
904
+ }
905
+
861
906
  private _rebuildSystemPrompt(toolNames: string[]): string {
862
907
  const validToolNames = toolNames.filter((name) => this._toolRegistry.has(name));
863
908
  const toolSnippets: Record<string, string> = {};
@@ -1103,25 +1148,78 @@ export class AgentSession {
1103
1148
  const skillName = spaceIndex === -1 ? text.slice(7) : text.slice(7, spaceIndex);
1104
1149
  const args = spaceIndex === -1 ? "" : text.slice(spaceIndex + 1).trim();
1105
1150
 
1106
- const skill = this.resourceLoader.getSkills().skills.find((s) => s.name === skillName);
1107
- if (!skill) return text; // Unknown skill, pass through
1151
+ if (!this._findSkillByName(skillName)) return text;
1108
1152
 
1109
1153
  try {
1110
- const content = readFileSync(skill.filePath, "utf-8");
1111
- const body = stripFrontmatter(content).trim();
1112
- const skillBlock = `<skill name="${skill.name}" location="${skill.filePath}">\nReferences are relative to ${skill.baseDir}.\n\n${body}\n</skill>`;
1113
- return args ? `${skillBlock}\n\n${args}` : skillBlock;
1114
- } catch (err) {
1115
- // Emit error like extension commands do
1116
- this._extensionRunner?.emitError({
1117
- extensionPath: skill.filePath,
1118
- event: "skill_expansion",
1119
- error: getErrorMessage(err),
1120
- });
1121
- return text; // Return original on error
1154
+ return this._formatSkillInvocation(skillName, args);
1155
+ } catch {
1156
+ return text;
1122
1157
  }
1123
1158
  }
1124
1159
 
1160
+ private _createBuiltInSkillTool(): AgentTool {
1161
+ const skillSchema = Type.Object({
1162
+ skill: Type.String({ description: "The skill name. E.g., 'commit', 'review-pr', or 'pdf'" }),
1163
+ args: Type.Optional(Type.String({ description: "Optional arguments for the skill" })),
1164
+ });
1165
+
1166
+ return {
1167
+ name: "Skill",
1168
+ label: "Skill",
1169
+ description:
1170
+ "Execute a skill within the main conversation. Use this tool when users ask for a slash command or reference a skill by name. Returns the expanded skill block and appends args after it.",
1171
+ parameters: skillSchema,
1172
+ execute: async (_toolCallId, params: unknown) => {
1173
+ const input = params as { skill: string; args?: string };
1174
+ try {
1175
+ return {
1176
+ content: [
1177
+ {
1178
+ type: "text",
1179
+ text: this._expandSkillByName(input.skill, input.args),
1180
+ },
1181
+ ],
1182
+ details: undefined,
1183
+ };
1184
+ } catch (err) {
1185
+ return {
1186
+ content: [{ type: "text", text: getErrorMessage(err) }],
1187
+ details: undefined,
1188
+ };
1189
+ }
1190
+ },
1191
+ };
1192
+ }
1193
+
1194
+ private _getBuiltinToolNames(): string[] {
1195
+ return this._getBuiltinTools().map((tool) => tool.name);
1196
+ }
1197
+
1198
+ private _getBuiltinTools(): AgentTool[] {
1199
+ return [this._createBuiltInSkillTool()];
1200
+ }
1201
+
1202
+ private _getRegisteredToolDefinitions(): ToolDefinition[] {
1203
+ const registeredTools = this._extensionRunner?.getAllRegisteredTools() ?? [];
1204
+ return registeredTools.map((tool) => tool.definition);
1205
+ }
1206
+
1207
+ private _getBuiltinToolDefinitions(): ToolDefinition[] {
1208
+ return this._getBuiltinTools().map((tool) => ({
1209
+ name: tool.name,
1210
+ label: tool.label,
1211
+ description: tool.description,
1212
+ parameters: tool.parameters,
1213
+ execute: async () => ({ content: [], details: undefined }),
1214
+ }));
1215
+ }
1216
+
1217
+ getRenderableToolDefinition(toolName: string): ToolDefinition | undefined {
1218
+ return [...this._getBuiltinToolDefinitions(), ...this._getRegisteredToolDefinitions()].find(
1219
+ (tool) => tool.name === toolName,
1220
+ );
1221
+ }
1222
+
1125
1223
  /**
1126
1224
  * Queue a steering message to interrupt the agent mid-run.
1127
1225
  * Delivered after current tool execution, skips remaining tools.
@@ -1967,8 +2065,12 @@ export class AgentSession {
1967
2065
  const wrappedExtensionTools = this._extensionRunner
1968
2066
  ? wrapRegisteredTools(allCustomTools, this._extensionRunner)
1969
2067
  : [];
2068
+ const builtinTools = this._getBuiltinTools();
1970
2069
 
1971
2070
  const toolRegistry = new Map(this._baseToolRegistry);
2071
+ for (const tool of builtinTools) {
2072
+ toolRegistry.set(tool.name, tool);
2073
+ }
1972
2074
  for (const tool of wrappedExtensionTools as AgentTool[]) {
1973
2075
  toolRegistry.set(tool.name, tool);
1974
2076
  }
@@ -2694,14 +2796,11 @@ export class AgentSession {
2694
2796
  async exportToHtml(outputPath?: string): Promise<string> {
2695
2797
  const themeName = this.settingsManager.getTheme();
2696
2798
 
2697
- // Create tool renderer if we have an extension runner (for custom tool HTML rendering)
2698
- let toolRenderer: ToolHtmlRenderer | undefined;
2699
- if (this._extensionRunner) {
2700
- toolRenderer = createToolHtmlRenderer({
2701
- getToolDefinition: (name) => this._extensionRunner!.getToolDefinition(name),
2702
- theme,
2703
- });
2704
- }
2799
+ // Create tool renderer for extension and built-in tool HTML rendering
2800
+ const toolRenderer = createToolHtmlRenderer({
2801
+ getToolDefinition: (name) => this.getRenderableToolDefinition(name),
2802
+ theme,
2803
+ });
2705
2804
 
2706
2805
  return await exportSessionToHtml(this.sessionManager, this.state, {
2707
2806
  outputPath,
@@ -23,6 +23,12 @@ import * as _bundledYaml from "yaml";
23
23
  import * as _bundledMcpClient from "@modelcontextprotocol/sdk/client";
24
24
  import * as _bundledMcpStdio from "@modelcontextprotocol/sdk/client/stdio.js";
25
25
  import * as _bundledMcpStreamableHttp from "@modelcontextprotocol/sdk/client/streamableHttp.js";
26
+ import * as _bundledMcpSse from "@modelcontextprotocol/sdk/client/sse.js";
27
+ import * as _bundledMcpServer from "@modelcontextprotocol/sdk/server";
28
+ import * as _bundledMcpServerStdio from "@modelcontextprotocol/sdk/server/stdio.js";
29
+ import * as _bundledMcpServerSse from "@modelcontextprotocol/sdk/server/sse.js";
30
+ import * as _bundledMcpServerStreamableHttp from "@modelcontextprotocol/sdk/server/streamableHttp.js";
31
+ import * as _bundledMcpTypes from "@modelcontextprotocol/sdk/types.js";
26
32
  import { getAgentDir, isBunBinary } from "../../config.js";
27
33
  // NOTE: This import works because loader.ts exports are NOT re-exported from index.ts,
28
34
  // avoiding a circular dependency. Extensions can import from @gsd/pi-coding-agent.
@@ -44,8 +50,11 @@ import type {
44
50
  ToolDefinition,
45
51
  } from "./types.js";
46
52
 
47
- /** Modules available to extensions via virtualModules (for compiled Bun binary) */
48
- const VIRTUAL_MODULES: Record<string, unknown> = {
53
+ /**
54
+ * Statically imported modules for Bun binary virtualModules.
55
+ * Maps specifier -> module object for subpaths that must be available in compiled binaries.
56
+ */
57
+ const STATIC_BUNDLED_MODULES: Record<string, unknown> = {
49
58
  "@sinclair/typebox": _bundledTypebox,
50
59
  "@gsd/pi-agent-core": _bundledPiAgentCore,
51
60
  "@gsd/pi-tui": _bundledPiTui,
@@ -58,6 +67,17 @@ const VIRTUAL_MODULES: Record<string, unknown> = {
58
67
  "@modelcontextprotocol/sdk/client/stdio.js": _bundledMcpStdio,
59
68
  "@modelcontextprotocol/sdk/client/streamableHttp": _bundledMcpStreamableHttp,
60
69
  "@modelcontextprotocol/sdk/client/streamableHttp.js": _bundledMcpStreamableHttp,
70
+ "@modelcontextprotocol/sdk/client/sse": _bundledMcpSse,
71
+ "@modelcontextprotocol/sdk/client/sse.js": _bundledMcpSse,
72
+ "@modelcontextprotocol/sdk/server": _bundledMcpServer,
73
+ "@modelcontextprotocol/sdk/server/stdio": _bundledMcpServerStdio,
74
+ "@modelcontextprotocol/sdk/server/stdio.js": _bundledMcpServerStdio,
75
+ "@modelcontextprotocol/sdk/server/sse": _bundledMcpServerSse,
76
+ "@modelcontextprotocol/sdk/server/sse.js": _bundledMcpServerSse,
77
+ "@modelcontextprotocol/sdk/server/streamableHttp": _bundledMcpServerStreamableHttp,
78
+ "@modelcontextprotocol/sdk/server/streamableHttp.js": _bundledMcpServerStreamableHttp,
79
+ "@modelcontextprotocol/sdk/types": _bundledMcpTypes,
80
+ "@modelcontextprotocol/sdk/types.js": _bundledMcpTypes,
61
81
  // Aliases for external PI ecosystem packages that import from the original scope
62
82
  "@mariozechner/pi-agent-core": _bundledPiAgentCore,
63
83
  "@mariozechner/pi-tui": _bundledPiTui,
@@ -66,9 +86,198 @@ const VIRTUAL_MODULES: Record<string, unknown> = {
66
86
  "@mariozechner/pi-coding-agent": _bundledPiCodingAgent,
67
87
  };
68
88
 
89
+ /** Modules available to extensions via virtualModules (for compiled Bun binary) */
90
+ const VIRTUAL_MODULES: Record<string, unknown> = { ...STATIC_BUNDLED_MODULES };
91
+
69
92
  const require = createRequire(import.meta.url);
70
93
  const EXTENSION_TIMING_ENABLED = process.env.GSD_STARTUP_TIMING === "1" || process.env.PI_TIMING === "1";
71
94
 
95
+ /**
96
+ * Bundled npm packages whose subpath exports should be auto-resolved for extensions.
97
+ * Each package listed here will have its `exports` field read from package.json,
98
+ * and all subpath exports will be registered as jiti aliases (Node.js mode) so that
99
+ * extensions can import any standard subpath without hitting jiti's CJS double-resolve bug.
100
+ */
101
+ const BUNDLED_PACKAGES_WITH_EXPORTS = [
102
+ "@modelcontextprotocol/sdk",
103
+ "yaml",
104
+ ];
105
+
106
+ /**
107
+ * Read a package's `exports` field and return alias entries mapping
108
+ * specifiers (e.g. `@modelcontextprotocol/sdk/server`) to resolved file paths.
109
+ *
110
+ * Handles:
111
+ * - Explicit subpath exports: `./client` -> `@pkg/client`
112
+ * - Wildcard exports (`./*`): scans the package's dist directory for actual files
113
+ * - Both `.js`-suffixed and bare specifiers for each subpath
114
+ */
115
+ function resolveSubpathExports(packageName: string): Record<string, string> {
116
+ const aliases: Record<string, string> = {};
117
+
118
+ let packageJsonPath: string;
119
+ try {
120
+ // Resolve the package's root directory via its package.json
121
+ packageJsonPath = require.resolve(`${packageName}/package.json`);
122
+ } catch {
123
+ // Package doesn't allow importing package.json via exports — find it manually
124
+ try {
125
+ const anyEntry = require.resolve(packageName);
126
+ // Walk up from the resolved entry to find package.json
127
+ let dir = path.dirname(anyEntry);
128
+ while (dir !== path.dirname(dir)) {
129
+ const candidate = path.join(dir, "package.json");
130
+ if (fs.existsSync(candidate)) {
131
+ try {
132
+ const pkg = JSON.parse(fs.readFileSync(candidate, "utf-8"));
133
+ if (pkg.name === packageName) {
134
+ packageJsonPath = candidate;
135
+ break;
136
+ }
137
+ } catch {
138
+ // not valid JSON, keep walking
139
+ }
140
+ }
141
+ dir = path.dirname(dir);
142
+ }
143
+ } catch {
144
+ return aliases;
145
+ }
146
+ if (!packageJsonPath!) return aliases;
147
+ }
148
+
149
+ let pkg: { exports?: Record<string, unknown> };
150
+ try {
151
+ pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
152
+ } catch {
153
+ return aliases;
154
+ }
155
+
156
+ const exports = pkg.exports;
157
+ if (!exports || typeof exports !== "object") return aliases;
158
+
159
+ const packageDir = path.dirname(packageJsonPath);
160
+
161
+ for (const [subpath, target] of Object.entries(exports)) {
162
+ if (subpath === ".") continue; // Root export handled by static imports
163
+
164
+ // Handle wildcard exports like "./*"
165
+ if (subpath.includes("*")) {
166
+ resolveWildcardExports(packageName, packageDir, subpath, target, aliases);
167
+ continue;
168
+ }
169
+
170
+ // Explicit subpath: "./client" -> "@pkg/client"
171
+ const specifier = `${packageName}/${subpath.replace(/^\.\//, "")}`;
172
+
173
+ try {
174
+ const resolved = require.resolve(specifier);
175
+ aliases[specifier] = resolved;
176
+
177
+ // Add .js-suffixed variant if the specifier doesn't already end in .js
178
+ if (!specifier.endsWith(".js")) {
179
+ const jsSpecifier = `${specifier}.js`;
180
+ try {
181
+ const jsResolved = require.resolve(jsSpecifier);
182
+ aliases[jsSpecifier] = jsResolved;
183
+ } catch {
184
+ // .js variant doesn't resolve — that's fine
185
+ }
186
+ }
187
+
188
+ // Add bare variant (without .js) if it ends in .js
189
+ if (specifier.endsWith(".js")) {
190
+ const bareSpecifier = specifier.slice(0, -3);
191
+ try {
192
+ const bareResolved = require.resolve(bareSpecifier);
193
+ aliases[bareSpecifier] = bareResolved;
194
+ } catch {
195
+ // bare variant doesn't resolve — that's fine
196
+ }
197
+ }
198
+ } catch {
199
+ // Subpath doesn't resolve — skip it
200
+ }
201
+ }
202
+
203
+ return aliases;
204
+ }
205
+
206
+ /**
207
+ * Resolve wildcard export patterns (e.g. `./*`) by scanning the package's
208
+ * file structure to find all matching files and generate alias entries.
209
+ */
210
+ function resolveWildcardExports(
211
+ packageName: string,
212
+ packageDir: string,
213
+ subpathPattern: string,
214
+ target: unknown,
215
+ aliases: Record<string, string>,
216
+ ): void {
217
+ // Extract the target directory pattern from the export target
218
+ // e.g. { "require": "./dist/cjs/*" } -> "dist/cjs"
219
+ let targetDir: string | null = null;
220
+
221
+ if (typeof target === "string") {
222
+ targetDir = target.replace(/\/\*$/, "").replace(/^\.\//, "");
223
+ } else if (target && typeof target === "object") {
224
+ const targetObj = target as Record<string, unknown>;
225
+ // Prefer "require" for CJS compatibility with jiti, fall back to "import"
226
+ const resolved = targetObj.require ?? targetObj.import ?? targetObj.default;
227
+ if (typeof resolved === "string") {
228
+ targetDir = resolved.replace(/\/\*$/, "").replace(/^\.\//, "");
229
+ }
230
+ }
231
+
232
+ if (!targetDir) return;
233
+
234
+ const fullTargetDir = path.join(packageDir, targetDir);
235
+ if (!fs.existsSync(fullTargetDir)) return;
236
+
237
+ // Scan for .js files and generate specifiers
238
+ const subpathPrefix = subpathPattern.replace(/\/?\*$/, "").replace(/^\.\//, "");
239
+ scanDirForExports(packageName, fullTargetDir, subpathPrefix, aliases);
240
+ }
241
+
242
+ /**
243
+ * Recursively scan a directory for .js files and register them as aliases.
244
+ */
245
+ function scanDirForExports(
246
+ packageName: string,
247
+ dir: string,
248
+ relativePath: string,
249
+ aliases: Record<string, string>,
250
+ ): void {
251
+ let entries: fs.Dirent[];
252
+ try {
253
+ entries = fs.readdirSync(dir, { withFileTypes: true });
254
+ } catch {
255
+ return;
256
+ }
257
+
258
+ for (const entry of entries) {
259
+ const entryRelative = relativePath ? `${relativePath}/${entry.name}` : entry.name;
260
+
261
+ if (entry.isDirectory()) {
262
+ // Skip examples/test directories — extensions don't need them
263
+ if (entry.name === "examples" || entry.name === "__tests__" || entry.name === "test") continue;
264
+ scanDirForExports(packageName, path.join(dir, entry.name), entryRelative, aliases);
265
+ } else if (entry.name.endsWith(".js") && !entry.name.endsWith(".d.js")) {
266
+ const filePath = path.join(dir, entry.name);
267
+ const specifier = `${packageName}/${entryRelative}`;
268
+ // Only add if not already covered by an explicit export
269
+ if (!(specifier in aliases)) {
270
+ aliases[specifier] = filePath;
271
+ }
272
+ // Also add bare (no .js) variant
273
+ const bareSpecifier = specifier.replace(/\.js$/, "");
274
+ if (!(bareSpecifier in aliases)) {
275
+ aliases[bareSpecifier] = filePath;
276
+ }
277
+ }
278
+ }
279
+ }
280
+
72
281
  function logExtensionTiming(extensionPath: string, ms: number, outcome: "loaded" | "failed"): void {
73
282
  if (!EXTENSION_TIMING_ENABLED) return;
74
283
  console.error(`[startup] extension ${outcome}: ${extensionPath} (${ms}ms)`);
@@ -100,7 +309,19 @@ function getAliases(): Record<string, string> {
100
309
  return fileURLToPath(import.meta.resolve(specifier));
101
310
  };
102
311
 
312
+ // Auto-discover subpath exports from bundled npm packages.
313
+ // This ensures extensions can import any standard subpath (e.g. @modelcontextprotocol/sdk/server)
314
+ // without hitting jiti's CJS double-resolve bug.
315
+ const autoDiscovered: Record<string, string> = {};
316
+ for (const packageName of BUNDLED_PACKAGES_WITH_EXPORTS) {
317
+ const subpathAliases = resolveSubpathExports(packageName);
318
+ Object.assign(autoDiscovered, subpathAliases);
319
+ }
320
+
103
321
  _aliases = {
322
+ // Auto-discovered subpath exports (lowest priority — overridden by manual entries below)
323
+ ...autoDiscovered,
324
+ // Manual entries for workspace packages and packages needing special resolution
104
325
  "@gsd/pi-coding-agent": packageIndex,
105
326
  "@gsd/pi-agent-core": resolveWorkspaceOrImport("agent/dist/index.js", "@gsd/pi-agent-core"),
106
327
  "@gsd/pi-tui": resolveWorkspaceOrImport("tui/dist/index.js", "@gsd/pi-tui"),
@@ -108,11 +329,6 @@ function getAliases(): Record<string, string> {
108
329
  "@gsd/pi-ai/oauth": resolveWorkspaceOrImport("ai/dist/oauth.js", "@gsd/pi-ai/oauth"),
109
330
  "@sinclair/typebox": typeboxRoot,
110
331
  "yaml": yamlRoot,
111
- "@modelcontextprotocol/sdk/client": require.resolve("@modelcontextprotocol/sdk/client"),
112
- "@modelcontextprotocol/sdk/client/stdio": require.resolve("@modelcontextprotocol/sdk/client/stdio.js"),
113
- "@modelcontextprotocol/sdk/client/stdio.js": require.resolve("@modelcontextprotocol/sdk/client/stdio.js"),
114
- "@modelcontextprotocol/sdk/client/streamableHttp": require.resolve("@modelcontextprotocol/sdk/client/streamableHttp.js"),
115
- "@modelcontextprotocol/sdk/client/streamableHttp.js": require.resolve("@modelcontextprotocol/sdk/client/streamableHttp.js"),
116
332
  // Aliases for external PI ecosystem packages that import from the original scope
117
333
  "@mariozechner/pi-coding-agent": packageIndex,
118
334
  "@mariozechner/pi-agent-core": resolveWorkspaceOrImport("agent/dist/index.js", "@gsd/pi-agent-core"),
@@ -414,7 +414,13 @@ function resolveExtensionEntries(dir: string): string[] | null {
414
414
  const packageJsonPath = join(dir, "package.json");
415
415
  if (existsSync(packageJsonPath)) {
416
416
  const manifest = readPiManifestFile(packageJsonPath);
417
- if (manifest?.extensions?.length) {
417
+ if (manifest) {
418
+ // When a pi manifest exists, it is authoritative — don't fall through
419
+ // to index.ts/index.js auto-detection. This allows library directories
420
+ // (like cmux) to opt out by declaring "pi": {} with no extensions.
421
+ if (!manifest.extensions?.length) {
422
+ return null;
423
+ }
418
424
  const entries: string[] = [];
419
425
  for (const extPath of manifest.extensions) {
420
426
  const resolvedExtPath = resolve(dir, extPath);
@@ -422,9 +428,7 @@ function resolveExtensionEntries(dir: string): string[] | null {
422
428
  entries.push(resolvedExtPath);
423
429
  }
424
430
  }
425
- if (entries.length > 0) {
426
- return entries;
427
- }
431
+ return entries.length > 0 ? entries : null;
428
432
  }
429
433
  }
430
434
 
@@ -0,0 +1,89 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { afterEach, beforeEach, describe, it } from "node:test";
6
+
7
+ import { Agent } from "@gsd/pi-agent-core";
8
+ import { AuthStorage } from "./auth-storage.js";
9
+ import { AgentSession } from "./agent-session.js";
10
+ import { ModelRegistry } from "./model-registry.js";
11
+ import { DefaultResourceLoader } from "./resource-loader.js";
12
+ import { SessionManager } from "./session-manager.js";
13
+ import { SettingsManager } from "./settings-manager.js";
14
+
15
+ let testDir: string;
16
+
17
+ function writeSkill(cwd: string, name: string, description: string, body = `# ${name}\n`): string {
18
+ const skillDir = join(cwd, ".pi", "skills", name);
19
+ mkdirSync(skillDir, { recursive: true });
20
+ const skillPath = join(skillDir, "SKILL.md");
21
+ writeFileSync(skillPath, `---\nname: ${name}\ndescription: ${description}\n---\n\n${body}`);
22
+ return skillPath;
23
+ }
24
+
25
+ describe("Skill tool", () => {
26
+ beforeEach(() => {
27
+ testDir = mkdtempSync(join(tmpdir(), "skill-tool-test-"));
28
+ });
29
+
30
+ afterEach(() => {
31
+ rmSync(testDir, { recursive: true, force: true });
32
+ });
33
+
34
+ async function createSession() {
35
+ const agentDir = join(testDir, "agent-home");
36
+ const authStorage = AuthStorage.inMemory({});
37
+ const modelRegistry = new ModelRegistry(authStorage, join(agentDir, "models.json"));
38
+ const settingsManager = SettingsManager.inMemory();
39
+ const resourceLoader = new DefaultResourceLoader({
40
+ cwd: testDir,
41
+ agentDir,
42
+ settingsManager,
43
+ noExtensions: true,
44
+ noPromptTemplates: true,
45
+ noThemes: true,
46
+ });
47
+ await resourceLoader.reload();
48
+
49
+ return new AgentSession({
50
+ agent: new Agent(),
51
+ sessionManager: SessionManager.inMemory(testDir),
52
+ settingsManager,
53
+ cwd: testDir,
54
+ resourceLoader,
55
+ modelRegistry,
56
+ });
57
+ }
58
+
59
+ it("resolves a project-level skill to the exact skill block format", async () => {
60
+ const skillPath = writeSkill(
61
+ testDir,
62
+ "swift-testing",
63
+ "Use for Swift Testing assertions and verification patterns.",
64
+ "# Swift Testing\nUse this skill.\n",
65
+ );
66
+ const session = await createSession();
67
+
68
+ const tool = session.state.tools.find((entry) => entry.name === "Skill");
69
+ assert.ok(tool, "Skill tool should be registered");
70
+
71
+ const result = await tool.execute("call-1", { skill: "swift-testing" });
72
+ assert.equal(
73
+ result.content[0]?.type === "text" ? result.content[0].text : "",
74
+ `<skill name="swift-testing" location="${skillPath}">\nReferences are relative to ${join(testDir, ".pi", "skills", "swift-testing")}.\n\n# Swift Testing\nUse this skill.\n</skill>`,
75
+ );
76
+ });
77
+
78
+ it("returns a helpful error for unknown skills", async () => {
79
+ writeSkill(testDir, "swift-testing", "Use for Swift Testing assertions and verification patterns.");
80
+ const session = await createSession();
81
+ const tool = session.state.tools.find((entry) => entry.name === "Skill");
82
+ assert.ok(tool, "Skill tool should be registered");
83
+
84
+ const result = await tool.execute("call-2", { skill: "nonexistent" });
85
+ const message = result.content[0]?.type === "text" ? result.content[0].text : "";
86
+ assert.match(message, /^Skill "nonexistent" not found\. Available skills: /);
87
+ assert.match(message, /swift-testing/);
88
+ });
89
+ });
@@ -81,6 +81,12 @@ export interface LoadSkillsResult {
81
81
  diagnostics: ResourceDiagnostic[];
82
82
  }
83
83
 
84
+ let loadedSkills: Skill[] = [];
85
+
86
+ export function getLoadedSkills(): Skill[] {
87
+ return [...loadedSkills];
88
+ }
89
+
84
90
  /**
85
91
  * Validate skill name per Agent Skills spec.
86
92
  * Returns array of validation error messages (empty if valid).
@@ -293,7 +299,8 @@ export function formatSkillsForPrompt(skills: Skill[]): string {
293
299
 
294
300
  const lines = [
295
301
  "\n\nThe following skills provide specialized instructions for specific tasks.",
296
- "Use the read tool to load a skill's file when the task matches its description.",
302
+ "Use the Skill tool with the exact skill name from <available_skills> when the task matches its description.",
303
+ "If the Skill tool reports an unknown skill, do not guess: use an exact name from <available_skills> or tell the user the skill is unavailable.",
297
304
  "When a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.",
298
305
  "",
299
306
  "<available_skills>",
@@ -449,8 +456,10 @@ export function loadSkills(options: LoadSkillsOptions = {}): LoadSkillsResult {
449
456
  }
450
457
  }
451
458
 
459
+ loadedSkills = Array.from(skillMap.values());
460
+
452
461
  return {
453
- skills: Array.from(skillMap.values()),
462
+ skills: [...loadedSkills],
454
463
  diagnostics: [...allDiagnostics, ...collisionDiagnostics],
455
464
  };
456
465
  }
@@ -213,6 +213,7 @@ export {
213
213
  // Skills
214
214
  export {
215
215
  formatSkillsForPrompt,
216
+ getLoadedSkills,
216
217
  type LoadSkillsFromDirOptions,
217
218
  type LoadSkillsResult,
218
219
  loadSkills,