specrails-desktop 2.8.0 → 2.9.1

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 (334) hide show
  1. package/README.md +23 -19
  2. package/client/dist/assets/{ActivityFeedPage-LKqd18-G.js → ActivityFeedPage-DpQzYMBz.js} +1 -1
  3. package/client/dist/assets/{AgentsPage-Cb-b-6Ot.js → AgentsPage-29fCY8qV.js} +1 -1
  4. package/client/dist/assets/{AnalyticsPage-HVxQQ1wy.js → AnalyticsPage-BwGtS6Hf.js} +1 -1
  5. package/client/dist/assets/{BarChart-BOyHB0dw.js → BarChart-CTR97DVC.js} +1 -1
  6. package/client/dist/assets/{CodePage-DnOnwKGB.js → CodePage-yAAxKasA.js} +1 -1
  7. package/client/dist/assets/{DesktopAnalyticsPage-D2auU39x.js → DesktopAnalyticsPage-BdK_XpsD.js} +1 -1
  8. package/client/dist/assets/{DocsDialog-CTuDX3GK.js → DocsDialog-BaE0cLlL.js} +2 -2
  9. package/client/dist/assets/{DocsPage-DRyMmu0Z.js → DocsPage-c1FgZX8_.js} +2 -2
  10. package/client/dist/assets/{ExportDropdown-DO-GGiMh.js → ExportDropdown-lPv_yDen.js} +1 -1
  11. package/client/dist/assets/{IntegrationsPage-BhbO4jFT.js → IntegrationsPage-DOpxRe7G.js} +1 -1
  12. package/client/dist/assets/{JobDetailPage-DJooEg1s.js → JobDetailPage-5ExzXY-F.js} +1 -1
  13. package/client/dist/assets/{JobsPage-BbaC-YOg.js → JobsPage-iW7WuPAc.js} +1 -1
  14. package/client/dist/assets/{dist-js-Xc2lRKp2.js → dist-js-A8aSaLng.js} +1 -1
  15. package/client/dist/assets/{dist-js-CiIVMsx3.js → dist-js-CD_m3Xj5.js} +1 -1
  16. package/client/dist/assets/index-D6BaYRRU.css +2 -0
  17. package/client/dist/assets/{index-DK214dak.js → index-DRhFPNAv.js} +44 -44
  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-1vkTuLY7.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/{tickets-9kdPXInd.js → tickets-CG_mo-Bg.js} +1 -1
  36. package/client/dist/assets/{tickets-n23kDqJT.js → tickets-CVJQ-vRm.js} +1 -1
  37. package/client/dist/assets/{tickets-tGx5AR5b.js → tickets-D5MSAPe_.js} +1 -1
  38. package/client/dist/assets/{tickets-1UIGf_oA.js → tickets-DBV3wgQZ.js} +1 -1
  39. package/client/dist/assets/{tickets-DNmXcAwu.js → tickets-Q0_pONEh.js} +1 -1
  40. package/client/dist/assets/{tickets-C6pwZwt4.js → tickets-RZ0LyeQe.js} +1 -1
  41. package/client/dist/assets/{tickets-DAjtxAVb.js → tickets-d1A6EOHa.js} +1 -1
  42. package/client/dist/assets/{tickets-0rM0lIXd.js → tickets-r4-oNV0R.js} +1 -1
  43. package/client/dist/assets/{useProjectCache-DVNypkmR.js → useProjectCache-CSi2xHri.js} +1 -1
  44. package/client/dist/index.html +5 -5
  45. package/docs/README.md +5 -2
  46. package/docs/agy-cli-provider-study.md +78 -0
  47. package/docs/cli.md +23 -4
  48. package/docs/codex.md +116 -58
  49. package/docs/creating-specs.md +19 -5
  50. package/docs/customizing.md +27 -6
  51. package/docs/gemini.md +225 -73
  52. package/docs/getting-started.md +18 -9
  53. package/docs/guide/de/agents/1-meet-the-agents.md +38 -0
  54. package/docs/guide/de/agents/2-profiles-and-the-balanced-default.md +45 -0
  55. package/docs/guide/de/agents/3-customizing-models-per-agent.md +60 -0
  56. package/docs/guide/de/agents/4-custom-agents-catalog.md +43 -0
  57. package/docs/guide/de/getting-started/1-what-is-specrails.md +49 -0
  58. package/docs/guide/de/getting-started/2-installing-and-first-run.md +42 -0
  59. package/docs/guide/de/getting-started/3-adding-your-first-project.md +58 -0
  60. package/docs/guide/de/getting-started/4-the-dashboard-tour.md +53 -0
  61. package/docs/guide/de/insights/1-analytics-and-cost-tracking.md +78 -0
  62. package/docs/guide/de/insights/2-the-integrated-terminal.md +46 -0
  63. package/docs/guide/de/insights/3-code-explorer.md +50 -0
  64. package/docs/guide/de/integrations/1-ai-providers.md +64 -0
  65. package/docs/guide/de/integrations/2-plugins.md +44 -0
  66. package/docs/guide/de/integrations/3-jira-integration.md +71 -0
  67. package/docs/guide/de/integrations/4-mobile-companion.md +38 -0
  68. package/docs/guide/de/pipeline/1-rails-and-jobs.md +94 -0
  69. package/docs/guide/de/pipeline/2-the-job-detail-view.md +90 -0
  70. package/docs/guide/de/pipeline/3-batch-implement-and-multi-feature.md +78 -0
  71. package/docs/guide/de/pipeline/4-picking-an-engine-per-rail.md +60 -0
  72. package/docs/guide/de/settings/1-themes.md +37 -0
  73. package/docs/guide/de/settings/2-language.md +39 -0
  74. package/docs/guide/de/settings/3-pipeline-telemetry-and-diagnostics.md +46 -0
  75. package/docs/guide/de/settings/4-where-your-data-lives.md +48 -0
  76. package/docs/guide/de/specs/1-specs-and-the-backlog.md +52 -0
  77. package/docs/guide/de/specs/2-add-spec-quick-mode.md +45 -0
  78. package/docs/guide/de/specs/3-add-spec-explore-mode.md +68 -0
  79. package/docs/guide/de/specs/4-drafts-and-contract-layer.md +81 -0
  80. package/docs/guide/en/agents/1-meet-the-agents.md +38 -0
  81. package/docs/guide/en/agents/2-profiles-and-the-balanced-default.md +45 -0
  82. package/docs/guide/en/agents/3-customizing-models-per-agent.md +60 -0
  83. package/docs/guide/en/agents/4-custom-agents-catalog.md +43 -0
  84. package/docs/guide/en/getting-started/1-what-is-specrails.md +49 -0
  85. package/docs/guide/en/getting-started/2-installing-and-first-run.md +42 -0
  86. package/docs/guide/en/getting-started/3-adding-your-first-project.md +58 -0
  87. package/docs/guide/en/getting-started/4-the-dashboard-tour.md +53 -0
  88. package/docs/guide/en/insights/1-analytics-and-cost-tracking.md +78 -0
  89. package/docs/guide/en/insights/2-the-integrated-terminal.md +46 -0
  90. package/docs/guide/en/insights/3-code-explorer.md +50 -0
  91. package/docs/guide/en/integrations/1-ai-providers.md +64 -0
  92. package/docs/guide/en/integrations/2-plugins.md +44 -0
  93. package/docs/guide/en/integrations/3-jira-integration.md +71 -0
  94. package/docs/guide/en/integrations/4-mobile-companion.md +38 -0
  95. package/docs/guide/en/pipeline/1-rails-and-jobs.md +94 -0
  96. package/docs/guide/en/pipeline/2-the-job-detail-view.md +90 -0
  97. package/docs/guide/en/pipeline/3-batch-implement-and-multi-feature.md +78 -0
  98. package/docs/guide/en/pipeline/4-picking-an-engine-per-rail.md +60 -0
  99. package/docs/guide/en/settings/1-themes.md +37 -0
  100. package/docs/guide/en/settings/2-language.md +39 -0
  101. package/docs/guide/en/settings/3-pipeline-telemetry-and-diagnostics.md +46 -0
  102. package/docs/guide/en/settings/4-where-your-data-lives.md +48 -0
  103. package/docs/guide/en/specs/1-specs-and-the-backlog.md +52 -0
  104. package/docs/guide/en/specs/2-add-spec-quick-mode.md +45 -0
  105. package/docs/guide/en/specs/3-add-spec-explore-mode.md +68 -0
  106. package/docs/guide/en/specs/4-drafts-and-contract-layer.md +81 -0
  107. package/docs/guide/es/agents/1-meet-the-agents.md +38 -0
  108. package/docs/guide/es/agents/2-profiles-and-the-balanced-default.md +45 -0
  109. package/docs/guide/es/agents/3-customizing-models-per-agent.md +60 -0
  110. package/docs/guide/es/agents/4-custom-agents-catalog.md +43 -0
  111. package/docs/guide/es/getting-started/1-what-is-specrails.md +49 -0
  112. package/docs/guide/es/getting-started/2-installing-and-first-run.md +42 -0
  113. package/docs/guide/es/getting-started/3-adding-your-first-project.md +58 -0
  114. package/docs/guide/es/getting-started/4-the-dashboard-tour.md +53 -0
  115. package/docs/guide/es/insights/1-analytics-and-cost-tracking.md +78 -0
  116. package/docs/guide/es/insights/2-the-integrated-terminal.md +46 -0
  117. package/docs/guide/es/insights/3-code-explorer.md +50 -0
  118. package/docs/guide/es/integrations/1-ai-providers.md +64 -0
  119. package/docs/guide/es/integrations/2-plugins.md +44 -0
  120. package/docs/guide/es/integrations/3-jira-integration.md +71 -0
  121. package/docs/guide/es/integrations/4-mobile-companion.md +38 -0
  122. package/docs/guide/es/pipeline/1-rails-and-jobs.md +94 -0
  123. package/docs/guide/es/pipeline/2-the-job-detail-view.md +90 -0
  124. package/docs/guide/es/pipeline/3-batch-implement-and-multi-feature.md +78 -0
  125. package/docs/guide/es/pipeline/4-picking-an-engine-per-rail.md +60 -0
  126. package/docs/guide/es/settings/1-themes.md +37 -0
  127. package/docs/guide/es/settings/2-language.md +39 -0
  128. package/docs/guide/es/settings/3-pipeline-telemetry-and-diagnostics.md +46 -0
  129. package/docs/guide/es/settings/4-where-your-data-lives.md +48 -0
  130. package/docs/guide/es/specs/1-specs-and-the-backlog.md +52 -0
  131. package/docs/guide/es/specs/2-add-spec-quick-mode.md +45 -0
  132. package/docs/guide/es/specs/3-add-spec-explore-mode.md +68 -0
  133. package/docs/guide/es/specs/4-drafts-and-contract-layer.md +81 -0
  134. package/docs/guide/fr/agents/1-meet-the-agents.md +38 -0
  135. package/docs/guide/fr/agents/2-profiles-and-the-balanced-default.md +45 -0
  136. package/docs/guide/fr/agents/3-customizing-models-per-agent.md +60 -0
  137. package/docs/guide/fr/agents/4-custom-agents-catalog.md +43 -0
  138. package/docs/guide/fr/getting-started/1-what-is-specrails.md +49 -0
  139. package/docs/guide/fr/getting-started/2-installing-and-first-run.md +42 -0
  140. package/docs/guide/fr/getting-started/3-adding-your-first-project.md +58 -0
  141. package/docs/guide/fr/getting-started/4-the-dashboard-tour.md +53 -0
  142. package/docs/guide/fr/insights/1-analytics-and-cost-tracking.md +78 -0
  143. package/docs/guide/fr/insights/2-the-integrated-terminal.md +46 -0
  144. package/docs/guide/fr/insights/3-code-explorer.md +50 -0
  145. package/docs/guide/fr/integrations/1-ai-providers.md +64 -0
  146. package/docs/guide/fr/integrations/2-plugins.md +44 -0
  147. package/docs/guide/fr/integrations/3-jira-integration.md +71 -0
  148. package/docs/guide/fr/integrations/4-mobile-companion.md +38 -0
  149. package/docs/guide/fr/pipeline/1-rails-and-jobs.md +94 -0
  150. package/docs/guide/fr/pipeline/2-the-job-detail-view.md +90 -0
  151. package/docs/guide/fr/pipeline/3-batch-implement-and-multi-feature.md +78 -0
  152. package/docs/guide/fr/pipeline/4-picking-an-engine-per-rail.md +60 -0
  153. package/docs/guide/fr/settings/1-themes.md +37 -0
  154. package/docs/guide/fr/settings/2-language.md +39 -0
  155. package/docs/guide/fr/settings/3-pipeline-telemetry-and-diagnostics.md +46 -0
  156. package/docs/guide/fr/settings/4-where-your-data-lives.md +48 -0
  157. package/docs/guide/fr/specs/1-specs-and-the-backlog.md +52 -0
  158. package/docs/guide/fr/specs/2-add-spec-quick-mode.md +45 -0
  159. package/docs/guide/fr/specs/3-add-spec-explore-mode.md +68 -0
  160. package/docs/guide/fr/specs/4-drafts-and-contract-layer.md +81 -0
  161. package/docs/guide/it/agents/1-meet-the-agents.md +38 -0
  162. package/docs/guide/it/agents/2-profiles-and-the-balanced-default.md +45 -0
  163. package/docs/guide/it/agents/3-customizing-models-per-agent.md +60 -0
  164. package/docs/guide/it/agents/4-custom-agents-catalog.md +43 -0
  165. package/docs/guide/it/getting-started/1-what-is-specrails.md +49 -0
  166. package/docs/guide/it/getting-started/2-installing-and-first-run.md +42 -0
  167. package/docs/guide/it/getting-started/3-adding-your-first-project.md +58 -0
  168. package/docs/guide/it/getting-started/4-the-dashboard-tour.md +53 -0
  169. package/docs/guide/it/insights/1-analytics-and-cost-tracking.md +78 -0
  170. package/docs/guide/it/insights/2-the-integrated-terminal.md +46 -0
  171. package/docs/guide/it/insights/3-code-explorer.md +50 -0
  172. package/docs/guide/it/integrations/1-ai-providers.md +64 -0
  173. package/docs/guide/it/integrations/2-plugins.md +44 -0
  174. package/docs/guide/it/integrations/3-jira-integration.md +71 -0
  175. package/docs/guide/it/integrations/4-mobile-companion.md +38 -0
  176. package/docs/guide/it/pipeline/1-rails-and-jobs.md +94 -0
  177. package/docs/guide/it/pipeline/2-the-job-detail-view.md +90 -0
  178. package/docs/guide/it/pipeline/3-batch-implement-and-multi-feature.md +78 -0
  179. package/docs/guide/it/pipeline/4-picking-an-engine-per-rail.md +60 -0
  180. package/docs/guide/it/settings/1-themes.md +37 -0
  181. package/docs/guide/it/settings/2-language.md +39 -0
  182. package/docs/guide/it/settings/3-pipeline-telemetry-and-diagnostics.md +46 -0
  183. package/docs/guide/it/settings/4-where-your-data-lives.md +48 -0
  184. package/docs/guide/it/specs/1-specs-and-the-backlog.md +52 -0
  185. package/docs/guide/it/specs/2-add-spec-quick-mode.md +45 -0
  186. package/docs/guide/it/specs/3-add-spec-explore-mode.md +68 -0
  187. package/docs/guide/it/specs/4-drafts-and-contract-layer.md +81 -0
  188. package/docs/guide/ja/agents/1-meet-the-agents.md +38 -0
  189. package/docs/guide/ja/agents/2-profiles-and-the-balanced-default.md +45 -0
  190. package/docs/guide/ja/agents/3-customizing-models-per-agent.md +60 -0
  191. package/docs/guide/ja/agents/4-custom-agents-catalog.md +43 -0
  192. package/docs/guide/ja/getting-started/1-what-is-specrails.md +49 -0
  193. package/docs/guide/ja/getting-started/2-installing-and-first-run.md +42 -0
  194. package/docs/guide/ja/getting-started/3-adding-your-first-project.md +58 -0
  195. package/docs/guide/ja/getting-started/4-the-dashboard-tour.md +53 -0
  196. package/docs/guide/ja/insights/1-analytics-and-cost-tracking.md +78 -0
  197. package/docs/guide/ja/insights/2-the-integrated-terminal.md +46 -0
  198. package/docs/guide/ja/insights/3-code-explorer.md +50 -0
  199. package/docs/guide/ja/integrations/1-ai-providers.md +64 -0
  200. package/docs/guide/ja/integrations/2-plugins.md +44 -0
  201. package/docs/guide/ja/integrations/3-jira-integration.md +71 -0
  202. package/docs/guide/ja/integrations/4-mobile-companion.md +38 -0
  203. package/docs/guide/ja/pipeline/1-rails-and-jobs.md +94 -0
  204. package/docs/guide/ja/pipeline/2-the-job-detail-view.md +90 -0
  205. package/docs/guide/ja/pipeline/3-batch-implement-and-multi-feature.md +78 -0
  206. package/docs/guide/ja/pipeline/4-picking-an-engine-per-rail.md +60 -0
  207. package/docs/guide/ja/settings/1-themes.md +37 -0
  208. package/docs/guide/ja/settings/2-language.md +39 -0
  209. package/docs/guide/ja/settings/3-pipeline-telemetry-and-diagnostics.md +46 -0
  210. package/docs/guide/ja/settings/4-where-your-data-lives.md +48 -0
  211. package/docs/guide/ja/specs/1-specs-and-the-backlog.md +52 -0
  212. package/docs/guide/ja/specs/2-add-spec-quick-mode.md +45 -0
  213. package/docs/guide/ja/specs/3-add-spec-explore-mode.md +68 -0
  214. package/docs/guide/ja/specs/4-drafts-and-contract-layer.md +81 -0
  215. package/docs/guide/pt/agents/1-meet-the-agents.md +38 -0
  216. package/docs/guide/pt/agents/2-profiles-and-the-balanced-default.md +45 -0
  217. package/docs/guide/pt/agents/3-customizing-models-per-agent.md +60 -0
  218. package/docs/guide/pt/agents/4-custom-agents-catalog.md +43 -0
  219. package/docs/guide/pt/getting-started/1-what-is-specrails.md +49 -0
  220. package/docs/guide/pt/getting-started/2-installing-and-first-run.md +42 -0
  221. package/docs/guide/pt/getting-started/3-adding-your-first-project.md +58 -0
  222. package/docs/guide/pt/getting-started/4-the-dashboard-tour.md +53 -0
  223. package/docs/guide/pt/insights/1-analytics-and-cost-tracking.md +78 -0
  224. package/docs/guide/pt/insights/2-the-integrated-terminal.md +46 -0
  225. package/docs/guide/pt/insights/3-code-explorer.md +50 -0
  226. package/docs/guide/pt/integrations/1-ai-providers.md +64 -0
  227. package/docs/guide/pt/integrations/2-plugins.md +44 -0
  228. package/docs/guide/pt/integrations/3-jira-integration.md +71 -0
  229. package/docs/guide/pt/integrations/4-mobile-companion.md +38 -0
  230. package/docs/guide/pt/pipeline/1-rails-and-jobs.md +94 -0
  231. package/docs/guide/pt/pipeline/2-the-job-detail-view.md +90 -0
  232. package/docs/guide/pt/pipeline/3-batch-implement-and-multi-feature.md +78 -0
  233. package/docs/guide/pt/pipeline/4-picking-an-engine-per-rail.md +60 -0
  234. package/docs/guide/pt/settings/1-themes.md +37 -0
  235. package/docs/guide/pt/settings/2-language.md +39 -0
  236. package/docs/guide/pt/settings/3-pipeline-telemetry-and-diagnostics.md +46 -0
  237. package/docs/guide/pt/settings/4-where-your-data-lives.md +48 -0
  238. package/docs/guide/pt/specs/1-specs-and-the-backlog.md +52 -0
  239. package/docs/guide/pt/specs/2-add-spec-quick-mode.md +45 -0
  240. package/docs/guide/pt/specs/3-add-spec-explore-mode.md +68 -0
  241. package/docs/guide/pt/specs/4-drafts-and-contract-layer.md +81 -0
  242. package/docs/guide/zh/agents/1-meet-the-agents.md +38 -0
  243. package/docs/guide/zh/agents/2-profiles-and-the-balanced-default.md +45 -0
  244. package/docs/guide/zh/agents/3-customizing-models-per-agent.md +60 -0
  245. package/docs/guide/zh/agents/4-custom-agents-catalog.md +43 -0
  246. package/docs/guide/zh/getting-started/1-what-is-specrails.md +49 -0
  247. package/docs/guide/zh/getting-started/2-installing-and-first-run.md +42 -0
  248. package/docs/guide/zh/getting-started/3-adding-your-first-project.md +58 -0
  249. package/docs/guide/zh/getting-started/4-the-dashboard-tour.md +53 -0
  250. package/docs/guide/zh/insights/1-analytics-and-cost-tracking.md +78 -0
  251. package/docs/guide/zh/insights/2-the-integrated-terminal.md +46 -0
  252. package/docs/guide/zh/insights/3-code-explorer.md +50 -0
  253. package/docs/guide/zh/integrations/1-ai-providers.md +64 -0
  254. package/docs/guide/zh/integrations/2-plugins.md +44 -0
  255. package/docs/guide/zh/integrations/3-jira-integration.md +71 -0
  256. package/docs/guide/zh/integrations/4-mobile-companion.md +38 -0
  257. package/docs/guide/zh/pipeline/1-rails-and-jobs.md +94 -0
  258. package/docs/guide/zh/pipeline/2-the-job-detail-view.md +90 -0
  259. package/docs/guide/zh/pipeline/3-batch-implement-and-multi-feature.md +78 -0
  260. package/docs/guide/zh/pipeline/4-picking-an-engine-per-rail.md +60 -0
  261. package/docs/guide/zh/settings/1-themes.md +37 -0
  262. package/docs/guide/zh/settings/2-language.md +39 -0
  263. package/docs/guide/zh/settings/3-pipeline-telemetry-and-diagnostics.md +46 -0
  264. package/docs/guide/zh/settings/4-where-your-data-lives.md +48 -0
  265. package/docs/guide/zh/specs/1-specs-and-the-backlog.md +52 -0
  266. package/docs/guide/zh/specs/2-add-spec-quick-mode.md +45 -0
  267. package/docs/guide/zh/specs/3-add-spec-explore-mode.md +68 -0
  268. package/docs/guide/zh/specs/4-drafts-and-contract-layer.md +81 -0
  269. package/docs/internals/README.md +1 -1
  270. package/docs/internals/adding-a-provider.md +192 -59
  271. package/docs/internals/api-reference.md +130 -21
  272. package/docs/internals/architecture.md +22 -9
  273. package/docs/internals/bundled-framework-build-plan.md +264 -0
  274. package/docs/internals/configuration.md +33 -8
  275. package/docs/internals/global-artifacts-alignment-contract.md +486 -0
  276. package/docs/internals/global-artifacts-relocation-evaluation.md +294 -0
  277. package/docs/internals/operations-runbook.md +16 -5
  278. package/docs/internals/profiles.md +42 -14
  279. package/docs/platforms/macos.md +27 -8
  280. package/docs/platforms/windows.md +20 -6
  281. package/docs/running-pipelines.md +17 -9
  282. package/docs/terminal.md +9 -3
  283. package/docs/tracking-cost.md +17 -11
  284. package/package.json +1 -1
  285. package/server/dist/agent-refine-manager.js +20 -5
  286. package/server/dist/artifact-registry.js +468 -0
  287. package/server/dist/attachment-manager.js +5 -8
  288. package/server/dist/browser-capture-manager.js +4 -4
  289. package/server/dist/bundled-core.js +72 -0
  290. package/server/dist/bundled-openspec.js +58 -0
  291. package/server/dist/chat-manager.js +42 -5
  292. package/server/dist/code-explorer-router.js +10 -7
  293. package/server/dist/config.js +7 -2
  294. package/server/dist/context-budget.js +17 -6
  295. package/server/dist/context-scope.js +6 -2
  296. package/server/dist/contract-refine-runner.js +31 -9
  297. package/server/dist/desktop-router.js +24 -1
  298. package/server/dist/docs-router.js +210 -132
  299. package/server/dist/file-summary-manager.js +41 -16
  300. package/server/dist/framework-manager.js +248 -0
  301. package/server/dist/framework-migration.js +308 -0
  302. package/server/dist/index.js +30 -0
  303. package/server/dist/install-config-path.js +73 -0
  304. package/server/dist/jira/jira-sync-manager.js +23 -11
  305. package/server/dist/openspec-shim.js +153 -0
  306. package/server/dist/plugins-router.js +19 -8
  307. package/server/dist/profiles-router.js +38 -16
  308. package/server/dist/project-registry.js +101 -3
  309. package/server/dist/project-router-chat.js +1 -1
  310. package/server/dist/project-router-helpers.js +25 -12
  311. package/server/dist/project-router-jobs.js +3 -3
  312. package/server/dist/project-router-settings.js +8 -6
  313. package/server/dist/project-router-setup.js +27 -10
  314. package/server/dist/project-router-spending.js +6 -1
  315. package/server/dist/project-router-tickets.js +30 -10
  316. package/server/dist/project-router.js +16 -1
  317. package/server/dist/queue-manager.js +149 -18
  318. package/server/dist/setup-manager.js +131 -29
  319. package/server/dist/smash-runner.js +21 -6
  320. package/server/dist/ticket-store.js +6 -2
  321. package/server/dist/ticket-watcher.js +5 -1
  322. package/server/dist/vitest-setup.js +25 -0
  323. package/server/dist/workspace-manager.js +199 -0
  324. package/server/dist/workspace-resolution.js +147 -0
  325. package/client/dist/assets/index-DgKfQFcf.css +0 -2
  326. package/client/dist/assets/setup-BIIkb-_K.js +0 -1
  327. package/client/dist/assets/setup-BeQxu9kD.js +0 -1
  328. package/client/dist/assets/setup-CPa6GnlI.js +0 -1
  329. package/client/dist/assets/setup-CZl4OEJx.js +0 -1
  330. package/client/dist/assets/setup-ChpodNfn.js +0 -1
  331. package/client/dist/assets/setup-D_fjJH6u.js +0 -1
  332. package/client/dist/assets/setup-YzD8DX4O.js +0 -1
  333. package/client/dist/assets/setup-fRpDozmq.js +0 -1
  334. package/docs/adding-a-provider.md +0 -107
