specrails-desktop 2.30.2 → 2.31.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 +28 -24
- package/client/dist/assets/{ActivityFeedPage-9DZOvIcY.js → ActivityFeedPage-CAhFRUO4.js} +1 -1
- package/client/dist/assets/{AgentBrowserCapture-DqyHbzk7.js → AgentBrowserCapture-Ct4G5lsE.js} +1 -1
- package/client/dist/assets/AgentModeCodePane-DQt9WIvu.js +2 -0
- package/client/dist/assets/{AgentModeJobsPane-CTIHMUKN.js → AgentModeJobsPane-podaJVl9.js} +1 -1
- package/client/dist/assets/AgentsPage-DTZdbW80.js +87 -0
- package/client/dist/assets/AnalyticsPage-BskgGPm0.js +1 -0
- package/client/dist/assets/CodePage-CvtYo1DU.js +2 -0
- package/client/dist/assets/{DesktopAnalyticsPage-CkXUFj3F.js → DesktopAnalyticsPage-Dnf99E8e.js} +1 -1
- package/client/dist/assets/{DocsDialog-PhdCJNVj.js → DocsDialog-DHi36rZ4.js} +1 -1
- package/client/dist/assets/{DocsPage-BY-wDthx.js → DocsPage-DIdVrQz1.js} +1 -1
- package/client/dist/assets/{InteractiveJobComposer-C9I0A4KR.js → InteractiveJobComposer-BHeB2inv.js} +1 -1
- package/client/dist/assets/{JobDetailModal-I_RoQJV4.js → JobDetailModal-CF1SKUHH.js} +1 -1
- package/client/dist/assets/JobDetailPage-De3rY18h.js +1 -0
- package/client/dist/assets/{JobsPage-DcllM4Ip.js → JobsPage-CfAqqJTx.js} +1 -1
- package/client/dist/assets/{LoopBuilderPage-DGm_iuff.js → LoopBuilderPage-DSBMuTb3.js} +2 -2
- package/client/dist/assets/{LoopPreviewModal-BC-TeFdD.js → LoopPreviewModal-B-c37kA0.js} +1 -1
- package/client/dist/assets/LoopsPage-DjnNXdmm.js +1 -0
- package/client/dist/assets/{MinimizedChatsContext-Cs34FPTz.js → MinimizedChatsContext-DIsnQMVz.js} +1 -1
- package/client/dist/assets/PluginsPage-DA5pQ_6u.js +2 -0
- package/client/dist/assets/{ProjectSettingsDialog-nEs-ENrE.js → ProjectSettingsDialog-DA23PSi6.js} +1 -1
- package/client/dist/assets/{TemplatePreviewModal-D4qW-J9B.js → TemplatePreviewModal-Cp7IQ-g2.js} +1 -1
- package/client/dist/assets/TicketDetailModal-BPD2i_0R.js +14 -0
- package/client/dist/assets/{Trans-CPISVji8.js → Trans-cWtExGkC.js} +1 -1
- package/client/dist/assets/addspec-4WmLJmBG.js +1 -0
- package/client/dist/assets/addspec-BRlk8jON.js +1 -0
- package/client/dist/assets/addspec-C7XRejU4.js +1 -0
- package/client/dist/assets/addspec-CHW9xf_X.js +1 -0
- package/client/dist/assets/addspec-DTmRrWrF.js +1 -0
- package/client/dist/assets/addspec-DhmnXER8.js +1 -0
- package/client/dist/assets/addspec-Di41DpYV.js +1 -0
- package/client/dist/assets/addspec-x3mizDxc.js +1 -0
- package/client/dist/assets/agent-BKsuIgIT.js +1 -0
- package/client/dist/assets/agent-Bs9NilfI.js +1 -0
- package/client/dist/assets/agent-C23h684q.js +1 -0
- package/client/dist/assets/agent-CqEjy3Z2.js +1 -0
- package/client/dist/assets/agent-D5DZTks1.js +1 -0
- package/client/dist/assets/agent-DE8hUYPl.js +1 -0
- package/client/dist/assets/agent-DsTuJGSc.js +1 -0
- package/client/dist/assets/agent-DwZM05-x.js +1 -0
- package/client/dist/assets/agents-45GbINfh.js +1 -0
- package/client/dist/assets/agents-BHp0_FQt.js +1 -0
- package/client/dist/assets/agents-Bvyi3y6R.js +1 -0
- package/client/dist/assets/agents-DMKFStHL.js +1 -0
- package/client/dist/assets/agents-DaYcxAr1.js +1 -0
- package/client/dist/assets/agents-IUOgqT5t.js +1 -0
- package/client/dist/assets/agents-ms7TwG16.js +1 -0
- package/client/dist/assets/agents-rzPnqoBR.js +1 -0
- package/client/dist/assets/agentstudio-8TQqBenW.js +1 -0
- package/client/dist/assets/agentstudio-B8Z_mmEX.js +1 -0
- package/client/dist/assets/agentstudio-BZR9YmLs.js +1 -0
- package/client/dist/assets/agentstudio-Bge9pAWb.js +1 -0
- package/client/dist/assets/agentstudio-Bz169Vzt.js +1 -0
- package/client/dist/assets/agentstudio-D6L2jpNi.js +1 -0
- package/client/dist/assets/agentstudio-K2-Z7CL0.js +1 -0
- package/client/dist/assets/agentstudio-Zmgzthv8.js +1 -0
- package/client/dist/assets/analytics-B8Qtptbl.js +1 -0
- package/client/dist/assets/analytics-BHqUazhP.js +1 -0
- package/client/dist/assets/analytics-BPptRY4f.js +1 -0
- package/client/dist/assets/analytics-BxDGOIwf.js +1 -0
- package/client/dist/assets/analytics-C9RaQcE3.js +1 -0
- package/client/dist/assets/analytics-CJIwqrEl.js +1 -0
- package/client/dist/assets/analytics-D_OZdQ9O.js +1 -0
- package/client/dist/assets/analytics-We6lUMI8.js +1 -0
- package/client/dist/assets/builder-3bygQtZi.js +1 -0
- package/client/dist/assets/builder-BiUG9Z5K.js +1 -0
- package/client/dist/assets/builder-CGRz_-zP.js +1 -0
- package/client/dist/assets/builder-CWYPw7Cw.js +1 -0
- package/client/dist/assets/builder-CX1437hF.js +1 -0
- package/client/dist/assets/{builder-CwA4T7gP.js → builder-Cowk9jtL.js} +1 -1
- package/client/dist/assets/builder-DwDexYRE.js +1 -0
- package/client/dist/assets/builder-skfnZ1uC.js +1 -0
- package/client/dist/assets/code-B0O8-p8k.js +1 -0
- package/client/dist/assets/code-B1VLZDhS.js +1 -0
- package/client/dist/assets/code-B86IMAfz.js +1 -0
- package/client/dist/assets/code-Bpj4Ddml.js +1 -0
- package/client/dist/assets/code-BykBZ5-e.js +1 -0
- package/client/dist/assets/code-CHPkOkDS.js +1 -0
- package/client/dist/assets/code-CkUuW4m9.js +1 -0
- package/client/dist/assets/code-cAqaqMTx.js +1 -0
- package/client/dist/assets/{dashboard-Dy2Um-US.js → dashboard-BVq6kRJe.js} +1 -1
- package/client/dist/assets/{dashboard-CHCEkds4.js → dashboard-BaKPPmLH.js} +1 -1
- package/client/dist/assets/{dashboard-CrlTV1ms.js → dashboard-Bm8e4zYG.js} +1 -1
- package/client/dist/assets/{dashboard-WhzOQw1S.js → dashboard-Bt6IesnM.js} +1 -1
- package/client/dist/assets/{dashboard-Cslu_GCH.js → dashboard-C_NQZle_.js} +1 -1
- package/client/dist/assets/{dashboard-DweT3Y4R.js → dashboard-CyZKoGoI.js} +1 -1
- package/client/dist/assets/{dashboard-DUU8-_Rg.js → dashboard-HIUdUBQ0.js} +1 -1
- package/client/dist/assets/{dashboard-Cqmmbh3l.js → dashboard-pm4Mr229.js} +1 -1
- package/client/dist/assets/{dist-js-jwoXo0YD.js → dist-js-BaP-xLOy.js} +1 -1
- package/client/dist/assets/{dist-js-Dj72W6qo.js → dist-js-CLlgOa5a.js} +1 -1
- package/client/dist/assets/format-command-C0D9IPmV.js +1 -0
- package/client/dist/assets/{formatDistanceToNow-ByKli7Q_.js → formatDistanceToNow-DUyzS4rr.js} +1 -1
- package/client/dist/assets/{getTimezoneOffsetInMilliseconds-qrcBaLW1.js → getTimezoneOffsetInMilliseconds-BCIwmmTj.js} +1 -1
- package/client/dist/assets/{i18n-Bf9Ckd10.js → i18n-D21Pr8Sj.js} +2 -2
- package/client/dist/assets/index-BdSDUFnA.css +2 -0
- package/client/dist/assets/index-Ci4-JKDr.js +77 -0
- package/client/dist/assets/{jobs-CVFD8rar.js → jobs-BAls3bWq.js} +1 -1
- package/client/dist/assets/{jobs-DoSaFzAa.js → jobs-B_TeSQxb.js} +1 -1
- package/client/dist/assets/jobs-Bgq-PZkC.js +1 -0
- package/client/dist/assets/jobs-Bpks6doU.js +1 -0
- package/client/dist/assets/jobs-CN_zv4Gr.js +1 -0
- package/client/dist/assets/jobs-DUlWSYq2.js +1 -0
- package/client/dist/assets/{jobs-B-sDHEay.js → jobs-DhxKgWFq.js} +1 -1
- package/client/dist/assets/{jobs-lk1_Dq1U.js → jobs-SwYS2M-B.js} +1 -1
- package/client/dist/assets/nav-BHDiSIVu.js +1 -0
- package/client/dist/assets/nav-CMiYAq4i.js +1 -0
- package/client/dist/assets/nav-CqS7NrDW.js +1 -0
- package/client/dist/assets/nav-D-0M7xfv.js +1 -0
- package/client/dist/assets/nav-D65O6u9I.js +1 -0
- package/client/dist/assets/nav-D_LKYvBr.js +1 -0
- package/client/dist/assets/nav-FDTJmU79.js +1 -0
- package/client/dist/assets/nav-VaB-C4k5.js +1 -0
- package/client/dist/assets/provider-capabilities-BvoExILy.js +1 -0
- package/client/dist/assets/settings-BveOJoaI.js +1 -0
- package/client/dist/assets/settings-Cj1C6F5k.js +1 -0
- package/client/dist/assets/settings-CuVwOpe0.js +1 -0
- package/client/dist/assets/settings-D5X4DfRc.js +1 -0
- package/client/dist/assets/settings-DXwxe-BW.js +1 -0
- package/client/dist/assets/settings-Di81Ba_V.js +1 -0
- package/client/dist/assets/settings-jbLjCYC_.js +1 -0
- package/client/dist/assets/settings-wIZNPFnn.js +1 -0
- package/client/dist/assets/setup-BBiHEO7h.js +1 -0
- package/client/dist/assets/setup-BlTWhNIQ.js +1 -0
- package/client/dist/assets/setup-Bp2L_oiP.js +1 -0
- package/client/dist/assets/setup-CInYXp1-.js +1 -0
- package/client/dist/assets/{setup-CITtPjed.js → setup-CYH3bP6K.js} +1 -1
- package/client/dist/assets/setup-DwcQ921P.js +1 -0
- package/client/dist/assets/setup-PBLjuDSs.js +1 -0
- package/client/dist/assets/setup-bx6FtoBp.js +1 -0
- package/client/dist/assets/{spending-DMnCXy84.js → spending-BCyUxoOI.js} +1 -1
- package/client/dist/assets/{tickets-B8BuAaj5.js → tickets-BL9r1G0e.js} +1 -1
- package/client/dist/assets/{tickets-DpmdE8tv.js → tickets-B_NyHJ8f.js} +1 -1
- package/client/dist/assets/{tickets-B_yh4qQk.js → tickets-BarF1wuG.js} +1 -1
- package/client/dist/assets/{tickets-pAUJkDTu.js → tickets-CclVhpQA.js} +1 -1
- package/client/dist/assets/{tickets-D_yDquEw.js → tickets-CqqOHbli.js} +1 -1
- package/client/dist/assets/{tickets-CFk2n2Or.js → tickets-DCkFSiqt.js} +1 -1
- package/client/dist/assets/{tickets-DQp7JS2R.js → tickets-Djn3ccfT.js} +1 -1
- package/client/dist/assets/{tickets-Bs37scwY.js → tickets-MiHBPsy_.js} +1 -1
- package/client/dist/assets/{useBrowserCaptureSession-CkBJShUw.js → useBrowserCaptureSession-CkVCDzer.js} +1 -1
- package/client/dist/assets/{useDesktop-B4pSBIDd.js → useDesktop-DCP_MHp9.js} +1 -1
- package/client/dist/index.html +25 -24
- package/docs/README.md +7 -3
- package/docs/cli.md +3 -3
- package/docs/codex.md +51 -64
- package/docs/creating-specs.md +36 -4
- package/docs/customizing.md +5 -5
- package/docs/gemini.md +35 -47
- package/docs/getting-started.md +15 -9
- package/docs/guide/de/agents/2-profiles-and-the-balanced-default.md +5 -1
- package/docs/guide/de/agents/4-custom-agents-catalog.md +7 -4
- package/docs/guide/de/getting-started/1-what-is-specrails.md +1 -0
- package/docs/guide/de/getting-started/2-installing-and-first-run.md +1 -0
- package/docs/guide/de/getting-started/4-the-dashboard-tour.md +4 -4
- package/docs/guide/de/integrations/1-ai-providers.md +29 -18
- package/docs/guide/de/integrations/2-plugins.md +3 -3
- package/docs/guide/de/integrations/6-agent-chat.md +8 -1
- package/docs/guide/de/pipeline/1-rails-and-jobs.md +9 -6
- package/docs/guide/de/pipeline/2-the-job-detail-view.md +3 -1
- package/docs/guide/de/pipeline/3-batch-implement-and-multi-feature.md +1 -1
- package/docs/guide/de/pipeline/4-picking-an-engine-per-rail.md +13 -13
- package/docs/guide/de/pipeline/5-the-loop-builder.md +6 -2
- package/docs/guide/de/specs/2-add-spec-quick-mode.md +6 -1
- package/docs/guide/en/agents/2-profiles-and-the-balanced-default.md +7 -1
- package/docs/guide/en/agents/4-custom-agents-catalog.md +17 -4
- package/docs/guide/en/getting-started/1-what-is-specrails.md +2 -1
- package/docs/guide/en/getting-started/2-installing-and-first-run.md +2 -1
- package/docs/guide/en/getting-started/4-the-dashboard-tour.md +5 -5
- package/docs/guide/en/insights/3-code-explorer.md +5 -0
- package/docs/guide/en/integrations/1-ai-providers.md +63 -22
- package/docs/guide/en/integrations/2-plugins.md +3 -3
- package/docs/guide/en/integrations/6-agent-chat.md +14 -2
- package/docs/guide/en/pipeline/1-rails-and-jobs.md +19 -7
- package/docs/guide/en/pipeline/2-the-job-detail-view.md +3 -1
- package/docs/guide/en/pipeline/3-batch-implement-and-multi-feature.md +1 -1
- package/docs/guide/en/pipeline/4-picking-an-engine-per-rail.md +25 -11
- package/docs/guide/en/pipeline/5-the-loop-builder.md +7 -2
- package/docs/guide/en/specs/2-add-spec-quick-mode.md +10 -1
- package/docs/guide/es/agents/2-profiles-and-the-balanced-default.md +5 -1
- package/docs/guide/es/agents/4-custom-agents-catalog.md +7 -4
- package/docs/guide/es/getting-started/1-what-is-specrails.md +1 -0
- package/docs/guide/es/getting-started/2-installing-and-first-run.md +1 -0
- package/docs/guide/es/getting-started/4-the-dashboard-tour.md +4 -4
- package/docs/guide/es/integrations/1-ai-providers.md +30 -18
- package/docs/guide/es/integrations/2-plugins.md +3 -3
- package/docs/guide/es/integrations/6-agent-chat.md +8 -1
- package/docs/guide/es/pipeline/1-rails-and-jobs.md +9 -6
- package/docs/guide/es/pipeline/2-the-job-detail-view.md +3 -1
- package/docs/guide/es/pipeline/3-batch-implement-and-multi-feature.md +1 -1
- package/docs/guide/es/pipeline/4-picking-an-engine-per-rail.md +15 -13
- package/docs/guide/es/pipeline/5-the-loop-builder.md +6 -2
- package/docs/guide/es/specs/2-add-spec-quick-mode.md +6 -1
- package/docs/guide/fr/agents/2-profiles-and-the-balanced-default.md +5 -1
- package/docs/guide/fr/agents/4-custom-agents-catalog.md +7 -4
- package/docs/guide/fr/getting-started/1-what-is-specrails.md +1 -0
- package/docs/guide/fr/getting-started/2-installing-and-first-run.md +1 -0
- package/docs/guide/fr/getting-started/4-the-dashboard-tour.md +4 -4
- package/docs/guide/fr/integrations/1-ai-providers.md +29 -18
- package/docs/guide/fr/integrations/2-plugins.md +3 -3
- package/docs/guide/fr/integrations/6-agent-chat.md +8 -1
- package/docs/guide/fr/pipeline/1-rails-and-jobs.md +9 -6
- package/docs/guide/fr/pipeline/2-the-job-detail-view.md +3 -1
- package/docs/guide/fr/pipeline/3-batch-implement-and-multi-feature.md +1 -1
- package/docs/guide/fr/pipeline/4-picking-an-engine-per-rail.md +15 -13
- package/docs/guide/fr/pipeline/5-the-loop-builder.md +6 -2
- package/docs/guide/fr/specs/2-add-spec-quick-mode.md +6 -1
- package/docs/guide/it/agents/2-profiles-and-the-balanced-default.md +5 -1
- package/docs/guide/it/agents/4-custom-agents-catalog.md +7 -4
- package/docs/guide/it/getting-started/1-what-is-specrails.md +1 -0
- package/docs/guide/it/getting-started/2-installing-and-first-run.md +1 -0
- package/docs/guide/it/getting-started/4-the-dashboard-tour.md +4 -4
- package/docs/guide/it/integrations/1-ai-providers.md +28 -18
- package/docs/guide/it/integrations/2-plugins.md +3 -3
- package/docs/guide/it/integrations/6-agent-chat.md +8 -1
- package/docs/guide/it/pipeline/1-rails-and-jobs.md +9 -6
- package/docs/guide/it/pipeline/2-the-job-detail-view.md +3 -1
- package/docs/guide/it/pipeline/3-batch-implement-and-multi-feature.md +1 -1
- package/docs/guide/it/pipeline/4-picking-an-engine-per-rail.md +13 -13
- package/docs/guide/it/pipeline/5-the-loop-builder.md +6 -2
- package/docs/guide/it/specs/2-add-spec-quick-mode.md +6 -1
- package/docs/guide/ja/agents/2-profiles-and-the-balanced-default.md +6 -1
- package/docs/guide/ja/agents/4-custom-agents-catalog.md +7 -4
- package/docs/guide/ja/getting-started/1-what-is-specrails.md +1 -0
- package/docs/guide/ja/getting-started/2-installing-and-first-run.md +1 -0
- package/docs/guide/ja/getting-started/4-the-dashboard-tour.md +4 -4
- package/docs/guide/ja/integrations/1-ai-providers.md +28 -18
- package/docs/guide/ja/integrations/2-plugins.md +3 -3
- package/docs/guide/ja/integrations/6-agent-chat.md +7 -1
- package/docs/guide/ja/pipeline/1-rails-and-jobs.md +9 -6
- package/docs/guide/ja/pipeline/2-the-job-detail-view.md +3 -1
- package/docs/guide/ja/pipeline/3-batch-implement-and-multi-feature.md +1 -1
- package/docs/guide/ja/pipeline/4-picking-an-engine-per-rail.md +14 -13
- package/docs/guide/ja/pipeline/5-the-loop-builder.md +6 -2
- package/docs/guide/ja/specs/2-add-spec-quick-mode.md +6 -1
- package/docs/guide/pt/agents/2-profiles-and-the-balanced-default.md +5 -1
- package/docs/guide/pt/agents/4-custom-agents-catalog.md +7 -4
- package/docs/guide/pt/getting-started/1-what-is-specrails.md +1 -0
- package/docs/guide/pt/getting-started/2-installing-and-first-run.md +1 -0
- package/docs/guide/pt/getting-started/4-the-dashboard-tour.md +4 -4
- package/docs/guide/pt/integrations/1-ai-providers.md +28 -18
- package/docs/guide/pt/integrations/2-plugins.md +3 -3
- package/docs/guide/pt/integrations/6-agent-chat.md +7 -1
- package/docs/guide/pt/pipeline/1-rails-and-jobs.md +9 -6
- package/docs/guide/pt/pipeline/2-the-job-detail-view.md +2 -1
- package/docs/guide/pt/pipeline/3-batch-implement-and-multi-feature.md +1 -1
- package/docs/guide/pt/pipeline/4-picking-an-engine-per-rail.md +14 -13
- package/docs/guide/pt/pipeline/5-the-loop-builder.md +6 -2
- package/docs/guide/pt/specs/2-add-spec-quick-mode.md +6 -1
- package/docs/guide/zh/agents/2-profiles-and-the-balanced-default.md +4 -1
- package/docs/guide/zh/agents/4-custom-agents-catalog.md +7 -4
- package/docs/guide/zh/getting-started/1-what-is-specrails.md +1 -0
- package/docs/guide/zh/getting-started/2-installing-and-first-run.md +1 -0
- package/docs/guide/zh/getting-started/4-the-dashboard-tour.md +4 -4
- package/docs/guide/zh/integrations/1-ai-providers.md +26 -18
- package/docs/guide/zh/integrations/2-plugins.md +3 -3
- package/docs/guide/zh/integrations/6-agent-chat.md +6 -1
- package/docs/guide/zh/pipeline/1-rails-and-jobs.md +9 -6
- package/docs/guide/zh/pipeline/2-the-job-detail-view.md +2 -1
- package/docs/guide/zh/pipeline/3-batch-implement-and-multi-feature.md +1 -1
- package/docs/guide/zh/pipeline/4-picking-an-engine-per-rail.md +12 -12
- package/docs/guide/zh/pipeline/5-the-loop-builder.md +6 -2
- package/docs/guide/zh/specs/2-add-spec-quick-mode.md +6 -1
- package/docs/internals/README.md +4 -1
- package/docs/internals/adding-a-provider.md +32 -28
- package/docs/internals/api-reference.md +37 -13
- package/docs/internals/architecture.md +5 -5
- package/docs/internals/bundled-framework-build-plan.md +3 -3
- package/docs/internals/code-explorer-story.md +4 -0
- package/docs/internals/configuration.md +17 -5
- package/docs/internals/global-artifacts-alignment-contract.md +1 -1
- package/docs/internals/operations-runbook.md +1 -1
- package/docs/internals/profiles.md +37 -45
- package/docs/internals/project-builder.md +16 -0
- package/docs/kimi.md +291 -0
- package/docs/mcp.md +9 -2
- package/docs/platforms/macos.md +17 -7
- package/docs/platforms/windows.md +9 -4
- package/docs/running-pipelines.md +46 -12
- package/docs/terminal.md +1 -1
- package/docs/tracking-cost.md +25 -6
- package/package.json +4 -2
- package/server/dist/agent-chat-manager.js +74 -32
- package/server/dist/agent-chat-router.js +40 -17
- package/server/dist/agent-cwd-manager.js +14 -7
- package/server/dist/agent-generator.js +69 -85
- package/server/dist/agent-mcp-config.js +17 -11
- package/server/dist/agent-operator-prompt.js +29 -20
- package/server/dist/agent-refine-manager.js +58 -38
- package/server/dist/ai-invocations.js +65 -20
- package/server/dist/attachment-manager.js +49 -15
- package/server/dist/blueprint-chat-manager.js +41 -16
- package/server/dist/blueprint-router.js +40 -6
- package/server/dist/chat-manager.js +165 -89
- package/server/dist/code-explorer-router.js +26 -7
- package/server/dist/contract-refine-runner.js +146 -113
- package/server/dist/core-compat.js +57 -2
- package/server/dist/core-package.js +3 -3
- package/server/dist/core-update-manager.js +17 -1
- package/server/dist/db.js +60 -5
- package/server/dist/desktop-analytics.js +44 -6
- package/server/dist/desktop-router.js +127 -11
- package/server/dist/explore-cwd-manager.js +19 -3
- package/server/dist/feature-flags.js +1 -1
- package/server/dist/file-story-manager.js +3 -3
- package/server/dist/file-summary-generator.js +21 -15
- package/server/dist/file-summary-manager.js +6 -6
- package/server/dist/framework-manager.js +19 -2
- package/server/dist/framework-migration.js +27 -12
- package/server/dist/install-config-path.js +1 -1
- package/server/dist/interactive-job-session.js +8 -6
- package/server/dist/loop-command-catalog.js +27 -10
- package/server/dist/loop-executors.js +59 -27
- package/server/dist/loop-factory.js +2 -2
- package/server/dist/loop-run-manager.js +100 -39
- package/server/dist/loops-router.js +1 -1
- package/server/dist/mcp/guide.js +23 -16
- package/server/dist/mcp/tools/agents.js +47 -41
- package/server/dist/mcp/tools/analytics.js +2 -2
- package/server/dist/mcp/tools/loops.js +2 -2
- package/server/dist/mcp/tools/plugins.js +14 -9
- package/server/dist/mcp/tools/rails.js +12 -13
- package/server/dist/mcp/tools/support.js +1 -1
- package/server/dist/mobile/mobile-router.js +5 -1
- package/server/dist/offline-assemble.js +13 -1
- package/server/dist/path-resolver.js +2 -2
- package/server/dist/plugin-manager.js +391 -106
- package/server/dist/plugins/claude-md-mutation.js +5 -4
- package/server/dist/plugins/drift.js +32 -29
- package/server/dist/plugins/rail-integration.js +13 -2
- package/server/dist/plugins/serena/install.js +25 -17
- package/server/dist/plugins/serena/manifest.js +22 -5
- package/server/dist/plugins/serena/verify.js +46 -16
- package/server/dist/plugins-router.js +59 -8
- package/server/dist/profile-manager.js +148 -36
- package/server/dist/profiles-router.js +311 -82
- package/server/dist/project-registry.js +2 -2
- package/server/dist/project-router-chat.js +17 -8
- package/server/dist/project-router-helpers.js +93 -5
- package/server/dist/project-router-jobs.js +76 -12
- package/server/dist/project-router-loop-runs.js +42 -14
- package/server/dist/project-router-settings.js +31 -10
- package/server/dist/project-router-setup.js +4 -5
- package/server/dist/project-router-spending.js +15 -3
- package/server/dist/project-router-tickets.js +187 -181
- package/server/dist/project-router.js +1 -0
- package/server/dist/proposal-manager.js +125 -85
- package/server/dist/providers/claude-adapter.js +14 -0
- package/server/dist/providers/codex-adapter.js +20 -0
- package/server/dist/providers/gemini-adapter.js +14 -0
- package/server/dist/providers/index.js +18 -1
- package/server/dist/providers/kimi-adapter.js +361 -0
- package/server/dist/providers/kimi-skill-prompt.js +332 -0
- package/server/dist/providers/runtime.js +137 -0
- package/server/dist/queue-manager.js +103 -78
- package/server/dist/rail-isolated-launch.js +1 -1
- package/server/dist/rails-router.js +51 -38
- package/server/dist/result-event.js +5 -0
- package/server/dist/schemas/profile.v1.json +3 -4
- package/server/dist/setup-manager.js +222 -101
- package/server/dist/setup-prerequisites.js +7 -1
- package/server/dist/smash-runner.js +104 -57
- package/server/dist/spawn-lifecycle.js +18 -14
- package/server/dist/spec-launcher-manager.js +66 -54
- package/server/dist/spec-models.js +15 -6
- package/server/dist/spending.js +110 -17
- package/server/dist/util/cli-prompt.js +142 -5
- package/server/dist/util/stream-display.js +46 -0
- package/server/dist/workspace-manager.js +100 -37
- package/server/dist/worktree-manager.js +4 -0
- package/server/dist/worktree-overlay.js +12 -3
- package/client/dist/assets/AgentModeCodePane-BAYyFRmY.js +0 -2
- package/client/dist/assets/AgentsPage-DuyJuaFs.js +0 -86
- package/client/dist/assets/AnalyticsPage-BY7gtCx6.js +0 -1
- package/client/dist/assets/CodePage-vyQr40Vl.js +0 -2
- package/client/dist/assets/JobDetailPage-CU4FEnUC.js +0 -1
- package/client/dist/assets/LoopsPage-SqglnXlp.js +0 -1
- package/client/dist/assets/PluginsPage-B89S8o1a.js +0 -2
- package/client/dist/assets/TicketDetailModal-CI47Dq9E.js +0 -14
- package/client/dist/assets/addspec-BXjvkh5q.js +0 -1
- package/client/dist/assets/addspec-C4bwrmpi.js +0 -1
- package/client/dist/assets/addspec-CArHXUDM.js +0 -1
- package/client/dist/assets/addspec-DTKJcXiR.js +0 -1
- package/client/dist/assets/addspec-DbNBbrha.js +0 -1
- package/client/dist/assets/addspec-Rw8uVBC1.js +0 -1
- package/client/dist/assets/addspec-htHGZHOe.js +0 -1
- package/client/dist/assets/addspec-lr_LP6jk.js +0 -1
- package/client/dist/assets/agent-BABjRt0P.js +0 -1
- package/client/dist/assets/agent-BuUpgqVx.js +0 -1
- package/client/dist/assets/agent-CCDZesgU.js +0 -1
- package/client/dist/assets/agent-CVn64zWH.js +0 -1
- package/client/dist/assets/agent-CWeOny0E.js +0 -1
- package/client/dist/assets/agent-DOysmad_.js +0 -1
- package/client/dist/assets/agent-DSsjTBhn.js +0 -1
- package/client/dist/assets/agent-DlZMxfM9.js +0 -1
- package/client/dist/assets/agents-6ZOJXWVk.js +0 -1
- package/client/dist/assets/agents-BZ0XQKjg.js +0 -1
- package/client/dist/assets/agents-BqWklnHR.js +0 -1
- package/client/dist/assets/agents-Bz-thHkB.js +0 -1
- package/client/dist/assets/agents-CJclElx6.js +0 -1
- package/client/dist/assets/agents-D_l9L0VA.js +0 -1
- package/client/dist/assets/agents-DhG7mg6-.js +0 -1
- package/client/dist/assets/agents-u_C0jPB5.js +0 -1
- package/client/dist/assets/agentstudio-BF19hbXj.js +0 -1
- package/client/dist/assets/agentstudio-BIUe1Im2.js +0 -1
- package/client/dist/assets/agentstudio-BLGvxWBx.js +0 -1
- package/client/dist/assets/agentstudio-D9UKiWTj.js +0 -1
- package/client/dist/assets/agentstudio-Drs37nIM.js +0 -1
- package/client/dist/assets/agentstudio-nEnKkcXM.js +0 -1
- package/client/dist/assets/agentstudio-pyFcSBKy.js +0 -1
- package/client/dist/assets/agentstudio-zfTzQh3U.js +0 -1
- package/client/dist/assets/analytics-BC7mwR4s.js +0 -1
- package/client/dist/assets/analytics-BiNg8edP.js +0 -1
- package/client/dist/assets/analytics-C0s_T2GN.js +0 -1
- package/client/dist/assets/analytics-CdsAPAwZ.js +0 -1
- package/client/dist/assets/analytics-Cr1_q-lC.js +0 -1
- package/client/dist/assets/analytics-DZ8Aoq3I.js +0 -1
- package/client/dist/assets/analytics-IEsKZvpW.js +0 -1
- package/client/dist/assets/analytics-SRPf84OT.js +0 -1
- package/client/dist/assets/builder-BH8Fua-g.js +0 -1
- package/client/dist/assets/builder-BoV7DgKd.js +0 -1
- package/client/dist/assets/builder-Cc_Lf79C.js +0 -1
- package/client/dist/assets/builder-CwZ7Bme2.js +0 -1
- package/client/dist/assets/builder-CxW-JFsR.js +0 -1
- package/client/dist/assets/builder-D94lrV7x.js +0 -1
- package/client/dist/assets/builder-cqHpCyGa.js +0 -1
- package/client/dist/assets/code-76gS0IdL.js +0 -1
- package/client/dist/assets/code-BDXEM4ml.js +0 -1
- package/client/dist/assets/code-C1VwxlML.js +0 -1
- package/client/dist/assets/code-CnMZyJpr.js +0 -1
- package/client/dist/assets/code-D20ef9Cf.js +0 -1
- package/client/dist/assets/code-DdOXaWRY.js +0 -1
- package/client/dist/assets/code-Duohuf9l.js +0 -1
- package/client/dist/assets/code-geHmknB9.js +0 -1
- package/client/dist/assets/format-command-Dfa1o2d-.js +0 -1
- package/client/dist/assets/index-Dh9i4CnN.css +0 -2
- package/client/dist/assets/index-F9r8mWP_.js +0 -76
- package/client/dist/assets/jobs-BCtGfgUA.js +0 -1
- package/client/dist/assets/jobs-BHR_EnDI.js +0 -1
- package/client/dist/assets/jobs-BMbZJjJJ.js +0 -1
- package/client/dist/assets/jobs-DzSIR8V6.js +0 -1
- package/client/dist/assets/nav-BRJBxaGt.js +0 -1
- package/client/dist/assets/nav-CAGKv3_F.js +0 -1
- package/client/dist/assets/nav-CNskRJKQ.js +0 -1
- package/client/dist/assets/nav-DY93irBL.js +0 -1
- package/client/dist/assets/nav-DhjvYgqt.js +0 -1
- package/client/dist/assets/nav-Gb-r4wop.js +0 -1
- package/client/dist/assets/nav-TvumAFD2.js +0 -1
- package/client/dist/assets/nav-myFaXRHD.js +0 -1
- package/client/dist/assets/settings-B0ypw9uw.js +0 -1
- package/client/dist/assets/settings-B9ilOSYO.js +0 -1
- package/client/dist/assets/settings-BqlWHRHA.js +0 -1
- package/client/dist/assets/settings-D8dlqt4d.js +0 -1
- package/client/dist/assets/settings-DAZqjX2a.js +0 -1
- package/client/dist/assets/settings-DRh39v1S.js +0 -1
- package/client/dist/assets/settings-Mj1OJbfd.js +0 -1
- package/client/dist/assets/settings-lRReaSYm.js +0 -1
- package/client/dist/assets/setup-3832BJCo.js +0 -1
- package/client/dist/assets/setup-B8J-gECe.js +0 -1
- package/client/dist/assets/setup-BCIOxVsc.js +0 -1
- package/client/dist/assets/setup-BciLj_tU.js +0 -1
- package/client/dist/assets/setup-CTkE3cfh.js +0 -1
- package/client/dist/assets/setup-ajB4rYm-.js +0 -1
- package/client/dist/assets/setup-xFO1wNl0.js +0 -1
|
@@ -59,9 +59,39 @@ exports.ProfileConflictError = ProfileConflictError;
|
|
|
59
59
|
function profilesDir(projectPath) {
|
|
60
60
|
return path_1.default.join(projectPath, '.specrails', 'profiles');
|
|
61
61
|
}
|
|
62
|
-
function
|
|
62
|
+
function legacyProfilePath(projectPath, name) {
|
|
63
63
|
return path_1.default.join(profilesDir(projectPath), `${name}.json`);
|
|
64
64
|
}
|
|
65
|
+
/** Provider-scoped filenames allow e.g. Claude and Kimi to both own a logical
|
|
66
|
+
* `default` profile. The JSON body keeps the user-facing name unchanged. */
|
|
67
|
+
function scopedProfilePath(projectPath, name, provider) {
|
|
68
|
+
return path_1.default.join(profilesDir(projectPath), `${provider}--${name}.json`);
|
|
69
|
+
}
|
|
70
|
+
function profileCandidates(projectPath, name, provider) {
|
|
71
|
+
return [
|
|
72
|
+
scopedProfilePath(projectPath, name, provider),
|
|
73
|
+
legacyProfilePath(projectPath, name),
|
|
74
|
+
];
|
|
75
|
+
}
|
|
76
|
+
function existingProfilePath(projectPath, name, provider) {
|
|
77
|
+
const [scoped, legacy] = profileCandidates(projectPath, name, provider);
|
|
78
|
+
if (fs_1.default.existsSync(scoped))
|
|
79
|
+
return scoped;
|
|
80
|
+
if (!fs_1.default.existsSync(legacy))
|
|
81
|
+
return null;
|
|
82
|
+
try {
|
|
83
|
+
const raw = JSON.parse(fs_1.default.readFileSync(legacy, 'utf8'));
|
|
84
|
+
// Historical unscoped profiles predate multi-provider support and belong
|
|
85
|
+
// to Claude unless they explicitly declare another provider.
|
|
86
|
+
return typeof raw.provider === 'string'
|
|
87
|
+
? raw.provider === provider ? legacy : null
|
|
88
|
+
: provider === 'claude' || name === `${provider}-default` ? legacy : null;
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// Preserve the editor's ability to surface malformed legacy Claude JSON.
|
|
92
|
+
return provider === 'claude' ? legacy : null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
65
95
|
function jobSnapshotPath(slug, jobId) {
|
|
66
96
|
return path_1.default.join(os_1.default.homedir(), '.specrails', 'projects', slug, 'jobs', jobId, 'profile.json');
|
|
67
97
|
}
|
|
@@ -84,8 +114,13 @@ function assertValidName(name) {
|
|
|
84
114
|
* profile itself omits the `provider` field. Defaults to
|
|
85
115
|
* `'claude'` so legacy callsites stay backwards compatible.
|
|
86
116
|
*/
|
|
87
|
-
function validateStructural(profile, expectedProvider
|
|
88
|
-
const providerId = profile.provider ?? expectedProvider;
|
|
117
|
+
function validateStructural(profile, expectedProvider) {
|
|
118
|
+
const providerId = profile.provider ?? expectedProvider ?? 'claude';
|
|
119
|
+
if (profile.provider && expectedProvider && profile.provider !== expectedProvider) {
|
|
120
|
+
throw new ProfileValidationError([
|
|
121
|
+
`profile provider '${profile.provider}' does not match requested provider '${expectedProvider}'`,
|
|
122
|
+
]);
|
|
123
|
+
}
|
|
89
124
|
if (!(0, providers_1.hasAdapter)(providerId)) {
|
|
90
125
|
throw new ProfileValidationError([
|
|
91
126
|
`profile references unknown provider '${providerId}'`,
|
|
@@ -104,15 +139,17 @@ function validateStructural(profile, expectedProvider = 'claude') {
|
|
|
104
139
|
`profile must include baseline agents for provider '${providerId}': missing ${missing.join(', ')}`,
|
|
105
140
|
]);
|
|
106
141
|
}
|
|
107
|
-
// Orchestrator model must be in the adapter
|
|
108
|
-
|
|
142
|
+
// Orchestrator model must be in the adapter catalog, or a safe configured
|
|
143
|
+
// alias when the adapter explicitly supports them.
|
|
144
|
+
if (!(0, providers_1.isModelAvailableForAdapter)(adapter, profile.orchestrator.model)) {
|
|
109
145
|
throw new ProfileValidationError([
|
|
110
146
|
`orchestrator.model '${profile.orchestrator.model}' is not valid for provider '${providerId}'. Valid models: ${[...validModels].join(', ')}`,
|
|
111
147
|
]);
|
|
112
148
|
}
|
|
113
149
|
// Per-agent model (when set) must also be in the catalog.
|
|
114
150
|
for (const agent of profile.agents) {
|
|
115
|
-
if (agent.model !== undefined
|
|
151
|
+
if (agent.model !== undefined
|
|
152
|
+
&& !(0, providers_1.isModelAvailableForAdapter)(adapter, agent.model)) {
|
|
116
153
|
throw new ProfileValidationError([
|
|
117
154
|
`agent '${agent.id}' uses model '${agent.model}' which is not valid for provider '${providerId}'. Valid models: ${[...validModels].join(', ')}`,
|
|
118
155
|
]);
|
|
@@ -149,7 +186,7 @@ function validateStructural(profile, expectedProvider = 'claude') {
|
|
|
149
186
|
}
|
|
150
187
|
}
|
|
151
188
|
}
|
|
152
|
-
function validateProfile(raw, expectedProvider
|
|
189
|
+
function validateProfile(raw, expectedProvider) {
|
|
153
190
|
const validate = getValidator();
|
|
154
191
|
if (!validate(raw)) {
|
|
155
192
|
const msgs = (validate.errors || []).map((e) => `${e.instancePath || '/'} ${e.message} (${JSON.stringify(e.params)})`);
|
|
@@ -160,11 +197,11 @@ function validateProfile(raw, expectedProvider = 'claude') {
|
|
|
160
197
|
return profile;
|
|
161
198
|
}
|
|
162
199
|
// ─── Public API ──────────────────────────────────────────────────────────────
|
|
163
|
-
function listProfiles(projectPath) {
|
|
200
|
+
function listProfiles(projectPath, _fallbackProvider = 'claude') {
|
|
164
201
|
const dir = profilesDir(projectPath);
|
|
165
202
|
if (!fs_1.default.existsSync(dir))
|
|
166
203
|
return [];
|
|
167
|
-
const entries =
|
|
204
|
+
const entries = new Map();
|
|
168
205
|
for (const file of fs_1.default.readdirSync(dir)) {
|
|
169
206
|
if (!file.endsWith('.json'))
|
|
170
207
|
continue;
|
|
@@ -172,30 +209,57 @@ function listProfiles(projectPath) {
|
|
|
172
209
|
continue;
|
|
173
210
|
if (file.startsWith('.'))
|
|
174
211
|
continue;
|
|
175
|
-
const name = file.slice(0, -'.json'.length);
|
|
176
212
|
const full = path_1.default.join(dir, file);
|
|
177
213
|
try {
|
|
178
214
|
const raw = JSON.parse(fs_1.default.readFileSync(full, 'utf8'));
|
|
179
215
|
const stat = fs_1.default.statSync(full);
|
|
180
|
-
|
|
216
|
+
const name = typeof raw?.name === 'string'
|
|
217
|
+
? raw.name
|
|
218
|
+
: file.slice(0, -'.json'.length);
|
|
219
|
+
const inferredDefaultProvider = typeof name === 'string'
|
|
220
|
+
? name.match(/^([a-z0-9][a-z0-9-]*)-default$/)?.[1]
|
|
221
|
+
: undefined;
|
|
222
|
+
const provider = typeof raw?.provider === 'string'
|
|
223
|
+
? raw.provider
|
|
224
|
+
: inferredDefaultProvider && (0, providers_1.hasAdapter)(inferredDefaultProvider)
|
|
225
|
+
? inferredDefaultProvider
|
|
226
|
+
// Unscoped/provider-less profiles predate multi-provider storage and
|
|
227
|
+
// are Claude profiles regardless of which provider is primary today.
|
|
228
|
+
// Provider defaults such as `kimi-default.json` are handled by the
|
|
229
|
+
// filename inference above. Keeping this aligned with
|
|
230
|
+
// existingProfilePath prevents a Kimi-primary project from listing a
|
|
231
|
+
// legacy Claude `project-default` as Kimi and then returning 404 when
|
|
232
|
+
// it is opened.
|
|
233
|
+
: 'claude';
|
|
234
|
+
const scoped = file === `${provider}--${name}.json`;
|
|
235
|
+
const key = `${provider}\0${name}`;
|
|
236
|
+
const candidate = {
|
|
181
237
|
name,
|
|
182
238
|
description: typeof raw?.description === 'string' ? raw.description : undefined,
|
|
183
|
-
|
|
239
|
+
provider,
|
|
240
|
+
isDefault: name === 'default'
|
|
241
|
+
|| name === 'project-default'
|
|
242
|
+
|| name === `${provider}-default`,
|
|
184
243
|
updatedAt: Math.floor(stat.mtimeMs),
|
|
185
|
-
|
|
244
|
+
scoped,
|
|
245
|
+
};
|
|
246
|
+
// Prefer the provider-scoped file over a stale legacy duplicate.
|
|
247
|
+
if (!entries.has(key) || scoped)
|
|
248
|
+
entries.set(key, candidate);
|
|
186
249
|
}
|
|
187
250
|
catch {
|
|
188
251
|
// Skip unparseable files silently; the caller can surface via getProfile.
|
|
189
252
|
}
|
|
190
253
|
}
|
|
191
|
-
return entries.
|
|
254
|
+
return [...entries.values()]
|
|
255
|
+
.map(({ scoped: _scoped, ...entry }) => entry)
|
|
256
|
+
.sort((a, b) => a.name.localeCompare(b.name) || a.provider.localeCompare(b.provider));
|
|
192
257
|
}
|
|
193
258
|
function getProfile(projectPath, name, expectedProvider = 'claude') {
|
|
194
259
|
assertValidName(name);
|
|
195
|
-
const full =
|
|
196
|
-
if (!
|
|
260
|
+
const full = existingProfilePath(projectPath, name, expectedProvider);
|
|
261
|
+
if (!full)
|
|
197
262
|
throw new ProfileNotFoundError(name);
|
|
198
|
-
}
|
|
199
263
|
const raw = JSON.parse(fs_1.default.readFileSync(full, 'utf8'));
|
|
200
264
|
return validateProfile(raw, expectedProvider);
|
|
201
265
|
}
|
|
@@ -209,10 +273,9 @@ function getProfile(projectPath, name, expectedProvider = 'claude') {
|
|
|
209
273
|
*/
|
|
210
274
|
function getProfileRaw(projectPath, name, expectedProvider = 'claude') {
|
|
211
275
|
assertValidName(name);
|
|
212
|
-
const full =
|
|
213
|
-
if (!
|
|
276
|
+
const full = existingProfilePath(projectPath, name, expectedProvider);
|
|
277
|
+
if (!full)
|
|
214
278
|
throw new ProfileNotFoundError(name);
|
|
215
|
-
}
|
|
216
279
|
const raw = JSON.parse(fs_1.default.readFileSync(full, 'utf8'));
|
|
217
280
|
try {
|
|
218
281
|
validateProfile(raw, expectedProvider);
|
|
@@ -226,8 +289,16 @@ function getProfileRaw(projectPath, name, expectedProvider = 'claude') {
|
|
|
226
289
|
function createProfile(projectPath, profile, expectedProvider = 'claude') {
|
|
227
290
|
assertValidName(profile.name);
|
|
228
291
|
validateProfile(profile, expectedProvider);
|
|
229
|
-
const
|
|
230
|
-
|
|
292
|
+
const provider = profile.provider ?? expectedProvider;
|
|
293
|
+
const full = profile.provider || expectedProvider !== 'claude'
|
|
294
|
+
? scopedProfilePath(projectPath, profile.name, provider)
|
|
295
|
+
: legacyProfilePath(projectPath, profile.name);
|
|
296
|
+
// Core may already own a provider-specific default in the historical
|
|
297
|
+
// unscoped shape (for example `kimi-default.json`). Treat that as the same
|
|
298
|
+
// logical profile as Desktop's collision-safe `kimi--kimi-default.json`;
|
|
299
|
+
// otherwise POST would silently create two defaults. `updateProfile` remains
|
|
300
|
+
// the explicit migration path from legacy to scoped storage.
|
|
301
|
+
if (existingProfilePath(projectPath, profile.name, provider)) {
|
|
231
302
|
throw new ProfileConflictError(profile.name);
|
|
232
303
|
}
|
|
233
304
|
fs_1.default.mkdirSync(profilesDir(projectPath), { recursive: true });
|
|
@@ -236,21 +307,29 @@ function createProfile(projectPath, profile, expectedProvider = 'claude') {
|
|
|
236
307
|
function updateProfile(projectPath, profile, expectedProvider = 'claude') {
|
|
237
308
|
assertValidName(profile.name);
|
|
238
309
|
validateProfile(profile, expectedProvider);
|
|
239
|
-
const
|
|
240
|
-
|
|
310
|
+
const provider = profile.provider ?? expectedProvider;
|
|
311
|
+
const existing = existingProfilePath(projectPath, profile.name, provider);
|
|
312
|
+
if (!existing)
|
|
241
313
|
throw new ProfileNotFoundError(profile.name);
|
|
242
|
-
|
|
243
|
-
|
|
314
|
+
const destination = profile.provider || expectedProvider !== 'claude'
|
|
315
|
+
? scopedProfilePath(projectPath, profile.name, provider)
|
|
316
|
+
: existing;
|
|
317
|
+
fs_1.default.writeFileSync(destination, JSON.stringify(profile, null, 2) + '\n', 'utf8');
|
|
318
|
+
// Updating a legacy provider-stamped file migrates it to the collision-safe
|
|
319
|
+
// scoped filename after the replacement is durably visible.
|
|
320
|
+
if (destination !== existing && fs_1.default.existsSync(existing))
|
|
321
|
+
fs_1.default.unlinkSync(existing);
|
|
244
322
|
}
|
|
245
|
-
function deleteProfile(projectPath, name) {
|
|
323
|
+
function deleteProfile(projectPath, name, expectedProvider = 'claude') {
|
|
246
324
|
assertValidName(name);
|
|
247
|
-
if (name === 'default'
|
|
325
|
+
if (name === 'default'
|
|
326
|
+
|| name === 'project-default'
|
|
327
|
+
|| name === `${expectedProvider}-default`) {
|
|
248
328
|
throw new ProfileValidationError(['cannot delete the default profile']);
|
|
249
329
|
}
|
|
250
|
-
const full =
|
|
251
|
-
if (!
|
|
330
|
+
const full = existingProfilePath(projectPath, name, expectedProvider);
|
|
331
|
+
if (!full)
|
|
252
332
|
throw new ProfileNotFoundError(name);
|
|
253
|
-
}
|
|
254
333
|
fs_1.default.unlinkSync(full);
|
|
255
334
|
}
|
|
256
335
|
function duplicateProfile(projectPath, sourceName, newName, expectedProvider = 'claude') {
|
|
@@ -262,12 +341,29 @@ function duplicateProfile(projectPath, sourceName, newName, expectedProvider = '
|
|
|
262
341
|
function renameProfile(projectPath, fromName, toName, expectedProvider = 'claude') {
|
|
263
342
|
// Renaming the default away would leave no default → resolveProfile returns
|
|
264
343
|
// null and rails silently drop to legacy/no-profile mode. Mirror deleteProfile.
|
|
265
|
-
if (fromName === 'default'
|
|
344
|
+
if (fromName === 'default'
|
|
345
|
+
|| fromName === 'project-default'
|
|
346
|
+
|| fromName === `${expectedProvider}-default`) {
|
|
266
347
|
throw new ProfileValidationError(['cannot rename the default profile']);
|
|
267
348
|
}
|
|
268
349
|
const source = getProfile(projectPath, fromName, expectedProvider);
|
|
269
350
|
assertValidName(toName);
|
|
270
|
-
if (
|
|
351
|
+
if (toName === 'default'
|
|
352
|
+
|| toName === 'project-default'
|
|
353
|
+
|| toName === `${expectedProvider}-default`) {
|
|
354
|
+
throw new ProfileValidationError(['cannot rename a profile to a reserved default name']);
|
|
355
|
+
}
|
|
356
|
+
const provider = source.provider ?? expectedProvider;
|
|
357
|
+
const sourcePath = existingProfilePath(projectPath, fromName, provider);
|
|
358
|
+
if (!sourcePath)
|
|
359
|
+
throw new ProfileNotFoundError(fromName);
|
|
360
|
+
const dest = source.provider || expectedProvider !== 'claude'
|
|
361
|
+
? scopedProfilePath(projectPath, toName, provider)
|
|
362
|
+
: legacyProfilePath(projectPath, toName);
|
|
363
|
+
// Collision is logical, not just destination-path based: a Core-authored
|
|
364
|
+
// legacy `<name>.json` may already represent this provider even when Desktop
|
|
365
|
+
// would publish the renamed profile as `<provider>--<name>.json`.
|
|
366
|
+
if (existingProfilePath(projectPath, toName, provider)) {
|
|
271
367
|
throw new ProfileConflictError(toName);
|
|
272
368
|
}
|
|
273
369
|
const renamed = { ...source, name: toName };
|
|
@@ -275,12 +371,11 @@ function renameProfile(projectPath, fromName, toName, expectedProvider = 'claude
|
|
|
275
371
|
// Atomic publish: write to a temp file in the same dir, fsync-rename into
|
|
276
372
|
// place, THEN remove the old file. A crash/unlink failure can no longer
|
|
277
373
|
// leave both files on disk (which listProfiles would surface as duplicates).
|
|
278
|
-
const dest = profilePath(projectPath, toName);
|
|
279
374
|
const tmp = `${dest}.tmp-${process.pid}`;
|
|
280
375
|
fs_1.default.writeFileSync(tmp, JSON.stringify(renamed, null, 2) + '\n', 'utf8');
|
|
281
376
|
fs_1.default.renameSync(tmp, dest);
|
|
282
377
|
try {
|
|
283
|
-
fs_1.default.unlinkSync(
|
|
378
|
+
fs_1.default.unlinkSync(sourcePath);
|
|
284
379
|
}
|
|
285
380
|
catch {
|
|
286
381
|
// The new file is fully published; a stale source is at worst a transient
|
|
@@ -315,7 +410,24 @@ function resolveProfile(projectPath, explicit, expectedProvider = 'claude') {
|
|
|
315
410
|
return resolved;
|
|
316
411
|
throw new ProfileNotFoundError(explicit);
|
|
317
412
|
}
|
|
318
|
-
|
|
413
|
+
const fallbacks = expectedProvider === 'claude'
|
|
414
|
+
? ['default', 'project-default']
|
|
415
|
+
: [`${expectedProvider}-default`, 'default', 'project-default'];
|
|
416
|
+
for (const fallback of fallbacks) {
|
|
417
|
+
try {
|
|
418
|
+
const resolved = tryName(fallback);
|
|
419
|
+
if (resolved)
|
|
420
|
+
return resolved;
|
|
421
|
+
}
|
|
422
|
+
catch (error) {
|
|
423
|
+
// A legacy unscoped default may belong to another provider. In a mixed
|
|
424
|
+
// project it must not prevent the selected provider from falling through
|
|
425
|
+
// to its own alternate default (or legacy mode).
|
|
426
|
+
if (!(error instanceof ProfileValidationError))
|
|
427
|
+
throw error;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return null;
|
|
319
431
|
}
|
|
320
432
|
// ─── Snapshot-per-job ────────────────────────────────────────────────────────
|
|
321
433
|
/**
|