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