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,235 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Synthetic OTEL bridge for codex (and any future provider whose CLI does not
|
|
3
|
+
// honour `OTEL_EXPORTER_OTLP_*` environment variables natively).
|
|
4
|
+
//
|
|
5
|
+
// Lifecycle:
|
|
6
|
+
//
|
|
7
|
+
// const bridge = createCodexOtelBridge({ jobId, projectId, desktopPort, model, threadId? })
|
|
8
|
+
// for each parsed AdapterEvent → bridge.consumeEvent(event)
|
|
9
|
+
// on child close → await bridge.finalize({ exitCode, stderr? })
|
|
10
|
+
//
|
|
11
|
+
// On finalize, the bridge composes one OTLP/JSON payload per signal (traces,
|
|
12
|
+
// metrics, logs) and POSTs them to the in-process receiver at
|
|
13
|
+
// http://127.0.0.1:<desktopPort>/otlp/v1/{traces,metrics,logs}. The receiver
|
|
14
|
+
// already routes by resource attributes, enforces the 10 MB cap, gzips the
|
|
15
|
+
// NDJSON, and updates `telemetry_blobs` — the bridge does not bypass any of
|
|
16
|
+
// that.
|
|
17
|
+
//
|
|
18
|
+
// Spec: openspec/changes/add-multi-provider-support/specs/pipeline-telemetry/spec.md
|
|
19
|
+
// - "Synthetic OTEL bridge for providers without native OTEL env support"
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.createCodexOtelBridge = createCodexOtelBridge;
|
|
22
|
+
const crypto_1 = require("crypto");
|
|
23
|
+
const LOGS_CAP_BYTES = 10 * 1024 * 1024; // 10 MB — mirrors telemetry-receiver cap
|
|
24
|
+
// ─── OTLP shape helpers ───────────────────────────────────────────────────────
|
|
25
|
+
function attr(key, value) {
|
|
26
|
+
return { key, value: { stringValue: value } };
|
|
27
|
+
}
|
|
28
|
+
function intAttr(key, value) {
|
|
29
|
+
return { key, value: { intValue: String(value) } };
|
|
30
|
+
}
|
|
31
|
+
function makeResource(opts, threadId) {
|
|
32
|
+
const attrs = [
|
|
33
|
+
attr('specrails.job_id', opts.jobId),
|
|
34
|
+
attr('specrails.project_id', opts.projectId),
|
|
35
|
+
attr('specrails.provider', 'codex'),
|
|
36
|
+
attr('service.name', 'specrails-desktop.codex-bridge'),
|
|
37
|
+
];
|
|
38
|
+
if (opts.model)
|
|
39
|
+
attrs.push(attr('specrails.codex.model', opts.model));
|
|
40
|
+
if (opts.cliVersion)
|
|
41
|
+
attrs.push(attr('specrails.codex.cli_version', opts.cliVersion));
|
|
42
|
+
if (threadId)
|
|
43
|
+
attrs.push(attr('specrails.codex.thread_id', threadId));
|
|
44
|
+
return { attributes: attrs };
|
|
45
|
+
}
|
|
46
|
+
// ─── Bridge factory ───────────────────────────────────────────────────────────
|
|
47
|
+
function createCodexOtelBridge(opts) {
|
|
48
|
+
const clock = opts.clock ?? { now: () => Date.now() };
|
|
49
|
+
const rb = opts.randomBytes ?? crypto_1.randomBytes;
|
|
50
|
+
const poster = opts.poster ?? defaultPoster;
|
|
51
|
+
const startedAtMs = clock.now();
|
|
52
|
+
const startedAtUnixNs = String(BigInt(startedAtMs) * 1000000n);
|
|
53
|
+
const traceId = rb(16).toString('hex');
|
|
54
|
+
const rootSpanId = rb(8).toString('hex');
|
|
55
|
+
let threadId;
|
|
56
|
+
let logsBytes = 0;
|
|
57
|
+
let truncated = false;
|
|
58
|
+
const logRecords = [];
|
|
59
|
+
const spanEvents = [];
|
|
60
|
+
let resultPayload = null;
|
|
61
|
+
function consumeEvent(event) {
|
|
62
|
+
const nowMs = clock.now();
|
|
63
|
+
const nowNs = String(BigInt(nowMs) * 1000000n);
|
|
64
|
+
switch (event.kind) {
|
|
65
|
+
case 'session-started':
|
|
66
|
+
threadId = event.sessionId;
|
|
67
|
+
break;
|
|
68
|
+
case 'tool-use': {
|
|
69
|
+
const tail = {
|
|
70
|
+
timeUnixNano: nowNs,
|
|
71
|
+
name: 'codex.tool_use',
|
|
72
|
+
attributes: [
|
|
73
|
+
attr('codex.tool.name', event.name),
|
|
74
|
+
attr('codex.tool.input_preview', event.inputPreview),
|
|
75
|
+
],
|
|
76
|
+
};
|
|
77
|
+
spanEvents.push(tail);
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
case 'text-delta': {
|
|
81
|
+
const recordSize = Buffer.byteLength(event.text, 'utf-8') + 64; // overhead estimate
|
|
82
|
+
if (logsBytes + recordSize > LOGS_CAP_BYTES) {
|
|
83
|
+
if (!truncated) {
|
|
84
|
+
truncated = true;
|
|
85
|
+
logRecords.push({
|
|
86
|
+
timeUnixNano: nowNs,
|
|
87
|
+
body: { stringValue: '[specrails.codex-bridge] logs truncated (10 MB cap)' },
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
logsBytes += recordSize;
|
|
93
|
+
logRecords.push({ timeUnixNano: nowNs, body: { stringValue: event.text } });
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
case 'result':
|
|
97
|
+
resultPayload = event.payload;
|
|
98
|
+
break;
|
|
99
|
+
case 'other':
|
|
100
|
+
// Ignore — these are progress markers, not actionable telemetry.
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async function finalize(input) {
|
|
105
|
+
const endedAtMs = clock.now();
|
|
106
|
+
const endedAtUnixNs = String(BigInt(endedAtMs) * 1000000n);
|
|
107
|
+
const resource = makeResource(opts, threadId);
|
|
108
|
+
const scopeKey = { name: 'specrails-desktop.codex-bridge' };
|
|
109
|
+
// ── Spans ──────────────────────────────────────────────────────────────
|
|
110
|
+
const usage = resultPayload?.usage ?? {};
|
|
111
|
+
const tokensIn = usage.input_tokens ?? 0;
|
|
112
|
+
const tokensOut = (usage.output_tokens ?? 0) + (usage.reasoning_output_tokens ?? 0);
|
|
113
|
+
const tokensCacheRead = usage.cached_input_tokens ?? 0;
|
|
114
|
+
const spanAttrs = [
|
|
115
|
+
intAttr('codex.tokens.input', tokensIn),
|
|
116
|
+
intAttr('codex.tokens.output', tokensOut),
|
|
117
|
+
intAttr('codex.tokens.cache_read', tokensCacheRead),
|
|
118
|
+
intAttr('codex.exit_code', input.exitCode ?? -1),
|
|
119
|
+
];
|
|
120
|
+
if (opts.model)
|
|
121
|
+
spanAttrs.push(attr('codex.model', opts.model));
|
|
122
|
+
const tracesPayload = {
|
|
123
|
+
resourceSpans: [{
|
|
124
|
+
resource,
|
|
125
|
+
scopeSpans: [{
|
|
126
|
+
scope: scopeKey,
|
|
127
|
+
spans: [{
|
|
128
|
+
traceId,
|
|
129
|
+
spanId: rootSpanId,
|
|
130
|
+
name: 'specrails.codex.turn',
|
|
131
|
+
kind: 1, // INTERNAL
|
|
132
|
+
startTimeUnixNano: startedAtUnixNs,
|
|
133
|
+
endTimeUnixNano: endedAtUnixNs,
|
|
134
|
+
attributes: spanAttrs,
|
|
135
|
+
events: spanEvents,
|
|
136
|
+
status: {
|
|
137
|
+
code: input.exitCode === 0 ? 1 : 2, // OK=1, ERROR=2
|
|
138
|
+
message: input.exitCode === 0 ? '' : (input.stderr?.slice(-300) ?? ''),
|
|
139
|
+
},
|
|
140
|
+
}],
|
|
141
|
+
}],
|
|
142
|
+
}],
|
|
143
|
+
};
|
|
144
|
+
// ── Metrics ────────────────────────────────────────────────────────────
|
|
145
|
+
function tokenMetric(name, value) {
|
|
146
|
+
return {
|
|
147
|
+
name,
|
|
148
|
+
unit: 'tokens',
|
|
149
|
+
sum: {
|
|
150
|
+
aggregationTemporality: 1, // DELTA
|
|
151
|
+
isMonotonic: true,
|
|
152
|
+
dataPoints: [{
|
|
153
|
+
timeUnixNano: endedAtUnixNs,
|
|
154
|
+
startTimeUnixNano: startedAtUnixNs,
|
|
155
|
+
asInt: String(value),
|
|
156
|
+
}],
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
const durationMs = endedAtMs - startedAtMs;
|
|
161
|
+
const metricsPayload = {
|
|
162
|
+
resourceMetrics: [{
|
|
163
|
+
resource,
|
|
164
|
+
scopeMetrics: [{
|
|
165
|
+
scope: scopeKey,
|
|
166
|
+
metrics: [
|
|
167
|
+
tokenMetric('specrails.codex.tokens.input', tokensIn),
|
|
168
|
+
tokenMetric('specrails.codex.tokens.output', tokensOut),
|
|
169
|
+
tokenMetric('specrails.codex.tokens.cache_read', tokensCacheRead),
|
|
170
|
+
{
|
|
171
|
+
name: 'specrails.codex.duration_ms',
|
|
172
|
+
unit: 'ms',
|
|
173
|
+
gauge: {
|
|
174
|
+
dataPoints: [{
|
|
175
|
+
timeUnixNano: endedAtUnixNs,
|
|
176
|
+
asInt: String(durationMs),
|
|
177
|
+
}],
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
}],
|
|
182
|
+
}],
|
|
183
|
+
};
|
|
184
|
+
// ── Logs ───────────────────────────────────────────────────────────────
|
|
185
|
+
const logsPayload = {
|
|
186
|
+
resourceLogs: [{
|
|
187
|
+
resource,
|
|
188
|
+
scopeLogs: [{
|
|
189
|
+
scope: scopeKey,
|
|
190
|
+
logRecords: logRecords.map((r) => ({
|
|
191
|
+
timeUnixNano: r.timeUnixNano,
|
|
192
|
+
severityNumber: 9, // INFO
|
|
193
|
+
severityText: 'INFO',
|
|
194
|
+
body: r.body,
|
|
195
|
+
})),
|
|
196
|
+
}],
|
|
197
|
+
}],
|
|
198
|
+
};
|
|
199
|
+
const url = (signal) => `http://127.0.0.1:${opts.desktopPort}/otlp/v1/${signal}`;
|
|
200
|
+
const results = await Promise.allSettled([
|
|
201
|
+
poster(url('traces'), JSON.stringify(tracesPayload)),
|
|
202
|
+
poster(url('metrics'), JSON.stringify(metricsPayload)),
|
|
203
|
+
poster(url('logs'), JSON.stringify(logsPayload)),
|
|
204
|
+
]);
|
|
205
|
+
// Log POST failures but never throw — telemetry is best-effort.
|
|
206
|
+
for (const r of results) {
|
|
207
|
+
if (r.status === 'rejected') {
|
|
208
|
+
console.warn('[codex-otel-bridge] poster rejected:', r.reason);
|
|
209
|
+
}
|
|
210
|
+
else if (!r.value.ok) {
|
|
211
|
+
console.warn(`[codex-otel-bridge] poster non-ok status ${r.value.status}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
consumeEvent,
|
|
217
|
+
finalize,
|
|
218
|
+
_debugLogBytes: () => logsBytes,
|
|
219
|
+
_debugTruncated: () => truncated,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
// ─── Default poster (uses globalThis.fetch) ─────────────────────────────────
|
|
223
|
+
const defaultPoster = async (url, body) => {
|
|
224
|
+
try {
|
|
225
|
+
const res = await fetch(url, {
|
|
226
|
+
method: 'POST',
|
|
227
|
+
headers: { 'content-type': 'application/json' },
|
|
228
|
+
body,
|
|
229
|
+
});
|
|
230
|
+
return { ok: res.ok, status: res.status };
|
|
231
|
+
}
|
|
232
|
+
catch (err) {
|
|
233
|
+
return { ok: false, status: 0, body: err.message };
|
|
234
|
+
}
|
|
235
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveCommand = resolveCommand;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
/**
|
|
7
|
+
* Locate the app repository root (the directory containing .claude/commands/).
|
|
8
|
+
* Works in both dev mode (tsx: __dirname = <repo>/server/) and
|
|
9
|
+
* compiled mode (tsc: __dirname = <repo>/server/dist/).
|
|
10
|
+
*/
|
|
11
|
+
function findDesktopRoot() {
|
|
12
|
+
let dir = (0, path_1.resolve)(__dirname, '..');
|
|
13
|
+
if ((0, fs_1.existsSync)((0, path_1.join)(dir, '.claude', 'commands')))
|
|
14
|
+
return dir;
|
|
15
|
+
dir = (0, path_1.resolve)(__dirname, '../..');
|
|
16
|
+
if ((0, fs_1.existsSync)((0, path_1.join)(dir, '.claude', 'commands')))
|
|
17
|
+
return dir;
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const DESKTOP_ROOT = findDesktopRoot();
|
|
21
|
+
function builtInCommand(commandPath, commandArgs) {
|
|
22
|
+
if (commandPath !== 'specrails:explore-spec')
|
|
23
|
+
return null;
|
|
24
|
+
return `You are a senior product engineer helping the user shape one backlog spec inside Specrails' Explore Spec experience.
|
|
25
|
+
|
|
26
|
+
Do not use any local skill, slash-command workflow, or repository-change workflow. Stay inside this chat and shape the draft ticket only. Do not inspect active change folders unless the user explicitly asks about them, and do not create or modify files. The app commits the final ticket only when the user clicks Create Spec.
|
|
27
|
+
|
|
28
|
+
Your job is to maintain a live draft. After every assistant turn that changes draft state, end your message with a fenced \`spec-draft\` JSON block. The visible prose should match the user's language. Draft fields must be written in English.
|
|
29
|
+
|
|
30
|
+
Use this draft schema. Omit fields you do not want to update:
|
|
31
|
+
|
|
32
|
+
\`\`\`spec-draft
|
|
33
|
+
{
|
|
34
|
+
"title": "Concise, action-oriented title",
|
|
35
|
+
"description": "## Problem Statement\\n2-3 sentences.\\n\\n## Proposed Solution\\n3-5 sentences.\\n\\n## Out of Scope\\n- bullet\\n\\n## Technical Considerations\\n- bullet\\n\\n## Estimated Complexity\\nMedium - one sentence justification.",
|
|
36
|
+
"labels": ["short-label"],
|
|
37
|
+
"priority": "low | medium | high | critical",
|
|
38
|
+
"acceptanceCriteria": ["Short, testable criterion"],
|
|
39
|
+
"chips": ["Up to 3 short replies"],
|
|
40
|
+
"ready": false
|
|
41
|
+
}
|
|
42
|
+
\`\`\`
|
|
43
|
+
|
|
44
|
+
Rules:
|
|
45
|
+
- Ask only the clarifying questions genuinely needed to make the spec concrete.
|
|
46
|
+
- Keep visible replies brief.
|
|
47
|
+
- Set \`ready: true\` only when the draft has a title, description, acceptance criteria, and no outstanding clarifying question.
|
|
48
|
+
- Never call \`/specrails:propose-spec\`, \`/specrails:implement\`, or any slash command with side effects.
|
|
49
|
+
|
|
50
|
+
The user's idea follows below. Begin the Explore Spec conversation.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
${commandArgs}`.trim();
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Try to find a command/skill .md file for the given command path parts
|
|
58
|
+
* within the given base directory. Returns the resolved path or null.
|
|
59
|
+
*/
|
|
60
|
+
function findCommandFile(baseDir, parts) {
|
|
61
|
+
const filePath = (0, path_1.join)(baseDir, '.claude', 'commands', ...parts) + '.md';
|
|
62
|
+
if ((0, fs_1.existsSync)(filePath))
|
|
63
|
+
return filePath;
|
|
64
|
+
const skillPath = (0, path_1.join)(baseDir, '.claude', 'skills', ...parts) + '.md';
|
|
65
|
+
if ((0, fs_1.existsSync)(skillPath))
|
|
66
|
+
return skillPath;
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Resolves a slash command string to its full prompt content.
|
|
71
|
+
* Reads the command file from .claude/commands/ or .claude/skills/,
|
|
72
|
+
* strips YAML frontmatter, and substitutes $ARGUMENTS.
|
|
73
|
+
*
|
|
74
|
+
* Searches the project directory first, then falls back to the app's
|
|
75
|
+
* own .claude/commands/ directory (for app-namespaced commands like
|
|
76
|
+
* /specrails:implement that aren't installed in the target project).
|
|
77
|
+
*
|
|
78
|
+
* Falls back to returning the command string as-is if the file is not found.
|
|
79
|
+
*/
|
|
80
|
+
function resolveCommand(command, cwd) {
|
|
81
|
+
const match = command.match(/^\/([^\s]+)\s*(.*)$/s);
|
|
82
|
+
if (!match)
|
|
83
|
+
return command;
|
|
84
|
+
const commandPath = match[1];
|
|
85
|
+
const commandArgs = match[2].trim();
|
|
86
|
+
const parts = commandPath.split(':');
|
|
87
|
+
const builtIn = builtInCommand(commandPath, commandArgs);
|
|
88
|
+
if (builtIn)
|
|
89
|
+
return builtIn;
|
|
90
|
+
// 1. Check the project directory
|
|
91
|
+
let resolvedPath = findCommandFile(cwd, parts);
|
|
92
|
+
// 2. Fallback: check the app's own directory
|
|
93
|
+
if (!resolvedPath && DESKTOP_ROOT && (0, path_1.resolve)(cwd) !== (0, path_1.resolve)(DESKTOP_ROOT)) {
|
|
94
|
+
resolvedPath = findCommandFile(DESKTOP_ROOT, parts);
|
|
95
|
+
}
|
|
96
|
+
if (!resolvedPath)
|
|
97
|
+
return command;
|
|
98
|
+
let content = (0, fs_1.readFileSync)(resolvedPath, 'utf-8');
|
|
99
|
+
content = content.replace(/^---[\s\S]*?---\s*/, '');
|
|
100
|
+
content = content.replace(/\$ARGUMENTS/g, commandArgs);
|
|
101
|
+
return content.trim();
|
|
102
|
+
}
|
|
@@ -0,0 +1,306 @@
|
|
|
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.__resetTrackerDetectionCacheForTest = __resetTrackerDetectionCacheForTest;
|
|
7
|
+
exports.getConfig = getConfig;
|
|
8
|
+
exports.fetchIssues = fetchIssues;
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const child_process_1 = require("child_process");
|
|
12
|
+
function runCommand(cmd, cwd) {
|
|
13
|
+
try {
|
|
14
|
+
return (0, child_process_1.execSync)(cmd, { stdio: ['ignore', 'pipe', 'ignore'], timeout: 5000, cwd }).toString().trim();
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Run a command with an explicit argv array via `execFileSync` — NO shell (H-12).
|
|
22
|
+
*
|
|
23
|
+
* `runCommand` routes through `/bin/sh -c`, so any shell metacharacter in an
|
|
24
|
+
* interpolated value is a command-injection sink. `fetchIssues` builds its
|
|
25
|
+
* arguments from HTTP query params (`search`/`label`) and the git remote, so it
|
|
26
|
+
* MUST use this argv form: each argument is passed literally to the program and
|
|
27
|
+
* the shell never sees it. `runCommand` is retained only for fixed, constant
|
|
28
|
+
* detection commands (`which gh`, `gh auth status`, `git remote get-url origin`)
|
|
29
|
+
* that carry no user input.
|
|
30
|
+
*/
|
|
31
|
+
function runCommandArgs(file, args, cwd) {
|
|
32
|
+
try {
|
|
33
|
+
return (0, child_process_1.execFileSync)(file, args, { stdio: ['ignore', 'pipe', 'ignore'], timeout: 5000, cwd }).toString().trim();
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function detectGithub() {
|
|
40
|
+
const ghPath = runCommand('which gh');
|
|
41
|
+
if (!ghPath)
|
|
42
|
+
return { available: false, authenticated: false };
|
|
43
|
+
const authOutput = runCommand('gh auth status');
|
|
44
|
+
const authenticated = authOutput !== null;
|
|
45
|
+
return { available: true, authenticated };
|
|
46
|
+
}
|
|
47
|
+
function detectJira() {
|
|
48
|
+
const jiraPath = runCommand('which jira');
|
|
49
|
+
if (!jiraPath)
|
|
50
|
+
return { available: false, authenticated: false };
|
|
51
|
+
// jira CLI availability means it is configured (auth is implicit via jira config)
|
|
52
|
+
return { available: true, authenticated: true };
|
|
53
|
+
}
|
|
54
|
+
// H19: `which gh` + `gh auth status` (a network round-trip, 0.3-2s) + `which
|
|
55
|
+
// jira` are project-independent but ran synchronously on every getConfig()
|
|
56
|
+
// call — once per project in the startup loadAll() and again on each
|
|
57
|
+
// GET /config | GET /issues — blocking the single event loop app-wide.
|
|
58
|
+
// Memoize with a short TTL (same pattern as setup-prerequisites.ts) so startup
|
|
59
|
+
// probes once and hot request paths reuse the cached result; auth/install
|
|
60
|
+
// changes surface after at most TTL.
|
|
61
|
+
let _trackerCache = null;
|
|
62
|
+
const TRACKER_CACHE_TTL_MS = 60_000;
|
|
63
|
+
function detectTrackers() {
|
|
64
|
+
const now = Date.now();
|
|
65
|
+
if (_trackerCache && now - _trackerCache.at < TRACKER_CACHE_TTL_MS) {
|
|
66
|
+
return _trackerCache;
|
|
67
|
+
}
|
|
68
|
+
_trackerCache = { at: now, github: detectGithub(), jira: detectJira() };
|
|
69
|
+
return _trackerCache;
|
|
70
|
+
}
|
|
71
|
+
/** Test-only: clear the tracker-detection memo so each test re-probes. */
|
|
72
|
+
function __resetTrackerDetectionCacheForTest() {
|
|
73
|
+
_trackerCache = null;
|
|
74
|
+
}
|
|
75
|
+
function getGitRepoName(projectRoot) {
|
|
76
|
+
const output = runCommand('git remote get-url origin', projectRoot);
|
|
77
|
+
if (!output)
|
|
78
|
+
return null;
|
|
79
|
+
// Parse both HTTPS and SSH remote URLs
|
|
80
|
+
// https://github.com/owner/repo.git → owner/repo
|
|
81
|
+
// git@github.com:owner/repo.git → owner/repo
|
|
82
|
+
const httpsMatch = output.match(/github\.com\/([^/]+\/[^/]+?)(?:\.git)?$/);
|
|
83
|
+
if (httpsMatch)
|
|
84
|
+
return httpsMatch[1];
|
|
85
|
+
const sshMatch = output.match(/github\.com:([^/]+\/[^/]+?)(?:\.git)?$/);
|
|
86
|
+
if (sshMatch)
|
|
87
|
+
return sshMatch[1];
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
function parseFrontmatter(content) {
|
|
91
|
+
const result = { scalars: {}, phases: [] };
|
|
92
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
93
|
+
if (!match)
|
|
94
|
+
return result;
|
|
95
|
+
const lines = match[1].split('\n');
|
|
96
|
+
let i = 0;
|
|
97
|
+
while (i < lines.length) {
|
|
98
|
+
const line = lines[i];
|
|
99
|
+
const colonIdx = line.indexOf(':');
|
|
100
|
+
if (colonIdx === -1) {
|
|
101
|
+
i++;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const key = line.slice(0, colonIdx).trim();
|
|
105
|
+
const rawValue = line.slice(colonIdx + 1).trim();
|
|
106
|
+
if (!key) {
|
|
107
|
+
i++;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
// Array field: value is empty, subsequent lines start with " - "
|
|
111
|
+
if (rawValue === '' && i + 1 < lines.length && lines[i + 1].startsWith(' - ')) {
|
|
112
|
+
i++;
|
|
113
|
+
if (key === 'phases') {
|
|
114
|
+
const items = [];
|
|
115
|
+
let current = null;
|
|
116
|
+
while (i < lines.length) {
|
|
117
|
+
const aLine = lines[i];
|
|
118
|
+
if (aLine.startsWith(' - ')) {
|
|
119
|
+
// New array item — flush current
|
|
120
|
+
if (current)
|
|
121
|
+
items.push(current);
|
|
122
|
+
current = {};
|
|
123
|
+
// Parse the inline key: value after " - "
|
|
124
|
+
const rest = aLine.slice(4);
|
|
125
|
+
const aColon = rest.indexOf(':');
|
|
126
|
+
if (aColon !== -1) {
|
|
127
|
+
const aKey = rest.slice(0, aColon).trim();
|
|
128
|
+
const aVal = rest.slice(aColon + 1).trim().replace(/^["']|["']$/g, '');
|
|
129
|
+
current[aKey] = aVal;
|
|
130
|
+
}
|
|
131
|
+
i++;
|
|
132
|
+
}
|
|
133
|
+
else if (aLine.startsWith(' ') && !aLine.startsWith(' - ')) {
|
|
134
|
+
// Continuation key: value for current item
|
|
135
|
+
if (current) {
|
|
136
|
+
const aColon = aLine.indexOf(':');
|
|
137
|
+
if (aColon !== -1) {
|
|
138
|
+
const aKey = aLine.slice(0, aColon).trim();
|
|
139
|
+
const aVal = aLine.slice(aColon + 1).trim().replace(/^["']|["']$/g, '');
|
|
140
|
+
current[aKey] = aVal;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
i++;
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (current)
|
|
150
|
+
items.push(current);
|
|
151
|
+
result.phases = items.filter((item) => typeof item.key === 'string' &&
|
|
152
|
+
typeof item.label === 'string' &&
|
|
153
|
+
typeof item.description === 'string');
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
// Skip unknown array fields
|
|
157
|
+
while (i < lines.length && (lines[i].startsWith(' - ') || lines[i].startsWith(' '))) {
|
|
158
|
+
i++;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
result.scalars[key] = rawValue.replace(/^["']|["']$/g, '');
|
|
164
|
+
i++;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
function slugify(name) {
|
|
170
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
171
|
+
}
|
|
172
|
+
function scanCommands(commandsDir) {
|
|
173
|
+
if (!fs_1.default.existsSync(commandsDir))
|
|
174
|
+
return [];
|
|
175
|
+
let files;
|
|
176
|
+
try {
|
|
177
|
+
files = fs_1.default.readdirSync(commandsDir).filter((f) => f.endsWith('.md'));
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
return [];
|
|
181
|
+
}
|
|
182
|
+
return files.map((file) => {
|
|
183
|
+
const slug = file.replace(/\.md$/, '');
|
|
184
|
+
let name = slug;
|
|
185
|
+
let description = '';
|
|
186
|
+
let phases = [];
|
|
187
|
+
try {
|
|
188
|
+
const content = fs_1.default.readFileSync(path_1.default.join(commandsDir, file), 'utf-8');
|
|
189
|
+
const fm = parseFrontmatter(content);
|
|
190
|
+
if (fm.scalars.name)
|
|
191
|
+
name = fm.scalars.name;
|
|
192
|
+
if (fm.scalars.description)
|
|
193
|
+
description = fm.scalars.description;
|
|
194
|
+
phases = fm.phases;
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
// Use filename-derived name if frontmatter parsing fails
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
id: slug,
|
|
201
|
+
name,
|
|
202
|
+
description,
|
|
203
|
+
slug,
|
|
204
|
+
phases,
|
|
205
|
+
};
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
function loadPersistedConfig(db) {
|
|
209
|
+
try {
|
|
210
|
+
const activeRow = db.prepare(`SELECT value FROM queue_state WHERE key = 'config.active_tracker'`).get();
|
|
211
|
+
const labelRow = db.prepare(`SELECT value FROM queue_state WHERE key = 'config.label_filter'`).get();
|
|
212
|
+
return {
|
|
213
|
+
active: activeRow?.value ?? null,
|
|
214
|
+
labelFilter: labelRow?.value ?? '',
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
return { active: null, labelFilter: '' };
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function getConfig(cwd, db, projectName) {
|
|
222
|
+
// Resolve project root. Super mode passes project.path directly, which has a
|
|
223
|
+
// `.claude` directory at its root; the walk-up fallback covers callers that
|
|
224
|
+
// pass a manager-relative cwd.
|
|
225
|
+
let projectRoot;
|
|
226
|
+
if (fs_1.default.existsSync(path_1.default.join(cwd, '.claude'))) {
|
|
227
|
+
projectRoot = cwd;
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
projectRoot = path_1.default.resolve(cwd, '../..');
|
|
231
|
+
}
|
|
232
|
+
const commandsDir = path_1.default.join(projectRoot, '.claude', 'commands', 'sr');
|
|
233
|
+
const commands = scanCommands(commandsDir);
|
|
234
|
+
const { github, jira } = detectTrackers();
|
|
235
|
+
const repo = getGitRepoName(projectRoot);
|
|
236
|
+
const persisted = db ? loadPersistedConfig(db) : { active: null, labelFilter: '' };
|
|
237
|
+
// Auto-detect active tracker if not persisted
|
|
238
|
+
let active = persisted.active;
|
|
239
|
+
if (!active) {
|
|
240
|
+
if (github.authenticated)
|
|
241
|
+
active = 'github';
|
|
242
|
+
else if (jira.authenticated)
|
|
243
|
+
active = 'jira';
|
|
244
|
+
}
|
|
245
|
+
return {
|
|
246
|
+
project: {
|
|
247
|
+
name: projectName ?? path_1.default.basename(projectRoot),
|
|
248
|
+
repo: repo,
|
|
249
|
+
},
|
|
250
|
+
issueTracker: {
|
|
251
|
+
github,
|
|
252
|
+
jira,
|
|
253
|
+
active,
|
|
254
|
+
labelFilter: persisted.labelFilter,
|
|
255
|
+
},
|
|
256
|
+
commands,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
function fetchIssues(tracker, opts) {
|
|
260
|
+
if (tracker === 'github') {
|
|
261
|
+
const args = ['issue', 'list', '--json', 'number,title,labels,body,url', '--limit', '50'];
|
|
262
|
+
if (opts.repo)
|
|
263
|
+
args.push('--repo', opts.repo);
|
|
264
|
+
if (opts.label)
|
|
265
|
+
args.push('--label', opts.label);
|
|
266
|
+
if (opts.search)
|
|
267
|
+
args.push('--search', opts.search);
|
|
268
|
+
const output = runCommandArgs('gh', args, opts.cwd);
|
|
269
|
+
if (!output)
|
|
270
|
+
return [];
|
|
271
|
+
try {
|
|
272
|
+
const raw = JSON.parse(output);
|
|
273
|
+
return raw.map((item) => ({
|
|
274
|
+
number: item.number,
|
|
275
|
+
title: item.title,
|
|
276
|
+
labels: item.labels.map((l) => l.name),
|
|
277
|
+
body: item.body ?? '',
|
|
278
|
+
url: item.url,
|
|
279
|
+
}));
|
|
280
|
+
}
|
|
281
|
+
catch {
|
|
282
|
+
return [];
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (tracker === 'jira') {
|
|
286
|
+
const jql = opts.search ? `summary ~ "${opts.search}"` : '';
|
|
287
|
+
const args = ['issue', 'list', '--plain', '--columns', 'KEY,SUMMARY,LABELS,STATUS'];
|
|
288
|
+
if (jql)
|
|
289
|
+
args.push('--jql', jql);
|
|
290
|
+
const output = runCommandArgs('jira', args);
|
|
291
|
+
if (!output)
|
|
292
|
+
return [];
|
|
293
|
+
// Parse plain text output: KEY SUMMARY LABELS STATUS
|
|
294
|
+
const lines = output.split('\n').filter(Boolean);
|
|
295
|
+
return lines.slice(1).map((line, idx) => {
|
|
296
|
+
const parts = line.split('\t');
|
|
297
|
+
return {
|
|
298
|
+
number: idx + 1,
|
|
299
|
+
title: parts[1]?.trim() ?? line,
|
|
300
|
+
labels: parts[2] ? parts[2].split(',').map((l) => l.trim()) : [],
|
|
301
|
+
body: '',
|
|
302
|
+
};
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
return [];
|
|
306
|
+
}
|