gsd-pi 2.68.0 → 2.68.1-dev.abc8f2b

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 (258) hide show
  1. package/README.md +66 -70
  2. package/dist/resources/extensions/gsd/auto-model-selection.js +27 -1
  3. package/dist/resources/extensions/gsd/auto.js +8 -2
  4. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +7 -0
  5. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +1 -5
  6. package/dist/resources/extensions/gsd/guided-flow.js +25 -70
  7. package/dist/resources/extensions/gsd/model-router.js +85 -2
  8. package/dist/resources/extensions/gsd/prompts/discuss.md +2 -0
  9. package/dist/resources/extensions/gsd/templates/context.md +34 -2
  10. package/dist/web/standalone/.next/BUILD_ID +1 -1
  11. package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
  12. package/dist/web/standalone/.next/build-manifest.json +3 -3
  13. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  14. package/dist/web/standalone/.next/required-server-files.json +3 -3
  15. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  16. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  17. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  18. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  19. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  20. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  21. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  22. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  23. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  24. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  25. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  26. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  27. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  28. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  29. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  30. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  32. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  33. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  35. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  36. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  37. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  38. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  39. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  40. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  41. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  42. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  43. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  44. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  45. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  46. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  47. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  48. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  49. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  50. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  51. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  52. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  53. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  54. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  55. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  56. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  57. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  58. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  59. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  60. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  61. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  62. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  63. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  64. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  65. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  66. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  67. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  68. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  69. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  70. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  71. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  72. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  73. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  74. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  75. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  76. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  77. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  78. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  79. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  80. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  81. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  82. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  83. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  84. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  86. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  87. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  88. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  89. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  90. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  92. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  93. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  106. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +2 -2
  110. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/index.html +1 -1
  120. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  121. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  122. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  123. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  124. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  125. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  126. package/dist/web/standalone/.next/server/app/page.js +2 -2
  127. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  128. package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
  129. package/dist/web/standalone/.next/server/chunks/63.js +3 -3
  130. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  131. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  132. package/dist/web/standalone/.next/server/middleware.js +2 -2
  133. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  134. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  135. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  136. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  137. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  138. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  139. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  140. package/dist/web/standalone/.next/static/chunks/app/page-f1e30ab6bb269149.js +1 -0
  141. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  142. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  143. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  144. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  145. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  146. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  147. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  148. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  149. package/dist/web/standalone/server.js +1 -1
  150. package/package.json +1 -1
  151. package/packages/pi-ai/dist/index.d.ts +3 -0
  152. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  153. package/packages/pi-ai/dist/index.js +2 -0
  154. package/packages/pi-ai/dist/index.js.map +1 -1
  155. package/packages/pi-ai/dist/providers/amazon-bedrock.js +2 -2
  156. package/packages/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
  157. package/packages/pi-ai/dist/providers/anthropic-shared.js +2 -2
  158. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
  159. package/packages/pi-ai/dist/providers/google-shared.js +2 -2
  160. package/packages/pi-ai/dist/providers/google-shared.js.map +1 -1
  161. package/packages/pi-ai/dist/providers/mistral.js +2 -2
  162. package/packages/pi-ai/dist/providers/mistral.js.map +1 -1
  163. package/packages/pi-ai/dist/providers/openai-completions.js +2 -2
  164. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  165. package/packages/pi-ai/dist/providers/openai-responses-shared.js +2 -2
  166. package/packages/pi-ai/dist/providers/openai-responses-shared.js.map +1 -1
  167. package/packages/pi-ai/dist/providers/provider-capabilities.d.ts +59 -0
  168. package/packages/pi-ai/dist/providers/provider-capabilities.d.ts.map +1 -0
  169. package/packages/pi-ai/dist/providers/provider-capabilities.js +173 -0
  170. package/packages/pi-ai/dist/providers/provider-capabilities.js.map +1 -0
  171. package/packages/pi-ai/dist/providers/provider-capabilities.test.d.ts +2 -0
  172. package/packages/pi-ai/dist/providers/provider-capabilities.test.d.ts.map +1 -0
  173. package/packages/pi-ai/dist/providers/provider-capabilities.test.js +132 -0
  174. package/packages/pi-ai/dist/providers/provider-capabilities.test.js.map +1 -0
  175. package/packages/pi-ai/dist/providers/transform-messages-report.test.d.ts +2 -0
  176. package/packages/pi-ai/dist/providers/transform-messages-report.test.d.ts.map +1 -0
  177. package/packages/pi-ai/dist/providers/transform-messages-report.test.js +172 -0
  178. package/packages/pi-ai/dist/providers/transform-messages-report.test.js.map +1 -0
  179. package/packages/pi-ai/dist/providers/transform-messages.d.ts +34 -1
  180. package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
  181. package/packages/pi-ai/dist/providers/transform-messages.js +73 -2
  182. package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
  183. package/packages/pi-ai/src/index.ts +3 -0
  184. package/packages/pi-ai/src/providers/amazon-bedrock.ts +2 -2
  185. package/packages/pi-ai/src/providers/anthropic-shared.ts +2 -2
  186. package/packages/pi-ai/src/providers/google-shared.ts +2 -2
  187. package/packages/pi-ai/src/providers/mistral.ts +2 -2
  188. package/packages/pi-ai/src/providers/openai-completions.ts +2 -2
  189. package/packages/pi-ai/src/providers/openai-responses-shared.ts +2 -2
  190. package/packages/pi-ai/src/providers/provider-capabilities.test.ts +174 -0
  191. package/packages/pi-ai/src/providers/provider-capabilities.ts +215 -0
  192. package/packages/pi-ai/src/providers/transform-messages-report.test.ts +189 -0
  193. package/packages/pi-ai/src/providers/transform-messages.ts +94 -1
  194. package/packages/pi-coding-agent/dist/core/extensions/index.d.ts +1 -1
  195. package/packages/pi-coding-agent/dist/core/extensions/index.d.ts.map +1 -1
  196. package/packages/pi-coding-agent/dist/core/extensions/index.js.map +1 -1
  197. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  198. package/packages/pi-coding-agent/dist/core/extensions/loader.js +10 -1
  199. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  200. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -1
  201. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  202. package/packages/pi-coding-agent/dist/core/extensions/runner.js +15 -0
  203. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  204. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +41 -0
  205. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  206. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  207. package/packages/pi-coding-agent/dist/core/tools/index.d.ts +1 -0
  208. package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
  209. package/packages/pi-coding-agent/dist/core/tools/index.js +1 -0
  210. package/packages/pi-coding-agent/dist/core/tools/index.js.map +1 -1
  211. package/packages/pi-coding-agent/dist/core/tools/tool-compatibility-registry.d.ts +27 -0
  212. package/packages/pi-coding-agent/dist/core/tools/tool-compatibility-registry.d.ts.map +1 -0
  213. package/packages/pi-coding-agent/dist/core/tools/tool-compatibility-registry.js +69 -0
  214. package/packages/pi-coding-agent/dist/core/tools/tool-compatibility-registry.js.map +1 -0
  215. package/packages/pi-coding-agent/dist/index.d.ts +2 -2
  216. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  217. package/packages/pi-coding-agent/dist/index.js +3 -1
  218. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  219. package/packages/pi-coding-agent/package.json +1 -1
  220. package/packages/pi-coding-agent/src/core/extensions/index.ts +4 -0
  221. package/packages/pi-coding-agent/src/core/extensions/loader.ts +11 -1
  222. package/packages/pi-coding-agent/src/core/extensions/runner.ts +18 -0
  223. package/packages/pi-coding-agent/src/core/extensions/types.ts +45 -0
  224. package/packages/pi-coding-agent/src/core/tools/index.ts +7 -0
  225. package/packages/pi-coding-agent/src/core/tools/tool-compatibility-registry.ts +83 -0
  226. package/packages/pi-coding-agent/src/index.ts +9 -0
  227. package/pkg/package.json +1 -1
  228. package/src/resources/extensions/gsd/auto-model-selection.ts +36 -4
  229. package/src/resources/extensions/gsd/auto.ts +8 -2
  230. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +8 -0
  231. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +1 -5
  232. package/src/resources/extensions/gsd/guided-flow.ts +22 -84
  233. package/src/resources/extensions/gsd/model-router.ts +117 -10
  234. package/src/resources/extensions/gsd/preferences-types.ts +3 -1
  235. package/src/resources/extensions/gsd/prompts/discuss.md +2 -0
  236. package/src/resources/extensions/gsd/templates/context.md +34 -2
  237. package/src/resources/extensions/gsd/tests/capability-router.test.ts +31 -7
  238. package/src/resources/extensions/gsd/tests/discord-invite-links.test.ts +1 -1
  239. package/src/resources/extensions/gsd/tests/model-router.test.ts +2 -2
  240. package/src/resources/extensions/gsd/tests/resource-loader-import-path.test.ts +37 -0
  241. package/src/resources/extensions/gsd/tests/tool-compatibility.test.ts +199 -0
  242. package/src/resources/extensions/gsd/tests/write-gate.test.ts +13 -16
  243. package/dist/resources/extensions/gsd/prompt-validation.js +0 -67
  244. package/dist/resources/extensions/gsd/prompts/discuss-prepared.md +0 -424
  245. package/dist/resources/extensions/gsd/templates/context-enhanced.md +0 -138
  246. package/dist/web/standalone/.next/static/chunks/app/page-7115e62689b5fd84.js +0 -1
  247. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  248. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  249. package/src/resources/extensions/gsd/prompt-validation.ts +0 -88
  250. package/src/resources/extensions/gsd/prompts/discuss-prepared.md +0 -424
  251. package/src/resources/extensions/gsd/templates/context-enhanced.md +0 -138
  252. package/src/resources/extensions/gsd/tests/adversarial-review-fixes.test.ts +0 -223
  253. package/src/resources/extensions/gsd/tests/integration/test-isolation.ts +0 -53
  254. package/src/resources/extensions/gsd/tests/integration-prepared-discussion.test.ts +0 -525
  255. package/src/resources/extensions/gsd/tests/preparation.test.ts +0 -1211
  256. package/src/resources/extensions/gsd/tests/prompt-builder.test.ts +0 -669
  257. /package/dist/web/standalone/.next/static/{ka3ShQTakcliYL-EXRRb6 → 3HMOXcBoys84RYd2F8a79}/_buildManifest.js +0 -0
  258. /package/dist/web/standalone/.next/static/{ka3ShQTakcliYL-EXRRb6 → 3HMOXcBoys84RYd2F8a79}/_ssgManifest.js +0 -0
