specrails-desktop 2.7.0 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (329) hide show
  1. package/README.md +23 -19
  2. package/client/dist/assets/{ActivityFeedPage-LKqd18-G.js → ActivityFeedPage-DNqnf1fZ.js} +1 -1
  3. package/client/dist/assets/{AgentsPage-Cb-b-6Ot.js → AgentsPage-vmNIEbGM.js} +1 -1
  4. package/client/dist/assets/{AnalyticsPage-HVxQQ1wy.js → AnalyticsPage-CdfN0ofZ.js} +1 -1
  5. package/client/dist/assets/{BarChart-BOyHB0dw.js → BarChart-CIkopHjl.js} +1 -1
  6. package/client/dist/assets/{CodePage-DnOnwKGB.js → CodePage-DDRNU5FN.js} +1 -1
  7. package/client/dist/assets/{DesktopAnalyticsPage-D2auU39x.js → DesktopAnalyticsPage-Cl3sKKSG.js} +1 -1
  8. package/client/dist/assets/{DocsDialog-CTuDX3GK.js → DocsDialog-BGrBOfUr.js} +2 -2
  9. package/client/dist/assets/{DocsPage-DRyMmu0Z.js → DocsPage-CY-2SSzw.js} +2 -2
  10. package/client/dist/assets/{ExportDropdown-DO-GGiMh.js → ExportDropdown-BRHcvP0r.js} +1 -1
  11. package/client/dist/assets/{IntegrationsPage-BhbO4jFT.js → IntegrationsPage-nKdLB4Ub.js} +1 -1
  12. package/client/dist/assets/{JobDetailPage-DJooEg1s.js → JobDetailPage-Bf0A6WWQ.js} +1 -1
  13. package/client/dist/assets/{JobsPage-BbaC-YOg.js → JobsPage-Vg4nXPdL.js} +1 -1
  14. package/client/dist/assets/{dist-js-CiIVMsx3.js → dist-js-0i_klubI.js} +1 -1
  15. package/client/dist/assets/{dist-js-Xc2lRKp2.js → dist-js-CUs5GjwA.js} +1 -1
  16. package/client/dist/assets/{index-DK214dak.js → index-BXoHFtfG.js} +8 -8
  17. package/client/dist/assets/index-D6BaYRRU.css +2 -0
  18. package/client/dist/assets/{integrations-2C7MkGT0.js → integrations-7YyTBuU9.js} +1 -1
  19. package/client/dist/assets/{integrations-CX4p_bij.js → integrations-B9CEpNF0.js} +1 -1
  20. package/client/dist/assets/{integrations-C2jQtv-s.js → integrations-BlvAdewo.js} +1 -1
  21. package/client/dist/assets/{integrations-eQPHAYsE.js → integrations-Bw8UM9Xd.js} +1 -1
  22. package/client/dist/assets/{integrations-BDC670cg.js → integrations-C5SxNKnG.js} +1 -1
  23. package/client/dist/assets/{integrations-BqUmRUef.js → integrations-CJQKMmdW.js} +1 -1
  24. package/client/dist/assets/{integrations-CB98NeH5.js → integrations-DWz1eU_K.js} +1 -1
  25. package/client/dist/assets/{integrations-_SuVeQIG.js → integrations-DiPR8Fzp.js} +1 -1
  26. package/client/dist/assets/{lib-Bo5s6xpe.js → lib-D6M_MvoC.js} +1 -1
  27. package/client/dist/assets/setup-B6egeeTM.js +1 -0
  28. package/client/dist/assets/setup-BHroXlke.js +1 -0
  29. package/client/dist/assets/setup-BIXsWUp1.js +1 -0
  30. package/client/dist/assets/setup-BJRdg1iE.js +1 -0
  31. package/client/dist/assets/setup-C0rVGnCy.js +1 -0
  32. package/client/dist/assets/setup-Cpu17hJv.js +1 -0
  33. package/client/dist/assets/setup-D-1r0uSx.js +1 -0
  34. package/client/dist/assets/setup-Dn2-veYO.js +1 -0
  35. package/client/dist/assets/{useProjectCache-DVNypkmR.js → useProjectCache-BeyBSNpD.js} +1 -1
  36. package/client/dist/index.html +4 -4
  37. package/docs/README.md +5 -2
  38. package/docs/agy-cli-provider-study.md +78 -0
  39. package/docs/cli.md +23 -4
  40. package/docs/codex.md +116 -58
  41. package/docs/creating-specs.md +19 -5
  42. package/docs/customizing.md +27 -6
  43. package/docs/gemini.md +225 -73
  44. package/docs/getting-started.md +18 -9
  45. package/docs/guide/de/agents/1-meet-the-agents.md +38 -0
  46. package/docs/guide/de/agents/2-profiles-and-the-balanced-default.md +45 -0
  47. package/docs/guide/de/agents/3-customizing-models-per-agent.md +60 -0
  48. package/docs/guide/de/agents/4-custom-agents-catalog.md +43 -0
  49. package/docs/guide/de/getting-started/1-what-is-specrails.md +49 -0
  50. package/docs/guide/de/getting-started/2-installing-and-first-run.md +42 -0
  51. package/docs/guide/de/getting-started/3-adding-your-first-project.md +58 -0
  52. package/docs/guide/de/getting-started/4-the-dashboard-tour.md +53 -0
  53. package/docs/guide/de/insights/1-analytics-and-cost-tracking.md +78 -0
  54. package/docs/guide/de/insights/2-the-integrated-terminal.md +46 -0
  55. package/docs/guide/de/insights/3-code-explorer.md +50 -0
  56. package/docs/guide/de/integrations/1-ai-providers.md +64 -0
  57. package/docs/guide/de/integrations/2-plugins.md +44 -0
  58. package/docs/guide/de/integrations/3-jira-integration.md +71 -0
  59. package/docs/guide/de/integrations/4-mobile-companion.md +38 -0
  60. package/docs/guide/de/pipeline/1-rails-and-jobs.md +94 -0
  61. package/docs/guide/de/pipeline/2-the-job-detail-view.md +90 -0
  62. package/docs/guide/de/pipeline/3-batch-implement-and-multi-feature.md +78 -0
  63. package/docs/guide/de/pipeline/4-picking-an-engine-per-rail.md +60 -0
  64. package/docs/guide/de/settings/1-themes.md +37 -0
  65. package/docs/guide/de/settings/2-language.md +39 -0
  66. package/docs/guide/de/settings/3-pipeline-telemetry-and-diagnostics.md +46 -0
  67. package/docs/guide/de/settings/4-where-your-data-lives.md +48 -0
  68. package/docs/guide/de/specs/1-specs-and-the-backlog.md +52 -0
  69. package/docs/guide/de/specs/2-add-spec-quick-mode.md +45 -0
  70. package/docs/guide/de/specs/3-add-spec-explore-mode.md +68 -0
  71. package/docs/guide/de/specs/4-drafts-and-contract-layer.md +81 -0
  72. package/docs/guide/en/agents/1-meet-the-agents.md +38 -0
  73. package/docs/guide/en/agents/2-profiles-and-the-balanced-default.md +45 -0
  74. package/docs/guide/en/agents/3-customizing-models-per-agent.md +60 -0
  75. package/docs/guide/en/agents/4-custom-agents-catalog.md +43 -0
  76. package/docs/guide/en/getting-started/1-what-is-specrails.md +49 -0
  77. package/docs/guide/en/getting-started/2-installing-and-first-run.md +42 -0
  78. package/docs/guide/en/getting-started/3-adding-your-first-project.md +58 -0
  79. package/docs/guide/en/getting-started/4-the-dashboard-tour.md +53 -0
  80. package/docs/guide/en/insights/1-analytics-and-cost-tracking.md +78 -0
  81. package/docs/guide/en/insights/2-the-integrated-terminal.md +46 -0
  82. package/docs/guide/en/insights/3-code-explorer.md +50 -0
  83. package/docs/guide/en/integrations/1-ai-providers.md +64 -0
  84. package/docs/guide/en/integrations/2-plugins.md +44 -0
  85. package/docs/guide/en/integrations/3-jira-integration.md +71 -0
  86. package/docs/guide/en/integrations/4-mobile-companion.md +38 -0
  87. package/docs/guide/en/pipeline/1-rails-and-jobs.md +94 -0
  88. package/docs/guide/en/pipeline/2-the-job-detail-view.md +90 -0
  89. package/docs/guide/en/pipeline/3-batch-implement-and-multi-feature.md +78 -0
  90. package/docs/guide/en/pipeline/4-picking-an-engine-per-rail.md +60 -0
  91. package/docs/guide/en/settings/1-themes.md +37 -0
  92. package/docs/guide/en/settings/2-language.md +39 -0
  93. package/docs/guide/en/settings/3-pipeline-telemetry-and-diagnostics.md +46 -0
  94. package/docs/guide/en/settings/4-where-your-data-lives.md +48 -0
  95. package/docs/guide/en/specs/1-specs-and-the-backlog.md +52 -0
  96. package/docs/guide/en/specs/2-add-spec-quick-mode.md +45 -0
  97. package/docs/guide/en/specs/3-add-spec-explore-mode.md +68 -0
  98. package/docs/guide/en/specs/4-drafts-and-contract-layer.md +81 -0
  99. package/docs/guide/es/agents/1-meet-the-agents.md +38 -0
  100. package/docs/guide/es/agents/2-profiles-and-the-balanced-default.md +45 -0
  101. package/docs/guide/es/agents/3-customizing-models-per-agent.md +60 -0
  102. package/docs/guide/es/agents/4-custom-agents-catalog.md +43 -0
  103. package/docs/guide/es/getting-started/1-what-is-specrails.md +49 -0
  104. package/docs/guide/es/getting-started/2-installing-and-first-run.md +42 -0
  105. package/docs/guide/es/getting-started/3-adding-your-first-project.md +58 -0
  106. package/docs/guide/es/getting-started/4-the-dashboard-tour.md +53 -0
  107. package/docs/guide/es/insights/1-analytics-and-cost-tracking.md +78 -0
  108. package/docs/guide/es/insights/2-the-integrated-terminal.md +46 -0
  109. package/docs/guide/es/insights/3-code-explorer.md +50 -0
  110. package/docs/guide/es/integrations/1-ai-providers.md +64 -0
  111. package/docs/guide/es/integrations/2-plugins.md +44 -0
  112. package/docs/guide/es/integrations/3-jira-integration.md +71 -0
  113. package/docs/guide/es/integrations/4-mobile-companion.md +38 -0
  114. package/docs/guide/es/pipeline/1-rails-and-jobs.md +94 -0
  115. package/docs/guide/es/pipeline/2-the-job-detail-view.md +90 -0
  116. package/docs/guide/es/pipeline/3-batch-implement-and-multi-feature.md +78 -0
  117. package/docs/guide/es/pipeline/4-picking-an-engine-per-rail.md +60 -0
  118. package/docs/guide/es/settings/1-themes.md +37 -0
  119. package/docs/guide/es/settings/2-language.md +39 -0
  120. package/docs/guide/es/settings/3-pipeline-telemetry-and-diagnostics.md +46 -0
  121. package/docs/guide/es/settings/4-where-your-data-lives.md +48 -0
  122. package/docs/guide/es/specs/1-specs-and-the-backlog.md +52 -0
  123. package/docs/guide/es/specs/2-add-spec-quick-mode.md +45 -0
  124. package/docs/guide/es/specs/3-add-spec-explore-mode.md +68 -0
  125. package/docs/guide/es/specs/4-drafts-and-contract-layer.md +81 -0
  126. package/docs/guide/fr/agents/1-meet-the-agents.md +38 -0
  127. package/docs/guide/fr/agents/2-profiles-and-the-balanced-default.md +45 -0
  128. package/docs/guide/fr/agents/3-customizing-models-per-agent.md +60 -0
  129. package/docs/guide/fr/agents/4-custom-agents-catalog.md +43 -0
  130. package/docs/guide/fr/getting-started/1-what-is-specrails.md +49 -0
  131. package/docs/guide/fr/getting-started/2-installing-and-first-run.md +42 -0
  132. package/docs/guide/fr/getting-started/3-adding-your-first-project.md +58 -0
  133. package/docs/guide/fr/getting-started/4-the-dashboard-tour.md +53 -0
  134. package/docs/guide/fr/insights/1-analytics-and-cost-tracking.md +78 -0
  135. package/docs/guide/fr/insights/2-the-integrated-terminal.md +46 -0
  136. package/docs/guide/fr/insights/3-code-explorer.md +50 -0
  137. package/docs/guide/fr/integrations/1-ai-providers.md +64 -0
  138. package/docs/guide/fr/integrations/2-plugins.md +44 -0
  139. package/docs/guide/fr/integrations/3-jira-integration.md +71 -0
  140. package/docs/guide/fr/integrations/4-mobile-companion.md +38 -0
  141. package/docs/guide/fr/pipeline/1-rails-and-jobs.md +94 -0
  142. package/docs/guide/fr/pipeline/2-the-job-detail-view.md +90 -0
  143. package/docs/guide/fr/pipeline/3-batch-implement-and-multi-feature.md +78 -0
  144. package/docs/guide/fr/pipeline/4-picking-an-engine-per-rail.md +60 -0
  145. package/docs/guide/fr/settings/1-themes.md +37 -0
  146. package/docs/guide/fr/settings/2-language.md +39 -0
  147. package/docs/guide/fr/settings/3-pipeline-telemetry-and-diagnostics.md +46 -0
  148. package/docs/guide/fr/settings/4-where-your-data-lives.md +48 -0
  149. package/docs/guide/fr/specs/1-specs-and-the-backlog.md +52 -0
  150. package/docs/guide/fr/specs/2-add-spec-quick-mode.md +45 -0
  151. package/docs/guide/fr/specs/3-add-spec-explore-mode.md +68 -0
  152. package/docs/guide/fr/specs/4-drafts-and-contract-layer.md +81 -0
  153. package/docs/guide/it/agents/1-meet-the-agents.md +38 -0
  154. package/docs/guide/it/agents/2-profiles-and-the-balanced-default.md +45 -0
  155. package/docs/guide/it/agents/3-customizing-models-per-agent.md +60 -0
  156. package/docs/guide/it/agents/4-custom-agents-catalog.md +43 -0
  157. package/docs/guide/it/getting-started/1-what-is-specrails.md +49 -0
  158. package/docs/guide/it/getting-started/2-installing-and-first-run.md +42 -0
  159. package/docs/guide/it/getting-started/3-adding-your-first-project.md +58 -0
  160. package/docs/guide/it/getting-started/4-the-dashboard-tour.md +53 -0
  161. package/docs/guide/it/insights/1-analytics-and-cost-tracking.md +78 -0
  162. package/docs/guide/it/insights/2-the-integrated-terminal.md +46 -0
  163. package/docs/guide/it/insights/3-code-explorer.md +50 -0
  164. package/docs/guide/it/integrations/1-ai-providers.md +64 -0
  165. package/docs/guide/it/integrations/2-plugins.md +44 -0
  166. package/docs/guide/it/integrations/3-jira-integration.md +71 -0
  167. package/docs/guide/it/integrations/4-mobile-companion.md +38 -0
  168. package/docs/guide/it/pipeline/1-rails-and-jobs.md +94 -0
  169. package/docs/guide/it/pipeline/2-the-job-detail-view.md +90 -0
  170. package/docs/guide/it/pipeline/3-batch-implement-and-multi-feature.md +78 -0
  171. package/docs/guide/it/pipeline/4-picking-an-engine-per-rail.md +60 -0
  172. package/docs/guide/it/settings/1-themes.md +37 -0
  173. package/docs/guide/it/settings/2-language.md +39 -0
  174. package/docs/guide/it/settings/3-pipeline-telemetry-and-diagnostics.md +46 -0
  175. package/docs/guide/it/settings/4-where-your-data-lives.md +48 -0
  176. package/docs/guide/it/specs/1-specs-and-the-backlog.md +52 -0
  177. package/docs/guide/it/specs/2-add-spec-quick-mode.md +45 -0
  178. package/docs/guide/it/specs/3-add-spec-explore-mode.md +68 -0
  179. package/docs/guide/it/specs/4-drafts-and-contract-layer.md +81 -0
  180. package/docs/guide/ja/agents/1-meet-the-agents.md +38 -0
  181. package/docs/guide/ja/agents/2-profiles-and-the-balanced-default.md +45 -0
  182. package/docs/guide/ja/agents/3-customizing-models-per-agent.md +60 -0
  183. package/docs/guide/ja/agents/4-custom-agents-catalog.md +43 -0
  184. package/docs/guide/ja/getting-started/1-what-is-specrails.md +49 -0
  185. package/docs/guide/ja/getting-started/2-installing-and-first-run.md +42 -0
  186. package/docs/guide/ja/getting-started/3-adding-your-first-project.md +58 -0
  187. package/docs/guide/ja/getting-started/4-the-dashboard-tour.md +53 -0
  188. package/docs/guide/ja/insights/1-analytics-and-cost-tracking.md +78 -0
  189. package/docs/guide/ja/insights/2-the-integrated-terminal.md +46 -0
  190. package/docs/guide/ja/insights/3-code-explorer.md +50 -0
  191. package/docs/guide/ja/integrations/1-ai-providers.md +64 -0
  192. package/docs/guide/ja/integrations/2-plugins.md +44 -0
  193. package/docs/guide/ja/integrations/3-jira-integration.md +71 -0
  194. package/docs/guide/ja/integrations/4-mobile-companion.md +38 -0
  195. package/docs/guide/ja/pipeline/1-rails-and-jobs.md +94 -0
  196. package/docs/guide/ja/pipeline/2-the-job-detail-view.md +90 -0
  197. package/docs/guide/ja/pipeline/3-batch-implement-and-multi-feature.md +78 -0
  198. package/docs/guide/ja/pipeline/4-picking-an-engine-per-rail.md +60 -0
  199. package/docs/guide/ja/settings/1-themes.md +37 -0
  200. package/docs/guide/ja/settings/2-language.md +39 -0
  201. package/docs/guide/ja/settings/3-pipeline-telemetry-and-diagnostics.md +46 -0
  202. package/docs/guide/ja/settings/4-where-your-data-lives.md +48 -0
  203. package/docs/guide/ja/specs/1-specs-and-the-backlog.md +52 -0
  204. package/docs/guide/ja/specs/2-add-spec-quick-mode.md +45 -0
  205. package/docs/guide/ja/specs/3-add-spec-explore-mode.md +68 -0
  206. package/docs/guide/ja/specs/4-drafts-and-contract-layer.md +81 -0
  207. package/docs/guide/pt/agents/1-meet-the-agents.md +38 -0
  208. package/docs/guide/pt/agents/2-profiles-and-the-balanced-default.md +45 -0
  209. package/docs/guide/pt/agents/3-customizing-models-per-agent.md +60 -0
  210. package/docs/guide/pt/agents/4-custom-agents-catalog.md +43 -0
  211. package/docs/guide/pt/getting-started/1-what-is-specrails.md +49 -0
  212. package/docs/guide/pt/getting-started/2-installing-and-first-run.md +42 -0
  213. package/docs/guide/pt/getting-started/3-adding-your-first-project.md +58 -0
  214. package/docs/guide/pt/getting-started/4-the-dashboard-tour.md +53 -0
  215. package/docs/guide/pt/insights/1-analytics-and-cost-tracking.md +78 -0
  216. package/docs/guide/pt/insights/2-the-integrated-terminal.md +46 -0
  217. package/docs/guide/pt/insights/3-code-explorer.md +50 -0
  218. package/docs/guide/pt/integrations/1-ai-providers.md +64 -0
  219. package/docs/guide/pt/integrations/2-plugins.md +44 -0
  220. package/docs/guide/pt/integrations/3-jira-integration.md +71 -0
  221. package/docs/guide/pt/integrations/4-mobile-companion.md +38 -0
  222. package/docs/guide/pt/pipeline/1-rails-and-jobs.md +94 -0
  223. package/docs/guide/pt/pipeline/2-the-job-detail-view.md +90 -0
  224. package/docs/guide/pt/pipeline/3-batch-implement-and-multi-feature.md +78 -0
  225. package/docs/guide/pt/pipeline/4-picking-an-engine-per-rail.md +60 -0
  226. package/docs/guide/pt/settings/1-themes.md +37 -0
  227. package/docs/guide/pt/settings/2-language.md +39 -0
  228. package/docs/guide/pt/settings/3-pipeline-telemetry-and-diagnostics.md +46 -0
  229. package/docs/guide/pt/settings/4-where-your-data-lives.md +48 -0
  230. package/docs/guide/pt/specs/1-specs-and-the-backlog.md +52 -0
  231. package/docs/guide/pt/specs/2-add-spec-quick-mode.md +45 -0
  232. package/docs/guide/pt/specs/3-add-spec-explore-mode.md +68 -0
  233. package/docs/guide/pt/specs/4-drafts-and-contract-layer.md +81 -0
  234. package/docs/guide/zh/agents/1-meet-the-agents.md +38 -0
  235. package/docs/guide/zh/agents/2-profiles-and-the-balanced-default.md +45 -0
  236. package/docs/guide/zh/agents/3-customizing-models-per-agent.md +60 -0
  237. package/docs/guide/zh/agents/4-custom-agents-catalog.md +43 -0
  238. package/docs/guide/zh/getting-started/1-what-is-specrails.md +49 -0
  239. package/docs/guide/zh/getting-started/2-installing-and-first-run.md +42 -0
  240. package/docs/guide/zh/getting-started/3-adding-your-first-project.md +58 -0
  241. package/docs/guide/zh/getting-started/4-the-dashboard-tour.md +53 -0
  242. package/docs/guide/zh/insights/1-analytics-and-cost-tracking.md +78 -0
  243. package/docs/guide/zh/insights/2-the-integrated-terminal.md +46 -0
  244. package/docs/guide/zh/insights/3-code-explorer.md +50 -0
  245. package/docs/guide/zh/integrations/1-ai-providers.md +64 -0
  246. package/docs/guide/zh/integrations/2-plugins.md +44 -0
  247. package/docs/guide/zh/integrations/3-jira-integration.md +71 -0
  248. package/docs/guide/zh/integrations/4-mobile-companion.md +38 -0
  249. package/docs/guide/zh/pipeline/1-rails-and-jobs.md +94 -0
  250. package/docs/guide/zh/pipeline/2-the-job-detail-view.md +90 -0
  251. package/docs/guide/zh/pipeline/3-batch-implement-and-multi-feature.md +78 -0
  252. package/docs/guide/zh/pipeline/4-picking-an-engine-per-rail.md +60 -0
  253. package/docs/guide/zh/settings/1-themes.md +37 -0
  254. package/docs/guide/zh/settings/2-language.md +39 -0
  255. package/docs/guide/zh/settings/3-pipeline-telemetry-and-diagnostics.md +46 -0
  256. package/docs/guide/zh/settings/4-where-your-data-lives.md +48 -0
  257. package/docs/guide/zh/specs/1-specs-and-the-backlog.md +52 -0
  258. package/docs/guide/zh/specs/2-add-spec-quick-mode.md +45 -0
  259. package/docs/guide/zh/specs/3-add-spec-explore-mode.md +68 -0
  260. package/docs/guide/zh/specs/4-drafts-and-contract-layer.md +81 -0
  261. package/docs/internals/README.md +1 -1
  262. package/docs/internals/adding-a-provider.md +192 -59
  263. package/docs/internals/api-reference.md +130 -21
  264. package/docs/internals/architecture.md +22 -9
  265. package/docs/internals/bundled-framework-build-plan.md +264 -0
  266. package/docs/internals/configuration.md +33 -8
  267. package/docs/internals/global-artifacts-alignment-contract.md +486 -0
  268. package/docs/internals/global-artifacts-relocation-evaluation.md +294 -0
  269. package/docs/internals/operations-runbook.md +16 -5
  270. package/docs/internals/profiles.md +42 -14
  271. package/docs/platforms/macos.md +27 -8
  272. package/docs/platforms/windows.md +20 -6
  273. package/docs/running-pipelines.md +17 -9
  274. package/docs/terminal.md +9 -3
  275. package/docs/tracking-cost.md +17 -11
  276. package/package.json +1 -1
  277. package/server/dist/agent-refine-manager.js +20 -5
  278. package/server/dist/artifact-registry.js +468 -0
  279. package/server/dist/attachment-manager.js +5 -8
  280. package/server/dist/browser-capture-manager.js +4 -4
  281. package/server/dist/bundled-core.js +72 -0
  282. package/server/dist/bundled-openspec.js +58 -0
  283. package/server/dist/chat-manager.js +42 -5
  284. package/server/dist/code-explorer-router.js +10 -7
  285. package/server/dist/config.js +7 -2
  286. package/server/dist/context-budget.js +17 -6
  287. package/server/dist/context-scope.js +6 -2
  288. package/server/dist/contract-refine-runner.js +31 -9
  289. package/server/dist/desktop-router.js +39 -14
  290. package/server/dist/docs-router.js +210 -132
  291. package/server/dist/file-summary-manager.js +41 -16
  292. package/server/dist/framework-manager.js +248 -0
  293. package/server/dist/framework-migration.js +308 -0
  294. package/server/dist/index.js +30 -0
  295. package/server/dist/install-config-path.js +73 -0
  296. package/server/dist/jira/jira-sync-manager.js +23 -11
  297. package/server/dist/openspec-shim.js +153 -0
  298. package/server/dist/plugins-router.js +19 -8
  299. package/server/dist/profiles-router.js +38 -16
  300. package/server/dist/project-registry.js +101 -3
  301. package/server/dist/project-router-chat.js +1 -1
  302. package/server/dist/project-router-helpers.js +25 -12
  303. package/server/dist/project-router-jobs.js +3 -3
  304. package/server/dist/project-router-settings.js +8 -6
  305. package/server/dist/project-router-setup.js +27 -10
  306. package/server/dist/project-router-spending.js +6 -1
  307. package/server/dist/project-router-tickets.js +30 -10
  308. package/server/dist/project-router.js +16 -1
  309. package/server/dist/providers/gemini-adapter.js +4 -0
  310. package/server/dist/providers/gemini-agent-ack.js +65 -0
  311. package/server/dist/queue-manager.js +156 -12
  312. package/server/dist/setup-manager.js +131 -29
  313. package/server/dist/smash-runner.js +21 -6
  314. package/server/dist/ticket-store.js +6 -2
  315. package/server/dist/ticket-watcher.js +5 -1
  316. package/server/dist/util/stream-display.js +18 -3
  317. package/server/dist/vitest-setup.js +25 -0
  318. package/server/dist/workspace-manager.js +199 -0
  319. package/server/dist/workspace-resolution.js +147 -0
  320. package/client/dist/assets/index-DgKfQFcf.css +0 -2
  321. package/client/dist/assets/setup-BIIkb-_K.js +0 -1
  322. package/client/dist/assets/setup-BeQxu9kD.js +0 -1
  323. package/client/dist/assets/setup-CPa6GnlI.js +0 -1
  324. package/client/dist/assets/setup-CZl4OEJx.js +0 -1
  325. package/client/dist/assets/setup-ChpodNfn.js +0 -1
  326. package/client/dist/assets/setup-D_fjJH6u.js +0 -1
  327. package/client/dist/assets/setup-YzD8DX4O.js +0 -1
  328. package/client/dist/assets/setup-fRpDozmq.js +0 -1
  329. package/docs/adding-a-provider.md +0 -107
