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
@@ -21,6 +21,7 @@ const context_scope_1 = require("./context-scope");
21
21
  const user_mcp_config_1 = require("./user-mcp-config");
22
22
  const binary_probe_1 = require("./binary-probe");
23
23
  const explore_stdin_session_1 = require("./explore-stdin-session");
24
+ const workspace_resolution_1 = require("./workspace-resolution");
24
25
  const COMMAND_INSTRUCTION = 'When you want to suggest a SpecRails command for the user to execute, wrap it in a command block like this: ' +
25
26
  ':::command\n/specrails:implement #42\n::: ' +
26
27
  'The user will be prompted to confirm before the command runs.';
@@ -263,9 +264,33 @@ class ChatManager {
263
264
  *
264
265
  * See openspec/changes/accelerate-spec-chat-first-token/design.md D1+D4.
265
266
  */
267
+ /**
268
+ * Relocate-artifacts: resolve execution for a NON-explore (sidebar / rail-like)
269
+ * spawn. Explore keeps its own explore-cwd logic (untouched). Returns the
270
+ * relocated cwd + env when relocated, else legacy (cwd = project.path, empty
271
+ * env). Cached per call — cheap registry read.
272
+ */
273
+ _resolveNonExploreExecution() {
274
+ if (!this._projectSlug || !this._cwd)
275
+ return null;
276
+ return (0, workspace_resolution_1.resolveProjectExecution)({ slug: this._projectSlug, path: this._cwd });
277
+ }
278
+ /**
279
+ * Relocate-artifacts: the dir whose `.specrails/local-tickets.json` the scoped
280
+ * system-prompt prefix reads. Workspace when relocated, else project.path.
281
+ */
282
+ _specrailsRoot() {
283
+ if (!this._cwd)
284
+ return undefined;
285
+ const exec = (0, workspace_resolution_1.resolveProjectExecution)({ path: this._cwd });
286
+ return exec.relocated && exec.workspaceDir ? exec.workspaceDir : this._cwd;
287
+ }
266
288
  _resolveSpawnCwd(kind, scope, providerId) {
267
- if (kind !== 'explore')
268
- return this._cwd;
289
+ if (kind !== 'explore') {
290
+ // Non-explore sidebar: route through the relocate-artifacts gate.
291
+ const exec = this._resolveNonExploreExecution();
292
+ return exec ? exec.cwd : this._cwd;
293
+ }
269
294
  if (!this._projectSlug || !this._cwd || !this._projectName)
270
295
  return this._cwd;
271
296
  // Per-conversation scope.mcp is the only source of truth. Legacy null
@@ -412,7 +437,7 @@ class ChatManager {
412
437
  `For roadmap-style requests like "suggest the next best spec", ground the answer in that context, avoid duplicates, and propose one concrete next spec instead of generic directions.`;
413
438
  if (!scope || !this._cwd)
414
439
  return scopedBase;
415
- const prefix = (0, context_scope_1.buildScopedSystemPromptPrefix)(scope, this._cwd);
440
+ const prefix = (0, context_scope_1.buildScopedSystemPromptPrefix)(scope, this._cwd, this._specrailsRoot());
416
441
  if (!prefix)
417
442
  return scopedBase;
418
443
  return `${scopedBase}\n\n${prefix}`;
@@ -528,7 +553,7 @@ class ChatManager {
528
553
  }
529
554
  let promptForAdapter = resolvedText;
530
555
  if (conversation.kind === 'explore' && adapter.id === 'codex' && conversationScope && this._cwd) {
531
- const scopedContext = (0, context_scope_1.buildScopedSystemPromptPrefix)(conversationScope, this._cwd);
556
+ const scopedContext = (0, context_scope_1.buildScopedSystemPromptPrefix)(conversationScope, this._cwd, this._specrailsRoot());
532
557
  if (scopedContext) {
533
558
  promptForAdapter =
534
559
  `Project context selected in Add Spec. Use it to avoid duplicate specs and to make project-specific recommendations.\n\n` +
@@ -568,6 +593,18 @@ class ChatManager {
568
593
  // not pipeline jobs. Telemetry is scoped to QueueManager pipeline runs only.
569
594
  // spawnAiCli reroutes multi-line argv values through stdin on Windows.
570
595
  const spawnCwd = this._resolveSpawnCwd(conversation.kind, conversationScope, adapter.id);
596
+ // Relocate-artifacts env for NON-explore (sidebar) spawns. Explore keeps its
597
+ // own explore-cwd path (no relocation env — it reaches the repo via the
598
+ // explore-cwd `./project` link). Legacy ⇒ process.env (byte-identical).
599
+ let spawnEnv = process.env;
600
+ if (conversation.kind !== 'explore') {
601
+ const exec = this._resolveNonExploreExecution();
602
+ if (exec?.relocated) {
603
+ spawnEnv = { ...process.env, ...exec.env };
604
+ if (adapter.id === 'gemini')
605
+ spawnEnv = { ...spawnEnv, GEMINI_CLI_TRUST_WORKSPACE: 'true' };
606
+ }
607
+ }
571
608
  // Big bet #3 fast-path: persistent-stdin multi-turn for Explore (claude
572
609
  // only, flag-gated default OFF). Reuses a single long-lived child across
573
610
  // turns so turns 2+ skip spawn + session rehydration. Full fallback to the
@@ -582,7 +619,7 @@ class ChatManager {
582
619
  });
583
620
  }
584
621
  const child = (0, cli_prompt_1.spawnAiCli)(binary, args, {
585
- env: process.env,
622
+ env: spawnEnv,
586
623
  stdio: ['ignore', 'pipe', 'pipe'],
587
624
  cwd: spawnCwd,
588
625
  });
@@ -335,6 +335,8 @@ function createCodeExplorerRouter(deps) {
335
335
  const router = (0, express_1.Router)({ mergeParams: true });
336
336
  const listByPath = deps.listProvenanceByPath ?? file_provenance_1.listProvenanceByPath;
337
337
  const listByTicket = deps.listProvenanceByTicket ?? file_provenance_1.listProvenanceByTicket;
338
+ // Summary OUTPUT root (workspace when relocated). Source reads use projectPath.
339
+ const summaryRoot = () => deps.resolveSummaryRoot?.() ?? deps.projectPath;
338
340
  // Short-TTL per-project cache so paginating a large `all` tree reuses ONE
339
341
  // synchronous filesystem walk instead of re-walking (and re-statting) on every
340
342
  // page — the cursor only slices an already-materialized array. Also caches the
@@ -354,7 +356,7 @@ function createCodeExplorerRouter(deps) {
354
356
  function getSummaryHashesCached() {
355
357
  if (summaryHashCache && nowMs() - summaryHashCache.at < WALK_CACHE_TTL_MS)
356
358
  return summaryHashCache.set;
357
- const set = readSummaryHashSet(deps.projectPath);
359
+ const set = readSummaryHashSet(summaryRoot());
358
360
  summaryHashCache = { at: nowMs(), set };
359
361
  return set;
360
362
  }
@@ -369,7 +371,7 @@ function createCodeExplorerRouter(deps) {
369
371
  // that broke terminals); attachWatcher is idempotent, so this is cheap on
370
372
  // every subsequent request.
371
373
  try {
372
- deps.fileSummaryManager.attachWatcher(deps.projectId, deps.projectPath);
374
+ deps.fileSummaryManager.attachWatcher(deps.projectId, deps.projectPath, summaryRoot());
373
375
  }
374
376
  catch { /* non-fatal */ }
375
377
  next();
@@ -463,7 +465,7 @@ function createCodeExplorerRouter(deps) {
463
465
  catch {
464
466
  // Honour the staleness scenario: even if content is unavailable, return
465
467
  // the existing summary so the client can render a "not found" banner.
466
- const summary = (0, file_summary_manager_1.readSummary)(deps.projectPath, rel);
468
+ const summary = (0, file_summary_manager_1.readSummary)(summaryRoot(), rel);
467
469
  const provenance = listByPath(deps.db, deps.projectId, rel);
468
470
  if (summary || provenance.length > 0) {
469
471
  res.json({
@@ -487,7 +489,7 @@ function createCodeExplorerRouter(deps) {
487
489
  tooLarge: true,
488
490
  sizeBytes: stat.size,
489
491
  provenance: provenanceRowsToJson(listByPath(deps.db, deps.projectId, rel)),
490
- summary: (0, file_summary_manager_1.readSummary)(deps.projectPath, rel),
492
+ summary: (0, file_summary_manager_1.readSummary)(summaryRoot(), rel),
491
493
  absolutePath: abs,
492
494
  });
493
495
  return;
@@ -514,7 +516,7 @@ function createCodeExplorerRouter(deps) {
514
516
  sizeBytes: stat.size,
515
517
  mime: 'application/octet-stream',
516
518
  provenance: provenanceRowsToJson(listByPath(deps.db, deps.projectId, rel)),
517
- summary: (0, file_summary_manager_1.readSummary)(deps.projectPath, rel),
519
+ summary: (0, file_summary_manager_1.readSummary)(summaryRoot(), rel),
518
520
  absolutePath: abs,
519
521
  });
520
522
  return;
@@ -527,7 +529,7 @@ function createCodeExplorerRouter(deps) {
527
529
  res.status(500).json({ error: 'failed to read file' });
528
530
  return;
529
531
  }
530
- const summary = (0, file_summary_manager_1.readSummary)(deps.projectPath, rel);
532
+ const summary = (0, file_summary_manager_1.readSummary)(summaryRoot(), rel);
531
533
  const summaryStale = await computeStaleness(abs, summary);
532
534
  res.json({
533
535
  content,
@@ -637,7 +639,7 @@ function createCodeExplorerRouter(deps) {
637
639
  res.status(403).json({ error: 'path is gitignored' });
638
640
  return;
639
641
  }
640
- const summary = (0, file_summary_manager_1.readSummary)(deps.projectPath, rel);
642
+ const summary = (0, file_summary_manager_1.readSummary)(summaryRoot(), rel);
641
643
  if (!summary) {
642
644
  res.json({ summary: null });
643
645
  return;
@@ -711,6 +713,7 @@ function createCodeExplorerRouter(deps) {
711
713
  // the content hash is unchanged (e.g. after an app language switch).
712
714
  const result = await deps.fileSummaryManager.enqueue({
713
715
  projectPath: deps.projectPath,
716
+ summaryRoot: summaryRoot(),
714
717
  projectId: deps.projectId,
715
718
  projectSlug: deps.projectId,
716
719
  relPath: rel,
@@ -218,7 +218,12 @@ function loadPersistedConfig(db) {
218
218
  return { active: null, labelFilter: '' };
219
219
  }
220
220
  }
221
- function getConfig(cwd, db, projectName) {
221
+ function getConfig(cwd, db, projectName,
222
+ /** Relocate-artifacts: the dir whose `.claude/commands/sr` holds the
223
+ * materialized sr/specrails commands — the workspace when the project is
224
+ * relocated, else === the project root. Git/repo detection ALWAYS uses the
225
+ * repo root (`projectRoot`). Defaults to `projectRoot` (byte-identical). */
226
+ commandsRoot) {
222
227
  // Resolve project root. Super mode passes project.path directly, which has a
223
228
  // `.claude` directory at its root; the walk-up fallback covers callers that
224
229
  // pass a manager-relative cwd.
@@ -229,7 +234,7 @@ function getConfig(cwd, db, projectName) {
229
234
  else {
230
235
  projectRoot = path_1.default.resolve(cwd, '../..');
231
236
  }
232
- const commandsDir = path_1.default.join(projectRoot, '.claude', 'commands', 'sr');
237
+ const commandsDir = path_1.default.join(commandsRoot ?? projectRoot, '.claude', 'commands', 'sr');
233
238
  const commands = scanCommands(commandsDir);
234
239
  const { github, jira } = detectTrackers();
235
240
  const repo = getGitRepoName(projectRoot);
@@ -15,6 +15,7 @@ exports.getContextBudget = getContextBudget;
15
15
  const node_fs_1 = __importDefault(require("node:fs"));
16
16
  const node_path_1 = __importDefault(require("node:path"));
17
17
  const context_scope_1 = require("./context-scope");
18
+ const workspace_resolution_1 = require("./workspace-resolution");
18
19
  const BYTES_PER_TOKEN = 4;
19
20
  const CACHE_TTL_MS = 60_000;
20
21
  const SKIP_DIRS = new Set([
@@ -72,8 +73,8 @@ function walkCodebase(root) {
72
73
  }
73
74
  return { fileCount, bytes };
74
75
  }
75
- function readMcpServers(projectPath) {
76
- const file = node_path_1.default.join(projectPath, '.mcp.json');
76
+ function readMcpServers(mcpRoot) {
77
+ const file = node_path_1.default.join(mcpRoot, '.mcp.json');
77
78
  if (!node_fs_1.default.existsSync(file))
78
79
  return [];
79
80
  try {
@@ -87,11 +88,17 @@ function readMcpServers(projectPath) {
87
88
  function bytesOfSection(section) {
88
89
  return section ? Buffer.byteLength(section, 'utf8') : 0;
89
90
  }
90
- function computeContextBudget(projectPath) {
91
+ function computeContextBudget(projectPath,
92
+ /** Relocate-artifacts: the dir whose `.specrails/local-tickets.json` + `.mcp.json`
93
+ * the relocated CLI actually loads — the workspace when relocated, else
94
+ * === projectPath. OpenSpec specs + the codebase walk ALWAYS read the repo
95
+ * (projectPath). Defaults to `projectPath` (byte-identical legacy). */
96
+ specrailsRoot) {
97
+ const root = specrailsRoot ?? projectPath;
91
98
  // Use the same builders the spawn path uses — token estimates reflect what
92
99
  // will actually land in the system prompt (caps + formatting included), not
93
100
  // the raw on-disk byte count of source files.
94
- const ticketsBytes = bytesOfSection((0, context_scope_1.buildSpecrailsTicketsSection)(projectPath));
101
+ const ticketsBytes = bytesOfSection((0, context_scope_1.buildSpecrailsTicketsSection)(root));
95
102
  const openspecBytes = bytesOfSection((0, context_scope_1.buildOpenSpecSpecsSection)(projectPath));
96
103
  const { fileCount, bytes: codebaseBytes } = walkCodebase(projectPath);
97
104
  return {
@@ -99,7 +106,7 @@ function computeContextBudget(projectPath) {
99
106
  openspecSpecsTokens: Math.round(openspecBytes / BYTES_PER_TOKEN),
100
107
  codebaseFileCount: fileCount,
101
108
  codebaseEstimatedTokens: Math.round(codebaseBytes / BYTES_PER_TOKEN),
102
- mcpServers: readMcpServers(projectPath),
109
+ mcpServers: readMcpServers(root),
103
110
  };
104
111
  }
105
112
  function getContextBudget(projectId, projectPath) {
@@ -107,7 +114,11 @@ function getContextBudget(projectId, projectPath) {
107
114
  const hit = cache.get(projectId);
108
115
  if (hit && hit.expiresAt > now)
109
116
  return hit.value;
110
- const value = computeContextBudget(projectPath);
117
+ // Relocate-artifacts: resolve the workspace root so the tickets + .mcp.json
118
+ // estimates reflect what the relocated CLI loads (legacy ⇒ === projectPath).
119
+ const exec = (0, workspace_resolution_1.resolveProjectExecution)({ path: projectPath });
120
+ const specrailsRoot = exec.relocated && exec.workspaceDir ? exec.workspaceDir : projectPath;
121
+ const value = computeContextBudget(projectPath, specrailsRoot);
111
122
  cache.set(projectId, { value, expiresAt: now + CACHE_TTL_MS });
112
123
  return value;
113
124
  }
@@ -247,10 +247,14 @@ function buildOpenSpecSpecsSection(projectPath) {
247
247
  return null;
248
248
  return `## OpenSpec Specs\n\n${text}${truncated ? '\n(truncated)\n' : ''}`;
249
249
  }
250
- function buildScopedSystemPromptPrefix(scope, projectPath) {
250
+ function buildScopedSystemPromptPrefix(scope, projectPath,
251
+ /** Relocate-artifacts: where `.specrails/local-tickets.json` lives — the
252
+ * workspace dir when relocated, else === projectPath. OpenSpec specs always
253
+ * read from the repo (`projectPath`). Defaults to `projectPath` (legacy). */
254
+ specrailsRoot) {
251
255
  const sections = [];
252
256
  if (scope.specrails) {
253
- const s = buildSpecrailsTicketsSection(projectPath);
257
+ const s = buildSpecrailsTicketsSection(specrailsRoot ?? projectPath);
254
258
  if (s)
255
259
  sections.push(s);
256
260
  }
@@ -27,7 +27,18 @@ const explore_cwd_manager_1 = require("./explore-cwd-manager");
27
27
  const ai_invocations_1 = require("./ai-invocations");
28
28
  const result_event_1 = require("./result-event");
29
29
  const ticket_store_1 = require("./ticket-store");
30
+ const workspace_resolution_1 = require("./workspace-resolution");
30
31
  const REFINE_TIMEOUT_MS = 60_000;
32
+ /**
33
+ * Relocate-artifacts: resolve the ticket store path honouring the gate.
34
+ * Relocated ⇒ the registry entry's tickets path (workspace); legacy ⇒
35
+ * `resolveTicketStoragePath(projectPath)` (preserves integration-contract.json
36
+ * custom-storagePath behaviour for existing repos).
37
+ */
38
+ function resolveContractTicketsPath(projectPath) {
39
+ const exec = (0, workspace_resolution_1.resolveProjectExecution)({ path: projectPath });
40
+ return exec.relocated ? exec.ticketsPath : (0, ticket_store_1.resolveTicketStoragePath)(projectPath);
41
+ }
31
42
  function normalizeClaudeCodeModel(model) {
32
43
  if (!model || typeof model !== 'string')
33
44
  return 'sonnet';
@@ -61,7 +72,13 @@ function prepareContractRefineSpawn(deps, conversation) {
61
72
  /* default false */
62
73
  }
63
74
  }
64
- let cwd = deps.projectPath;
75
+ // Relocate-artifacts: when mcp is on, the spawn cwd is the project's spec root,
76
+ // which becomes the workspace when relocated (else project.path). When mcp is
77
+ // off the explore-cwd is used as before. Env carries SPECRAILS_REPO_DIR for the
78
+ // relocated case so source/openspec I/O re-points into the repo.
79
+ const exec = (0, workspace_resolution_1.resolveProjectExecution)({ slug: deps.projectSlug, path: deps.projectPath });
80
+ let cwd = exec.cwd;
81
+ let env = exec.relocated ? { ...process.env, ...exec.env } : undefined;
65
82
  if (!mcpEnabled) {
66
83
  try {
67
84
  cwd = (0, explore_cwd_manager_1.ensureExploreCwd)({
@@ -69,13 +86,15 @@ function prepareContractRefineSpawn(deps, conversation) {
69
86
  projectPath: deps.projectPath,
70
87
  projectName: deps.projectName,
71
88
  });
89
+ // explore-cwd reaches the repo via its own `./project` link; keep the
90
+ // relocation env so SPECRAILS_REPO_DIR is still exported when relocated.
72
91
  }
73
92
  catch {
74
- cwd = deps.projectPath;
93
+ cwd = exec.cwd;
75
94
  }
76
95
  }
77
96
  const args = buildRefineArgs(conversation.model ?? 'sonnet', systemPrompt, conversation.session_id ?? '');
78
- return { args, cwd, systemPrompt };
97
+ return { args, cwd, systemPrompt, env };
79
98
  }
80
99
  /**
81
100
  * Test-friendly inner runner: takes a child-like object and returns the parsed
@@ -238,7 +257,7 @@ async function runContractRefine(deps, conversationId, ticketId) {
238
257
  ticketId,
239
258
  timestamp: now().toISOString(),
240
259
  });
241
- const { args, cwd } = prepareContractRefineSpawn({
260
+ const { args, cwd, env: refineEnv } = prepareContractRefineSpawn({
242
261
  projectSlug: deps.projectSlug,
243
262
  projectPath: deps.projectPath,
244
263
  projectName: deps.projectName,
@@ -256,7 +275,7 @@ async function runContractRefine(deps, conversationId, ticketId) {
256
275
  binary: 'claude',
257
276
  argv: args,
258
277
  cwd,
259
- env: process.env,
278
+ env: refineEnv ?? process.env,
260
279
  spawn,
261
280
  timeoutMs,
262
281
  onStdoutLine: (line) => {
@@ -344,7 +363,7 @@ async function runContractRefine(deps, conversationId, ticketId) {
344
363
  // Patch the ticket description.
345
364
  let updated = null;
346
365
  try {
347
- const filePath = (0, ticket_store_1.resolveTicketStoragePath)(deps.projectPath);
366
+ const filePath = resolveContractTicketsPath(deps.projectPath);
348
367
  updated = applyContractLayerToTicket(filePath, ticketId, parse.value, finishedAt);
349
368
  }
350
369
  catch (err) {
@@ -416,12 +435,15 @@ async function runContractRefineForQuick(deps, ticketId, generatedTitle, generat
416
435
  ticketId,
417
436
  timestamp: startedAt,
418
437
  });
438
+ // Relocate-artifacts gate: spawn from the workspace + SPECRAILS_REPO_DIR when
439
+ // relocated, else cwd = project.path + process.env (byte-identical legacy).
440
+ const quickExec = (0, workspace_resolution_1.resolveProjectExecution)({ slug: deps.projectSlug, path: deps.projectPath });
419
441
  let child;
420
442
  try {
421
443
  child = spawn('claude', args, {
422
- env: process.env,
444
+ env: quickExec.relocated ? { ...process.env, ...quickExec.env } : process.env,
423
445
  stdio: ['ignore', 'pipe', 'pipe'],
424
- cwd: deps.projectPath,
446
+ cwd: quickExec.cwd,
425
447
  });
426
448
  }
427
449
  catch (err) {
@@ -480,7 +502,7 @@ async function runContractRefineForQuick(deps, ticketId, generatedTitle, generat
480
502
  }
481
503
  let updated = null;
482
504
  try {
483
- const filePath = (0, ticket_store_1.resolveTicketStoragePath)(deps.projectPath);
505
+ const filePath = resolveContractTicketsPath(deps.projectPath);
484
506
  updated = applyContractLayerToTicket(filePath, ticketId, parse.value, finishedAt);
485
507
  }
486
508
  catch (err) {
@@ -21,6 +21,25 @@ const terminal_settings_1 = require("./terminal-settings");
21
21
  function slugify(name) {
22
22
  return name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
23
23
  }
24
+ /**
25
+ * Deterministic slug allocation with a `-N` dedup suffix — byte-parity with
26
+ * specrails-core's `allocateSlug` (and artifact-registry). Without this, two
27
+ * repos that share a basename (e.g. `frontend` in two parent dirs) both slugify
28
+ * to `frontend`, and the second `addProject` dies on the `projects.slug UNIQUE`
29
+ * constraint with a misleading 409 ("already registered") even though the PATH
30
+ * is new. The `-2`, `-3`… suffix gives the second repo a distinct slug.
31
+ */
32
+ function allocateSlug(name, existing) {
33
+ let base = slugify(name);
34
+ if (base === '')
35
+ base = 'project';
36
+ if (!existing.has(base))
37
+ return base;
38
+ let n = 2;
39
+ while (existing.has(`${base}-${n}`))
40
+ n += 1;
41
+ return `${base}-${n}`;
42
+ }
24
43
  // Emergency rollback for the codex provider: SPECRAILS_CODEX_BETA=0 forces
25
44
  // codex back to "unavailable" without redeploying. The pre-rebrand
26
45
  // SPECRAILS_HUB_CODEX_BETA name is read as a legacy fallback when the new
@@ -29,13 +48,15 @@ function isCodexBetaDisabled() {
29
48
  const v = process.env.SPECRAILS_CODEX_BETA ?? process.env.SPECRAILS_HUB_CODEX_BETA;
30
49
  return v === '0';
31
50
  }
32
- // Gemini is opt-IN (default off): unlike codex, its stream-json schema has not
33
- // yet been validated against a live binary, so it stays hidden + unselectable
34
- // until SPECRAILS_GEMINI_BETA=1 (or 'true'). The adapter is always registered
35
- // (pricing/getAdapter work); only project selection is gated.
36
- function isGeminiBetaEnabled() {
37
- const v = (process.env.SPECRAILS_GEMINI_BETA ?? '').toLowerCase();
38
- return v === '1' || v === 'true';
51
+ // Gemini is enabled by DEFAULT now that its stream-json schema and the full rails
52
+ // pipeline (architect→developer→reviewer delegation, headless agent loading, the
53
+ // MAX_TURNS resume + reviewer gate) are validated against the live binary
54
+ // (0.46/0.47). Emergency rollback: SPECRAILS_GEMINI_BETA=0 forces it back to
55
+ // "unavailable" without redeploying — parity with codex's SPECRAILS_CODEX_BETA.
56
+ // The adapter is always registered (pricing/getAdapter work); only project
57
+ // selection is gated.
58
+ function isGeminiBetaDisabled() {
59
+ return process.env.SPECRAILS_GEMINI_BETA === '0';
39
60
  }
40
61
  // Theme allow-list. Mirror of THEME_IDS in `client/src/lib/themes.ts` —
41
62
  // kept duplicated to avoid pulling client code into the server bundle.
@@ -160,10 +181,10 @@ function createDesktopRouter(registry, broadcast) {
160
181
  const gated = { ...providers };
161
182
  if (isCodexBetaDisabled())
162
183
  gated.codex = false;
163
- // Gemini is opt-in: omit it entirely (not just `false`) when the beta flag is
164
- // off, so it stays fully invisible in the UI until SPECRAILS_GEMINI_BETA=1.
165
- if (!isGeminiBetaEnabled())
166
- delete gated.gemini;
184
+ // Gemini: enabled by default; forced unavailable only when
185
+ // SPECRAILS_GEMINI_BETA=0 (emergency rollback, parity with codex).
186
+ if (isGeminiBetaDisabled())
187
+ gated.gemini = false;
167
188
  res.json({ ...gated, tiers });
168
189
  });
169
190
  router.get('/setup-prerequisites', (req, res) => {
@@ -231,9 +252,9 @@ function createDesktopRouter(registry, broadcast) {
231
252
  });
232
253
  return;
233
254
  }
234
- if (providers.includes('gemini') && !isGeminiBetaEnabled()) {
255
+ if (providers.includes('gemini') && isGeminiBetaDisabled()) {
235
256
  res.status(400).json({
236
- error: 'Gemini provider is in beta and disabled by default. Set SPECRAILS_GEMINI_BETA=1 to enable.',
257
+ error: 'Gemini provider is currently disabled (SPECRAILS_GEMINI_BETA=0). Unset or set to 1 to enable.',
237
258
  });
238
259
  return;
239
260
  }
@@ -252,7 +273,11 @@ function createDesktopRouter(registry, broadcast) {
252
273
  const derivedName = (name && typeof name === 'string' && name.trim())
253
274
  ? name.trim()
254
275
  : deriveProjectName(canonicalPath);
255
- const slug = slugify(derivedName);
276
+ // Dedup the slug against already-registered projects so two same-basename
277
+ // repos don't collide on the projects.slug UNIQUE constraint (which would
278
+ // surface as a misleading 409 for a brand-new PATH).
279
+ const existingSlugs = new Set((0, desktop_db_1.listProjects)(registry.desktopDb).map((p) => p.slug));
280
+ const slug = allocateSlug(derivedName, existingSlugs);
256
281
  const id = (0, crypto_1.randomUUID)();
257
282
  const specrailsInstalled = hasSpecrails(canonicalPath);
258
283
  try {