specrails-desktop 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/specrails/batch-implement.md +287 -0
- package/.claude/commands/specrails/compat-check.md +271 -0
- package/.claude/commands/specrails/doctor.md +62 -0
- package/.claude/commands/specrails/enrich.md +1635 -0
- package/.claude/commands/specrails/explore-spec.md +173 -0
- package/.claude/commands/specrails/health-check.md +527 -0
- package/.claude/commands/specrails/implement.md +1457 -0
- package/.claude/commands/specrails/memory-inspect.md +259 -0
- package/.claude/commands/specrails/opsx-diff.md +419 -0
- package/.claude/commands/specrails/propose-spec.md +102 -0
- package/.claude/commands/specrails/reconfig.md +89 -0
- package/.claude/commands/specrails/refactor-recommender.md +212 -0
- package/.claude/commands/specrails/retry.md +363 -0
- package/.claude/commands/specrails/telemetry.md +552 -0
- package/.claude/commands/specrails/why.md +96 -0
- package/LICENSE +21 -0
- package/README.md +290 -0
- package/cli/dist/specrails-desktop.js +1098 -0
- package/client/dist/assets/ActivityFeedPage-Gy4x8dBt.js +1 -0
- package/client/dist/assets/AgentsPage-CPgu--Fb.js +86 -0
- package/client/dist/assets/AnalyticsPage-B5sJEee2.js +1 -0
- package/client/dist/assets/BarChart-7IMQ8HY1.js +33 -0
- package/client/dist/assets/CodePage-CBdFvbwe.js +2 -0
- package/client/dist/assets/DesktopAnalyticsPage-w0rdTq4w.js +1 -0
- package/client/dist/assets/DocsDialog-BZUYM7wm.js +11 -0
- package/client/dist/assets/DocsPage-9QglWl46.js +11 -0
- package/client/dist/assets/ExportDropdown-BLZFXtNi.js +1 -0
- package/client/dist/assets/IntegrationsPage-BxBE4y99.js +3 -0
- package/client/dist/assets/JobDetailPage-DydWx_5S.js +16 -0
- package/client/dist/assets/JobsPage-20ibw0IO.js +1 -0
- package/client/dist/assets/abap-Bw6f2wDG.js +1 -0
- package/client/dist/assets/activity-BEIp_Y1A.js +1 -0
- package/client/dist/assets/activity-BdrPln96.js +1 -0
- package/client/dist/assets/activity-CpkRS8Sx.js +1 -0
- package/client/dist/assets/activity-DKCpESPt.js +1 -0
- package/client/dist/assets/activity-DOUVEjJi.js +1 -0
- package/client/dist/assets/activity-DRwkql_y.js +1 -0
- package/client/dist/assets/activity-DcDQ7tjw.js +1 -0
- package/client/dist/assets/activity-Dv6H7wEr.js +1 -0
- package/client/dist/assets/addon-image-3WCl5Vhd.js +1 -0
- package/client/dist/assets/addon-ligatures-C5OdliKs.js +2 -0
- package/client/dist/assets/addon-webgl-BbX6pSjl.js +44 -0
- package/client/dist/assets/addspec-B5yl4Loj.js +1 -0
- package/client/dist/assets/addspec-BEeF5-zc.js +1 -0
- package/client/dist/assets/addspec-D33ocMxf.js +1 -0
- package/client/dist/assets/addspec-DFswZ0jK.js +1 -0
- package/client/dist/assets/addspec-DRE-jZv7.js +1 -0
- package/client/dist/assets/addspec-DVZ15Jp8.js +1 -0
- package/client/dist/assets/addspec-Fkv91Opc.js +1 -0
- package/client/dist/assets/addspec-GWm4ffKl.js +1 -0
- package/client/dist/assets/agents-1nCDWRmP.js +1 -0
- package/client/dist/assets/agents-Bm9rPqnt.js +1 -0
- package/client/dist/assets/agents-CMxtJMLD.js +1 -0
- package/client/dist/assets/agents-DK-Dlc0i.js +1 -0
- package/client/dist/assets/agents-Q6Ldfpxx.js +1 -0
- package/client/dist/assets/agents-TeOSy-ax.js +1 -0
- package/client/dist/assets/agents-iTqjRajS.js +1 -0
- package/client/dist/assets/agents-s87sMGzL.js +1 -0
- package/client/dist/assets/agentstudio-B6Wb59E7.js +1 -0
- package/client/dist/assets/agentstudio-BADhZ41e.js +1 -0
- package/client/dist/assets/agentstudio-BSnWLR63.js +1 -0
- package/client/dist/assets/agentstudio-BdidyBzZ.js +1 -0
- package/client/dist/assets/agentstudio-CxlUllqI.js +1 -0
- package/client/dist/assets/agentstudio-D3I62TLJ.js +1 -0
- package/client/dist/assets/agentstudio-DuH9TogZ.js +1 -0
- package/client/dist/assets/agentstudio-Kw88_dUF.js +1 -0
- package/client/dist/assets/aiedit-BWxHGsYA.js +1 -0
- package/client/dist/assets/aiedit-D2ji6Qy0.js +1 -0
- package/client/dist/assets/aiedit-DAhZTvtk.js +1 -0
- package/client/dist/assets/aiedit-DJMny-D5.js +1 -0
- package/client/dist/assets/aiedit-DOcxERkU.js +1 -0
- package/client/dist/assets/aiedit-DvrcbwGv.js +1 -0
- package/client/dist/assets/aiedit-TTwzL1TS.js +1 -0
- package/client/dist/assets/aiedit-WBSjT_C1.js +1 -0
- package/client/dist/assets/analytics-BIdr0YfL.js +1 -0
- package/client/dist/assets/analytics-C6EzgtdE.js +1 -0
- package/client/dist/assets/analytics-C9Zc-rkM.js +1 -0
- package/client/dist/assets/analytics-CVx3YOc0.js +1 -0
- package/client/dist/assets/analytics-CYj0tfj7.js +1 -0
- package/client/dist/assets/analytics-CnY4kNG3.js +1 -0
- package/client/dist/assets/analytics-CrPCZRJ-.js +1 -0
- package/client/dist/assets/analytics-DMCto-TF.js +1 -0
- package/client/dist/assets/apex-Cw8_REBo.js +1 -0
- package/client/dist/assets/atom-one-dark-B-oHczHB.css +1 -0
- package/client/dist/assets/attachments-BIsSSnHJ.js +1 -0
- package/client/dist/assets/attachments-BW4L3l2L.js +1 -0
- package/client/dist/assets/attachments-Bcf6BG6V.js +1 -0
- package/client/dist/assets/attachments-Bke8sCU4.js +1 -0
- package/client/dist/assets/attachments-COcrGRFz.js +1 -0
- package/client/dist/assets/attachments-DYHGA2Dj.js +1 -0
- package/client/dist/assets/attachments-Dd92KpUH.js +1 -0
- package/client/dist/assets/attachments-DzdU6DV6.js +1 -0
- package/client/dist/assets/azcli-Cz6HAoOw.js +1 -0
- package/client/dist/assets/bat-CcJ-xyqL.js +1 -0
- package/client/dist/assets/bicep-z1WDCKYz.js +2 -0
- package/client/dist/assets/browser-5ErDlJoR.js +1 -0
- package/client/dist/assets/browser-Bc-YdlVg.js +1 -0
- package/client/dist/assets/browser-BlYF4OOq.js +1 -0
- package/client/dist/assets/browser-CT-ReZGt.js +1 -0
- package/client/dist/assets/browser-DGITz3fC.js +1 -0
- package/client/dist/assets/browser-JsAIGCEW.js +1 -0
- package/client/dist/assets/browser-M5-rbPlw.js +1 -0
- package/client/dist/assets/browser-Qya9cARy.js +1 -0
- package/client/dist/assets/cameligo-BRewOpfa.js +1 -0
- package/client/dist/assets/chat-BEGuC03z.js +1 -0
- package/client/dist/assets/chat-BEW60P_u.js +1 -0
- package/client/dist/assets/chat-BQNMD0PL.js +1 -0
- package/client/dist/assets/chat-BsbNGPW9.js +1 -0
- package/client/dist/assets/chat-CboQguCi.js +1 -0
- package/client/dist/assets/chat-DRCa9pOt.js +1 -0
- package/client/dist/assets/chat-DwUm6W9z.js +1 -0
- package/client/dist/assets/chat-yoXwguQu.js +1 -0
- package/client/dist/assets/chunk-CilyBKbf.js +1 -0
- package/client/dist/assets/clojure-DBjRWN6g.js +1 -0
- package/client/dist/assets/clsx-DnqN-uhr.js +1 -0
- package/client/dist/assets/code-AL1rVIMb.js +1 -0
- package/client/dist/assets/code-C0BKpkht.js +1 -0
- package/client/dist/assets/code-C0FTS3ew.js +1 -0
- package/client/dist/assets/code-CPcHxzxw.js +1 -0
- package/client/dist/assets/code-D3ryDniw.js +1 -0
- package/client/dist/assets/code-D3zVVQTj.js +1 -0
- package/client/dist/assets/code-PCmfS3dn.js +1 -0
- package/client/dist/assets/code-exI0G5Wd.js +1 -0
- package/client/dist/assets/codicon-ngg6Pgfi.ttf +0 -0
- package/client/dist/assets/coffee-Cfk_XHGR.js +1 -0
- package/client/dist/assets/commands-B772IyDa.js +1 -0
- package/client/dist/assets/commands-BDDp6xFG.js +1 -0
- package/client/dist/assets/commands-CJxCry-o.js +1 -0
- package/client/dist/assets/commands-CfgY-_of.js +1 -0
- package/client/dist/assets/commands-DLrvnPNg.js +1 -0
- package/client/dist/assets/commands-IXMOKBYt.js +1 -0
- package/client/dist/assets/commands-UD1NzmwX.js +1 -0
- package/client/dist/assets/commands-sqrqsxyE.js +1 -0
- package/client/dist/assets/common-DCr6VzJ7.js +1 -0
- package/client/dist/assets/common-Dard9UNH.js +1 -0
- package/client/dist/assets/common-DeDELLZJ.js +1 -0
- package/client/dist/assets/common-DltqHaAe.js +1 -0
- package/client/dist/assets/common-Dmm1GhdD.js +1 -0
- package/client/dist/assets/common-DnjcgkPH.js +1 -0
- package/client/dist/assets/common-GbpxfPG8.js +1 -0
- package/client/dist/assets/common-wA36jmj1.js +1 -0
- package/client/dist/assets/cpp-BVob6BaP.js +1 -0
- package/client/dist/assets/csharp-C4fbRuOu.js +1 -0
- package/client/dist/assets/csp-DthFP_vT.js +1 -0
- package/client/dist/assets/css-CGMH0hcW.js +3 -0
- package/client/dist/assets/css.worker-Wv5dxAWO.js +89 -0
- package/client/dist/assets/cssMode-Cc6ozl-J.js +1 -0
- package/client/dist/assets/cypher-Pnf68BRV.js +1 -0
- package/client/dist/assets/dart-PMMOtxZX.js +1 -0
- package/client/dist/assets/dashboard-B4ixDVk8.js +1 -0
- package/client/dist/assets/dashboard-BZBADHSj.js +1 -0
- package/client/dist/assets/dashboard-C1MfeUHs.js +1 -0
- package/client/dist/assets/dashboard-C7SK6xu5.js +1 -0
- package/client/dist/assets/dashboard-CB6Le1yN.js +1 -0
- package/client/dist/assets/dashboard-CoTpMOBM.js +1 -0
- package/client/dist/assets/dashboard-Duo4DDCW.js +1 -0
- package/client/dist/assets/dashboard-I19DXBxw.js +1 -0
- package/client/dist/assets/dist-js-BY-Fv_fg.js +1 -0
- package/client/dist/assets/dist-js-Bakc4uxT.js +1 -0
- package/client/dist/assets/dockerfile-di1nsJCc.js +1 -0
- package/client/dist/assets/ecl-D_WVcB5M.js +1 -0
- package/client/dist/assets/editor-Br_kD0ds.css +1 -0
- package/client/dist/assets/editor.api2-XLGzZfbc.js +872 -0
- package/client/dist/assets/editor.main-CfXxHimg.js +6 -0
- package/client/dist/assets/editor.worker-Bd9IXS8d.js +26 -0
- package/client/dist/assets/elixir-OAdJEMOn.js +1 -0
- package/client/dist/assets/explore-4mFpnrKU.js +1 -0
- package/client/dist/assets/explore-A8Ltoblq.js +1 -0
- package/client/dist/assets/explore-B9A3iN2W.js +1 -0
- package/client/dist/assets/explore-BV5Xxlsn.js +1 -0
- package/client/dist/assets/explore-BrBJvfjP.js +1 -0
- package/client/dist/assets/explore-C3FSE42C.js +1 -0
- package/client/dist/assets/explore-D2EFgt8J.js +1 -0
- package/client/dist/assets/explore-hFc3HFcp.js +1 -0
- package/client/dist/assets/flow9-D3QEZjgn.js +1 -0
- package/client/dist/assets/format-command-CwGuwzGA.js +1 -0
- package/client/dist/assets/freemarker2-DP7J1gG3.js +3 -0
- package/client/dist/assets/fsharp-BF0k_8N8.js +1 -0
- package/client/dist/assets/go-BAQO5Jsz.js +1 -0
- package/client/dist/assets/graphql-hdFVFkiV.js +1 -0
- package/client/dist/assets/handlebars-BjRlucw6.js +1 -0
- package/client/dist/assets/hcl-DWnl1o-X.js +1 -0
- package/client/dist/assets/html-OumBQJ-U.js +1 -0
- package/client/dist/assets/html.worker-CQP8QQsS.js +502 -0
- package/client/dist/assets/htmlMode-CStc3zXM.js +1 -0
- package/client/dist/assets/index-CimDRRi7.css +2 -0
- package/client/dist/assets/index-XGZaKl_u.js +142 -0
- package/client/dist/assets/ini-CB-6OVu3.js +1 -0
- package/client/dist/assets/integrations-C3p12Ms6.js +1 -0
- package/client/dist/assets/integrations-Cr6hH7XR.js +1 -0
- package/client/dist/assets/integrations-Cublz3m6.js +1 -0
- package/client/dist/assets/integrations-D28q1kF6.js +1 -0
- package/client/dist/assets/integrations-DRdbki5W.js +1 -0
- package/client/dist/assets/integrations-DaC4SzzL.js +1 -0
- package/client/dist/assets/integrations-DmQYCUvN.js +1 -0
- package/client/dist/assets/integrations-HIlUxXVs.js +1 -0
- package/client/dist/assets/java-d1CmfiHX.js +1 -0
- package/client/dist/assets/javascript-CMk--e7g.js +1 -0
- package/client/dist/assets/jobs-BE1siB0M.js +1 -0
- package/client/dist/assets/jobs-BHcQ_Faf.js +1 -0
- package/client/dist/assets/jobs-CFfc2dNX.js +1 -0
- package/client/dist/assets/jobs-CSi5n8X_.js +1 -0
- package/client/dist/assets/jobs-Dc3X86PY.js +1 -0
- package/client/dist/assets/jobs-De5tASex.js +1 -0
- package/client/dist/assets/jobs-DsoXEdo7.js +1 -0
- package/client/dist/assets/jobs-Wl-ApPMb.js +1 -0
- package/client/dist/assets/json.worker-DzV-CpCQ.js +58 -0
- package/client/dist/assets/jsonMode-C2h3ZcjZ.js +7 -0
- package/client/dist/assets/julia-Bgv08lKa.js +1 -0
- package/client/dist/assets/kotlin-u98kaVTf.js +1 -0
- package/client/dist/assets/less-CjYwpgg5.js +2 -0
- package/client/dist/assets/lexon-YTjaAFBB.js +1 -0
- package/client/dist/assets/lib-CPxTMOAq.js +7 -0
- package/client/dist/assets/liquid-mI3KJrBE.js +1 -0
- package/client/dist/assets/lspLanguageFeatures-DU09ggWi.js +4 -0
- package/client/dist/assets/lua-BzmkWv27.js +1 -0
- package/client/dist/assets/m3-CFwk9fw0.js +1 -0
- package/client/dist/assets/markdown-CR5iMpSZ.js +1 -0
- package/client/dist/assets/mdx-C41VDTR_.js +1 -0
- package/client/dist/assets/mips-CcEalc17.js +1 -0
- package/client/dist/assets/monaco.contribution-CPObAXMC.js +2 -0
- package/client/dist/assets/msdax-BQbkawnr.js +1 -0
- package/client/dist/assets/mysql-GTlaaW_P.js +1 -0
- package/client/dist/assets/nav-0fwkrgHt.js +1 -0
- package/client/dist/assets/nav-BEL3MTwK.js +1 -0
- package/client/dist/assets/nav-B_G-TJDW.js +1 -0
- package/client/dist/assets/nav-C2YXcbZS.js +1 -0
- package/client/dist/assets/nav-ClzOE4mA.js +1 -0
- package/client/dist/assets/nav-CtYwmMgu.js +1 -0
- package/client/dist/assets/nav-D2bOGSEg.js +1 -0
- package/client/dist/assets/nav-iH1V5j6o.js +1 -0
- package/client/dist/assets/objective-c-Byu1T5if.js +1 -0
- package/client/dist/assets/pascal-BrfzBfRm.js +1 -0
- package/client/dist/assets/pascaligo-BXXKFUeo.js +1 -0
- package/client/dist/assets/perl-B3OikKq-.js +1 -0
- package/client/dist/assets/pgsql-CTsa0Acc.js +1 -0
- package/client/dist/assets/php-DiQh3FUW.js +1 -0
- package/client/dist/assets/pla-92uH8Fzm.js +1 -0
- package/client/dist/assets/postiats-BbeWkKUr.js +1 -0
- package/client/dist/assets/powerquery-DgDMzpsm.js +1 -0
- package/client/dist/assets/powershell-BfdUUzaG.js +1 -0
- package/client/dist/assets/preload-helper-DSXbuxSR.js +1 -0
- package/client/dist/assets/protobuf-BojW2ftW.js +2 -0
- package/client/dist/assets/pug-BxqTg3IU.js +1 -0
- package/client/dist/assets/python-Y27rKQtk.js +1 -0
- package/client/dist/assets/qsharp-BX_A-MW9.js +1 -0
- package/client/dist/assets/r-D9BMnxvJ.js +1 -0
- package/client/dist/assets/razor-Cd5-q9Bp.js +1 -0
- package/client/dist/assets/redis-5cJqEQJJ.js +1 -0
- package/client/dist/assets/redshift-d8BBqiwb.js +1 -0
- package/client/dist/assets/restructuredtext-C8a6yIcZ.js +1 -0
- package/client/dist/assets/ruby-egeh-6KX.js +1 -0
- package/client/dist/assets/rust-a3r9IInB.js +1 -0
- package/client/dist/assets/sb-y8iRIDei.js +1 -0
- package/client/dist/assets/scala-BPDK2AmK.js +1 -0
- package/client/dist/assets/scheme-BIWUEoOs.js +1 -0
- package/client/dist/assets/scss-CA-PSzwg.js +3 -0
- package/client/dist/assets/settings-55oDcbSh.js +1 -0
- package/client/dist/assets/settings-Bd4Tq1RB.js +1 -0
- package/client/dist/assets/settings-CCSM-Fhn.js +1 -0
- package/client/dist/assets/settings-D3e_bDoW.js +1 -0
- package/client/dist/assets/settings-DKbTkbn7.js +1 -0
- package/client/dist/assets/settings-Dxpo6_w7.js +1 -0
- package/client/dist/assets/settings-bt84e3Aa.js +1 -0
- package/client/dist/assets/settings-nu68QukM.js +1 -0
- package/client/dist/assets/setup-BMqwfbW9.js +1 -0
- package/client/dist/assets/setup-Bb5LcG28.js +1 -0
- package/client/dist/assets/setup-BeEx2_da.js +1 -0
- package/client/dist/assets/setup-CCCrB53Q.js +1 -0
- package/client/dist/assets/setup-CJA0ATmd.js +1 -0
- package/client/dist/assets/setup-CeiDbZcb.js +1 -0
- package/client/dist/assets/setup-Cus7TApA.js +1 -0
- package/client/dist/assets/setup-D9qOs2Xo.js +1 -0
- package/client/dist/assets/shell--LiT1Bja.js +1 -0
- package/client/dist/assets/solidity-DdqZccZg.js +1 -0
- package/client/dist/assets/sophia-S6-YxNG_.js +1 -0
- package/client/dist/assets/sparql-BSf5kMp2.js +1 -0
- package/client/dist/assets/specs-BFfu3u-a.js +1 -0
- package/client/dist/assets/specs-B__C8-8a.js +1 -0
- package/client/dist/assets/specs-CZ1PsXsC.js +1 -0
- package/client/dist/assets/specs-D2FzlLn9.js +1 -0
- package/client/dist/assets/specs-DaUTrNF9.js +1 -0
- package/client/dist/assets/specs-Dyc5hYeE.js +1 -0
- package/client/dist/assets/specs-cKEh2LXt.js +1 -0
- package/client/dist/assets/specs-k0PyLDVt.js +1 -0
- package/client/dist/assets/sql-D7KgjR8G.js +1 -0
- package/client/dist/assets/st-BnoDa-Ml.js +1 -0
- package/client/dist/assets/swift-DEUHTkUX.js +1 -0
- package/client/dist/assets/systemverilog-Tqb_KPnW.js +1 -0
- package/client/dist/assets/tcl-BmBFS2qq.js +1 -0
- package/client/dist/assets/terminal-80yDMgMF.js +1 -0
- package/client/dist/assets/terminal-Bje4ziIa.js +1 -0
- package/client/dist/assets/terminal-C2WYcFHF.js +1 -0
- package/client/dist/assets/terminal-CSONJOex.js +1 -0
- package/client/dist/assets/terminal-DEqzGtcr.js +1 -0
- package/client/dist/assets/terminal-DeWzh6ys.js +1 -0
- package/client/dist/assets/terminal-YOlsJCQj.js +1 -0
- package/client/dist/assets/terminal-lkZYR4wJ.js +1 -0
- package/client/dist/assets/tickets-CB7N30gm.js +1 -0
- package/client/dist/assets/tickets-CF2PYelu.js +1 -0
- package/client/dist/assets/tickets-DNOANUXr.js +1 -0
- package/client/dist/assets/tickets-DU1aqsbr.js +1 -0
- package/client/dist/assets/tickets-DYvafSaY.js +1 -0
- package/client/dist/assets/tickets-DlpC_iTg.js +1 -0
- package/client/dist/assets/tickets-DucYgtdl.js +1 -0
- package/client/dist/assets/tickets-clefmXLv.js +1 -0
- package/client/dist/assets/ts.worker-METxwbDZ.js +67719 -0
- package/client/dist/assets/tsMode-B0y_xEci.js +11 -0
- package/client/dist/assets/twig-BQV8igWC.js +1 -0
- package/client/dist/assets/typescript-BzK0OgwW.js +1 -0
- package/client/dist/assets/typespec-DlFroUGY.js +1 -0
- package/client/dist/assets/useProjectCache-DSaiGFjV.js +1 -0
- package/client/dist/assets/vb-BlrJpIMX.js +1 -0
- package/client/dist/assets/wgsl-BWgIc6FZ.js +298 -0
- package/client/dist/assets/workers-rt--R2Qy.js +1 -0
- package/client/dist/assets/xml-eX9QXAmI.js +1 -0
- package/client/dist/assets/yaml-fcsNkpOt.js +1 -0
- package/client/dist/index.html +246 -0
- package/docs/README.md +54 -0
- package/docs/cli.md +198 -0
- package/docs/codex.md +210 -0
- package/docs/creating-specs.md +197 -0
- package/docs/customizing.md +197 -0
- package/docs/getting-started.md +140 -0
- package/docs/internals/README.md +25 -0
- package/docs/internals/adding-a-provider.md +238 -0
- package/docs/internals/api-reference.md +634 -0
- package/docs/internals/architecture.md +332 -0
- package/docs/internals/configuration.md +172 -0
- package/docs/internals/openspec-workflow.md +282 -0
- package/docs/internals/operations-runbook.md +198 -0
- package/docs/internals/profiles.md +152 -0
- package/docs/platforms/macos.md +130 -0
- package/docs/platforms/windows.md +81 -0
- package/docs/running-pipelines.md +240 -0
- package/docs/terminal.md +138 -0
- package/docs/tracking-cost.md +155 -0
- package/package.json +82 -0
- package/server/dist/agent-generator.js +232 -0
- package/server/dist/agent-refine-db.js +124 -0
- package/server/dist/agent-refine-manager.js +526 -0
- package/server/dist/ai-invocations.js +111 -0
- package/server/dist/attachment-manager.js +299 -0
- package/server/dist/auth.js +207 -0
- package/server/dist/binary-probe.js +35 -0
- package/server/dist/browser-capture-manager.js +576 -0
- package/server/dist/browser-capture-types.js +28 -0
- package/server/dist/browser-network.js +149 -0
- package/server/dist/browser-playwright.js +888 -0
- package/server/dist/build-dirs.js +44 -0
- package/server/dist/changes-reader.js +120 -0
- package/server/dist/chat-manager.js +1060 -0
- package/server/dist/chromium-resolver.js +311 -0
- package/server/dist/code-explorer-router.js +788 -0
- package/server/dist/codex-otel-bridge.js +235 -0
- package/server/dist/command-resolver.js +102 -0
- package/server/dist/config.js +306 -0
- package/server/dist/context-budget.js +113 -0
- package/server/dist/context-scope.js +279 -0
- package/server/dist/contract-refine-runner.js +521 -0
- package/server/dist/core-compat.js +207 -0
- package/server/dist/core-package.js +14 -0
- package/server/dist/db.js +1034 -0
- package/server/dist/desktop-analytics.js +156 -0
- package/server/dist/desktop-db.js +456 -0
- package/server/dist/desktop-router.js +735 -0
- package/server/dist/docs-router.js +207 -0
- package/server/dist/explore-contract-refine.js +421 -0
- package/server/dist/explore-cwd-manager.js +242 -0
- package/server/dist/explore-draft-title.js +47 -0
- package/server/dist/explore-smash.js +450 -0
- package/server/dist/feature-flags.js +17 -0
- package/server/dist/file-provenance.js +382 -0
- package/server/dist/file-summary-generator.js +221 -0
- package/server/dist/file-summary-manager.js +689 -0
- package/server/dist/hooks.js +102 -0
- package/server/dist/ids.js +7 -0
- package/server/dist/index.js +586 -0
- package/server/dist/metrics.js +136 -0
- package/server/dist/mobile/index.js +16 -0
- package/server/dist/mobile/mobile-admin-router.js +84 -0
- package/server/dist/mobile/mobile-auth.js +67 -0
- package/server/dist/mobile/mobile-devices.js +80 -0
- package/server/dist/mobile/mobile-event-bus.js +39 -0
- package/server/dist/mobile/mobile-gateway.js +285 -0
- package/server/dist/mobile/mobile-mdns.js +81 -0
- package/server/dist/mobile/mobile-pairing.js +179 -0
- package/server/dist/mobile/mobile-redact.js +53 -0
- package/server/dist/mobile/mobile-router.js +411 -0
- package/server/dist/mobile/mobile-tls.js +86 -0
- package/server/dist/mobile/mobile-types.js +9 -0
- package/server/dist/mobile/mobile-ws.js +275 -0
- package/server/dist/path-resolver.js +298 -0
- package/server/dist/plugin-manager.js +617 -0
- package/server/dist/plugins/claude-approval.js +179 -0
- package/server/dist/plugins/claude-md-mutation.js +146 -0
- package/server/dist/plugins/codex-mcp.js +108 -0
- package/server/dist/plugins/contributors.js +72 -0
- package/server/dist/plugins/drift.js +58 -0
- package/server/dist/plugins/index.js +14 -0
- package/server/dist/plugins/json-mutation.js +120 -0
- package/server/dist/plugins/manager.js +32 -0
- package/server/dist/plugins/ownership.js +86 -0
- package/server/dist/plugins/paths.js +37 -0
- package/server/dist/plugins/prereq-installer.js +104 -0
- package/server/dist/plugins/rail-integration.js +79 -0
- package/server/dist/plugins/serena/index.js +13 -0
- package/server/dist/plugins/serena/install.js +91 -0
- package/server/dist/plugins/serena/instructions-content.js +21 -0
- package/server/dist/plugins/serena/manifest.js +111 -0
- package/server/dist/plugins/serena/verify.js +78 -0
- package/server/dist/plugins-router.js +215 -0
- package/server/dist/pricing.js +89 -0
- package/server/dist/profile-manager.js +310 -0
- package/server/dist/profiles-router.js +759 -0
- package/server/dist/project-registry.js +443 -0
- package/server/dist/project-router.js +4016 -0
- package/server/dist/proposal-manager.js +291 -0
- package/server/dist/provider-selection.js +69 -0
- package/server/dist/providers/claude-adapter.js +281 -0
- package/server/dist/providers/codex-adapter.js +264 -0
- package/server/dist/providers/index.js +23 -0
- package/server/dist/providers/registry.js +37 -0
- package/server/dist/providers/types.js +22 -0
- package/server/dist/queue-manager.js +1511 -0
- package/server/dist/rails-router.js +362 -0
- package/server/dist/rails-store.js +116 -0
- package/server/dist/result-event.js +106 -0
- package/server/dist/schemas/profile.v1.json +151 -0
- package/server/dist/setup-manager.js +1165 -0
- package/server/dist/setup-prerequisites.js +372 -0
- package/server/dist/smash-runner.js +663 -0
- package/server/dist/spec-draft-parser.js +133 -0
- package/server/dist/spec-launcher-manager.js +174 -0
- package/server/dist/spec-models.js +32 -0
- package/server/dist/specrails-tech-client.js +82 -0
- package/server/dist/spending.js +448 -0
- package/server/dist/telemetry-compactor.js +180 -0
- package/server/dist/telemetry-export.js +317 -0
- package/server/dist/telemetry-receiver.js +224 -0
- package/server/dist/terminal-manager.js +633 -0
- package/server/dist/terminal-marks-store.js +117 -0
- package/server/dist/terminal-osc-parser.js +159 -0
- package/server/dist/terminal-settings.js +282 -0
- package/server/dist/terminal-shell-integration.js +196 -0
- package/server/dist/ticket-broadcast.js +47 -0
- package/server/dist/ticket-store.js +397 -0
- package/server/dist/ticket-watcher.js +117 -0
- package/server/dist/types.js +10 -0
- package/server/dist/user-mcp-config.js +117 -0
- package/server/dist/util/cli-prompt.js +181 -0
- package/server/dist/util/secure-fs.js +50 -0
- package/server/dist/util/win-spawn.js +43 -0
- package/server/dist/webhook-manager.js +89 -0
- package/server/dist/ws-routing.js +47 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getClaudeApprovalState = getClaudeApprovalState;
|
|
7
|
+
exports.findEnabledMarketplaceKeys = findEnabledMarketplaceKeys;
|
|
8
|
+
exports.findInstalledMarketplaceKeys = findInstalledMarketplaceKeys;
|
|
9
|
+
exports.findInstalledButNotEnabledMarketplaceKeys = findInstalledButNotEnabledMarketplaceKeys;
|
|
10
|
+
exports.disableMarketplacePlugin = disableMarketplacePlugin;
|
|
11
|
+
const fs_1 = __importDefault(require("fs"));
|
|
12
|
+
const os_1 = __importDefault(require("os"));
|
|
13
|
+
const path_1 = __importDefault(require("path"));
|
|
14
|
+
/**
|
|
15
|
+
* Returns the approval state for `<projectPath>` × `<serverName>`. The state
|
|
16
|
+
* is sourced from EITHER:
|
|
17
|
+
*
|
|
18
|
+
* 1. `~/.claude.json` → projects[<path>].{enabled,disabled}McpjsonServers —
|
|
19
|
+
* the per-project approval set when the user accepts (or denies) the
|
|
20
|
+
* `.mcp.json` prompt the first time Claude opens the repo.
|
|
21
|
+
*
|
|
22
|
+
* 2. `~/.claude/settings.json` → enabledPlugins["<serverName>@*"] — Claude
|
|
23
|
+
* Code's plugin-marketplace mechanism. When a user installs an MCP via
|
|
24
|
+
* the marketplace, Serena (or similar) is available in EVERY project
|
|
25
|
+
* regardless of `.mcp.json`. The card should reflect that the tool is
|
|
26
|
+
* loaded, not that our app-managed `.mcp.json` entry is dormant.
|
|
27
|
+
*
|
|
28
|
+
* Resolution order:
|
|
29
|
+
* - explicit disable in (1) → `disabled`
|
|
30
|
+
* - explicit enable in (1) OR (2) → `enabled`
|
|
31
|
+
* - otherwise → `pending`
|
|
32
|
+
*
|
|
33
|
+
* Defensive: any I/O / parse error falls back to 'pending'. Never throws.
|
|
34
|
+
*/
|
|
35
|
+
function getClaudeApprovalState(projectPath, serverName) {
|
|
36
|
+
// (1) Per-project mcpjson approval.
|
|
37
|
+
// Order:
|
|
38
|
+
// - explicit disabled list → disabled
|
|
39
|
+
// - explicit enabled list → enabled
|
|
40
|
+
// - trusted project → enabled (Claude auto-loads .mcp.json
|
|
41
|
+
// without prompting once trust is granted)
|
|
42
|
+
// - none of the above → pending (first prompt not yet shown)
|
|
43
|
+
let mcpjsonState = 'pending';
|
|
44
|
+
try {
|
|
45
|
+
const configPath = path_1.default.join(os_1.default.homedir(), '.claude.json');
|
|
46
|
+
if (fs_1.default.existsSync(configPath)) {
|
|
47
|
+
const parsed = JSON.parse(fs_1.default.readFileSync(configPath, 'utf8'));
|
|
48
|
+
const entry = parsed.projects?.[path_1.default.resolve(projectPath)];
|
|
49
|
+
if (entry) {
|
|
50
|
+
if (entry.disabledMcpjsonServers?.includes(serverName))
|
|
51
|
+
mcpjsonState = 'disabled';
|
|
52
|
+
else if (entry.enabledMcpjsonServers?.includes(serverName))
|
|
53
|
+
mcpjsonState = 'enabled';
|
|
54
|
+
else if (entry.hasTrustDialogAccepted === true)
|
|
55
|
+
mcpjsonState = 'enabled';
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// ignore — falls through to marketplace check
|
|
61
|
+
}
|
|
62
|
+
// Explicit disabled wins over marketplace enable.
|
|
63
|
+
if (mcpjsonState === 'disabled')
|
|
64
|
+
return 'disabled';
|
|
65
|
+
// (2) Claude marketplace plugin (per-user, project-agnostic).
|
|
66
|
+
let marketplaceEnabled = false;
|
|
67
|
+
try {
|
|
68
|
+
const settingsPath = path_1.default.join(os_1.default.homedir(), '.claude', 'settings.json');
|
|
69
|
+
if (fs_1.default.existsSync(settingsPath)) {
|
|
70
|
+
const settings = JSON.parse(fs_1.default.readFileSync(settingsPath, 'utf8'));
|
|
71
|
+
const enabled = settings.enabledPlugins ?? {};
|
|
72
|
+
const prefix = `${serverName}@`;
|
|
73
|
+
for (const [key, value] of Object.entries(enabled)) {
|
|
74
|
+
if (value === true && key.startsWith(prefix)) {
|
|
75
|
+
marketplaceEnabled = true;
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// ignore
|
|
83
|
+
}
|
|
84
|
+
if (mcpjsonState === 'enabled' || marketplaceEnabled)
|
|
85
|
+
return 'enabled';
|
|
86
|
+
return 'pending';
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Returns the keys (e.g., `serena@claude-plugins-official`) under
|
|
90
|
+
* `~/.claude/settings.json#enabledPlugins` that match `<serverName>@*` and
|
|
91
|
+
* are set to `true`. Empty array if none.
|
|
92
|
+
*/
|
|
93
|
+
function findEnabledMarketplaceKeys(serverName) {
|
|
94
|
+
try {
|
|
95
|
+
const settingsPath = path_1.default.join(os_1.default.homedir(), '.claude', 'settings.json');
|
|
96
|
+
if (!fs_1.default.existsSync(settingsPath))
|
|
97
|
+
return [];
|
|
98
|
+
const settings = JSON.parse(fs_1.default.readFileSync(settingsPath, 'utf8'));
|
|
99
|
+
const enabled = settings.enabledPlugins ?? {};
|
|
100
|
+
const prefix = `${serverName}@`;
|
|
101
|
+
return Object.entries(enabled)
|
|
102
|
+
.filter(([k, v]) => v === true && k.startsWith(prefix))
|
|
103
|
+
.map(([k]) => k);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return [];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Returns the keys present in `~/.claude/plugins/installed_plugins.json`
|
|
111
|
+
* that match `<serverName>@*`, regardless of the `enabledPlugins` toggle.
|
|
112
|
+
* Used to detect plugins that are physically in Claude's cache and may be
|
|
113
|
+
* loaded by Claude even when `enabledPlugins[key]=false` (observed: Claude
|
|
114
|
+
* keeps them resolvable from the cache `.mcp.json`).
|
|
115
|
+
*/
|
|
116
|
+
function findInstalledMarketplaceKeys(serverName) {
|
|
117
|
+
try {
|
|
118
|
+
const filePath = path_1.default.join(os_1.default.homedir(), '.claude', 'plugins', 'installed_plugins.json');
|
|
119
|
+
if (!fs_1.default.existsSync(filePath))
|
|
120
|
+
return [];
|
|
121
|
+
const parsed = JSON.parse(fs_1.default.readFileSync(filePath, 'utf8'));
|
|
122
|
+
const map = parsed.plugins ?? {};
|
|
123
|
+
const prefix = `${serverName}@`;
|
|
124
|
+
return Object.keys(map).filter((k) => k.startsWith(prefix));
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Returns marketplace keys that are physically installed in Claude's plugin
|
|
132
|
+
* cache but NOT enabled (i.e., `enabledPlugins[key] !== true`). These are
|
|
133
|
+
* the ones likely to confuse users — the app disabled them but the binary +
|
|
134
|
+
* shipped `.mcp.json` are still on disk; Claude may still resolve the server.
|
|
135
|
+
*/
|
|
136
|
+
function findInstalledButNotEnabledMarketplaceKeys(serverName) {
|
|
137
|
+
const installed = findInstalledMarketplaceKeys(serverName);
|
|
138
|
+
if (installed.length === 0)
|
|
139
|
+
return [];
|
|
140
|
+
let enabledMap = {};
|
|
141
|
+
try {
|
|
142
|
+
const settingsPath = path_1.default.join(os_1.default.homedir(), '.claude', 'settings.json');
|
|
143
|
+
if (fs_1.default.existsSync(settingsPath)) {
|
|
144
|
+
const settings = JSON.parse(fs_1.default.readFileSync(settingsPath, 'utf8'));
|
|
145
|
+
enabledMap = settings.enabledPlugins ?? {};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch { /* fall through */ }
|
|
149
|
+
return installed.filter((k) => enabledMap[k] !== true);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Mutates `~/.claude/settings.json` so the named marketplace plugin is set
|
|
153
|
+
* to `false` (disabling it). Atomic temp+rename. Returns true on success.
|
|
154
|
+
*
|
|
155
|
+
* NOTE: this is the only place the app touches Claude's per-user config.
|
|
156
|
+
* Surface to the user via an explicit action (button/CLI), never silently.
|
|
157
|
+
*/
|
|
158
|
+
function disableMarketplacePlugin(marketplaceKey) {
|
|
159
|
+
try {
|
|
160
|
+
const settingsPath = path_1.default.join(os_1.default.homedir(), '.claude', 'settings.json');
|
|
161
|
+
if (!fs_1.default.existsSync(settingsPath)) {
|
|
162
|
+
return { ok: false, reason: 'settings-not-found' };
|
|
163
|
+
}
|
|
164
|
+
const raw = fs_1.default.readFileSync(settingsPath, 'utf8');
|
|
165
|
+
const settings = JSON.parse(raw);
|
|
166
|
+
settings.enabledPlugins ??= {};
|
|
167
|
+
if (settings.enabledPlugins[marketplaceKey] === false) {
|
|
168
|
+
return { ok: true, reason: 'already-disabled' };
|
|
169
|
+
}
|
|
170
|
+
settings.enabledPlugins[marketplaceKey] = false;
|
|
171
|
+
const tmp = `${settingsPath}.${process.pid}.${Date.now()}.tmp`;
|
|
172
|
+
fs_1.default.writeFileSync(tmp, JSON.stringify(settings, null, 2) + '\n', 'utf8');
|
|
173
|
+
fs_1.default.renameSync(tmp, settingsPath);
|
|
174
|
+
return { ok: true };
|
|
175
|
+
}
|
|
176
|
+
catch (err) {
|
|
177
|
+
return { ok: false, reason: err.message };
|
|
178
|
+
}
|
|
179
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getBlockContent = getBlockContent;
|
|
7
|
+
exports.upsertBlock = upsertBlock;
|
|
8
|
+
exports.removeBlock = removeBlock;
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const json_mutation_1 = require("./json-mutation");
|
|
12
|
+
/**
|
|
13
|
+
* Plugins contribute named sections to the project's top-level instructions
|
|
14
|
+
* file (`CLAUDE.md` for claude projects, `AGENTS.md` for codex projects, and
|
|
15
|
+
* the adapter-declared filename for future providers) so the agent's global
|
|
16
|
+
* context includes per-plugin usage hints (e.g. "prefer Serena tools over
|
|
17
|
+
* raw Read/Grep when locating symbols"). Each plugin owns one named block
|
|
18
|
+
* delimited by HTML-comment markers; multiple plugins coexist without
|
|
19
|
+
* stomping each other.
|
|
20
|
+
*
|
|
21
|
+
* <!-- specrails-desktop-managed:<plugin>:start -->
|
|
22
|
+
* ...content...
|
|
23
|
+
* <!-- specrails-desktop-managed:<plugin>:end -->
|
|
24
|
+
*
|
|
25
|
+
* Operations are surgical, atomic (temp+rename), and serialized via the
|
|
26
|
+
* shared in-process file mutex.
|
|
27
|
+
*
|
|
28
|
+
* The `filename` parameter on every function defaults to `'CLAUDE.md'` so
|
|
29
|
+
* existing callsites compile unchanged during the multi-provider migration.
|
|
30
|
+
* Callers that know the resolved provider pass `adapter.instructionsFilename`
|
|
31
|
+
* explicitly.
|
|
32
|
+
*/
|
|
33
|
+
function startMarker(pluginName) {
|
|
34
|
+
return `<!-- specrails-desktop-managed:${pluginName}:start -->`;
|
|
35
|
+
}
|
|
36
|
+
function endMarker(pluginName) {
|
|
37
|
+
return `<!-- specrails-desktop-managed:${pluginName}:end -->`;
|
|
38
|
+
}
|
|
39
|
+
// Legacy marker compat — blocks written by pre-rebrand "Specrails Hub"
|
|
40
|
+
// installs used the `specrails-hub-managed` namespace. We still locate (and
|
|
41
|
+
// therefore replace/remove) those blocks so existing user files never end up
|
|
42
|
+
// with orphaned managed sections. New writes always use the new markers.
|
|
43
|
+
function legacyStartMarker(pluginName) {
|
|
44
|
+
return `<!-- specrails-hub-managed:${pluginName}:start -->`;
|
|
45
|
+
}
|
|
46
|
+
function legacyEndMarker(pluginName) {
|
|
47
|
+
return `<!-- specrails-hub-managed:${pluginName}:end -->`;
|
|
48
|
+
}
|
|
49
|
+
function instructionsMdPath(projectPath, filename) {
|
|
50
|
+
return path_1.default.join(projectPath, filename);
|
|
51
|
+
}
|
|
52
|
+
/** Backwards-compat alias for callers that haven't been threaded the
|
|
53
|
+
* adapter-driven filename yet. */
|
|
54
|
+
function claudeMdPath(projectPath) {
|
|
55
|
+
return instructionsMdPath(projectPath, 'CLAUDE.md');
|
|
56
|
+
}
|
|
57
|
+
function locateBlockWithMarkers(text, start, end) {
|
|
58
|
+
const sIdx = text.indexOf(start);
|
|
59
|
+
if (sIdx < 0)
|
|
60
|
+
return null;
|
|
61
|
+
const eIdx = text.indexOf(end, sIdx + start.length);
|
|
62
|
+
if (eIdx < 0)
|
|
63
|
+
return null;
|
|
64
|
+
const innerStart = sIdx + start.length;
|
|
65
|
+
const innerEnd = eIdx;
|
|
66
|
+
return {
|
|
67
|
+
start: sIdx,
|
|
68
|
+
end: eIdx + end.length,
|
|
69
|
+
content: text.slice(innerStart, innerEnd).replace(/^\n+|\n+$/g, ''),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function locateBlock(text, pluginName) {
|
|
73
|
+
return (locateBlockWithMarkers(text, startMarker(pluginName), endMarker(pluginName)) ??
|
|
74
|
+
// Fall back to the legacy pre-rebrand markers (see legacyStartMarker).
|
|
75
|
+
locateBlockWithMarkers(text, legacyStartMarker(pluginName), legacyEndMarker(pluginName)));
|
|
76
|
+
}
|
|
77
|
+
/** Returns the current managed-block content for `pluginName`, or null if absent. */
|
|
78
|
+
function getBlockContent(projectPath, pluginName, filename = 'CLAUDE.md') {
|
|
79
|
+
const file = instructionsMdPath(projectPath, filename);
|
|
80
|
+
if (!fs_1.default.existsSync(file))
|
|
81
|
+
return null;
|
|
82
|
+
const text = fs_1.default.readFileSync(file, 'utf8');
|
|
83
|
+
const match = locateBlock(text, pluginName);
|
|
84
|
+
return match ? match.content : null;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Insert or replace the managed block for `pluginName`. If the instructions
|
|
88
|
+
* file does not exist, it is created with just the block. If it exists
|
|
89
|
+
* without our block, the block is appended at the end (separated by a blank
|
|
90
|
+
* line). The filename defaults to `CLAUDE.md` for backwards compatibility;
|
|
91
|
+
* codex projects pass `AGENTS.md`.
|
|
92
|
+
*/
|
|
93
|
+
async function upsertBlock(projectPath, pluginName, content, filename = 'CLAUDE.md') {
|
|
94
|
+
const file = instructionsMdPath(projectPath, filename);
|
|
95
|
+
const block = `${startMarker(pluginName)}\n${content.trim()}\n${endMarker(pluginName)}`;
|
|
96
|
+
await (0, json_mutation_1.withFileLock)(file, async () => {
|
|
97
|
+
let next;
|
|
98
|
+
if (!fs_1.default.existsSync(file)) {
|
|
99
|
+
next = block + '\n';
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
const cur = fs_1.default.readFileSync(file, 'utf8');
|
|
103
|
+
const match = locateBlock(cur, pluginName);
|
|
104
|
+
if (match) {
|
|
105
|
+
next = cur.slice(0, match.start) + block + cur.slice(match.end);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
const sep = cur.endsWith('\n\n') ? '' : cur.endsWith('\n') ? '\n' : '\n\n';
|
|
109
|
+
next = cur + sep + block + '\n';
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
(0, json_mutation_1.atomicWriteFileSync)(file, next);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Remove the managed block for `pluginName`. No-op when the instructions
|
|
117
|
+
* file is missing or the block is absent. Surrounding user content is
|
|
118
|
+
* preserved byte-identical.
|
|
119
|
+
*
|
|
120
|
+
* If removing the block leaves the file empty (the managed block was the
|
|
121
|
+
* only content), the file is deleted — this restores the pre-install state
|
|
122
|
+
* for projects that didn't have an instructions file before any plugin
|
|
123
|
+
* install. The filename defaults to `CLAUDE.md`.
|
|
124
|
+
*/
|
|
125
|
+
async function removeBlock(projectPath, pluginName, filename = 'CLAUDE.md') {
|
|
126
|
+
const file = instructionsMdPath(projectPath, filename);
|
|
127
|
+
if (!fs_1.default.existsSync(file))
|
|
128
|
+
return;
|
|
129
|
+
await (0, json_mutation_1.withFileLock)(file, async () => {
|
|
130
|
+
const cur = fs_1.default.readFileSync(file, 'utf8');
|
|
131
|
+
const match = locateBlock(cur, pluginName);
|
|
132
|
+
if (!match)
|
|
133
|
+
return;
|
|
134
|
+
// Strip the block plus a single trailing newline if present, so we don't
|
|
135
|
+
// leave a stray blank line behind.
|
|
136
|
+
let next = cur.slice(0, match.start) + cur.slice(match.end);
|
|
137
|
+
next = next.replace(/\n{3,}$/, '\n').replace(/^\n+/, '');
|
|
138
|
+
if (next.trim() === '') {
|
|
139
|
+
fs_1.default.unlinkSync(file);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
(0, json_mutation_1.atomicWriteFileSync)(file, next);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
// Suppress unused-warning for the legacy alias — kept for API ergonomics.
|
|
146
|
+
void claudeMdPath;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Codex MCP registration helpers. Plugins on codex projects don't write
|
|
3
|
+
// `<project>/.mcp.json`; instead, the app invokes `codex mcp add` against
|
|
4
|
+
// a per-project `CODEX_HOME` so the resulting MCP registration is scoped
|
|
5
|
+
// to that project alone, never leaking into the user's terminal codex
|
|
6
|
+
// state or another managed project.
|
|
7
|
+
//
|
|
8
|
+
// Spec: openspec/changes/add-multi-provider-support/specs/plugin-system/spec.md
|
|
9
|
+
// - "Plugin install path is provider-aware" / "Per-provider home directory
|
|
10
|
+
// isolates plugin state"
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.codexHomeFor = codexHomeFor;
|
|
16
|
+
exports.ensureCodexHome = ensureCodexHome;
|
|
17
|
+
exports.codexMcpAdd = codexMcpAdd;
|
|
18
|
+
exports.codexMcpRemove = codexMcpRemove;
|
|
19
|
+
exports.codexMcpList = codexMcpList;
|
|
20
|
+
const child_process_1 = require("child_process");
|
|
21
|
+
const fs_1 = __importDefault(require("fs"));
|
|
22
|
+
const path_1 = __importDefault(require("path"));
|
|
23
|
+
const os_1 = __importDefault(require("os"));
|
|
24
|
+
/** Per-project CODEX_HOME root, sibling of the existing
|
|
25
|
+
* `~/.specrails/projects/<slug>/{telemetry,jobs,explore-cwd,...}` tree. */
|
|
26
|
+
function codexHomeFor(slug) {
|
|
27
|
+
return path_1.default.join(os_1.default.homedir(), '.specrails', 'projects', slug, 'codex-home');
|
|
28
|
+
}
|
|
29
|
+
/** Lazy-create the per-project codex home so `codex mcp add` writes its
|
|
30
|
+
* config there without touching the user's `~/.codex/`. */
|
|
31
|
+
function ensureCodexHome(slug) {
|
|
32
|
+
const dir = codexHomeFor(slug);
|
|
33
|
+
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
34
|
+
return dir;
|
|
35
|
+
}
|
|
36
|
+
/** Run `codex mcp add <name> -- <command> <args...>` with the project's
|
|
37
|
+
* CODEX_HOME exported. Idempotent at the caller level — invokers should
|
|
38
|
+
* probe `codexMcpList` first when re-registration matters. */
|
|
39
|
+
function codexMcpAdd(slug, name, entry) {
|
|
40
|
+
const home = ensureCodexHome(slug);
|
|
41
|
+
const argv = ['mcp', 'add', name, '--', entry.command, ...entry.args];
|
|
42
|
+
const result = (0, child_process_1.spawnSync)('codex', argv, {
|
|
43
|
+
env: {
|
|
44
|
+
...process.env,
|
|
45
|
+
...(entry.env ?? {}),
|
|
46
|
+
CODEX_HOME: home,
|
|
47
|
+
},
|
|
48
|
+
encoding: 'utf-8',
|
|
49
|
+
timeout: 10_000,
|
|
50
|
+
});
|
|
51
|
+
if (result.error) {
|
|
52
|
+
return { ok: false, stdout: '', stderr: `${result.error.message}` };
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
ok: (result.status ?? 1) === 0,
|
|
56
|
+
stdout: `${result.stdout ?? ''}`,
|
|
57
|
+
stderr: `${result.stderr ?? ''}`,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/** Run `codex mcp remove <name>` against the per-project CODEX_HOME. */
|
|
61
|
+
function codexMcpRemove(slug, name) {
|
|
62
|
+
const home = ensureCodexHome(slug);
|
|
63
|
+
const result = (0, child_process_1.spawnSync)('codex', ['mcp', 'remove', name], {
|
|
64
|
+
env: { ...process.env, CODEX_HOME: home },
|
|
65
|
+
encoding: 'utf-8',
|
|
66
|
+
timeout: 10_000,
|
|
67
|
+
});
|
|
68
|
+
if (result.error) {
|
|
69
|
+
return { ok: false, stdout: '', stderr: `${result.error.message}` };
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
ok: (result.status ?? 1) === 0,
|
|
73
|
+
stdout: `${result.stdout ?? ''}`,
|
|
74
|
+
stderr: `${result.stderr ?? ''}`,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/** Run `codex mcp list` against the per-project CODEX_HOME and return the
|
|
78
|
+
* list of registered server names. Output format is `codex mcp list`'s
|
|
79
|
+
* plain text — best-effort line scan, since codex doesn't expose --json
|
|
80
|
+
* for this subcommand. */
|
|
81
|
+
function codexMcpList(slug) {
|
|
82
|
+
const home = ensureCodexHome(slug);
|
|
83
|
+
const result = (0, child_process_1.spawnSync)('codex', ['mcp', 'list'], {
|
|
84
|
+
env: { ...process.env, CODEX_HOME: home },
|
|
85
|
+
encoding: 'utf-8',
|
|
86
|
+
timeout: 10_000,
|
|
87
|
+
});
|
|
88
|
+
if (result.error || (result.status ?? 1) !== 0) {
|
|
89
|
+
return { ok: false, servers: [], raw: `${result.stderr ?? result.stdout ?? ''}` };
|
|
90
|
+
}
|
|
91
|
+
const raw = `${result.stdout ?? ''}`;
|
|
92
|
+
// Heuristic line scan: a server name appears at the start of a line, then
|
|
93
|
+
// whitespace, then the command. Lines starting with `#` or empty are
|
|
94
|
+
// ignored. Empty list also returns ok with []. We intentionally don't
|
|
95
|
+
// hard-fail when the format drifts — manifest-level checks downstream
|
|
96
|
+
// catch real misalignments.
|
|
97
|
+
const servers = [];
|
|
98
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
99
|
+
const trimmed = line.trim();
|
|
100
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
101
|
+
continue;
|
|
102
|
+
const firstToken = trimmed.split(/\s+/)[0];
|
|
103
|
+
if (firstToken && /^[A-Za-z][A-Za-z0-9_-]*$/.test(firstToken)) {
|
|
104
|
+
servers.push(firstToken);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return { ok: true, servers, raw };
|
|
108
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SHARED_FILE_CONTRIBUTORS = void 0;
|
|
4
|
+
exports.applyContributors = applyContributors;
|
|
5
|
+
exports.revertContributors = revertContributors;
|
|
6
|
+
exports.contributorPaths = contributorPaths;
|
|
7
|
+
const claude_md_mutation_1 = require("./claude-md-mutation");
|
|
8
|
+
const providers_1 = require("../providers");
|
|
9
|
+
/** Resolve the instructions filename for a provider id, defaulting to
|
|
10
|
+
* `CLAUDE.md` when the id is unknown or absent. */
|
|
11
|
+
function instructionsFilenameFor(providerId) {
|
|
12
|
+
if (!providerId)
|
|
13
|
+
return 'CLAUDE.md';
|
|
14
|
+
try {
|
|
15
|
+
return (0, providers_1.getAdapter)(providerId).instructionsFilename;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return 'CLAUDE.md';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/** Provider-aware instructions-file contributor. Same block format
|
|
22
|
+
* (`<!-- specrails-desktop-managed:<plugin>:start/end -->`); destination
|
|
23
|
+
* filename is the adapter's `instructionsFilename`. */
|
|
24
|
+
const instructionsMdContributor = {
|
|
25
|
+
id: 'instructions-md',
|
|
26
|
+
appliesTo: (p) => typeof p.manifest.claudeMdInstructions === 'string' && p.manifest.claudeMdInstructions.trim().length > 0,
|
|
27
|
+
relativePaths: (_p, providerId) => [instructionsFilenameFor(providerId)],
|
|
28
|
+
apply: async (p, projectPath, providerId) => {
|
|
29
|
+
await (0, claude_md_mutation_1.upsertBlock)(projectPath, p.manifest.name, p.manifest.claudeMdInstructions, instructionsFilenameFor(providerId));
|
|
30
|
+
},
|
|
31
|
+
revert: async (p, projectPath, providerId) => {
|
|
32
|
+
await (0, claude_md_mutation_1.removeBlock)(projectPath, p.manifest.name, instructionsFilenameFor(providerId));
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
exports.SHARED_FILE_CONTRIBUTORS = [
|
|
36
|
+
instructionsMdContributor,
|
|
37
|
+
];
|
|
38
|
+
/** Run `apply` for every contributor that applies to this plugin. */
|
|
39
|
+
async function applyContributors(plugin, projectPath, providerId) {
|
|
40
|
+
const touchedPaths = [];
|
|
41
|
+
for (const c of exports.SHARED_FILE_CONTRIBUTORS) {
|
|
42
|
+
if (!c.appliesTo(plugin))
|
|
43
|
+
continue;
|
|
44
|
+
await c.apply(plugin, projectPath, providerId);
|
|
45
|
+
for (const rel of c.relativePaths(plugin, providerId)) {
|
|
46
|
+
if (!touchedPaths.includes(rel))
|
|
47
|
+
touchedPaths.push(rel);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return touchedPaths;
|
|
51
|
+
}
|
|
52
|
+
/** Run `revert` for every contributor that applies to this plugin. */
|
|
53
|
+
async function revertContributors(plugin, projectPath, providerId) {
|
|
54
|
+
for (const c of exports.SHARED_FILE_CONTRIBUTORS) {
|
|
55
|
+
if (!c.appliesTo(plugin))
|
|
56
|
+
continue;
|
|
57
|
+
await c.revert(plugin, projectPath, providerId);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** Project-relative paths every applicable contributor will touch. */
|
|
61
|
+
function contributorPaths(plugin, providerId) {
|
|
62
|
+
const out = [];
|
|
63
|
+
for (const c of exports.SHARED_FILE_CONTRIBUTORS) {
|
|
64
|
+
if (!c.appliesTo(plugin))
|
|
65
|
+
continue;
|
|
66
|
+
for (const rel of c.relativePaths(plugin, providerId)) {
|
|
67
|
+
if (!out.includes(rel))
|
|
68
|
+
out.push(rel);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.detectMcpDrift = detectMcpDrift;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const paths_1 = require("./paths");
|
|
9
|
+
const claude_md_mutation_1 = require("./claude-md-mutation");
|
|
10
|
+
/**
|
|
11
|
+
* Returns true when the project's `.mcp.json` entry for any of the plugin's
|
|
12
|
+
* owned mcpServers no longer matches the bundled manifest. Used to surface
|
|
13
|
+
* an "Update available" affordance when upstream changes args (e.g.,
|
|
14
|
+
* Serena's `serena-mcp-server` → `serena start-mcp-server`).
|
|
15
|
+
*
|
|
16
|
+
* Only inspects entries the plugin owns; user-authored entries are ignored.
|
|
17
|
+
*/
|
|
18
|
+
function detectMcpDrift(projectPath, plugin) {
|
|
19
|
+
const ownedKeys = plugin.manifest.owns.mcpServers ?? [];
|
|
20
|
+
if (ownedKeys.length === 0)
|
|
21
|
+
return false;
|
|
22
|
+
// The plugin tells us what its current canonical entry should look like
|
|
23
|
+
// via `previewInstall` is too heavy; we instead introspect via a marker:
|
|
24
|
+
// plugins that ship their canonical entry expose it via `expectedMcpEntry`.
|
|
25
|
+
// We accept Plugins without it (drift detection silently disabled).
|
|
26
|
+
const expected = plugin
|
|
27
|
+
.expectedMcpEntry?.();
|
|
28
|
+
if (!expected)
|
|
29
|
+
return false;
|
|
30
|
+
const file = (0, paths_1.mcpJsonPath)(projectPath);
|
|
31
|
+
if (!fs_1.default.existsSync(file))
|
|
32
|
+
return false;
|
|
33
|
+
let parsed;
|
|
34
|
+
try {
|
|
35
|
+
parsed = JSON.parse(fs_1.default.readFileSync(file, 'utf8'));
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
const servers = parsed.mcpServers ?? {};
|
|
41
|
+
for (const key of ownedKeys) {
|
|
42
|
+
const current = servers[key];
|
|
43
|
+
if (current === undefined)
|
|
44
|
+
continue;
|
|
45
|
+
if (JSON.stringify(current) !== JSON.stringify(expected))
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
// Shared-file contributor drift. Today only CLAUDE.md is contributed to;
|
|
49
|
+
// we hardcode the comparison here. If/when more shared-file contributors
|
|
50
|
+
// land we move this to a `compareDrift` hook on the contributor type.
|
|
51
|
+
if (plugin.manifest.claudeMdInstructions) {
|
|
52
|
+
const expectedMd = plugin.manifest.claudeMdInstructions.trim();
|
|
53
|
+
const actualMd = ((0, claude_md_mutation_1.getBlockContent)(projectPath, plugin.manifest.name) ?? '').trim();
|
|
54
|
+
if (actualMd !== expectedMd)
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BUNDLED_PLUGINS = void 0;
|
|
4
|
+
const serena_1 = require("./serena");
|
|
5
|
+
/**
|
|
6
|
+
* Bundled plugins available to every project on this app build. Adding a new
|
|
7
|
+
* plugin requires only:
|
|
8
|
+
* 1. Implementing the `Plugin` interface under `server/plugins/<name>/`.
|
|
9
|
+
* 2. Importing and appending it to this array.
|
|
10
|
+
*
|
|
11
|
+
* No other code in the app needs to change. PluginManager iterates this list
|
|
12
|
+
* dynamically; conflicts (overlapping ownership) fail fast at startup.
|
|
13
|
+
*/
|
|
14
|
+
exports.BUNDLED_PLUGINS = [serena_1.serenaPlugin];
|