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
@@ -57,10 +57,17 @@ const node_readline_1 = require("node:readline");
57
57
  const cli_prompt_1 = require("./util/cli-prompt");
58
58
  const explore_smash_1 = require("./explore-smash");
59
59
  const explore_cwd_manager_1 = require("./explore-cwd-manager");
60
+ const workspace_resolution_1 = require("./workspace-resolution");
60
61
  const ai_invocations_1 = require("./ai-invocations");
61
62
  const result_event_1 = require("./result-event");
62
63
  const ticket_store_1 = require("./ticket-store");
63
64
  const ticket_store_2 = require("./ticket-store");
65
+ /** Resolve the tickets-store path for a SMASH run: the gated `ticketsPath` when
66
+ * the caller threaded it (relocation-aware), else the legacy repo-relative
67
+ * derivation. */
68
+ function smashTicketsPath(deps) {
69
+ return deps.ticketsPath ?? (0, ticket_store_2.resolveTicketStoragePath)(deps.projectPath);
70
+ }
64
71
  const SMASH_TIMEOUT_MS_SIMPLE = 60_000;
65
72
  const SMASH_TIMEOUT_MS_FULL = 900_000; // 15 min — super-spec mode, no rush
66
73
  const SMASH_MAX_TURNS_SIMPLE = 1;