@@ -28,6 +28,10 @@ const interactive_job_session_1 = require("./interactive-job-session");
28
28
  const attachment_manager_1 = require("./attachment-manager");
29
29
  const ticket_store_1 = require("./ticket-store");
30
30
  const binary_probe_1 = require("./binary-probe");
31
+ const workspace_resolution_1 = require("./workspace-resolution");
32
+ const framework_manager_1 = require("./framework-manager");
33
+ const openspec_shim_1 = require("./openspec-shim");
34
+ const artifact_registry_1 = require("./artifact-registry");
31
35
  // ─── Telemetry env helpers ────────────────────────────────────────────────────
32
36
  /** Build the OTEL environment variable block for a spawned claude process.
33
37
  * Extracted as a pure function so it is unit-testable without a full spawn. */
@@ -156,6 +160,13 @@ class QueueManager {
156
160
  /** Pre-spawn working-tree snapshot refs keyed by jobId — read at exit time
157
161
  * by the Code-Explorer provenance hook. Cleared on job exit. */
158
162
  _snapshotRefs;
163
+ /** Per-job resolved execution context (relocate-artifacts gate), captured at
164
+ * spawn time so `_onJobExit`'s provenance hook uses the SAME repoDir the
165
+ * snapshot used (= project.path, never the workspace). Cleared on job exit. */
166
+ _jobExecution;
167
+ /** Per-job openspec PATH shim dir (relocated claude rails only). Cleaned up on
168
+ * job exit. In-memory map of jobId → shim dir. */
169
+ _openspecShims = new Map();
159
170
  /** Pending per-job interactive flag keyed by jobId — read at spawn time.
160
171
  * In-memory only (mirrors _jobModelSelection). */
161
172
  _jobInteractiveSelection;
@@ -190,6 +201,7 @@ class QueueManager {
190
201
  this._jobProviderSelection = new Map();
191
202
  this._jobModelSelection = new Map();
192
203
  this._snapshotRefs = new Map();
204
+ this._jobExecution = new Map();
193
205
  this._jobInteractiveSelection = new Map();
194
206
  this._interactiveSessions = new Map();
195
207
  const envTimeout = process.env.WM_ZOMBIE_TIMEOUT_MS !== undefined
@@ -261,6 +273,8 @@ class QueueManager {
261
273
  this._interactiveSessions.clear();
262
274
  // Release any per-job provenance snapshots so teardown leaves no map entries.
263
275
  this._snapshotRefs.clear();
276
+ this._jobExecution.clear();
277
+ this._openspecShims.clear();
264
278
  // Drop the DB reference last so any in-flight 'close' callback sees null
265
279
  // and skips all DB work via the existing `if (this._db)` guards.
266
280
  this._db = null;
@@ -522,7 +536,7 @@ class QueueManager {
522
536
  if (ticketIds.length === 0)
523
537
  return '';
524
538
  try {
525
- const store = (0, ticket_store_1.readStore)((0, ticket_store_1.resolveTicketStoragePath)(this._cwd));
539
+ const store = (0, ticket_store_1.readStore)(this._resolveTicketsPath());
526
540
  const sections = [];
527
541
  for (const ticketId of ticketIds) {
528
542
  const storeAttachmentIds = new Set((store.tickets[String(ticketId)]?.attachments ?? []).map((attachment) => attachment.id));
@@ -563,7 +577,7 @@ class QueueManager {
563
577
  const specs = [];
564
578
  if (this._cwd) {
565
579
  try {
566
- const store = (0, ticket_store_1.readStore)((0, ticket_store_1.resolveTicketStoragePath)(this._cwd));
580
+ const store = (0, ticket_store_1.readStore)(this._resolveTicketsPath());
567
581
  for (const ticketId of ticketIds) {
568
582
  const ticket = store.tickets[String(ticketId)];
569
583
  if (!ticket)
@@ -634,6 +648,46 @@ class QueueManager {
634
648
  }
635
649
  return this._adapter;
636
650
  }
651
+ /**
652
+ * Resolve the relocate-artifacts execution context for this manager's project.
653
+ * The gate: relocated only when a registry entry exists AND core has populated
654
+ * the workspace; otherwise legacy (cwd = project.path, empty env) — preserving
655
+ * byte-identical behaviour for every existing in-repo project. Falls back to a
656
+ * legacy resolution rooted at `this._cwd` when slug/cwd are unavailable
657
+ * (non-Super contexts / tests that construct the manager without a slug).
658
+ */
659
+ _resolveExecution() {
660
+ const repoDir = this._cwd ?? process.cwd();
661
+ if (!this._projectSlug || !this._cwd) {
662
+ return {
663
+ relocated: false,
664
+ cwd: repoDir,
665
+ repoDir,
666
+ workspaceDir: null,
667
+ ticketsPath: path_1.default.join(repoDir, '.specrails', 'local-tickets.json'),
668
+ backlogConfigPath: path_1.default.join(repoDir, '.specrails', 'backlog-config.json'),
669
+ profilesDir: path_1.default.join(repoDir, '.specrails', 'profiles'),
670
+ pluginsStateDir: path_1.default.join(repoDir, '.specrails', 'plugins'),
671
+ fileSummariesDir: path_1.default.join(repoDir, '.specrails', 'file-summaries'),
672
+ specrailsDir: path_1.default.join(repoDir, '.specrails'),
673
+ stateDir: path_1.default.join(repoDir, '.claude'),
674
+ env: {},
675
+ };
676
+ }
677
+ return (0, workspace_resolution_1.resolveProjectExecution)({ slug: this._projectSlug, path: this._cwd });
678
+ }
679
+ /**
680
+ * Resolve the local-tickets.json path honouring the relocate-artifacts gate.
681
+ * Relocated ⇒ the registry entry's ticketsPath (workspace). Legacy ⇒
682
+ * `resolveTicketStoragePath(this._cwd)` which preserves the
683
+ * integration-contract.json custom-storagePath behaviour for existing repos.
684
+ */
685
+ _resolveTicketsPath() {
686
+ const exec = this._resolveExecution();
687
+ if (exec.relocated)
688
+ return exec.ticketsPath;
689
+ return (0, ticket_store_1.resolveTicketStoragePath)(this._cwd ?? process.cwd());
690
+ }
637
691
  /**
638
692
  * Spawn an interactive ultracode session. The job row is created with the
639
693
  * `interactive` flag set; the resident child runs the first turn (the
@@ -687,6 +741,13 @@ class QueueManager {
687
741
  // Interactive jobs skip provenance, but a defensive delete keeps the map
688
742
  // clean if a snapshot was ever recorded for this id.
689
743
  this._snapshotRefs.delete(jobId);
744
+ // Clean up the per-job openspec PATH shim (relocated claude rails only).
745
+ const shim = this._openspecShims.get(jobId);
746
+ if (shim) {
747
+ this._openspecShims.delete(jobId);
748
+ if (this._projectSlug)
749
+ (0, openspec_shim_1.removeOpenspecShim)(this._projectSlug, jobId, (0, artifact_registry_1.resolveHome)());
750
+ }
690
751
  if (this._disposed)
691
752
  return;
692
753
  const job = this._jobs.get(jobId);
@@ -785,6 +846,13 @@ class QueueManager {
785
846
  // primary; everything in this spawn (binary, argv, model, profile, OTEL,
786
847
  // plugins, result parsing, ai_invocations.provider) flows from `adapter`.
787
848
  const adapter = this._resolveJobAdapter(jobId);
849
+ // Relocate-artifacts gate: resolve cwd/repoDir/env for this spawn. Legacy
850
+ // projects get cwd = project.path + empty env (byte-identical to today);
851
+ // relocated projects get cwd = workspace + SPECRAILS_REPO_DIR. Captured per
852
+ // job so the post-exit provenance hook uses the SAME repoDir.
853
+ const execution = this._resolveExecution();
854
+ this._jobExecution.set(jobId, execution);
855
+ const spawnCwd = execution.cwd;
788
856
  job.status = 'running';
789
857
  job.startedAt = new Date().toISOString();
790
858
  job.queuePosition = null;
@@ -875,10 +943,17 @@ class QueueManager {
875
943
  : adapter.id === 'claude' && this._db
876
944
  ? (0, db_1.getProjectSettings)(this._db).orchestratorModel
877
945
  : (this._resolvedModel ?? adapter.defaultModel());
946
+ // Relocate-artifacts: when relocated, claude is spawned from the workspace
947
+ // so add `--add-dir <repoDir>` so its tools can still reach repo files by
948
+ // absolute path. (gemini/codex get env-only tweaks at spawn time below.)
949
+ const railExtraArgs = execution.relocated && adapter.id === 'claude'
950
+ ? ['--add-dir', execution.repoDir]
951
+ : undefined;
878
952
  const args = adapter.buildArgs('rail-job', {
879
953
  prompt: railPrompt,
880
954
  systemPrompt: systemAppend || undefined,
881
955
  model: railModel,
956
+ extraArgs: railExtraArgs,
882
957
  });
883
958
  // Resolve agent profile (if any) and snapshot per-job before spawn.
884
959
  // Super mode only (projectId + projectSlug + cwd all present).
@@ -894,11 +969,13 @@ class QueueManager {
894
969
  try {
895
970
  const selection = this._jobProfileSelection.get(jobId); // undefined|null|string
896
971
  this._jobProfileSelection.delete(jobId);
897
- const coreSupports = projectSupportsProfiles(this._cwd);
972
+ // When relocated, core + `.specrails/profiles` live in the workspace
973
+ // (execution.cwd); legacy reads from the repo (execution.cwd === repo).
974
+ const coreSupports = projectSupportsProfiles(execution.cwd);
898
975
  if (selection !== null && coreSupports) {
899
976
  // selection is string (explicit) or undefined (default resolution)
900
977
  const { resolveProfile, snapshotForJob, persistJobProfile, } = require('./profile-manager');
901
- const resolved = resolveProfile(this._cwd, selection ?? undefined, adapter.id);
978
+ const resolved = resolveProfile(execution.cwd, selection ?? undefined, adapter.id);
902
979
  if (resolved) {
903
980
  profileSnapshotPath = snapshotForJob(this._projectSlug, jobId, resolved);
904
981
  profileName = resolved.name;
@@ -921,12 +998,22 @@ class QueueManager {
921
998
  // gets signals synthesised by the codex-otel-bridge attached below.
922
999
  let spawnEnv = process.env;
923
1000
  const telemetryEnabled = !!(this._projectId && this._db && (0, db_1.getProjectSettings)(this._db).pipelineTelemetryEnabled);
1001
+ // Resolve the framework version ONCE at spawn time — `framework/current`
1002
+ // is read from `~/.specrails/framework/current`. A concurrent atomic swap
1003
+ // (FrameworkManager.swapCurrent) does not disturb this job: we captured the
1004
+ // version here and the per-job snapshot resolved its handles from this
1005
+ // value. GATED on `execution.relocated`: a LEGACY (in-repo) job does NOT
1006
+ // assemble from `framework/current`, so stamping it with a sibling project's
1007
+ // materialized framework version would be wrong telemetry. Null otherwise.
1008
+ const frameworkVersion = execution.relocated ? (0, framework_manager_1.readCurrentFrameworkVersion)() : null;
924
1009
  if (telemetryEnabled && adapter.capabilities.nativeOtelEnv && this._projectId) {
925
1010
  const extra = {};
926
1011
  if (profileName)
927
1012
  extra['specrails.profile_name'] = profileName;
928
1013
  if (profileName)
929
1014
  extra['specrails.profile_schema_version'] = '1';
1015
+ if (frameworkVersion)
1016
+ extra['specrails.framework_version'] = frameworkVersion;
930
1017
  spawnEnv = {
931
1018
  ...process.env,
932
1019
  ...buildTelemetryEnv(jobId, this._projectId, this._desktopPort, extra),
@@ -952,7 +1039,8 @@ class QueueManager {
952
1039
  if (adapter.mcpRegistration === 'project-json' && this._projectId && this._projectSlug && this._cwd) {
953
1040
  try {
954
1041
  const { resolvePluginsForSpawn, snapshotPluginsForJob } = require('./plugins/rail-integration');
955
- const resolution = await resolvePluginsForSpawn(this._cwd, this._projectId, jobId);
1042
+ // Relocated `.mcp.json`/plugin state live in the workspace (execution.cwd).
1043
+ const resolution = await resolvePluginsForSpawn(execution.cwd, this._projectId, jobId);
956
1044
  pluginActive = resolution.active;
957
1045
  pluginDegraded = resolution.degraded;
958
1046
  if (pluginActive.length > 0 || pluginDegraded.length > 0) {
@@ -987,6 +1075,14 @@ class QueueManager {
987
1075
  const settings = (0, db_1.getProjectSettings)(this._db);
988
1076
  if (settings.pipelineTelemetryEnabled && (pluginActive.length > 0 || pluginDegraded.length > 0)) {
989
1077
  const extra = {};
1078
+ // Re-thread the resolved framework version (this block rebuilds the
1079
+ // whole telemetry env, so it must carry the same attr as the first one).
1080
+ if (frameworkVersion)
1081
+ extra['specrails.framework_version'] = frameworkVersion;
1082
+ if (profileName)
1083
+ extra['specrails.profile_name'] = profileName;
1084
+ if (profileName)
1085
+ extra['specrails.profile_schema_version'] = '1';
990
1086
  if (pluginActive.length > 0) {
991
1087
  extra['specrails.plugins.active'] = JSON.stringify(pluginActive.map((p) => p.name));
992
1088
  extra['specrails.plugins.versions'] = JSON.stringify(Object.fromEntries(pluginActive.map((p) => [p.name, p.version])));
@@ -1004,13 +1100,38 @@ class QueueManager {
1004
1100
  // uses this to pre-acknowledge the project's custom subagents so they load
1005
1101
  // in `gemini -p` mode (else invoke_agent reports "Subagent not found" and the
1006
1102
  // orchestrator silently falls back to a generic agent). No-op for claude/codex.
1007
- if (this._cwd) {
1008
- try {
1009
- adapter.prepareHeadlessSpawn?.(this._cwd);
1103
+ // Runs in the SPAWN cwd (workspace when relocated) — gemini acks the
1104
+ // project's subagents where it will actually discover them.
1105
+ try {
1106
+ adapter.prepareHeadlessSpawn?.(spawnCwd);
1107
+ }
1108
+ catch (err) {
1109
+ /* c8 ignore next -- best-effort prep; a failure is non-fatal */
1110
+ console.warn(`[queue-manager] headless-spawn prep failed: ${err.message}`);
1111
+ }
1112
+ // ─── Relocate-artifacts spawn env ──────────────────────────────────────
1113
+ // Merge SPECRAILS_REPO_DIR (+ workspace/tickets/state/etc.) so stage-3
1114
+ // `${SPECRAILS_REPO_DIR:-.}` re-pointing drives source/openspec/git I/O back
1115
+ // into the repo. Per provider: gemini trusts the workspace cwd; codex gets
1116
+ // NO CODEX_HOME override (all-or-nothing incl. auth → breaks the rail) and
1117
+ // relies on cwd-based discovery from the workspace. Legacy ⇒ empty env, no-op.
1118
+ if (execution.relocated) {
1119
+ spawnEnv = { ...spawnEnv, ...execution.env };
1120
+ if (adapter.id === 'gemini') {
1121
+ spawnEnv = { ...spawnEnv, GEMINI_CLI_TRUST_WORKSPACE: 'true' };
1010
1122
  }
1011
- catch (err) {
1012
- /* c8 ignore next -- best-effort prep; a failure is non-fatal */
1013
- console.warn(`[queue-manager] headless-spawn prep failed: ${err.message}`);
1123
+ // openspec PATH shim (claude rails only): prepend a per-job shim dir that
1124
+ // re-points every BARE `openspec` call at the repo working tree, so a
1125
+ // skill- or un-wrapped-template-driven `openspec <verb>` from the workspace
1126
+ // cwd still operates on the repo's OpenSpec project (see openspec-shim.ts).
1127
+ // claude is the only adapter that runs the openspec-backed sr-* rails;
1128
+ // gemini/codex skill scaffolds carry their own repo-dir wrapping.
1129
+ if (adapter.id === 'claude' && this._projectSlug) {
1130
+ const shimDir = (0, openspec_shim_1.ensureOpenspecShim)(this._projectSlug, jobId, (0, artifact_registry_1.resolveHome)());
1131
+ if (shimDir) {
1132
+ this._openspecShims.set(jobId, shimDir);
1133
+ spawnEnv = { ...spawnEnv, PATH: (0, openspec_shim_1.prependShimToPath)(spawnEnv.PATH, shimDir) };
1134
+ }
1014
1135
  }
1015
1136
  }
1016
1137
  // ─── Interactive ultracode branch ──────────────────────────────────────
@@ -1028,16 +1149,20 @@ class QueueManager {
1028
1149
  prompt: '',
1029
1150
  systemPrompt: systemAppend || undefined,
1030
1151
  model: railModel,
1152
+ extraArgs: railExtraArgs,
1031
1153
  });
1032
- this._startInteractiveJob(jobId, job, adapter, { binary, args: interactiveArgs, cwd: this._cwd, env: spawnEnv }, railPrompt);
1154
+ this._startInteractiveJob(jobId, job, adapter, { binary, args: interactiveArgs, cwd: spawnCwd, env: spawnEnv }, railPrompt);
1033
1155
  return;
1034
1156
  }
1035
1157
  // Code-Explorer pre-spawn snapshot. Captures the working-tree state via
1036
1158
  // `git stash create --include-untracked` so the post-exit hook can diff
1037
1159
  // against it. Gated by SPECRAILS_CODE_EXPLORER — when off, no-op.
1038
- if ((0, feature_flags_1.isCodeExplorerEnabled)() && this._cwd) {
1160
+ // CRITICAL: snapshot the REPO working tree (execution.repoDir), never the
1161
+ // workspace — else a relocated job would diff an empty workspace and silently
1162
+ // record zero "touched by AI" files.
1163
+ if ((0, feature_flags_1.isCodeExplorerEnabled)()) {
1039
1164
  try {
1040
- const snap = (0, file_provenance_1.snapshotWorkingTree)(this._cwd);
1165
+ const snap = (0, file_provenance_1.snapshotWorkingTree)(execution.repoDir);
1041
1166
  this._snapshotRefs.set(jobId, snap);
1042
1167
  }
1043
1168
  catch (err) {
@@ -1048,7 +1173,7 @@ class QueueManager {
1048
1173
  const child = (0, cli_prompt_1.spawnAiCli)(binary, args, {
1049
1174
  env: spawnEnv,
1050
1175
  stdio: ['ignore', 'pipe', 'pipe'],
1051
- cwd: this._cwd,
1176
+ cwd: spawnCwd,
1052
1177
  });
1053
1178
  this._activeProcess = child;
1054
1179
  this._activeJobId = jobId;
@@ -1237,6 +1362,12 @@ class QueueManager {
1237
1362
  // stash commit it references is dangling and git-GC'd on its own).
1238
1363
  const snapshot = this._snapshotRefs.get(jobId);
1239
1364
  this._snapshotRefs.delete(jobId);
1365
+ // Relocate-artifacts: the repo dir this job snapshotted against (= repoDir,
1366
+ // never the workspace). Falls back to this._cwd for jobs spawned before this
1367
+ // map existed (e.g. restored-from-db) so provenance still targets the repo.
1368
+ const jobExecution = this._jobExecution.get(jobId);
1369
+ this._jobExecution.delete(jobId);
1370
+ const provenanceRepoDir = jobExecution?.repoDir ?? this._cwd;
1240
1371
  // A3: release the active slot for THIS job before any early return, so a
1241
1372
  // disposed/unknown-job exit can never leave the slot reserved (which would
1242
1373
  // wedge the queue). Guarded by identity in case a stale exit fires late.
@@ -1341,11 +1472,11 @@ class QueueManager {
1341
1472
  // the pre-spawn snapshot and inserts one row per touched path. Gated by
1342
1473
  // SPECRAILS_CODE_EXPLORER (re-checked at each completion so the flag can
1343
1474
  // be flipped off mid-session without leaving partial writes).
1344
- if ((0, feature_flags_1.isCodeExplorerEnabled)() && this._cwd && this._projectId) {
1475
+ if ((0, feature_flags_1.isCodeExplorerEnabled)() && provenanceRepoDir && this._projectId) {
1345
1476
  const ref = snapshot?.ref ?? '';
1346
1477
  try {
1347
- const diff = (0, file_provenance_1.diffAgainstSnapshot)(this._cwd, ref, snapshot?.untracked, snapshot?.headSha);
1348
- const patches = (0, file_provenance_1.collectDiffPatches)(this._cwd, ref, diff, snapshot?.headSha);
1478
+ const diff = (0, file_provenance_1.diffAgainstSnapshot)(provenanceRepoDir, ref, snapshot?.untracked, snapshot?.headSha);
1479
+ const patches = (0, file_provenance_1.collectDiffPatches)(provenanceRepoDir, ref, diff, snapshot?.headSha);
1349
1480
  if (diff.length > 50) {
1350
1481
  console.warn(`[provenance.large_job] job=${jobId} files=${diff.length}`);
1351
1482
  }
@@ -20,6 +20,11 @@ const win_spawn_1 = require("./util/win-spawn");
20
20
  const setup_prerequisites_1 = require("./setup-prerequisites");
21
21
  const core_package_1 = require("./core-package");
22
22
  const providers_1 = require("./providers");
23
+ const artifact_registry_1 = require("./artifact-registry");
24
+ const install_config_path_1 = require("./install-config-path");
25
+ const bundled_core_1 = require("./bundled-core");
26
+ const bundled_openspec_1 = require("./bundled-openspec");
27
+ const framework_manager_1 = require("./framework-manager");
23
28
  /**
24
29
  * specrails-core's installer (Node-native from v4.2.0 onward, bash
25
30
  * prior) always scaffolds into `.claude/` regardless of which AI
@@ -75,6 +80,45 @@ function spawnCoreInit(args, cwd) {
75
80
  stdio: ['ignore', 'pipe', 'pipe'],
76
81
  });
77
82
  }
83
+ /**
84
+ * Spawn the BUNDLED specrails-core `init` (offline framework + assemble; openspec
85
+ * is the only network step). Runs `node <bundled-cli> init <args>` with
86
+ * `SPECRAILS_CORE_SCRIPT_DIR` pointed at the bundle so core's template/command
87
+ * sources resolve from the app bundle, NOT a global install or npm registry. The
88
+ * framework was already materialized by FrameworkManager.materialize() (idempotent
89
+ * — core's ensureFramework skips re-materialization), so this call only assembles
90
+ * the workspace by symlink + runs openspec init.
91
+ *
92
+ * Returns null when no bundled core is present (caller falls back to spawnCoreInit).
93
+ */
94
+ function spawnBundledCoreInit(args, cwd) {
95
+ const cli = (0, bundled_core_1.getBundledCoreCli)();
96
+ const coreRoot = (0, bundled_core_1.getBundledCoreRoot)();
97
+ if (!cli || !coreRoot)
98
+ return null;
99
+ const fullArgs = [cli, 'init', ...args];
100
+ const env = { ...process.env, SPECRAILS_CORE_SCRIPT_DIR: coreRoot };
101
+ // Bundled openspec (offline) — the LAST network step of project-add. When the
102
+ // app ships @fission-ai/openspec, point specrails-core's `installOpenSpecProject`
103
+ // at the bundled CLI and the SAME node we run the bundled core with. Tauri
104
+ // strips exec bits from bundled resources and the openspec CLI is a node script,
105
+ // so it MUST be invoked as `node <cli> init …` — hence we set BOTH the BIN and
106
+ // the NODE env (specrails-core's buildOpenSpecInvocation form 1). When absent we
107
+ // leave them unset → core falls back to `npx @fission-ai/openspec` (still works
108
+ // online). Bundled core + bundled openspec ⇒ project-add is FULLY OFFLINE.
109
+ const openspecCli = (0, bundled_openspec_1.getBundledOpenspecCli)();
110
+ if (openspecCli) {
111
+ env.SPECRAILS_OPENSPEC_BIN = openspecCli;
112
+ env.SPECRAILS_OPENSPEC_NODE = process.execPath;
113
+ }
114
+ console.log(`[SetupManager] spawning BUNDLED core: ${process.execPath} ${fullArgs.join(' ')} (cwd=${cwd})` +
115
+ (openspecCli ? ' [bundled openspec: offline]' : ' [openspec: npx fallback]'));
116
+ return (0, win_spawn_1.spawnCli)(process.execPath, fullArgs, {
117
+ cwd,
118
+ env,
119
+ stdio: ['ignore', 'pipe', 'pipe'],
120
+ });
121
+ }
78
122
  // H19: hard cap on the runtime probe. `npx --yes --prefer-online
79
123
  // <CORE_PACKAGE_SPEC> version` does a network round-trip to the npm
80
124
  // registry, and spawnSync blocks the single event loop — without a timeout a
@@ -120,8 +164,8 @@ function writeSpawnInstallConfig(projectId, yamlText) {
120
164
  (0, fs_1.writeFileSync)(tempPath, yamlText, 'utf-8');
121
165
  return tempPath;
122
166
  }
123
- function readInstallConfig(projectPath) {
124
- const configPath = (0, path_1.join)(projectPath, '.specrails', 'install-config.yaml');
167
+ function readInstallConfig(project) {
168
+ const configPath = (0, install_config_path_1.installConfigPath)(project);
125
169
  try {
126
170
  const text = (0, fs_1.readFileSync)(configPath, 'utf-8');
127
171
  const tierMatch = text.match(/^tier:\s*(\w+)/m);
@@ -591,14 +635,33 @@ class SetupManager {
591
635
  this._projectNames = new Map();
592
636
  }
593
637
  // ─── Full Install: TUI installer (npx specrails-core) ────────────────────────
594
- startInstall(projectId, projectPath) {
638
+ startInstall(projectId, projectPath, projectSlug) {
595
639
  if (this._installProcesses.has(projectId)) {
596
640
  console.warn(`[SetupManager] install already running for ${projectId}`);
597
641
  return;
598
642
  }
599
- const configPath = (0, path_1.join)(projectPath, '.specrails', 'install-config.yaml');
643
+ // Relocate-artifacts: ensure the shared registry entry exists (slug
644
+ // agreement) BEFORE core's `init` runs, so core resolves its workspace from
645
+ // the registry and installs the relocated artifacts under desktop's slug.
646
+ // addProject already mirrors the entry; this is belt-and-suspenders for the
647
+ // (rare) case where the project was registered before the mirror existed.
648
+ // Wrapped so a registry write failure never blocks install (core then
649
+ // installs in-repo and the project simply stays legacy — gate-safe).
650
+ if (projectSlug) {
651
+ try {
652
+ (0, artifact_registry_1.mirrorProjectEntry)({ repoPath: projectPath, slug: projectSlug, desktopProjectId: projectId });
653
+ }
654
+ catch (err) {
655
+ console.warn(`[SetupManager] registry mirror before install failed (non-fatal): ${err.message}`);
656
+ }
657
+ }
658
+ // Relocate-artifacts: the install config lives in the per-project HOME dir
659
+ // (NOT the repo). `--from-config` accepts any path, so the spawn below points
660
+ // core at the relocated location.
661
+ const installProject = { slug: projectSlug, path: projectPath };
662
+ const configPath = (0, install_config_path_1.installConfigPath)(installProject);
600
663
  const hasConfig = (0, fs_1.existsSync)(configPath);
601
- const parsedConfig = hasConfig ? readInstallConfig(projectPath) : null;
664
+ const parsedConfig = hasConfig ? readInstallConfig(installProject) : null;
602
665
  const tier = parsedConfig?.tier ?? 'full';
603
666
  this._projectTiers.set(projectId, tier);
604
667
  // Pull provider out of the just-written install-config.yaml so the
@@ -628,24 +691,54 @@ class SetupManager {
628
691
  });
629
692
  return;
630
693
  }
631
- const probe = probeCoreRuntimeVersion(projectPath);
632
- if (!probe.ok) {
633
- this._broadcast({
634
- type: 'setup_error',
635
- projectId,
636
- error: `Failed to verify specrails-core runtime before install: ${probe.error ?? 'unknown error'}`,
637
- });
638
- return;
694
+ // ─── Bundled-core fast path (offline framework) ──────────────────────────
695
+ // When the app ships specrails-core, materialize the versioned framework
696
+ // ONCE (instant/offline) and run the BUNDLED core `init` (offline framework
697
+ // assemble by symlink; openspec init is the only remaining network step) —
698
+ // NO `npx specrails-core` round-trip. When NO bundled core is present we fall
699
+ // through to the legacy npx probe + spawn, byte-identical to today.
700
+ const useBundledCore = (0, bundled_core_1.getBundledCoreCli)() !== null;
701
+ if (useBundledCore) {
702
+ // Materialize the framework for the selected provider (idempotent). The
703
+ // subsequent bundled `init` finds it already present (ensureFramework
704
+ // skips) and just assembles the workspace by symlink. Best-effort: a
705
+ // materialize failure is surfaced but the bundled init also re-runs
706
+ // ensureFramework, so it self-heals.
707
+ const provider = this._projectProviders.get(projectId) ?? 'claude';
708
+ try {
709
+ const fm = new framework_manager_1.FrameworkManager({
710
+ broadcast: (msg) => this._broadcast(msg),
711
+ });
712
+ const mat = fm.materialize((0, bundled_core_1.getBundledCoreVersion)() ?? undefined, [provider]);
713
+ if (mat.ran && mat.errors.length > 0) {
714
+ console.warn(`[SetupManager] framework materialize had errors: ${mat.errors.map((e) => `${e.provider}: ${e.message}`).join('; ')}`);
715
+ }
716
+ }
717
+ catch (err) {
718
+ console.warn(`[SetupManager] framework materialize threw (non-fatal): ${err.message}`);
719
+ }
639
720
  }
640
- console.log(`[SetupManager] core runtime probe: ${probe.bin} -> ${probe.version}`);
641
- const probeCmp = compareSemver(probe.version, MIN_NODE_NATIVE_CORE_VERSION);
642
- if (probeCmp !== null && probeCmp < 0) {
643
- this._broadcast({
644
- type: 'setup_error',
645
- projectId,
646
- error: `Resolved specrails-core@${probe.version} is legacy; expected Node-native >= ${MIN_NODE_NATIVE_CORE_VERSION}.`,
647
- });
648
- return;
721
+ else {
722
+ // Legacy path: probe the npx-resolved core runtime (network) before spawn.
723
+ const probe = probeCoreRuntimeVersion(projectPath);
724
+ if (!probe.ok) {
725
+ this._broadcast({
726
+ type: 'setup_error',
727
+ projectId,
728
+ error: `Failed to verify specrails-core runtime before install: ${probe.error ?? 'unknown error'}`,
729
+ });
730
+ return;
731
+ }
732
+ console.log(`[SetupManager] core runtime probe: ${probe.bin} -> ${probe.version}`);
733
+ const probeCmp = compareSemver(probe.version, MIN_NODE_NATIVE_CORE_VERSION);
734
+ if (probeCmp !== null && probeCmp < 0) {
735
+ this._broadcast({
736
+ type: 'setup_error',
737
+ projectId,
738
+ error: `Resolved specrails-core@${probe.version} is legacy; expected Node-native >= ${MIN_NODE_NATIVE_CORE_VERSION}.`,
739
+ });
740
+ return;
741
+ }
649
742
  }
650
743
  let spawnConfigPath = null;
651
744
  if (hasConfig) {
@@ -659,7 +752,10 @@ class SetupManager {
659
752
  const initArgs = hasConfig
660
753
  ? ['--yes', '--from-config', spawnConfigPath ?? configPath]
661
754
  : ['--yes', '--root-dir', projectPath];
662
- const child = spawnCoreInit(initArgs, projectPath);
755
+ // Bundled core (offline, node <cli> init) when available, else legacy npx.
756
+ const child = useBundledCore
757
+ ? spawnBundledCoreInit(initArgs, projectPath)
758
+ : spawnCoreInit(initArgs, projectPath);
663
759
  this._installProcesses.set(projectId, child);
664
760
  this._installLogBuffer.set(projectId, []);
665
761
  // spawnCoreInit uses shell:false on POSIX, so a spawn failure emits 'error'
@@ -738,7 +834,7 @@ class SetupManager {
738
834
  this._broadcast({
739
835
  type: 'setup_error',
740
836
  projectId,
741
- error: formatBufferedInstallError(`npx specrails-core exited with code ${code ?? 'unknown'}`, logBuffer),
837
+ error: formatBufferedInstallError(`${useBundledCore ? 'bundled specrails-core' : 'npx specrails-core'} exited with code ${code ?? 'unknown'}`, logBuffer),
742
838
  });
743
839
  }
744
840
  });
@@ -769,7 +865,12 @@ class SetupManager {
769
865
  catch (err) {
770
866
  console.warn(`[SetupManager] Failed to pre-create enrich directories: ${err}`);
771
867
  }
772
- const configPath = (0, path_1.join)(projectPath, '.specrails', 'install-config.yaml');
868
+ // Relocate-artifacts: the install config lives in the per-project HOME dir.
869
+ // The slug was allocated at addProject and mirrored into the shared registry
870
+ // before/at install time, so resolve it from there (legacy full-flow path —
871
+ // not exercised by the app's quick flow).
872
+ const enrichSlug = (0, artifact_registry_1.resolveArtifacts)(projectPath).entry?.slug;
873
+ const configPath = (0, install_config_path_1.installConfigPath)({ slug: enrichSlug, path: projectPath });
773
874
  const hasConfig = (0, fs_1.existsSync)(configPath);
774
875
  const enrichCmd = hasConfig ? '/specrails:enrich --from-config' : '/specrails:enrich';
775
876
  this._spawnSetupWithAdapter(projectId, projectPath, {
@@ -1148,16 +1249,17 @@ class SetupManager {
1148
1249
  getInstallTier(projectId) {
1149
1250
  return this._projectTiers.get(projectId);
1150
1251
  }
1151
- getSummary(projectPath) {
1152
- const config = readInstallConfig(projectPath);
1252
+ getSummary(project) {
1253
+ const config = readInstallConfig(project);
1153
1254
  const tier = config?.tier ?? 'quick';
1154
1255
  // Provider is authoritative from install-config.yaml when present; we
1155
1256
  // do NOT fall back to filesystem heuristics because both `.codex/` and
1156
1257
  // `.claude/` can legitimately coexist (e.g. a project that's been
1157
1258
  // re-init'd) and a generic `existsSync` probe would mis-route.
1259
+ // Relocate-artifacts: the config lives in the per-project HOME dir.
1158
1260
  let provider = 'claude';
1159
1261
  try {
1160
- const text = (0, fs_1.readFileSync)((0, path_1.join)(projectPath, '.specrails', 'install-config.yaml'), 'utf-8');
1262
+ const text = (0, fs_1.readFileSync)((0, install_config_path_1.installConfigPath)(project), 'utf-8');
1161
1263
  const m = text.match(/^provider:\s*(\w+)/m);
1162
1264
  if (m && m[1] && (0, providers_1.hasAdapter)(m[1]))
1163
1265
  provider = m[1];
@@ -1165,7 +1267,7 @@ class SetupManager {
1165
1267
  catch {
1166
1268
  // Missing install-config — stay on claude default.
1167
1269
  }
1168
- return computeSummary(projectPath, tier, provider);
1270
+ return computeSummary(project.path, tier, provider);
1169
1271
  }
1170
1272
  }
1171
1273
  exports.SetupManager = SetupManager;
@@ -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 {