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,291 @@
|
|
|
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.ProposalManager = void 0;
|
|
7
|
+
const readline_1 = require("readline");
|
|
8
|
+
const tree_kill_1 = __importDefault(require("tree-kill"));
|
|
9
|
+
const cli_prompt_1 = require("./util/cli-prompt");
|
|
10
|
+
const db_1 = require("./db");
|
|
11
|
+
const command_resolver_1 = require("./command-resolver");
|
|
12
|
+
// ─── ProposalManager ──────────────────────────────────────────────────────────
|
|
13
|
+
class ProposalManager {
|
|
14
|
+
_broadcast;
|
|
15
|
+
_db;
|
|
16
|
+
_cwd;
|
|
17
|
+
_activeProcesses;
|
|
18
|
+
_buffers;
|
|
19
|
+
_disposed = false;
|
|
20
|
+
constructor(broadcast, db, cwd) {
|
|
21
|
+
this._broadcast = broadcast;
|
|
22
|
+
this._db = db;
|
|
23
|
+
this._cwd = cwd;
|
|
24
|
+
this._activeProcesses = new Map();
|
|
25
|
+
this._buffers = new Map();
|
|
26
|
+
}
|
|
27
|
+
isActive(proposalId) {
|
|
28
|
+
return this._activeProcesses.has(proposalId);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Tear down before the project's DB is closed (M12). Disposes so in-flight
|
|
32
|
+
* close/error handlers skip their updateProposal DB writes (which would throw
|
|
33
|
+
* on the closed connection and crash the app), and SIGTERMs orphaned children.
|
|
34
|
+
*/
|
|
35
|
+
shutdown() {
|
|
36
|
+
this._disposed = true;
|
|
37
|
+
for (const child of this._activeProcesses.values()) {
|
|
38
|
+
if (child.pid) {
|
|
39
|
+
try {
|
|
40
|
+
(0, tree_kill_1.default)(child.pid, 'SIGTERM');
|
|
41
|
+
}
|
|
42
|
+
catch { /* ignore */ }
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
this._activeProcesses.clear();
|
|
46
|
+
this._buffers.clear();
|
|
47
|
+
}
|
|
48
|
+
async startExploration(proposalId, idea) {
|
|
49
|
+
const proposal = (0, db_1.getProposal)(this._db, proposalId);
|
|
50
|
+
if (!proposal) {
|
|
51
|
+
this._broadcastError(proposalId, 'Proposal not found');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
// Resolve the command file — error if not installed
|
|
55
|
+
const rawCommand = `/specrails:propose-feature ${idea}`;
|
|
56
|
+
const prompt = (0, command_resolver_1.resolveCommand)(rawCommand, this._cwd);
|
|
57
|
+
if (prompt === rawCommand) {
|
|
58
|
+
(0, db_1.updateProposal)(this._db, proposalId, { status: 'cancelled' });
|
|
59
|
+
this._broadcastError(proposalId, 'This project does not have the /specrails:propose-feature command installed. Run "npx specrails-core@latest" to update.');
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
(0, db_1.updateProposal)(this._db, proposalId, { status: 'exploring' });
|
|
63
|
+
const args = [
|
|
64
|
+
'--dangerously-skip-permissions',
|
|
65
|
+
'--tools', 'default',
|
|
66
|
+
'--output-format', 'stream-json',
|
|
67
|
+
'--verbose',
|
|
68
|
+
'-p', prompt,
|
|
69
|
+
];
|
|
70
|
+
await this._runProcess(proposalId, args, (fullText, sessionId) => {
|
|
71
|
+
(0, db_1.updateProposal)(this._db, proposalId, {
|
|
72
|
+
status: 'review',
|
|
73
|
+
result_markdown: fullText,
|
|
74
|
+
...(sessionId ? { session_id: sessionId } : {}),
|
|
75
|
+
});
|
|
76
|
+
this._broadcast({
|
|
77
|
+
type: 'proposal_ready',
|
|
78
|
+
projectId: '',
|
|
79
|
+
proposalId,
|
|
80
|
+
markdown: fullText,
|
|
81
|
+
timestamp: new Date().toISOString(),
|
|
82
|
+
});
|
|
83
|
+
}, () => {
|
|
84
|
+
(0, db_1.updateProposal)(this._db, proposalId, { status: 'input' });
|
|
85
|
+
this._broadcastError(proposalId, 'Exploration failed');
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
async sendRefinement(proposalId, feedback) {
|
|
89
|
+
const proposal = (0, db_1.getProposal)(this._db, proposalId);
|
|
90
|
+
if (!proposal) {
|
|
91
|
+
this._broadcastError(proposalId, 'Proposal not found');
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (!proposal.session_id) {
|
|
95
|
+
this._broadcastError(proposalId, 'No session available for refinement');
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
(0, db_1.updateProposal)(this._db, proposalId, { status: 'refining' });
|
|
99
|
+
const args = [
|
|
100
|
+
'--dangerously-skip-permissions',
|
|
101
|
+
'--tools', 'default',
|
|
102
|
+
'--output-format', 'stream-json',
|
|
103
|
+
'--verbose',
|
|
104
|
+
'--resume', proposal.session_id,
|
|
105
|
+
'-p', feedback,
|
|
106
|
+
];
|
|
107
|
+
await this._runProcess(proposalId, args, (fullText, sessionId) => {
|
|
108
|
+
(0, db_1.updateProposal)(this._db, proposalId, {
|
|
109
|
+
status: 'review',
|
|
110
|
+
result_markdown: fullText,
|
|
111
|
+
...(sessionId ? { session_id: sessionId } : {}),
|
|
112
|
+
});
|
|
113
|
+
this._broadcast({
|
|
114
|
+
type: 'proposal_refined',
|
|
115
|
+
projectId: '',
|
|
116
|
+
proposalId,
|
|
117
|
+
markdown: fullText,
|
|
118
|
+
timestamp: new Date().toISOString(),
|
|
119
|
+
});
|
|
120
|
+
}, () => {
|
|
121
|
+
(0, db_1.updateProposal)(this._db, proposalId, { status: 'review' });
|
|
122
|
+
this._broadcastError(proposalId, 'Refinement failed');
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
async createIssue(proposalId) {
|
|
126
|
+
const proposal = (0, db_1.getProposal)(this._db, proposalId);
|
|
127
|
+
if (!proposal) {
|
|
128
|
+
this._broadcastError(proposalId, 'Proposal not found');
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (!proposal.session_id) {
|
|
132
|
+
this._broadcastError(proposalId, 'No session available for issue creation');
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
(0, db_1.updateProposal)(this._db, proposalId, { status: 'refining' });
|
|
136
|
+
const prompt = "Based on the proposal above, create a GitHub Issue with the label 'user-proposed'. " +
|
|
137
|
+
"Output only the URL of the created issue on the last line of your response.";
|
|
138
|
+
const args = [
|
|
139
|
+
'--dangerously-skip-permissions',
|
|
140
|
+
'--tools', 'default',
|
|
141
|
+
'--output-format', 'stream-json',
|
|
142
|
+
'--verbose',
|
|
143
|
+
'--resume', proposal.session_id,
|
|
144
|
+
'-p', prompt,
|
|
145
|
+
];
|
|
146
|
+
await this._runProcess(proposalId, args, (fullText, sessionId) => {
|
|
147
|
+
const match = fullText.match(/https:\/\/github\.com\/[^\s]+\/issues\/\d+/);
|
|
148
|
+
const issueUrl = match ? match[0] : null;
|
|
149
|
+
if (issueUrl) {
|
|
150
|
+
(0, db_1.updateProposal)(this._db, proposalId, {
|
|
151
|
+
status: 'created',
|
|
152
|
+
issue_url: issueUrl,
|
|
153
|
+
...(sessionId ? { session_id: sessionId } : {}),
|
|
154
|
+
});
|
|
155
|
+
this._broadcast({
|
|
156
|
+
type: 'proposal_issue_created',
|
|
157
|
+
projectId: '',
|
|
158
|
+
proposalId,
|
|
159
|
+
issueUrl,
|
|
160
|
+
timestamp: new Date().toISOString(),
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
(0, db_1.updateProposal)(this._db, proposalId, { status: 'review' });
|
|
165
|
+
this._broadcastError(proposalId, 'Issue creation failed — GitHub CLI may not be available or not authenticated');
|
|
166
|
+
}
|
|
167
|
+
}, () => {
|
|
168
|
+
(0, db_1.updateProposal)(this._db, proposalId, { status: 'review' });
|
|
169
|
+
this._broadcastError(proposalId, 'Issue creation failed');
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
cancel(proposalId) {
|
|
173
|
+
const child = this._activeProcesses.get(proposalId);
|
|
174
|
+
if (child?.pid) {
|
|
175
|
+
(0, tree_kill_1.default)(child.pid, 'SIGTERM');
|
|
176
|
+
}
|
|
177
|
+
(0, db_1.updateProposal)(this._db, proposalId, { status: 'cancelled' });
|
|
178
|
+
this._broadcast({
|
|
179
|
+
type: 'proposal_error',
|
|
180
|
+
projectId: '',
|
|
181
|
+
proposalId,
|
|
182
|
+
error: 'cancelled',
|
|
183
|
+
timestamp: new Date().toISOString(),
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
// ─── Private helpers ───────────────────────────────────────────────────────
|
|
187
|
+
async _runProcess(proposalId, args, onSuccess, onError) {
|
|
188
|
+
// spawnClaude reroutes multi-line argv values through stdin on Windows.
|
|
189
|
+
const child = (0, cli_prompt_1.spawnClaude)(args, {
|
|
190
|
+
env: process.env,
|
|
191
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
192
|
+
cwd: this._cwd,
|
|
193
|
+
});
|
|
194
|
+
this._activeProcesses.set(proposalId, child);
|
|
195
|
+
this._buffers.set(proposalId, '');
|
|
196
|
+
let capturedSessionId = null;
|
|
197
|
+
const stdoutReader = (0, readline_1.createInterface)({ input: child.stdout, crlfDelay: Infinity });
|
|
198
|
+
stdoutReader.on('line', (line) => {
|
|
199
|
+
let parsed = null;
|
|
200
|
+
try {
|
|
201
|
+
parsed = JSON.parse(line);
|
|
202
|
+
}
|
|
203
|
+
catch { /* skip non-JSON */ }
|
|
204
|
+
if (!parsed)
|
|
205
|
+
return;
|
|
206
|
+
const eventType = parsed.type;
|
|
207
|
+
if (eventType === 'result') {
|
|
208
|
+
const sid = parsed.session_id;
|
|
209
|
+
if (sid)
|
|
210
|
+
capturedSessionId = sid;
|
|
211
|
+
}
|
|
212
|
+
if (eventType === 'assistant') {
|
|
213
|
+
const msg = parsed.message;
|
|
214
|
+
const blocks = msg?.content ?? [];
|
|
215
|
+
// Extract text from text blocks (skip thinking blocks)
|
|
216
|
+
const texts = blocks
|
|
217
|
+
.filter((c) => c.type === 'text')
|
|
218
|
+
.map((c) => c.text ?? '');
|
|
219
|
+
const newText = texts.join('');
|
|
220
|
+
if (newText) {
|
|
221
|
+
const prev = this._buffers.get(proposalId) ?? '';
|
|
222
|
+
this._buffers.set(proposalId, prev + newText);
|
|
223
|
+
this._broadcast({
|
|
224
|
+
type: 'proposal_stream',
|
|
225
|
+
projectId: '',
|
|
226
|
+
proposalId,
|
|
227
|
+
delta: newText,
|
|
228
|
+
timestamp: new Date().toISOString(),
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
// Broadcast tool_use activity so the UI can show "reading codebase..."
|
|
232
|
+
for (const block of blocks) {
|
|
233
|
+
if (block.type === 'tool_use' && block.name) {
|
|
234
|
+
this._broadcast({
|
|
235
|
+
type: 'proposal_stream',
|
|
236
|
+
projectId: '',
|
|
237
|
+
proposalId,
|
|
238
|
+
delta: `<!--tool:${block.name}-->`,
|
|
239
|
+
timestamp: new Date().toISOString(),
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
return new Promise((resolve) => {
|
|
246
|
+
// Without this handler an ENOENT on spawn (e.g. `claude` not on
|
|
247
|
+
// PATH) propagates as an unhandled 'error' event and crashes the
|
|
248
|
+
// entire app process. Surface to the user instead.
|
|
249
|
+
/* c8 ignore start -- spawn-failure path; exercised manually, not in CI */
|
|
250
|
+
child.on('error', (err) => {
|
|
251
|
+
console.error(`[ProposalManager] spawn failed for ${proposalId}: ${err.message}`);
|
|
252
|
+
this._activeProcesses.delete(proposalId);
|
|
253
|
+
this._buffers.delete(proposalId);
|
|
254
|
+
if (this._disposed) {
|
|
255
|
+
resolve();
|
|
256
|
+
return;
|
|
257
|
+
} // M12: project removed mid-flight; DB closing
|
|
258
|
+
this._broadcastError(proposalId, `Failed to launch claude: ${err.message}`);
|
|
259
|
+
onError();
|
|
260
|
+
resolve();
|
|
261
|
+
});
|
|
262
|
+
/* c8 ignore stop */
|
|
263
|
+
child.on('close', (code) => {
|
|
264
|
+
const fullText = this._buffers.get(proposalId) ?? '';
|
|
265
|
+
this._activeProcesses.delete(proposalId);
|
|
266
|
+
this._buffers.delete(proposalId);
|
|
267
|
+
if (this._disposed) {
|
|
268
|
+
resolve();
|
|
269
|
+
return;
|
|
270
|
+
} // M12: project removed mid-flight; DB closing
|
|
271
|
+
if (code === 0) {
|
|
272
|
+
onSuccess(fullText, capturedSessionId);
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
onError();
|
|
276
|
+
}
|
|
277
|
+
resolve();
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
_broadcastError(proposalId, error) {
|
|
282
|
+
this._broadcast({
|
|
283
|
+
type: 'proposal_error',
|
|
284
|
+
projectId: '',
|
|
285
|
+
proposalId,
|
|
286
|
+
error,
|
|
287
|
+
timestamp: new Date().toISOString(),
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
exports.ProposalManager = ProposalManager;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Per-invocation provider selection for multi-provider projects.
|
|
3
|
+
//
|
|
4
|
+
// A project carries a primary `provider` (single source for single-provider
|
|
5
|
+
// projects, and the fallback default) plus a `providers` list of every
|
|
6
|
+
// installed provider. Add Spec, rails and the terminal can request a specific
|
|
7
|
+
// engine per invocation; these helpers resolve and validate that request
|
|
8
|
+
// against what the project actually has installed.
|
|
9
|
+
//
|
|
10
|
+
// Invariant: when `providers.length === 1` every helper collapses to the single
|
|
11
|
+
// provider, so single-provider projects behave exactly as before.
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.isProviderEnabled = isProviderEnabled;
|
|
14
|
+
exports.isMultiProvider = isMultiProvider;
|
|
15
|
+
exports.resolveProvider = resolveProvider;
|
|
16
|
+
exports.validateRequestedProvider = validateRequestedProvider;
|
|
17
|
+
/**
|
|
18
|
+
* Normalised installed-provider list. `getProject` always populates
|
|
19
|
+
* `providers`, but this tolerates a row where it is missing/empty (legacy or
|
|
20
|
+
* hand-built rows) by falling back to the primary provider.
|
|
21
|
+
*/
|
|
22
|
+
function installedProviders(project) {
|
|
23
|
+
if (Array.isArray(project.providers) && project.providers.length > 0)
|
|
24
|
+
return project.providers;
|
|
25
|
+
return [project.provider ?? 'claude'];
|
|
26
|
+
}
|
|
27
|
+
/** True when `id` is one of the providers installed on the project. */
|
|
28
|
+
function isProviderEnabled(project, id) {
|
|
29
|
+
if (!id)
|
|
30
|
+
return false;
|
|
31
|
+
return installedProviders(project).includes(id);
|
|
32
|
+
}
|
|
33
|
+
/** True when the project offers a choice of engines (more than one installed). */
|
|
34
|
+
function isMultiProvider(project) {
|
|
35
|
+
return installedProviders(project).length > 1;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Resolve the effective provider for a per-invocation request.
|
|
39
|
+
* Returns the requested provider when it is installed on the project; otherwise
|
|
40
|
+
* falls back to the project's primary provider. Never throws — callers that
|
|
41
|
+
* want strict validation should use `validateRequestedProvider` first.
|
|
42
|
+
*/
|
|
43
|
+
function resolveProvider(project, requested) {
|
|
44
|
+
if (isProviderEnabled(project, requested))
|
|
45
|
+
return requested;
|
|
46
|
+
return project.provider ?? installedProviders(project)[0];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Strict validation for route handlers. Returns the resolved provider when the
|
|
50
|
+
* request is acceptable, or an `error` string when an explicit, non-empty
|
|
51
|
+
* provider was requested that the project does not have installed. Omitting the
|
|
52
|
+
* provider (undefined/null/empty) is always acceptable and resolves to primary.
|
|
53
|
+
*/
|
|
54
|
+
function validateRequestedProvider(project, requested) {
|
|
55
|
+
const installed = installedProviders(project);
|
|
56
|
+
if (requested === undefined || requested === null || requested === '') {
|
|
57
|
+
return { ok: true, provider: project.provider ?? installed[0] };
|
|
58
|
+
}
|
|
59
|
+
if (typeof requested !== 'string') {
|
|
60
|
+
return { ok: false, error: 'provider must be a string' };
|
|
61
|
+
}
|
|
62
|
+
if (!isProviderEnabled(project, requested)) {
|
|
63
|
+
return {
|
|
64
|
+
ok: false,
|
|
65
|
+
error: `provider '${requested}' is not installed for this project (installed: ${installed.join(', ')})`,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return { ok: true, provider: requested };
|
|
69
|
+
}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Claude adapter. Ports the existing claude spawn / stream / result logic from
|
|
3
|
+
// chat-manager.ts, queue-manager.ts, agent-refine-manager.ts,
|
|
4
|
+
// project-router.ts (generate-spec), setup-manager.ts, and result-event.ts
|
|
5
|
+
// without behaviour change. Managers will migrate to consume this adapter in
|
|
6
|
+
// later tasks (see openspec/changes/add-multi-provider-support/tasks.md §2.x).
|
|
7
|
+
//
|
|
8
|
+
// Spec: openspec/specs/multi-provider-architecture/spec.md
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.claudeAdapter = void 0;
|
|
11
|
+
exports._normaliseClaudeModel = normaliseModel;
|
|
12
|
+
const child_process_1 = require("child_process");
|
|
13
|
+
const WHICH_CMD = process.platform === 'win32' ? 'where' : 'which';
|
|
14
|
+
const CLAUDE_MODELS = [
|
|
15
|
+
{ value: 'sonnet', label: 'Claude Sonnet', default: true },
|
|
16
|
+
{ value: 'opus', label: 'Claude Opus' },
|
|
17
|
+
{ value: 'haiku', label: 'Claude Haiku' },
|
|
18
|
+
];
|
|
19
|
+
/** Mirror of ChatManager.normalizeClaudeCodeModel — pinned model strings the
|
|
20
|
+
* CLI accepts collapse to the short alias. */
|
|
21
|
+
function normaliseModel(model) {
|
|
22
|
+
switch (model) {
|
|
23
|
+
case 'claude-sonnet-4-6':
|
|
24
|
+
case 'claude-sonnet-4-5':
|
|
25
|
+
case 'claude-sonnet-4-0':
|
|
26
|
+
case 'claude-sonnet-4-20250514':
|
|
27
|
+
return 'sonnet';
|
|
28
|
+
case 'claude-opus-4-8':
|
|
29
|
+
case 'claude-opus-4-5':
|
|
30
|
+
case 'claude-opus-4-1-20250805':
|
|
31
|
+
case 'claude-opus-4-20250514':
|
|
32
|
+
return 'opus';
|
|
33
|
+
case 'claude-haiku-4-5-20251001':
|
|
34
|
+
case 'claude-3-5-haiku-20241022':
|
|
35
|
+
case 'claude-3-5-haiku-latest':
|
|
36
|
+
return 'haiku';
|
|
37
|
+
default:
|
|
38
|
+
return model || 'sonnet';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** Default arg block every claude spawn shares. */
|
|
42
|
+
const COMMON_FLAGS = [
|
|
43
|
+
'--dangerously-skip-permissions',
|
|
44
|
+
'--tools', 'default',
|
|
45
|
+
'--output-format', 'stream-json',
|
|
46
|
+
'--verbose',
|
|
47
|
+
// Isolate app-spawned claude from the *user's* global Claude config. Without
|
|
48
|
+
// this, the child loads ~/.claude (user CLAUDE.md memory, plugins like
|
|
49
|
+
// claude-mem, SessionStart hooks). That bled cross-project memory into
|
|
50
|
+
// Explore turns (e.g. an unrelated "fighting game" surfaced for a fresh
|
|
51
|
+
// project) and inflated spec-gen tool usage past --max-turns. `project,local`
|
|
52
|
+
// still loads the target repo's own .claude settings + CLAUDE.md, which is
|
|
53
|
+
// exactly the context an app run should see.
|
|
54
|
+
'--setting-sources', 'project,local',
|
|
55
|
+
];
|
|
56
|
+
function buildClaudeArgs(action, opts) {
|
|
57
|
+
const args = [];
|
|
58
|
+
const model = normaliseModel(opts.model);
|
|
59
|
+
switch (action) {
|
|
60
|
+
case 'chat-turn': {
|
|
61
|
+
args.push('--model', model);
|
|
62
|
+
args.push(...COMMON_FLAGS);
|
|
63
|
+
if (opts.systemPrompt)
|
|
64
|
+
args.push('--system-prompt', opts.systemPrompt);
|
|
65
|
+
args.push('-p', opts.prompt);
|
|
66
|
+
if (opts.maxTurns != null)
|
|
67
|
+
args.push('--max-turns', String(opts.maxTurns));
|
|
68
|
+
if (opts.extraArgs)
|
|
69
|
+
args.push(...opts.extraArgs);
|
|
70
|
+
return args;
|
|
71
|
+
}
|
|
72
|
+
case 'chat-resume': {
|
|
73
|
+
if (!opts.sessionId) {
|
|
74
|
+
throw new Error('chat-resume requires sessionId');
|
|
75
|
+
}
|
|
76
|
+
args.push('--model', model);
|
|
77
|
+
args.push(...COMMON_FLAGS);
|
|
78
|
+
if (opts.systemPrompt)
|
|
79
|
+
args.push('--system-prompt', opts.systemPrompt);
|
|
80
|
+
args.push('--resume', opts.sessionId);
|
|
81
|
+
args.push('-p', opts.prompt);
|
|
82
|
+
if (opts.maxTurns != null)
|
|
83
|
+
args.push('--max-turns', String(opts.maxTurns));
|
|
84
|
+
if (opts.extraArgs)
|
|
85
|
+
args.push(...opts.extraArgs);
|
|
86
|
+
return args;
|
|
87
|
+
}
|
|
88
|
+
case 'rail-job': {
|
|
89
|
+
// QueueManager spawns with `--append-system-prompt` (not `--system-prompt`)
|
|
90
|
+
// because the slash command in the prompt brings its own system prompt;
|
|
91
|
+
// we ADD to it rather than overwrite.
|
|
92
|
+
args.push(...COMMON_FLAGS);
|
|
93
|
+
args.push('--model', model);
|
|
94
|
+
if (opts.systemPrompt)
|
|
95
|
+
args.push('--append-system-prompt', opts.systemPrompt);
|
|
96
|
+
args.push('-p', opts.prompt);
|
|
97
|
+
if (opts.extraArgs)
|
|
98
|
+
args.push(...opts.extraArgs);
|
|
99
|
+
return args;
|
|
100
|
+
}
|
|
101
|
+
case 'spec-gen': {
|
|
102
|
+
args.push(...COMMON_FLAGS);
|
|
103
|
+
args.push('--model', model);
|
|
104
|
+
if (opts.maxTurns != null)
|
|
105
|
+
args.push('--max-turns', String(opts.maxTurns));
|
|
106
|
+
// Caller passes --tools override via extraArgs when scoped; otherwise
|
|
107
|
+
// 'default' from COMMON_FLAGS applies.
|
|
108
|
+
if (opts.systemPrompt)
|
|
109
|
+
args.push('--system-prompt', opts.systemPrompt);
|
|
110
|
+
args.push('-p', opts.prompt);
|
|
111
|
+
if (opts.extraArgs)
|
|
112
|
+
args.push(...opts.extraArgs);
|
|
113
|
+
return args;
|
|
114
|
+
}
|
|
115
|
+
case 'agent-refine': {
|
|
116
|
+
args.push(...COMMON_FLAGS);
|
|
117
|
+
if (opts.sessionId)
|
|
118
|
+
args.push('--resume', opts.sessionId);
|
|
119
|
+
args.push('-p', opts.prompt);
|
|
120
|
+
if (opts.extraArgs)
|
|
121
|
+
args.push(...opts.extraArgs);
|
|
122
|
+
return args;
|
|
123
|
+
}
|
|
124
|
+
case 'setup-enrich': {
|
|
125
|
+
args.push('-p', opts.prompt);
|
|
126
|
+
args.push(...COMMON_FLAGS);
|
|
127
|
+
if (opts.extraArgs)
|
|
128
|
+
args.push(...opts.extraArgs);
|
|
129
|
+
return args;
|
|
130
|
+
}
|
|
131
|
+
case 'setup-enrich-resume': {
|
|
132
|
+
if (!opts.sessionId) {
|
|
133
|
+
throw new Error('setup-enrich-resume requires sessionId');
|
|
134
|
+
}
|
|
135
|
+
args.push('--resume', opts.sessionId);
|
|
136
|
+
args.push(...COMMON_FLAGS);
|
|
137
|
+
args.push('-p', opts.prompt);
|
|
138
|
+
if (opts.extraArgs)
|
|
139
|
+
args.push(...opts.extraArgs);
|
|
140
|
+
return args;
|
|
141
|
+
}
|
|
142
|
+
case 'auto-title': {
|
|
143
|
+
args.push(...COMMON_FLAGS);
|
|
144
|
+
args.push('-p', opts.prompt);
|
|
145
|
+
return args;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function parseClaudeStreamLine(line) {
|
|
150
|
+
if (line.length === 0)
|
|
151
|
+
return null;
|
|
152
|
+
let parsed;
|
|
153
|
+
try {
|
|
154
|
+
parsed = JSON.parse(line);
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
const type = parsed.type;
|
|
160
|
+
if (!type)
|
|
161
|
+
return { kind: 'other', type: '<missing>', raw: parsed };
|
|
162
|
+
if (type === 'system' || type === 'init') {
|
|
163
|
+
const sid = parsed.session_id ??
|
|
164
|
+
(parsed.session?.id ?? undefined);
|
|
165
|
+
if (sid)
|
|
166
|
+
return { kind: 'session-started', sessionId: sid };
|
|
167
|
+
return { kind: 'other', type, raw: parsed };
|
|
168
|
+
}
|
|
169
|
+
if (type === 'result') {
|
|
170
|
+
return { kind: 'result', payload: parsed };
|
|
171
|
+
}
|
|
172
|
+
if (type === 'assistant') {
|
|
173
|
+
const msg = parsed.message;
|
|
174
|
+
const blocks = msg?.content ?? [];
|
|
175
|
+
// Concatenate all text blocks; tool_use blocks are surfaced separately as a
|
|
176
|
+
// tool-use event. For simplicity we synthesise the first text block here
|
|
177
|
+
// and let callers consume tool_use from a fan-out (matching current
|
|
178
|
+
// chat-manager behaviour).
|
|
179
|
+
const text = blocks
|
|
180
|
+
.filter((b) => b.type === 'text')
|
|
181
|
+
.map((b) => b.text ?? '')
|
|
182
|
+
.join('');
|
|
183
|
+
if (text)
|
|
184
|
+
return { kind: 'text-delta', text };
|
|
185
|
+
// Surface a single tool-use (the historical pattern only emitted one per
|
|
186
|
+
// assistant frame anyway).
|
|
187
|
+
const tool = blocks.find((b) => b.type === 'tool_use');
|
|
188
|
+
if (tool?.name) {
|
|
189
|
+
const input = JSON.stringify(parsed.message?.content?.[0]?.input ?? {});
|
|
190
|
+
return { kind: 'tool-use', name: tool.name, inputPreview: input.slice(0, 200) };
|
|
191
|
+
}
|
|
192
|
+
return { kind: 'other', type, raw: parsed };
|
|
193
|
+
}
|
|
194
|
+
if (type === 'tool_use') {
|
|
195
|
+
const name = parsed.name ?? '<unnamed>';
|
|
196
|
+
const input = JSON.stringify(parsed.input ?? {});
|
|
197
|
+
return { kind: 'tool-use', name, inputPreview: input.slice(0, 200) };
|
|
198
|
+
}
|
|
199
|
+
return { kind: 'other', type, raw: parsed };
|
|
200
|
+
}
|
|
201
|
+
function extractClaudeResult(events) {
|
|
202
|
+
// Find the latest `result` event and the latest `session-started` event.
|
|
203
|
+
let resultPayload = null;
|
|
204
|
+
let sessionId;
|
|
205
|
+
for (const ev of events) {
|
|
206
|
+
if (ev.kind === 'result')
|
|
207
|
+
resultPayload = ev.payload;
|
|
208
|
+
else if (ev.kind === 'session-started')
|
|
209
|
+
sessionId = ev.sessionId;
|
|
210
|
+
}
|
|
211
|
+
if (!resultPayload)
|
|
212
|
+
return { session_id: sessionId };
|
|
213
|
+
const usage = resultPayload.usage;
|
|
214
|
+
// result event may also carry session_id directly — prefer that over the
|
|
215
|
+
// earlier captured one (it's authoritative for the final state).
|
|
216
|
+
const finalSessionId = resultPayload.session_id ?? sessionId;
|
|
217
|
+
return {
|
|
218
|
+
tokens_in: usage?.input_tokens,
|
|
219
|
+
tokens_out: usage?.output_tokens,
|
|
220
|
+
tokens_cache_read: usage?.cache_read_input_tokens,
|
|
221
|
+
tokens_cache_create: usage?.cache_creation_input_tokens,
|
|
222
|
+
total_cost_usd: resultPayload.total_cost_usd,
|
|
223
|
+
num_turns: resultPayload.num_turns,
|
|
224
|
+
model: resultPayload.model,
|
|
225
|
+
duration_ms: resultPayload.duration_ms,
|
|
226
|
+
duration_api_ms: resultPayload.api_duration_ms,
|
|
227
|
+
session_id: finalSessionId,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
async function detectClaudeInstalled() {
|
|
231
|
+
let installed = false;
|
|
232
|
+
try {
|
|
233
|
+
(0, child_process_1.execSync)(`${WHICH_CMD} claude`, { stdio: 'ignore' });
|
|
234
|
+
installed = true;
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
return { installed: false, executable: false };
|
|
238
|
+
}
|
|
239
|
+
// Probe version for executability + reporting.
|
|
240
|
+
try {
|
|
241
|
+
const raw = (0, child_process_1.execSync)('claude --version', {
|
|
242
|
+
encoding: 'utf-8',
|
|
243
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
244
|
+
timeout: 3000,
|
|
245
|
+
}).trim();
|
|
246
|
+
const match = raw.match(/\d+\.\d+\.\d+[\w.-]*/);
|
|
247
|
+
return {
|
|
248
|
+
installed,
|
|
249
|
+
executable: true,
|
|
250
|
+
version: match ? match[0] : raw,
|
|
251
|
+
meetsMinimum: true, // claude has no pinned minimum in this adapter
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
return { installed, executable: false };
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
exports.claudeAdapter = {
|
|
259
|
+
id: 'claude',
|
|
260
|
+
displayName: 'Claude Code',
|
|
261
|
+
binary: 'claude',
|
|
262
|
+
minCliVersion: null,
|
|
263
|
+
projectDirName: '.claude',
|
|
264
|
+
instructionsFilename: 'CLAUDE.md',
|
|
265
|
+
mcpRegistration: 'project-json',
|
|
266
|
+
capabilities: {
|
|
267
|
+
nativeResume: true,
|
|
268
|
+
nativeStreamJson: true,
|
|
269
|
+
nativeCostUsd: true,
|
|
270
|
+
nativeOtelEnv: true,
|
|
271
|
+
profileEnvSupport: true,
|
|
272
|
+
systemPromptArg: true,
|
|
273
|
+
},
|
|
274
|
+
modelCatalog: () => CLAUDE_MODELS,
|
|
275
|
+
defaultModel: () => 'sonnet',
|
|
276
|
+
buildArgs: buildClaudeArgs,
|
|
277
|
+
parseStreamLine: parseClaudeStreamLine,
|
|
278
|
+
extractResult: extractClaudeResult,
|
|
279
|
+
baselineAgents: () => ['sr-architect', 'sr-developer', 'sr-reviewer'],
|
|
280
|
+
detectInstalled: detectClaudeInstalled,
|
|
281
|
+
};
|