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,155 @@
|
|
|
1
|
+
# Tracking cost
|
|
2
|
+
|
|
3
|
+
specrails-desktop records every AI CLI invocation it spawns — both Claude and Codex — and surfaces the totals on one Analytics page per project. This guide walks through what's tracked, what's not, and how to read the dashboard.
|
|
4
|
+
|
|
5
|
+
## What gets tracked
|
|
6
|
+
|
|
7
|
+
Six surfaces, all per-project:
|
|
8
|
+
|
|
9
|
+
| Surface | What it captures |
|
|
10
|
+
|---------|------------------|
|
|
11
|
+
| **`job`** | Every rail run (the Architect → Developer → Reviewer → Ship pipeline, plus Ultracode and Batch runs) |
|
|
12
|
+
| **`quick-spec`** | One-shot Quick spec generation (including a Quick-mode Contract Refine run) |
|
|
13
|
+
| **`explore-spec`** | Every Explore conversation turn (including the Explore Contract Refine run) |
|
|
14
|
+
| **`ai-edit`** | The **AI Edit / Refine** overlay (refining an agent or a ticket) |
|
|
15
|
+
| **`smash`** | SMASH runs that break an epic spec into sub-specs |
|
|
16
|
+
| **`file-summary`** | Code-explorer AI summaries of individual files |
|
|
17
|
+
|
|
18
|
+
Each invocation row carries: provider (Claude or Codex), model, status, started/finished timestamps, duration (wall clock + API-only), tokens (in / out / cache read / cache create), total USD cost, turn count, and — when applicable — the ticket and conversation IDs it touched.
|
|
19
|
+
|
|
20
|
+
> Cost averages exclude `failed`/`aborted` rows, but those rows still count toward the total run count and the failure rate.
|
|
21
|
+
|
|
22
|
+
## Claude vs Codex cost
|
|
23
|
+
|
|
24
|
+
Claude cost is **provider-billed and authoritative** — the figure comes straight from the CLI.
|
|
25
|
+
|
|
26
|
+
Codex has no native cost field, so the app **estimates** its cost from a local rate-card (`server/pricing.ts`). Estimated rows are flagged: they render with a `~` tilde in the raw table and feed an "includes ~$X estimated" footnote in the Hero. On multi-provider projects, a **Provider breakdown** card splits spend by engine so you can see authoritative vs estimated at a glance.
|
|
27
|
+
|
|
28
|
+
## What's NOT tracked (intentionally)
|
|
29
|
+
|
|
30
|
+
- **Sidebar chat** — the general-purpose chat panel in the right sidebar. It spawns an AI process but isn't pipeline work, so the app excludes it from analytics by design.
|
|
31
|
+
- **Setup wizard** — the install/enrich flow when you add a project. It *does* spawn an AI CLI (a genuine model invocation), but it's an interactive one-time wizard rather than a repeatable pipeline job, so it's deliberately left uninstrumented.
|
|
32
|
+
|
|
33
|
+
If you want the absolute total of what an engine has cost you, your provider's own console (e.g. the Anthropic console for Claude) is the source of truth.
|
|
34
|
+
|
|
35
|
+
## The Analytics page
|
|
36
|
+
|
|
37
|
+
Open **Analytics** from the project right sidebar.
|
|
38
|
+
|
|
39
|
+
### Sticky filter header
|
|
40
|
+
|
|
41
|
+
- **Period** — `7d`, `30d`, `90d`, or `All`. (There's no 24-hour or custom-range option here — the custom calendar range lives on Desktop Analytics, below.)
|
|
42
|
+
- **Surface** — chips for `All` plus each of the six surfaces (Jobs, Explore, Quick, Refine, SMASH, File summaries). All are included by default; toggle chips to narrow the view.
|
|
43
|
+
- **Engine** — provider chips (Claude / Codex) that appear **only on multi-provider projects**, letting you filter by engine.
|
|
44
|
+
|
|
45
|
+
The period and surface filters are URL-synced, so you can share or bookmark a view.
|
|
46
|
+
|
|
47
|
+
### Hero burn meter
|
|
48
|
+
|
|
49
|
+
The big number at the top is total spend in the selected period, with:
|
|
50
|
+
|
|
51
|
+
- a `% vs prev` delta (the same period length ending where the previous interval ended),
|
|
52
|
+
- the invocation count and total token count,
|
|
53
|
+
- a per-surface cost breakdown bar with a legend (cost + count per surface),
|
|
54
|
+
- and, when any rows are estimated, an "includes ~$X estimated" footnote.
|
|
55
|
+
|
|
56
|
+
When the project has zero invocations in the period (e.g. you just started), the Hero shows "Tracking started YYYY-MM-DD" — the date of the first invocation ever recorded. There's no historical backfill; tracking begins at that first row.
|
|
57
|
+
|
|
58
|
+
### Provider breakdown
|
|
59
|
+
|
|
60
|
+
On multi-provider projects, a dedicated card splits total spend between Claude and Codex. It's hidden on single-provider projects.
|
|
61
|
+
|
|
62
|
+
### Daily stacked timeline
|
|
63
|
+
|
|
64
|
+
A daily bar chart for the period, stacked by surface. Days with zero activity are zero-filled so the x-axis stays regular.
|
|
65
|
+
|
|
66
|
+
Surface colours are consistent across the whole page:
|
|
67
|
+
|
|
68
|
+
- `job` → blue (`accent-info`)
|
|
69
|
+
- `quick-spec` → purple (`accent-secondary`)
|
|
70
|
+
- `explore-spec` → highlight (`accent-highlight`)
|
|
71
|
+
- `ai-edit` → green (`accent-success`)
|
|
72
|
+
- `smash` → highlight (`accent-highlight`)
|
|
73
|
+
- `file-summary` → yellow (`accent-warning`)
|
|
74
|
+
|
|
75
|
+
### Quick vs Explore card
|
|
76
|
+
|
|
77
|
+
Share-of-spend between Quick spec generation and Explore conversations, with a small sparkline. If Explore has fewer than 5 runs in the period, a CTA nudges you to try it.
|
|
78
|
+
|
|
79
|
+
### Top models
|
|
80
|
+
|
|
81
|
+
The top 10 models used in the period with their share of spend. Click a row to filter the rest of the page by that model.
|
|
82
|
+
|
|
83
|
+
### Cost-vs-turns scatter
|
|
84
|
+
|
|
85
|
+
A scatter plot of the last 500 invocations: x = turns, y = cost. Handy for spotting the outlier "this Explore session burned 4×" runs.
|
|
86
|
+
|
|
87
|
+
### Top tickets
|
|
88
|
+
|
|
89
|
+
The top 10 tickets by total spend across all surfaces, ranked by cost. Two special cases compete for those slots:
|
|
90
|
+
|
|
91
|
+
- **Deleted tickets** appear individually, one per ticket (e.g. `deleted ticket #42`) — invocations whose `ticket_id` no longer resolves.
|
|
92
|
+
- **Unattributed** is a single synthetic bucket for invocations with no `ticket_id` (mostly mid-Explore turns before commit).
|
|
93
|
+
|
|
94
|
+
Neither is pinned — they show up only when they rank into the top 10.
|
|
95
|
+
|
|
96
|
+
### Raw invocations table
|
|
97
|
+
|
|
98
|
+
At the bottom of the page. It loads up to 100 rows in one batch and shows a "Showing first N of M matching rows" note when there are more. Secondary filters scoped to the table only (a narrower date range, status, and minimum cost) let you drill in.
|
|
99
|
+
|
|
100
|
+
Columns: **Surface, Ticket, Cost, Turns, Tokens, Model, Status, Started.**
|
|
101
|
+
|
|
102
|
+
A few rendering details to know:
|
|
103
|
+
|
|
104
|
+
- Contract Refine invocations show as **Contract Layer** in the Surface column and "Contract Layer refinement" in the Ticket column.
|
|
105
|
+
- Uncommitted Explore turns show a provisional, italicised title derived from the first user message — these are tickets that aren't committed yet.
|
|
106
|
+
|
|
107
|
+
## Per-ticket spending
|
|
108
|
+
|
|
109
|
+
Inside any spec's detail modal there's a one-line spending summary just below the title:
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
$0.42 · 3 turns · 4m 12s active · 2 jobs + 1 explore
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The breakdown segment is a **count** of invocations per surface, not a cost split. Clicking the line opens Analytics filtered to that ticket (`?ticketId=<id>` in the URL).
|
|
116
|
+
|
|
117
|
+
## Exports
|
|
118
|
+
|
|
119
|
+
Top-right of the Analytics page → the **Export** dropdown. Four options:
|
|
120
|
+
|
|
121
|
+
| Export | Format | What's included |
|
|
122
|
+
|--------|--------|-----------------|
|
|
123
|
+
| **Summary CSV** | Multi-section CSV | Totals, daily timeline, by-surface, by-model, top tickets |
|
|
124
|
+
| **Summary JSON** | JSON | Same data, structured |
|
|
125
|
+
| **Raw CSV** | CSV | One row per invocation, capped at 10,000 rows |
|
|
126
|
+
| **Raw JSON** | JSON | Same |
|
|
127
|
+
|
|
128
|
+
Filename patterns differ by mode: Summary exports use `<project-slug>-analytics-<period>-<YYYY-MM-DD>.{csv,json}`; Raw exports use `<project-slug>-invocations-<period>[-<surface>]-<YYYY-MM-DD>.{csv,json}` — the optional `-<surface>` segment appears only on raw exports.
|
|
129
|
+
|
|
130
|
+
Raw exports above 10,000 rows append a `# truncated_at=N of M` comment line. If you need more, narrow the period or surface filter first.
|
|
131
|
+
|
|
132
|
+
Exports use the standard `fetch → Blob → URL.createObjectURL → anchor.click()` pattern, so they work the same in the Tauri desktop webview as in a browser. On failure a sonner `Export failed` toast surfaces.
|
|
133
|
+
|
|
134
|
+
## Setting a budget
|
|
135
|
+
|
|
136
|
+
Open **Settings** from the project right sidebar → the **Budget** card:
|
|
137
|
+
|
|
138
|
+
- **Daily budget (USD)** — the app auto-pauses the queue when the day's spend (the sum of completed-job cost since midnight) exceeds this. New rail launches queue rather than start; in-flight jobs aren't killed.
|
|
139
|
+
- **Per-job cost alert (USD)** — emits an alert when a single job exceeds this amount.
|
|
140
|
+
|
|
141
|
+
Leave either field blank to disable that limit.
|
|
142
|
+
|
|
143
|
+
When the daily budget is hit, a dismissible banner appears across the project pages ("Daily budget exceeded — … Queue is paused.") and the queue stays paused server-side. Resume by raising the budget, clearing it, or waiting for the counter to reset at midnight (it counts completed-job cost since the start of the current calendar day, not a rolling 24-hour window).
|
|
144
|
+
|
|
145
|
+
See [Customising the app](customizing.md#budget) for the full setup.
|
|
146
|
+
|
|
147
|
+
## App-wide analytics
|
|
148
|
+
|
|
149
|
+
For the cross-project view, open **Analytics** from the Arc sidebar on the left. Desktop Analytics is a separate page with its own blocks — KPI cards, a cost timeline, and per-project breakdowns — rolled up across all your registered projects. It also offers a custom calendar date range that the per-project page doesn't.
|
|
150
|
+
|
|
151
|
+
## Where to go next
|
|
152
|
+
|
|
153
|
+
- [Customising the app](customizing.md) — set the budget, configure notifications.
|
|
154
|
+
- [Creating specs](creating-specs.md) — every spec you create adds rows to your analytics.
|
|
155
|
+
- [Using Codex](codex.md) — how cost works when you run a project on Codex.
|
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "specrails-desktop",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/fjpulidop/specrails-desktop.git"
|
|
8
|
+
},
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=20.0.0"
|
|
11
|
+
},
|
|
12
|
+
"bin": {
|
|
13
|
+
"specrails-desktop": "cli/dist/specrails-desktop.js"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"cli/dist",
|
|
17
|
+
"server/dist",
|
|
18
|
+
"client/dist",
|
|
19
|
+
"docs",
|
|
20
|
+
".claude/commands/specrails"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"dev": "concurrently \"npm run dev:server\" \"npm run dev:client\"",
|
|
24
|
+
"dev:server": "tsx watch server/index.ts",
|
|
25
|
+
"dev:client": "cd client && npm run dev",
|
|
26
|
+
"dev:desktop": "tauri dev",
|
|
27
|
+
"build:server": "node -e \"require('fs').rmSync('server/dist',{recursive:true,force:true})\" && tsc --project tsconfig.json",
|
|
28
|
+
"build:cli": "tsc --project tsconfig.cli.json",
|
|
29
|
+
"build": "npm run build:server && cd client && npm run build && cd .. && npm run build:cli",
|
|
30
|
+
"build:sidecar": "node scripts/build-sidecar.mjs",
|
|
31
|
+
"build:desktop": "npm run build:server && cd client && npm run build && cd .. && npm run build:sidecar && tauri build",
|
|
32
|
+
"assemble-runtimes": "node scripts/assemble-runtimes-local.mjs",
|
|
33
|
+
"build:desktop:local": "npm run assemble-runtimes && npm run build:desktop",
|
|
34
|
+
"generate-icons": "tauri icon src-tauri/icons/icon.svg",
|
|
35
|
+
"typecheck": "tsc --noEmit && tsc -p tsconfig.cli.json --noEmit && cd client && tsc --noEmit",
|
|
36
|
+
"check-core-compat": "tsx scripts/check-core-compat.ts",
|
|
37
|
+
"test": "vitest run && tsx scripts/check-core-compat.ts",
|
|
38
|
+
"test:watch": "vitest",
|
|
39
|
+
"test:coverage": "vitest run --coverage",
|
|
40
|
+
"test:client": "cd client && npx vitest run",
|
|
41
|
+
"test:all": "vitest run && cd client && npx vitest run",
|
|
42
|
+
"ci": "npm run typecheck && npm run check-core-compat && npm run test:coverage && cd client && npm run test:coverage"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@homebridge/ciao": "^1.3.9",
|
|
46
|
+
"@tailwindcss/typography": "^0.5.19",
|
|
47
|
+
"ajv": "^8.17.1",
|
|
48
|
+
"better-sqlite3": "^12.8.0",
|
|
49
|
+
"chokidar": "^5.0.0",
|
|
50
|
+
"cross-spawn": "^7.0.6",
|
|
51
|
+
"diff": "^9.0.0",
|
|
52
|
+
"express": "^5.2.1",
|
|
53
|
+
"multer": "^2.1.1",
|
|
54
|
+
"node-pty": "^1.0.0",
|
|
55
|
+
"pdf-parse": "^2.4.5",
|
|
56
|
+
"playwright": "^1.60.0",
|
|
57
|
+
"read-excel-file": "^9.0.6",
|
|
58
|
+
"selfsigned": "^5.5.0",
|
|
59
|
+
"tree-kill": "^1.2.2",
|
|
60
|
+
"ws": "^8.16.0"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@tauri-apps/cli": "^2.0.0",
|
|
64
|
+
"@types/better-sqlite3": "^7.6.0",
|
|
65
|
+
"@types/chokidar": "^1.7.5",
|
|
66
|
+
"@types/cross-spawn": "^6.0.6",
|
|
67
|
+
"@types/diff": "^7.0.2",
|
|
68
|
+
"@types/express": "^5.0.6",
|
|
69
|
+
"@types/multer": "^2.1.0",
|
|
70
|
+
"@types/node": "^25.5.0",
|
|
71
|
+
"@types/pdf-parse": "^1.1.5",
|
|
72
|
+
"@types/supertest": "^7.2.0",
|
|
73
|
+
"@types/ws": "^8.5.0",
|
|
74
|
+
"@vitest/coverage-v8": "^4.1.0",
|
|
75
|
+
"concurrently": "^9.2.1",
|
|
76
|
+
"esbuild": "^0.28.0",
|
|
77
|
+
"supertest": "^7.2.2",
|
|
78
|
+
"tsx": "^4.7.0",
|
|
79
|
+
"typescript": "^5.4.0",
|
|
80
|
+
"vitest": "^4.1.0"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateCustomAgent = generateCustomAgent;
|
|
4
|
+
exports.testCustomAgent = testCustomAgent;
|
|
5
|
+
const readline_1 = require("readline");
|
|
6
|
+
const crypto_1 = require("crypto");
|
|
7
|
+
const cli_prompt_1 = require("./util/cli-prompt");
|
|
8
|
+
/**
|
|
9
|
+
* Generate a draft `custom-*.md` body by spawning a one-shot claude
|
|
10
|
+
* invocation with an agent-authoring system prompt. Resolves with the full
|
|
11
|
+
* response text after the child process closes; rejects on non-zero exit or
|
|
12
|
+
* spawn error.
|
|
13
|
+
*
|
|
14
|
+
* Hard cap on the child: 90 seconds wall-clock. Callers should also set a
|
|
15
|
+
* timeout on their fetch/HTTP layer for safety.
|
|
16
|
+
*/
|
|
17
|
+
async function generateCustomAgent(cwd, opts) {
|
|
18
|
+
const systemPrompt = [
|
|
19
|
+
'You are a specrails agent-authoring assistant.',
|
|
20
|
+
'',
|
|
21
|
+
'Your task: given a short description of what the user wants, produce a COMPLETE',
|
|
22
|
+
'Markdown file for a specrails custom agent. The file MUST be valid input for',
|
|
23
|
+
'Claude Code: YAML frontmatter between `---` separators, followed by the agent body.',
|
|
24
|
+
'',
|
|
25
|
+
'Required frontmatter fields:',
|
|
26
|
+
' - name: the exact agent id (starts with `custom-`, lowercase, kebab-case)',
|
|
27
|
+
' - description: one sentence saying when this agent should run (include tag hints in square brackets)',
|
|
28
|
+
' - model: one of `sonnet`, `opus`, `haiku`',
|
|
29
|
+
' - color: one of `blue`, `green`, `red`, `yellow`, `purple`, `cyan`',
|
|
30
|
+
' - memory: `project`',
|
|
31
|
+
'',
|
|
32
|
+
'Body sections (use `#` headings): Identity, Mission, Workflow protocol, Personality.',
|
|
33
|
+
'Personality block: bullet list of tone, risk_tolerance, detail_level, focus_areas.',
|
|
34
|
+
'',
|
|
35
|
+
'Be concise. No conversational preamble. Output ONLY the Markdown file — no code',
|
|
36
|
+
'fences, no explanations. Start at `---`.',
|
|
37
|
+
].join('\n');
|
|
38
|
+
const userPrompt = [
|
|
39
|
+
`Generate a custom agent with id "${opts.name}".`,
|
|
40
|
+
'',
|
|
41
|
+
'Description of what it should do:',
|
|
42
|
+
opts.description,
|
|
43
|
+
].join('\n');
|
|
44
|
+
return new Promise((resolve, reject) => {
|
|
45
|
+
const child = (0, cli_prompt_1.spawnClaude)([
|
|
46
|
+
'--dangerously-skip-permissions',
|
|
47
|
+
'--output-format',
|
|
48
|
+
'stream-json',
|
|
49
|
+
'--verbose',
|
|
50
|
+
'--append-system-prompt',
|
|
51
|
+
systemPrompt,
|
|
52
|
+
'-p',
|
|
53
|
+
userPrompt,
|
|
54
|
+
], {
|
|
55
|
+
env: process.env,
|
|
56
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
57
|
+
cwd,
|
|
58
|
+
});
|
|
59
|
+
let collected = '';
|
|
60
|
+
const killer = setTimeout(() => {
|
|
61
|
+
try {
|
|
62
|
+
child.kill('SIGTERM');
|
|
63
|
+
}
|
|
64
|
+
catch { /* ignore */ }
|
|
65
|
+
reject(new Error('agent generation timed out after 90s'));
|
|
66
|
+
}, 90_000);
|
|
67
|
+
const reader = (0, readline_1.createInterface)({ input: child.stdout, crlfDelay: Infinity });
|
|
68
|
+
reader.on('line', (line) => {
|
|
69
|
+
let parsed;
|
|
70
|
+
try {
|
|
71
|
+
parsed = JSON.parse(line);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (!parsed || typeof parsed !== 'object')
|
|
77
|
+
return;
|
|
78
|
+
// Claude stream-json format: {type:"assistant", message:{content:[{type:"text", text:"..."}]}}
|
|
79
|
+
const p = parsed;
|
|
80
|
+
const message = p.message;
|
|
81
|
+
const content = message?.content;
|
|
82
|
+
if (Array.isArray(content)) {
|
|
83
|
+
for (const block of content) {
|
|
84
|
+
if (block && typeof block === 'object' && block.type === 'text') {
|
|
85
|
+
const text = block.text;
|
|
86
|
+
if (typeof text === 'string')
|
|
87
|
+
collected += text;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
let stderr = '';
|
|
93
|
+
child.stderr.on('data', (chunk) => { stderr += chunk.toString(); });
|
|
94
|
+
child.on('error', (err) => {
|
|
95
|
+
clearTimeout(killer);
|
|
96
|
+
reject(err);
|
|
97
|
+
});
|
|
98
|
+
child.on('close', (code) => {
|
|
99
|
+
clearTimeout(killer);
|
|
100
|
+
if (code !== 0) {
|
|
101
|
+
reject(new Error(`claude exited with code ${code}${stderr ? `: ${stderr.slice(-500)}` : ''}`));
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const trimmed = collected.trim();
|
|
105
|
+
if (!trimmed) {
|
|
106
|
+
reject(new Error('claude returned empty output'));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
resolve(trimmed);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Smoke-test a draft custom agent. Strips the frontmatter, uses the agent body
|
|
115
|
+
* as a claude system prompt, and runs the sample task as the user prompt. Does
|
|
116
|
+
* not touch the filesystem or register the agent anywhere — purely sandboxed.
|
|
117
|
+
*
|
|
118
|
+
* Returns the full assistant output plus token usage and duration for the
|
|
119
|
+
* Studio's Test pane and the agent_tests table.
|
|
120
|
+
*
|
|
121
|
+
* Hard cap: 120 seconds wall-clock, 4000-token configurable ceiling (callers
|
|
122
|
+
* can override via `tokenCeiling`).
|
|
123
|
+
*/
|
|
124
|
+
async function testCustomAgent(cwd, opts) {
|
|
125
|
+
const tokenCeiling = opts.tokenCeiling ?? 4000;
|
|
126
|
+
// Strip YAML frontmatter so we feed only the agent's instructions.
|
|
127
|
+
const body = opts.draftBody.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, '').trim();
|
|
128
|
+
if (!body) {
|
|
129
|
+
throw new Error('agent body is empty after stripping frontmatter');
|
|
130
|
+
}
|
|
131
|
+
const systemPrompt = [
|
|
132
|
+
'You are acting as the agent described below. Follow its Identity, Mission,',
|
|
133
|
+
'Workflow protocol, and Personality. Respond to the user task using those',
|
|
134
|
+
'instructions. Do NOT preface your response; produce only the agent output.',
|
|
135
|
+
'',
|
|
136
|
+
'--- agent instructions ---',
|
|
137
|
+
body,
|
|
138
|
+
'--- end agent instructions ---',
|
|
139
|
+
].join('\n');
|
|
140
|
+
const draftHash = (0, crypto_1.createHash)('sha256').update(opts.draftBody).digest('hex').slice(0, 16);
|
|
141
|
+
const started = Date.now();
|
|
142
|
+
return new Promise((resolve, reject) => {
|
|
143
|
+
const child = (0, cli_prompt_1.spawnClaude)([
|
|
144
|
+
'--dangerously-skip-permissions',
|
|
145
|
+
'--output-format',
|
|
146
|
+
'stream-json',
|
|
147
|
+
'--verbose',
|
|
148
|
+
'--append-system-prompt',
|
|
149
|
+
systemPrompt,
|
|
150
|
+
'-p',
|
|
151
|
+
opts.sampleTask,
|
|
152
|
+
], {
|
|
153
|
+
env: process.env,
|
|
154
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
155
|
+
cwd,
|
|
156
|
+
});
|
|
157
|
+
let collected = '';
|
|
158
|
+
let tokensIn = 0;
|
|
159
|
+
let tokensOut = 0;
|
|
160
|
+
let truncated = false;
|
|
161
|
+
const killer = setTimeout(() => {
|
|
162
|
+
try {
|
|
163
|
+
child.kill('SIGTERM');
|
|
164
|
+
}
|
|
165
|
+
catch { /* ignore */ }
|
|
166
|
+
reject(new Error('test agent run timed out after 120s'));
|
|
167
|
+
}, 120_000);
|
|
168
|
+
const reader = (0, readline_1.createInterface)({ input: child.stdout, crlfDelay: Infinity });
|
|
169
|
+
reader.on('line', (line) => {
|
|
170
|
+
let parsed;
|
|
171
|
+
try {
|
|
172
|
+
parsed = JSON.parse(line);
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (!parsed || typeof parsed !== 'object')
|
|
178
|
+
return;
|
|
179
|
+
const p = parsed;
|
|
180
|
+
// Text blocks
|
|
181
|
+
const message = p.message;
|
|
182
|
+
const content = message?.content;
|
|
183
|
+
if (Array.isArray(content)) {
|
|
184
|
+
for (const block of content) {
|
|
185
|
+
if (block && typeof block === 'object' && block.type === 'text') {
|
|
186
|
+
const text = block.text;
|
|
187
|
+
if (typeof text === 'string')
|
|
188
|
+
collected += text;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// Usage in result / stop events
|
|
193
|
+
const usage = (p.usage ?? message?.usage);
|
|
194
|
+
if (usage) {
|
|
195
|
+
if (typeof usage.input_tokens === 'number')
|
|
196
|
+
tokensIn += usage.input_tokens;
|
|
197
|
+
if (typeof usage.output_tokens === 'number')
|
|
198
|
+
tokensOut += usage.output_tokens;
|
|
199
|
+
}
|
|
200
|
+
// Enforce token ceiling
|
|
201
|
+
if (tokensIn + tokensOut >= tokenCeiling && !truncated) {
|
|
202
|
+
truncated = true;
|
|
203
|
+
try {
|
|
204
|
+
child.kill('SIGTERM');
|
|
205
|
+
}
|
|
206
|
+
catch { /* ignore */ }
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
let stderr = '';
|
|
210
|
+
child.stderr.on('data', (chunk) => { stderr += chunk.toString(); });
|
|
211
|
+
child.on('error', (err) => {
|
|
212
|
+
clearTimeout(killer);
|
|
213
|
+
reject(err);
|
|
214
|
+
});
|
|
215
|
+
child.on('close', (code) => {
|
|
216
|
+
clearTimeout(killer);
|
|
217
|
+
const durationMs = Date.now() - started;
|
|
218
|
+
if (!truncated && code !== 0 && !collected) {
|
|
219
|
+
reject(new Error(`claude exited with code ${code}${stderr ? `: ${stderr.slice(-500)}` : ''}`));
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
resolve({
|
|
223
|
+
output: truncated
|
|
224
|
+
? collected + `\n\n[… output truncated after reaching ${tokenCeiling}-token ceiling]`
|
|
225
|
+
: collected,
|
|
226
|
+
tokens: tokensIn + tokensOut,
|
|
227
|
+
durationMs,
|
|
228
|
+
draftHash,
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createRefineSession = createRefineSession;
|
|
4
|
+
exports.getRefineSession = getRefineSession;
|
|
5
|
+
exports.listRefineSessionsForAgent = listRefineSessionsForAgent;
|
|
6
|
+
exports.updateRefineSession = updateRefineSession;
|
|
7
|
+
exports.deleteRefineSession = deleteRefineSession;
|
|
8
|
+
exports.pruneStaleRefineSessions = pruneStaleRefineSessions;
|
|
9
|
+
function rowToSession(row) {
|
|
10
|
+
let history = [];
|
|
11
|
+
try {
|
|
12
|
+
const parsed = JSON.parse(row.history_json);
|
|
13
|
+
if (Array.isArray(parsed))
|
|
14
|
+
history = parsed;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
// corrupted — start fresh
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
id: row.id,
|
|
21
|
+
agent_id: row.agent_id,
|
|
22
|
+
session_id: row.session_id,
|
|
23
|
+
base_version: row.base_version,
|
|
24
|
+
base_body_hash: row.base_body_hash,
|
|
25
|
+
draft_body: row.draft_body,
|
|
26
|
+
history,
|
|
27
|
+
phase: row.phase,
|
|
28
|
+
status: row.status,
|
|
29
|
+
auto_test: row.auto_test === 0 ? 0 : 1,
|
|
30
|
+
last_test_at: row.last_test_at,
|
|
31
|
+
last_test_hash: row.last_test_hash,
|
|
32
|
+
created_at: row.created_at,
|
|
33
|
+
updated_at: row.updated_at,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function createRefineSession(db, input) {
|
|
37
|
+
const now = Date.now();
|
|
38
|
+
db.prepare(`INSERT INTO agent_refine_sessions
|
|
39
|
+
(id, agent_id, session_id, base_version, base_body_hash, draft_body,
|
|
40
|
+
history_json, phase, status, auto_test, last_test_at, last_test_hash,
|
|
41
|
+
created_at, updated_at)
|
|
42
|
+
VALUES (?, ?, NULL, ?, ?, NULL, '[]', 'idle', 'idle', ?, NULL, NULL, ?, ?)`).run(input.id, input.agentId, input.baseVersion, input.baseBodyHash, input.autoTest ? 1 : 0, now, now);
|
|
43
|
+
return getRefineSession(db, input.id);
|
|
44
|
+
}
|
|
45
|
+
function getRefineSession(db, id) {
|
|
46
|
+
const row = db
|
|
47
|
+
.prepare(`SELECT * FROM agent_refine_sessions WHERE id = ?`)
|
|
48
|
+
.get(id);
|
|
49
|
+
if (!row)
|
|
50
|
+
return undefined;
|
|
51
|
+
return rowToSession(row);
|
|
52
|
+
}
|
|
53
|
+
function listRefineSessionsForAgent(db, agentId) {
|
|
54
|
+
const rows = db
|
|
55
|
+
.prepare(`SELECT * FROM agent_refine_sessions WHERE agent_id = ? ORDER BY updated_at DESC`)
|
|
56
|
+
.all(agentId);
|
|
57
|
+
return rows.map(rowToSession);
|
|
58
|
+
}
|
|
59
|
+
function updateRefineSession(db, id, patch) {
|
|
60
|
+
const fields = [];
|
|
61
|
+
const values = [];
|
|
62
|
+
if (Object.prototype.hasOwnProperty.call(patch, 'session_id')) {
|
|
63
|
+
fields.push('session_id = ?');
|
|
64
|
+
values.push(patch.session_id);
|
|
65
|
+
}
|
|
66
|
+
if (Object.prototype.hasOwnProperty.call(patch, 'draft_body')) {
|
|
67
|
+
fields.push('draft_body = ?');
|
|
68
|
+
values.push(patch.draft_body);
|
|
69
|
+
}
|
|
70
|
+
if (patch.history !== undefined) {
|
|
71
|
+
fields.push('history_json = ?');
|
|
72
|
+
values.push(JSON.stringify(patch.history));
|
|
73
|
+
}
|
|
74
|
+
if (patch.phase !== undefined) {
|
|
75
|
+
fields.push('phase = ?');
|
|
76
|
+
values.push(patch.phase);
|
|
77
|
+
}
|
|
78
|
+
if (patch.status !== undefined) {
|
|
79
|
+
fields.push('status = ?');
|
|
80
|
+
values.push(patch.status);
|
|
81
|
+
}
|
|
82
|
+
if (patch.auto_test !== undefined) {
|
|
83
|
+
fields.push('auto_test = ?');
|
|
84
|
+
values.push(patch.auto_test);
|
|
85
|
+
}
|
|
86
|
+
if (Object.prototype.hasOwnProperty.call(patch, 'last_test_at')) {
|
|
87
|
+
fields.push('last_test_at = ?');
|
|
88
|
+
values.push(patch.last_test_at);
|
|
89
|
+
}
|
|
90
|
+
if (Object.prototype.hasOwnProperty.call(patch, 'last_test_hash')) {
|
|
91
|
+
fields.push('last_test_hash = ?');
|
|
92
|
+
values.push(patch.last_test_hash);
|
|
93
|
+
}
|
|
94
|
+
if (fields.length === 0)
|
|
95
|
+
return;
|
|
96
|
+
fields.push('updated_at = ?');
|
|
97
|
+
values.push(Date.now());
|
|
98
|
+
values.push(id);
|
|
99
|
+
db.prepare(`UPDATE agent_refine_sessions SET ${fields.join(', ')} WHERE id = ?`).run(...values);
|
|
100
|
+
}
|
|
101
|
+
function deleteRefineSession(db, id) {
|
|
102
|
+
db.prepare(`DELETE FROM agent_refine_sessions WHERE id = ?`).run(id);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Startup retention prune (per design D11):
|
|
106
|
+
* - Delete cancelled/error sessions older than 24h
|
|
107
|
+
* - Mark stuck streaming sessions (>24h since update) as error, then delete
|
|
108
|
+
* - Retain ready/applied sessions indefinitely
|
|
109
|
+
*
|
|
110
|
+
* Returns the number of rows removed.
|
|
111
|
+
*/
|
|
112
|
+
function pruneStaleRefineSessions(db, now = Date.now()) {
|
|
113
|
+
const cutoff = now - 24 * 60 * 60 * 1000;
|
|
114
|
+
// Mark stuck streaming sessions as error first so any UI rehydrates with a
|
|
115
|
+
// sane state. Preserve `updated_at` so the DELETE below still matches them.
|
|
116
|
+
db.prepare(`UPDATE agent_refine_sessions
|
|
117
|
+
SET status = 'error'
|
|
118
|
+
WHERE status = 'streaming' AND updated_at < ?`).run(cutoff);
|
|
119
|
+
const res = db
|
|
120
|
+
.prepare(`DELETE FROM agent_refine_sessions
|
|
121
|
+
WHERE status IN ('cancelled', 'error', 'idle') AND updated_at < ?`)
|
|
122
|
+
.run(cutoff);
|
|
123
|
+
return Number(res.changes ?? 0);
|
|
124
|
+
}
|