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
|
@@ -16,16 +16,12 @@ const agent_capability_1 = require("./agent-capability");
|
|
|
16
16
|
function jsonRpcError(message, code = -32000) {
|
|
17
17
|
return { jsonrpc: '2.0', error: { code, message }, id: null };
|
|
18
18
|
}
|
|
19
|
-
|
|
20
|
-
* Embedded MCP server manager. Mirrors the MobileGateway lifecycle shape
|
|
21
|
-
* (isEnabledSetting / setEnabled / start / stop / status) but mounts on the
|
|
22
|
-
* EXISTING Express app rather than binding a second port. Uses the SDK's
|
|
23
|
-
* stateful streamable-HTTP transport (a transport per MCP session).
|
|
24
|
-
*/
|
|
19
|
+
const SESSION_IDLE_MS = 30 * 60 * 1000;
|
|
25
20
|
class McpServerManager {
|
|
26
21
|
_ctx;
|
|
27
22
|
_version;
|
|
28
|
-
|
|
23
|
+
_sessions = new Map();
|
|
24
|
+
_pending = new Set();
|
|
29
25
|
_toolCount;
|
|
30
26
|
constructor(deps) {
|
|
31
27
|
this._ctx = {
|
|
@@ -45,7 +41,7 @@ class McpServerManager {
|
|
|
45
41
|
async setEnabled(enabled) {
|
|
46
42
|
(0, desktop_db_1.setDesktopSetting)(this._ctx.desktopDb, mcp_tiers_1.MCP_ENABLED_KEY, enabled ? 'true' : 'false');
|
|
47
43
|
if (!enabled)
|
|
48
|
-
await this.stop();
|
|
44
|
+
await this.stop(true);
|
|
49
45
|
this._ctx.broadcast({ type: 'mcp.state', enabled, running: enabled });
|
|
50
46
|
return this.status();
|
|
51
47
|
}
|
|
@@ -55,33 +51,53 @@ class McpServerManager {
|
|
|
55
51
|
async start() {
|
|
56
52
|
/* no-op: served on the shared app, gated by isEnabledSetting() */
|
|
57
53
|
}
|
|
58
|
-
/**
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
54
|
+
/** Shutdown/rotation closes everything; the external Settings toggle must
|
|
55
|
+
* preserve first-party turns governed by their independent permission tier. */
|
|
56
|
+
async stop(externalOnly = false) {
|
|
57
|
+
const sessions = [...new Set([...this._sessions.values(), ...this._pending])]
|
|
58
|
+
.filter((session) => !externalOnly || session.capabilityHash === null);
|
|
59
|
+
await Promise.allSettled(sessions.map((session) => this.closeSession(session)));
|
|
60
|
+
}
|
|
61
|
+
async closeSession(session) {
|
|
62
|
+
if (session.closed)
|
|
63
|
+
return;
|
|
64
|
+
this.forgetSession(session);
|
|
65
|
+
try {
|
|
66
|
+
await session.server.close();
|
|
68
67
|
}
|
|
68
|
+
catch { /* best-effort teardown */ }
|
|
69
|
+
}
|
|
70
|
+
forgetSession(session) {
|
|
71
|
+
session.closed = true;
|
|
72
|
+
if (session.timer)
|
|
73
|
+
clearTimeout(session.timer);
|
|
74
|
+
session.unsubscribe?.();
|
|
75
|
+
this._pending.delete(session);
|
|
76
|
+
if (session.transport.sessionId)
|
|
77
|
+
this._sessions.delete(session.transport.sessionId);
|
|
78
|
+
}
|
|
79
|
+
touchSession(session, expiresAt) {
|
|
80
|
+
if (session.timer)
|
|
81
|
+
clearTimeout(session.timer);
|
|
82
|
+
session.timer = setTimeout(() => { void this.closeSession(session); }, Math.max(1, Math.min(SESSION_IDLE_MS, (expiresAt ?? Infinity) - Date.now())));
|
|
83
|
+
session.timer.unref?.();
|
|
69
84
|
}
|
|
70
85
|
status() {
|
|
71
86
|
return {
|
|
72
87
|
enabled: this.isEnabledSetting(),
|
|
73
88
|
running: this.isEnabledSetting(),
|
|
74
|
-
activeSessions:
|
|
89
|
+
activeSessions: this._sessions.size,
|
|
75
90
|
toolCount: this._toolCount,
|
|
76
91
|
};
|
|
77
92
|
}
|
|
78
93
|
/** Build a fresh McpServer with the full tool + resource catalog registered. */
|
|
79
94
|
buildServer() {
|
|
80
95
|
const server = new mcp_js_1.McpServer({ name: 'specrails-desktop', version: this._version }, { capabilities: { logging: {}, tools: {}, resources: {} } });
|
|
96
|
+
const context = { ...this._ctx, sessionState: { activeProjectId: null } };
|
|
81
97
|
for (const spec of (0, catalog_1.buildToolSpecs)()) {
|
|
82
|
-
(0, types_1.registerTieredTool)(server,
|
|
98
|
+
(0, types_1.registerTieredTool)(server, context, spec);
|
|
83
99
|
}
|
|
84
|
-
(0, resources_1.registerResources)(server,
|
|
100
|
+
(0, resources_1.registerResources)(server, context);
|
|
85
101
|
return server;
|
|
86
102
|
}
|
|
87
103
|
/**
|
|
@@ -93,53 +109,83 @@ class McpServerManager {
|
|
|
93
109
|
// The external Settings toggle gates third-party clients. Only a live,
|
|
94
110
|
// server-minted per-turn capability identifies the in-app agent and may
|
|
95
111
|
// bypass it; caller-authored context headers grant no authority.
|
|
96
|
-
const
|
|
97
|
-
|
|
112
|
+
const suppliedCapability = req.headers[agent_tier_1.AGENT_CAPABILITY_HEADER];
|
|
113
|
+
const capability = (0, agent_capability_1.verifyAgentCapability)(suppliedCapability);
|
|
114
|
+
if (suppliedCapability !== undefined && !capability) {
|
|
115
|
+
res.status(401).json(jsonRpcError('Agent capability expired or was revoked. Start a new mission turn.', -32001));
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (!this.isEnabledSetting() && !capability) {
|
|
98
119
|
res.status(404).json(jsonRpcError('MCP is disabled. Enable it in the Specrails app under Settings ▸ MCP.', -32004));
|
|
99
120
|
return;
|
|
100
121
|
}
|
|
101
|
-
const
|
|
122
|
+
const suppliedSession = req.headers['mcp-session-id'];
|
|
123
|
+
if (suppliedSession !== undefined && (typeof suppliedSession !== 'string' || !suppliedSession.trim())) {
|
|
124
|
+
res.status(400).json(jsonRpcError('Invalid mcp-session-id.'));
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const sessionId = suppliedSession;
|
|
128
|
+
const capabilityHash = capability && typeof suppliedCapability === 'string'
|
|
129
|
+
? (0, crypto_1.createHash)('sha256').update(suppliedCapability.trim()).digest('hex') : null;
|
|
130
|
+
let session = sessionId ? this._sessions.get(sessionId) : undefined;
|
|
131
|
+
if (sessionId && !session) {
|
|
132
|
+
// Streamable HTTP clients recognize 404 as an expired session and must
|
|
133
|
+
// initialize again. 400 left SDK clients stuck with the stale id forever.
|
|
134
|
+
res.status(404).json(jsonRpcError('Unknown or expired MCP session. Reinitialize.'));
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (session && session.capabilityHash !== capabilityHash) {
|
|
138
|
+
res.status(403).json(jsonRpcError('MCP session belongs to a different agent turn or client. Reinitialize.'));
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
102
141
|
try {
|
|
103
142
|
if (req.method === 'POST') {
|
|
104
|
-
|
|
105
|
-
if (!transport) {
|
|
106
|
-
if (sessionId) {
|
|
107
|
-
res.status(400).json(jsonRpcError('Unknown or expired MCP session. Reinitialize.'));
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
143
|
+
if (!session) {
|
|
110
144
|
if (!(0, types_js_1.isInitializeRequest)(req.body)) {
|
|
111
145
|
res.status(400).json(jsonRpcError('Missing mcp-session-id and body is not an initialize request.'));
|
|
112
146
|
return;
|
|
113
147
|
}
|
|
114
|
-
|
|
148
|
+
const server = this.buildServer();
|
|
149
|
+
const transport = new streamableHttp_js_1.StreamableHTTPServerTransport({
|
|
115
150
|
sessionIdGenerator: () => (0, crypto_1.randomUUID)(),
|
|
116
151
|
onsessioninitialized: (sid) => {
|
|
117
|
-
this.
|
|
152
|
+
this._pending.delete(created);
|
|
153
|
+
this._sessions.set(sid, created);
|
|
118
154
|
},
|
|
119
155
|
});
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
156
|
+
const created = { transport, server, capabilityHash, closed: false };
|
|
157
|
+
session = created;
|
|
158
|
+
this._pending.add(created);
|
|
159
|
+
transport.onclose = () => { this.forgetSession(created); };
|
|
160
|
+
if (capability && typeof suppliedCapability === 'string') {
|
|
161
|
+
created.unsubscribe = (0, agent_capability_1.onAgentCapabilityRevoked)(suppliedCapability, () => { void this.closeSession(created); });
|
|
162
|
+
}
|
|
126
163
|
await server.connect(transport);
|
|
127
164
|
}
|
|
128
|
-
|
|
165
|
+
if (session.closed) {
|
|
166
|
+
res.status(503).json(jsonRpcError('MCP session closed during initialization. Reconnect.'));
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
this.touchSession(session, capability?.expiresAt);
|
|
170
|
+
await session.transport.handleRequest(req, res, req.body);
|
|
171
|
+
if (!session.transport.sessionId)
|
|
172
|
+
await this.closeSession(session);
|
|
129
173
|
return;
|
|
130
174
|
}
|
|
131
175
|
if (req.method === 'GET' || req.method === 'DELETE') {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
res.status(400).json(jsonRpcError('Missing or unknown mcp-session-id.'));
|
|
176
|
+
if (!session) {
|
|
177
|
+
res.status(400).json(jsonRpcError('Missing mcp-session-id.'));
|
|
135
178
|
return;
|
|
136
179
|
}
|
|
137
|
-
|
|
180
|
+
this.touchSession(session, capability?.expiresAt);
|
|
181
|
+
await session.transport.handleRequest(req, res);
|
|
138
182
|
return;
|
|
139
183
|
}
|
|
140
184
|
res.status(405).json(jsonRpcError('Method not allowed.'));
|
|
141
185
|
}
|
|
142
186
|
catch (err) {
|
|
187
|
+
if (session && !session.transport.sessionId)
|
|
188
|
+
await this.closeSession(session);
|
|
143
189
|
if (!res.headersSent) {
|
|
144
190
|
res.status(500).json(jsonRpcError(err instanceof Error ? err.message : 'Internal MCP error', -32603));
|
|
145
191
|
}
|
|
@@ -7,26 +7,27 @@ exports.tierLabel = tierLabel;
|
|
|
7
7
|
exports.tierRefusalMessage = tierRefusalMessage;
|
|
8
8
|
const desktop_db_1 = require("../desktop-db");
|
|
9
9
|
exports.MCP_TIERS = ['read', 'write', 'ai-spawn', 'destructive'];
|
|
10
|
-
/** desktop_settings keys for the opt-
|
|
10
|
+
/** desktop_settings keys for the opt-out tiers (all on by default; read is implicit). */
|
|
11
11
|
exports.TIER_SETTING_KEY = {
|
|
12
12
|
write: 'mcp_tier_write',
|
|
13
13
|
'ai-spawn': 'mcp_tier_ai_spawn',
|
|
14
14
|
destructive: 'mcp_tier_destructive',
|
|
15
15
|
};
|
|
16
16
|
exports.MCP_ENABLED_KEY = 'mcp_enabled';
|
|
17
|
-
/** True when MCP itself is enabled (default false). */
|
|
17
|
+
/** True when MCP itself is enabled (default TRUE — only an explicit 'false' disables). */
|
|
18
18
|
function isMcpEnabled(db) {
|
|
19
|
-
return (0, desktop_db_1.getDesktopSetting)(db, exports.MCP_ENABLED_KEY)
|
|
19
|
+
return (0, desktop_db_1.getDesktopSetting)(db, exports.MCP_ENABLED_KEY) !== 'false';
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
22
|
* True when a tool of the given tier may run right now. Read is always allowed
|
|
23
23
|
* (its availability is governed by MCP being enabled, checked at the transport
|
|
24
|
-
* gate). The other tiers
|
|
24
|
+
* gate). The other tiers default ON and are disabled only when the user has
|
|
25
|
+
* explicitly stored 'false' for them (opt-out).
|
|
25
26
|
*/
|
|
26
27
|
function isTierEnabled(db, tier) {
|
|
27
28
|
if (tier === 'read')
|
|
28
29
|
return true;
|
|
29
|
-
return (0, desktop_db_1.getDesktopSetting)(db, exports.TIER_SETTING_KEY[tier])
|
|
30
|
+
return (0, desktop_db_1.getDesktopSetting)(db, exports.TIER_SETTING_KEY[tier]) !== 'false';
|
|
30
31
|
}
|
|
31
32
|
/** Human-readable label for a tier, used in refusal messages. */
|
|
32
33
|
function tierLabel(tier) {
|
|
@@ -84,8 +84,10 @@ function getMcpToken() {
|
|
|
84
84
|
*/
|
|
85
85
|
function regenerateMcpToken() {
|
|
86
86
|
const next = generateToken();
|
|
87
|
+
// Commit the file before switching the live credential. If persistence
|
|
88
|
+
// fails, bridges must still authenticate with the existing on-disk token.
|
|
89
|
+
(0, secure_token_file_1.writePrivateTextFile)(tokenPath(), next);
|
|
87
90
|
_mcpToken = next;
|
|
88
|
-
persist(next);
|
|
89
91
|
return next;
|
|
90
92
|
}
|
|
91
93
|
/** Extracts a bearer token from `Authorization: Bearer <t>` or `X-Desktop-Token`. */
|
|
@@ -4,6 +4,7 @@ exports.registerResources = registerResources;
|
|
|
4
4
|
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
5
5
|
const guide_1 = require("./guide");
|
|
6
6
|
const projects_1 = require("./tools/projects");
|
|
7
|
+
const desktop_db_1 = require("../desktop-db");
|
|
7
8
|
// Read-only state exposed as MCP resources. Read-tier only (resources are never
|
|
8
9
|
// mutating). Per-project resource templates (tickets, rails, analytics) are
|
|
9
10
|
// added in subsequent phases.
|
|
@@ -12,13 +13,13 @@ function registerResources(server, ctx) {
|
|
|
12
13
|
contents: [{ uri: uri.href, text: guide_1.SPECRAILS_GUIDE }],
|
|
13
14
|
}));
|
|
14
15
|
server.registerResource('projects', 'specrails://projects', { title: 'Projects', description: 'All registered Specrails projects.', mimeType: 'application/json' }, async (uri) => ({
|
|
15
|
-
contents: [{ uri: uri.href, text: JSON.stringify(ctx.
|
|
16
|
+
contents: [{ uri: uri.href, text: JSON.stringify((0, desktop_db_1.listProjects)(ctx.desktopDb).map(projects_1.serializeProject), null, 2) }],
|
|
16
17
|
}));
|
|
17
18
|
server.registerResource('project', new mcp_js_1.ResourceTemplate('specrails://projects/{projectId}', { list: undefined }), { title: 'Project', description: 'A single project by id.', mimeType: 'application/json' }, async (uri, variables) => {
|
|
18
19
|
const projectId = String(variables.projectId);
|
|
19
|
-
const
|
|
20
|
-
if (!
|
|
20
|
+
const project = (0, desktop_db_1.getProject)(ctx.desktopDb, projectId);
|
|
21
|
+
if (!project)
|
|
21
22
|
throw new Error(`Unknown projectId "${projectId}".`);
|
|
22
|
-
return { contents: [{ uri: uri.href, text: JSON.stringify((0, projects_1.serializeProject)(
|
|
23
|
+
return { contents: [{ uri: uri.href, text: JSON.stringify((0, projects_1.serializeProject)(project), null, 2) }] };
|
|
23
24
|
});
|
|
24
25
|
}
|
|
@@ -19,6 +19,8 @@ const analytics_1 = require("./analytics");
|
|
|
19
19
|
const git_1 = require("./git");
|
|
20
20
|
const support_1 = require("./support");
|
|
21
21
|
const env_1 = require("./env");
|
|
22
|
+
const context_1 = require("./context");
|
|
23
|
+
const mission_1 = require("./mission");
|
|
22
24
|
/**
|
|
23
25
|
* The full MCP tool catalog. Domain-facade tools each expose an `action` enum
|
|
24
26
|
* and call the app's REST API over loopback (see apiCall). Meta tools receive a
|
|
@@ -28,6 +30,8 @@ function buildToolSpecs() {
|
|
|
28
30
|
const specs = [];
|
|
29
31
|
// App-level + meta + watch
|
|
30
32
|
specs.push(...(0, projects_1.projectsTools)());
|
|
33
|
+
specs.push(...(0, context_1.contextTools)());
|
|
34
|
+
specs.push(...(0, mission_1.missionTools)());
|
|
31
35
|
specs.push(...(0, app_settings_1.appTools)());
|
|
32
36
|
specs.push((0, watch_1.watchTool)());
|
|
33
37
|
// Per-project domains
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.codeTools = codeTools;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const types_1 = require("./types");
|
|
6
|
+
const project_repositories_1 = require("../../project-repositories");
|
|
6
7
|
/**
|
|
7
8
|
* Read-only Code Explorer surface for a project: the AI-touched file tree,
|
|
8
9
|
* file contents + plain-language AI summaries, "touched by AI" provenance, and
|
|
@@ -23,29 +24,47 @@ function codeTools() {
|
|
|
23
24
|
name: 'specrails_code',
|
|
24
25
|
title: 'Code Explorer',
|
|
25
26
|
description: 'Read-only browse a project\'s source through the Code Explorer: list the file tree ' +
|
|
26
|
-
'(default scoped to AI-touched files, optional provenance rollup),
|
|
27
|
-
'
|
|
28
|
-
'AI summary (cost-incurring, async), list "touched by
|
|
29
|
-
'stored unified-diff a job applied to a file. ' +
|
|
30
|
-
'Actions: tree,
|
|
27
|
+
'(default scoped to AI-touched files, optional provenance rollup), locate a file by name / ' +
|
|
28
|
+
'path suffix, read a bounded file range with a stable content hash, fetch a stored ' +
|
|
29
|
+
'summary alone, regenerate a file\'s AI summary (cost-incurring, async), list "touched by ' +
|
|
30
|
+
'AI" provenance rows, and fetch the stored unified-diff a job applied to a file. ' +
|
|
31
|
+
'Actions: tree, find (name / path-suffix / fragment → ranked project-relative paths; use it ' +
|
|
32
|
+
'when read_file 404s — a path copied from a stack trace or import is usually relative to a ' +
|
|
33
|
+
'subdirectory), search (literal source content, scoped path and explicit scan budgets), ' +
|
|
34
|
+
'read_file (bounded line/column pages with hash and continuation, default 200 lines), summary, regenerate_summary (ai-spawn — spawns an AI CLI, ' +
|
|
31
35
|
'incurs cost, returns 202 then completes over WS), provenance, diff. ' +
|
|
32
36
|
'Requires SPECRAILS_CODE_EXPLORER (default on); the /code prefix 404s when disabled.',
|
|
33
37
|
hintTier: 'read',
|
|
34
38
|
tier: (a) => (a.action === 'regenerate_summary' ? 'ai-spawn' : 'read'),
|
|
35
39
|
inputSchema: {
|
|
36
40
|
action: zod_1.z
|
|
37
|
-
.enum(['tree', 'read_file', 'summary', 'regenerate_summary', 'provenance', 'diff'])
|
|
41
|
+
.enum(['tree', 'find', 'search', 'read_file', 'summary', 'regenerate_summary', 'provenance', 'diff'])
|
|
38
42
|
.describe('Operation to perform'),
|
|
39
43
|
projectId: zod_1.z.string().optional().describe('Project id (defaults to the active project)'),
|
|
44
|
+
repositoryId: zod_1.z.string().min(1).optional().describe('Repository membership ID. Required for individual tree/file/summary/provenance/diff actions in multi-repository projects. Omit for find/search to discover across members under one shared budget.'),
|
|
40
45
|
path: zod_1.z
|
|
41
46
|
.string()
|
|
42
47
|
.optional()
|
|
43
48
|
.describe('Project-relative file path. Required for read_file/summary/regenerate_summary/diff; ' +
|
|
44
|
-
'optional narrowing filter for provenance.'),
|
|
49
|
+
'optional directory/file prefix for search or narrowing filter for provenance.'),
|
|
50
|
+
file: zod_1.z
|
|
51
|
+
.string()
|
|
52
|
+
.optional()
|
|
53
|
+
.describe('Compatibility alias for path (accepted for read_file/summary/regenerate_summary/diff)'),
|
|
54
|
+
query: zod_1.z
|
|
55
|
+
.string()
|
|
56
|
+
.optional()
|
|
57
|
+
.describe('search: literal single-line source text (not regex). find: file name, path suffix (e.g. components/detail/LessonView.tsx) or fragment ' +
|
|
58
|
+
'to locate, case-insensitive. Falls back to path/file when omitted.'),
|
|
45
59
|
filter: zod_1.z
|
|
46
60
|
.enum(['touched-by-ai', 'all'])
|
|
47
61
|
.optional()
|
|
48
62
|
.describe('tree only: scope to AI-touched files (default) or the whole repo'),
|
|
63
|
+
caseSensitive: zod_1.z.boolean().optional().describe('search only: match exact character case (default false)'),
|
|
64
|
+
startLine: zod_1.z.number().int().positive().optional().describe('read_file only: first line, 1-based (default 1)'),
|
|
65
|
+
endLine: zod_1.z.number().int().positive().optional().describe('read_file only: last line inclusive (default startLine + 199; at most 500 lines returned)'),
|
|
66
|
+
startColumn: zod_1.z.number().int().positive().optional().describe('read_file only: resume at nextColumn when a long line was split by the character budget (default 1)'),
|
|
67
|
+
expectedHash: zod_1.z.string().regex(/^[a-f0-9]{64}$/i).optional().describe('read_file only: hash from the previous page/search; rejects changed files so pages cannot silently mix revisions'),
|
|
49
68
|
withProvenance: zod_1.z
|
|
50
69
|
.boolean()
|
|
51
70
|
.optional()
|
|
@@ -54,6 +73,13 @@ function codeTools() {
|
|
|
54
73
|
.string()
|
|
55
74
|
.optional()
|
|
56
75
|
.describe('tree only: opaque pagination cursor from a prior response\'s nextCursor'),
|
|
76
|
+
limit: zod_1.z
|
|
77
|
+
.number()
|
|
78
|
+
.int()
|
|
79
|
+
.positive()
|
|
80
|
+
.max(500)
|
|
81
|
+
.optional()
|
|
82
|
+
.describe('tree: entries per page (default 200, maximum 500); find: matches (default 20, max 50); search: matching lines (default 30, max 100)'),
|
|
57
83
|
ticketId: zod_1.z
|
|
58
84
|
.number()
|
|
59
85
|
.int()
|
|
@@ -71,14 +97,47 @@ function codeTools() {
|
|
|
71
97
|
'incurs cost beyond the configured limit.'),
|
|
72
98
|
},
|
|
73
99
|
async handler(ctx, args) {
|
|
74
|
-
const base = (0, types_1.projectPath)(ctx, args.projectId);
|
|
75
100
|
const action = args.action;
|
|
101
|
+
const projectId = args.projectId;
|
|
102
|
+
const repositoryId = args.repositoryId;
|
|
103
|
+
if (repositoryId === undefined && ['find', 'search'].includes(action) && (0, project_repositories_1.getProjectRepositories)((0, types_1.requireProject)(ctx, projectId).project).length > 1) {
|
|
104
|
+
const query = (args.query ?? args.path ?? args.file);
|
|
105
|
+
if (!query?.trim())
|
|
106
|
+
throw new Error(`Action "${action}" requires a "query".`);
|
|
107
|
+
const qs = new URLSearchParams({ kind: action, q: query });
|
|
108
|
+
if (args.limit !== undefined)
|
|
109
|
+
qs.set('limit', String(args.limit));
|
|
110
|
+
if (action === 'search' && args.path)
|
|
111
|
+
qs.set('path', String(args.path));
|
|
112
|
+
if (args.caseSensitive === true)
|
|
113
|
+
qs.set('caseSensitive', 'true');
|
|
114
|
+
return (0, types_1.apiCall)(ctx, 'GET', `${(0, types_1.projectPath)(ctx, projectId)}/code/discover?${qs}`);
|
|
115
|
+
}
|
|
116
|
+
const base = (0, types_1.repositoryPath)(ctx, projectId, repositoryId);
|
|
76
117
|
const requirePath = () => {
|
|
77
|
-
const p = args.path;
|
|
118
|
+
const p = (args.path ?? args.file);
|
|
78
119
|
if (!p || p.trim() === '')
|
|
79
120
|
throw new Error(`Action "${action}" requires a "path".`);
|
|
80
121
|
return p;
|
|
81
122
|
};
|
|
123
|
+
// A bare `→ 404: {"error":"file not found"}` sent callers into a
|
|
124
|
+
// ToolSearch/retry loop: the path they hold is usually right, just
|
|
125
|
+
// relative to a subdirectory. Point them at `find` instead of leaving
|
|
126
|
+
// them to page through `tree`.
|
|
127
|
+
const withNotFoundHint = async (p, run) => {
|
|
128
|
+
try {
|
|
129
|
+
return await run();
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
133
|
+
if (!/→ 404: .*file not found/.test(msg))
|
|
134
|
+
throw err;
|
|
135
|
+
const base = p.replace(/\\/g, '/').replace(/\/+$/, '').split('/').pop() || p;
|
|
136
|
+
throw new Error(`${msg}. Paths are relative to the project root and "${p}" may live under a subdirectory ` +
|
|
137
|
+
'(a path copied from a stack trace or import usually does). Locate it with ' +
|
|
138
|
+
`specrails_code(action: "find", query: ${JSON.stringify(base)}) and retry with the returned path.`);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
82
141
|
switch (action) {
|
|
83
142
|
case 'tree': {
|
|
84
143
|
const qs = new URLSearchParams();
|
|
@@ -88,20 +147,72 @@ function codeTools() {
|
|
|
88
147
|
qs.set('withProvenance', '1');
|
|
89
148
|
if (args.cursor)
|
|
90
149
|
qs.set('cursor', args.cursor);
|
|
150
|
+
qs.set('limit', String(typeof args.limit === 'number' ? args.limit : 200));
|
|
91
151
|
if (typeof args.ticketId === 'number')
|
|
92
152
|
qs.set('ticketId', String(args.ticketId));
|
|
93
153
|
if (args.jobId)
|
|
94
154
|
qs.set('jobId', args.jobId);
|
|
95
155
|
const suffix = qs.toString() ? `?${qs.toString()}` : '';
|
|
96
|
-
|
|
156
|
+
const result = await (0, types_1.apiCall)(ctx, 'GET', `${base}/code/tree${suffix}`);
|
|
157
|
+
if (args.withProvenance === true || !Array.isArray(result.entries))
|
|
158
|
+
return result;
|
|
159
|
+
return {
|
|
160
|
+
...result,
|
|
161
|
+
entries: result.entries.map((entry) => {
|
|
162
|
+
const item = entry;
|
|
163
|
+
return { path: item.path, kind: item.kind, ...(item.repositoryId ? { repositoryId: item.repositoryId } : {}) };
|
|
164
|
+
}),
|
|
165
|
+
hint: result.nextCursor
|
|
166
|
+
? 'More files are available. Call tree again with this nextCursor; prefer targeted read_file calls.'
|
|
167
|
+
: 'Tree complete. Prefer targeted read_file calls.',
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
case 'find': {
|
|
171
|
+
const q = (args.query ?? args.path ?? args.file)?.trim();
|
|
172
|
+
if (!q)
|
|
173
|
+
throw new Error('Action "find" requires a "query" (file name, path suffix or fragment).');
|
|
174
|
+
const qs = new URLSearchParams({ q });
|
|
175
|
+
qs.set('limit', String(typeof args.limit === 'number' ? Math.min(args.limit, 50) : 20));
|
|
176
|
+
const r = (await (0, types_1.apiCall)(ctx, 'GET', `${base}/code/find?${qs.toString()}`));
|
|
177
|
+
const matches = Array.isArray(r.matches) ? r.matches : [];
|
|
178
|
+
return {
|
|
179
|
+
...r,
|
|
180
|
+
hint: matches.length
|
|
181
|
+
? 'Call read_file with one of these project-relative paths (best match first).'
|
|
182
|
+
: r.truncated
|
|
183
|
+
? 'Partial scan found no matches; this does not prove absence. Narrow the query and retry or inspect the project tree.'
|
|
184
|
+
: 'No file matches. The scan skips build/dependency trees, dot-directories and gitignored paths; ' +
|
|
185
|
+
'try a shorter fragment or the bare file name.',
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
case 'search': {
|
|
189
|
+
const query = args.query;
|
|
190
|
+
if (!query?.trim())
|
|
191
|
+
throw new Error('Action "search" requires a literal source-text "query".');
|
|
192
|
+
if (typeof args.limit === 'number' && args.limit > 100)
|
|
193
|
+
throw new Error('Action "search" limit must be at most 100.');
|
|
194
|
+
const qs = new URLSearchParams({ q: query, limit: String(args.limit ?? 30), caseSensitive: String(args.caseSensitive === true) });
|
|
195
|
+
if (args.path)
|
|
196
|
+
qs.set('path', args.path);
|
|
197
|
+
return (0, types_1.apiCall)(ctx, 'GET', `${base}/code/search?${qs.toString()}`);
|
|
97
198
|
}
|
|
98
199
|
case 'read_file': {
|
|
99
|
-
const
|
|
100
|
-
|
|
200
|
+
const p = requirePath();
|
|
201
|
+
const startLine = typeof args.startLine === 'number' ? args.startLine : 1;
|
|
202
|
+
const endLine = typeof args.endLine === 'number' ? args.endLine : startLine + 199;
|
|
203
|
+
if (endLine < startLine)
|
|
204
|
+
throw new Error('endLine must be greater than or equal to startLine.');
|
|
205
|
+
const qs = new URLSearchParams({ path: p, startLine: String(startLine), endLine: String(endLine) });
|
|
206
|
+
if (args.startColumn !== undefined)
|
|
207
|
+
qs.set('startColumn', String(args.startColumn));
|
|
208
|
+
if (args.expectedHash)
|
|
209
|
+
qs.set('expectedHash', args.expectedHash);
|
|
210
|
+
return withNotFoundHint(p, () => (0, types_1.apiCall)(ctx, 'GET', `${base}/code/file?${qs.toString()}`));
|
|
101
211
|
}
|
|
102
212
|
case 'summary': {
|
|
103
|
-
const
|
|
104
|
-
|
|
213
|
+
const p = requirePath();
|
|
214
|
+
const qs = new URLSearchParams({ path: p });
|
|
215
|
+
return withNotFoundHint(p, () => (0, types_1.apiCall)(ctx, 'GET', `${base}/code/summary?${qs.toString()}`));
|
|
105
216
|
}
|
|
106
217
|
case 'regenerate_summary': {
|
|
107
218
|
const qs = new URLSearchParams({ path: requirePath() });
|