@iowarp/clio-coder 0.3.9 → 0.4.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/CHANGELOG.md +53 -0
- package/NOTICE +33 -0
- package/README.md +7 -2
- package/dist/{acp-7LOELQFP.js → acp-G5WJBNCT.js} +13 -12
- package/dist/{agents-FIBG2SHA.js → agents-FMV2Q5G4.js} +39 -36
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-OI4LIH2I.js → auth-3IDSJEIK.js} +16 -15
- package/dist/{builtins-AD25UL3C.js → builtins-XCZWXSC7.js} +3 -3
- package/dist/{chunk-IFBNV6H6.js → chunk-2ANTL7MR.js} +3 -3
- package/dist/{chunk-56KB5IJP.js → chunk-2OQE55CK.js} +3 -3
- package/dist/{chunk-N3PBVRTZ.js → chunk-32KWKNSF.js} +9 -7
- package/dist/{chunk-VKBMFOYV.js → chunk-36EJLSQQ.js} +23 -27
- package/dist/{chunk-465CC7FK.js → chunk-3BT2XMV4.js} +16 -13
- package/dist/{chunk-NQ6UCCOD.js → chunk-3URVFKWK.js} +7 -7
- package/dist/{chunk-WIE7ZOSW.js → chunk-3XML7CDN.js} +2 -2
- package/dist/chunk-42FMPA75.js +101 -0
- package/dist/{chunk-A2NJGIB3.js → chunk-5LXZXPKX.js} +2 -2
- package/dist/{chunk-HAY4ZE2P.js → chunk-5PSMVOLM.js} +2 -2
- package/dist/{chunk-JEQQR47K.js → chunk-6DB53AJS.js} +168 -9
- package/dist/{chunk-KV2AOLDF.js → chunk-76ONBSIA.js} +5 -25
- package/dist/{chunk-FALJGAWU.js → chunk-7MCTRUCE.js} +2 -2
- package/dist/{chunk-5T7RBWN2.js → chunk-AB44T6BB.js} +5 -5
- package/dist/chunk-B7OBL7PK.js +1405 -0
- package/dist/{chunk-B5CSFE7B.js → chunk-BBVJUZHB.js} +2 -2
- package/dist/{chunk-EQ63NRB7.js → chunk-BBVYXMFO.js} +2 -2
- package/dist/{chunk-O6I4CIEU.js → chunk-BKFJHQCA.js} +6 -6
- package/dist/chunk-BKFM6EJV.js +462 -0
- package/dist/chunk-BMS5RKQY.js +27 -0
- package/dist/{chunk-MV2VUEJC.js → chunk-BPKCPIL7.js} +2 -2
- package/dist/{chunk-TEO2TLVN.js → chunk-BUMFYQFY.js} +964 -1120
- package/dist/{chunk-BNAZZHFG.js → chunk-BYP5D4HI.js} +1 -1
- package/dist/chunk-C2LTL2W6.js +2447 -0
- package/dist/{chunk-EBOC7MT3.js → chunk-CODPRO7Q.js} +8 -8
- package/dist/{chunk-HPCTNZM2.js → chunk-CTJ4RNAA.js} +4 -4
- package/dist/{chunk-ODFEOB4F.js → chunk-CY6FY24N.js} +26 -8
- package/dist/{chunk-MIX5N5AC.js → chunk-DQITNCXG.js} +394 -154
- package/dist/{chunk-S4COXYBG.js → chunk-DYJP44XW.js} +129 -106
- package/dist/{chunk-N5XKWMDW.js → chunk-F4CKPOEQ.js} +6 -6
- package/dist/{chunk-AMKHQW3C.js → chunk-FEFIFZTL.js} +3 -3
- package/dist/{chunk-MV3K5QF2.js → chunk-GCSMB2KY.js} +2 -2
- package/dist/{chunk-7C6RYZGQ.js → chunk-GKF55TAZ.js} +3 -3
- package/dist/{chunk-B5XRQOLB.js → chunk-GR5G2PVF.js} +9 -8
- package/dist/{chunk-XDOQXGFO.js → chunk-GXNLGKAB.js} +64 -8
- package/dist/chunk-HHV2GANA.js +88 -0
- package/dist/{chunk-774ILSRL.js → chunk-HI63TFOG.js} +4 -3
- package/dist/{chunk-R7LNVMCS.js → chunk-HJJTYUHX.js} +64 -72
- package/dist/{chunk-RKRLDWD3.js → chunk-HLW2MRKE.js} +1 -1
- package/dist/{chunk-CA42X6KT.js → chunk-HWHKMHUA.js} +6 -6
- package/dist/chunk-HZHHCK24.js +1631 -0
- package/dist/{chunk-HJB5IUKP.js → chunk-I5VEOC6I.js} +12 -12
- package/dist/chunk-IBEBSCYA.js +564 -0
- package/dist/chunk-IQ7KR472.js +362 -0
- package/dist/{chunk-4H6ULJ3H.js → chunk-J4W7KFM7.js} +921 -990
- package/dist/{chunk-4LJX2PUC.js → chunk-JDG2WCRO.js} +5 -5
- package/dist/{chunk-NZU6YDNV.js → chunk-K5C3NCBD.js} +4 -4
- package/dist/{chunk-CGKSTWHD.js → chunk-K6BSR66V.js} +2 -1
- package/dist/{chunk-NNNWO6F2.js → chunk-KFZI4NIL.js} +93 -3
- package/dist/chunk-KMVISBZR.js +132 -0
- package/dist/{chunk-BVDVID7E.js → chunk-LDQ2ZF2M.js} +2 -2
- package/dist/{chunk-OEDBCISO.js → chunk-LQ3DZAMX.js} +2 -2
- package/dist/{chunk-PZ4I4JE2.js → chunk-LY4S7GJC.js} +124 -116
- package/dist/{chunk-3DUR4WUA.js → chunk-MLKNTWH2.js} +19 -19
- package/dist/chunk-NEKRRTYW.js +56 -0
- package/dist/{chunk-5DQRIYDZ.js → chunk-NHCZP4K7.js} +2 -2
- package/dist/chunk-NHLBIGRH.js +1506 -0
- package/dist/chunk-NQQH3YT7.js +302 -0
- package/dist/chunk-NYS75XW5.js +15 -0
- package/dist/{chunk-TD7UE2L5.js → chunk-O4XIVISU.js} +7 -7
- package/dist/{chunk-WXCJ7VME.js → chunk-O6TL7WWY.js} +6 -6
- package/dist/chunk-OQBA45DZ.js +97 -0
- package/dist/{chunk-LU7P4LHA.js → chunk-P3FOHJT4.js} +2 -2
- package/dist/chunk-PMZCIOCJ.js +25 -0
- package/dist/{chunk-I4HZDVNP.js → chunk-PQEFIJ36.js} +2 -2
- package/dist/{chunk-HKO36JWF.js → chunk-QBJA7R7N.js} +31 -3
- package/dist/chunk-QDC3K2U3.js +262 -0
- package/dist/{chunk-5HFBWUMU.js → chunk-QLFS5GO2.js} +4 -4
- package/dist/{chunk-GAYUJ7LE.js → chunk-QLL7ILRG.js} +41 -32
- package/dist/{chunk-LXPJXFM5.js → chunk-QREDIESB.js} +7 -7
- package/dist/chunk-QSNYB6ZV.js +195 -0
- package/dist/chunk-R346GLFC.js +31 -0
- package/dist/{chunk-VPTUJU4P.js → chunk-SJ5ZKQ4S.js} +2 -2
- package/dist/{chunk-QQ7EKM72.js → chunk-SP2RXXYO.js} +6 -54
- package/dist/chunk-SUCTJL45.js +45 -0
- package/dist/{chunk-5QKCQQ3E.js → chunk-SUW5DORT.js} +2 -2
- package/dist/chunk-T56WDKA5.js +183 -0
- package/dist/chunk-TVHHYFHE.js +255 -0
- package/dist/{chunk-IHKBWSXF.js → chunk-TZ3SGWZZ.js} +2 -2
- package/dist/{chunk-ECUO3KDP.js → chunk-U77AMWDL.js} +47 -7
- package/dist/{chunk-3UUY7R3Z.js → chunk-ULC6OTWO.js} +7 -7
- package/dist/{chunk-AEYBF3TB.js → chunk-UM7N4G5A.js} +2 -2
- package/dist/{chunk-3V5AYSEQ.js → chunk-UXMFQ54G.js} +43 -36
- package/dist/{chunk-YW7UVM5V.js → chunk-V5DHCITQ.js} +34 -1
- package/dist/{chunk-47CMYGET.js → chunk-VAZSBTKF.js} +2 -2
- package/dist/{chunk-3MRC2YSQ.js → chunk-VEO4AP2K.js} +2 -2
- package/dist/{chunk-PNY46YEY.js → chunk-VFA6GDY5.js} +47 -3
- package/dist/chunk-VO67MWHC.js +75 -0
- package/dist/{chunk-XK56QHLX.js → chunk-VPKWYKEY.js} +19 -5
- package/dist/{chunk-AD7Y7STJ.js → chunk-VYMXRQI6.js} +36 -22
- package/dist/{chunk-T3Z6VAAF.js → chunk-W5VSYASO.js} +19 -17
- package/dist/{chunk-5PVQ4SRS.js → chunk-WZR7K7ZX.js} +7 -123
- package/dist/{chunk-DG4M6ZUE.js → chunk-X3YGUTOB.js} +4 -4
- package/dist/chunk-X75E3D2N.js +686 -0
- package/dist/{chunk-P3JGPQFL.js → chunk-YDFRH54B.js} +4 -4
- package/dist/chunk-YJX4SHTD.js +40 -0
- package/dist/{chunk-ZI647VB5.js → chunk-YPI3QQCF.js} +2 -2
- package/dist/{chunk-YSEHGPCT.js → chunk-Z2RR6MAK.js} +3 -3
- package/dist/{chunk-HCBCAYZU.js → chunk-Z4TXYIEG.js} +5 -5
- package/dist/cli/index.js +47 -36
- package/dist/{clio-LT5V7SSZ.js → clio-2JXHBBY5.js} +7 -7
- package/dist/{code-nav-LMW275PA.js → code-nav-3YYRMYNF.js} +6 -6
- package/dist/{compile-cache-CVJMMODC.js → compile-cache-7FPE6PS3.js} +3 -3
- package/dist/{components-ZFA3SAER.js → components-RYZV4JGP.js} +5 -5
- package/dist/{config-RXS5T3JT.js → config-QZPCMYSO.js} +58 -50
- package/dist/{configure-2WYWSCSD.js → configure-TEGEBYCA.js} +21 -19
- package/dist/{context-I3BTOTCS.js → context-AV7OEZ4D.js} +11 -11
- package/dist/{context-MVOORGMF.js → context-E6H5RNMC.js} +49 -41
- package/dist/{context-PALKKQYL.js → context-GSXUE4CT.js} +25 -23
- package/dist/{context-clear-N2WOYZ2K.js → context-clear-SHIBYK6T.js} +48 -40
- package/dist/{context-working-set-MIEVECVZ.js → context-working-set-5ZGKPGZQ.js} +11 -10
- package/dist/{dispatch-runner-VVA4SRRH.js → dispatch-runner-EFMJT4LD.js} +88 -60
- package/dist/{docs-7LQ23DLM.js → docs-23KQS3XK.js} +5 -5
- package/dist/doctor-QOA5FNY5.js +313 -0
- package/dist/{eval-IJ5VEZDJ.js → eval-TFBYQH4H.js} +66 -64
- package/dist/eval-inventory-SXH7PDKX.js +316 -0
- package/dist/{evidence-L5APPXNV.js → evidence-ERGESKGN.js} +198 -44
- package/dist/{evolve-RGNKFJ52.js → evolve-VDXTSYCJ.js} +46 -38
- package/dist/{extensions-7WYWUX5A.js → extensions-7BGBHN57.js} +6 -6
- package/dist/{fleet-6CNVBZZP.js → fleet-2RRVDF2V.js} +189 -101
- package/dist/{fleet-commands-L2SXSYEI.js → fleet-commands-VJ726XIA.js} +10 -10
- package/dist/fleet-decisions-EPAPM3XJ.js +157 -0
- package/dist/{fleet-graph-2J3OOIPO.js → fleet-graph-JF5QOATM.js} +16 -15
- package/dist/fleet-inspect-VLY4S7QM.js +442 -0
- package/dist/{fleet-preflight-CZRJ4JP5.js → fleet-preflight-AIZUEJOY.js} +6 -5
- package/dist/{fleet-validate-C5RI6DP7.js → fleet-validate-AJRPDMDV.js} +20 -18
- package/dist/fleet-verify-JFEL2L3H.js +175 -0
- package/dist/fleet-view-ZCON35AG.js +102 -0
- package/dist/{init-VBN2ACVA.js → init-DN2WWLFE.js} +64 -56
- package/dist/install-XGLBQY5E.js +13 -0
- package/dist/interop-OZBKXAYL.js +114 -0
- package/dist/{library-JHGUMLY2.js → library-YWZG7IMW.js} +19 -18
- package/dist/{memory-K4OQIYWG.js → memory-I4C4HMLW.js} +48 -40
- package/dist/{models-2NCZUWDD.js → models-CEYXJBO6.js} +30 -28
- package/dist/{monitor-MMVTJABD.js → monitor-NZ6GCI3P.js} +53 -47
- package/dist/{orchestrator-ZKBPCHW6.js → orchestrator-GCGQ4N5I.js} +6311 -7145
- package/dist/panes-HMABYVO4.js +58 -0
- package/dist/panes-KY6W3V2E.js +103 -0
- package/dist/{paths-DBXMZMDU.js → paths-II4K7DNR.js} +5 -5
- package/dist/{reset-DD5JGOY3.js → reset-DQ6FGCSH.js} +13 -11
- package/dist/resources-BB3MVJMD.js +111 -0
- package/dist/{run-QEGNX7FL.js → run-H2GQDUER.js} +113 -82
- package/dist/{share-JKD3BQMW.js → share-GTJN6A5O.js} +18 -17
- package/dist/{skills-LMQIKDOZ.js → skills-L55TEW6R.js} +31 -24
- package/dist/{skills-eval-I7X2774U.js → skills-eval-XVXPH2JI.js} +61 -52
- package/dist/skills-inventory-S4MXPJFV.js +126 -0
- package/dist/slash-commands-ZSGASKJC.js +77 -0
- package/dist/{steer-CF5TDANS.js → steer-RZGSCY4R.js} +3 -3
- package/dist/{support-I7LOJLIF.js → support-PKEUNNQL.js} +4 -4
- package/dist/{targets-RUSR6B5Z.js → targets-NCPZ644J.js} +31 -29
- package/dist/{terminal-lease-QYVORFR4.js → terminal-lease-44SV3YCN.js} +4 -4
- package/dist/tools-DAF3DI3C.js +27 -0
- package/dist/{trace-ODOQIVIW.js → trace-FYVW2MQA.js} +149 -7
- package/dist/tui-primitives-2AKXQNZK.js +13 -0
- package/dist/{uninstall-ZJF5H5ZN.js → uninstall-DW2PNOIC.js} +5 -5
- package/dist/{upgrade-XANW3FXB.js → upgrade-3XPP6OQL.js} +26 -24
- package/dist/{usage-4H7ZRXQT.js → usage-3NLHGTU2.js} +57 -49
- package/dist/{verifiers-UZXNBZEB.js → verifiers-SSQONKRT.js} +171 -12
- package/dist/{verify-BVKWTNDL.js → verify-3U6J7FZI.js} +9 -9
- package/dist/{web-fetch-2YHJ3KTG.js → web-fetch-S7RR6GZ7.js} +3 -3
- package/dist/{wiki-generate-MY7WV2QI.js → wiki-generate-CEHYGPGQ.js} +67 -59
- package/dist/with-panes-MKB46MPQ.js +782 -0
- package/dist/worker/entry.js +101 -66
- package/docs/README.md +3 -2
- package/docs/acp.md +24 -3
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +2 -2
- package/docs/artifact-versions.md +4 -1
- package/docs/built-in-agents.md +1 -1
- package/docs/capacity-and-scheduling.md +43 -5
- package/docs/commands-and-modes.md +31 -2
- package/docs/configuration-and-targets.md +40 -10
- package/docs/context-engine.md +1 -1
- package/docs/development-pipeline.md +19 -0
- package/docs/dispatch-typed-intent.md +385 -0
- package/docs/documentation-coverage.md +2 -2
- package/docs/documentation-guide.md +1 -1
- package/docs/environment-variables.md +1 -0
- package/docs/eval-runner.md +1 -1
- package/docs/evals-internal.md +1 -1
- package/docs/evidence-and-memory.md +2 -2
- package/docs/evolution.md +1 -1
- package/docs/exit-codes-and-output.md +1 -1
- package/docs/extensions-and-sharing.md +25 -1
- package/docs/fleet-dispatch.md +22 -1
- package/docs/installation-and-lifecycle.md +15 -2
- package/docs/middleware-and-components.md +1 -1
- package/docs/model-catalog.md +10 -1
- package/docs/observability.md +3 -3
- package/docs/proactive-memory.md +1 -1
- package/docs/prompt-envelope-and-tools.md +1 -1
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/safety-model.md +2 -2
- package/docs/scientific-validation.md +3 -3
- package/docs/session-lifecycle.md +1 -1
- package/docs/skills-marketplace.md +1 -1
- package/docs/tool-usage.md +18 -8
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +1 -1
- package/docs/tui-design.md +1 -1
- package/docs/worker-dispatch-mechanics.md +1 -1
- package/package.json +4 -2
- package/src/cli/acp.ts +6 -2
- package/src/cli/argv.ts +25 -0
- package/src/cli/configure.ts +23 -21
- package/src/cli/doctor-panes.ts +124 -0
- package/src/cli/doctor-toolchain.ts +57 -0
- package/src/cli/doctor.ts +20 -1
- package/src/cli/eval-inventory.ts +436 -0
- package/src/cli/eval.ts +13 -0
- package/src/cli/evidence-detail.ts +88 -0
- package/src/cli/evidence-inventory.ts +183 -0
- package/src/cli/evidence.ts +30 -5
- package/src/cli/fleet-decisions.ts +69 -0
- package/src/cli/fleet-inspect.ts +334 -0
- package/src/cli/fleet-verify.ts +133 -0
- package/src/cli/fleet-view.ts +810 -0
- package/src/cli/fleet.ts +148 -37
- package/src/cli/index.ts +14 -2
- package/src/cli/interop-inspect.ts +128 -0
- package/src/cli/interop.ts +34 -0
- package/src/cli/panes.ts +35 -0
- package/src/cli/reset.ts +5 -2
- package/src/cli/run.ts +58 -0
- package/src/cli/skills-inventory.ts +185 -0
- package/src/cli/skills.ts +16 -13
- package/src/cli/tools.ts +321 -0
- package/src/cli/trace-inspect.ts +252 -0
- package/src/cli/trace.ts +23 -2
- package/src/cli/verifiers-inspect.ts +347 -0
- package/src/cli/verifiers.ts +9 -0
- package/src/core/bus-events.ts +4 -0
- package/src/core/config.ts +49 -0
- package/src/core/defaults.ts +93 -7
- package/src/core/response-schema.ts +44 -4
- package/src/core/theme-token-hex.ts +43 -0
- package/src/core/tool-names.ts +2 -1
- package/src/core/xdg.ts +1 -1
- package/src/domains/agents/fleets/build-review.md +0 -3
- package/src/domains/agents/fleets/build-test.md +0 -3
- package/src/domains/agents/result-contract-filesystem.ts +32 -0
- package/src/domains/agents/result-contract.ts +164 -35
- package/src/domains/config/classify.ts +3 -0
- package/src/domains/dispatch/admission-error.ts +9 -0
- package/src/domains/dispatch/admission.ts +22 -21
- package/src/domains/dispatch/capacity-lease.ts +21 -1
- package/src/domains/dispatch/council-topology.ts +398 -0
- package/src/domains/dispatch/extension.ts +222 -50
- package/src/domains/dispatch/fleet-node-prompt.ts +62 -0
- package/src/domains/dispatch/fleet-plan.ts +7 -2
- package/src/domains/dispatch/fleet-run.ts +64 -1
- package/src/domains/dispatch/gate-decisions.ts +11 -1
- package/src/domains/dispatch/gate-role-prompts.ts +9 -0
- package/src/domains/dispatch/gate-topology.ts +289 -0
- package/src/domains/dispatch/index.ts +19 -0
- package/src/domains/dispatch/intent-compatibility.ts +330 -0
- package/src/domains/dispatch/intent.ts +85 -1
- package/src/domains/dispatch/reservation-store.ts +24 -4
- package/src/domains/dispatch/run-event-journal-bridge.ts +149 -0
- package/src/domains/dispatch/run-event-journal.ts +598 -0
- package/src/domains/dispatch/state.ts +45 -1
- package/src/domains/dispatch/types.ts +13 -0
- package/src/domains/dispatch/validation.ts +33 -8
- package/src/domains/eval/compare/compare.ts +3 -16
- package/src/domains/eval/inventory.ts +113 -0
- package/src/domains/eval/schema/serving.ts +31 -0
- package/src/domains/evidence/store.ts +6 -0
- package/src/domains/memory/index.ts +0 -1
- package/src/domains/memory/task-memory-policy.ts +6 -10
- package/src/domains/memory/task-memory-telemetry.ts +1 -0
- package/src/domains/middleware/memory-intervention.ts +33 -21
- package/src/domains/mux/contract.ts +434 -0
- package/src/domains/mux/detect.ts +158 -0
- package/src/domains/mux/extension.ts +47 -0
- package/src/domains/mux/index.ts +96 -0
- package/src/domains/mux/manifest.ts +6 -0
- package/src/domains/mux/operations.ts +164 -0
- package/src/domains/mux/pane-registry.ts +90 -0
- package/src/domains/mux/protocol.ts +49 -0
- package/src/domains/mux/socket-client.ts +816 -0
- package/src/domains/mux/types.ts +222 -0
- package/src/domains/mux/viewer-command.ts +59 -0
- package/src/domains/mux/yazi/assets/init.lua +2 -0
- package/src/domains/mux/yazi/assets/plugins/git.yazi/LICENSE +21 -0
- package/src/domains/mux/yazi/assets/plugins/git.yazi/README.md +78 -0
- package/src/domains/mux/yazi/assets/plugins/git.yazi/main.lua +255 -0
- package/src/domains/mux/yazi/assets/plugins/git.yazi/types.lua +12 -0
- package/src/domains/mux/yazi/assets/yazi.toml +17 -0
- package/src/domains/mux/yazi/event-stream.ts +180 -0
- package/src/domains/mux/yazi/profile.ts +299 -0
- package/src/domains/mux/yazi/session.ts +228 -0
- package/src/domains/mux/yazi/theme.ts +30 -0
- package/src/domains/observability/cost.ts +21 -0
- package/src/domains/observability/index.ts +2 -0
- package/src/domains/observability/trace-store.ts +42 -0
- package/src/domains/providers/endpoint-capacity.ts +154 -22
- package/src/domains/providers/endpoint-slots-store.ts +189 -0
- package/src/domains/providers/extension.ts +20 -3
- package/src/domains/providers/index.ts +22 -0
- package/src/domains/providers/model-runtime-capabilities.ts +32 -0
- package/src/domains/providers/runtimes/boot-manifest.ts +1 -0
- package/src/domains/providers/runtimes/builtins.ts +2 -0
- package/src/domains/providers/runtimes/protocol/litellm.ts +375 -0
- package/src/domains/providers/support.ts +1 -0
- package/src/domains/providers/target-model-cache.ts +124 -0
- package/src/domains/resources/index.ts +3 -0
- package/src/domains/resources/skills/loader.ts +33 -0
- package/src/domains/safety/action-classifier.ts +6 -0
- package/src/domains/toolchain/archive.ts +175 -0
- package/src/domains/toolchain/contract.ts +28 -0
- package/src/domains/toolchain/extension.ts +47 -0
- package/src/domains/toolchain/index.ts +39 -0
- package/src/domains/toolchain/install.ts +327 -0
- package/src/domains/toolchain/manifest.ts +8 -0
- package/src/domains/toolchain/paths.ts +34 -0
- package/src/domains/toolchain/registry.ts +265 -0
- package/src/domains/toolchain/remove.ts +218 -0
- package/src/domains/toolchain/resolve.ts +182 -0
- package/src/domains/toolchain/types.ts +113 -0
- package/src/domains/toolchain/version.ts +88 -0
- package/src/engine/acp/server.ts +413 -70
- package/src/engine/acp/types.ts +19 -1
- package/src/engine/claude/sdk-module.ts +98 -0
- package/src/engine/claude/sdk-runtime.ts +19 -11
- package/src/engine/tui-primitives.ts +21 -0
- package/src/engine/tui.ts +1 -0
- package/src/engine/worker-runtime.ts +2 -13
- package/src/entry/boot-options.ts +2 -0
- package/src/entry/orchestrator.ts +103 -5
- package/src/entry/panes-activation.ts +31 -0
- package/src/entry/with-panes.ts +20 -0
- package/src/interactive/context-overlay.ts +1 -0
- package/src/interactive/cost-overlay.ts +2 -0
- package/src/interactive/council-grid.ts +1 -3
- package/src/interactive/council.ts +11 -0
- package/src/interactive/dispatch-board.ts +26 -16
- package/src/interactive/interactive-application.ts +153 -6
- package/src/interactive/interactive-input-runtime.ts +7 -0
- package/src/interactive/interactive-slash-runtime.ts +36 -6
- package/src/interactive/memory-overlay.ts +1 -0
- package/src/interactive/modal-marker.ts +170 -0
- package/src/interactive/mux-bridge.ts +214 -0
- package/src/interactive/overlay-frame.ts +58 -2
- package/src/interactive/overlay-general-openers.ts +1 -0
- package/src/interactive/overlay-key-routing.ts +14 -3
- package/src/interactive/overlay-lifecycle.ts +19 -3
- package/src/interactive/overlay-model-selectors.ts +40 -3
- package/src/interactive/overlay-permission-lifecycle.ts +90 -22
- package/src/interactive/overlay-transitions.ts +18 -4
- package/src/interactive/overlays/agents.ts +1 -0
- package/src/interactive/overlays/ask-user.ts +136 -30
- package/src/interactive/overlays/auth-dialog.ts +1 -0
- package/src/interactive/overlays/context-reset.ts +1 -0
- package/src/interactive/overlays/cwd-fallback.ts +1 -0
- package/src/interactive/overlays/decisions.ts +11 -11
- package/src/interactive/overlays/extensions.ts +1 -0
- package/src/interactive/overlays/fleet-run-approval.ts +1 -0
- package/src/interactive/overlays/handoff-review.ts +1 -0
- package/src/interactive/overlays/help-reference.ts +2 -0
- package/src/interactive/overlays/interop.ts +1 -0
- package/src/interactive/overlays/library-install-confirm.ts +1 -0
- package/src/interactive/overlays/library-tabs.ts +28 -0
- package/src/interactive/overlays/list-overlay.ts +10 -1
- package/src/interactive/overlays/message-picker.ts +1 -0
- package/src/interactive/overlays/model-scope.ts +86 -0
- package/src/interactive/overlays/model-selector.ts +1 -0
- package/src/interactive/overlays/prompts.ts +1 -0
- package/src/interactive/overlays/session-selector.ts +1 -0
- package/src/interactive/overlays/settings-sections.ts +30 -0
- package/src/interactive/overlays/settings.ts +579 -44
- package/src/interactive/overlays/side-question.ts +1 -0
- package/src/interactive/overlays/skills-hub.ts +3 -11
- package/src/interactive/overlays/tree-selector.ts +1 -0
- package/src/interactive/pane-policy.ts +46 -0
- package/src/interactive/panes-runtime.ts +292 -0
- package/src/interactive/renderers/compaction-summary.ts +29 -0
- package/src/interactive/renderers/worker-entry.ts +122 -14
- package/src/interactive/slash-commands.ts +251 -15
- package/src/interactive/tasks-overlay.ts +1 -0
- package/src/interactive/theme/tokens.ts +3 -14
- package/src/interactive/turn-context.ts +47 -2
- package/src/interactive/view/artifacts.ts +109 -1
- package/src/interactive/view/view-overlay.ts +1 -0
- package/src/interactive/watch-pane.ts +152 -0
- package/src/interactive/worker-progress.ts +7 -1
- package/src/interactive/worker-receipts.ts +19 -1
- package/src/interactive/worker-stream.ts +5 -0
- package/src/interactive/yazi-bridge.ts +444 -0
- package/src/tools/bootstrap.ts +26 -2
- package/src/tools/builtin-tool-catalog.ts +15 -0
- package/src/tools/compete-worktrees.ts +83 -2
- package/src/tools/core-bootstrap.ts +2 -1
- package/src/tools/dispatch-admission.ts +14 -3
- package/src/tools/dispatch-arguments.ts +20 -20
- package/src/tools/dispatch-run-events.ts +134 -19
- package/src/tools/dispatch-runner.ts +29 -7
- package/src/tools/dispatch-types.ts +15 -3
- package/src/tools/dispatch.ts +1 -1
- package/src/tools/executables.ts +17 -14
- package/src/tools/observation.ts +54 -4
- package/src/tools/panes-surface.ts +38 -0
- package/src/tools/panes.ts +112 -0
- package/src/tools/policy.ts +10 -1
- package/src/tools/presentation.ts +1 -0
- package/dist/chunk-D73KXYPF.js +0 -37
- package/dist/chunk-MLOK6ZOS.js +0 -2888
- package/dist/chunk-YKOFT37S.js +0 -279
- package/dist/doctor-TWBWFK5V.js +0 -165
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import type { PinnedTool, ToolPlatform } from "./types.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The pinned table.
|
|
5
|
+
*
|
|
6
|
+
* Every checksum here was computed from the asset as downloaded from the URL
|
|
7
|
+
* beside it, not copied from a release note. Where upstream publishes its own
|
|
8
|
+
* checksum file the two were compared and agree; where upstream publishes no
|
|
9
|
+
* checksums, only the platform whose asset was actually fetched and hashed is
|
|
10
|
+
* listed, because a hash nobody verified is worse than a missing platform: it
|
|
11
|
+
* turns an honest "no asset for your platform" into a failed install that
|
|
12
|
+
* looks like tampering.
|
|
13
|
+
*
|
|
14
|
+
* Bumping a version means re-downloading each listed asset and re-computing
|
|
15
|
+
* each hash. The registry-shape contract test refuses an entry that carries a
|
|
16
|
+
* platform without a checksum. A bump also supersedes whatever version the
|
|
17
|
+
* machine already has, and the installer prunes it, so a tool holds exactly one
|
|
18
|
+
* version directory.
|
|
19
|
+
*
|
|
20
|
+
* `minimumVersion` is the floor a copy already on PATH has to clear. A floor
|
|
21
|
+
* moves on evidence and on nothing else: name the older release, say which of
|
|
22
|
+
* its surfaces you exercised and how, and put that in the comment beside the
|
|
23
|
+
* number. A rejection that felt noisy is not evidence. Where no such
|
|
24
|
+
* measurement exists the floor sits at the pin, because a release Clio was
|
|
25
|
+
* never run against is not something to discover through a failure that reads
|
|
26
|
+
* as a bug in the feature.
|
|
27
|
+
*
|
|
28
|
+
* The cost of a floor lands on an operator whose own copy is a release or two
|
|
29
|
+
* behind, so `describeResolution` in `resolve.ts` is required to name what it
|
|
30
|
+
* found, the floor it missed, and the command that fixes it.
|
|
31
|
+
*/
|
|
32
|
+
export const PINNED_TOOLS: ReadonlyArray<PinnedTool> = [
|
|
33
|
+
{
|
|
34
|
+
id: "herdr",
|
|
35
|
+
version: "0.8.2",
|
|
36
|
+
summary: "terminal multiplexer with an agent-aware socket API; powers Clio panes",
|
|
37
|
+
homepage: "https://herdr.dev",
|
|
38
|
+
license: "Apache-2.0",
|
|
39
|
+
binaries: ["herdr"],
|
|
40
|
+
primaryBinary: "herdr",
|
|
41
|
+
// Lowered from the pin on evidence, which is the bar this file sets for
|
|
42
|
+
// moving a floor. `herdr api schema --json` was read from 0.7.5
|
|
43
|
+
// (protocol 17) and 0.8.2 (protocol 20): every method the mux domain
|
|
44
|
+
// sends exists in both, and the only two 0.8.2 adds, `workspace.move_block`
|
|
45
|
+
// and `workspace.reordered`, are ones Clio never sends. The two methods
|
|
46
|
+
// that are not universal are already gated at runtime by protocol number
|
|
47
|
+
// in `src/domains/mux/protocol.ts`, whose own floor is 17, so an operator's
|
|
48
|
+
// 0.7.5 takes the documented fallback rather than failing. 0.7.5 is the
|
|
49
|
+
// oldest release actually checked, not the oldest that might work.
|
|
50
|
+
minimumVersion: "0.7.5",
|
|
51
|
+
versionArgs: ["--version"],
|
|
52
|
+
downloads: {
|
|
53
|
+
"linux-x64": {
|
|
54
|
+
url: "https://github.com/herdrdev/herdr/releases/download/v0.8.2/herdr-linux-x86_64",
|
|
55
|
+
sha256: "976150a14d490c94b243ea2e1a7eb2dfb67f12e36b182db90936f6728e6aecf4",
|
|
56
|
+
archive: "raw",
|
|
57
|
+
binaryMembers: { herdr: "" },
|
|
58
|
+
documentMembers: [],
|
|
59
|
+
},
|
|
60
|
+
"linux-arm64": {
|
|
61
|
+
url: "https://github.com/herdrdev/herdr/releases/download/v0.8.2/herdr-linux-aarch64",
|
|
62
|
+
sha256: "f55610658e1c2e0d2aaef730b4b2ab885f7f8ba00285ab372bfb14f2e3d5b40d",
|
|
63
|
+
archive: "raw",
|
|
64
|
+
binaryMembers: { herdr: "" },
|
|
65
|
+
documentMembers: [],
|
|
66
|
+
},
|
|
67
|
+
"darwin-x64": {
|
|
68
|
+
url: "https://github.com/herdrdev/herdr/releases/download/v0.8.2/herdr-macos-x86_64",
|
|
69
|
+
sha256: "ab50262c8190cd7aa9056d249d255c08c328c3e8716de9cfa29db4f131b8e2c1",
|
|
70
|
+
archive: "raw",
|
|
71
|
+
binaryMembers: { herdr: "" },
|
|
72
|
+
documentMembers: [],
|
|
73
|
+
},
|
|
74
|
+
"darwin-arm64": {
|
|
75
|
+
url: "https://github.com/herdrdev/herdr/releases/download/v0.8.2/herdr-macos-aarch64",
|
|
76
|
+
sha256: "a5d4f4d504d8b309c91f811050559300faba31258425f53c50852fc96f6ae574",
|
|
77
|
+
archive: "raw",
|
|
78
|
+
binaryMembers: { herdr: "" },
|
|
79
|
+
documentMembers: [],
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
// No `win32-x64` entry, and not for want of an asset. herdr publishes
|
|
83
|
+
// `herdr-windows-x86_64.zip` for this tag and it was downloaded and read:
|
|
84
|
+
// alongside `herdr.exe` it carries a ConPTY runtime, `conpty/conpty.dll`
|
|
85
|
+
// and `conpty/x64/OpenConsole.exe`, that has to sit in a subdirectory
|
|
86
|
+
// beside the executable. The installer places every declared member flat
|
|
87
|
+
// under its basename, so declaring this asset would install a `herdr.exe`
|
|
88
|
+
// with its runtime scattered next to it, which is a broken install that
|
|
89
|
+
// checksums and unpacks cleanly. Windows herdr waits on the installer
|
|
90
|
+
// learning to preserve a member's relative path.
|
|
91
|
+
//
|
|
92
|
+
// The release asset is a bare executable, so the Apache-2.0 text comes
|
|
93
|
+
// from the repository at the pinned tag.
|
|
94
|
+
documents: [
|
|
95
|
+
{
|
|
96
|
+
name: "LICENSE",
|
|
97
|
+
url: "https://raw.githubusercontent.com/herdrdev/herdr/v0.8.2/LICENSE",
|
|
98
|
+
sha256: "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4",
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
id: "yazi",
|
|
104
|
+
version: "26.8.15",
|
|
105
|
+
summary: "terminal file manager; the file-picker pane preset",
|
|
106
|
+
homepage: "https://yazi-rs.github.io",
|
|
107
|
+
license: "MIT",
|
|
108
|
+
binaries: ["yazi", "ya"],
|
|
109
|
+
primaryBinary: "yazi",
|
|
110
|
+
// Stays at the pin, now for a measured reason rather than caution. On
|
|
111
|
+
// 26.1.22 the one command Clio sends, `ya emit-to <receiver> cd <path>`,
|
|
112
|
+
// has the same signature it has at the pin. What was never exercised
|
|
113
|
+
// there is the rest of the surface: the managed profile Clio generates
|
|
114
|
+
// (`yazi.toml`, `keymap.toml`, `init.lua`) and the DDS payload shapes a
|
|
115
|
+
// pick comes back in. A profile schema mismatch degrades quietly into a
|
|
116
|
+
// file manager that opens and does the wrong thing, which is worse than
|
|
117
|
+
// vendoring a second copy. Lower this once a pick round trip has been
|
|
118
|
+
// driven end to end on the older release.
|
|
119
|
+
minimumVersion: "26.8.15",
|
|
120
|
+
versionArgs: ["--version"],
|
|
121
|
+
downloads: {
|
|
122
|
+
"linux-x64": {
|
|
123
|
+
url: "https://github.com/sxyazi/yazi/releases/download/v26.8.15/yazi-x86_64-unknown-linux-gnu.zip",
|
|
124
|
+
sha256: "cc67eb7991550c2f9407cda52d3f5af0937627aa6884e7de99a04fcf059807e0",
|
|
125
|
+
archive: "zip",
|
|
126
|
+
binaryMembers: {
|
|
127
|
+
yazi: "yazi-x86_64-unknown-linux-gnu/yazi",
|
|
128
|
+
ya: "yazi-x86_64-unknown-linux-gnu/ya",
|
|
129
|
+
},
|
|
130
|
+
documentMembers: ["yazi-x86_64-unknown-linux-gnu/LICENSE"],
|
|
131
|
+
},
|
|
132
|
+
"linux-arm64": {
|
|
133
|
+
url: "https://github.com/sxyazi/yazi/releases/download/v26.8.15/yazi-aarch64-unknown-linux-gnu.zip",
|
|
134
|
+
sha256: "f5a85771f06bb0e8c488136ae0aedaec8d341a7cee995549df391d7d852fe8d1",
|
|
135
|
+
archive: "zip",
|
|
136
|
+
binaryMembers: {
|
|
137
|
+
yazi: "yazi-aarch64-unknown-linux-gnu/yazi",
|
|
138
|
+
ya: "yazi-aarch64-unknown-linux-gnu/ya",
|
|
139
|
+
},
|
|
140
|
+
documentMembers: ["yazi-aarch64-unknown-linux-gnu/LICENSE"],
|
|
141
|
+
},
|
|
142
|
+
"darwin-x64": {
|
|
143
|
+
url: "https://github.com/sxyazi/yazi/releases/download/v26.8.15/yazi-x86_64-apple-darwin.zip",
|
|
144
|
+
sha256: "70bb2bcf57d8af862a54e2d12f2fddceefb9aa4ba3783e9a4dcbf2a8e64aacb3",
|
|
145
|
+
archive: "zip",
|
|
146
|
+
binaryMembers: {
|
|
147
|
+
yazi: "yazi-x86_64-apple-darwin/yazi",
|
|
148
|
+
ya: "yazi-x86_64-apple-darwin/ya",
|
|
149
|
+
},
|
|
150
|
+
documentMembers: ["yazi-x86_64-apple-darwin/LICENSE"],
|
|
151
|
+
},
|
|
152
|
+
"darwin-arm64": {
|
|
153
|
+
url: "https://github.com/sxyazi/yazi/releases/download/v26.8.15/yazi-aarch64-apple-darwin.zip",
|
|
154
|
+
sha256: "3f54907ea08abe96506f4b22239340ed8923a6aeaeae78f33d59bce57daca4cd",
|
|
155
|
+
archive: "zip",
|
|
156
|
+
binaryMembers: {
|
|
157
|
+
yazi: "yazi-aarch64-apple-darwin/yazi",
|
|
158
|
+
ya: "yazi-aarch64-apple-darwin/ya",
|
|
159
|
+
},
|
|
160
|
+
documentMembers: ["yazi-aarch64-apple-darwin/LICENSE"],
|
|
161
|
+
},
|
|
162
|
+
// Same shape as the other three: two executables and a LICENSE at
|
|
163
|
+
// known member paths, so the installer needs nothing new to place it.
|
|
164
|
+
"win32-x64": {
|
|
165
|
+
url: "https://github.com/sxyazi/yazi/releases/download/v26.8.15/yazi-x86_64-pc-windows-msvc.zip",
|
|
166
|
+
sha256: "451f6770999fa8f9b08e6c9f94a688c263b6d3007b0944c4407f1ae335eace30",
|
|
167
|
+
archive: "zip",
|
|
168
|
+
binaryMembers: {
|
|
169
|
+
yazi: "yazi-x86_64-pc-windows-msvc/yazi.exe",
|
|
170
|
+
ya: "yazi-x86_64-pc-windows-msvc/ya.exe",
|
|
171
|
+
},
|
|
172
|
+
documentMembers: ["yazi-x86_64-pc-windows-msvc/LICENSE"],
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
documents: [],
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
id: "croc",
|
|
179
|
+
version: "11.3.6",
|
|
180
|
+
summary: "relay file transfer between machines; the transfer primitive's first backend",
|
|
181
|
+
homepage: "https://schollz.com/software/croc6",
|
|
182
|
+
license: "MIT",
|
|
183
|
+
binaries: ["croc"],
|
|
184
|
+
primaryBinary: "croc",
|
|
185
|
+
// Croc negotiates its relay protocol by major version, so any 11.x on
|
|
186
|
+
// PATH speaks to a pinned 11.x relay.
|
|
187
|
+
minimumVersion: "11.0.0",
|
|
188
|
+
versionArgs: ["--version"],
|
|
189
|
+
downloads: {
|
|
190
|
+
"linux-x64": {
|
|
191
|
+
url: "https://github.com/schollz/croc/releases/download/v11.3.6/croc_v11.3.6_Linux-64bit.tar.gz",
|
|
192
|
+
sha256: "bd18e01024f5ccc8e101c08c8233d4cffbfda4ff59acad80eaa1fc2963efc0b2",
|
|
193
|
+
archive: "tar.gz",
|
|
194
|
+
binaryMembers: { croc: "croc" },
|
|
195
|
+
documentMembers: ["LICENSE", "THIRD_PARTY_NOTICES.md"],
|
|
196
|
+
},
|
|
197
|
+
"linux-arm64": {
|
|
198
|
+
url: "https://github.com/schollz/croc/releases/download/v11.3.6/croc_v11.3.6_Linux-ARM64.tar.gz",
|
|
199
|
+
sha256: "c26ac67207301ed75ae0ece63796ec8a2a002b1cf64f4e3d3d8e7bcee508b5b3",
|
|
200
|
+
archive: "tar.gz",
|
|
201
|
+
binaryMembers: { croc: "croc" },
|
|
202
|
+
documentMembers: ["LICENSE", "THIRD_PARTY_NOTICES.md"],
|
|
203
|
+
},
|
|
204
|
+
"darwin-x64": {
|
|
205
|
+
url: "https://github.com/schollz/croc/releases/download/v11.3.6/croc_v11.3.6_macOS-64bit.tar.gz",
|
|
206
|
+
sha256: "701817a20f4d2bb4312f3234e6d328e0bdd68d6d1311db3a4ab4daf10906c5b2",
|
|
207
|
+
archive: "tar.gz",
|
|
208
|
+
binaryMembers: { croc: "croc" },
|
|
209
|
+
documentMembers: ["LICENSE", "THIRD_PARTY_NOTICES.md"],
|
|
210
|
+
},
|
|
211
|
+
"darwin-arm64": {
|
|
212
|
+
url: "https://github.com/schollz/croc/releases/download/v11.3.6/croc_v11.3.6_macOS-ARM64.tar.gz",
|
|
213
|
+
sha256: "96c4ef67751b4387e3d44a7a559aafe54094f0089f34a6c71dfb5361bd48d368",
|
|
214
|
+
archive: "tar.gz",
|
|
215
|
+
binaryMembers: { croc: "croc" },
|
|
216
|
+
documentMembers: ["LICENSE", "THIRD_PARTY_NOTICES.md"],
|
|
217
|
+
},
|
|
218
|
+
// A zip rather than a tarball on Windows, and the only entry in the
|
|
219
|
+
// table whose hash is confirmed by upstream's own checksums file
|
|
220
|
+
// rather than only by the download this repository made.
|
|
221
|
+
"win32-x64": {
|
|
222
|
+
url: "https://github.com/schollz/croc/releases/download/v11.3.6/croc_v11.3.6_Windows-64bit.zip",
|
|
223
|
+
sha256: "ed22552d371d55a9e3c3b612b982484fa00adaff8fb32c3f19f36dbf8e248bbf",
|
|
224
|
+
archive: "zip",
|
|
225
|
+
binaryMembers: { croc: "croc.exe" },
|
|
226
|
+
documentMembers: ["LICENSE", "THIRD_PARTY_NOTICES.md"],
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
documents: [],
|
|
230
|
+
},
|
|
231
|
+
];
|
|
232
|
+
|
|
233
|
+
/** The registry's platform key for the running process, or null when unmapped. */
|
|
234
|
+
export function currentToolPlatform(): ToolPlatform | null {
|
|
235
|
+
const platform = process.platform;
|
|
236
|
+
const arch = process.arch;
|
|
237
|
+
if (platform === "linux") {
|
|
238
|
+
if (arch === "x64") return "linux-x64";
|
|
239
|
+
if (arch === "arm64") return "linux-arm64";
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
if (platform === "darwin") {
|
|
243
|
+
if (arch === "x64") return "darwin-x64";
|
|
244
|
+
if (arch === "arm64") return "darwin-arm64";
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
if (platform === "win32" && arch === "x64") return "win32-x64";
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/** The row with this id. */
|
|
252
|
+
export function findPinnedTool(id: string): PinnedTool | null {
|
|
253
|
+
return PINNED_TOOLS.find((entry) => entry.id === id) ?? null;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* The row that owns this executable name.
|
|
258
|
+
*
|
|
259
|
+
* Resolution is asked for by binary name (`ya` belongs to yazi), so the lookup
|
|
260
|
+
* has to cover every name an entry installs, not just the tool id.
|
|
261
|
+
*/
|
|
262
|
+
export function findPinnedToolByBinary(name: string): PinnedTool | null {
|
|
263
|
+
const bare = name.endsWith(".exe") ? name.slice(0, -4) : name;
|
|
264
|
+
return PINNED_TOOLS.find((entry) => entry.binaries.includes(bare)) ?? null;
|
|
265
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { readdirSync, rmdirSync, rmSync, statSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { toolchainRoot } from "./paths.js";
|
|
4
|
+
import { findPinnedTool } from "./registry.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Deleting vendored installs, and pruning the versions a pin bump superseded.
|
|
8
|
+
*
|
|
9
|
+
* Everything here only ever unlinks inside `<data>/tools/<id>/`, a directory
|
|
10
|
+
* Clio created by downloading it, so nothing an operator wrote is reachable
|
|
11
|
+
* from this file. That is what makes removal safe without a confirmation
|
|
12
|
+
* prompt: the worst outcome is a re-download of bytes the registry pins by
|
|
13
|
+
* checksum anyway.
|
|
14
|
+
*
|
|
15
|
+
* Version directories are the only thing considered. A name starting with `.`
|
|
16
|
+
* is an installer's staging or retired directory (`.<version>.incomplete-`,
|
|
17
|
+
* `.<version>.replaced-`), and a prune that raced an install and deleted one
|
|
18
|
+
* would corrupt that install. A full remove is different: it takes the whole
|
|
19
|
+
* `<id>` tree because no install of that tool is meant to survive it.
|
|
20
|
+
*
|
|
21
|
+
* A staging directory old enough to be dead is swept anyway. A killed install
|
|
22
|
+
* used to leave one behind permanently, invisible to every verb and reachable
|
|
23
|
+
* only by an operator who went reading under the data root.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
export interface ToolRemoveOptions {
|
|
27
|
+
/** Vendor root override. Defaults to the data root's `tools` directory. */
|
|
28
|
+
root?: string;
|
|
29
|
+
/**
|
|
30
|
+
* How old a staging directory must be before it counts as abandoned.
|
|
31
|
+
* Defaults to `STALE_STAGING_MS`. Tests set it; nothing else should need to.
|
|
32
|
+
*/
|
|
33
|
+
staleStagingMs?: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* How long a staging directory may sit before a sweep treats it as dead.
|
|
38
|
+
*
|
|
39
|
+
* The window is far shorter than it looks. `installPinnedTool` downloads every
|
|
40
|
+
* asset and verifies every checksum *before* it creates the staging directory,
|
|
41
|
+
* so that directory's whole life is a handful of local file writes and two
|
|
42
|
+
* renames: sub-second normally, seconds on a struggling disk. An hour is twelve
|
|
43
|
+
* times the installer's own per-asset download timeout and orders of magnitude
|
|
44
|
+
* past the writes, which leaves no plausible live install inside it.
|
|
45
|
+
*
|
|
46
|
+
* The pid in the name is deliberately not consulted. Pids are reused, a pid
|
|
47
|
+
* belonging to another user answers a liveness probe with EPERM rather than an
|
|
48
|
+
* honest yes or no, and a data root shared over a network filesystem makes the
|
|
49
|
+
* number meaningless. Age is the one signal that means the same thing
|
|
50
|
+
* everywhere.
|
|
51
|
+
*/
|
|
52
|
+
export const STALE_STAGING_MS = 3_600_000;
|
|
53
|
+
|
|
54
|
+
/** What one sweep of a tool's directory deleted, and what it could not. */
|
|
55
|
+
export interface ToolRemoveResult {
|
|
56
|
+
ok: boolean;
|
|
57
|
+
id: string;
|
|
58
|
+
/** `<root>/<id>`, whether or not it exists. */
|
|
59
|
+
dir: string;
|
|
60
|
+
/** Version directories this sweep deleted, oldest name first. */
|
|
61
|
+
removed: string[];
|
|
62
|
+
/** Version directories that survived a delete attempt, with the reason. */
|
|
63
|
+
failed: ReadonlyArray<{ version: string; error: string }>;
|
|
64
|
+
/** Abandoned staging directories this sweep collected, by name. */
|
|
65
|
+
staleStaging: string[];
|
|
66
|
+
message: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Version directories currently installed for a tool, sorted. */
|
|
70
|
+
export function installedToolVersions(id: string, options: ToolRemoveOptions = {}): string[] {
|
|
71
|
+
const dir = join(options.root ?? toolchainRoot(), id);
|
|
72
|
+
let names: string[];
|
|
73
|
+
try {
|
|
74
|
+
names = readdirSync(dir);
|
|
75
|
+
} catch {
|
|
76
|
+
// A tool nobody ever installed has no directory, which is not an error:
|
|
77
|
+
// the answer to "which versions are installed" is none.
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
return names
|
|
81
|
+
.filter((name) => !name.startsWith(".") && isDirectory(join(dir, name)))
|
|
82
|
+
.sort((a, b) => a.localeCompare(b));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Delete every vendored version of a tool, then the tool's own directory.
|
|
87
|
+
*
|
|
88
|
+
* A tool with nothing installed is a no-op that reports success: the operator
|
|
89
|
+
* asked for a state ("this tool is not vendored") that already holds, and
|
|
90
|
+
* failing them for it would make the verb unusable in a script.
|
|
91
|
+
*/
|
|
92
|
+
export function removeTool(id: string, options: ToolRemoveOptions = {}): ToolRemoveResult {
|
|
93
|
+
if (findPinnedTool(id) === null) {
|
|
94
|
+
const dir = join(options.root ?? toolchainRoot(), id);
|
|
95
|
+
return { ok: false, id, dir, removed: [], failed: [], staleStaging: [], message: `unknown tool: ${id}` };
|
|
96
|
+
}
|
|
97
|
+
return removeVersions(id, null, options);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Delete every vendored version of a tool except `keep`.
|
|
102
|
+
*
|
|
103
|
+
* Called by the installer once a version is in place, so a pin bump leaves one
|
|
104
|
+
* version directory behind rather than one per version the machine ever ran.
|
|
105
|
+
* The kept version is not required to exist: pruning to a version that is not
|
|
106
|
+
* installed empties the tool, which is what `--force` recovery wants.
|
|
107
|
+
*/
|
|
108
|
+
export function pruneSupersededVersions(id: string, keep: string, options: ToolRemoveOptions = {}): ToolRemoveResult {
|
|
109
|
+
return removeVersions(id, keep, options);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function removeVersions(id: string, keep: string | null, options: ToolRemoveOptions): ToolRemoveResult {
|
|
113
|
+
const root = options.root ?? toolchainRoot();
|
|
114
|
+
const dir = join(root, id);
|
|
115
|
+
const versions = installedToolVersions(id, { root }).filter((version) => version !== keep);
|
|
116
|
+
const removed: string[] = [];
|
|
117
|
+
const failed: { version: string; error: string }[] = [];
|
|
118
|
+
for (const version of versions) {
|
|
119
|
+
try {
|
|
120
|
+
rmSync(join(dir, version), { recursive: true, force: true });
|
|
121
|
+
removed.push(version);
|
|
122
|
+
} catch (error) {
|
|
123
|
+
failed.push({ version, error: error instanceof Error ? error.message : String(error) });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Before the rmdir below, so an install killed weeks ago cannot keep the tool
|
|
127
|
+
// directory alive after every version of it is gone.
|
|
128
|
+
const staleStaging = sweepStaleStaging(dir, options.staleStagingMs ?? STALE_STAGING_MS);
|
|
129
|
+
if (keep === null && failed.length === 0) {
|
|
130
|
+
// The tool is gone, so its directory is litter. `rmdir` takes it only when
|
|
131
|
+
// it is empty, so a staging directory young enough to belong to a running
|
|
132
|
+
// installer makes this fail instead of destroying that install.
|
|
133
|
+
try {
|
|
134
|
+
rmdirSync(dir);
|
|
135
|
+
} catch {
|
|
136
|
+
// Either it was never there or something still lives in it. Neither
|
|
137
|
+
// changes what this call promised: no version of the tool remains.
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
ok: failed.length === 0,
|
|
142
|
+
id,
|
|
143
|
+
dir,
|
|
144
|
+
removed,
|
|
145
|
+
failed,
|
|
146
|
+
staleStaging,
|
|
147
|
+
message: describeRemoval(id, dir, keep, removed, failed, staleStaging),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Delete the abandoned staging directories under one tool.
|
|
153
|
+
*
|
|
154
|
+
* Only names the installer produces are considered, so an operator who parked
|
|
155
|
+
* something of their own under `<data>/tools/<id>` with a leading dot keeps it.
|
|
156
|
+
* A directory whose age cannot be read is left alone: an unreadable stat is not
|
|
157
|
+
* evidence of death.
|
|
158
|
+
*/
|
|
159
|
+
function sweepStaleStaging(dir: string, staleMs: number): string[] {
|
|
160
|
+
let names: string[];
|
|
161
|
+
try {
|
|
162
|
+
names = readdirSync(dir);
|
|
163
|
+
} catch {
|
|
164
|
+
return [];
|
|
165
|
+
}
|
|
166
|
+
const cutoff = Date.now() - staleMs;
|
|
167
|
+
const swept: string[] = [];
|
|
168
|
+
for (const name of names.sort((a, b) => a.localeCompare(b))) {
|
|
169
|
+
if (!STAGING_NAME.test(name)) continue;
|
|
170
|
+
const stat = statSync(join(dir, name), { throwIfNoEntry: false });
|
|
171
|
+
if (stat === undefined || !stat.isDirectory() || stat.mtimeMs > cutoff) continue;
|
|
172
|
+
try {
|
|
173
|
+
rmSync(join(dir, name), { recursive: true, force: true });
|
|
174
|
+
swept.push(name);
|
|
175
|
+
} catch {
|
|
176
|
+
// A staging directory that resists deletion is litter, not a failure:
|
|
177
|
+
// nothing resolves it, and reporting it would turn a successful install
|
|
178
|
+
// into a failed one over a directory the ladder never looks at.
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return swept;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** `.<version>.incomplete-<pid>-<t>` and `.<version>.replaced-<pid>-<t>`. */
|
|
185
|
+
const STAGING_NAME = /^\.[^/]+\.(?:incomplete|replaced)-\d+-[a-z0-9]+$/;
|
|
186
|
+
|
|
187
|
+
function describeRemoval(
|
|
188
|
+
id: string,
|
|
189
|
+
dir: string,
|
|
190
|
+
keep: string | null,
|
|
191
|
+
removed: ReadonlyArray<string>,
|
|
192
|
+
failed: ReadonlyArray<{ version: string; error: string }>,
|
|
193
|
+
staleStaging: ReadonlyArray<string>,
|
|
194
|
+
): string {
|
|
195
|
+
// Reported as a suffix rather than as the headline: an abandoned staging
|
|
196
|
+
// directory is housekeeping, and the operator asked about versions.
|
|
197
|
+
const swept =
|
|
198
|
+
staleStaging.length === 0
|
|
199
|
+
? ""
|
|
200
|
+
: `; swept ${staleStaging.length} abandoned install ${staleStaging.length === 1 ? "directory" : "directories"}`;
|
|
201
|
+
const trouble = failed.map((entry) => `${entry.version} (${entry.error})`).join(", ");
|
|
202
|
+
if (failed.length > 0) {
|
|
203
|
+
const done = removed.length > 0 ? `removed ${removed.join(", ")}; ` : "";
|
|
204
|
+
return `${done}could not remove ${id} ${trouble} under ${dir}${swept}`;
|
|
205
|
+
}
|
|
206
|
+
if (removed.length === 0) {
|
|
207
|
+
return keep === null
|
|
208
|
+
? `${id} is not installed under ${dir}; nothing to remove${swept}`
|
|
209
|
+
: `${id} has no superseded versions under ${dir}${swept}`;
|
|
210
|
+
}
|
|
211
|
+
const what = `${id} ${removed.join(", ")}`;
|
|
212
|
+
const headline = keep === null ? `removed ${what} from ${dir}` : `pruned ${what} from ${dir}, keeping ${keep}`;
|
|
213
|
+
return `${headline}${swept}`;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function isDirectory(path: string): boolean {
|
|
217
|
+
return statSync(path, { throwIfNoEntry: false })?.isDirectory() === true;
|
|
218
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { statSync } from "node:fs";
|
|
2
|
+
import { delimiter, join } from "node:path";
|
|
3
|
+
import { toolVersionDir, vendoredBinaryPath } from "./paths.js";
|
|
4
|
+
import { currentToolPlatform, findPinnedTool, findPinnedToolByBinary, PINNED_TOOLS } from "./registry.js";
|
|
5
|
+
import type { PinnedTool, ToolPathCandidate, ToolResolution, ToolStatus } from "./types.js";
|
|
6
|
+
import { probeBinaryVersion, satisfiesMinimum } from "./version.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The shared resolution ladder: PATH, then the vendored pin, then nothing.
|
|
10
|
+
*
|
|
11
|
+
* PATH comes first because a tool the operator installed themselves is the one
|
|
12
|
+
* they maintain, and Clio downloading a second copy of a program already on the
|
|
13
|
+
* machine is a surprise. It only wins when its version clears the registry's
|
|
14
|
+
* floor, because the alternative is Clio driving a surface that release does not
|
|
15
|
+
* have and reporting the resulting failure as a bug in the feature.
|
|
16
|
+
*
|
|
17
|
+
* Nothing here downloads, spawns a server, or writes to disk. It is safe on the
|
|
18
|
+
* interactive startup path and inside doctor, both of which promise to create
|
|
19
|
+
* nothing.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/** First executable named `name` on PATH, or null. */
|
|
23
|
+
export function findExecutableOnPath(name: string): string | null {
|
|
24
|
+
const pathEnv = process.env.PATH;
|
|
25
|
+
if (!pathEnv) return null;
|
|
26
|
+
const candidates = process.platform === "win32" ? [`${name}.exe`, name] : [name];
|
|
27
|
+
for (const dir of pathEnv.split(delimiter)) {
|
|
28
|
+
if (!dir) continue;
|
|
29
|
+
for (const candidate of candidates) {
|
|
30
|
+
const full = join(dir, candidate);
|
|
31
|
+
const stat = statSync(full, { throwIfNoEntry: false });
|
|
32
|
+
if (stat?.isFile() && (process.platform === "win32" || (stat.mode & 0o111) !== 0)) return full;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Resolve one executable name through the ladder.
|
|
40
|
+
*
|
|
41
|
+
* A name that is not in the registry gets a plain PATH lookup: there is no pin
|
|
42
|
+
* to compare against and nothing vendored to fall back to, so the answer is
|
|
43
|
+
* whatever PATH says.
|
|
44
|
+
*/
|
|
45
|
+
export function resolveToolBinary(name: string): ToolResolution {
|
|
46
|
+
const entry = findPinnedToolByBinary(name) ?? findPinnedTool(name);
|
|
47
|
+
if (entry === null) {
|
|
48
|
+
const found = findExecutableOnPath(name);
|
|
49
|
+
return {
|
|
50
|
+
source: found === null ? "none" : "path",
|
|
51
|
+
binaryPath: found,
|
|
52
|
+
version: null,
|
|
53
|
+
entry: null,
|
|
54
|
+
pathCandidate: found === null ? null : { path: found, version: null, satisfiesMinimum: true },
|
|
55
|
+
vendoredPath: null,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
// An entry asked for by tool id resolves its primary binary; asked for by
|
|
59
|
+
// binary name it resolves that binary, so `ya` does not silently become `yazi`.
|
|
60
|
+
const binary = entry.binaries.includes(name) ? name : entry.primaryBinary;
|
|
61
|
+
return resolveEntryBinary(entry, binary);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** The ladder for a known registry row and one of its binaries. */
|
|
65
|
+
export function resolveEntryBinary(entry: PinnedTool, binary: string): ToolResolution {
|
|
66
|
+
const pathCandidate = probePathCandidate(entry, binary);
|
|
67
|
+
const vendored = vendoredBinaryPath(entry.id, entry.version, binary);
|
|
68
|
+
const vendoredStat = statSync(vendored, { throwIfNoEntry: false });
|
|
69
|
+
const vendoredPath = vendoredStat?.isFile() ? vendored : null;
|
|
70
|
+
|
|
71
|
+
if (pathCandidate?.satisfiesMinimum) {
|
|
72
|
+
return {
|
|
73
|
+
source: "path",
|
|
74
|
+
binaryPath: pathCandidate.path,
|
|
75
|
+
version: pathCandidate.version,
|
|
76
|
+
entry,
|
|
77
|
+
pathCandidate,
|
|
78
|
+
vendoredPath,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
if (vendoredPath !== null) {
|
|
82
|
+
return {
|
|
83
|
+
source: "vendored",
|
|
84
|
+
binaryPath: vendoredPath,
|
|
85
|
+
version: entry.version,
|
|
86
|
+
entry,
|
|
87
|
+
pathCandidate,
|
|
88
|
+
vendoredPath,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return { source: "none", binaryPath: null, version: null, entry, pathCandidate, vendoredPath: null };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Per-entry state for the CLI and doctor. */
|
|
95
|
+
export function toolStatus(entry: PinnedTool): ToolStatus {
|
|
96
|
+
const platform = currentToolPlatform();
|
|
97
|
+
const supported = platform !== null && entry.downloads[platform] !== undefined;
|
|
98
|
+
const resolution = resolveEntryBinary(entry, entry.primaryBinary);
|
|
99
|
+
return {
|
|
100
|
+
id: entry.id,
|
|
101
|
+
version: entry.version,
|
|
102
|
+
license: entry.license,
|
|
103
|
+
platform,
|
|
104
|
+
supported,
|
|
105
|
+
installed: resolution.vendoredPath !== null,
|
|
106
|
+
installDir: toolVersionDir(entry.id, entry.version),
|
|
107
|
+
resolution,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Per-entry state for every row, in registry order. */
|
|
112
|
+
export function toolStatuses(): ToolStatus[] {
|
|
113
|
+
return PINNED_TOOLS.map((entry) => toolStatus(entry));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** The one command that fixes a tool Clio cannot resolve. */
|
|
117
|
+
export function installRemedy(id: string): string {
|
|
118
|
+
return `clio-coder tools install ${id}`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* What a rejected PATH copy was, and which floor it failed. Null when there is
|
|
123
|
+
* no PATH copy or when the one there was accepted.
|
|
124
|
+
*
|
|
125
|
+
* The floors are deliberately conservative: they sit at the pin for herdr and
|
|
126
|
+
* yazi because Clio drives socket and plugin surfaces verified against only
|
|
127
|
+
* that release. That is a defensible choice and a costly one, since it rejects
|
|
128
|
+
* binaries an operator installed themselves and that mostly work. The cost is
|
|
129
|
+
* only payable if the rejection is legible, so every renderer of a rejection
|
|
130
|
+
* names the path, the version found, and the floor it missed, and never reports
|
|
131
|
+
* a rejected copy as an absent one.
|
|
132
|
+
*/
|
|
133
|
+
export function describeFloorRejection(status: ToolStatus): string | null {
|
|
134
|
+
const candidate = status.resolution.pathCandidate;
|
|
135
|
+
if (candidate === null || candidate.satisfiesMinimum) return null;
|
|
136
|
+
const floor = status.resolution.entry?.minimumVersion ?? status.version;
|
|
137
|
+
const found = candidate.version === null ? "an unreadable version" : candidate.version;
|
|
138
|
+
return `PATH copy ${candidate.path} is ${found}, below the ${floor} floor`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* One sentence describing where a tool resolved and how its version compares to
|
|
143
|
+
* the pin. Shared by `clio-coder tools status` and the doctor rows so the two
|
|
144
|
+
* cannot drift into describing the same machine differently.
|
|
145
|
+
*/
|
|
146
|
+
export function describeResolution(status: ToolStatus): string {
|
|
147
|
+
const { resolution } = status;
|
|
148
|
+
const rejection = describeFloorRejection(status);
|
|
149
|
+
if (resolution.source === "path") {
|
|
150
|
+
const version = resolution.version ?? "unreadable version";
|
|
151
|
+
return `PATH ${resolution.binaryPath} (${version}, pin ${status.version})`;
|
|
152
|
+
}
|
|
153
|
+
if (resolution.source === "vendored") {
|
|
154
|
+
// The remedy is deliberately absent here. The rejection cost the operator
|
|
155
|
+
// nothing: Clio has a copy at the pin and is running it, and printing an
|
|
156
|
+
// install command that would change nothing is its own kind of dishonesty.
|
|
157
|
+
const rejected = rejection === null ? "" : `; ${rejection}, so Clio runs the vendored copy`;
|
|
158
|
+
return `vendored ${resolution.binaryPath} (${status.version})${rejected}`;
|
|
159
|
+
}
|
|
160
|
+
if (!status.supported) {
|
|
161
|
+
const platform = status.platform ?? `${process.platform}-${process.arch}`;
|
|
162
|
+
// No remedy either: there is no asset to install on this machine, so the
|
|
163
|
+
// rejection is the whole story and naming a command would be a dead end.
|
|
164
|
+
const rejected = rejection === null ? "" : `; ${rejection}`;
|
|
165
|
+
return `not installed and no pinned asset for this platform (${platform})${rejected}`;
|
|
166
|
+
}
|
|
167
|
+
if (rejection !== null) {
|
|
168
|
+
return `${rejection}, and nothing is vendored (install with \`${installRemedy(status.id)}\`)`;
|
|
169
|
+
}
|
|
170
|
+
return `not found (install with \`${installRemedy(status.id)}\`)`;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function probePathCandidate(entry: PinnedTool, binary: string): ToolPathCandidate | null {
|
|
174
|
+
const found = findExecutableOnPath(binary);
|
|
175
|
+
if (found === null) return null;
|
|
176
|
+
// The floor is a property of the tool, and only the primary binary is asked
|
|
177
|
+
// for its version, so a secondary binary inherits the primary's verdict from
|
|
178
|
+
// the same install root rather than being probed with flags it may not have.
|
|
179
|
+
const probeTarget = binary === entry.primaryBinary ? found : (findExecutableOnPath(entry.primaryBinary) ?? found);
|
|
180
|
+
const version = probeBinaryVersion(probeTarget, entry.versionArgs);
|
|
181
|
+
return { path: found, version, satisfiesMinimum: satisfiesMinimum(version, entry.minimumVersion) };
|
|
182
|
+
}
|