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
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ // Gemini headless subagent pre-acknowledgment.
3
+ //
4
+ // gemini 0.46+ DISCOVERS `<project>/.gemini/agents/*.md` but only ENABLES a
5
+ // project's custom subagents after an interactive "New Agents Discovered →
6
+ // Acknowledge and Enable" prompt. That prompt never fires in headless
7
+ // (`gemini -p`) spawns — which is how the desktop runs every rail — so
8
+ // `invoke_agent sr-architect` returns "Subagent not found" and the implement
9
+ // orchestrator silently falls back to a generic agent (the specialised
10
+ // architect/developer/reviewer personas never run in isolation).
11
+ //
12
+ // specrails-core writes the acknowledgment file at install time; this is the
13
+ // defence-in-depth copy the desktop runs right before a gemini rail spawn, so a
14
+ // project installed with an older core (or whose agents changed since install)
15
+ // is still trusted headless. The file gemini reads is
16
+ // `~/.gemini/acknowledgments/agents.json`, shaped
17
+ // { [projectRoot]: { [agentName]: <sha256-hex of the agent .md file> } }
18
+ // where the hash is sha256 of the FULL agent markdown file (verified empirically
19
+ // against gemini 0.47). Entries are MERGED so other projects (and other agents)
20
+ // survive. Best-effort — callers swallow any error; a failure only means the
21
+ // agents need the one-time interactive acknowledge.
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.acknowledgeGeminiProjectAgents = acknowledgeGeminiProjectAgents;
24
+ const crypto_1 = require("crypto");
25
+ const fs_1 = require("fs");
26
+ const os_1 = require("os");
27
+ const path_1 = require("path");
28
+ function ackFilePath() {
29
+ return (0, path_1.join)((0, os_1.homedir)(), '.gemini', 'acknowledgments', 'agents.json');
30
+ }
31
+ /**
32
+ * Pre-acknowledge every `<projectPath>/.gemini/agents/*.md` so gemini loads them
33
+ * in headless mode. No-op when the project has no `.gemini/agents` dir or no
34
+ * agent files. The `projectPath` is the key gemini uses (the spawn cwd / repo
35
+ * root), matching what `specrails-core` writes at install.
36
+ */
37
+ function acknowledgeGeminiProjectAgents(projectPath) {
38
+ const agentsDir = (0, path_1.join)(projectPath, '.gemini', 'agents');
39
+ if (!(0, fs_1.existsSync)(agentsDir))
40
+ return;
41
+ const agentFiles = (0, fs_1.readdirSync)(agentsDir).filter((f) => f.endsWith('.md') && !f.startsWith('_'));
42
+ if (agentFiles.length === 0)
43
+ return;
44
+ const ackPath = ackFilePath();
45
+ let store = {};
46
+ if ((0, fs_1.existsSync)(ackPath)) {
47
+ try {
48
+ const parsed = JSON.parse((0, fs_1.readFileSync)(ackPath, 'utf8'));
49
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
50
+ store = parsed;
51
+ }
52
+ }
53
+ catch {
54
+ // Corrupt/unreadable file — start fresh rather than crash the spawn.
55
+ }
56
+ }
57
+ const projectEntry = { ...(store[projectPath] ?? {}) };
58
+ for (const file of agentFiles) {
59
+ const content = (0, fs_1.readFileSync)((0, path_1.join)(agentsDir, file), 'utf8');
60
+ projectEntry[file.slice(0, -3)] = (0, crypto_1.createHash)('sha256').update(content).digest('hex');
61
+ }
62
+ store[projectPath] = projectEntry;
63
+ (0, fs_1.mkdirSync)((0, path_1.join)((0, os_1.homedir)(), '.gemini', 'acknowledgments'), { recursive: true });
64
+ (0, fs_1.writeFileSync)(ackPath, `${JSON.stringify(store, null, 2)}\n`);
65
+ }
@@ -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])));
@@ -1000,6 +1096,44 @@ class QueueManager {
1000
1096
  };
1001
1097
  }
1002
1098
  }
1099
+ // Provider-specific filesystem prep before a headless rail spawn. Gemini
1100
+ // uses this to pre-acknowledge the project's custom subagents so they load
1101
+ // in `gemini -p` mode (else invoke_agent reports "Subagent not found" and the
1102
+ // orchestrator silently falls back to a generic agent). No-op for claude/codex.
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' };
1122
+ }
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
+ }
1135
+ }
1136
+ }
1003
1137
  // ─── Interactive ultracode branch ──────────────────────────────────────
1004
1138
  // When the launch requested interactive mode AND the command is ultracode
1005
1139
  // AND the adapter supports persistent stdin (claude), hand off to a resident
@@ -1015,16 +1149,20 @@ class QueueManager {
1015
1149
  prompt: '',
1016
1150
  systemPrompt: systemAppend || undefined,
1017
1151
  model: railModel,
1152
+ extraArgs: railExtraArgs,
1018
1153
  });
1019
- 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);
1020
1155
  return;
1021
1156
  }
1022
1157
  // Code-Explorer pre-spawn snapshot. Captures the working-tree state via
1023
1158
  // `git stash create --include-untracked` so the post-exit hook can diff
1024
1159
  // against it. Gated by SPECRAILS_CODE_EXPLORER — when off, no-op.
1025
- 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)()) {
1026
1164
  try {
1027
- const snap = (0, file_provenance_1.snapshotWorkingTree)(this._cwd);
1165
+ const snap = (0, file_provenance_1.snapshotWorkingTree)(execution.repoDir);
1028
1166
  this._snapshotRefs.set(jobId, snap);
1029
1167
  }
1030
1168
  catch (err) {
@@ -1035,7 +1173,7 @@ class QueueManager {
1035
1173
  const child = (0, cli_prompt_1.spawnAiCli)(binary, args, {
1036
1174
  env: spawnEnv,
1037
1175
  stdio: ['ignore', 'pipe', 'pipe'],
1038
- cwd: this._cwd,
1176
+ cwd: spawnCwd,
1039
1177
  });
1040
1178
  this._activeProcess = child;
1041
1179
  this._activeJobId = jobId;
@@ -1224,6 +1362,12 @@ class QueueManager {
1224
1362
  // stash commit it references is dangling and git-GC'd on its own).
1225
1363
  const snapshot = this._snapshotRefs.get(jobId);
1226
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;
1227
1371
  // A3: release the active slot for THIS job before any early return, so a
1228
1372
  // disposed/unknown-job exit can never leave the slot reserved (which would
1229
1373
  // wedge the queue). Guarded by identity in case a stale exit fires late.
@@ -1328,11 +1472,11 @@ class QueueManager {
1328
1472
  // the pre-spawn snapshot and inserts one row per touched path. Gated by
1329
1473
  // SPECRAILS_CODE_EXPLORER (re-checked at each completion so the flag can
1330
1474
  // be flipped off mid-session without leaving partial writes).
1331
- if ((0, feature_flags_1.isCodeExplorerEnabled)() && this._cwd && this._projectId) {
1475
+ if ((0, feature_flags_1.isCodeExplorerEnabled)() && provenanceRepoDir && this._projectId) {
1332
1476
  const ref = snapshot?.ref ?? '';
1333
1477
  try {
1334
- const diff = (0, file_provenance_1.diffAgainstSnapshot)(this._cwd, ref, snapshot?.untracked, snapshot?.headSha);
1335
- 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);
1336
1480
  if (diff.length > 50) {
1337
1481
  console.warn(`[provenance.large_job] job=${jobId} files=${diff.length}`);
1338
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;