@@ -53,25 +53,8 @@ import {
53
53
  runPreparation,
54
54
  formatCodebaseBrief,
55
55
  formatPriorContextBrief,
56
- formatEcosystemBrief,
57
- type PreparationResult,
58
56
  } from "./preparation.js";
59
57
 
60
- // ─── Preparation result storage ─────────────────────────────────────────────
61
- // Stores the most recent preparation result for injection into discuss prompts.
62
- // S02 will consume this when building the prepared discussion prompt.
63
- let lastPreparationResult: PreparationResult | null = null;
64
-
65
- /** Get the most recent preparation result (for S02 prompt building). */
66
- export function getLastPreparationResult(): PreparationResult | null {
67
- return lastPreparationResult;
68
- }
69
-
70
- /** Clear the preparation result (called after discussion completes). */
71
- export function clearPreparationResult(): void {
72
- lastPreparationResult = null;
73
- }
74
-
75
58
  // ─── Re-exports (preserve public API for existing importers) ────────────────
76
59
  export {
77
60
  MILESTONE_ID_RE, generateMilestoneSuffix, nextMilestoneId,
@@ -427,7 +410,7 @@ function resolveAvailableModel<T extends { id: string; provider: string }>(
427
410
  * Build the discuss-and-plan prompt for a new milestone.
428
411
  * Used by all three "new milestone" paths (first ever, no active, all complete).
429
412
  */
430
- function buildDiscussPrompt(nextId: string, preamble: string, _basePath: string): string {
413
+ function buildDiscussPrompt(nextId: string, preamble: string, _basePath: string, preparationContext?: string): string {
431
414
  const milestoneRel = `.gsd/milestones/${nextId}`;
432
415
  const inlinedTemplates = [
433
416
  inlineTemplate("project", "Project"),
@@ -439,6 +422,7 @@ function buildDiscussPrompt(nextId: string, preamble: string, _basePath: string)
439
422
  return loadPrompt("discuss", {
440
423
  milestoneId: nextId,
441
424
  preamble,
425
+ preparationContext: preparationContext ?? "",
442
426
  contextPath: `${milestoneRel}/${nextId}-CONTEXT.md`,
443
427
  roadmapPath: `${milestoneRel}/${nextId}-ROADMAP.md`,
444
428
  inlinedTemplates,
@@ -471,59 +455,12 @@ function buildHeadlessDiscussPrompt(nextId: string, seedContext: string, _basePa
471
455
  });
472
456
  }
473
457
 
474
- /**
475
- * Build the prepared discuss prompt with brief injection.
476
- * Uses the discuss-prepared template which encodes the 4-layer discussion protocol.
477
- *
478
- * @param nextId - The milestone ID being discussed
479
- * @param preamble - Preamble text for the discuss prompt
480
- * @param _basePath - Root directory of the project (unused, kept for signature consistency)
481
- * @param prepResult - Preparation result containing briefs to inject
482
- * @returns The prepared discuss prompt string
483
- */
484
- function buildPreparedPrompt(
485
- nextId: string,
486
- preamble: string,
487
- _basePath: string,
488
- prepResult: PreparationResult,
489
- ): string {
490
- const milestoneRel = `.gsd/milestones/${nextId}`;
491
-
492
- // Use context-enhanced instead of context for prepared discussions
493
- const inlinedTemplates = [
494
- inlineTemplate("project", "Project"),
495
- inlineTemplate("requirements", "Requirements"),
496
- inlineTemplate("context-enhanced", "Context Enhanced"),
497
- inlineTemplate("roadmap", "Roadmap"),
498
- inlineTemplate("decisions", "Decisions"),
499
- ].join("\n\n---\n\n");
500
-
501
- // Format the briefs from the preparation result
502
- const codebaseBrief = prepResult.codebaseBrief || formatCodebaseBrief(prepResult.codebase);
503
- const priorContextBrief = prepResult.priorContextBrief || formatPriorContextBrief(prepResult.priorContext);
504
- const ecosystemBrief = prepResult.ecosystemBrief || formatEcosystemBrief(prepResult.ecosystem);
505
-
506
- return loadPrompt("discuss-prepared", {
507
- milestoneId: nextId,
508
- preamble,
509
- codebaseBrief,
510
- priorContextBrief,
511
- ecosystemBrief,
512
- contextPath: `${milestoneRel}/${nextId}-CONTEXT.md`,
513
- roadmapPath: `${milestoneRel}/${nextId}-ROADMAP.md`,
514
- inlinedTemplates,
515
- commitInstruction: buildDocsCommitInstruction(`docs(${nextId}): context, requirements, and roadmap`),
516
- multiMilestoneCommitInstruction: buildDocsCommitInstruction("docs: project plan — N milestones"),
517
- });
518
- }
519
-
520
458
  /**
521
459
  * Run preparation phase if enabled, then build the discuss prompt.
522
- * This is the main entry point for new milestone discussions with preparation.
523
- * Stores the preparation result for S02 to inject into the discuss prompt.
524
- *
525
- * When preparation succeeds, uses the discuss-prepared template with brief injection.
526
- * Falls back to the standard discuss template when preparation is disabled or fails.
460
+ * Preparation analyzes the codebase and prior context, injecting the results
461
+ * as supplementary context into the standard discuss template. The discuss
462
+ * template drives the conversation (asks "What's the vision?" first), while
463
+ * the preparation briefs give the agent grounding in the existing codebase.
527
464
  *
528
465
  * @param ctx - Extension command context with UI for progress notifications
529
466
  * @param nextId - The milestone ID being discussed
@@ -537,14 +474,13 @@ async function prepareAndBuildDiscussPrompt(
537
474
  preamble: string,
538
475
  basePath: string,
539
476
  ): Promise<string> {
540
- // Clear stale preparation result immediately to prevent cross-session/project
541
- // state leaks. This ensures data from a prior milestone/project never leaks
542
- // into subsequent discussions (adversarial review fix #3602).
543
- lastPreparationResult = null;
544
-
545
477
  const prefs = loadEffectiveGSDPreferences()?.preferences ?? {};
546
478
 
547
- // Run preparation if enabled (default: true)
479
+ // Run preparation if enabled (default: true) — results are injected as
480
+ // supplementary context into the standard discuss prompt, NOT as a
481
+ // replacement template. The discuss prompt always leads with "What's the
482
+ // vision?" so the user defines the scope, not the codebase analysis.
483
+ let preparationContext = "";
548
484
  if (prefs.discuss_preparation !== false) {
549
485
  try {
550
486
  const prepResult = await runPreparation(basePath, ctx.ui, {
@@ -552,21 +488,23 @@ async function prepareAndBuildDiscussPrompt(
552
488
  discuss_web_research: prefs.discuss_web_research,
553
489
  discuss_depth: prefs.discuss_depth,
554
490
  });
555
- lastPreparationResult = prepResult;
556
491
 
557
- // Use prepared prompt if preparation was enabled and produced results
558
492
  if (prepResult.enabled) {
559
- return buildPreparedPrompt(nextId, preamble, basePath, prepResult);
493
+ const codebaseBrief = prepResult.codebaseBrief || formatCodebaseBrief(prepResult.codebase);
494
+ const priorContextBrief = prepResult.priorContextBrief || formatPriorContextBrief(prepResult.priorContext);
495
+ const parts: string[] = [];
496
+ if (codebaseBrief) parts.push(`### Codebase Brief\n\n${codebaseBrief}`);
497
+ if (priorContextBrief) parts.push(`### Prior Context Brief\n\n${priorContextBrief}`);
498
+ if (parts.length > 0) {
499
+ preparationContext = `\n\n## Preparation Context\n\nThe system analyzed the codebase before this discussion. Use these findings as background context — they describe what already exists, NOT what the user wants to build. Always ask the user what they want to build first.\n\n${parts.join("\n\n")}`;
500
+ }
560
501
  }
561
- } catch {
562
- // If preparation throws, ensure stale data doesn't persist
563
- lastPreparationResult = null;
502
+ } catch (err) {
503
+ logWarning("guided", `preparation failed, proceeding without context: ${(err as Error).message}`);
564
504
  }
565
505
  }
566
506
 
567
- // Fall back to standard discuss prompt for backward compatibility
568
- // lastPreparationResult is already null (cleared at entry or on error)
569
- return buildDiscussPrompt(nextId, preamble, basePath);
507
+ return buildDiscussPrompt(nextId, preamble, basePath, preparationContext);
570
508
  }
571
509
 
572
510
  /**
@@ -5,6 +5,9 @@
5
5
  import type { ComplexityTier, ClassificationResult, TaskMetadata } from "./complexity-classifier.js";
6
6
  import { tierOrdinal } from "./complexity-classifier.js";
7
7
  import type { ResolvedModelConfig } from "./preferences.js";
8
+ import { getProviderCapabilities, type ProviderCapabilities } from "@gsd/pi-ai";
9
+ import { getToolCompatibility, getAllToolCompatibility } from "@gsd/pi-coding-agent";
10
+ import type { ToolCompatibility } from "@gsd/pi-coding-agent";
8
11
 
9
12
  // ─── Types ───────────────────────────────────────────────────────────────────
10
13
 
@@ -37,6 +40,8 @@ export interface RoutingDecision {
37
40
  selectionMethod: "tier-only" | "capability-scored";
38
41
  /** Capability scores per eligible model (capability-scored path only) */
39
42
  capabilityScores?: Record<string, number>;
43
+ /** Tools filtered out due to provider incompatibility (ADR-005) */
44
+ filteredTools?: string[];
40
45
  /** Task requirement vector used for scoring */
41
46
  taskRequirements?: Partial<Record<string, number>>;
42
47
  }
@@ -58,7 +63,7 @@ export interface ModelCapabilities {
58
63
  // Maps known model IDs to their capability tier. Used when tier_models is not
59
64
  // explicitly configured to pick the best available model for each tier.
60
65
 
61
- const MODEL_CAPABILITY_TIER: Record<string, ComplexityTier> = {
66
+ export const MODEL_CAPABILITY_TIER: Record<string, ComplexityTier> = {
62
67
  // Light-tier models (cheapest)
63
68
  "claude-haiku-4-5": "light",
64
69
  "claude-3-5-haiku-latest": "light",
@@ -139,15 +144,49 @@ const MODEL_COST_PER_1K_INPUT: Record<string, number> = {
139
144
  // model selection within an eligible tier set.
140
145
 
141
146
  export const MODEL_CAPABILITY_PROFILES: Record<string, ModelCapabilities> = {
142
- "claude-opus-4-6": { coding: 95, debugging: 90, research: 85, reasoning: 95, speed: 30, longContext: 80, instruction: 90 },
143
- "claude-sonnet-4-6": { coding: 85, debugging: 80, research: 75, reasoning: 80, speed: 60, longContext: 75, instruction: 85 },
144
- "claude-haiku-4-5": { coding: 60, debugging: 50, research: 45, reasoning: 50, speed: 95, longContext: 50, instruction: 75 },
145
- "gpt-4o": { coding: 80, debugging: 75, research: 70, reasoning: 75, speed: 65, longContext: 70, instruction: 80 },
146
- "gpt-4o-mini": { coding: 55, debugging: 45, research: 40, reasoning: 45, speed: 90, longContext: 45, instruction: 70 },
147
- "gemini-2.5-pro": { coding: 75, debugging: 70, research: 85, reasoning: 75, speed: 55, longContext: 90, instruction: 75 },
148
- "gemini-2.0-flash": { coding: 50, debugging: 40, research: 50, reasoning: 40, speed: 95, longContext: 60, instruction: 65 },
149
- "deepseek-chat": { coding: 75, debugging: 65, research: 55, reasoning: 70, speed: 70, longContext: 55, instruction: 65 },
150
- "o3": { coding: 80, debugging: 85, research: 80, reasoning: 92, speed: 25, longContext: 70, instruction: 85 },
147
+ // ── Anthropic ──────────────────────────────────────────────────────────────
148
+ "claude-opus-4-6": { coding: 95, debugging: 90, research: 85, reasoning: 95, speed: 30, longContext: 80, instruction: 90 },
149
+ "claude-sonnet-4-6": { coding: 85, debugging: 80, research: 75, reasoning: 80, speed: 60, longContext: 75, instruction: 85 },
150
+ "claude-sonnet-4-5-20250514": { coding: 85, debugging: 80, research: 75, reasoning: 80, speed: 60, longContext: 75, instruction: 85 },
151
+ "claude-3-5-sonnet-latest": { coding: 82, debugging: 78, research: 72, reasoning: 78, speed: 62, longContext: 70, instruction: 82 },
152
+ "claude-haiku-4-5": { coding: 60, debugging: 50, research: 45, reasoning: 50, speed: 95, longContext: 50, instruction: 75 },
153
+ "claude-3-5-haiku-latest": { coding: 60, debugging: 50, research: 45, reasoning: 50, speed: 95, longContext: 50, instruction: 75 },
154
+ "claude-3-haiku-20240307": { coding: 50, debugging: 40, research: 35, reasoning: 40, speed: 95, longContext: 40, instruction: 65 },
155
+ "claude-3-opus-latest": { coding: 90, debugging: 85, research: 82, reasoning: 90, speed: 35, longContext: 75, instruction: 88 },
156
+
157
+ // ── OpenAI GPT ─────────────────────────────────────────────────────────────
158
+ "gpt-4o": { coding: 80, debugging: 75, research: 70, reasoning: 75, speed: 65, longContext: 70, instruction: 80 },
159
+ "gpt-4o-mini": { coding: 55, debugging: 45, research: 40, reasoning: 45, speed: 90, longContext: 45, instruction: 70 },
160
+ "gpt-4-turbo": { coding: 78, debugging: 72, research: 68, reasoning: 72, speed: 50, longContext: 65, instruction: 78 },
161
+ "gpt-4.1": { coding: 82, debugging: 78, research: 72, reasoning: 78, speed: 62, longContext: 72, instruction: 82 },
162
+ "gpt-4.1-mini": { coding: 58, debugging: 48, research: 42, reasoning: 48, speed: 88, longContext: 48, instruction: 72 },
163
+ "gpt-4.1-nano": { coding: 40, debugging: 30, research: 25, reasoning: 30, speed: 95, longContext: 30, instruction: 60 },
164
+ "gpt-5": { coding: 92, debugging: 88, research: 85, reasoning: 92, speed: 40, longContext: 85, instruction: 90 },
165
+ "gpt-5-mini": { coding: 62, debugging: 52, research: 48, reasoning: 52, speed: 88, longContext: 52, instruction: 74 },
166
+ "gpt-5-nano": { coding: 42, debugging: 32, research: 28, reasoning: 32, speed: 95, longContext: 32, instruction: 62 },
167
+ "gpt-5-pro": { coding: 94, debugging: 90, research: 88, reasoning: 94, speed: 35, longContext: 88, instruction: 92 },
168
+ "gpt-5.1": { coding: 93, debugging: 89, research: 86, reasoning: 93, speed: 42, longContext: 86, instruction: 91 },
169
+ "gpt-5.1-codex-max": { coding: 90, debugging: 85, research: 70, reasoning: 85, speed: 55, longContext: 75, instruction: 85 },
170
+ "gpt-5.1-codex-mini": { coding: 65, debugging: 55, research: 40, reasoning: 50, speed: 88, longContext: 48, instruction: 72 },
171
+ "gpt-5.2": { coding: 93, debugging: 90, research: 87, reasoning: 93, speed: 42, longContext: 87, instruction: 91 },
172
+ "gpt-5.2-codex": { coding: 93, debugging: 90, research: 72, reasoning: 88, speed: 50, longContext: 78, instruction: 88 },
173
+ "gpt-5.3-codex": { coding: 94, debugging: 91, research: 74, reasoning: 89, speed: 50, longContext: 80, instruction: 89 },
174
+ "gpt-5.3-codex-spark": { coding: 68, debugging: 58, research: 42, reasoning: 52, speed: 90, longContext: 50, instruction: 74 },
175
+ "gpt-5.4": { coding: 95, debugging: 92, research: 88, reasoning: 94, speed: 42, longContext: 88, instruction: 92 },
176
+
177
+ // ── OpenAI o-series (reasoning-first) ──────────────────────────────────────
178
+ "o1": { coding: 78, debugging: 82, research: 78, reasoning: 90, speed: 20, longContext: 65, instruction: 82 },
179
+ "o3": { coding: 80, debugging: 85, research: 80, reasoning: 92, speed: 25, longContext: 70, instruction: 85 },
180
+ "o4-mini": { coding: 75, debugging: 80, research: 72, reasoning: 88, speed: 60, longContext: 65, instruction: 80 },
181
+ "o4-mini-deep-research": { coding: 75, debugging: 80, research: 85, reasoning: 88, speed: 30, longContext: 80, instruction: 80 },
182
+
183
+ // ── Google ─────────────────────────────────────────────────────────────────
184
+ "gemini-2.5-pro": { coding: 75, debugging: 70, research: 85, reasoning: 75, speed: 55, longContext: 90, instruction: 75 },
185
+ "gemini-2.0-flash": { coding: 50, debugging: 40, research: 50, reasoning: 40, speed: 95, longContext: 60, instruction: 65 },
186
+ "gemini-flash-2.0": { coding: 50, debugging: 40, research: 50, reasoning: 40, speed: 95, longContext: 60, instruction: 65 },
187
+
188
+ // ── DeepSeek ───────────────────────────────────────────────────────────────
189
+ "deepseek-chat": { coding: 75, debugging: 65, research: 55, reasoning: 70, speed: 70, longContext: 55, instruction: 65 },
151
190
  };
152
191
 
153
192
  // ─── Base Task Requirements Data Table ───────────────────────────────────────
@@ -502,3 +541,71 @@ function getModelCost(modelId: string): number {
502
541
  // Unknown cost — assume expensive to avoid routing to unknown cheap models
503
542
  return 999;
504
543
  }
544
+
545
+ // ─── Tool Compatibility Filter (ADR-005 Phase 3) ───────────────────────────
546
+
547
+ /**
548
+ * Check if a tool is compatible with a provider's capabilities.
549
+ * Returns true if the tool can be used with the provider.
550
+ */
551
+ export function isToolCompatibleWithProvider(
552
+ toolName: string,
553
+ providerCaps: ProviderCapabilities,
554
+ ): boolean {
555
+ const compat = getToolCompatibility(toolName);
556
+ if (!compat) return true; // no metadata = always compatible
557
+
558
+ // Hard filter: provider doesn't support image tool results
559
+ if (compat.producesImages && !providerCaps.imageToolResults) return false;
560
+
561
+ // Hard filter: tool uses schema features provider doesn't support
562
+ if (compat.schemaFeatures?.some(f => providerCaps.unsupportedSchemaFeatures.includes(f))) {
563
+ return false;
564
+ }
565
+
566
+ return true;
567
+ }
568
+
569
+ /**
570
+ * Filter a list of tool names to only those compatible with a provider.
571
+ * Used by the routing pipeline to adjust tool sets when switching providers.
572
+ */
573
+ export function filterToolsForProvider(
574
+ toolNames: string[],
575
+ providerApi: string,
576
+ ): { compatible: string[]; filtered: string[] } {
577
+ const providerCaps = getProviderCapabilities(providerApi);
578
+
579
+ // Provider doesn't support tool calling at all
580
+ if (!providerCaps.toolCalling) {
581
+ return { compatible: [], filtered: toolNames };
582
+ }
583
+
584
+ const compatible: string[] = [];
585
+ const filtered: string[] = [];
586
+
587
+ for (const name of toolNames) {
588
+ if (isToolCompatibleWithProvider(name, providerCaps)) {
589
+ compatible.push(name);
590
+ } else {
591
+ filtered.push(name);
592
+ }
593
+ }
594
+
595
+ return { compatible, filtered };
596
+ }
597
+
598
+ /**
599
+ * Adjust the active tool set for a selected model's provider capabilities.
600
+ * Returns tool names that should be active — removes incompatible tools.
601
+ *
602
+ * This is a hard filter only — it removes tools that would fail at the
603
+ * provider level. It does NOT remove tools based on soft heuristics.
604
+ */
605
+ export function adjustToolSet(
606
+ activeToolNames: string[],
607
+ selectedModelApi: string,
608
+ ): { toolNames: string[]; removedTools: string[] } {
609
+ const { compatible, filtered } = filterToolsForProvider(activeToolNames, selectedModelApi);
610
+ return { toolNames: compatible, removedTools: filtered };
611
+ }
@@ -20,7 +20,7 @@ import type {
20
20
  ReactiveExecutionConfig,
21
21
  GateEvaluationConfig,
22
22
  } from "./types.js";
23
- import type { DynamicRoutingConfig } from "./model-router.js";
23
+ import type { DynamicRoutingConfig, ModelCapabilities } from "./model-router.js";
24
24
 
25
25
  export interface ContextManagementConfig {
26
26
  observation_masking?: boolean; // default: true
@@ -255,6 +255,8 @@ export interface GSDPreferences {
255
255
  post_unit_hooks?: PostUnitHookConfig[];
256
256
  pre_dispatch_hooks?: PreDispatchHookConfig[];
257
257
  dynamic_routing?: DynamicRoutingConfig;
258
+ /** Per-model capability overrides. Deep-merged with built-in profiles for capability-aware routing (ADR-004). */
259
+ modelOverrides?: Record<string, { capabilities?: Partial<ModelCapabilities> }>;
258
260
  context_management?: ContextManagementConfig;
259
261
  token_profile?: TokenProfile;
260
262
  phases?: PhaseSkipPreferences;
@@ -28,6 +28,8 @@ After reflection is confirmed, decide the approach based on the actual scope —
28
28
 
29
29
  **Anti-reduction rule:** If the user describes a big vision, plan the big vision. Do not ask "what's the minimum viable version?" or try to reduce scope unless the user explicitly asks for an MVP or minimal version. When something is complex or risky, phase it into a later milestone — do not cut it. The user's ambition is the target, and your job is to sequence it intelligently, not shrink it.
30
30
 
31
+ {{preparationContext}}
32
+
31
33
  ## Mandatory Investigation Before First Question Round
32
34
 
33
35
  Before asking your first question, do a mandatory investigation pass. This is not optional.
@@ -38,6 +38,28 @@ To call this milestone complete, we must prove:
38
38
  - {{one real end-to-end scenario}}
39
39
  - {{what cannot be simulated if this milestone is to be considered truly done}}
40
40
 
41
+ ## Architectural Decisions
42
+
43
+ ### {{decisionTitle}}
44
+
45
+ **Decision:** {{decisionStatement}}
46
+
47
+ **Rationale:** {{rationale}}
48
+
49
+ **Alternatives Considered:**
50
+ - {{alternative}} — {{whyNotChosen}}
51
+
52
+ ---
53
+
54
+ > Add additional decisions as separate `### Decision Title` blocks following the same structure above.
55
+ > See `.gsd/DECISIONS.md` for the full append-only register of all project decisions.
56
+
57
+ ## Error Handling Strategy
58
+
59
+ {{errorHandlingStrategy}}
60
+
61
+ > Describe the approach for handling failures, edge cases, and error propagation. Include retry policies, fallback behaviors, and user-facing error messages where relevant.
62
+
41
63
  ## Risks and Unknowns
42
64
 
43
65
  - {{riskOrUnknown}} — {{whyItMatters}}
@@ -47,8 +69,6 @@ To call this milestone complete, we must prove:
47
69
  - `{{fileOrModule}}` — {{howItRelates}}
48
70
  - `{{fileOrModule}}` — {{howItRelates}}
49
71
 
50
- > See `.gsd/DECISIONS.md` for all architectural and pattern decisions — it is an append-only register; read it during planning, append to it during execution.
51
-
52
72
  ## Relevant Requirements
53
73
 
54
74
  - {{requirementId}} — {{howThisMilestoneAdvancesIt}}
@@ -71,6 +91,18 @@ To call this milestone complete, we must prove:
71
91
 
72
92
  - {{systemOrService}} — {{howThisMilestoneInteractsWithIt}}
73
93
 
94
+ ## Testing Requirements
95
+
96
+ {{testingRequirements}}
97
+
98
+ > Specify test types (unit, integration, e2e), coverage expectations, and specific test scenarios that must pass.
99
+
100
+ ## Acceptance Criteria
101
+
102
+ {{acceptanceCriteria}}
103
+
104
+ > Per-slice acceptance criteria gathered during discussion. Each slice should have clear, testable criteria.
105
+
74
106
  ## Open Questions
75
107
 
76
108
  - {{question}} — {{currentThinking}}
@@ -11,6 +11,7 @@ import {
11
11
  getEligibleModels,
12
12
  resolveModelForComplexity,
13
13
  MODEL_CAPABILITY_PROFILES,
14
+ MODEL_CAPABILITY_TIER,
14
15
  BASE_REQUIREMENTS,
15
16
  defaultRoutingConfig,
16
17
  } from "../model-router.js";
@@ -125,13 +126,9 @@ describe("computeTaskRequirements", () => {
125
126
  // ─── MODEL_CAPABILITY_PROFILES ───────────────────────────────────────────────
126
127
 
127
128
  describe("MODEL_CAPABILITY_PROFILES", () => {
128
- test("contains all 9 required models", () => {
129
- const required = [
130
- "claude-opus-4-6", "claude-sonnet-4-6", "claude-haiku-4-5",
131
- "gpt-4o", "gpt-4o-mini", "gemini-2.5-pro", "gemini-2.0-flash",
132
- "deepseek-chat", "o3",
133
- ];
134
- for (const model of required) {
129
+ test("contains profiles for all tier-mapped models", () => {
130
+ const tierModels = Object.keys(MODEL_CAPABILITY_TIER);
131
+ for (const model of tierModels) {
135
132
  assert.ok(MODEL_CAPABILITY_PROFILES[model], `Missing profile for ${model}`);
136
133
  }
137
134
  });
@@ -345,3 +342,30 @@ describe("RoutingDecision.selectionMethod", () => {
345
342
  assert.equal(result.selectionMethod, "tier-only");
346
343
  });
347
344
  });
345
+
346
+ // ─── ADR-004: Profile Completeness Lint ─────────────────────────────────────
347
+ // Every model in MODEL_CAPABILITY_TIER must have an entry in
348
+ // MODEL_CAPABILITY_PROFILES. This prevents profile staleness as new models
349
+ // are added to the tier map without corresponding capability data.
350
+
351
+ describe("profile completeness (ADR-004 lint)", () => {
352
+ test("every model in MODEL_CAPABILITY_TIER has a MODEL_CAPABILITY_PROFILES entry", () => {
353
+ const tierModels = Object.keys(MODEL_CAPABILITY_TIER);
354
+ const missing = tierModels.filter(id => !MODEL_CAPABILITY_PROFILES[id]);
355
+ assert.equal(
356
+ missing.length,
357
+ 0,
358
+ `Models in MODEL_CAPABILITY_TIER but missing from MODEL_CAPABILITY_PROFILES:\n ${missing.join("\n ")}\n\nAdd capability profiles for these models in model-router.ts.`,
359
+ );
360
+ });
361
+
362
+ test("MODEL_CAPABILITY_PROFILES does not contain models absent from MODEL_CAPABILITY_TIER", () => {
363
+ const profileModels = Object.keys(MODEL_CAPABILITY_PROFILES);
364
+ const orphaned = profileModels.filter(id => !MODEL_CAPABILITY_TIER[id]);
365
+ assert.equal(
366
+ orphaned.length,
367
+ 0,
368
+ `Models in MODEL_CAPABILITY_PROFILES but not in MODEL_CAPABILITY_TIER:\n ${orphaned.join("\n ")}\n\nEither add these to MODEL_CAPABILITY_TIER or remove stale profiles.`,
369
+ );
370
+ });
371
+ });
@@ -18,7 +18,7 @@ const VALID_INVITE = "https://discord.com/invite/nKXTsAcmbT";
18
18
  /** Files that contain user-facing Discord invite links. */
19
19
  const FILES_WITH_INVITE_LINKS: string[] = [
20
20
  "README.md",
21
- "docs/what-is-pi/15-pi-packages-the-ecosystem.md",
21
+ "docs/dev/what-is-pi/15-pi-packages-the-ecosystem.md",
22
22
  ];
23
23
 
24
24
  describe("Discord invite links (#2699)", () => {
@@ -287,9 +287,9 @@ test("resolveModelForComplexity falls back to tier-only when capability_routing
287
287
  assert.ok(!result.selectionMethod || result.selectionMethod === "tier-only");
288
288
  });
289
289
 
290
- test("MODEL_CAPABILITY_PROFILES has entries for core models", () => {
290
+ test("MODEL_CAPABILITY_PROFILES has entries for all tier-mapped models", () => {
291
291
  const profiledModels = Object.keys(MODEL_CAPABILITY_PROFILES);
292
- assert.ok(profiledModels.length >= 9, `Expected ≥9 profiles, got ${profiledModels.length}`);
292
+ assert.ok(profiledModels.length >= 30, `Expected ≥30 profiles, got ${profiledModels.length}`);
293
293
  assert.ok(MODEL_CAPABILITY_PROFILES["claude-opus-4-6"]);
294
294
  assert.ok(MODEL_CAPABILITY_PROFILES["claude-haiku-4-5"]);
295
295
  });
@@ -0,0 +1,37 @@
1
+ // GSD2 — Regression test for broken resource-loader import path
2
+ // Ensures auto.ts imports resource-loader via package resolution, not a
3
+ // relative path that breaks when deployed to ~/.gsd/agent/extensions/gsd/.
4
+
5
+ import { describe, test } from "node:test";
6
+ import assert from "node:assert/strict";
7
+ import { readFileSync } from "node:fs";
8
+ import { join } from "node:path";
9
+
10
+ const autoSrc = readFileSync(join(import.meta.dirname, "..", "auto.ts"), "utf-8");
11
+
12
+ describe("resource-loader import path", () => {
13
+ test("must not use relative import reaching above extensions/", () => {
14
+ // The old broken pattern: import("../../../" + "resource-loader.js")
15
+ // This resolves to ~/.gsd/resource-loader.js from deployed location, which
16
+ // doesn't exist. Regression introduced in #3899.
17
+ const brokenPattern = /import\(\s*["']\.\.\/\.\.\/\.\..*resource-loader/;
18
+ assert.ok(
19
+ !brokenPattern.test(autoSrc),
20
+ "auto.ts must not import resource-loader via relative path above extensions/ — " +
21
+ "breaks when deployed to ~/.gsd/agent/extensions/gsd/ (see #3899)",
22
+ );
23
+ });
24
+
25
+ test("uses createRequire to resolve resource-loader from package root", () => {
26
+ // The fix uses createRequire to find gsd-pi/package.json, then imports
27
+ // dist/resource-loader.js from there — works in both source and deployed.
28
+ assert.ok(
29
+ autoSrc.includes('createRequire(import.meta.url)'),
30
+ "auto.ts should use createRequire to resolve resource-loader",
31
+ );
32
+ assert.ok(
33
+ autoSrc.includes('resolve("gsd-pi/package.json")'),
34
+ "auto.ts should resolve gsd-pi package root via package.json",
35
+ );
36
+ });
37
+ });