@@ -117,10 +124,17 @@ function prepareSmashSpawn(deps, ticket) {
117
124
  const systemPrompt = (0, explore_smash_1.buildSmashSystemPrompt)(mode);
118
125
  const userPrompt = `${ticket.title}\n\n${ticket.description}`;
119
126
  let cwd;
127
+ let env;
120
128
  // Full mode needs access to the project tree (so Read/Grep/Glob hit the
121
129
  // real repo). Simple mode keeps the app-managed dir for a clean scope.
122
130
  if (mode === 'full') {
123
- cwd = deps.projectPath;
131
+ // Relocate-artifacts: when relocated, spawn from the workspace (so the CLI
132
+ // discovers the relocated agents/profiles + reaches the repo via the
133
+ // `./project` symlink + SPECRAILS_REPO_DIR). Legacy ⇒ cwd = project.path,
134
+ // empty env (byte-identical).
135
+ const exec = (0, workspace_resolution_1.resolveProjectExecution)({ slug: deps.projectSlug, path: deps.projectPath });
136
+ cwd = exec.cwd;
137
+ env = exec.relocated ? exec.env : undefined;
124
138
  }
125
139
  else {
126
140
  try {
@@ -135,7 +149,7 @@ function prepareSmashSpawn(deps, ticket) {
135
149
  }
136
150
  }
137
151
  const model = deps.model ?? 'sonnet';
138
- return { args: buildSmashArgs(model, systemPrompt, userPrompt, mode), cwd, systemPrompt, userPrompt, mode };
152
+ return { args: buildSmashArgs(model, systemPrompt, userPrompt, mode), cwd, env, systemPrompt, userPrompt, mode };
139
153
  }
140
154
  /**
141
155
  * Read stream-json output from a child process. Exported for tests.
@@ -444,7 +458,7 @@ async function runSmash(deps, ticketId) {
444
458
  _smashInFlight.add(inFlightKey);
445
459
  try {
446
460
  // Pre-flight: read store and check eligibility.
447
- const filePath = (0, ticket_store_2.resolveTicketStoragePath)(deps.projectPath);
461
+ const filePath = smashTicketsPath(deps);
448
462
  let ticket;
449
463
  try {
450
464
  const { readStore } = await Promise.resolve().then(() => __importStar(require('./ticket-store')));
@@ -469,16 +483,17 @@ async function runSmash(deps, ticketId) {
469
483
  ticketTitle: ticket.title,
470
484
  timestamp: startedAt,
471
485
  });
472
- const { args, cwd } = prepareSmashSpawn({
486
+ const { args, cwd, env: spawnEnv } = prepareSmashSpawn({
473
487
  projectSlug: deps.projectSlug,
474
488
  projectPath: deps.projectPath,
475
489
  projectName: deps.projectName,
476
490
  model: deps.model,
491
+ mode,
477
492
  }, ticket);
478
493
  let child;
479
494
  try {
480
495
  child = spawn('claude', args, {
481
- env: process.env,
496
+ env: spawnEnv ? { ...process.env, ...spawnEnv } : process.env,
482
497
  stdio: ['ignore', 'pipe', 'pipe'],
483
498
  cwd,
484
499
  });
@@ -625,7 +640,7 @@ async function runSmashUndo(deps, ticketId, smashedAt) {
625
640
  return { ok: false, deletedChildren: [], reason: 'disabled' };
626
641
  }
627
642
  const now = deps.now ?? (() => new Date());
628
- const filePath = (0, ticket_store_2.resolveTicketStoragePath)(deps.projectPath);
643
+ const filePath = smashTicketsPath(deps);
629
644
  const finishedAt = now().toISOString();
630
645
  let undone;
631
646
  try {
@@ -340,11 +340,15 @@ function extractTicketIdsFromCommand(command) {
340
340
  * resolve to `title: null`. Returns `[]` when the command has no ticket
341
341
  * references.
342
342
  */
343
- function resolveTicketsFromCommand(projectPath, command) {
343
+ function resolveTicketsFromCommand(projectPath, command,
344
+ /** Relocate-artifacts: when provided, read the store from this absolute path
345
+ * (the workspace ticket store) instead of `resolveTicketStoragePath(
346
+ * projectPath)`. Legacy callers omit it and behave byte-identically. */
347
+ ticketsPathOverride) {
344
348
  const ids = extractTicketIdsFromCommand(command);
345
349
  if (ids.length === 0)
346
350
  return [];
347
- const store = readStore(resolveTicketStoragePath(projectPath));
351
+ const store = readStore(ticketsPathOverride ?? resolveTicketStoragePath(projectPath));
348
352
  return ids.map((id) => ({
349
353
  id,
350
354
  title: store.tickets[String(id)]?.title ?? null,
@@ -7,6 +7,7 @@ exports.TicketWatcher = void 0;
7
7
  const path_1 = __importDefault(require("path"));
8
8
  const fs_1 = __importDefault(require("fs"));
9
9
  const chokidar_1 = require("chokidar");
10
+ const workspace_resolution_1 = require("./workspace-resolution");
10
11
  const TICKET_FILE = '.specrails/local-tickets.json';
11
12
  const DEBOUNCE_MS = 150;
12
13
  /**
@@ -33,7 +34,10 @@ class TicketWatcher {
33
34
  start() {
34
35
  if (this._closed)
35
36
  return;
36
- const filePath = path_1.default.join(this._projectPath, TICKET_FILE);
37
+ // Relocate-artifacts gate: watch the workspace ticket file when relocated,
38
+ // else the repo-relative file (byte-identical for existing projects).
39
+ const exec = (0, workspace_resolution_1.resolveProjectExecution)({ path: this._projectPath });
40
+ const filePath = exec.relocated ? exec.ticketsPath : path_1.default.join(this._projectPath, TICKET_FILE);
37
41
  // Seed initial revision so we can detect external changes
38
42
  this._lastRevision = this._readRevision(filePath);
39
43
  this._watcher = new chokidar_1.FSWatcher({
@@ -9,7 +9,8 @@ exports.extractDisplayText = extractDisplayText;
9
9
  /**
10
10
  * Map a parsed provider stream-json frame to the single display line the Job
11
11
  * Detail log shows (or null when the frame carries no user-facing text).
12
- * Handles both Claude `--output-format stream-json` and Codex `exec --json`.
12
+ * Handles Claude `--output-format stream-json`, Codex `exec --json`, and Gemini
13
+ * `--output-format stream-json` frame shapes.
13
14
  */
14
15
  function extractDisplayText(event) {
15
16
  const type = event.type;
@@ -22,10 +23,24 @@ function extractDisplayText(event) {
22
23
  return texts.join('') || null;
23
24
  }
24
25
  if (type === 'tool_use') {
25
- const name = event.name;
26
- const input = JSON.stringify(event.input ?? {});
26
+ // Claude emits `name`/`input`; Gemini stream-json uses `tool_name`/`parameters`.
27
+ // Tolerating both keeps a single tool_use branch across providers — without
28
+ // this fallback every Gemini tool call renders as `[tool: undefined] {}`.
29
+ const e = event;
30
+ const name = e.name ?? e.tool_name ?? '<unnamed>';
31
+ const input = JSON.stringify(e.input ?? e.parameters ?? {});
27
32
  return `[tool: ${name}] ${input.slice(0, 120)}`;
28
33
  }
34
+ if (type === 'message') {
35
+ // Gemini stream-json streams assistant text as `message` delta frames whose
36
+ // `content` is a plain string; the `role:"user"` echo carries no display
37
+ // value. Without this branch the Job Detail log drops all Gemini narration.
38
+ const e = event;
39
+ if (e.role !== 'assistant')
40
+ return null;
41
+ const text = e.content ?? '';
42
+ return text.length > 0 ? text : null;
43
+ }
29
44
  if (type === 'tool_result' || type === 'system_prompt' || type === 'user' || type === 'system' || type === 'result') {
30
45
  return null;
31
46
  }
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const fs_1 = require("fs");
7
+ const os_1 = __importDefault(require("os"));
8
+ const path_1 = __importDefault(require("path"));
9
+ /**
10
+ * Test safety net: NEVER let a test write the relocation registry into the
11
+ * developer's real `$HOME/.specrails/registry.json`.
12
+ *
13
+ * `ProjectRegistry.loadAll()` runs `reconcileFromProjects`, and `addProject`
14
+ * runs `mirrorProjectEntry`; both resolve the registry path from
15
+ * `resolveHome()`, which falls back to `os.homedir()` when neither a `home`
16
+ * arg nor `SPECRAILS_REGISTRY_HOME` is set. A test that constructs a
17
+ * `ProjectRegistry` without pinning a tmp home would then pollute the real
18
+ * registry. This setup file (run once per test file by vitest `setupFiles`)
19
+ * points `SPECRAILS_REGISTRY_HOME` at a throwaway tmp dir unless the test has
20
+ * already set its own, so the real home is unreachable from tests by
21
+ * construction. Tests that need a specific home still override it per-test.
22
+ */
23
+ if (!process.env.SPECRAILS_REGISTRY_HOME) {
24
+ process.env.SPECRAILS_REGISTRY_HOME = (0, fs_1.mkdtempSync)(path_1.default.join(os_1.default.tmpdir(), 'specrails-desktop-test-home-'));
25
+ }
@@ -0,0 +1,199 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.workspacePathFor = workspacePathFor;
7
+ exports.ensureWorkspace = ensureWorkspace;
8
+ exports.assembleWorkspaceFramework = assembleWorkspaceFramework;
9
+ exports.removeWorkspace = removeWorkspace;
10
+ const fs_1 = __importDefault(require("fs"));
11
+ const path_1 = __importDefault(require("path"));
12
+ const os_1 = __importDefault(require("os"));
13
+ const framework_manager_1 = require("./framework-manager");
14
+ const framework_migration_1 = require("./framework-migration");
15
+ /**
16
+ * WorkspaceManager — a reusable materializer for the per-project workspace dir
17
+ * under `~/.specrails/projects/<slug>/workspace`. This is the relocation target
18
+ * for a repo's artifacts (see `server/artifact-registry.ts` + the global-
19
+ * artifacts-alignment contract): the workspace holds the `./project` link back
20
+ * to the user's repo so tools spawned with the workspace as cwd can still reach
21
+ * the source.
22
+ *
23
+ * This generalizes the materialization logic of `explore-cwd-manager.ts`
24
+ * (the `ensureProjectLink` symlink/junction/fallback dance) without coupling to
25
+ * the Explore-specific embedded instructions file. Explore-cwd is intentionally
26
+ * left UNCHANGED (it keeps its own parallel copy); this module is the forward-
27
+ * looking home for workspace materialization.
28
+ *
29
+ * NOTE (this stage): nothing yet spawns from the workspace dir. This is the
30
+ * additive foundation — wiring spawn cwd/env to the workspace is a later stage.
31
+ */
32
+ /** Base dir holding all per-project app data. Overridable for tests. */
33
+ function projectsBaseDir(home) {
34
+ return path_1.default.join(home ?? os_1.default.homedir(), '.specrails', 'projects');
35
+ }
36
+ /**
37
+ * Compute the workspace path for a project without touching the filesystem.
38
+ * Mirrors `artifact-registry.workspaceLayout(...).workspaceDir`.
39
+ */
40
+ function workspacePathFor(slug, home) {
41
+ return path_1.default.join(projectsBaseDir(home), slug, 'workspace');
42
+ }
43
+ /**
44
+ * Create or refresh the workspace dir for a project. Idempotent and cheap when
45
+ * already up-to-date. Creates the workspace directory and a `./project`
46
+ * symlink (junction on Windows) pointing at `projectPath`, with a
47
+ * `project-path.txt` fallback when symlink/junction creation fails. Returns the
48
+ * absolute workspace path.
49
+ */
50
+ function ensureWorkspace(slug, projectPath, home) {
51
+ const ws = workspacePathFor(slug, home);
52
+ fs_1.default.mkdirSync(ws, { recursive: true });
53
+ ensureProjectLink(ws, projectPath);
54
+ return ws;
55
+ }
56
+ /**
57
+ * Ensure the workspace exists (with the `./project` link) AND assemble the
58
+ * framework into it by SYMLINK (offline) via the bundled specrails-core
59
+ * `assemble` subcommand. The providerDir static subtrees
60
+ * (`agents/`/`commands/`/`skills/`/`rules/`) become symlinks into
61
+ * `~/.specrails/framework/current/<provider>/`; `agent-memory/` stays a real
62
+ * writable dir (core's assemble handles that distinction).
63
+ *
64
+ * EXISTENCE-GATED: when no bundled core is present, `assembled` is false and the
65
+ * caller must fall back to the legacy `npx specrails-core init` assembly. The
66
+ * workspace + `./project` link are still ensured so the legacy path has a cwd.
67
+ */
68
+ function assembleWorkspaceFramework(slug, projectPath, provider, opts = {}) {
69
+ const ws = ensureWorkspace(slug, projectPath, opts.home);
70
+ const fm = opts.framework ?? new framework_manager_1.FrameworkManager({ home: opts.home });
71
+ if (!fm.isAvailable()) {
72
+ return { assembled: false, workspace: ws };
73
+ }
74
+ // Lazy-on-first-touch migration: if this workspace still holds a per-workspace
75
+ // framework COPY (the pre-bundled relocate-core layout), convert it to symlinks
76
+ // into `framework/current` non-destructively BEFORE assembling. Idempotent +
77
+ // safe to run repeatedly; it no-ops once the workspace is symlinked. The
78
+ // migration re-enters this function with `_skipMigrate` to do the actual
79
+ // re-link, so we guard against infinite recursion here.
80
+ if (!opts._skipMigrate) {
81
+ try {
82
+ (0, framework_migration_1.migrateWorkspaceToSymlinks)(slug, projectPath, provider, {
83
+ home: opts.home,
84
+ framework: fm,
85
+ broadcast: opts.broadcast,
86
+ });
87
+ }
88
+ catch {
89
+ /* migration is best-effort; assemble below still produces a usable layout */
90
+ }
91
+ }
92
+ const res = fm.assembleWorkspace({
93
+ workspace: ws,
94
+ provider,
95
+ version: opts.version,
96
+ codeRoot: projectPath,
97
+ });
98
+ if (!res.ran)
99
+ return { assembled: false, workspace: ws };
100
+ return { assembled: true, workspace: ws, error: res.error };
101
+ }
102
+ /**
103
+ * Recursively remove the workspace dir for a project. The `project`
104
+ * symlink/junction is unlinked explicitly (never followed) so the user's repo
105
+ * is never touched. No-op when the dir does not exist.
106
+ */
107
+ function removeWorkspace(slug, home) {
108
+ const ws = workspacePathFor(slug, home);
109
+ if (!fs_1.default.existsSync(ws))
110
+ return;
111
+ const linkPath = path_1.default.join(ws, 'project');
112
+ try {
113
+ const st = fs_1.default.lstatSync(linkPath);
114
+ if (st.isSymbolicLink() || (process.platform === 'win32' && st.isDirectory())) {
115
+ // unlink works on POSIX symlinks; rmdir on Windows junctions
116
+ try {
117
+ fs_1.default.unlinkSync(linkPath);
118
+ }
119
+ catch {
120
+ try {
121
+ fs_1.default.rmdirSync(linkPath);
122
+ }
123
+ catch { /* best-effort */ }
124
+ }
125
+ }
126
+ }
127
+ catch {
128
+ /* link may not exist */
129
+ }
130
+ fs_1.default.rmSync(ws, { recursive: true, force: true });
131
+ }
132
+ /**
133
+ * Ensure `<ws>/project` resolves to `<projectPath>` (symlink on POSIX, junction
134
+ * on Windows). Recreated when the existing target differs. On both symlink and
135
+ * junction failure, writes a `project-path.txt` fallback. This is the EXACT
136
+ * logic from `explore-cwd-manager.ts`'s private `ensureProjectLink`.
137
+ */
138
+ function ensureProjectLink(cwd, projectPath) {
139
+ const linkPath = path_1.default.join(cwd, 'project');
140
+ const fallbackPath = path_1.default.join(cwd, 'project-path.txt');
141
+ let needsCreate = true;
142
+ try {
143
+ const st = fs_1.default.lstatSync(linkPath);
144
+ if (st.isSymbolicLink()) {
145
+ const current = fs_1.default.readlinkSync(linkPath);
146
+ if (path_1.default.resolve(cwd, current) === path_1.default.resolve(projectPath)) {
147
+ needsCreate = false;
148
+ }
149
+ else {
150
+ fs_1.default.unlinkSync(linkPath);
151
+ }
152
+ }
153
+ else {
154
+ // existing non-symlink (e.g. Windows junction or stale dir) — replace
155
+ try {
156
+ fs_1.default.unlinkSync(linkPath);
157
+ }
158
+ catch {
159
+ try {
160
+ fs_1.default.rmdirSync(linkPath);
161
+ }
162
+ catch { /* best-effort */ }
163
+ }
164
+ }
165
+ }
166
+ catch {
167
+ /* link does not exist — fall through to create */
168
+ }
169
+ if (needsCreate) {
170
+ let created = false;
171
+ if (process.platform === 'win32') {
172
+ try {
173
+ fs_1.default.symlinkSync(projectPath, linkPath, 'junction');
174
+ created = true;
175
+ }
176
+ catch { /* fall through to plain symlink */ }
177
+ }
178
+ if (!created) {
179
+ try {
180
+ fs_1.default.symlinkSync(projectPath, linkPath);
181
+ created = true;
182
+ }
183
+ catch { /* fall through to text fallback */ }
184
+ }
185
+ if (!created) {
186
+ // Final fallback: write the absolute path so the model can use it.
187
+ fs_1.default.writeFileSync(fallbackPath, projectPath, 'utf-8');
188
+ return;
189
+ }
190
+ }
191
+ // If we successfully created/verified the symlink, clean up any stale
192
+ // fallback file from a prior failed attempt.
193
+ if (fs_1.default.existsSync(fallbackPath)) {
194
+ try {
195
+ fs_1.default.unlinkSync(fallbackPath);
196
+ }
197
+ catch { /* ignore */ }
198
+ }
199
+ }
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+ /**
3
+ * workspace-resolution — the SINGLE gate for the relocate-artifacts-to-home
4
+ * feature (DESKTOP STAGE 2). Every spawner cwd/env decision and every relocated
5
+ * artifact-read site funnels through `resolveProjectExecution(...)`.
6
+ *
7
+ * THE GATE — a project is "relocated" iff BOTH hold:
8
+ * 1. a registry entry exists for the repo (`resolveArtifacts(...).isLegacy ===
9
+ * false`), AND
10
+ * 2. the workspace is actually populated by core — detected by the presence of
11
+ * `<workspace>/.specrails/specrails-version` (core installed there).
12
+ *
13
+ * When NOT relocated (every existing in-repo project, or a relocated registry
14
+ * entry whose workspace core has not yet populated) the resolution is
15
+ * BYTE-IDENTICAL to pre-stage-2 behaviour: cwd = project.path, repo-relative
16
+ * artifact paths, empty env. This is what keeps stage 2 regression-safe for
17
+ * existing users — only relocated projects diverge.
18
+ *
19
+ * When relocated:
20
+ * - cwd = the workspace dir (core discovers sr-* agents there),
21
+ * - repoDir = project.path (the user's repo; source/git/openspec stay here),
22
+ * - env = { SPECRAILS_REPO_DIR: project.path, ... } so stage-3
23
+ * `${SPECRAILS_REPO_DIR:-.}` re-pointing drives source/openspec/
24
+ * git I/O back into the repo,
25
+ * - artifact paths point at the workspace (from the registry entry),
26
+ * - the `<workspace>/project` symlink is (re)created before returning so tools
27
+ * spawned from the workspace can still reach the repo by relative path.
28
+ *
29
+ * The workspace path is ALWAYS read from the registry entry (an adopted repo's
30
+ * registry slug may differ from the desktop.sqlite slug); never recompute it
31
+ * from a desktop slug.
32
+ */
33
+ var __importDefault = (this && this.__importDefault) || function (mod) {
34
+ return (mod && mod.__esModule) ? mod : { "default": mod };
35
+ };
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.isWorkspacePopulated = isWorkspacePopulated;
38
+ exports.resolveProjectExecution = resolveProjectExecution;
39
+ const fs_1 = __importDefault(require("fs"));
40
+ const path_1 = __importDefault(require("path"));
41
+ const artifact_registry_1 = require("./artifact-registry");
42
+ const workspace_manager_1 = require("./workspace-manager");
43
+ /** Marker file core writes into the workspace once it has installed there. The
44
+ * presence of this file is the "populated" half of the gate. */
45
+ const WORKSPACE_VERSION_MARKER = path_1.default.join('.specrails', 'specrails-version');
46
+ /** True when `<workspaceDir>/.specrails/specrails-version` exists (core has
47
+ * installed into the workspace). The legacy `.specrails-version` location is
48
+ * NOT a workspace marker (a bare workspace dir never has the dot-file form).
49
+ *
50
+ * Bundled-framework layout: `assemble` SYMLINKS the providerDir subtrees
51
+ * (`agents/` per-file, `commands/`/`skills/`/`rules/` whole-dir) into the
52
+ * workspace but writes the `.specrails/specrails-version` marker as a REAL file
53
+ * (via core's `writeManifestFiles`). So "populated" depends only on the real
54
+ * marker — it is agnostic to whether the providerDir is a real dir or a tree of
55
+ * symlinks into `framework/current/<provider>/`. `fs.existsSync` resolves the
56
+ * marker (and would also resolve it through a symlinked `.specrails`, though the
57
+ * manifest layer is never linked). */
58
+ function isWorkspacePopulated(workspaceDir) {
59
+ try {
60
+ return fs_1.default.existsSync(path_1.default.join(workspaceDir, WORKSPACE_VERSION_MARKER));
61
+ }
62
+ catch {
63
+ return false;
64
+ }
65
+ }
66
+ /**
67
+ * THE GATE. Resolve how a project's AI CLIs should be spawned and where its
68
+ * relocated artifacts live. Read-only with respect to the registry; the only
69
+ * side effect (when relocated) is (re)creating the workspace `./project` symlink
70
+ * via `ensureWorkspace`.
71
+ */
72
+ function resolveProjectExecution(project, home) {
73
+ const repoDir = project.path;
74
+ let art;
75
+ try {
76
+ art = (0, artifact_registry_1.resolveArtifacts)(project.path, { allocate: false }, home);
77
+ }
78
+ catch {
79
+ // A registry read failure must never break a spawn — fall back to legacy.
80
+ return legacyExecution(repoDir);
81
+ }
82
+ // Gate part 1: no registry entry ⇒ legacy.
83
+ if (art.isLegacy) {
84
+ return legacyExecution(repoDir);
85
+ }
86
+ // Gate part 2: registry entry exists but the workspace is NOT yet populated by
87
+ // core ⇒ stay legacy (existing project that has a registry entry but whose
88
+ // core install still lives in-repo). This is what keeps every existing
89
+ // assertion valid: a registry entry alone never flips a project to relocated.
90
+ if (!isWorkspacePopulated(art.workspaceDir)) {
91
+ return legacyExecution(repoDir);
92
+ }
93
+ // Relocated. (Re)create the workspace `./project` symlink so tools spawned from
94
+ // the workspace can still reach the repo. ALWAYS use the REGISTRY entry's slug
95
+ // (an adopted repo's registry slug may differ from the desktop slug) so the
96
+ // symlink lands in the same workspace dir core populated. Best-effort — a
97
+ // symlink failure must not abort the spawn (ensureWorkspace falls back to
98
+ // project-path.txt internally).
99
+ const workspaceSlug = art.entry?.slug ?? project.slug;
100
+ try {
101
+ if (workspaceSlug)
102
+ (0, workspace_manager_1.ensureWorkspace)(workspaceSlug, repoDir, home);
103
+ }
104
+ catch {
105
+ /* non-fatal — ensureWorkspace already has its own fallbacks */
106
+ }
107
+ const env = {
108
+ SPECRAILS_REPO_DIR: repoDir,
109
+ SPECRAILS_WORKSPACE_DIR: art.workspaceDir,
110
+ SPECRAILS_TICKETS_PATH: art.ticketsPath,
111
+ SPECRAILS_STATE_DIR: art.stateDir,
112
+ SPECRAILS_BACKLOG_CONFIG_PATH: art.backlogConfigPath,
113
+ SPECRAILS_PROFILES_DIR: art.profilesDir,
114
+ };
115
+ return {
116
+ relocated: true,
117
+ cwd: art.workspaceDir,
118
+ repoDir,
119
+ workspaceDir: art.workspaceDir,
120
+ ticketsPath: art.ticketsPath,
121
+ backlogConfigPath: art.backlogConfigPath,
122
+ profilesDir: art.profilesDir,
123
+ pluginsStateDir: art.pluginsStateDir,
124
+ fileSummariesDir: art.fileSummariesDir,
125
+ specrailsDir: art.specrailsDir,
126
+ stateDir: art.stateDir,
127
+ env,
128
+ };
129
+ }
130
+ /** Build the legacy (in-repo, byte-identical-to-today) execution. */
131
+ function legacyExecution(repoDir) {
132
+ const specrailsDir = path_1.default.join(repoDir, '.specrails');
133
+ return {
134
+ relocated: false,
135
+ cwd: repoDir,
136
+ repoDir,
137
+ workspaceDir: null,
138
+ ticketsPath: path_1.default.join(specrailsDir, 'local-tickets.json'),
139
+ backlogConfigPath: path_1.default.join(specrailsDir, 'backlog-config.json'),
140
+ profilesDir: path_1.default.join(specrailsDir, 'profiles'),
141
+ pluginsStateDir: path_1.default.join(specrailsDir, 'plugins'),
142
+ fileSummariesDir: path_1.default.join(specrailsDir, 'file-summaries'),
143
+ specrailsDir,
144
+ stateDir: path_1.default.join(repoDir, '.claude'),
145
+ env: {},
146
+ };
147
+ }