specrails-desktop 2.36.0 → 2.42.2
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 +94 -247
- package/client/dist/assets/ActivityFeedPage-DoEMumQS.js +1 -0
- package/client/dist/assets/AgentBrowserCapture-DIh4K0gh.js +1 -0
- package/client/dist/assets/AgentModeAnalyticsPane-mbq4WEoO.js +2 -0
- package/client/dist/assets/AgentModeCodePane-CKh7zuA4.js +2 -0
- package/client/dist/assets/AgentModeJobsPane-BDokPiLJ.js +1 -0
- package/client/dist/assets/AgentsPage-BFVAZbBj.js +87 -0
- package/client/dist/assets/AnalyticsPage-3Y9LuWwz.js +1 -0
- package/client/dist/assets/CodePage-C9KyuV_C.js +3 -0
- package/client/dist/assets/DesktopAnalyticsPage-Bhi54bXQ.js +1 -0
- package/client/dist/assets/{DocsDialog-C76VdBJC.js → DocsDialog-Dj6mt4ga.js} +2 -2
- package/client/dist/assets/{DocsPage-ziMlj6B6.js → DocsPage-24yWglNI.js} +1 -1
- package/client/dist/assets/ExportDropdown-ophapHrT.js +1 -0
- package/client/dist/assets/InteractiveJobComposer-C1ucmxQh.js +19 -0
- package/client/dist/assets/JobDetailModal-DNWmgVHD.js +1 -0
- package/client/dist/assets/JobDetailPage-BcajZyuu.js +1 -0
- package/client/dist/assets/JobsPage-B9nTZw6e.js +1 -0
- package/client/dist/assets/LoopBuilderPage-Oc48iOFW.js +7 -0
- package/client/dist/assets/LoopPreviewModal-ae0Fr6SU.js +1 -0
- package/client/dist/assets/LoopsPage-CUEqzUBI.js +1 -0
- package/client/dist/assets/MinimizedChatsContext-Deg39peL.js +1 -0
- package/client/dist/assets/PluginsPage-C1xtARIx.js +2 -0
- package/client/dist/assets/ProjectSettingsDialog-JZzrXFBN.js +1 -0
- package/client/dist/assets/RepositoryDeliveries-CSDsiVUz.js +5 -0
- package/client/dist/assets/RepositoryScopeSelector-D-RCaknl.js +1 -0
- package/client/dist/assets/ReviewPacketPage-x5EfGTiL.js +1 -0
- package/client/dist/assets/TemplatePreviewModal-BSyuZxGq.js +1 -0
- package/client/dist/assets/TicketDetailModalContext-DqyF0so-.js +14 -0
- package/client/dist/assets/Trans-SnMX4MhW.js +1 -0
- package/client/dist/assets/agent-BdT0SB1D.js +1 -0
- package/client/dist/assets/agent-Bh9K7iEt.js +1 -0
- package/client/dist/assets/agent-C4ibdFk9.js +1 -0
- package/client/dist/assets/agent-CSFY22Zo.js +1 -0
- package/client/dist/assets/agent-DAnqxUn8.js +1 -0
- package/client/dist/assets/agent-DTQiu0Mi.js +1 -0
- package/client/dist/assets/agent-T54WuR_Y.js +1 -0
- package/client/dist/assets/agent-i75WDZOA.js +1 -0
- package/client/dist/assets/browser-05vRDg_y.js +1 -0
- package/client/dist/assets/browser-Bulb-cB1.js +1 -0
- package/client/dist/assets/browser-C7f-Jsg1.js +1 -0
- package/client/dist/assets/browser-CG0PE1j_.js +1 -0
- package/client/dist/assets/browser-CMzaqAjo.js +1 -0
- package/client/dist/assets/browser-DMajYdNX.js +1 -0
- package/client/dist/assets/browser-DxcOHrdP.js +1 -0
- package/client/dist/assets/browser-ObE7xVo3.js +1 -0
- package/client/dist/assets/builder-765NeYTp.js +1 -0
- package/client/dist/assets/builder-8MG3V6ea.js +1 -0
- package/client/dist/assets/builder-B-1UBs5K.js +1 -0
- package/client/dist/assets/builder-BTiGnPT0.js +1 -0
- package/client/dist/assets/builder-Cx9Rz-7u.js +1 -0
- package/client/dist/assets/builder-DKFMELBl.js +1 -0
- package/client/dist/assets/builder-OcmKrC0h.js +1 -0
- package/client/dist/assets/builder-P9qVBXFZ.js +1 -0
- package/client/dist/assets/{button-CJqFnUOL.js → button-DCBqEaZl.js} +1 -1
- package/client/dist/assets/{code-CkUuW4m9.js → code-B37GRg9C.js} +1 -1
- package/client/dist/assets/{code-B0O8-p8k.js → code-BBOEZbf9.js} +1 -1
- package/client/dist/assets/{code-B1VLZDhS.js → code-BRm-jcyx.js} +1 -1
- package/client/dist/assets/{code-Bpj4Ddml.js → code-CF7w62CR.js} +1 -1
- package/client/dist/assets/{code-BykBZ5-e.js → code-CLuvJt2r.js} +1 -1
- package/client/dist/assets/{code-B86IMAfz.js → code-D0RCm59f.js} +1 -1
- package/client/dist/assets/code-DaZ-ZwCT.js +1 -0
- package/client/dist/assets/{code-cAqaqMTx.js → code-iIHkGaCL.js} +1 -1
- package/client/dist/assets/commands-0XjAftla.js +1 -0
- package/client/dist/assets/commands-BAWSYHjQ.js +1 -0
- package/client/dist/assets/commands-BBwowgiE.js +1 -0
- package/client/dist/assets/commands-BacjIZLN.js +1 -0
- package/client/dist/assets/commands-Bs62IAZ3.js +1 -0
- package/client/dist/assets/commands-CLPdh-Hh.js +1 -0
- package/client/dist/assets/commands-DlbFzgrp.js +1 -0
- package/client/dist/assets/commands-LjPSsi5F.js +1 -0
- package/client/dist/assets/common-B5AZNYMH.js +1 -0
- package/client/dist/assets/common-BFZ0c_vN.js +1 -0
- package/client/dist/assets/common-BrlwEnL7.js +1 -0
- package/client/dist/assets/common-BttQx3z4.js +1 -0
- package/client/dist/assets/common-CM65k_-e.js +1 -0
- package/client/dist/assets/common-D4mvFFlu.js +1 -0
- package/client/dist/assets/common-TZ9Ny96S.js +1 -0
- package/client/dist/assets/common-nFpSF4GC.js +1 -0
- package/client/dist/assets/dashboard-B14uYXkG.js +1 -0
- package/client/dist/assets/dashboard-BK_mez-z.js +1 -0
- package/client/dist/assets/dashboard-BfBir_6o.js +1 -0
- package/client/dist/assets/dashboard-Bt0cCzeG.js +1 -0
- package/client/dist/assets/dashboard-BwkjxI6H.js +1 -0
- package/client/dist/assets/{dashboard-C8WOEiPu.js → dashboard-C3bDKf7s.js} +1 -1
- package/client/dist/assets/dashboard-CdvjGfog.js +1 -0
- package/client/dist/assets/dashboard-fRrP0Kv0.js +1 -0
- package/client/dist/assets/dialog-SRw01_QU.js +45 -0
- package/client/dist/assets/dist-js-CJ_2XX1Y.js +1 -0
- package/client/dist/assets/formatDistanceToNow-DSj5DprF.js +1 -0
- package/client/dist/assets/getTimezoneOffsetInMilliseconds-YhD8rIEs.js +1 -0
- package/client/dist/assets/index-DwAmL7m5.css +2 -0
- package/client/dist/assets/index-EzrkUb8B.js +76 -0
- package/client/dist/assets/{jira-api-B1-MUehg.js → jira-api-CSCqGr-Q.js} +1 -1
- package/client/dist/assets/{jobs-CN_zv4Gr.js → jobs-B8rrzfyZ.js} +1 -1
- package/client/dist/assets/{jobs-DUlWSYq2.js → jobs-BS6RTurf.js} +1 -1
- package/client/dist/assets/{jobs-Bpks6doU.js → jobs-BTT0Bsks.js} +1 -1
- package/client/dist/assets/{jobs-Bgq-PZkC.js → jobs-CezU69AE.js} +1 -1
- package/client/dist/assets/{jobs-SwYS2M-B.js → jobs-ClFJae0s.js} +1 -1
- package/client/dist/assets/{jobs-BAls3bWq.js → jobs-DJSFIFVy.js} +1 -1
- package/client/dist/assets/{jobs-DhxKgWFq.js → jobs-DhgGr8vm.js} +1 -1
- package/client/dist/assets/{jobs-B_TeSQxb.js → jobs-DzMjUcaN.js} +1 -1
- package/client/dist/assets/mcp-6rMoZfEO.js +1 -0
- package/client/dist/assets/mcp-BD8sL5mP.js +1 -0
- package/client/dist/assets/mcp-BkcA3I5s.js +1 -0
- package/client/dist/assets/mcp-CCpSelOh.js +1 -0
- package/client/dist/assets/mcp-CUAN6ORK.js +1 -0
- package/client/dist/assets/mcp-CqbRxRTE.js +1 -0
- package/client/dist/assets/mcp-DP2YfVhO.js +1 -0
- package/client/dist/assets/mcp-DUVQamDz.js +1 -0
- package/client/dist/assets/narration-B-ynWTpn.js +1 -0
- package/client/dist/assets/narration-Bj_laZwg.js +1 -0
- package/client/dist/assets/narration-BxHpmMv_.js +1 -0
- package/client/dist/assets/narration-C1Px0-0j.js +1 -0
- package/client/dist/assets/narration-CAXhGbzM.js +1 -0
- package/client/dist/assets/narration-DQ1l_Kq6.js +1 -0
- package/client/dist/assets/narration-XISXwqZd.js +1 -0
- package/client/dist/assets/narration-kwzfglQo.js +1 -0
- package/client/dist/assets/nav-1-Hh2kOz.js +1 -0
- package/client/dist/assets/nav-B34QkESm.js +1 -0
- package/client/dist/assets/nav-Bap_UXAq.js +1 -0
- package/client/dist/assets/{nav-pYuXq7sf.js → nav-BwxXIB24.js} +1 -1
- package/client/dist/assets/nav-Ch-zHBG5.js +1 -0
- package/client/dist/assets/nav-CygC_WN-.js +1 -0
- package/client/dist/assets/{nav-iwjCg5FR.js → nav-KnnLQtOQ.js} +1 -1
- package/client/dist/assets/nav-m00uxYUG.js +1 -0
- package/client/dist/assets/packet-BQ2tXfJI.js +1 -0
- package/client/dist/assets/packet-BdpOJ1Cy.js +1 -0
- package/client/dist/assets/packet-C3Ievb9R.js +1 -0
- package/client/dist/assets/packet-C4zJIG0Y.js +1 -0
- package/client/dist/assets/packet-Cx-zRuWn.js +1 -0
- package/client/dist/assets/packet-DUTGX52M.js +1 -0
- package/client/dist/assets/packet-DhsKtUPB.js +1 -0
- package/client/dist/assets/packet-z3LJ8Zyy.js +1 -0
- package/client/dist/assets/project-repositories-DYhTvwQ1.js +1 -0
- package/client/dist/assets/provider-capabilities-B-wOHfLP.js +1 -0
- package/client/dist/assets/settings-BBt2xtDQ.js +1 -0
- package/client/dist/assets/settings-BJB9RKJj.js +1 -0
- package/client/dist/assets/settings-CHwQR2rB.js +1 -0
- package/client/dist/assets/settings-CrMi3kSi.js +1 -0
- package/client/dist/assets/settings-DKyhDGD1.js +1 -0
- package/client/dist/assets/settings-Deupji9c.js +1 -0
- package/client/dist/assets/settings-DqRMmwAM.js +1 -0
- package/client/dist/assets/settings-J4rj6vo-.js +1 -0
- package/client/dist/assets/setup-2xGgu4Sk.js +1 -0
- package/client/dist/assets/setup-Ba11l2JA.js +1 -0
- package/client/dist/assets/setup-BzW4kMok.js +1 -0
- package/client/dist/assets/setup-COzwYhkJ.js +1 -0
- package/client/dist/assets/setup-CRVULhtF.js +1 -0
- package/client/dist/assets/setup-DgvlxwpE.js +1 -0
- package/client/dist/assets/setup-Dw_wYeEY.js +1 -0
- package/client/dist/assets/setup-ZPtEeJPW.js +1 -0
- package/client/dist/assets/spending-BqbdPm_4.js +1 -0
- package/client/dist/assets/square-Ci-PFcFe.js +1 -0
- package/client/dist/assets/{terminal-BFRZnMPP.js → terminal-BAzpHtpY.js} +1 -1
- package/client/dist/assets/{terminal-IahE4LW9.js → terminal-C7dHeR3x.js} +1 -1
- package/client/dist/assets/{terminal-tITa81iz.js → terminal-CMla5Qsz.js} +1 -1
- package/client/dist/assets/{terminal-BZiIu3rf.js → terminal-CdNXH-n9.js} +1 -1
- package/client/dist/assets/{terminal-ScjyXNxQ.js → terminal-Cw5bZsmV.js} +1 -1
- package/client/dist/assets/{terminal-C2NAd7BW.js → terminal-DFUP4eNv.js} +1 -1
- package/client/dist/assets/{terminal-d7GadXbN.js → terminal-DGqljeK_.js} +1 -1
- package/client/dist/assets/{terminal-QpXZU2Bn.js → terminal-cU70NodO.js} +1 -1
- package/client/dist/assets/useDesktop-RSMTyy7V.js +1 -0
- package/client/dist/assets/useSharedWebSocket-BOdS5_Iz.js +2 -0
- package/client/dist/index.html +34 -35
- package/docs/README.md +3 -0
- package/docs/agent-live-steering.md +57 -0
- package/docs/ci-cd.md +57 -0
- package/docs/code-explorer.md +48 -0
- package/docs/codex.md +12 -1
- package/docs/features/detachable-mission-windows.md +59 -0
- package/docs/guide/de/integrations/5-mcp-server.md +13 -9
- package/docs/guide/de/integrations/6-agent-chat.md +1 -0
- package/docs/guide/en/integrations/5-mcp-server.md +13 -9
- package/docs/guide/en/integrations/6-agent-chat.md +1 -0
- package/docs/guide/es/integrations/5-mcp-server.md +13 -9
- package/docs/guide/es/integrations/6-agent-chat.md +1 -0
- package/docs/guide/fr/integrations/5-mcp-server.md +13 -9
- package/docs/guide/fr/integrations/6-agent-chat.md +1 -0
- package/docs/guide/it/integrations/5-mcp-server.md +13 -9
- package/docs/guide/it/integrations/6-agent-chat.md +1 -0
- package/docs/guide/ja/integrations/5-mcp-server.md +13 -9
- package/docs/guide/ja/integrations/6-agent-chat.md +1 -0
- package/docs/guide/pt/integrations/5-mcp-server.md +13 -9
- package/docs/guide/pt/integrations/6-agent-chat.md +1 -0
- package/docs/guide/zh/integrations/5-mcp-server.md +13 -9
- package/docs/guide/zh/integrations/6-agent-chat.md +1 -0
- package/docs/internals/README.md +1 -0
- package/docs/internals/browser-capture-performance.md +55 -3
- package/docs/internals/browser-login-popups.md +45 -0
- package/docs/internals/browser-native-retina-audit.md +39 -0
- package/docs/internals/ci-cd-audit.md +39 -0
- package/docs/internals/core-runtime-updates.md +68 -0
- package/docs/internals/embedded-browser-native-webview-evaluation.md +2 -0
- package/docs/internals/implementation-reliability-audit.md +93 -0
- package/docs/internals/interactive-jobs.md +110 -6
- package/docs/internals/mcp-mission-audit.md +76 -0
- package/docs/internals/project-builder.md +404 -42
- package/docs/internals/safe-pr-review-flow.md +52 -10
- package/docs/internals/startup-recovery-audit.md +58 -0
- package/docs/mcp.md +74 -41
- package/docs/mission-processes.md +59 -0
- package/docs/multi-repo-projects.md +51 -0
- package/docs/platforms/windows-parity.md +70 -0
- package/docs/platforms/windows.md +13 -11
- package/docs/running-pipelines.md +14 -0
- package/mcp-bridge/dist/specrails-mcp.js +7186 -0
- package/package.json +9 -4
- package/server/dist/agent-chat-manager.js +537 -68
- package/server/dist/agent-chat-router.js +87 -6
- package/server/dist/agent-context-resolver.js +129 -25
- package/server/dist/agent-defaults.js +19 -11
- package/server/dist/agent-input-store.js +206 -0
- package/server/dist/agent-mcp-config.js +54 -15
- package/server/dist/agent-operator-prompt.js +255 -65
- package/server/dist/agent-spec-framing.js +202 -0
- package/server/dist/agent-steering.js +218 -0
- package/server/dist/agent-store.js +153 -0
- package/server/dist/api-not-found.js +8 -0
- package/server/dist/background-process-control.js +164 -0
- package/server/dist/background-process-service.js +61 -0
- package/server/dist/background-process-store.js +242 -0
- package/server/dist/background-windows-bootstrap.js +238 -0
- package/server/dist/blueprint-chat-manager.js +476 -62
- package/server/dist/blueprint-commit.js +21 -1
- package/server/dist/blueprint-draft-parser.js +122 -17
- package/server/dist/blueprint-generation.js +238 -0
- package/server/dist/blueprint-operator-prompt.js +91 -63
- package/server/dist/blueprint-router.js +55 -3
- package/server/dist/blueprint-spec-fixtures.js +85 -0
- package/server/dist/blueprint-spec-quality.js +57 -16
- package/server/dist/blueprint-store.js +92 -1
- package/server/dist/browser-capture-manager.js +86 -30
- package/server/dist/browser-playwright.js +168 -45
- package/server/dist/browser-viewport.js +26 -0
- package/server/dist/chat-manager.js +23 -15
- package/server/dist/chromium-archive.cjs +83 -0
- package/server/dist/chromium-resolver.js +72 -64
- package/server/dist/code-activity.js +145 -0
- package/server/dist/code-explorer-router.js +714 -114
- package/server/dist/core-compat.js +28 -1
- package/server/dist/core-execution.js +111 -0
- package/server/dist/core-package.js +4 -16
- package/server/dist/core-runtime.js +202 -0
- package/server/dist/core-update-manager.js +161 -60
- package/server/dist/core-update-state.js +29 -0
- package/server/dist/db.js +66 -35
- package/server/dist/delivery-evidence.js +142 -39
- package/server/dist/desktop-db.js +239 -43
- package/server/dist/desktop-router.js +109 -9
- package/server/dist/dev-ports.js +8 -3
- package/server/dist/file-provenance.js +34 -22
- package/server/dist/file-story-manager.js +62 -64
- package/server/dist/file-story.js +118 -35
- package/server/dist/file-summary-generator.js +46 -5
- package/server/dist/file-summary-manager.js +355 -106
- package/server/dist/framework-manager.js +98 -110
- package/server/dist/framework-reseed.js +76 -10
- package/server/dist/git-diagnostics.js +2 -2
- package/server/dist/headroom-manager.js +38 -3
- package/server/dist/host-control.js +52 -0
- package/server/dist/index.js +43 -2
- package/server/dist/interactive-job-session.js +149 -16
- package/server/dist/internal-api.js +48 -0
- package/server/dist/jira/jira-materializer.js +1 -0
- package/server/dist/json-tolerant.js +174 -0
- package/server/dist/legacy-migration.js +4 -2
- package/server/dist/loop-command-catalog.js +83 -21
- package/server/dist/loop-constants.js +6 -1
- package/server/dist/loop-decider.js +11 -1
- package/server/dist/loop-executors.js +194 -45
- package/server/dist/loop-factory.js +11 -4
- package/server/dist/loop-graph.js +90 -4
- package/server/dist/loop-run-manager.js +301 -69
- package/server/dist/loop-shell-invocation.js +21 -0
- package/server/dist/loop-step-idle.js +55 -0
- package/server/dist/loop-templates.js +31 -7
- package/server/dist/mcp/agent-capability.js +37 -4
- package/server/dist/mcp/guide.js +149 -36
- package/server/dist/mcp/mcp-admin-router.js +20 -4
- package/server/dist/mcp/mcp-server.js +89 -43
- package/server/dist/mcp/mcp-tiers.js +6 -5
- package/server/dist/mcp/mcp-token.js +3 -1
- package/server/dist/mcp/resources.js +5 -4
- package/server/dist/mcp/tools/catalog.js +4 -0
- package/server/dist/mcp/tools/code.js +125 -14
- package/server/dist/mcp/tools/context.js +153 -0
- package/server/dist/mcp/tools/git.js +15 -5
- package/server/dist/mcp/tools/jobs.js +84 -71
- package/server/dist/mcp/tools/loops.js +17 -6
- package/server/dist/mcp/tools/meta.js +74 -27
- package/server/dist/mcp/tools/mission.js +29 -0
- package/server/dist/mcp/tools/projects.js +22 -6
- package/server/dist/mcp/tools/rails.js +48 -11
- package/server/dist/mcp/tools/specs.js +86 -14
- package/server/dist/mcp/tools/support.js +1 -1
- package/server/dist/mcp/tools/types.js +117 -43
- package/server/dist/mcp/tools/watch.js +144 -51
- package/server/dist/milestone-chain-store.js +164 -0
- package/server/dist/milestone-chain.js +487 -0
- package/server/dist/milestone-progress.js +348 -0
- package/server/dist/mobile/mobile-devices.js +10 -7
- package/server/dist/mobile/mobile-gateway.js +2 -0
- package/server/dist/mobile/mobile-missions.js +214 -0
- package/server/dist/mobile/mobile-redact.js +17 -3
- package/server/dist/mobile/mobile-router.js +46 -4
- package/server/dist/mobile/mobile-ws.js +49 -4
- package/server/dist/multi-repo-bases.js +56 -0
- package/server/dist/multi-repo-checkout.js +101 -0
- package/server/dist/multi-repo-delivery.js +100 -0
- package/server/dist/multi-repo-execution-store.js +65 -0
- package/server/dist/multi-repo-execution.js +453 -0
- package/server/dist/offline-assemble.js +10 -8
- package/server/dist/openspec-runtime-plugin-commands.json +18 -0
- package/server/dist/openspec-runtime-plugin.js +120 -0
- package/server/dist/plugins/codex-spawn.js +49 -0
- package/server/dist/plugins/serena/manifest.js +2 -2
- package/server/dist/plugins/serena/templates/instructions.md +16 -0
- package/server/dist/pr-publisher.js +1 -1
- package/server/dist/profiles-router.js +3 -18
- package/server/dist/project-code-discovery.js +142 -0
- package/server/dist/project-git.js +19 -21
- package/server/dist/project-registry.js +272 -20
- package/server/dist/project-repositories.js +157 -0
- package/server/dist/project-repository-provenance.js +31 -0
- package/server/dist/project-router-background-processes.js +105 -0
- package/server/dist/project-router-git.js +75 -16
- package/server/dist/project-router-jobs.js +2 -126
- package/server/dist/project-router-loop-runs.js +72 -2
- package/server/dist/project-router-tickets.js +53 -8
- package/server/dist/project-router.js +239 -38
- package/server/dist/provider-detection.js +18 -6
- package/server/dist/provider-limit.js +64 -0
- package/server/dist/providers/claude-adapter.js +26 -1
- package/server/dist/providers/claude-background-tasks.js +18 -0
- package/server/dist/providers/claude-live-session.js +383 -0
- package/server/dist/providers/codex-adapter.js +19 -0
- package/server/dist/providers/codex-live-session.js +545 -0
- package/server/dist/providers/gemini-adapter.js +1 -0
- package/server/dist/providers/live-session-types.js +12 -0
- package/server/dist/providers/live-session.js +13 -0
- package/server/dist/providers/terminal-result.js +24 -0
- package/server/dist/queue-manager.js +2 -0
- package/server/dist/rail-isolated-launch.js +141 -16
- package/server/dist/rail-pr-decision.js +364 -99
- package/server/dist/rail-pr-store.js +24 -10
- package/server/dist/rail-worktree-release.js +34 -2
- package/server/dist/rail-worktrees-store.js +4 -4
- package/server/dist/rails-router.js +249 -55
- package/server/dist/repo-lock.js +92 -17
- package/server/dist/review-packet.js +23 -13
- package/server/dist/schemas/file-summary.v1.json +59 -59
- package/server/dist/schemas/profile.v1.json +137 -137
- package/server/dist/setup-manager.js +52 -8
- package/server/dist/shell-integration/bash-shim.bash +45 -0
- package/server/dist/shell-integration/fish-shim.fish +36 -0
- package/server/dist/shell-integration/powershell-shim.ps1 +48 -0
- package/server/dist/shell-integration/zsh-shim.zsh +47 -0
- package/server/dist/smash-runner.js +1 -0
- package/server/dist/spawn-lifecycle.js +44 -27
- package/server/dist/spec-contract-prompt.js +143 -0
- package/server/dist/spec-draft-parser.js +6 -1
- package/server/dist/spec-models.js +1 -0
- package/server/dist/stuck-run-detector.js +1 -0
- package/server/dist/terminal-manager.js +21 -1
- package/server/dist/terminal-shell-integration.js +4 -1
- package/server/dist/transient-children.js +619 -230
- package/server/dist/util/cli-prompt.js +78 -5
- package/server/dist/util/sqlite-migrations.js +20 -0
- package/server/dist/util/win-spawn.js +18 -3
- package/server/dist/util/windows-provider-shell.js +29 -0
- package/server/dist/verification-sentinel.js +14 -0
- package/server/dist/vitest-setup.js +2 -0
- package/server/dist/windows-job-supervisor.js +133 -0
- package/server/dist/workspace-manager.js +9 -30
- package/server/dist/worktree-manager.js +17 -2
- package/server/dist/worktree-overlay.js +3 -1
- package/client/dist/assets/ActivityFeedPage-tAQcWs1R.js +0 -1
- package/client/dist/assets/AgentBrowserCapture-BX8BNae5.js +0 -1
- package/client/dist/assets/AgentModeAnalyticsPane-BVAqCL0K.js +0 -2
- package/client/dist/assets/AgentModeCodePane-9XfqQc9c.js +0 -2
- package/client/dist/assets/AgentModeJobsPane-BCjLsPhc.js +0 -1
- package/client/dist/assets/AgentsPage-C8J_42sm.js +0 -87
- package/client/dist/assets/AnalyticsPage-BtQuyfJm.js +0 -1
- package/client/dist/assets/CodePage-BZ2MwdCu.js +0 -2
- package/client/dist/assets/DesktopAnalyticsPage-Bh_29V4g.js +0 -1
- package/client/dist/assets/ExportDropdown-BrtfWzEG.js +0 -1
- package/client/dist/assets/InteractiveJobComposer-BCZiSfzc.js +0 -19
- package/client/dist/assets/JobDetailModal-NAJmuYIT.js +0 -1
- package/client/dist/assets/JobDetailPage-fE0ZycUX.js +0 -1
- package/client/dist/assets/JobsPage-z5M_HVJG.js +0 -1
- package/client/dist/assets/LoopBuilderPage-j1FQS47c.js +0 -7
- package/client/dist/assets/LoopPreviewModal-DhDJjdCe.js +0 -1
- package/client/dist/assets/LoopsPage-Ll7zORZb.js +0 -1
- package/client/dist/assets/MinimizedChatsContext-Bd3OqR_S.js +0 -1
- package/client/dist/assets/PluginsPage-DN4kGvJt.js +0 -2
- package/client/dist/assets/ProjectSettingsDialog-OgsgxjzF.js +0 -1
- package/client/dist/assets/ReviewPacketPage-DFog4sXn.js +0 -1
- package/client/dist/assets/TemplatePreviewModal-L2XxqiOD.js +0 -1
- package/client/dist/assets/TicketDetailModal-CsF-AWZX.js +0 -14
- package/client/dist/assets/Trans-Bsz9_gyq.js +0 -1
- package/client/dist/assets/agent-BQgKv_2F.js +0 -1
- package/client/dist/assets/agent-C1NRDJqL.js +0 -1
- package/client/dist/assets/agent-CLNleiy6.js +0 -1
- package/client/dist/assets/agent-CmceArkQ.js +0 -1
- package/client/dist/assets/agent-D3UxQ3SR.js +0 -1
- package/client/dist/assets/agent-DB1JeE64.js +0 -1
- package/client/dist/assets/agent-DXkHcQ-D.js +0 -1
- package/client/dist/assets/agent-DjJJJ2M8.js +0 -1
- package/client/dist/assets/auth-D_5bQv5W.js +0 -1
- package/client/dist/assets/ban-E4EaZe3a.js +0 -1
- package/client/dist/assets/bot-DzuuuC5R.js +0 -1
- package/client/dist/assets/browser-BgqHDMXP.js +0 -1
- package/client/dist/assets/browser-CsKJtrpv.js +0 -1
- package/client/dist/assets/browser-Cu58EpZU.js +0 -1
- package/client/dist/assets/browser-CvXOJm-j.js +0 -1
- package/client/dist/assets/browser-DOrFh1c7.js +0 -1
- package/client/dist/assets/browser-DQv_Jo7x.js +0 -1
- package/client/dist/assets/browser-LPgxvJu2.js +0 -1
- package/client/dist/assets/browser-eL-3MIEI.js +0 -1
- package/client/dist/assets/builder-0IWtosUm.js +0 -1
- package/client/dist/assets/builder-Bf2N6XWn.js +0 -1
- package/client/dist/assets/builder-BnqaaBkT.js +0 -1
- package/client/dist/assets/builder-BxxfGgAO.js +0 -1
- package/client/dist/assets/builder-C5IIgWsq.js +0 -1
- package/client/dist/assets/builder-CHuahItu.js +0 -1
- package/client/dist/assets/builder-CjWosXL_.js +0 -1
- package/client/dist/assets/builder-CpCIOoKj.js +0 -1
- package/client/dist/assets/code-CHPkOkDS.js +0 -1
- package/client/dist/assets/commands-6tYQih5m.js +0 -1
- package/client/dist/assets/commands-BK2QinXN.js +0 -1
- package/client/dist/assets/commands-C2Nca76h.js +0 -1
- package/client/dist/assets/commands-CKdLihNX.js +0 -1
- package/client/dist/assets/commands-CYIBTAlc.js +0 -1
- package/client/dist/assets/commands-CjUtYJIM.js +0 -1
- package/client/dist/assets/commands-Cq_u_8y9.js +0 -1
- package/client/dist/assets/commands-D_ZHYQug.js +0 -1
- package/client/dist/assets/common-6MLbD-jM.js +0 -1
- package/client/dist/assets/common-BKGEu4rm.js +0 -1
- package/client/dist/assets/common-BNbnv8cE.js +0 -1
- package/client/dist/assets/common-BqGWtDbt.js +0 -1
- package/client/dist/assets/common-DBtTzeYm.js +0 -1
- package/client/dist/assets/common-DEW3Nr8n.js +0 -1
- package/client/dist/assets/common-DHsv9N5d.js +0 -1
- package/client/dist/assets/common-mkrEcW-Y.js +0 -1
- package/client/dist/assets/dashboard-B9GhC5ku.js +0 -1
- package/client/dist/assets/dashboard-CBnv6zOI.js +0 -1
- package/client/dist/assets/dashboard-CaP0fU87.js +0 -1
- package/client/dist/assets/dashboard-D4ZZ4x84.js +0 -1
- package/client/dist/assets/dashboard-DmuTQBX8.js +0 -1
- package/client/dist/assets/dashboard-DynLYj3d.js +0 -1
- package/client/dist/assets/dashboard-zj4gGFJ1.js +0 -1
- package/client/dist/assets/dialog-CdWCb02j.js +0 -45
- package/client/dist/assets/dist-bVqfYmby.js +0 -1
- package/client/dist/assets/dist-js-CWeUdf5Z.js +0 -1
- package/client/dist/assets/formatDistanceToNow-DQCvMgUc.js +0 -1
- package/client/dist/assets/getTimezoneOffsetInMilliseconds-C16cxSe1.js +0 -1
- package/client/dist/assets/git-refresh-wTW0rO0Z.js +0 -1
- package/client/dist/assets/i18n-hnX2DPmo.js +0 -2
- package/client/dist/assets/index-DIhMEAVw.js +0 -66
- package/client/dist/assets/index-DwygcU7s.css +0 -2
- package/client/dist/assets/mcp-B8X8wBLu.js +0 -1
- package/client/dist/assets/mcp-C2AojZZN.js +0 -1
- package/client/dist/assets/mcp-D8BT78_L.js +0 -1
- package/client/dist/assets/mcp-Df5RWBNk.js +0 -1
- package/client/dist/assets/mcp-Dj7BgMQW.js +0 -1
- package/client/dist/assets/mcp-Q7iT7rcz.js +0 -1
- package/client/dist/assets/mcp-YlvNvJbW.js +0 -1
- package/client/dist/assets/mcp-mKFpTSRb.js +0 -1
- package/client/dist/assets/narration-4Z2VUhZw.js +0 -1
- package/client/dist/assets/narration-BGuiEDj3.js +0 -1
- package/client/dist/assets/narration-BuSoqRmv.js +0 -1
- package/client/dist/assets/narration-CB9t29fy.js +0 -1
- package/client/dist/assets/narration-CU3x8FHD.js +0 -1
- package/client/dist/assets/narration-CWeXhfP5.js +0 -1
- package/client/dist/assets/narration-DRaDnhT5.js +0 -1
- package/client/dist/assets/narration-DtEbQgCw.js +0 -1
- package/client/dist/assets/nav-ApJVg3xu.js +0 -1
- package/client/dist/assets/nav-BKNky1Bg.js +0 -1
- package/client/dist/assets/nav-C6vWWKYx.js +0 -1
- package/client/dist/assets/nav-CXJ6Aw89.js +0 -1
- package/client/dist/assets/nav-CsmFBIDf.js +0 -1
- package/client/dist/assets/nav-DjkmQJjd.js +0 -1
- package/client/dist/assets/packet-B1IxaL5f.js +0 -1
- package/client/dist/assets/packet-Bh5kkbiB.js +0 -1
- package/client/dist/assets/packet-By8fqYxW.js +0 -1
- package/client/dist/assets/packet-CZdvA44E.js +0 -1
- package/client/dist/assets/packet-CrjHRQCP.js +0 -1
- package/client/dist/assets/packet-Dwi2pTtx.js +0 -1
- package/client/dist/assets/packet-_-ZIa0IR.js +0 -1
- package/client/dist/assets/packet-n0W6E1Hn.js +0 -1
- package/client/dist/assets/play-DNlrawbS.js +0 -1
- package/client/dist/assets/provider-capabilities-Doej9lfa.js +0 -1
- package/client/dist/assets/settings-Bd0x4TBa.js +0 -1
- package/client/dist/assets/settings-C1-7cXzC.js +0 -1
- package/client/dist/assets/settings-CAFaT38b.js +0 -1
- package/client/dist/assets/settings-Ct3v1nW2.js +0 -1
- package/client/dist/assets/settings-DHh7_IF5.js +0 -1
- package/client/dist/assets/settings-DQXAmdcv.js +0 -1
- package/client/dist/assets/settings-guzlhpup.js +0 -1
- package/client/dist/assets/settings-sZfTPMEH.js +0 -1
- package/client/dist/assets/setup-3jWaoVUO.js +0 -1
- package/client/dist/assets/setup-BjgRneb2.js +0 -1
- package/client/dist/assets/setup-BzFIjv-f.js +0 -1
- package/client/dist/assets/setup-C4VfQsxE.js +0 -1
- package/client/dist/assets/setup-DfUkpDbN.js +0 -1
- package/client/dist/assets/setup-ECe90Pmu.js +0 -1
- package/client/dist/assets/setup-dlqC4Jmm.js +0 -1
- package/client/dist/assets/setup-jwfLMiM2.js +0 -1
- package/client/dist/assets/spending-EC9xqio-.js +0 -1
- package/client/dist/assets/useDesktop-BQVt8lFd.js +0 -1
|
@@ -21,7 +21,7 @@ const GIT_MAX_BUFFER = 4 * 1024 * 1024;
|
|
|
21
21
|
const COMMIT_SHA_RE = /^[0-9a-f]{40,64}$/i;
|
|
22
22
|
function git(repoDir, args) {
|
|
23
23
|
return new Promise((resolve, reject) => {
|
|
24
|
-
(0, child_process_1.execFile)('git', args, { cwd: repoDir, env: file_provenance_1.
|
|
24
|
+
(0, child_process_1.execFile)('git', args, { cwd: repoDir, env: (0, file_provenance_1.gitExecEnv)(), timeout: GIT_TIMEOUT_MS, maxBuffer: GIT_MAX_BUFFER, windowsHide: true }, (err, stdout, stderr) => {
|
|
25
25
|
if (err) {
|
|
26
26
|
const detail = String(stderr || err.message).trim();
|
|
27
27
|
reject(new Error(detail));
|
|
@@ -56,7 +56,7 @@ function parseWorktreePorcelain(out) {
|
|
|
56
56
|
return worktrees;
|
|
57
57
|
}
|
|
58
58
|
async function listLocalBranches(repoDir) {
|
|
59
|
-
const out = await git(repoDir, ['branch', '--format=%(refname:
|
|
59
|
+
const out = await git(repoDir, ['branch', '--format=%(refname:lstrip=2)']);
|
|
60
60
|
return out.split('\n').map((b) => b.trim()).filter(Boolean);
|
|
61
61
|
}
|
|
62
62
|
/** Lossless checkout preflight. Unlike the dashboard-oriented aggregate info,
|
|
@@ -90,7 +90,7 @@ async function getProjectGitInfo(repoDir) {
|
|
|
90
90
|
let branch = null;
|
|
91
91
|
let detached = false;
|
|
92
92
|
try {
|
|
93
|
-
branch = await git(repoDir, ['symbolic-ref', '
|
|
93
|
+
branch = (await git(repoDir, ['symbolic-ref', 'HEAD'])).replace(/^refs\/heads\//, '');
|
|
94
94
|
}
|
|
95
95
|
catch {
|
|
96
96
|
detached = true;
|
|
@@ -159,7 +159,7 @@ async function checkoutProjectBranch(repoDir, branch) {
|
|
|
159
159
|
return { ok: false, error: `Unknown local branch: ${branch}` };
|
|
160
160
|
}
|
|
161
161
|
try {
|
|
162
|
-
await git(repoDir, ['checkout', branch]);
|
|
162
|
+
await git(repoDir, ['checkout', '--no-overwrite-ignore', branch]);
|
|
163
163
|
return { ok: true };
|
|
164
164
|
}
|
|
165
165
|
catch (err) {
|
|
@@ -192,19 +192,13 @@ async function checkoutProjectReviewBranch(repoDir, branch, expectedSha) {
|
|
|
192
192
|
if (expected !== null && !COMMIT_SHA_RE.test(expected)) {
|
|
193
193
|
return { ok: false, error: 'The verified delivery commit is invalid; checkout was not attempted.' };
|
|
194
194
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
return { ok: false, error:
|
|
202
|
-
}
|
|
203
|
-
if (dirty)
|
|
204
|
-
return { ok: false, error: 'Working tree has uncommitted changes. Commit or stash them before checkout.' };
|
|
205
|
-
// Best effort: refresh remote-tracking refs before deciding whether a branch
|
|
206
|
-
// can be materialized. Failure degrades to local-only checkout.
|
|
207
|
-
await git(repoDir, ['fetch', 'origin']).catch(() => { });
|
|
195
|
+
// Git's checkout transaction preserves compatible staged/unstaged changes
|
|
196
|
+
// and refuses only paths it would overwrite. A blanket dirty guard prevented
|
|
197
|
+
// reviewing any delivery while unrelated local edits existed. Include
|
|
198
|
+
// ignored files in that protection (Git otherwise overwrites them by default).
|
|
199
|
+
const cleanliness = await inspectProjectCheckoutCleanliness(repoDir);
|
|
200
|
+
if (!cleanliness.ok)
|
|
201
|
+
return { ok: false, error: cleanliness.detail };
|
|
208
202
|
let branches;
|
|
209
203
|
try {
|
|
210
204
|
branches = await listLocalBranches(repoDir);
|
|
@@ -213,6 +207,10 @@ async function checkoutProjectReviewBranch(repoDir, branch, expectedSha) {
|
|
|
213
207
|
return { ok: false, error: err instanceof Error ? err.message : 'git failed' };
|
|
214
208
|
}
|
|
215
209
|
try {
|
|
210
|
+
// A verified local branch needs no network. Refresh only when it must be
|
|
211
|
+
// materialized from origin, avoiding a fetch timeout on every local handoff.
|
|
212
|
+
if (!branches.includes(target))
|
|
213
|
+
await git(repoDir, ['fetch', 'origin']).catch(() => { });
|
|
216
214
|
if (branches.includes(target)) {
|
|
217
215
|
if (expected) {
|
|
218
216
|
const localSha = (await git(repoDir, ['rev-parse', '--verify', `refs/heads/${target}`])).toLowerCase();
|
|
@@ -223,7 +221,7 @@ async function checkoutProjectReviewBranch(repoDir, branch, expectedSha) {
|
|
|
223
221
|
};
|
|
224
222
|
}
|
|
225
223
|
}
|
|
226
|
-
await git(repoDir, ['checkout', target]);
|
|
224
|
+
await git(repoDir, ['checkout', '--no-overwrite-ignore', target]);
|
|
227
225
|
}
|
|
228
226
|
else if (await remoteBranchExists(repoDir, target)) {
|
|
229
227
|
if (expected) {
|
|
@@ -235,7 +233,7 @@ async function checkoutProjectReviewBranch(repoDir, branch, expectedSha) {
|
|
|
235
233
|
};
|
|
236
234
|
}
|
|
237
235
|
}
|
|
238
|
-
await git(repoDir, ['checkout', '-b', target, '--track', `origin/${target}`]);
|
|
236
|
+
await git(repoDir, ['checkout', '--no-overwrite-ignore', '-b', target, '--track', `origin/${target}`]);
|
|
239
237
|
}
|
|
240
238
|
else {
|
|
241
239
|
return { ok: false, error: `Unknown branch: ${target}` };
|
|
@@ -247,10 +245,10 @@ async function checkoutProjectReviewBranch(repoDir, branch, expectedSha) {
|
|
|
247
245
|
if (expected) {
|
|
248
246
|
try {
|
|
249
247
|
const [currentBranch, currentSha] = await Promise.all([
|
|
250
|
-
git(repoDir, ['
|
|
248
|
+
git(repoDir, ['symbolic-ref', '--quiet', 'HEAD']),
|
|
251
249
|
git(repoDir, ['rev-parse', '--verify', 'HEAD']),
|
|
252
250
|
]);
|
|
253
|
-
if (currentBranch !== target || currentSha.toLowerCase() !== expected) {
|
|
251
|
+
if (currentBranch !== `refs/heads/${target}` || currentSha.toLowerCase() !== expected) {
|
|
254
252
|
return {
|
|
255
253
|
ok: false,
|
|
256
254
|
error: `Checkout changed concurrently and did not land on verified delivery ${expected.slice(0, 8)}. No reset or cleanup was performed.`,
|
|
@@ -10,6 +10,8 @@ const path_1 = __importDefault(require("path"));
|
|
|
10
10
|
const fs_1 = __importDefault(require("fs"));
|
|
11
11
|
const os_1 = __importDefault(require("os"));
|
|
12
12
|
const db_1 = require("./db");
|
|
13
|
+
const project_repositories_1 = require("./project-repositories");
|
|
14
|
+
const multi_repo_execution_store_1 = require("./multi-repo-execution-store");
|
|
13
15
|
const queue_manager_1 = require("./queue-manager");
|
|
14
16
|
const agent_defaults_1 = require("./agent-defaults");
|
|
15
17
|
const chat_manager_1 = require("./chat-manager");
|
|
@@ -37,6 +39,13 @@ const workspace_manager_1 = require("./workspace-manager");
|
|
|
37
39
|
const ticket_store_1 = require("./ticket-store");
|
|
38
40
|
const jira_sync_manager_1 = require("./jira/jira-sync-manager");
|
|
39
41
|
const stuck_run_detector_1 = require("./stuck-run-detector");
|
|
42
|
+
const milestone_progress_1 = require("./milestone-progress");
|
|
43
|
+
const milestone_chain_1 = require("./milestone-chain");
|
|
44
|
+
const internal_api_1 = require("./internal-api");
|
|
45
|
+
const blueprint_render_1 = require("./blueprint-render");
|
|
46
|
+
const worktree_manager_1 = require("./worktree-manager");
|
|
47
|
+
const integration_branch_1 = require("./integration-branch");
|
|
48
|
+
const db_2 = require("./db");
|
|
40
49
|
const loop_run_manager_1 = require("./loop-run-manager");
|
|
41
50
|
const loop_executors_1 = require("./loop-executors");
|
|
42
51
|
const rail_isolated_launch_1 = require("./rail-isolated-launch");
|
|
@@ -85,6 +94,9 @@ function reprojectActivePrDeliveries(ctx) {
|
|
|
85
94
|
return rows.length;
|
|
86
95
|
}
|
|
87
96
|
// ─── ProjectRegistry ──────────────────────────────────────────────────────────
|
|
97
|
+
class ProjectDatabaseBusyError extends Error {
|
|
98
|
+
}
|
|
99
|
+
const PROJECT_LOAD_RETRY_DELAYS_MS = [1000, 2000, 5000, 30000];
|
|
88
100
|
class ProjectRegistry {
|
|
89
101
|
_desktopDb;
|
|
90
102
|
_contexts;
|
|
@@ -94,6 +106,8 @@ class ProjectRegistry {
|
|
|
94
106
|
// M9: projects whose per-project DB failed to load at startup (corrupt, locked,
|
|
95
107
|
// or migration-stuck). They stay registered but have no live context.
|
|
96
108
|
_failedProjects;
|
|
109
|
+
_loadRetryTimers = new Map();
|
|
110
|
+
_shuttingDown = false;
|
|
97
111
|
// App-wide shared browser context for "Add Spec from a website": ONE persistent
|
|
98
112
|
// Chromium profile under ~/.specrails/browser-profile, so cookies/logins are
|
|
99
113
|
// shared across every project. Launched lazily, disposed once at shutdown().
|
|
@@ -129,21 +143,52 @@ class ProjectRegistry {
|
|
|
129
143
|
console.error('[project-registry] registry reconcile failed (non-fatal):', err);
|
|
130
144
|
}
|
|
131
145
|
for (const project of projects) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
146
|
+
this._tryLoadProject(project);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
_tryLoadProject(project, retry = 0) {
|
|
150
|
+
this._clearLoadRetry(project.id);
|
|
151
|
+
if (this._shuttingDown)
|
|
152
|
+
return;
|
|
153
|
+
try {
|
|
154
|
+
this._loadProjectContext(project);
|
|
155
|
+
const recovered = this._failedProjects.delete(project.id);
|
|
156
|
+
if (recovered) {
|
|
157
|
+
try {
|
|
158
|
+
this._broadcast({ type: 'desktop.project_recovered', projectId: project.id, timestamp: new Date().toISOString() });
|
|
159
|
+
}
|
|
160
|
+
catch { /* the available context remains authoritative */ }
|
|
135
161
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
165
|
+
console.error(`[project-registry] failed to load project ${project.id} (${project.slug}): ${msg}`);
|
|
166
|
+
this._failedProjects.set(project.id, { project, error: msg });
|
|
167
|
+
// Only an unopened DB can be retried safely: no managers/watchers or
|
|
168
|
+
// recovery work exist yet. Corruption and later constructor errors stay
|
|
169
|
+
// visible without repeatedly constructing partially initialized contexts.
|
|
170
|
+
if (err instanceof ProjectDatabaseBusyError) {
|
|
171
|
+
const timer = setTimeout(() => {
|
|
172
|
+
this._loadRetryTimers.delete(project.id);
|
|
173
|
+
const current = (0, desktop_db_1.getProject)(this._desktopDb, project.id);
|
|
174
|
+
if (current)
|
|
175
|
+
this._tryLoadProject(current, Math.min(retry + 1, PROJECT_LOAD_RETRY_DELAYS_MS.length - 1));
|
|
176
|
+
}, PROJECT_LOAD_RETRY_DELAYS_MS[retry]);
|
|
177
|
+
timer.unref?.();
|
|
178
|
+
this._loadRetryTimers.set(project.id, timer);
|
|
144
179
|
}
|
|
145
180
|
}
|
|
146
181
|
}
|
|
182
|
+
_clearLoadRetry(id) {
|
|
183
|
+
const timer = this._loadRetryTimers.get(id);
|
|
184
|
+
if (timer)
|
|
185
|
+
clearTimeout(timer);
|
|
186
|
+
this._loadRetryTimers.delete(id);
|
|
187
|
+
}
|
|
188
|
+
/** The durable catalog includes projects whose DB could not be opened. */
|
|
189
|
+
listProjects() {
|
|
190
|
+
return (0, desktop_db_1.listProjects)(this._desktopDb);
|
|
191
|
+
}
|
|
147
192
|
/** Projects whose per-project DB failed to load at startup (M9). */
|
|
148
193
|
listFailedProjects() {
|
|
149
194
|
return Array.from(this._failedProjects.values());
|
|
@@ -217,6 +262,7 @@ class ProjectRegistry {
|
|
|
217
262
|
}
|
|
218
263
|
}
|
|
219
264
|
removeProject(id) {
|
|
265
|
+
this._clearLoadRetry(id);
|
|
220
266
|
// Resolve the repo path BEFORE the DB row is deleted below so we can drop the
|
|
221
267
|
// shared artifact-registry entry. Prefer the live context, fall back to the
|
|
222
268
|
// desktop DB row (project may be registered-but-not-loaded, e.g. M9 failure).
|
|
@@ -249,6 +295,9 @@ class ProjectRegistry {
|
|
|
249
295
|
// deleting the data dir here would make that effect unrecoverable.
|
|
250
296
|
throw new Error('Project removal deferred: terminal job recovery is still pending');
|
|
251
297
|
}
|
|
298
|
+
// Persistent history must be removable before deleting project databases
|
|
299
|
+
// or directories; a storage failure leaves the registered data retryable.
|
|
300
|
+
(0, transient_children_1.purgeBackgroundProcessHistory)({ projectId: id });
|
|
252
301
|
try {
|
|
253
302
|
ctx.chatManager.shutdown();
|
|
254
303
|
}
|
|
@@ -297,6 +346,10 @@ class ProjectRegistry {
|
|
|
297
346
|
ctx.stuckRunDetector.stop();
|
|
298
347
|
}
|
|
299
348
|
catch { /* ignore */ }
|
|
349
|
+
try {
|
|
350
|
+
ctx.milestoneProgress?.dispose();
|
|
351
|
+
}
|
|
352
|
+
catch { /* ignore */ }
|
|
300
353
|
// Kill any terminal sessions belonging to this project
|
|
301
354
|
try {
|
|
302
355
|
(0, terminal_manager_1.getTerminalManager)().killAllForProject(id);
|
|
@@ -333,6 +386,9 @@ class ProjectRegistry {
|
|
|
333
386
|
catch { /* ignore */ }
|
|
334
387
|
this._contexts.delete(id);
|
|
335
388
|
}
|
|
389
|
+
else {
|
|
390
|
+
(0, transient_children_1.purgeBackgroundProcessHistory)({ projectId: id });
|
|
391
|
+
}
|
|
336
392
|
// B54: remove the ENTIRE app-managed data dir even when context hydration
|
|
337
393
|
// failed. The persisted row is authoritative for registered-but-not-loaded
|
|
338
394
|
// projects and contains the slug needed for safe cleanup.
|
|
@@ -383,12 +439,82 @@ class ProjectRegistry {
|
|
|
383
439
|
getContext(id) {
|
|
384
440
|
return this._contexts.get(id);
|
|
385
441
|
}
|
|
386
|
-
getContextByPath(projectPath) {
|
|
387
|
-
const row = (0, desktop_db_1.
|
|
442
|
+
getContextByPath(projectPath, projectId) {
|
|
443
|
+
const row = (0, project_repositories_1.resolveRepositoryProject)((0, desktop_db_1.listProjects)(this._desktopDb), projectPath, projectId);
|
|
388
444
|
if (!row)
|
|
389
445
|
return undefined;
|
|
390
446
|
return this._contexts.get(row.id);
|
|
391
447
|
}
|
|
448
|
+
/** Preserve manager closures and shared stores when the repository catalog changes. */
|
|
449
|
+
refreshProjectRepositories(projectId) {
|
|
450
|
+
const row = (0, desktop_db_1.getProject)(this._desktopDb, projectId);
|
|
451
|
+
if (!row)
|
|
452
|
+
throw new project_repositories_1.RepositoryValidationError('Project not found', 'project_not_found', 404);
|
|
453
|
+
const context = this._contexts.get(projectId);
|
|
454
|
+
if (context) {
|
|
455
|
+
for (const previous of (0, project_repositories_1.getProjectRepositories)(context.project)) {
|
|
456
|
+
const next = (0, project_repositories_1.getProjectRepositories)(row).find((repository) => repository.id === previous.id);
|
|
457
|
+
if (!next || next.path !== previous.path) {
|
|
458
|
+
try {
|
|
459
|
+
context.fileSummaryManager.detachWatcher(projectId, previous.id);
|
|
460
|
+
}
|
|
461
|
+
catch (err) {
|
|
462
|
+
console.warn('[project-registry] repository watcher cleanup failed:', err);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
Object.assign(context.project, row);
|
|
467
|
+
}
|
|
468
|
+
const failed = this._failedProjects.get(projectId);
|
|
469
|
+
if (failed)
|
|
470
|
+
Object.assign(failed.project, row);
|
|
471
|
+
return context?.project ?? row;
|
|
472
|
+
}
|
|
473
|
+
addRepository(projectId, input) {
|
|
474
|
+
const repository = (0, desktop_db_1.addProjectRepository)(this._desktopDb, projectId, input);
|
|
475
|
+
this.refreshProjectRepositories(projectId);
|
|
476
|
+
return repository;
|
|
477
|
+
}
|
|
478
|
+
updateRepository(projectId, repositoryId, input) {
|
|
479
|
+
if (input.path !== undefined) {
|
|
480
|
+
const project = (0, desktop_db_1.getProject)(this._desktopDb, projectId);
|
|
481
|
+
if (!project)
|
|
482
|
+
throw new project_repositories_1.RepositoryValidationError('Project not found', 'project_not_found', 404);
|
|
483
|
+
const current = (0, project_repositories_1.resolveProjectRepository)(project, repositoryId);
|
|
484
|
+
if (!current.isPrimary && (0, project_repositories_1.repositoryPathKey)((0, project_repositories_1.canonicalRepositoryPath)(input.path)) === (0, project_repositories_1.repositoryPathKey)((0, project_repositories_1.canonicalRepositoryPath)(current.path))) {
|
|
485
|
+
input = { ...input };
|
|
486
|
+
delete input.path;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
if (input.path !== undefined)
|
|
490
|
+
this._assertRepositoryUnreferenced(projectId, repositoryId);
|
|
491
|
+
const repository = (0, desktop_db_1.updateProjectRepository)(this._desktopDb, projectId, repositoryId, input);
|
|
492
|
+
this.refreshProjectRepositories(projectId);
|
|
493
|
+
return repository;
|
|
494
|
+
}
|
|
495
|
+
removeRepository(projectId, repositoryId) {
|
|
496
|
+
this._assertRepositoryUnreferenced(projectId, repositoryId);
|
|
497
|
+
(0, desktop_db_1.removeProjectRepository)(this._desktopDb, projectId, repositoryId);
|
|
498
|
+
this.refreshProjectRepositories(projectId);
|
|
499
|
+
}
|
|
500
|
+
_assertRepositoryUnreferenced(projectId, repositoryId) {
|
|
501
|
+
const project = (0, desktop_db_1.getProject)(this._desktopDb, projectId);
|
|
502
|
+
if (!project)
|
|
503
|
+
throw new project_repositories_1.RepositoryValidationError('Project not found', 'project_not_found', 404);
|
|
504
|
+
const repository = (0, project_repositories_1.resolveProjectRepository)(project, repositoryId);
|
|
505
|
+
if (repository.isPrimary)
|
|
506
|
+
throw new project_repositories_1.RepositoryValidationError('The primary repository cannot be removed or relocated here', 'primary_repository_protected', 409);
|
|
507
|
+
const context = this._contexts.get(projectId);
|
|
508
|
+
if (!context)
|
|
509
|
+
throw new project_repositories_1.RepositoryValidationError('Load this project before removing or relocating a repository', 'project_unavailable', 503);
|
|
510
|
+
const execution = (0, workspace_resolution_1.resolveProjectExecution)({ slug: project.slug, path: project.path });
|
|
511
|
+
const store = (0, ticket_store_1.readStore)(execution.relocated ? execution.ticketsPath : (0, ticket_store_1.resolveTicketStoragePath)(project.path));
|
|
512
|
+
const ticketIds = Object.values(store.tickets).filter((ticket) => ticket.repositoryIds?.includes(repositoryId)).map((ticket) => ticket.id);
|
|
513
|
+
const references = (0, multi_repo_execution_store_1.getRepositoryExecutionReferences)(context.db, repositoryId);
|
|
514
|
+
if (ticketIds.length || references.runIds.length || references.deliveryIds.length) {
|
|
515
|
+
throw new project_repositories_1.RepositoryValidationError('Repository is referenced by specs, runs or pending deliveries', 'repository_in_use', 409, { ticketIds, ...references });
|
|
516
|
+
}
|
|
517
|
+
}
|
|
392
518
|
listContexts() {
|
|
393
519
|
return Array.from(this._contexts.values());
|
|
394
520
|
}
|
|
@@ -400,6 +526,9 @@ class ProjectRegistry {
|
|
|
400
526
|
* exiting anyway).
|
|
401
527
|
*/
|
|
402
528
|
shutdown() {
|
|
529
|
+
this._shuttingDown = true;
|
|
530
|
+
for (const id of this._loadRetryTimers.keys())
|
|
531
|
+
this._clearLoadRetry(id);
|
|
403
532
|
for (const ctx of this._contexts.values()) {
|
|
404
533
|
try {
|
|
405
534
|
ctx.queueManager.shutdown();
|
|
@@ -447,6 +576,10 @@ class ProjectRegistry {
|
|
|
447
576
|
ctx.stuckRunDetector.stop();
|
|
448
577
|
}
|
|
449
578
|
catch { /* ignore */ }
|
|
579
|
+
try {
|
|
580
|
+
ctx.milestoneProgress?.dispose();
|
|
581
|
+
}
|
|
582
|
+
catch { /* ignore */ }
|
|
450
583
|
// Release chokidar watchers + abort in-flight generations so a restart
|
|
451
584
|
// does not leak handles/children — mirror removeProject()'s per-project teardown.
|
|
452
585
|
try {
|
|
@@ -473,13 +606,33 @@ class ProjectRegistry {
|
|
|
473
606
|
// A context is visible synchronously, but no subprocess launch may enter
|
|
474
607
|
// until durable loop callbacks and isolated worktrees have been reconciled.
|
|
475
608
|
(0, process_admission_1.beginProjectProcessQuiescence)(project.id);
|
|
476
|
-
|
|
609
|
+
let db;
|
|
610
|
+
try {
|
|
611
|
+
db = (0, db_1.initDb)(project.db_path);
|
|
612
|
+
}
|
|
613
|
+
catch (err) {
|
|
614
|
+
const code = err?.code;
|
|
615
|
+
if (typeof code === 'string' && /^SQLITE_(BUSY|LOCKED)(_|$)/.test(code)) {
|
|
616
|
+
throw new ProjectDatabaseBusyError(err instanceof Error ? err.message : String(err), { cause: err });
|
|
617
|
+
}
|
|
618
|
+
throw err;
|
|
619
|
+
}
|
|
477
620
|
// Bind broadcast with projectId so all WS messages carry context.
|
|
478
621
|
// Also wire agent status: when a queued job reaches a terminal state,
|
|
479
622
|
// clear current_job_id on any agent that was assigned to it.
|
|
623
|
+
// Milestone progress tap (premium-milestone-progress): every project-scoped
|
|
624
|
+
// broadcast that can change a milestone's counts/rails/chain schedules ONE
|
|
625
|
+
// debounced `blueprint.milestone_progress` re-derivation. Assigned below,
|
|
626
|
+
// once the rail maps exist; null until then (startup messages are few).
|
|
627
|
+
let milestoneProgress = null;
|
|
628
|
+
let milestoneChains = null;
|
|
480
629
|
const boundBroadcast = (msg) => {
|
|
481
630
|
const enriched = { ...msg, projectId: project.id };
|
|
482
631
|
this._broadcast(enriched);
|
|
632
|
+
// Chain first: a delivery settle may launch the next chunk (async) and
|
|
633
|
+
// emit its own `milestone.chain_changed`; the progress tap then coalesces.
|
|
634
|
+
milestoneChains?.observe(msg);
|
|
635
|
+
milestoneProgress?.observe(msg);
|
|
483
636
|
if (msg.type === 'queue') {
|
|
484
637
|
for (const job of msg.jobs) {
|
|
485
638
|
if (isTerminalJobStatus(job.status)) {
|
|
@@ -811,7 +964,7 @@ class ProjectRegistry {
|
|
|
811
964
|
}
|
|
812
965
|
},
|
|
813
966
|
});
|
|
814
|
-
const chatManager = new chat_manager_1.ChatManager(boundBroadcast, db, project.path, project.name, project.provider ?? 'claude', project.id, project.slug);
|
|
967
|
+
const chatManager = new chat_manager_1.ChatManager(boundBroadcast, db, project.path, project.name, project.provider ?? 'claude', project.id, project.slug, () => (0, project_repositories_1.getProjectRepositories)(project));
|
|
815
968
|
const setupManager = new setup_manager_1.SetupManager(boundBroadcast, (pid, sid) => (0, desktop_db_1.setProjectSetupSession)(this._desktopDb, pid, sid), (pid) => (0, desktop_db_1.clearProjectSetupSession)(this._desktopDb, pid),
|
|
816
969
|
// LOW-2: resolve the per-project DB so phase-4 setup-chat AI turns record
|
|
817
970
|
// an `ai_invocations` row (surface='setup'). Returns null until the project
|
|
@@ -912,11 +1065,11 @@ class ProjectRegistry {
|
|
|
912
1065
|
// byte-identical except for the explicit per-project passthrough overlay.
|
|
913
1066
|
// See resolveLoopBaseEnv.
|
|
914
1067
|
const loopRunManager = new loop_run_manager_1.LoopRunManager(db, boundBroadcast, (0, loop_executors_1.createLoopExecutors)({
|
|
1068
|
+
pluginScope: () => ({ stateRoot: (0, workspace_resolution_1.resolveProjectExecution)(project).cwd, legacyProviderId: project.provider }),
|
|
915
1069
|
env: () => (0, workspace_resolution_1.resolveLoopBaseEnv)({ slug: project.slug, path: project.path }, undefined, (0, project_env_1.applyWorktreeEnvPassthrough)(db, process.env)),
|
|
916
|
-
//
|
|
917
|
-
//
|
|
918
|
-
//
|
|
919
|
-
// models for the provider — byte-identical legacy behaviour otherwise.
|
|
1070
|
+
// Named rail profiles and global per-agent defaults ride the same
|
|
1071
|
+
// SPECRAILS_PROFILE_PATH snapshot in both AI transports. An explicit null
|
|
1072
|
+
// rail selection opts out; omitted selections keep Core's default rules.
|
|
920
1073
|
profilePathFor: (0, agent_defaults_1.createLoopProfilePathResolver)({
|
|
921
1074
|
desktopDb: this._desktopDb,
|
|
922
1075
|
profileRoot: () => (0, workspace_resolution_1.resolveProjectExecution)({ slug: project.slug, path: project.path }).cwd,
|
|
@@ -945,6 +1098,7 @@ class ProjectRegistry {
|
|
|
945
1098
|
status: t.status,
|
|
946
1099
|
priority: t.priority,
|
|
947
1100
|
labels: t.labels,
|
|
1101
|
+
repositoryIds: t.repositoryIds,
|
|
948
1102
|
jira_key: t.jira_key ?? null,
|
|
949
1103
|
jira_url: t.jira_url ?? null,
|
|
950
1104
|
openspecChangeName: typeof t.metadata?.openspecChangeName === 'string' ? t.metadata.openspecChangeName : undefined,
|
|
@@ -1074,6 +1228,15 @@ class ProjectRegistry {
|
|
|
1074
1228
|
});
|
|
1075
1229
|
const delivered = deliver();
|
|
1076
1230
|
railLoopRuns.delete(runId);
|
|
1231
|
+
// Milestone chain fallback hook: records the engine outcome (names the
|
|
1232
|
+
// pause reason) and settles delivery-less chunks. Isolated chunks
|
|
1233
|
+
// advance from the later `rail.pr_state` settle instead.
|
|
1234
|
+
try {
|
|
1235
|
+
milestoneChains?.onRunSettled(runId, effectiveOutcome, opts?.stallReason);
|
|
1236
|
+
}
|
|
1237
|
+
catch (err) {
|
|
1238
|
+
console.error('[milestone-chain] onRunSettled failed:', err);
|
|
1239
|
+
}
|
|
1077
1240
|
for (const ticketId of delivered.changedIds) {
|
|
1078
1241
|
const ticket = delivered.store?.tickets[String(ticketId)];
|
|
1079
1242
|
if (!ticket)
|
|
@@ -1113,7 +1276,96 @@ class ProjectRegistry {
|
|
|
1113
1276
|
};
|
|
1114
1277
|
const stuckRunDetector = new stuck_run_detector_1.StuckRunDetector(project.id, { db }, boundBroadcast);
|
|
1115
1278
|
stuckRunDetector.start();
|
|
1116
|
-
const
|
|
1279
|
+
const blueprintWorkspace = () => (0, milestone_progress_1.resolveBlueprintWorkspace)({ slug: project.slug, path: project.path });
|
|
1280
|
+
// Milestone launch chain (premium-milestone-progress D3): chunks launch
|
|
1281
|
+
// through the app's OWN rails launch route over loopback (the single
|
|
1282
|
+
// authority for every guard) — never by re-implementing the handler.
|
|
1283
|
+
const internalApi = (0, internal_api_1.createInternalApi)({ port: this._desktopPort });
|
|
1284
|
+
const railsBase = `/projects/${project.id}/rails`;
|
|
1285
|
+
milestoneChains = new milestone_chain_1.MilestoneChainManager(db, project.id, {
|
|
1286
|
+
createRail: async (name) => {
|
|
1287
|
+
const r = await internalApi.call('POST', railsBase, { name });
|
|
1288
|
+
const railIndex = r.body?.rail?.railIndex;
|
|
1289
|
+
return r.ok && typeof railIndex === 'number'
|
|
1290
|
+
? { ok: true, railIndex }
|
|
1291
|
+
: { ok: false, status: r.status, ...(0, internal_api_1.internalApiError)(r) };
|
|
1292
|
+
},
|
|
1293
|
+
assignTickets: async (railIndex, ticketIds) => {
|
|
1294
|
+
const r = await internalApi.call('PUT', `${railsBase}/${railIndex}/tickets`, { ticketIds, mode: 'batch-implement' });
|
|
1295
|
+
return r.ok ? { ok: true } : { ok: false, status: r.status, ...(0, internal_api_1.internalApiError)(r) };
|
|
1296
|
+
},
|
|
1297
|
+
launch: async (railIndex, body) => {
|
|
1298
|
+
const r = await internalApi.call('POST', `${railsBase}/${railIndex}/launch`, body);
|
|
1299
|
+
if (!r.ok)
|
|
1300
|
+
return { ok: false, status: r.status, ...(0, internal_api_1.internalApiError)(r) };
|
|
1301
|
+
const data = (r.body ?? {});
|
|
1302
|
+
const ids = Array.isArray(data.loopRunIds) ? data.loopRunIds
|
|
1303
|
+
: Array.isArray(data.jobIds) ? data.jobIds
|
|
1304
|
+
: typeof data.jobId === 'string' ? [data.jobId] : [];
|
|
1305
|
+
return { ok: true, loopRunIds: ids.filter((v) => typeof v === 'string') };
|
|
1306
|
+
},
|
|
1307
|
+
activeDeliveryForRail: (railIndex) => {
|
|
1308
|
+
const row = (0, rail_pr_store_1.getActivePrDeliveryByRail)(db, railIndex);
|
|
1309
|
+
return row ? (0, rail_pr_store_1.toPrDeliverySnapshot)(row) : null;
|
|
1310
|
+
},
|
|
1311
|
+
findRailByName: (name) => {
|
|
1312
|
+
try {
|
|
1313
|
+
const hit = (0, rails_store_1.getRails)(db).find((r) => r.name === name);
|
|
1314
|
+
return hit ? hit.railIndex : null;
|
|
1315
|
+
}
|
|
1316
|
+
catch {
|
|
1317
|
+
return null;
|
|
1318
|
+
}
|
|
1319
|
+
},
|
|
1320
|
+
getDelivery: (id) => {
|
|
1321
|
+
const row = (0, rail_pr_store_1.getPrDelivery)(db, id);
|
|
1322
|
+
return row ? (0, rail_pr_store_1.toPrDeliverySnapshot)(row) : null;
|
|
1323
|
+
},
|
|
1324
|
+
branchExists: async (branch) => {
|
|
1325
|
+
const r = await worktree_manager_1.defaultGitRunner.run(['rev-parse', '--verify', '--quiet', `refs/heads/${branch}`], project.path);
|
|
1326
|
+
return r.code === 0;
|
|
1327
|
+
},
|
|
1328
|
+
readTickets: () => {
|
|
1329
|
+
try {
|
|
1330
|
+
return Object.values((0, ticket_store_1.readStore)(ticketStorePath()).tickets).map((t) => ({ id: t.id, status: t.status, labels: t.labels ?? [] }));
|
|
1331
|
+
}
|
|
1332
|
+
catch {
|
|
1333
|
+
return [];
|
|
1334
|
+
}
|
|
1335
|
+
},
|
|
1336
|
+
readBlueprint: () => { const ws = blueprintWorkspace(); return ws ? (0, blueprint_render_1.readBlueprint)(ws) : null; },
|
|
1337
|
+
integrationBranch: async () => {
|
|
1338
|
+
try {
|
|
1339
|
+
return (await (0, integration_branch_1.resolveIntegrationBranch)(worktree_manager_1.defaultGitRunner, { repoDir: project.path, projectSetting: (0, db_2.getProjectSettings)(db).integrationBranch })).branch;
|
|
1340
|
+
}
|
|
1341
|
+
catch {
|
|
1342
|
+
return null;
|
|
1343
|
+
}
|
|
1344
|
+
},
|
|
1345
|
+
runState: (runId) => {
|
|
1346
|
+
const run = (0, loop_runs_store_1.getLoopRun)(db, runId);
|
|
1347
|
+
if (!run)
|
|
1348
|
+
return null;
|
|
1349
|
+
return { settled: run.status !== 'running' && run.status !== 'paused', outcome: run.final_outcome ?? null };
|
|
1350
|
+
},
|
|
1351
|
+
broadcast: boundBroadcast,
|
|
1352
|
+
});
|
|
1353
|
+
milestoneProgress = new milestone_progress_1.MilestoneProgressBroadcaster({
|
|
1354
|
+
projectId: project.id,
|
|
1355
|
+
read: () => (0, milestone_progress_1.readMilestoneProgress)({
|
|
1356
|
+
db,
|
|
1357
|
+
projectId: project.id,
|
|
1358
|
+
workspaceDir: blueprintWorkspace,
|
|
1359
|
+
ticketsPath: ticketStorePath,
|
|
1360
|
+
railJobs,
|
|
1361
|
+
railLoopRuns,
|
|
1362
|
+
chains: () => milestoneChains?.listForProgress() ?? [],
|
|
1363
|
+
}),
|
|
1364
|
+
persistDone: (milestoneId) => (0, milestone_progress_1.markMilestoneDone)(blueprintWorkspace(), milestoneId) !== null,
|
|
1365
|
+
broadcast: boundBroadcast,
|
|
1366
|
+
});
|
|
1367
|
+
const ctx = { project, db, queueManager, chatManager, setupManager, proposalManager, agentRefineManager, fileSummaryManager, specLauncherManager, ticketWatcher, browserCaptureManager, jiraSyncManager, stuckRunDetector, broadcast: boundBroadcast, railJobs, loopRunManager, railLoopRuns, onLoopRunFinished, getTicketSpec, desktopDb: this._desktopDb, milestoneProgress };
|
|
1368
|
+
ctx.milestoneChains = milestoneChains;
|
|
1117
1369
|
this._contexts.set(project.id, ctx);
|
|
1118
1370
|
const loopRecoveryOk = this._recoverOrphanLoopRuns(project, db, railLoopRuns, onLoopRunFinished, orphanLoopRuns);
|
|
1119
1371
|
if (loopRecoveryOk) {
|