@@ -23,6 +23,9 @@ const ticket_watcher_1 = require("./ticket-watcher");
23
23
  const terminal_manager_1 = require("./terminal-manager");
24
24
  const browser_capture_manager_1 = require("./browser-capture-manager");
25
25
  const explore_cwd_manager_1 = require("./explore-cwd-manager");
26
+ const artifact_registry_1 = require("./artifact-registry");
27
+ const workspace_resolution_1 = require("./workspace-resolution");
28
+ const workspace_manager_1 = require("./workspace-manager");
26
29
  const ticket_store_1 = require("./ticket-store");
27
30
  const jira_sync_manager_1 = require("./jira/jira-sync-manager");
28
31
  const rails_store_1 = require("./rails-store");
@@ -51,6 +54,22 @@ class ProjectRegistry {
51
54
  }
52
55
  loadAll() {
53
56
  const projects = (0, desktop_db_1.listProjects)(this._desktopDb);
57
+ // Self-heal the shared artifact registry: project one entry per desktop
58
+ // project, leaving non-desktop (core-standalone) entries untouched. Wrapped
59
+ // so a registry write failure never blocks app startup — a missing entry is
60
+ // recreated on the next addProject/reconcile.
61
+ try {
62
+ (0, artifact_registry_1.reconcileFromProjects)(projects.map((p) => ({
63
+ repoPath: p.path,
64
+ slug: p.slug,
65
+ providers: p.providers,
66
+ primaryProvider: p.provider,
67
+ desktopProjectId: p.id,
68
+ })));
69
+ }
70
+ catch (err) {
71
+ console.error('[project-registry] registry reconcile failed (non-fatal):', err);
72
+ }
54
73
  for (const project of projects) {
55
74
  try {
56
75
  this._loadProjectContext(project);
@@ -71,11 +90,48 @@ class ProjectRegistry {
71
90
  listFailedProjects() {
72
91
  return Array.from(this._failedProjects.values());
73
92
  }
93
+ /**
94
+ * The deduped union of providers across every registered project (from the
95
+ * desktop DB, so it includes projects that failed to load a per-project DB).
96
+ * Used by the framework boot `versionCheck` to decide which providerDirs to
97
+ * materialize. Defaults to `['claude']` when there are no projects yet (so a
98
+ * fresh install still materializes the claude framework for the first add).
99
+ */
100
+ installedProvidersUnion() {
101
+ const set = new Set();
102
+ for (const p of (0, desktop_db_1.listProjects)(this._desktopDb)) {
103
+ const list = p.providers && p.providers.length > 0 ? p.providers : [p.provider];
104
+ for (const prov of list) {
105
+ if (prov && prov.length > 0)
106
+ set.add(prov);
107
+ }
108
+ }
109
+ return set.size > 0 ? Array.from(set) : ['claude'];
110
+ }
74
111
  addProject(opts) {
75
112
  const row = (0, desktop_db_1.addProject)(this._desktopDb, opts);
113
+ // Mirror the new project into the shared artifact registry so specrails-core
114
+ // resolves its relocated artifacts. Wrapped so a registry write failure never
115
+ // breaks project creation — the startup reconcile will recreate the entry.
116
+ try {
117
+ (0, artifact_registry_1.mirrorProjectEntry)({
118
+ repoPath: row.path,
119
+ slug: row.slug,
120
+ providers: row.providers,
121
+ primaryProvider: row.provider,
122
+ desktopProjectId: row.id,
123
+ });
124
+ }
125
+ catch (err) {
126
+ console.error('[project-registry] registry mirror failed (non-fatal):', err);
127
+ }
76
128
  return this._loadProjectContext(row);
77
129
  }
78
130
  removeProject(id) {
131
+ // Resolve the repo path BEFORE the DB row is deleted below so we can drop the
132
+ // shared artifact-registry entry. Prefer the live context, fall back to the
133
+ // desktop DB row (project may be registered-but-not-loaded, e.g. M9 failure).
134
+ const repoPath = this._contexts.get(id)?.project.path ?? (0, desktop_db_1.getProject)(this._desktopDb, id)?.path;
79
135
  const ctx = this._contexts.get(id);
80
136
  if (ctx) {
81
137
  // Tear down spawners BEFORE closing the DB. QueueManager.shutdown() drops
@@ -163,6 +219,38 @@ class ProjectRegistry {
163
219
  catch { /* ignore — non-fatal */ }
164
220
  this._contexts.delete(id);
165
221
  }
222
+ // Drop the relocated WORKSPACE for an adopted project whose REGISTRY slug
223
+ // differs from the desktop slug. The per-project data-dir rm above only
224
+ // removes `projects/<desktop-slug>`; an adopted repo's workspace lives under
225
+ // `projects/<registry-slug>/workspace` and would otherwise leak. Resolve the
226
+ // registry entry (BEFORE removeRegistryEntry deletes it) and, when relocated,
227
+ // remove the workspace under the registry slug. Best-effort — never blocks
228
+ // removal. (When the slugs match, the dir is already gone; removeWorkspace
229
+ // no-ops on a missing dir.)
230
+ if (repoPath) {
231
+ try {
232
+ const art = (0, artifact_registry_1.resolveArtifacts)(repoPath);
233
+ if (!art.isLegacy && art.entry?.slug) {
234
+ // Use the SAME home the registry/workspace live under (== os.homedir()
235
+ // in production; overridable via SPECRAILS_REGISTRY_HOME in tests) so
236
+ // the workspace dir resolves identically to the registry entry.
237
+ (0, workspace_manager_1.removeWorkspace)(art.entry.slug, (0, artifact_registry_1.resolveHome)());
238
+ }
239
+ }
240
+ catch (err) {
241
+ console.error('[project-registry] workspace remove failed (non-fatal):', err);
242
+ }
243
+ }
244
+ // Drop the shared artifact-registry entry for this repo. Wrapped so a
245
+ // registry write failure never blocks project removal.
246
+ if (repoPath) {
247
+ try {
248
+ (0, artifact_registry_1.removeRegistryEntry)(repoPath);
249
+ }
250
+ catch (err) {
251
+ console.error('[project-registry] registry remove failed (non-fatal):', err);
252
+ }
253
+ }
166
254
  (0, desktop_db_1.removeProject)(this._desktopDb, id);
167
255
  }
168
256
  getContext(id) {
@@ -319,7 +407,12 @@ class ProjectRegistry {
319
407
  if (completedTicketIds.length > 0 &&
320
408
  (status === 'completed' || status === 'failed' || status === 'canceled' || status === 'zombie_terminated')) {
321
409
  try {
322
- const ticketFile = (0, ticket_store_1.resolveTicketStoragePath)(project.path);
410
+ // Relocate-artifacts gate: write the job outcome to the workspace
411
+ // ticket store when relocated, else the repo-relative store (legacy).
412
+ const outcomeExec = (0, workspace_resolution_1.resolveProjectExecution)({ slug: project.slug, path: project.path });
413
+ const ticketFile = outcomeExec.relocated
414
+ ? outcomeExec.ticketsPath
415
+ : (0, ticket_store_1.resolveTicketStoragePath)(project.path);
323
416
  const now = new Date().toISOString();
324
417
  let changedIds = [];
325
418
  const store = (0, ticket_store_1.mutateStore)(ticketFile, (s) => {
@@ -453,9 +546,14 @@ class ProjectRegistry {
453
546
  // persistent recursive watcher per project, the source of the fd leak that
454
547
  // broke terminals. The watcher is now attached lazily on the first
455
548
  // code-explorer request (see code-explorer-router.ts).
456
- // Load commands for this project
549
+ // Load commands for this project. Relocate-artifacts: sr/specrails commands
550
+ // are materialized into the WORKSPACE when the project is relocated; the repo
551
+ // has none, so scanning project.path would find nothing. Resolve the gate so
552
+ // command discovery reads the same tree the rails load (legacy ⇒ repo).
457
553
  try {
458
- const config = (0, config_1.getConfig)(project.path, db, project.name);
554
+ const cmdExec = (0, workspace_resolution_1.resolveProjectExecution)({ slug: project.slug, path: project.path });
555
+ const commandsRoot = cmdExec.relocated && cmdExec.workspaceDir ? cmdExec.workspaceDir : project.path;
556
+ const config = (0, config_1.getConfig)(project.path, db, project.name, commandsRoot);
459
557
  queueManager.setCommands(config.commands);
460
558
  }
461
559
  catch {
@@ -32,7 +32,7 @@ function registerChatRoutes(deps) {
32
32
  const rawModel = req.body?.model;
33
33
  let model;
34
34
  if (rawModel === undefined || rawModel === null || rawModel === '') {
35
- model = (0, project_router_helpers_1.resolveDefaultSpecModel)({ projectPath: project.path, provider });
35
+ model = (0, project_router_helpers_1.resolveDefaultSpecModel)({ projectPath: project.path, slug: project.slug, provider });
36
36
  }
37
37
  else if ((0, spec_models_1.isValidModelForProvider)(rawModel, provider)) {
38
38
  model = rawModel;
@@ -21,6 +21,8 @@ const path_1 = __importDefault(require("path"));
21
21
  const core_package_1 = require("./core-package");
22
22
  const spec_models_1 = require("./spec-models");
23
23
  const ticket_store_1 = require("./ticket-store");
24
+ const install_config_path_1 = require("./install-config-path");
25
+ const workspace_resolution_1 = require("./workspace-resolution");
24
26
  const TERMINAL_PANEL_ENABLED = process.env.SPECRAILS_TERMINAL_PANEL !== 'false';
25
27
  exports.TERMINAL_PANEL_ENABLED = TERMINAL_PANEL_ENABLED;
26
28
  // ─── YAML helpers ─────────────────────────────────────────────────────────────
@@ -55,12 +57,23 @@ function serializeInstallConfigYaml(config) {
55
57
  // ─── Agent model helpers ──────────────────────────────────────────────────────
56
58
  const VALID_MODEL_ALIASES = ['sonnet', 'opus', 'haiku'];
57
59
  exports.VALID_MODEL_ALIASES = VALID_MODEL_ALIASES;
60
+ /**
61
+ * Relocate-artifacts: where the agent `.claude/agents/*.md` files live. When the
62
+ * project is relocated AND the workspace is populated by core, the agents are
63
+ * materialized into `<workspace>/.claude/agents` (same reasoning the profiles
64
+ * router uses for profiles). Legacy ⇒ `<project>/.claude/agents` (byte-identical).
65
+ */
66
+ function resolveAgentsDir(project) {
67
+ const exec = (0, workspace_resolution_1.resolveProjectExecution)({ slug: project.slug, path: project.path });
68
+ const root = exec.relocated && exec.workspaceDir ? exec.workspaceDir : project.path;
69
+ return path_1.default.join(root, '.claude', 'agents');
70
+ }
58
71
  /**
59
72
  * Read installed agents from `.claude/agents/*.md` (top-level only, no subdirs).
60
73
  * Extracts the `model:` field from YAML frontmatter.
61
74
  */
62
- function readAgentModels(projectPath) {
63
- const agentsDir = path_1.default.join(projectPath, '.claude', 'agents');
75
+ function readAgentModels(project) {
76
+ const agentsDir = resolveAgentsDir(project);
64
77
  if (!fs_1.default.existsSync(agentsDir))
65
78
  return [];
66
79
  let entries;
@@ -97,12 +110,12 @@ function readAgentModels(projectPath) {
97
110
  return agents;
98
111
  }
99
112
  /**
100
- * Read `.specrails/install-config.yaml` and patch the `model:` line in each
101
- * `.claude/agents/*.md` frontmatter to match the config's defaults/overrides.
102
- * No-op if the config file does not exist.
113
+ * Read the install-config.yaml (per-project HOME dir) and patch the `model:`
114
+ * line in each `.claude/agents/*.md` frontmatter to match the config's
115
+ * defaults/overrides. No-op if the config file does not exist.
103
116
  */
104
- function applyModelConfig(projectPath) {
105
- const configPath = path_1.default.join(projectPath, '.specrails', 'install-config.yaml');
117
+ function applyModelConfig(project) {
118
+ const configPath = (0, install_config_path_1.installConfigPath)(project);
106
119
  if (!fs_1.default.existsSync(configPath))
107
120
  return;
108
121
  let configText;
@@ -127,7 +140,7 @@ function applyModelConfig(projectPath) {
127
140
  overrides[m[1]] = m[2];
128
141
  }
129
142
  }
130
- const agentsDir = path_1.default.join(projectPath, '.claude', 'agents');
143
+ const agentsDir = resolveAgentsDir(project);
131
144
  if (!fs_1.default.existsSync(agentsDir))
132
145
  return;
133
146
  let entries;
@@ -244,16 +257,16 @@ function formatDescriptionWithCriteria(body, criteria) {
244
257
  * Resolve the default model used by Add Spec for a project.
245
258
  *
246
259
  * Order:
247
- * 1. `models.defaults.model` from `<project>/.specrails/install-config.yaml`,
248
- * if it parses AND is in the provider allow-list.
260
+ * 1. `models.defaults.model` from the install-config.yaml (per-project HOME
261
+ * dir), if it parses AND is in the provider allow-list.
249
262
  * 2. Provider default from `PROVIDER_DEFAULT_MODEL` (`sonnet` / `gpt-5.5`).
250
263
  *
251
264
  * Logs a warning when the configured value exists but is not valid for the
252
265
  * project's provider.
253
266
  */
254
267
  function resolveDefaultSpecModel(args) {
255
- const { projectPath, provider } = args;
256
- const configPath = path_1.default.join(projectPath, '.specrails', 'install-config.yaml');
268
+ const { projectPath, slug, provider } = args;
269
+ const configPath = (0, install_config_path_1.installConfigPath)({ slug, path: projectPath });
257
270
  if (!fs_1.default.existsSync(configPath))
258
271
  return (0, spec_models_1.getProviderDefault)(provider);
259
272
  let configText;
@@ -96,7 +96,7 @@ function registerJobsRoutes(deps) {
96
96
  // response also lists every installed provider so the Add Spec modal can
97
97
  // render its AI Engine selector without a second round-trip.
98
98
  const provider = (0, provider_selection_1.resolveProvider)(project, typeof req.query.provider === 'string' ? req.query.provider : undefined);
99
- const model = (0, project_router_helpers_1.resolveDefaultSpecModel)({ projectPath: project.path, provider });
99
+ const model = (0, project_router_helpers_1.resolveDefaultSpecModel)({ projectPath: project.path, slug: project.slug, provider });
100
100
  const allowed = (0, spec_models_1.getModelsForProvider)(provider);
101
101
  res.json({ model, provider, allowed, providers: project.providers });
102
102
  });
@@ -389,13 +389,13 @@ function registerJobsRoutes(deps) {
389
389
  skip_reason: inMemory.skipReason,
390
390
  };
391
391
  const phaseDefinitions = queueManager.phasesForCommand(synthetic.command);
392
- const tickets = (0, ticket_store_1.resolveTicketsFromCommand)(project.path, synthetic.command);
392
+ const tickets = (0, ticket_store_1.resolveTicketsFromCommand)(project.path, synthetic.command, ticketPath(req));
393
393
  res.json({ job: { ...synthetic, hasTelemetry: false, tickets }, events: [], phaseDefinitions });
394
394
  return;
395
395
  }
396
396
  const events = (0, db_1.getJobEvents)(db, jobId);
397
397
  const phaseDefinitions = queueManager.phasesForCommand(job.command);
398
- const tickets = (0, ticket_store_1.resolveTicketsFromCommand)(project.path, job.command);
398
+ const tickets = (0, ticket_store_1.resolveTicketsFromCommand)(project.path, job.command, ticketPath(req));
399
399
  const annotated = { ...job, hasTelemetry: (0, db_1.hasJobTelemetry)(db, jobId), tickets };
400
400
  res.json({ job: annotated, events, phaseDefinitions });
401
401
  });
@@ -15,6 +15,7 @@ const context_scope_1 = require("./context-scope");
15
15
  const terminal_settings_1 = require("./terminal-settings");
16
16
  const terminal_marks_store_1 = require("./terminal-marks-store");
17
17
  const project_router_helpers_1 = require("./project-router-helpers");
18
+ const install_config_path_1 = require("./install-config-path");
18
19
  function registerSettingsRoutes(deps) {
19
20
  const { router, registry, ctx, ticketPath } = deps;
20
21
  // ─── Project settings (pipeline telemetry) ───────────────────────────────────
@@ -112,7 +113,7 @@ function registerSettingsRoutes(deps) {
112
113
  // ─── Agent models ────────────────────────────────────────────────────────────
113
114
  router.get('/:projectId/agent-models', (req, res) => {
114
115
  const { project } = ctx(req);
115
- const agents = (0, project_router_helpers_1.readAgentModels)(project.path);
116
+ const agents = (0, project_router_helpers_1.readAgentModels)(project);
116
117
  res.json({ agents });
117
118
  });
118
119
  router.patch('/:projectId/agent-models', (req, res) => {
@@ -138,8 +139,9 @@ function registerSettingsRoutes(deps) {
138
139
  }
139
140
  }
140
141
  }
141
- const configDir = path_1.default.join(project.path, '.specrails');
142
- const configPath = path_1.default.join(configDir, 'install-config.yaml');
142
+ // Relocate-artifacts: the install config lives in the per-project HOME dir,
143
+ // NEVER `<project>/.specrails` (which would leak into the user's repo).
144
+ const configPath = (0, install_config_path_1.installConfigPath)(project);
143
145
  // Read existing config or build default shape
144
146
  let existingConfig = {
145
147
  version: 1,
@@ -206,11 +208,11 @@ function registerSettingsRoutes(deps) {
206
208
  }
207
209
  existingConfig.models = mergedModels;
208
210
  try {
209
- fs_1.default.mkdirSync(configDir, { recursive: true });
211
+ fs_1.default.mkdirSync(path_1.default.dirname(configPath), { recursive: true });
210
212
  const yaml = (0, project_router_helpers_1.serializeInstallConfigYaml)(existingConfig);
211
213
  fs_1.default.writeFileSync(configPath, yaml, 'utf-8');
212
- (0, project_router_helpers_1.applyModelConfig)(project.path);
213
- const agents = (0, project_router_helpers_1.readAgentModels)(project.path);
214
+ (0, project_router_helpers_1.applyModelConfig)(project);
215
+ const agents = (0, project_router_helpers_1.readAgentModels)(project);
214
216
  res.json({ agents });
215
217
  }
216
218
  catch (err) {
@@ -15,6 +15,8 @@ const queue_manager_1 = require("./queue-manager");
15
15
  const command_resolver_1 = require("./command-resolver");
16
16
  const changes_reader_1 = require("./changes-reader");
17
17
  const project_router_helpers_1 = require("./project-router-helpers");
18
+ const install_config_path_1 = require("./install-config-path");
19
+ const workspace_resolution_1 = require("./workspace-resolution");
18
20
  function registerSetupRoutes(deps) {
19
21
  const { router, registry, ctx, ticketPath } = deps;
20
22
  // ─── Install-config route ─────────────────────────────────────────────────────
@@ -25,10 +27,12 @@ function registerSetupRoutes(deps) {
25
27
  res.status(400).json({ error: 'Request body must be a config object' });
26
28
  return;
27
29
  }
28
- const configDir = path_1.default.join(project.path, '.specrails');
29
- const configPath = path_1.default.join(configDir, 'install-config.yaml');
30
+ // Relocate-artifacts: write to the per-project app-managed HOME dir, NEVER
31
+ // `<project>/.specrails` so a fresh setup creates ZERO `.specrails` in the
32
+ // user's repo. See server/install-config-path.ts.
33
+ const configPath = (0, install_config_path_1.installConfigPath)(project);
30
34
  try {
31
- fs_1.default.mkdirSync(configDir, { recursive: true });
35
+ fs_1.default.mkdirSync(path_1.default.dirname(configPath), { recursive: true });
32
36
  const yaml = (0, project_router_helpers_1.serializeInstallConfigYaml)(config);
33
37
  fs_1.default.writeFileSync(configPath, yaml, 'utf-8');
34
38
  res.json({ ok: true, path: configPath });
@@ -45,7 +49,7 @@ function registerSetupRoutes(deps) {
45
49
  return;
46
50
  }
47
51
  res.status(202).json({ ok: true });
48
- setupManager.startInstall(project.id, project.path);
52
+ setupManager.startInstall(project.id, project.path, project.slug);
49
53
  });
50
54
  router.post('/:projectId/enrich/start', (req, res) => {
51
55
  const { project, setupManager } = ctx(req);
@@ -115,7 +119,7 @@ function registerSetupRoutes(deps) {
115
119
  tier: setupManager.getInstallTier(project.id) ?? null,
116
120
  savedSessionId: savedSessionId ?? null,
117
121
  logLines: setupManager.getInstallLog(project.id),
118
- summary: setupManager.getSummary(project.path),
122
+ summary: setupManager.getSummary(project),
119
123
  });
120
124
  });
121
125
  router.post('/:projectId/setup/abort', (req, res) => {
@@ -428,11 +432,17 @@ function registerSetupRoutes(deps) {
428
432
  });
429
433
  // ─── Integration contract ──────────────────────────────────────────────────
430
434
  const DEFAULT_TICKET_CAPABILITIES = ['crud', 'labels', 'status', 'priorities', 'dependencies'];
431
- const DEFAULT_TICKET_STORAGE_PATH = '.specrails/local-tickets.json';
432
435
  // GET /:projectId/integration-contract — Return the project's integration contract with ticketProvider
433
436
  router.get('/:projectId/integration-contract', (req, res) => {
434
- const projectPath = ctx(req).project.path;
435
- const contractFile = path_1.default.join(projectPath, '.claude', 'integration-contract.json');
437
+ const project = ctx(req).project;
438
+ const projectPath = project.path;
439
+ // Relocate-artifacts: the integration-contract.json + tickets store live in
440
+ // the workspace when relocated. Mirror the gated ticketPath() so this
441
+ // informational endpoint reports the path the relocated CLI actually loads,
442
+ // not a stale repo path. Legacy projects are byte-identical.
443
+ const exec = (0, workspace_resolution_1.resolveProjectExecution)({ slug: project.slug, path: projectPath });
444
+ const contractRoot = exec.relocated && exec.workspaceDir ? exec.workspaceDir : projectPath;
445
+ const contractFile = path_1.default.join(contractRoot, '.claude', 'integration-contract.json');
436
446
  let rawContract = {};
437
447
  let source = 'default';
438
448
  if (fs_1.default.existsSync(contractFile)) {
@@ -445,10 +455,17 @@ function registerSetupRoutes(deps) {
445
455
  }
446
456
  }
447
457
  const rawProvider = rawContract.ticketProvider;
448
- const storagePath = rawProvider?.storagePath ?? DEFAULT_TICKET_STORAGE_PATH;
458
+ // When relocated, the tickets store is the registry entry's ticketsPath (an
459
+ // absolute workspace path); a custom storagePath from the contract is still
460
+ // resolved relative to the contract root. Legacy ⇒ ticketPath() (which
461
+ // honours the contract's custom storagePath).
462
+ const storagePath = rawProvider?.storagePath;
463
+ const resolvedStorage = storagePath
464
+ ? path_1.default.resolve(contractRoot, storagePath)
465
+ : ticketPath(req);
449
466
  const ticketProvider = {
450
467
  type: rawProvider?.type ?? 'local',
451
- storagePath: path_1.default.resolve(projectPath, storagePath),
468
+ storagePath: resolvedStorage,
452
469
  capabilities: rawProvider?.capabilities ?? DEFAULT_TICKET_CAPABILITIES,
453
470
  };
454
471
  res.json({ ticketProvider, source });
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.registerSpendingRoutes = registerSpendingRoutes;
4
4
  const queue_manager_1 = require("./queue-manager");
5
5
  const config_1 = require("./config");
6
+ const workspace_resolution_1 = require("./workspace-resolution");
6
7
  const ai_invocations_1 = require("./ai-invocations");
7
8
  const spending_1 = require("./spending");
8
9
  const ticket_store_1 = require("./ticket-store");
@@ -205,7 +206,11 @@ function registerSpendingRoutes(deps) {
205
206
  router.get('/:projectId/config', (req, res) => {
206
207
  const { project, db } = ctx(req);
207
208
  try {
208
- const config = (0, config_1.getConfig)(project.path, db, project.name);
209
+ // Relocate-artifacts: commands are materialized into the workspace when
210
+ // relocated; resolve the gate so the /config commands list is non-empty.
211
+ const cfgExec = (0, workspace_resolution_1.resolveProjectExecution)({ slug: project.slug, path: project.path });
212
+ const commandsRoot = cfgExec.relocated && cfgExec.workspaceDir ? cfgExec.workspaceDir : project.path;
213
+ const config = (0, config_1.getConfig)(project.path, db, project.name, commandsRoot);
209
214
  const dailyBudgetRaw = db.prepare(`SELECT value FROM queue_state WHERE key = 'config.daily_budget_usd'`).get()?.value;
210
215
  const dailyBudgetUsd = dailyBudgetRaw != null ? parseFloat(dailyBudgetRaw) : null;
211
216
  const zombieTimeoutRaw = db.prepare(`SELECT value FROM queue_state WHERE key = 'config.zombie_timeout_ms'`).get()?.value;
@@ -58,6 +58,7 @@ const explore_draft_title_1 = require("./explore-draft-title");
58
58
  const cli_prompt_1 = require("./util/cli-prompt");
59
59
  const readline_1 = require("readline");
60
60
  const tree_kill_1 = __importDefault(require("tree-kill"));
61
+ const workspace_resolution_1 = require("./workspace-resolution");
61
62
  const multer_1 = __importDefault(require("multer"));
62
63
  const attachment_manager_1 = require("./attachment-manager");
63
64
  const project_router_helpers_1 = require("./project-router-helpers");
@@ -160,7 +161,7 @@ function registerTicketsRoutes(deps) {
160
161
  const rawModel = req.body?.model;
161
162
  let resolvedModel;
162
163
  if (rawModel === undefined || rawModel === null || rawModel === '') {
163
- resolvedModel = (0, project_router_helpers_1.resolveDefaultSpecModel)({ projectPath: project.path, provider });
164
+ resolvedModel = (0, project_router_helpers_1.resolveDefaultSpecModel)({ projectPath: project.path, slug: project.slug, provider });
164
165
  }
165
166
  else if ((0, spec_models_1.isValidModelForProvider)(rawModel, provider)) {
166
167
  resolvedModel = rawModel;
@@ -218,7 +219,12 @@ function registerTicketsRoutes(deps) {
218
219
  };
219
220
  // Persist Quick mode Contract Refine choice (per-project last value).
220
221
  (0, db_1.setQuickContractRefineLast)(ctx(req).db, quickContractRefine);
221
- const specsPrefix = (0, context_scope_1.buildScopedSystemPromptPrefix)(quickScope, project.path);
222
+ // Relocate-artifacts: tickets read from the workspace when relocated.
223
+ const quickSpecRoot = (() => {
224
+ const e = (0, workspace_resolution_1.resolveProjectExecution)({ slug: project.slug, path: project.path });
225
+ return e.relocated && e.workspaceDir ? e.workspaceDir : project.path;
226
+ })();
227
+ const specsPrefix = (0, context_scope_1.buildScopedSystemPromptPrefix)(quickScope, project.path, quickSpecRoot);
222
228
  const codebaseRule = quickScope.full
223
229
  ? `- You MAY use Read, Grep, and Glob to inspect the project codebase. Bash is not available.`
224
230
  : hasAttachments
@@ -284,13 +290,22 @@ function registerTicketsRoutes(deps) {
284
290
  loadUserEnv: provider === 'claude' && quickScope.userMcp,
285
291
  });
286
292
  const binary = adapter.binary;
293
+ // Relocate-artifacts gate: spawn from the workspace + SPECRAILS_REPO_DIR when
294
+ // relocated, else cwd = project.path + process.env (byte-identical legacy).
295
+ const specGenExec = (0, workspace_resolution_1.resolveProjectExecution)({ slug: project.slug, path: project.path });
296
+ let specGenEnv = process.env;
297
+ if (specGenExec.relocated) {
298
+ specGenEnv = { ...process.env, ...specGenExec.env };
299
+ if (adapter.id === 'gemini')
300
+ specGenEnv = { ...specGenEnv, GEMINI_CLI_TRUST_WORKSPACE: 'true' };
301
+ }
287
302
  // spawnAiCli reroutes multi-line argv values through stdin on Windows;
288
303
  // POSIX argv path unchanged.
289
- console.log(`[project-router] spec-gen spawn: ${binary} (cwd=${project.path}, requestId=${requestId})`);
304
+ console.log(`[project-router] spec-gen spawn: ${binary} (cwd=${specGenExec.cwd}, requestId=${requestId})`);
290
305
  const child = (0, cli_prompt_1.spawnAiCli)(binary, args, {
291
- env: process.env,
306
+ env: specGenEnv,
292
307
  stdio: ['ignore', 'pipe', 'pipe'],
293
- cwd: project.path,
308
+ cwd: specGenExec.cwd,
294
309
  });
295
310
  // Watchdog: unlike ai-edit, generate-spec keeps no cancellable handle, so a
296
311
  // hung CLI (network stall, model never emitting a terminating event) would
@@ -511,7 +526,7 @@ function registerTicketsRoutes(deps) {
511
526
  slug: project.slug,
512
527
  pendingId: pendingSpecId,
513
528
  realTicketId: created.id,
514
- projectPath: project.path,
529
+ ticketStorePath: ticketPath(req),
515
530
  });
516
531
  if (migrated.length > 0) {
517
532
  created.attachments = migrated;
@@ -894,7 +909,7 @@ function registerTicketsRoutes(deps) {
894
909
  slug: project.slug,
895
910
  pendingId: pendingSpecId,
896
911
  realTicketId: created.id,
897
- projectPath: project.path,
912
+ ticketStorePath: ticketPath(req),
898
913
  });
899
914
  if (migrated.length > 0) {
900
915
  created.attachments = migrated;
@@ -1040,7 +1055,7 @@ function registerTicketsRoutes(deps) {
1040
1055
  slug: project.slug,
1041
1056
  pendingId: pendingSpecId,
1042
1057
  realTicketId: created.id,
1043
- projectPath: project.path,
1058
+ ticketStorePath: ticketPath(req),
1044
1059
  });
1045
1060
  if (migrated.length > 0) {
1046
1061
  created.attachments = migrated;
@@ -1147,6 +1162,10 @@ function registerTicketsRoutes(deps) {
1147
1162
  projectSlug: project.slug,
1148
1163
  projectPath: project.path,
1149
1164
  projectName: project.name,
1165
+ // Relocate-artifacts: pass the gated tickets-store path so SMASH
1166
+ // reads/writes the same store the rails load (workspace when
1167
+ // relocated), not a stale repo copy.
1168
+ ticketsPath: filePath,
1150
1169
  broadcast: broadcast,
1151
1170
  mode,
1152
1171
  model,
@@ -1184,6 +1203,7 @@ function registerTicketsRoutes(deps) {
1184
1203
  projectSlug: project.slug,
1185
1204
  projectPath: project.path,
1186
1205
  projectName: project.name,
1206
+ ticketsPath: ticketPath(req),
1187
1207
  broadcast: broadcast,
1188
1208
  }, ticketId, smashedAt);
1189
1209
  if (!result.ok) {
@@ -1768,7 +1788,7 @@ function registerTicketsRoutes(deps) {
1768
1788
  const attachment = await attachment_manager_1.attachmentManager.upload({
1769
1789
  slug: ctx(req).project.slug,
1770
1790
  ticketKey: parsed.key,
1771
- projectPath: parsed.isPending ? null : ctx(req).project.path,
1791
+ ticketStorePath: parsed.isPending ? null : ticketPath(req),
1772
1792
  file: {
1773
1793
  buffer: file.buffer,
1774
1794
  originalname: file.originalname,
@@ -1829,7 +1849,7 @@ function registerTicketsRoutes(deps) {
1829
1849
  slug: ctx(req).project.slug,
1830
1850
  ticketKey: parsed.key,
1831
1851
  attachmentId,
1832
- projectPath: parsed.isPending ? null : ctx(req).project.path,
1852
+ ticketStorePath: parsed.isPending ? null : ticketPath(req),
1833
1853
  });
1834
1854
  if (!ok) {
1835
1855
  res.status(404).json({ error: 'Attachment not found' });
@@ -10,6 +10,7 @@ const plugins_router_1 = require("./plugins-router");
10
10
  const code_explorer_router_1 = require("./code-explorer-router");
11
11
  const jira_router_1 = require("./jira-router");
12
12
  const ticket_store_1 = require("./ticket-store");
13
+ const workspace_resolution_1 = require("./workspace-resolution");
13
14
  const project_router_jobs_1 = require("./project-router-jobs");
14
15
  const project_router_spending_1 = require("./project-router-spending");
15
16
  const project_router_chat_1 = require("./project-router-chat");
@@ -88,10 +89,24 @@ function createProjectRouter(registry) {
88
89
  projectId: projectCtx.project.id,
89
90
  broadcast: projectCtx.broadcast,
90
91
  fileSummaryManager: projectCtx.fileSummaryManager,
92
+ // Relocate-artifacts: summary JSON OUTPUTS live in the workspace when
93
+ // relocated (source tree still read from project.path). Resolved per-call.
94
+ resolveSummaryRoot: () => {
95
+ const exec = (0, workspace_resolution_1.resolveProjectExecution)({ slug: projectCtx.project.slug, path: projectCtx.project.path });
96
+ return exec.relocated && exec.workspaceDir ? exec.workspaceDir : projectCtx.project.path;
97
+ },
91
98
  }));
92
99
  codeRouter(req, res, next);
93
100
  });
94
- const ticketPath = (req) => (0, ticket_store_1.resolveTicketStoragePath)(ctx(req).project.path);
101
+ // Relocate-artifacts gate (single chokepoint for ALL project-router ticket
102
+ // I/O): relocated ⇒ the registry entry's ticketsPath (workspace); legacy ⇒
103
+ // resolveTicketStoragePath (preserves integration-contract.json custom
104
+ // storagePath). Existing in-repo projects are byte-identical.
105
+ const ticketPath = (req) => {
106
+ const project = ctx(req).project;
107
+ const exec = (0, workspace_resolution_1.resolveProjectExecution)({ slug: project.slug, path: project.path });
108
+ return exec.relocated ? exec.ticketsPath : (0, ticket_store_1.resolveTicketStoragePath)(project.path);
109
+ };
95
110
  const deps = { router, registry, ctx, ticketPath };
96
111
  (0, project_router_jobs_1.registerJobsRoutes)(deps);
97
112
  (0, project_router_spending_1.registerSpendingRoutes)(deps);
@@ -29,6 +29,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
29
29
  exports._GEMINI_MIN_VERSION = exports.geminiAdapter = void 0;
30
30
  exports._compareSemver = compareSemver;
31
31
  const child_process_1 = require("child_process");
32
+ const gemini_agent_ack_1 = require("./gemini-agent-ack");
32
33
  const WHICH_CMD = process.platform === 'win32' ? 'where' : 'which';
33
34
  // Floor where `--output-format stream-json` + headless `--resume` are available.
34
35
  const GEMINI_MIN_VERSION = '0.11.0';
@@ -231,4 +232,7 @@ exports.geminiAdapter = {
231
232
  extractResult: extractGeminiResult,
232
233
  baselineAgents: () => ['sr-architect', 'sr-developer', 'sr-reviewer'],
233
234
  detectInstalled: detectGeminiInstalled,
235
+ // Pre-acknowledge the project's custom subagents so they load in headless
236
+ // `gemini -p` rail spawns (else invoke_agent reports "Subagent not found").
237
+ prepareHeadlessSpawn: gemini_agent_ack_1.acknowledgeGeminiProjectAgents,
234
238
  };