@iowarp/clio-coder 0.4.1 → 0.4.3
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 +127 -0
- package/CONTRIBUTING.md +142 -52
- package/README.md +434 -473
- package/SECURITY.md +2 -1
- package/dist/{acp-ZILU3AUO.js → acp-H2NGRPWO.js} +12 -12
- package/dist/{agents-HYWGBGQR.js → agents-TL5LLUQP.js} +56 -55
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-N3QT7CBO.js → auth-E5SW4HMS.js} +23 -21
- package/dist/builtins-IA7V7FUC.js +22 -0
- package/dist/{chunk-7RY5VZPH.js → chunk-2APPQIER.js} +8 -8
- package/dist/{chunk-72GZI5EV.js → chunk-2JH2WHGE.js} +2 -2
- package/dist/{chunk-JA5QWE4Z.js → chunk-2UG5F4C5.js} +1973 -1664
- package/dist/{chunk-5YHDIDBP.js → chunk-2UH2KFUP.js} +2 -2
- package/dist/{chunk-CTJ4RNAA.js → chunk-2VIKGWFZ.js} +2 -2
- package/dist/{chunk-I66EAJFY.js → chunk-2WZ546HR.js} +267 -232
- package/dist/{chunk-GIZNH63R.js → chunk-35MSIRKH.js} +9 -4
- package/dist/chunk-3EBYEESD.js +314 -0
- package/dist/{chunk-J5LZHVIT.js → chunk-3M6DQK6S.js} +113 -35
- package/dist/{chunk-RKSR6VSF.js → chunk-4IUZQIJ3.js} +29 -1
- package/dist/{chunk-6FN3E6KX.js → chunk-4O6MANBS.js} +2 -2
- package/dist/chunk-4UVU7BJ5.js +39 -0
- package/dist/{chunk-VKRH2TCS.js → chunk-4WR7VSYB.js} +2 -2
- package/dist/{chunk-BBTJOK6Y.js → chunk-54CBCGIR.js} +5 -5
- package/dist/{chunk-AP73CFDC.js → chunk-5ICU3EUH.js} +2 -2
- package/dist/chunk-5MEZN6CB.js +1334 -0
- package/dist/{chunk-O42A54GG.js → chunk-5OIVVPHF.js} +2 -2
- package/dist/{chunk-ABLSQ6JX.js → chunk-64I3JVYM.js} +8 -2
- package/dist/{chunk-AFKWHWXF.js → chunk-6PTFB5VS.js} +39 -22
- package/dist/{chunk-VN3SHNBN.js → chunk-7DICMOS6.js} +2 -2
- package/dist/chunk-7DRAWPTZ.js +360 -0
- package/dist/chunk-7E7I3WLS.js +3762 -0
- package/dist/{chunk-BJGUKIG4.js → chunk-7ZYNNDKC.js} +7 -7
- package/dist/{chunk-XKA2ICR3.js → chunk-AF4YM7Z4.js} +652 -252
- package/dist/{chunk-GVQJ5CCZ.js → chunk-AX2THNSA.js} +12 -12
- package/dist/{chunk-IG7BCQBA.js → chunk-B4OAX3SI.js} +65 -3
- package/dist/{chunk-TD3PGPQA.js → chunk-B4VEBZKF.js} +3 -3
- package/dist/{chunk-74YWRRU5.js → chunk-BEPZRGGU.js} +10 -10
- package/dist/{chunk-FEFIFZTL.js → chunk-CE5AX47J.js} +2 -2
- package/dist/{chunk-UAPGZHYC.js → chunk-DWUOQKRU.js} +25 -11
- package/dist/{chunk-THYWACCR.js → chunk-E3TPLWFX.js} +3 -3
- package/dist/{chunk-7EPLI7VL.js → chunk-EKCHAPYA.js} +2 -2
- package/dist/{chunk-HLW2MRKE.js → chunk-F4EKGO4N.js} +3 -1
- package/dist/{chunk-PJJ6MY27.js → chunk-F5JHEYZM.js} +7 -7
- package/dist/{chunk-6CCS4G3W.js → chunk-FTMGRKEF.js} +3 -3
- package/dist/{chunk-SINK3QR6.js → chunk-G76U63X4.js} +17 -17
- package/dist/{chunk-EIMVLWB3.js → chunk-GHS5EBTQ.js} +64 -9
- package/dist/{chunk-QMXC4JB7.js → chunk-GI7YYQ3F.js} +187 -1419
- package/dist/{chunk-TZSKNMZG.js → chunk-GTUD2WMY.js} +2 -1
- package/dist/{chunk-6HMJX2VU.js → chunk-GWZNEVM2.js} +44 -12
- package/dist/chunk-GYV6VZOC.js +26 -0
- package/dist/{chunk-MQXIVJ35.js → chunk-HAXOFFRH.js} +5 -5
- package/dist/{chunk-UXN6JT4W.js → chunk-HEQY7ZFI.js} +3 -3
- package/dist/{chunk-7PWAODYW.js → chunk-I7XBWTYH.js} +2 -2
- package/dist/{chunk-GCSMB2KY.js → chunk-I7ZPNEJM.js} +145 -102
- package/dist/{chunk-WNP7O5WZ.js → chunk-ID64D7PE.js} +4 -4
- package/dist/{chunk-QTFGO774.js → chunk-IGLP3ODT.js} +29 -16
- package/dist/chunk-IJNZMHLA.js +101 -0
- package/dist/{chunk-BDPT6GTK.js → chunk-INY6HTFL.js} +7 -7
- package/dist/{chunk-PBP4B7XR.js → chunk-IUE3Y34X.js} +2 -2
- package/dist/{chunk-6NJQITNH.js → chunk-IWT4SF4R.js} +6 -3
- package/dist/{chunk-R23Z6K6I.js → chunk-JDAY6FIL.js} +19 -19
- package/dist/chunk-JEQ3XTHC.js +42 -0
- package/dist/{chunk-FSP7CMNU.js → chunk-JGRC33J2.js} +50 -4
- package/dist/{chunk-TVH4ONAM.js → chunk-JKKCYP3C.js} +10 -10
- package/dist/{chunk-HJWWJ6IL.js → chunk-JSC3U7TI.js} +16 -4
- package/dist/{chunk-C537JADH.js → chunk-KK4JZPBQ.js} +19 -141
- package/dist/{chunk-K6BF4U2H.js → chunk-KKOJXO6R.js} +62 -14
- package/dist/{chunk-IHXBNWMM.js → chunk-KXDSS5WJ.js} +7 -3
- package/dist/{chunk-6DWBAZ5U.js → chunk-L47TF46W.js} +5 -7
- package/dist/{chunk-HUAS7ITX.js → chunk-LDJG7DW3.js} +91 -42
- package/dist/{chunk-CDNVLKUX.js → chunk-LLDJM5XK.js} +13 -7
- package/dist/{chunk-YPI3QQCF.js → chunk-MCEPRMZW.js} +2 -4
- package/dist/{chunk-Y4CAGMM6.js → chunk-MNJGS2IN.js} +5 -6
- package/dist/{chunk-VKFQTNDV.js → chunk-MUW2BDDH.js} +4 -4
- package/dist/{chunk-E67WX76H.js → chunk-MWUZBSAQ.js} +104 -152
- package/dist/{chunk-OJTRZGR3.js → chunk-N2Z7HLVY.js} +21 -21
- package/dist/{chunk-TVHHYFHE.js → chunk-NEDJ26B5.js} +2 -2
- package/dist/{chunk-FYUN5KZ3.js → chunk-NIQJ66N4.js} +21 -21
- package/dist/{chunk-U2WB7TZS.js → chunk-NMJXSHBJ.js} +97 -85
- package/dist/{chunk-CWVRRIEI.js → chunk-NZMNUPZZ.js} +2 -2
- package/dist/{chunk-VEGN6WIQ.js → chunk-O5CVSAG5.js} +3 -3
- package/dist/{chunk-MOPSG2X7.js → chunk-OML5D5V5.js} +8 -8
- package/dist/{chunk-2VG7KLYV.js → chunk-PAJQJ7BS.js} +5816 -3255
- package/dist/{chunk-ZW55JB7N.js → chunk-PUVDKJ2Y.js} +2 -2
- package/dist/{chunk-BTGG6BG2.js → chunk-QWGDJJYJ.js} +158 -19
- package/dist/chunk-R6Q67RJH.js +134 -0
- package/dist/{chunk-ZJLUDYFY.js → chunk-RRNP2ANY.js} +6 -6
- package/dist/{chunk-PVAMAVBB.js → chunk-RSJ25QSL.js} +102 -2
- package/dist/{chunk-NLFAQR7Z.js → chunk-S66XZJOF.js} +3 -23
- package/dist/chunk-SKHCAU7K.js +385 -0
- package/dist/chunk-SZAA6XDG.js +30 -0
- package/dist/{chunk-J4HBWF6Y.js → chunk-TM6LQDI3.js} +131 -28
- package/dist/chunk-UOIZ7DA4.js +41 -0
- package/dist/{chunk-MA3H6DM5.js → chunk-UPZU6GE4.js} +25 -3
- package/dist/{chunk-BWW4HLO4.js → chunk-UXCU4E3T.js} +8 -6
- package/dist/{chunk-N5UK64DP.js → chunk-V2ANDPVT.js} +4 -4
- package/dist/{chunk-AK5XEFVZ.js → chunk-VA5FNYMT.js} +26 -13
- package/dist/{chunk-6VC4OV3Z.js → chunk-VIA6RFQZ.js} +3 -11
- package/dist/{chunk-ZAZB4JMW.js → chunk-VKPAQYEB.js} +27 -8
- package/dist/{chunk-QKIFBZKT.js → chunk-VW6DOEDG.js} +497 -81
- package/dist/{chunk-SCYB3HA4.js → chunk-W6RRQCPQ.js} +63 -19
- package/dist/{chunk-2NM363SV.js → chunk-WBKFA554.js} +10 -10
- package/dist/{chunk-R32CLGZ6.js → chunk-WCXUNS7U.js} +82 -21
- package/dist/{chunk-GPPB3JBE.js → chunk-WRBAGUNF.js} +3 -3
- package/dist/{chunk-IXJT6DCX.js → chunk-XIVNBFZS.js} +85 -30
- package/dist/{chunk-UEDMSP56.js → chunk-XPWWI35G.js} +417 -201
- package/dist/chunk-XRZT5WY5.js +47 -0
- package/dist/{chunk-3QSOM6PA.js → chunk-Y3CBHOR6.js} +2 -2
- package/dist/{chunk-VXMFAE2W.js → chunk-YPC6ZR5L.js} +19 -6
- package/dist/{chunk-AKB4GYDL.js → chunk-YQWYVTMC.js} +5 -5
- package/dist/{chunk-6I5ILFOF.js → chunk-ZA4VCIGV.js} +3 -3
- package/dist/{chunk-7OBGU7UB.js → chunk-ZDN3Y73Y.js} +12 -18
- package/dist/{chunk-3I5NY75V.js → chunk-ZWPRK62N.js} +8 -5
- package/dist/cli/index.js +41 -39
- package/dist/{clio-IT3G3VQH.js → clio-CMMK4KRR.js} +9 -9
- package/dist/{code-nav-RK6S7F6E.js → code-nav-MDZNQS33.js} +89 -21
- package/dist/{components-UBWCQSRW.js → components-UCUQ4QXW.js} +4 -4
- package/dist/{config-3QZRWZJF.js → config-SVM5P5YI.js} +131 -84
- package/dist/{configure-FL7Y3KJF.js → configure-LE3IK2TJ.js} +28 -26
- package/dist/{context-5HE7ODYK.js → context-2OHRKS42.js} +69 -64
- package/dist/{context-KYQFRVDC.js → context-E3VC7RX5.js} +15 -11
- package/dist/{context-XNHL75JV.js → context-VNCR7KAG.js} +93 -65
- package/dist/{context-clear-N545L53A.js → context-clear-BW4O37TG.js} +64 -60
- package/dist/context-map-COB37XXN.js +505 -0
- package/dist/{context-working-set-QHKXSV2F.js → context-working-set-VDS25HXZ.js} +19 -18
- package/dist/{dispatch-runner-RGIE5PCT.js → dispatch-runner-5AHT53RF.js} +93 -82
- package/dist/{docs-5NAF6AU7.js → docs-PD3EXDKU.js} +21 -20
- package/dist/{doctor-ZGPEGHIP.js → doctor-WNNVO6FY.js} +48 -47
- package/dist/{eval-GXLL44RD.js → eval-7G7SGAYO.js} +287 -115
- package/dist/{eval-inventory-HBWSWQOK.js → eval-inventory-Y6QRFOH5.js} +4 -4
- package/dist/{evidence-HWLBRH3Q.js → evidence-VD6736FQ.js} +67 -64
- package/dist/{evolve-FTZBMNVW.js → evolve-AL3NGVRL.js} +65 -62
- package/dist/{extensions-VHRBEID7.js → extensions-MOVJ32NM.js} +9 -7
- package/dist/{fleet-CKZHJWZJ.js → fleet-QZHUMAGI.js} +114 -111
- package/dist/{fleet-commands-EXDXBMV6.js → fleet-commands-BAYT5FJZ.js} +10 -10
- package/dist/{fleet-decisions-OTHB6KRL.js → fleet-decisions-IREVMRU4.js} +7 -6
- package/dist/{fleet-graph-YTEZUCUT.js → fleet-graph-YCTT3HTI.js} +22 -19
- package/dist/{fleet-inspect-SS6YMDCK.js → fleet-inspect-QVJTDAVB.js} +58 -55
- package/dist/{fleet-preflight-PBY4VYOM.js → fleet-preflight-25QAFPK4.js} +4 -4
- package/dist/{fleet-validate-KMEM5L3S.js → fleet-validate-5O57AAJ7.js} +26 -23
- package/dist/{fleet-verify-QD5M7E7Q.js → fleet-verify-CPH2W2T6.js} +59 -56
- package/dist/{fleet-view-WAMJYNDT.js → fleet-view-SWBR3VGQ.js} +58 -55
- package/dist/{init-5XQRBOFV.js → init-J477LKZH.js} +82 -79
- package/dist/{interop-34TVO25M.js → interop-3FCM6XLG.js} +11 -11
- package/dist/{library-3QY6KF57.js → library-QUQEIUG6.js} +30 -27
- package/dist/{memory-L4UTIIIW.js → memory-SGGSEP65.js} +67 -64
- package/dist/{models-ZVX3QOWE.js → models-HEKUAXXK.js} +53 -46
- package/dist/{monitor-CEKVSYTS.js → monitor-HKU57TYQ.js} +63 -60
- package/dist/{orchestrator-77BAP6BC.js → orchestrator-VDFAEFAI.js} +1831 -1057
- package/dist/{panes-7STHOAUJ.js → panes-DN2SSFOH.js} +5 -5
- package/dist/{panes-SHAUIRXY.js → panes-TALGNPZT.js} +29 -14
- package/dist/{paths-L7LGY6RN.js → paths-NBMFAIEZ.js} +5 -5
- package/dist/reset-EAJFFJVB.js +344 -0
- package/dist/{resources-74GKTLSF.js → resources-OVKSEFVE.js} +29 -20
- package/dist/{run-HBAUJNNZ.js → run-7DP7ZF2J.js} +120 -115
- package/dist/{share-G3APVLVP.js → share-WML67FT3.js} +32 -27
- package/dist/{skills-35HHUKCR.js → skills-SG662R2K.js} +41 -31
- package/dist/{skills-eval-QN4HSHDC.js → skills-eval-VVZEUU46.js} +78 -77
- package/dist/{skills-inventory-J357J34F.js → skills-inventory-I2E23GET.js} +23 -20
- package/dist/{slash-commands-JZZCQA32.js → slash-commands-S7MBJDQK.js} +40 -36
- package/dist/{steer-XAVHJM22.js → steer-2LQOMCPB.js} +3 -3
- package/dist/{support-U7QOWY26.js → support-CC2UJBJ6.js} +6 -6
- package/dist/{targets-DSM6CY3M.js → targets-4QC3HIEW.js} +54 -54
- package/dist/{terminal-lease-JOPFUVEM.js → terminal-lease-TUHIJ6Y2.js} +5 -5
- package/dist/{tools-MKNWVPBH.js → tools-TFGJICCU.js} +10 -10
- package/dist/{trace-ECQ7TIYZ.js → trace-FXMXUZUF.js} +55 -7
- package/dist/uninstall-5PEVOE5B.js +408 -0
- package/dist/upgrade-M4WXY6KN.js +303 -0
- package/dist/{usage-X52N3IDJ.js → usage-N7ZNVLEM.js} +151 -104
- package/dist/{verifiers-EJTVVSMA.js → verifiers-DJTP4XX6.js} +15 -15
- package/dist/{verify-YJL6XET2.js → verify-RWE4PPEK.js} +9 -9
- package/dist/{web-fetch-MPIFL3LL.js → web-fetch-MPARV2K7.js} +2 -2
- package/dist/{wiki-generate-4NDZTQ4B.js → wiki-generate-C7IQOXSP.js} +89 -86
- package/dist/{with-panes-OBOBFIIR.js → with-panes-4GCGSL7J.js} +53 -257
- package/dist/worker/entry.js +90 -74
- package/docs/README.md +176 -81
- package/docs/{acp.md → architecture/acp.md} +36 -20
- package/docs/{alcf-provider.md → architecture/alcf-provider.md} +8 -5
- package/docs/{architecture.md → architecture/architecture.md} +43 -22
- package/docs/{artifact-placement.md → architecture/artifact-placement.md} +27 -23
- package/docs/architecture/artifact-versions.md +90 -0
- package/docs/{capacity-and-scheduling.md → architecture/capacity-and-scheduling.md} +26 -13
- package/docs/{context-engine.md → architecture/context-engine.md} +29 -25
- package/docs/{context-working-set.md → architecture/context-working-set.md} +13 -10
- package/docs/{dispatch-architecture-rationale.md → architecture/dispatch-architecture-rationale.md} +12 -9
- package/docs/{dispatch-typed-intent.md → architecture/dispatch-typed-intent.md} +68 -46
- package/docs/{evidence-and-memory.md → architecture/evidence-and-memory.md} +23 -16
- package/docs/{middleware-and-components.md → architecture/middleware-and-components.md} +11 -5
- package/docs/{model-catalog.md → architecture/model-catalog.md} +61 -27
- package/docs/{observability.md → architecture/observability.md} +38 -14
- package/docs/{pi-boundary.md → architecture/pi-boundary.md} +24 -11
- package/docs/{prompt-envelope-and-tools.md → architecture/prompt-envelope-and-tools.md} +57 -20
- package/docs/{provider-adapter-cookbook.md → architecture/provider-adapter-cookbook.md} +99 -25
- package/docs/{safety-model.md → architecture/safety-model.md} +35 -20
- package/docs/{session-lifecycle.md → architecture/session-lifecycle.md} +8 -5
- package/docs/architecture/time-conventions.md +125 -0
- package/docs/{trace-store.md → architecture/trace-store.md} +13 -5
- package/docs/{tui-design.md → architecture/tui-design.md} +13 -13
- package/docs/{worker-dispatch-mechanics.md → architecture/worker-dispatch-mechanics.md} +27 -30
- package/docs/{built-in-agents.md → guide/built-in-agents.md} +65 -35
- package/docs/{commands-and-modes.md → guide/commands-and-modes.md} +66 -61
- package/docs/{configuration-and-targets.md → guide/configuration-and-targets.md} +323 -297
- package/docs/guide/configuration-reference.md +1163 -0
- package/docs/{environment-variables.md → guide/environment-variables.md} +33 -28
- package/docs/{exit-codes-and-output.md → guide/exit-codes-and-output.md} +6 -3
- package/docs/{extensions-and-sharing.md → guide/extensions-and-sharing.md} +41 -14
- package/docs/{fleet-dispatch.md → guide/fleet-dispatch.md} +39 -43
- package/docs/{glossary.md → guide/glossary.md} +14 -11
- package/docs/{installation-and-lifecycle.md → guide/installation-and-lifecycle.md} +81 -17
- package/docs/guide/panes-and-files.md +290 -0
- package/docs/{proactive-memory.md → guide/proactive-memory.md} +131 -107
- package/docs/{resource-library.md → guide/resource-library.md} +13 -4
- package/docs/{skills-marketplace.md → guide/skills-marketplace.md} +25 -3
- package/docs/{tool-usage.md → guide/tool-usage.md} +87 -23
- package/docs/{troubleshooting.md → guide/troubleshooting.md} +9 -4
- package/docs/{config-knobs-audit.md → history/config-knobs-audit.md} +11 -11
- package/docs/{release-cut-checklist.md → history/release-cut-checklist.md} +29 -2
- package/docs/process/development-pipeline.md +152 -0
- package/docs/process/documentation-coverage.md +100 -0
- package/docs/process/documentation-guide.md +187 -0
- package/docs/{eval-runner.md → process/eval-runner.md} +108 -53
- package/docs/{evals-internal.md → process/evals-internal.md} +10 -10
- package/docs/{evolution.md → process/evolution.md} +2 -2
- package/docs/{fleet-demo-runbook.md → process/fleet-demo-runbook.md} +11 -7
- package/docs/{git-commit-provenance.md → process/git-commit-provenance.md} +11 -4
- package/docs/{performance-methodology.md → process/performance-methodology.md} +87 -69
- package/docs/{scientific-validation.md → process/scientific-validation.md} +4 -4
- package/evals/README.md +2 -2
- package/evals/behavioral-model.yaml +3 -2
- package/package.json +10 -8
- package/skills/README.md +52 -41
- package/skills/coding/ast-grep/SKILL.md +102 -31
- package/skills/coding/ast-grep/evals.md +26 -0
- package/skills/coding/coding-standards/SKILL.md +41 -6
- package/skills/coding/coding-standards/evals.md +23 -0
- package/skills/coding/prototype/SKILL.md +88 -29
- package/skills/coding/prototype/evals.md +19 -0
- package/skills/coding/tdd/SKILL.md +81 -54
- package/skills/coding/tdd/evals.md +20 -0
- package/skills/context/context-handoff/SKILL.md +44 -3
- package/skills/context/context-handoff/evals.md +44 -0
- package/skills/context/context-prime/SKILL.md +46 -16
- package/skills/context/context-prime/evals.md +45 -0
- package/skills/git/branch-closeout/SKILL.md +132 -0
- package/skills/git/branch-closeout/evals.md +133 -0
- package/skills/git/branch-closeout/references/closeout-checklist.md +81 -0
- package/skills/git/file-ticket/SKILL.md +78 -64
- package/skills/git/file-ticket/assets/issue-template.md +22 -0
- package/skills/git/file-ticket/evals.md +31 -26
- package/skills/git/file-ticket/references/issue-discovery.md +49 -0
- package/skills/git/fix-issue/SKILL.md +88 -65
- package/skills/git/fix-issue/evals.md +35 -31
- package/skills/git/fix-issue/references/diagnosis-and-rca.md +46 -0
- package/skills/git/resolve-merge-conflicts/SKILL.md +101 -52
- package/skills/git/resolve-merge-conflicts/evals.md +52 -25
- package/skills/git/resolve-merge-conflicts/references/conflict-matrix.md +126 -0
- package/skills/git/ship/SKILL.md +103 -67
- package/skills/git/ship/assets/pr-template.md +21 -0
- package/skills/git/ship/evals.md +44 -28
- package/skills/git/ship/references/remote-and-branch-policy.md +62 -0
- package/skills/git/worktree-create/SKILL.md +80 -50
- package/skills/git/worktree-create/evals.md +40 -33
- package/skills/git/worktree-create/references/worktree-setup.md +62 -66
- package/skills/git/worktree-merge/SKILL.md +112 -65
- package/skills/git/worktree-merge/evals.md +42 -34
- package/skills/git/worktree-merge/references/merge-strategies.md +52 -0
- package/skills/meta/clio-coder-dev/SKILL.md +9 -5
- package/skills/meta/clio-coder-dev/evals.md +3 -2
- package/skills/meta/clio-coder-test/SKILL.md +102 -95
- package/skills/meta/clio-coder-test/evals.md +9 -4
- package/skills/meta/clio-coder-test/references/harness.md +100 -124
- package/skills/meta/clio-coder-test/references/test-map.md +77 -50
- package/skills/meta/credentials/SKILL.md +2 -2
- package/skills/meta/find-skills/SKILL.md +2 -2
- package/skills/meta/herdr/SKILL.md +2 -2
- package/skills/meta/skill-craft/SKILL.md +22 -16
- package/skills/planning/archify/SKILL.md +196 -0
- package/skills/planning/archify/evals.md +65 -0
- package/skills/planning/architecture/SKILL.md +62 -13
- package/skills/planning/architecture/evals.md +65 -0
- package/skills/planning/backlog/SKILL.md +131 -15
- package/skills/planning/backlog/evals.md +142 -0
- package/skills/planning/prd/SKILL.md +47 -7
- package/skills/planning/prd/evals.md +54 -0
- package/skills/planning/product-intent/SKILL.md +58 -3
- package/skills/planning/product-intent/evals.md +70 -0
- package/skills/planning/tech-spec/SKILL.md +54 -3
- package/skills/planning/tech-spec/evals.md +73 -0
- package/skills/registry.yaml +70 -62
- package/skills/remote.yaml +13 -0
- package/skills/research/arxiv-literature/SKILL.md +77 -19
- package/skills/research/arxiv-literature/evals.md +50 -0
- package/skills/research/experiment-protocol/SKILL.md +21 -2
- package/skills/research/experiment-protocol/evals.md +23 -0
- package/skills/research/scientific-debugging/SKILL.md +24 -2
- package/skills/research/scientific-debugging/evals.md +18 -0
- package/skills/research/scientific-modernization/SKILL.md +27 -2
- package/skills/research/scientific-modernization/evals.md +27 -0
- package/skills/skill-marketplace.json +97 -62
- package/skills/workflow/cut-it/SKILL.md +66 -6
- package/skills/workflow/cut-it/evals.md +101 -0
- package/skills/workflow/design-council/SKILL.md +118 -28
- package/skills/workflow/design-council/evals.md +161 -0
- package/skills/workflow/grill-me/SKILL.md +87 -11
- package/skills/workflow/grill-me/evals.md +153 -0
- package/skills/workflow/workflow-distiller/SKILL.md +77 -18
- package/skills/workflow/workflow-distiller/evals.md +118 -0
- package/src/cli/args.ts +2 -2
- package/src/cli/bootstrap-generate.ts +1 -1
- package/src/cli/config-inspect.ts +65 -12
- package/src/cli/configure-interop.ts +105 -13
- package/src/cli/configure-oauth.ts +57 -0
- package/src/cli/configure-onboarding.ts +980 -0
- package/src/cli/configure-target.ts +594 -0
- package/src/cli/configure.ts +1082 -532
- package/src/cli/context-map.ts +114 -0
- package/src/cli/context.ts +4 -0
- package/src/cli/docs.ts +22 -14
- package/src/cli/doctor-naming.ts +5 -5
- package/src/cli/doctor-toolchain.ts +3 -3
- package/src/cli/eval.ts +1 -2
- package/src/cli/extensions.ts +2 -1
- package/src/cli/fleet.ts +1 -1
- package/src/cli/index.ts +3 -1
- package/src/cli/internal-dispatch.ts +3 -4
- package/src/cli/lifecycle-presenter.ts +436 -0
- package/src/cli/models.ts +10 -2
- package/src/cli/modes/print.ts +5 -1
- package/src/cli/panes.ts +19 -5
- package/src/cli/reset.ts +228 -106
- package/src/cli/run.ts +9 -4
- package/src/cli/select.ts +664 -0
- package/src/cli/share.ts +5 -1
- package/src/cli/skills-eval.ts +3 -3
- package/src/cli/skills.ts +9 -2
- package/src/cli/targets.ts +5 -6
- package/src/cli/trace.ts +55 -4
- package/src/cli/uninstall.ts +233 -165
- package/src/cli/upgrade.ts +204 -149
- package/src/cli/usage.ts +86 -27
- package/src/cli/validate-model.ts +3 -3
- package/src/cli/wiki-generate.ts +1 -1
- package/src/core/artifact-paths.ts +1 -1
- package/src/core/bash-exec.ts +131 -86
- package/src/core/bus-events.ts +51 -6
- package/src/core/config.ts +61 -1
- package/src/core/defaults.ts +7 -4
- package/src/core/dispatch-outcome.ts +16 -0
- package/src/core/external-diagnostic.ts +44 -0
- package/src/core/gateway-routing.ts +157 -0
- package/src/core/guardrails.ts +10 -49
- package/src/core/prompt-hint.ts +9 -0
- package/src/core/safe-exec.ts +17 -2
- package/src/core/skill-activation.ts +89 -2
- package/src/domains/agents/builtins/architect.md +2 -3
- package/src/domains/agents/builtins/coder.md +3 -2
- package/src/domains/agents/builtins/debugger.md +2 -2
- package/src/domains/agents/builtins/documenter.md +2 -2
- package/src/domains/agents/builtins/git-master.md +1 -1
- package/src/domains/agents/builtins/oracle.md +1 -1
- package/src/domains/agents/builtins/provenance.md +1 -1
- package/src/domains/agents/builtins/researcher.md +1 -1
- package/src/domains/agents/builtins/scout.md +1 -1
- package/src/domains/agents/builtins/tester.md +2 -2
- package/src/domains/agents/builtins/verifier.md +2 -2
- package/src/domains/agents/builtins/wiki-writer.md +1 -1
- package/src/domains/agents/builtins/world-knowledge.md +31 -0
- package/src/domains/agents/catalog.ts +13 -15
- package/src/domains/agents/contract.ts +2 -0
- package/src/domains/agents/extension.ts +23 -1
- package/src/domains/agents/result-contract.ts +70 -0
- package/src/domains/config/keybindings.ts +8 -0
- package/src/domains/context/extension.ts +0 -3
- package/src/domains/context/wiki/map-seed.ts +589 -0
- package/src/domains/context/wiki/plan.ts +2 -2
- package/src/domains/context/working-set/path-index.ts +1 -0
- package/src/domains/dispatch/admission.ts +29 -0
- package/src/domains/dispatch/agent-candidates.ts +10 -0
- package/src/domains/dispatch/budget-envelope.ts +86 -1
- package/src/domains/dispatch/capability-match.ts +11 -0
- package/src/domains/dispatch/capacity-lease.ts +17 -0
- package/src/domains/dispatch/contract.ts +11 -1
- package/src/domains/dispatch/extension.ts +237 -49
- package/src/domains/dispatch/host-verification.ts +435 -39
- package/src/domains/dispatch/intent-requirements.ts +10 -0
- package/src/domains/dispatch/intent.ts +18 -1
- package/src/domains/dispatch/path-scope.ts +235 -24
- package/src/domains/dispatch/run-event-journal.ts +4 -15
- package/src/domains/dispatch/state.ts +2 -3
- package/src/domains/dispatch/transport.ts +45 -21
- package/src/domains/dispatch/types.ts +58 -3
- package/src/domains/dispatch/worker-model-metadata.ts +38 -0
- package/src/domains/eval/artifacts/store.ts +5 -0
- package/src/domains/eval/metrics/call-ledger-stream.ts +34 -11
- package/src/domains/eval/metrics/token-stream.ts +201 -31
- package/src/domains/eval/metrics/tracked.ts +40 -4
- package/src/domains/eval/runners/clio-run.ts +5 -2
- package/src/domains/eval/schema/suite.ts +28 -0
- package/src/domains/eval/schema/verdict.ts +2 -2
- package/src/domains/eval/store.ts +8 -1
- package/src/domains/eval/suites/resolve.ts +13 -1
- package/src/domains/eval/suites/run.ts +24 -3
- package/src/domains/evidence/trust-status.ts +10 -1
- package/src/domains/extensions/contract.ts +15 -1
- package/src/domains/extensions/discovery.ts +238 -41
- package/src/domains/extensions/extension.ts +105 -6
- package/src/domains/extensions/index.ts +24 -0
- package/src/domains/extensions/integrity.ts +189 -0
- package/src/domains/extensions/manager.ts +17 -1
- package/src/domains/extensions/resource-path.ts +27 -0
- package/src/domains/extensions/resources.ts +18 -38
- package/src/domains/extensions/snapshot-store.ts +39 -0
- package/src/domains/extensions/snapshot.ts +180 -0
- package/src/domains/extensions/state.ts +385 -57
- package/src/domains/extensions/types.ts +118 -1
- package/src/domains/interop/registry.ts +6 -2
- package/src/domains/interop/types.ts +4 -0
- package/src/domains/lifecycle/migrations/2026-09-01-extension-install-digests.ts +27 -0
- package/src/domains/lifecycle/migrations/index.ts +6 -0
- package/src/domains/lifecycle/naming-resources.ts +19 -4
- package/src/domains/lifecycle/naming-yazi.ts +10 -5
- package/src/domains/memory/task-memory-policy.ts +70 -26
- package/src/domains/memory/task-memory-telemetry.ts +1 -0
- package/src/domains/middleware/contract.ts +26 -0
- package/src/domains/middleware/extension.ts +24 -24
- package/src/domains/middleware/hook-receipts.ts +27 -4
- package/src/domains/middleware/hooks-io.ts +65 -32
- package/src/domains/middleware/hooks.ts +64 -0
- package/src/domains/middleware/index.ts +28 -5
- package/src/domains/middleware/marketplace-offer.ts +3 -35
- package/src/domains/middleware/memory-intervention.ts +127 -32
- package/src/domains/middleware/memory-step-endpoint.ts +3 -2
- package/src/domains/middleware/registrations.ts +326 -0
- package/src/domains/middleware/runtime.ts +28 -0
- package/src/domains/middleware/skills-reminder.ts +31 -2
- package/src/domains/middleware/snapshot.ts +20 -7
- package/src/domains/mux/contract.ts +38 -0
- package/src/domains/mux/detect.ts +6 -13
- package/src/domains/mux/index.ts +1 -1
- package/src/domains/mux/operations.ts +44 -5
- package/src/domains/mux/yazi/assets/yazi.toml +2 -2
- package/src/domains/mux/yazi/session.ts +53 -4
- package/src/domains/mux/yazi/theme.ts +117 -17
- package/src/domains/observability/compaction-usage.ts +118 -0
- package/src/domains/observability/contract.ts +10 -11
- package/src/domains/observability/cost.ts +1 -1
- package/src/domains/observability/extension.ts +17 -4
- package/src/domains/observability/out-of-turn-usage.ts +52 -21
- package/src/domains/observability/projection.ts +14 -90
- package/src/domains/observability/trace-store.ts +43 -7
- package/src/domains/prompts/compiler.ts +73 -53
- package/src/domains/prompts/contract.ts +15 -3
- package/src/domains/prompts/extension.ts +97 -9
- package/src/domains/prompts/fragments/identity/clio-worker.md +1 -3
- package/src/domains/prompts/fragments/identity/clio.md +6 -12
- package/src/domains/prompts/fragments/identity/docs-routing.md +1 -2
- package/src/domains/prompts/fragments/identity/self-awareness.md +3 -11
- package/src/domains/prompts/fragments/operating/contract.md +7 -15
- package/src/domains/prompts/fragments/operating/delegation.md +32 -34
- package/src/domains/prompts/fragments/operating/skills.md +10 -24
- package/src/domains/prompts/fragments/operating/worker.md +1 -8
- package/src/domains/providers/contract.ts +4 -1
- package/src/domains/providers/extension.ts +40 -9
- package/src/domains/providers/index.ts +1 -1
- package/src/domains/providers/model-capabilities.ts +9 -0
- package/src/domains/providers/model-discovery.ts +2 -0
- package/src/domains/providers/model-runtime-capabilities.ts +99 -25
- package/src/domains/providers/models/local-models/clio-coder-local-coding-targets.yaml +699 -114
- package/src/domains/providers/runtime-resolution.ts +31 -0
- package/src/domains/providers/runtimes/antigravity/antigravity-code.ts +225 -45
- package/src/domains/providers/runtimes/common/lmstudio-http.ts +6 -2
- package/src/domains/providers/runtimes/common/local-synth.ts +2 -0
- package/src/domains/providers/runtimes/common/probe-helpers.ts +7 -2
- package/src/domains/providers/runtimes/local-native/llamacpp.ts +9 -1
- package/src/domains/providers/runtimes/protocol/litellm.ts +119 -29
- package/src/domains/providers/support.ts +11 -5
- package/src/domains/providers/target-model-cache.ts +25 -2
- package/src/domains/providers/types/capability-flags.ts +2 -0
- package/src/domains/providers/types/cost-provenance.ts +19 -0
- package/src/domains/providers/types/local-model-quirks.ts +85 -37
- package/src/domains/providers/types/runtime-descriptor.ts +20 -1
- package/src/domains/providers/types/target-descriptor.ts +19 -0
- package/src/domains/resources/index.ts +3 -0
- package/src/domains/resources/skills/install.ts +72 -7
- package/src/domains/resources/skills/loader.ts +23 -19
- package/src/domains/resources/skills/marketplace.ts +63 -11
- package/src/domains/safety/autonomy.ts +15 -0
- package/src/domains/safety/call-target.ts +1 -1
- package/src/domains/safety/index.ts +1 -0
- package/src/domains/safety/loop-detector.ts +7 -4
- package/src/domains/safety/path-policy.ts +1 -1
- package/src/domains/safety/policy-engine.ts +34 -11
- package/src/domains/safety/protected-artifacts.ts +191 -88
- package/src/domains/safety/run-effects.ts +2 -22
- package/src/domains/safety/skill-authority.ts +55 -0
- package/src/domains/session/compaction/compact.ts +72 -22
- package/src/domains/session/entries.ts +6 -0
- package/src/domains/session/task-board.ts +10 -9
- package/src/domains/session/usage.ts +3 -3
- package/src/domains/share/archive.ts +164 -7
- package/src/engine/acp/server.ts +62 -9
- package/src/engine/agent.ts +13 -3
- package/src/engine/ai.ts +26 -8
- package/src/engine/antigravity/subprocess-runtime.ts +386 -120
- package/src/engine/api-registry.ts +3 -0
- package/src/engine/apis/llamacpp-residency.ts +3 -4
- package/src/engine/apis/lmstudio.ts +3 -3
- package/src/engine/apis/ollama-native.ts +6 -6
- package/src/engine/apis/openai-completions.ts +145 -39
- package/src/engine/apis/output-budget.ts +8 -18
- package/src/engine/apis/residency.ts +8 -27
- package/src/engine/external-subprocess.ts +114 -6
- package/src/engine/gemma-channel-filter.ts +19 -0
- package/src/engine/loop-guard.ts +92 -12
- package/src/engine/worker-runtime.ts +40 -11
- package/src/engine/worker-tools.ts +3 -1
- package/src/entry/background-model-metadata.ts +18 -0
- package/src/entry/compaction-prompt.ts +57 -0
- package/src/entry/extension-hook-sources.ts +28 -0
- package/src/entry/extension-reload.ts +309 -0
- package/src/entry/orchestrator.ts +464 -251
- package/src/entry/task-memory-lifecycle.ts +35 -0
- package/src/interactive/application-controller.ts +2 -1
- package/src/interactive/bus-notices.ts +8 -1
- package/src/interactive/chat-loop-messages.ts +16 -17
- package/src/interactive/chat-loop.ts +75 -3
- package/src/interactive/chat-panel.ts +36 -13
- package/src/interactive/chat-renderer.ts +72 -7
- package/src/interactive/cost-overlay.ts +26 -2
- package/src/interactive/dispatch-board.ts +6 -11
- package/src/interactive/footer/widgets.ts +13 -0
- package/src/interactive/interactive-application.ts +39 -4
- package/src/interactive/interactive-input-runtime.ts +4 -0
- package/src/interactive/interactive-presentation.ts +2 -2
- package/src/interactive/interactive-slash-runtime.ts +4 -1
- package/src/interactive/overlays/extensions.ts +9 -1
- package/src/interactive/overlays/help-reference.ts +13 -0
- package/src/interactive/overlays/settings.ts +27 -16
- package/src/interactive/panes-runtime.ts +111 -35
- package/src/interactive/prompt-cache-identity.ts +88 -0
- package/src/interactive/renderers/worker-entry.ts +32 -0
- package/src/interactive/slash-commands.ts +153 -20
- package/src/interactive/stream-pacing-policy.ts +0 -23
- package/src/interactive/theme/labels.ts +19 -13
- package/src/interactive/turn-context.ts +39 -20
- package/src/interactive/turn-recovery.ts +8 -0
- package/src/interactive/turn-runtime.ts +27 -11
- package/src/interactive/turn-state.ts +7 -0
- package/src/interactive/worker-receipts.ts +1 -0
- package/src/interactive/worker-stream.ts +6 -1
- package/src/interactive/yazi-bridge.ts +60 -6
- package/src/tools/agent-tools.ts +30 -1
- package/src/tools/artifact.ts +2 -2
- package/src/tools/ask-user.ts +3 -3
- package/src/tools/bash.ts +1 -1
- package/src/tools/bootstrap.ts +4 -0
- package/src/tools/builtin-tool-catalog.ts +52 -22
- package/src/tools/codewiki/code-nav-surface.ts +6 -0
- package/src/tools/codewiki/code-nav.ts +99 -13
- package/src/tools/context/docs-engine.ts +20 -7
- package/src/tools/context/index.ts +59 -21
- package/src/tools/core-bootstrap.ts +28 -6
- package/src/tools/credential-present.ts +1 -2
- package/src/tools/dispatch-arguments.ts +6 -1
- package/src/tools/dispatch-event-text.ts +10 -0
- package/src/tools/dispatch-plan.ts +49 -4
- package/src/tools/dispatch-run-events.ts +1 -1
- package/src/tools/dispatch-runner.ts +12 -0
- package/src/tools/dispatch-schema.ts +338 -0
- package/src/tools/dispatch-types.ts +3 -0
- package/src/tools/dispatch.ts +9 -254
- package/src/tools/ledger.ts +3 -5
- package/src/tools/monitor-surface.ts +5 -13
- package/src/tools/observation.ts +4 -5
- package/src/tools/panes-surface.ts +4 -11
- package/src/tools/panes.ts +4 -2
- package/src/tools/policy.ts +15 -2
- package/src/tools/read.ts +5 -6
- package/src/tools/registry.ts +41 -12
- package/src/tools/result-shaping.ts +18 -14
- package/src/tools/steer-surface.ts +1 -1
- package/src/tools/tasks.ts +1 -1
- package/src/tools/truncate.ts +6 -5
- package/src/tools/verify/surface.ts +6 -12
- package/src/tools/web-fetch-surface.ts +1 -3
- package/src/tools/worker-evidence.ts +3 -1
- package/src/worker/spec-contract.ts +4 -0
- package/dist/builtins-UJLMOVOV.js +0 -17
- package/dist/chunk-5QIAJV2D.js +0 -48
- package/dist/chunk-JZWT5J3Y.js +0 -814
- package/dist/chunk-K7VKOLQQ.js +0 -15
- package/dist/chunk-PMZCIOCJ.js +0 -25
- package/dist/chunk-SUW5DORT.js +0 -819
- package/dist/chunk-UOV2BYIW.js +0 -107
- package/dist/chunk-WR6U3OVP.js +0 -45
- package/dist/chunk-Y45G3AXC.js +0 -1558
- package/dist/reset-EOLM7GVE.js +0 -230
- package/dist/uninstall-N34PCTGJ.js +0 -331
- package/dist/upgrade-H7TOM7YL.js +0 -323
- package/docs/artifact-versions.md +0 -67
- package/docs/development-pipeline.md +0 -121
- package/docs/documentation-coverage.md +0 -46
- package/docs/documentation-guide.md +0 -167
- package/docs/time-conventions.md +0 -101
package/src/cli/configure.ts
CHANGED
|
@@ -1,40 +1,44 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
1
2
|
import { stdin as input, stdout as output } from "node:process";
|
|
2
3
|
import { createInterface } from "node:readline/promises";
|
|
3
4
|
import {
|
|
5
|
+
bindAgentProfileInSettings,
|
|
4
6
|
type ClioSettings,
|
|
5
7
|
readSettings,
|
|
6
8
|
removeTargetFromSettings,
|
|
7
9
|
settingsPath,
|
|
8
10
|
updateSettings,
|
|
9
11
|
} from "../core/config.js";
|
|
12
|
+
import {
|
|
13
|
+
type AutonomyLevel,
|
|
14
|
+
type OutputVerbosity,
|
|
15
|
+
type PanesSettings,
|
|
16
|
+
type SmoothStreaming,
|
|
17
|
+
THINKING_LEVELS,
|
|
18
|
+
type ThinkingLevel,
|
|
19
|
+
type TuiMode,
|
|
20
|
+
type WorkerPermissionMode,
|
|
21
|
+
} from "../core/defaults.js";
|
|
10
22
|
import { initializeClioHome } from "../core/init.js";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
23
|
+
import { resolveClioDirs } from "../core/xdg.js";
|
|
24
|
+
import { getVersionInfo } from "../domains/lifecycle/version.js";
|
|
25
|
+
import { openAuthStorage, targetRequiresAuth } from "../domains/providers/auth/index.js";
|
|
14
26
|
import {
|
|
15
27
|
buildProviderSupportEntry,
|
|
16
28
|
configuredTargetsForRuntime,
|
|
17
29
|
describeRuntimeModels,
|
|
18
30
|
isOrchestratorEligibleRuntime,
|
|
19
|
-
listKnownModelsForRuntime,
|
|
20
31
|
listProviderSupportEntries,
|
|
21
32
|
type ProviderSupportEntry,
|
|
22
|
-
readTargetModelSnapshot,
|
|
23
33
|
recordTargetModelSnapshot,
|
|
24
34
|
resolveRuntimeAuthTarget,
|
|
25
35
|
supportGroupLabel,
|
|
26
36
|
} from "../domains/providers/index.js";
|
|
27
|
-
import { probeCapabilitiesForModel } from "../domains/providers/model-capabilities.js";
|
|
28
37
|
import { fingerprintNativeRuntime } from "../domains/providers/probe/fingerprint.js";
|
|
29
38
|
import { getRuntimeRegistry } from "../domains/providers/registry.js";
|
|
30
39
|
import { registerBuiltinRuntimes } from "../domains/providers/runtimes/builtins.js";
|
|
31
40
|
import { greetLmStudio } from "../domains/providers/runtimes/common/lmstudio-http.js";
|
|
32
|
-
import type {
|
|
33
|
-
ProbeContext,
|
|
34
|
-
ProbeModelStatus,
|
|
35
|
-
ProbeResult,
|
|
36
|
-
RuntimeDescriptor,
|
|
37
|
-
} from "../domains/providers/types/runtime-descriptor.js";
|
|
41
|
+
import type { ProbeResult, RuntimeDescriptor } from "../domains/providers/types/runtime-descriptor.js";
|
|
38
42
|
import type { TargetDescriptor } from "../domains/providers/types/target-descriptor.js";
|
|
39
43
|
import { registerClioOAuthProviders } from "../engine/oauth.js";
|
|
40
44
|
import { ask, askYesNo } from "./ask.js";
|
|
@@ -47,22 +51,57 @@ import {
|
|
|
47
51
|
matchCategoryChoice,
|
|
48
52
|
type RuntimeListRow,
|
|
49
53
|
} from "./configure-layout.js";
|
|
50
|
-
import {
|
|
51
|
-
import {
|
|
54
|
+
import { loginOAuthRuntime } from "./configure-oauth.js";
|
|
55
|
+
import { canRunOnboarding, runOnboardingWizard } from "./configure-onboarding.js";
|
|
56
|
+
import {
|
|
57
|
+
applyTarget,
|
|
58
|
+
assertOrchestratorReplacementEligible,
|
|
59
|
+
buildDescriptor,
|
|
60
|
+
defaultUrlFor,
|
|
61
|
+
deriveTargetId,
|
|
62
|
+
describeAuthStatus,
|
|
63
|
+
normalizeUrl,
|
|
64
|
+
PROTOCOL_COMPAT_RUNTIME_IDS,
|
|
65
|
+
probeLines,
|
|
66
|
+
railPrefix,
|
|
67
|
+
resolveSupportedWireModels,
|
|
68
|
+
runtimeProbe,
|
|
69
|
+
runtimesForCategory,
|
|
70
|
+
setBackgroundPointer,
|
|
71
|
+
setOrchestratorPointer,
|
|
72
|
+
setWorkerDefaultPointer,
|
|
73
|
+
setWorkerProfilePointer,
|
|
74
|
+
validateContextWindowOverride,
|
|
75
|
+
validateResolvedModel,
|
|
76
|
+
type WireModelInventory,
|
|
77
|
+
warnUndiscoveredContextWindow,
|
|
78
|
+
} from "./configure-target.js";
|
|
79
|
+
import { createLifecyclePresenter, type LifecyclePresenter, shortenPath } from "./lifecycle-presenter.js";
|
|
80
|
+
import { canSelect, promptSelect } from "./select.js";
|
|
52
81
|
import { credentialWriteFailed, printError, printOk, printPlaintextCredentialWarning } from "./shared.js";
|
|
53
82
|
import { terminalColumns, wrapPlain } from "./text-layout.js";
|
|
54
|
-
import { type LiveModelInventory, validateModelChoice } from "./validate-model.js";
|
|
55
83
|
|
|
56
84
|
const HELP = `clio-coder configure
|
|
57
85
|
|
|
58
|
-
Configure model targets for chat and fleet
|
|
86
|
+
Configure model targets and runtime settings for chat and fleet dispatch.
|
|
59
87
|
|
|
60
88
|
Usage:
|
|
61
|
-
clio-coder configure interactive wizard
|
|
89
|
+
clio-coder configure interactive configuration wizard
|
|
90
|
+
clio-coder configure --section <name> open one section directly:
|
|
91
|
+
targets, models, chat, fleet,
|
|
92
|
+
permissions, panes, skills, diagnostics
|
|
93
|
+
Without a terminal this prints the
|
|
94
|
+
section's values and exits.
|
|
95
|
+
clio-coder configure --json emit the effective settings as JSON
|
|
62
96
|
clio-coder configure --interop review detected coding agents as delegation peers
|
|
63
97
|
clio-coder configure --list list target runtimes (user-facing only)
|
|
64
98
|
clio-coder configure --list --all list every registered runtime including aliases
|
|
65
99
|
clio-coder configure --id <targetId> [flags] --runtime <runtimeId>
|
|
100
|
+
clio-coder configure --help, -h show this message
|
|
101
|
+
|
|
102
|
+
In the interactive menus: arrow keys move, enter opens, escape goes back, q
|
|
103
|
+
quits. Where the terminal cannot support that, the same menus are numbered and
|
|
104
|
+
answered by typing a number, b for back, or q to quit.
|
|
66
105
|
|
|
67
106
|
Non-interactive flags:
|
|
68
107
|
--id <targetId> target id to register (required when non-interactive)
|
|
@@ -75,6 +114,7 @@ Non-interactive flags:
|
|
|
75
114
|
(mutually exclusive with --agent-profile)
|
|
76
115
|
--agent-profile <name> save this target as a named fleet profile
|
|
77
116
|
--agent-profile-model <id> model to use for --agent-profile
|
|
117
|
+
--bind-agent <agentId> bind this --agent-profile to an agent
|
|
78
118
|
--api-key-env <VAR> read API key from this env var at call time
|
|
79
119
|
--api-key <literal> store API key in credentials.yaml
|
|
80
120
|
--force save a model outside the runtime catalog, or one the
|
|
@@ -90,30 +130,14 @@ Non-interactive flags:
|
|
|
90
130
|
--reasoning <true|false> capability override
|
|
91
131
|
`;
|
|
92
132
|
|
|
93
|
-
const DEFAULT_PORTS: Record<string, number> = {
|
|
94
|
-
llamacpp: 8080,
|
|
95
|
-
"llamacpp-anthropic": 8080,
|
|
96
|
-
"llamacpp-completion": 8080,
|
|
97
|
-
"llamacpp-embed": 8080,
|
|
98
|
-
"llamacpp-rerank": 8080,
|
|
99
|
-
"lmstudio-native": 1234,
|
|
100
|
-
lmstudio: 1234,
|
|
101
|
-
"ollama-native": 11434,
|
|
102
|
-
vllm: 8000,
|
|
103
|
-
sglang: 30000,
|
|
104
|
-
"lemonade-anthropic": 8000,
|
|
105
|
-
lemonade: 8000,
|
|
106
|
-
"anthropic-compat": 8000,
|
|
107
|
-
"openai-compat": 8000,
|
|
108
|
-
litellm: 4000,
|
|
109
|
-
};
|
|
110
|
-
|
|
111
133
|
interface ParsedArgs {
|
|
112
134
|
positional: string[];
|
|
113
135
|
help: boolean;
|
|
114
136
|
list: boolean;
|
|
115
137
|
all: boolean;
|
|
116
138
|
interop: boolean;
|
|
139
|
+
json: boolean;
|
|
140
|
+
section?: string;
|
|
117
141
|
remove?: string;
|
|
118
142
|
renameOld?: string;
|
|
119
143
|
renameNew?: string;
|
|
@@ -126,6 +150,7 @@ interface ParsedArgs {
|
|
|
126
150
|
workerModel?: string;
|
|
127
151
|
workerProfile?: string;
|
|
128
152
|
workerProfileModel?: string;
|
|
153
|
+
bindAgent?: string;
|
|
129
154
|
apiKeyEnv?: string;
|
|
130
155
|
apiKey?: string;
|
|
131
156
|
force: boolean;
|
|
@@ -146,6 +171,7 @@ function parseSetupArgs(argv: ReadonlyArray<string>): ParsedArgs {
|
|
|
146
171
|
list: false,
|
|
147
172
|
all: false,
|
|
148
173
|
interop: false,
|
|
174
|
+
json: false,
|
|
149
175
|
force: false,
|
|
150
176
|
gateway: false,
|
|
151
177
|
setOrchestrator: false,
|
|
@@ -165,6 +191,12 @@ function parseSetupArgs(argv: ReadonlyArray<string>): ParsedArgs {
|
|
|
165
191
|
case "-h":
|
|
166
192
|
out.help = true;
|
|
167
193
|
break;
|
|
194
|
+
case "--json":
|
|
195
|
+
out.json = true;
|
|
196
|
+
break;
|
|
197
|
+
case "--section":
|
|
198
|
+
out.section = need();
|
|
199
|
+
break;
|
|
168
200
|
case "--list":
|
|
169
201
|
out.list = true;
|
|
170
202
|
break;
|
|
@@ -200,17 +232,17 @@ function parseSetupArgs(argv: ReadonlyArray<string>): ParsedArgs {
|
|
|
200
232
|
out.backgroundModel = need();
|
|
201
233
|
break;
|
|
202
234
|
case "--fleet-model":
|
|
203
|
-
case "--worker-model":
|
|
204
235
|
out.workerModel = need();
|
|
205
236
|
break;
|
|
206
237
|
case "--agent-profile":
|
|
207
|
-
case "--worker-profile":
|
|
208
238
|
out.workerProfile = need();
|
|
209
239
|
break;
|
|
210
240
|
case "--agent-profile-model":
|
|
211
|
-
case "--worker-profile-model":
|
|
212
241
|
out.workerProfileModel = need();
|
|
213
242
|
break;
|
|
243
|
+
case "--bind-agent":
|
|
244
|
+
out.bindAgent = need();
|
|
245
|
+
break;
|
|
214
246
|
case "--api-key-env":
|
|
215
247
|
out.apiKeyEnv = need();
|
|
216
248
|
break;
|
|
@@ -238,7 +270,6 @@ function parseSetupArgs(argv: ReadonlyArray<string>): ParsedArgs {
|
|
|
238
270
|
out.setBackground = true;
|
|
239
271
|
break;
|
|
240
272
|
case "--set-fleet-default":
|
|
241
|
-
case "--set-worker-default":
|
|
242
273
|
out.setWorkerDefault = true;
|
|
243
274
|
break;
|
|
244
275
|
case "--context-window": {
|
|
@@ -273,32 +304,6 @@ function ensureRegistryPopulated(): void {
|
|
|
273
304
|
if (registry.list().length === 0) registerBuiltinRuntimes(registry);
|
|
274
305
|
}
|
|
275
306
|
|
|
276
|
-
function trimTrailing(url: string): string {
|
|
277
|
-
return url.endsWith("/") && url.length > 1 ? url.slice(0, -1) : url;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
function normalizeUrl(input: string, runtimeId: string): string {
|
|
281
|
-
const trimmed = input.trim();
|
|
282
|
-
if (trimmed.length === 0) return defaultUrlFor(runtimeId);
|
|
283
|
-
const hasScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed);
|
|
284
|
-
let withScheme = hasScheme ? trimmed : `http://${trimmed}`;
|
|
285
|
-
if (runtimeId === "lmstudio") withScheme = withScheme.replace(/^ws:/u, "http:").replace(/^wss:/u, "https:");
|
|
286
|
-
try {
|
|
287
|
-
const parsed = new URL(withScheme);
|
|
288
|
-
if (!hasScheme && !parsed.port && DEFAULT_PORTS[runtimeId]) {
|
|
289
|
-
parsed.port = String(DEFAULT_PORTS[runtimeId]);
|
|
290
|
-
}
|
|
291
|
-
return trimTrailing(parsed.toString());
|
|
292
|
-
} catch {
|
|
293
|
-
return trimTrailing(withScheme);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
function defaultUrlFor(runtimeId: string): string {
|
|
298
|
-
const port = DEFAULT_PORTS[runtimeId];
|
|
299
|
-
return port ? `http://127.0.0.1:${port}` : "http://127.0.0.1:8080";
|
|
300
|
-
}
|
|
301
|
-
|
|
302
307
|
const RUNTIME_LIST_CAPTION =
|
|
303
308
|
"every registered runtime. run `clio-coder auth list` for the ones clio authenticates itself; the rest authenticate through their own tool (claude-cli, aws-sdk) or need no credential.";
|
|
304
309
|
|
|
@@ -346,256 +351,6 @@ function printRuntimeList(includeHidden: boolean): void {
|
|
|
346
351
|
}
|
|
347
352
|
}
|
|
348
353
|
|
|
349
|
-
function deriveTargetId(runtimeId: string, existing: ReadonlyArray<TargetDescriptor>): string {
|
|
350
|
-
const base = runtimeId;
|
|
351
|
-
const taken = new Set(existing.map((e) => e.id));
|
|
352
|
-
if (!taken.has(base)) return base;
|
|
353
|
-
for (let i = 2; i < 1000; i++) {
|
|
354
|
-
const candidate = `${base}-${i}`;
|
|
355
|
-
if (!taken.has(candidate)) return candidate;
|
|
356
|
-
}
|
|
357
|
-
return `${base}-${Date.now()}`;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
async function buildProbeContext(runtime?: RuntimeDescriptor, target?: TargetDescriptor): Promise<ProbeContext> {
|
|
361
|
-
const probeCtx: ProbeContext = {
|
|
362
|
-
credentialsPresent: credentialsPresent(),
|
|
363
|
-
httpTimeoutMs: 5000,
|
|
364
|
-
};
|
|
365
|
-
if (!runtime || !target || !targetRequiresAuth(target, runtime)) return probeCtx;
|
|
366
|
-
try {
|
|
367
|
-
const resolution = await openAuthStorage().resolveForTarget(resolveAuthTarget(target, runtime), {
|
|
368
|
-
includeFallback: false,
|
|
369
|
-
});
|
|
370
|
-
if (resolution.apiKey) probeCtx.authToken = resolution.apiKey;
|
|
371
|
-
} catch {
|
|
372
|
-
// Let the runtime's probe report its own missing-auth diagnostic.
|
|
373
|
-
}
|
|
374
|
-
return probeCtx;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
function runtimeKnownModelsFor(runtimeId: string): ReadonlyArray<string> {
|
|
378
|
-
return listKnownModelsForRuntime(runtimeId);
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
function catalogContextWindowFor(runtime: RuntimeDescriptor, modelId: string): number | null {
|
|
382
|
-
const catalogModel = getCatalogModelForRuntime(runtime.id, modelId);
|
|
383
|
-
if (typeof catalogModel?.contextWindow === "number" && catalogModel.contextWindow > 0) {
|
|
384
|
-
return catalogModel.contextWindow;
|
|
385
|
-
}
|
|
386
|
-
if (runtimeKnownModelsFor(runtime.id).includes(modelId) && runtime.defaultCapabilities.contextWindow > 0) {
|
|
387
|
-
return runtime.defaultCapabilities.contextWindow;
|
|
388
|
-
}
|
|
389
|
-
return null;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
/**
|
|
393
|
-
* Check the resolved model against the catalog where there is one and against
|
|
394
|
-
* the target's own list where there is not. A runtime with no catalog used to
|
|
395
|
-
* pass any string here while the same command had a live list that ruled it
|
|
396
|
-
* out, so the target saved as `ok` and failed on its first
|
|
397
|
-
* turn. Without a catalog or a live list there is nothing to check against, and
|
|
398
|
-
* the note says so rather than implying the id was verified.
|
|
399
|
-
*/
|
|
400
|
-
function validateResolvedModel(
|
|
401
|
-
runtime: RuntimeDescriptor,
|
|
402
|
-
target: Pick<TargetDescriptor, "id" | "url">,
|
|
403
|
-
modelId: string | undefined,
|
|
404
|
-
force: boolean,
|
|
405
|
-
inventory: WireModelInventory,
|
|
406
|
-
): boolean {
|
|
407
|
-
if (!modelId) return true;
|
|
408
|
-
const knownModels = runtimeKnownModelsFor(runtime.id);
|
|
409
|
-
const live = inventory.source === "probe" ? liveInventoryFor(target, inventory) : undefined;
|
|
410
|
-
const validation = validateModelChoice({ runtimeId: runtime.id, modelId, knownModels, live, force });
|
|
411
|
-
if (!validation.ok) {
|
|
412
|
-
process.stderr.write(`error: ${validation.reason}\n`);
|
|
413
|
-
return false;
|
|
414
|
-
}
|
|
415
|
-
if (validation.warning) process.stderr.write(`warning: ${validation.warning}\n`);
|
|
416
|
-
if (knownModels.length === 0 && live === undefined && runtime.kind === "http") {
|
|
417
|
-
process.stderr.write(
|
|
418
|
-
`warning: could not verify model '${modelId}': ${target.url ?? `target '${target.id}'`} returned no model list; it is saved as written\n`,
|
|
419
|
-
);
|
|
420
|
-
}
|
|
421
|
-
return true;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
function validateContextWindowOverride(
|
|
425
|
-
runtime: RuntimeDescriptor,
|
|
426
|
-
modelId: string | undefined,
|
|
427
|
-
contextWindow: number | undefined,
|
|
428
|
-
force: boolean,
|
|
429
|
-
): boolean {
|
|
430
|
-
if (contextWindow === undefined || !modelId) return true;
|
|
431
|
-
const catalogMax = catalogContextWindowFor(runtime, modelId);
|
|
432
|
-
if (catalogMax === null || contextWindow <= catalogMax) return true;
|
|
433
|
-
const message = `--context-window ${contextWindow} exceeds catalog max ${catalogMax} for model '${modelId}'`;
|
|
434
|
-
if (!force) {
|
|
435
|
-
process.stderr.write(`error: ${message}. Use --force to override.\n`);
|
|
436
|
-
return false;
|
|
437
|
-
}
|
|
438
|
-
process.stderr.write(`warning: ${message}\n`);
|
|
439
|
-
return true;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
async function runtimeProbe(
|
|
443
|
-
runtime: RuntimeDescriptor,
|
|
444
|
-
target: TargetDescriptor,
|
|
445
|
-
authToken?: string,
|
|
446
|
-
): Promise<ProbeResult | null> {
|
|
447
|
-
if (typeof runtime.probe !== "function") return null;
|
|
448
|
-
try {
|
|
449
|
-
const context = await buildProbeContext(runtime, target);
|
|
450
|
-
return await runtime.probe(target, authToken === undefined ? context : { ...context, authToken });
|
|
451
|
-
} catch (err) {
|
|
452
|
-
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
async function runtimeProbeModels(
|
|
457
|
-
runtime: RuntimeDescriptor,
|
|
458
|
-
target: TargetDescriptor,
|
|
459
|
-
authToken?: string,
|
|
460
|
-
): Promise<string[]> {
|
|
461
|
-
if (typeof runtime.probeModels !== "function") return [];
|
|
462
|
-
try {
|
|
463
|
-
const context = await buildProbeContext(runtime, target);
|
|
464
|
-
return await runtime.probeModels(target, authToken === undefined ? context : { ...context, authToken });
|
|
465
|
-
} catch {
|
|
466
|
-
return [];
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
function applyTarget(settings: ClioSettings, descriptor: TargetDescriptor): void {
|
|
471
|
-
const idx = settings.targets.findIndex((e) => e.id === descriptor.id);
|
|
472
|
-
if (idx >= 0) settings.targets[idx] = descriptor;
|
|
473
|
-
else settings.targets.push(descriptor);
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
function setOrchestratorPointer(settings: ClioSettings, descriptor: TargetDescriptor, model?: string | null): void {
|
|
477
|
-
const runtime = getRuntimeRegistry().get(descriptor.runtime);
|
|
478
|
-
if (!runtime) {
|
|
479
|
-
throw new Error(
|
|
480
|
-
`cannot use target '${descriptor.id}' as orchestrator target because runtime '${descriptor.runtime}' is not registered`,
|
|
481
|
-
);
|
|
482
|
-
}
|
|
483
|
-
if (!isOrchestratorEligibleRuntime(runtime)) {
|
|
484
|
-
throw new Error(
|
|
485
|
-
`cannot use target '${descriptor.id}' as orchestrator target because runtime '${runtime.id}' is not an HTTP/native runtime`,
|
|
486
|
-
);
|
|
487
|
-
}
|
|
488
|
-
settings.chat.target = descriptor.id;
|
|
489
|
-
settings.chat.model = model ?? descriptor.defaultModel ?? null;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
function setWorkerDefaultPointer(settings: ClioSettings, descriptor: TargetDescriptor, model?: string | null): void {
|
|
493
|
-
settings.fleet.default.target = descriptor.id;
|
|
494
|
-
settings.fleet.default.model = model ?? descriptor.defaultModel ?? null;
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
function setBackgroundPointer(settings: ClioSettings, descriptor: TargetDescriptor, model?: string | null): void {
|
|
498
|
-
const runtime = getRuntimeRegistry().get(descriptor.runtime);
|
|
499
|
-
if (!runtime || !isOrchestratorEligibleRuntime(runtime)) {
|
|
500
|
-
throw new Error(
|
|
501
|
-
`cannot use target '${descriptor.id}' as background target because runtime '${descriptor.runtime}' is not an HTTP/native runtime`,
|
|
502
|
-
);
|
|
503
|
-
}
|
|
504
|
-
settings.context.memory.target = descriptor.id;
|
|
505
|
-
settings.context.memory.model = model ?? descriptor.defaultModel ?? null;
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
function assertOrchestratorReplacementEligible(settings: ClioSettings, descriptor: TargetDescriptor): void {
|
|
509
|
-
if (settings.chat.target !== descriptor.id && settings.context.memory.target !== descriptor.id) return;
|
|
510
|
-
const role = settings.chat.target === descriptor.id ? "orchestrator" : "background";
|
|
511
|
-
const runtime = getRuntimeRegistry().get(descriptor.runtime);
|
|
512
|
-
if (!runtime) {
|
|
513
|
-
throw new Error(
|
|
514
|
-
`cannot update ${role} target '${descriptor.id}' because runtime '${descriptor.runtime}' is not registered`,
|
|
515
|
-
);
|
|
516
|
-
}
|
|
517
|
-
if (!isOrchestratorEligibleRuntime(runtime)) {
|
|
518
|
-
throw new Error(`cannot update ${role} target '${descriptor.id}' to non-HTTP/native runtime '${runtime.id}'`);
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
function setWorkerProfilePointer(
|
|
523
|
-
settings: ClioSettings,
|
|
524
|
-
name: string,
|
|
525
|
-
descriptor: TargetDescriptor,
|
|
526
|
-
model?: string | null,
|
|
527
|
-
): void {
|
|
528
|
-
const trimmed = name.trim();
|
|
529
|
-
if (trimmed.length === 0) throw new Error("fleet profile name must be non-empty");
|
|
530
|
-
settings.fleet.profiles[trimmed] = {
|
|
531
|
-
target: descriptor.id,
|
|
532
|
-
model: model ?? descriptor.defaultModel ?? null,
|
|
533
|
-
thinkingLevel: "off",
|
|
534
|
-
};
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
/**
|
|
538
|
-
* A target that was probed successfully and still reported no context window
|
|
539
|
-
* will run against the runtime descriptor's placeholder. Say so at the moment
|
|
540
|
-
* the target is written, because the alternative is a plausible-looking number
|
|
541
|
-
* the operator never chose and the server never claimed.
|
|
542
|
-
*/
|
|
543
|
-
function warnUndiscoveredContextWindow(descriptor: TargetDescriptor, probe: ProbeResult | null): void {
|
|
544
|
-
if (!probe?.ok) return;
|
|
545
|
-
if (typeof descriptor.capabilities?.contextWindow === "number" && descriptor.capabilities.contextWindow > 0) return;
|
|
546
|
-
const discovered = probeCapabilitiesForModel(
|
|
547
|
-
{
|
|
548
|
-
target: descriptor,
|
|
549
|
-
probeCapabilities: probe.discoveredCapabilities ?? null,
|
|
550
|
-
probeModelCapabilities: probe.modelCapabilities ?? null,
|
|
551
|
-
probeModelId: probe.capabilityModelId ?? null,
|
|
552
|
-
},
|
|
553
|
-
descriptor.defaultModel,
|
|
554
|
-
);
|
|
555
|
-
if (typeof discovered?.contextWindow === "number" && discovered.contextWindow > 0) return;
|
|
556
|
-
process.stdout.write(
|
|
557
|
-
" warning: the target reported no context window; Clio will use the runtime default as a guess. Set one with --context-window.\n",
|
|
558
|
-
);
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
/**
|
|
562
|
-
* What the probe read, as opposed to whether it connected.
|
|
563
|
-
*
|
|
564
|
-
* A health check can pass while every enrichment read behind it returns
|
|
565
|
-
* nothing, and reporting that as `probe ok` makes it indistinguishable from a
|
|
566
|
-
* full read. That is how a target gets blessed at configure time and then
|
|
567
|
-
* fails on the first turn. Naming the gap is the difference between a user who
|
|
568
|
-
* knows to look now and one who finds out from a raw 404 later.
|
|
569
|
-
*/
|
|
570
|
-
function probeReadings(probe: ProbeResult): string[] {
|
|
571
|
-
const readings: string[] = [];
|
|
572
|
-
if (probe.models && probe.models.length > 0) readings.push(`${probe.models.length} models`);
|
|
573
|
-
if (probe.serverVersion) readings.push(probe.serverVersion);
|
|
574
|
-
if (probe.discoveredCapabilities && Object.keys(probe.discoveredCapabilities).length > 0) {
|
|
575
|
-
readings.push("capabilities");
|
|
576
|
-
}
|
|
577
|
-
return readings;
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
/** The probe result as the lines a user reads, cause and next step included. */
|
|
581
|
-
function probeLines(descriptor: TargetDescriptor, probe: ProbeResult): string[] {
|
|
582
|
-
const latency = probe.latencyMs !== undefined ? ` (${probe.latencyMs}ms)` : "";
|
|
583
|
-
if (!probe.ok) {
|
|
584
|
-
const where = descriptor.url ? ` ${descriptor.url}` : "";
|
|
585
|
-
return [
|
|
586
|
-
`probe failed${latency}:${where} ${probe.error ?? "unknown"}`,
|
|
587
|
-
` check the server is running and reachable, then re-run: clio-coder configure --id ${descriptor.id} --url <url>`,
|
|
588
|
-
];
|
|
589
|
-
}
|
|
590
|
-
const readings = probeReadings(probe);
|
|
591
|
-
if (readings.length > 0) return [`probe ok${latency} ${readings.join(", ")}`];
|
|
592
|
-
return [
|
|
593
|
-
`probe reachable${latency}, but the target answered no model list and no version`,
|
|
594
|
-
" Clio cannot verify the model id or the context window from here and will send both as written.",
|
|
595
|
-
" re-read them once the server serves them with: clio-coder targets --probe",
|
|
596
|
-
];
|
|
597
|
-
}
|
|
598
|
-
|
|
599
354
|
function printSummary(settings: ClioSettings, descriptor: TargetDescriptor, probe: ProbeResult | null): void {
|
|
600
355
|
process.stdout.write(`\nsaved target ${descriptor.id} (runtime=${descriptor.runtime})\n`);
|
|
601
356
|
if (descriptor.url) process.stdout.write(` url ${descriptor.url}\n`);
|
|
@@ -616,166 +371,6 @@ function printSummary(settings: ClioSettings, descriptor: TargetDescriptor, prob
|
|
|
616
371
|
process.stdout.write(`\nsettings written to ${settingsPath()}\n`);
|
|
617
372
|
}
|
|
618
373
|
|
|
619
|
-
function buildDescriptor(
|
|
620
|
-
runtime: RuntimeDescriptor,
|
|
621
|
-
id: string,
|
|
622
|
-
parts: {
|
|
623
|
-
url?: string;
|
|
624
|
-
model?: string;
|
|
625
|
-
apiKeyEnv?: string;
|
|
626
|
-
apiKeyRef?: string;
|
|
627
|
-
oauthProfile?: string;
|
|
628
|
-
gateway?: boolean;
|
|
629
|
-
lifecycle?: TargetDescriptor["lifecycle"];
|
|
630
|
-
contextWindow?: number;
|
|
631
|
-
maxTokens?: number;
|
|
632
|
-
reasoning?: boolean;
|
|
633
|
-
lmstudio?: TargetDescriptor["lmstudio"];
|
|
634
|
-
},
|
|
635
|
-
): TargetDescriptor {
|
|
636
|
-
const descriptor: TargetDescriptor = { id, runtime: runtime.id };
|
|
637
|
-
if (parts.url) descriptor.url = parts.url;
|
|
638
|
-
if (parts.model) descriptor.defaultModel = parts.model;
|
|
639
|
-
const auth: NonNullable<TargetDescriptor["auth"]> = {};
|
|
640
|
-
if (parts.apiKeyEnv) auth.apiKeyEnvVar = parts.apiKeyEnv;
|
|
641
|
-
if (parts.apiKeyRef) auth.apiKeyRef = parts.apiKeyRef;
|
|
642
|
-
if (parts.oauthProfile) auth.oauthProfile = parts.oauthProfile;
|
|
643
|
-
if (Object.keys(auth).length > 0) descriptor.auth = auth;
|
|
644
|
-
if (parts.gateway) descriptor.gateway = true;
|
|
645
|
-
if (parts.lifecycle) descriptor.lifecycle = parts.lifecycle;
|
|
646
|
-
const caps: NonNullable<TargetDescriptor["capabilities"]> = {};
|
|
647
|
-
if (parts.contextWindow !== undefined) caps.contextWindow = parts.contextWindow;
|
|
648
|
-
if (parts.maxTokens !== undefined) caps.maxTokens = parts.maxTokens;
|
|
649
|
-
if (parts.reasoning !== undefined) caps.reasoning = parts.reasoning;
|
|
650
|
-
if (Object.keys(caps).length > 0) descriptor.capabilities = caps;
|
|
651
|
-
if (parts.lmstudio) descriptor.lmstudio = parts.lmstudio;
|
|
652
|
-
return descriptor;
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
function describeAuthStatus(runtime: RuntimeDescriptor): string {
|
|
656
|
-
const status = openAuthStorage().statusForTarget(resolveRuntimeAuthTarget(runtime), { includeFallback: false });
|
|
657
|
-
// This describes a credential, not a network state. It is printed before the
|
|
658
|
-
// wizard has asked for a URL, so `not connected` read as a connection that
|
|
659
|
-
// had already been tried and failed.
|
|
660
|
-
if (!status.available) return "none stored";
|
|
661
|
-
if (status.source === "environment") return `environment${status.detail ? ` (${status.detail})` : ""}`;
|
|
662
|
-
if (status.source === "stored-api-key") return "stored api key";
|
|
663
|
-
if (status.source === "stored-oauth") return "stored oauth";
|
|
664
|
-
return status.source;
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
async function loginOAuthRuntime(rl: ReturnType<typeof createInterface>, runtime: RuntimeDescriptor): Promise<boolean> {
|
|
668
|
-
const auth = openAuthStorage();
|
|
669
|
-
if (runtime.authNotice) process.stdout.write(`note: ${runtime.authNotice}\n`);
|
|
670
|
-
const manualCodeInput = createDelayedManualCodeInput(
|
|
671
|
-
rl,
|
|
672
|
-
"Paste verification code if browser callback does not complete automatically: ",
|
|
673
|
-
);
|
|
674
|
-
try {
|
|
675
|
-
await auth.login(runtime.oauthProviderId ?? runtime.id, {
|
|
676
|
-
onAuth: ({ url, instructions }) => {
|
|
677
|
-
process.stdout.write(`\nOpen: ${url}\n`);
|
|
678
|
-
if (instructions) process.stdout.write(`${instructions}\n`);
|
|
679
|
-
process.stdout.write("Waiting for the browser callback. A manual code prompt will appear if needed.\n");
|
|
680
|
-
},
|
|
681
|
-
onDeviceCode: ({ verificationUri, userCode }) => {
|
|
682
|
-
process.stdout.write(`\nOpen: ${verificationUri}\n`);
|
|
683
|
-
process.stdout.write(`Enter code: ${userCode}\n`);
|
|
684
|
-
},
|
|
685
|
-
onPrompt: async (prompt) => {
|
|
686
|
-
const answer = await rl.question(`${prompt.message}${prompt.allowEmpty ? " " : ": "}`);
|
|
687
|
-
return prompt.allowEmpty ? answer : answer.trim();
|
|
688
|
-
},
|
|
689
|
-
onSelect: (prompt) => promptOAuthSelection(rl, prompt),
|
|
690
|
-
onManualCodeInput: manualCodeInput.onManualCodeInput,
|
|
691
|
-
onProgress: (message) => {
|
|
692
|
-
process.stderr.write(`${message}\n`);
|
|
693
|
-
},
|
|
694
|
-
});
|
|
695
|
-
if (credentialWriteFailed(auth, `credential for ${runtime.id} was not stored`)) return false;
|
|
696
|
-
printOk(`authenticated ${runtime.id}`);
|
|
697
|
-
return true;
|
|
698
|
-
} catch (error) {
|
|
699
|
-
printError(error instanceof Error ? error.message : String(error));
|
|
700
|
-
return false;
|
|
701
|
-
} finally {
|
|
702
|
-
manualCodeInput.cancel();
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
/**
|
|
707
|
-
* The model ids a target can be configured with, and where they came from.
|
|
708
|
-
* `probe` is the only source that says anything about the server in front of
|
|
709
|
-
* us right now; `catalog` is static provider knowledge, and `existing` is the
|
|
710
|
-
* list a previous configure recorded, which the server may no longer match.
|
|
711
|
-
*/
|
|
712
|
-
interface WireModelInventory {
|
|
713
|
-
models: string[];
|
|
714
|
-
source: "catalog" | "probe" | "cache" | "legacy" | "none";
|
|
715
|
-
/** Per-model load state when the probe reported it. */
|
|
716
|
-
modelStates?: Readonly<Record<string, ProbeModelStatus>> | undefined;
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
/**
|
|
720
|
-
* Every id the runtime would resolve. LM Studio lists a loaded model under its
|
|
721
|
-
* instance id and keeps the model key only in the state map, and the request
|
|
722
|
-
* path accepts either, so both count as advertised.
|
|
723
|
-
*/
|
|
724
|
-
function advertisedModelIds(inventory: Pick<WireModelInventory, "models" | "modelStates">): string[] {
|
|
725
|
-
const ids = [...inventory.models];
|
|
726
|
-
for (const id of Object.keys(inventory.modelStates ?? {})) if (!ids.includes(id)) ids.push(id);
|
|
727
|
-
return ids;
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
function residentModelIds(modelStates: WireModelInventory["modelStates"]): string[] {
|
|
731
|
-
return Object.entries(modelStates ?? {})
|
|
732
|
-
.filter(([, status]) => status.state === "loaded" || status.state === "loading")
|
|
733
|
-
.map(([id]) => id);
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
function liveInventoryFor(
|
|
737
|
-
target: Pick<TargetDescriptor, "id" | "url">,
|
|
738
|
-
inventory: WireModelInventory,
|
|
739
|
-
): LiveModelInventory {
|
|
740
|
-
return {
|
|
741
|
-
targetId: target.id,
|
|
742
|
-
url: target.url,
|
|
743
|
-
models: advertisedModelIds(inventory),
|
|
744
|
-
resident: residentModelIds(inventory.modelStates),
|
|
745
|
-
};
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
async function resolveSupportedWireModels(
|
|
749
|
-
runtime: RuntimeDescriptor,
|
|
750
|
-
target: TargetDescriptor,
|
|
751
|
-
existing?: TargetDescriptor,
|
|
752
|
-
authToken?: string,
|
|
753
|
-
): Promise<WireModelInventory> {
|
|
754
|
-
const known = listKnownModelsForRuntime(runtime.id);
|
|
755
|
-
if (known.length > 0) return { models: known, source: "catalog" };
|
|
756
|
-
if (runtime.kind === "http") {
|
|
757
|
-
// The full probe carries load state alongside the ids; a runtime that
|
|
758
|
-
// lists models only through probeModels still gets its ids checked.
|
|
759
|
-
const probe = await runtimeProbe(runtime, target, authToken);
|
|
760
|
-
if (probe?.ok && probe.models && probe.models.length > 0) {
|
|
761
|
-
recordTargetModelSnapshot(target, probe.models);
|
|
762
|
-
return { models: [...probe.models], source: "probe", modelStates: probe.modelStates };
|
|
763
|
-
}
|
|
764
|
-
const discovered = await runtimeProbeModels(runtime, target, authToken);
|
|
765
|
-
if (discovered.length > 0) {
|
|
766
|
-
recordTargetModelSnapshot(target, discovered);
|
|
767
|
-
return { models: discovered, source: "probe" };
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
const cached = readTargetModelSnapshot(target);
|
|
771
|
-
if (cached && cached.models.length > 0) return { models: [...cached.models], source: "cache" };
|
|
772
|
-
if (existing?.wireModels && existing.wireModels.length > 0) {
|
|
773
|
-
recordTargetModelSnapshot(target, existing.wireModels);
|
|
774
|
-
return { models: [...existing.wireModels], source: "legacy" };
|
|
775
|
-
}
|
|
776
|
-
return { models: [], source: "none" };
|
|
777
|
-
}
|
|
778
|
-
|
|
779
374
|
function resolveModelChoice(
|
|
780
375
|
answer: string,
|
|
781
376
|
wireModels: ReadonlyArray<string>,
|
|
@@ -823,6 +418,10 @@ async function runNonInteractive(runtime: RuntimeDescriptor, args: ParsedArgs):
|
|
|
823
418
|
printError("--agent-profile-model requires --agent-profile");
|
|
824
419
|
return 2;
|
|
825
420
|
}
|
|
421
|
+
if (args.bindAgent !== undefined && args.workerProfile === undefined) {
|
|
422
|
+
printError("--bind-agent requires --agent-profile");
|
|
423
|
+
return 2;
|
|
424
|
+
}
|
|
826
425
|
if (args.workerProfile !== undefined && args.workerProfile.trim().length === 0) {
|
|
827
426
|
printError("--agent-profile must be non-empty");
|
|
828
427
|
return 2;
|
|
@@ -893,13 +492,14 @@ async function runNonInteractive(runtime: RuntimeDescriptor, args: ParsedArgs):
|
|
|
893
492
|
...(args.maxTokens !== undefined ? { maxTokens: args.maxTokens } : {}),
|
|
894
493
|
...(args.reasoning !== undefined ? { reasoning: args.reasoning } : {}),
|
|
895
494
|
...(existing?.lmstudio ? { lmstudio: existing.lmstudio } : {}),
|
|
495
|
+
...(existing?.litellm ? { litellm: existing.litellm } : {}),
|
|
896
496
|
});
|
|
897
497
|
const inventory = await resolveSupportedWireModels(runtime, seed, existing, args.apiKey);
|
|
898
498
|
const wireModels = inventory.models;
|
|
899
499
|
const model =
|
|
900
500
|
args.model ??
|
|
901
501
|
existing?.defaultModel ??
|
|
902
|
-
support.defaultModel ??
|
|
502
|
+
(inventory.source === "probe" ? wireModels[0] : support.defaultModel) ??
|
|
903
503
|
(support.modelSource === "catalog" ? undefined : wireModels[0]);
|
|
904
504
|
if (model === undefined && support.modelSource === "catalog") {
|
|
905
505
|
refuseCatalogSeededModel(runtime, support);
|
|
@@ -923,6 +523,7 @@ async function runNonInteractive(runtime: RuntimeDescriptor, args: ParsedArgs):
|
|
|
923
523
|
...(args.maxTokens !== undefined ? { maxTokens: args.maxTokens } : {}),
|
|
924
524
|
...(args.reasoning !== undefined ? { reasoning: args.reasoning } : {}),
|
|
925
525
|
...(existing?.lmstudio ? { lmstudio: existing.lmstudio } : {}),
|
|
526
|
+
...(existing?.litellm ? { litellm: existing.litellm } : {}),
|
|
926
527
|
});
|
|
927
528
|
const setOrchestrator = args.setOrchestrator || args.orchestratorModel !== undefined;
|
|
928
529
|
const setBackground = args.setBackground || args.backgroundModel !== undefined;
|
|
@@ -962,6 +563,7 @@ async function runNonInteractive(runtime: RuntimeDescriptor, args: ParsedArgs):
|
|
|
962
563
|
descriptor,
|
|
963
564
|
args.workerProfileModel ?? descriptor.defaultModel ?? null,
|
|
964
565
|
);
|
|
566
|
+
if (args.bindAgent !== undefined) bindAgentProfileInSettings(target, args.bindAgent, args.workerProfile);
|
|
965
567
|
}
|
|
966
568
|
};
|
|
967
569
|
// Apply to the local snapshot for the summary below, then persist the same
|
|
@@ -970,7 +572,9 @@ async function runNonInteractive(runtime: RuntimeDescriptor, args: ParsedArgs):
|
|
|
970
572
|
applyConfiguration(settings);
|
|
971
573
|
updateSettings(applyConfiguration);
|
|
972
574
|
const probe = await runtimeProbe(runtime, descriptor);
|
|
973
|
-
if (probe?.ok && probe.models)
|
|
575
|
+
if (probe?.ok && probe.models) {
|
|
576
|
+
recordTargetModelSnapshot(descriptor, probe.models, probe.modelLabels ? { modelLabels: probe.modelLabels } : {});
|
|
577
|
+
}
|
|
974
578
|
printSummary(settings, descriptor, probe);
|
|
975
579
|
if (
|
|
976
580
|
runtime.auth === "oauth" &&
|
|
@@ -1041,45 +645,6 @@ async function pickRuntimeFromEntries(
|
|
|
1041
645
|
}
|
|
1042
646
|
}
|
|
1043
647
|
|
|
1044
|
-
const LOCAL_APP_RUNTIME_IDS: ReadonlySet<string> = new Set(["ollama-native", "lmstudio"]);
|
|
1045
|
-
|
|
1046
|
-
// Generic protocol-compatible runtimes. They are classified local-http (they
|
|
1047
|
-
// carry a probe and no cloud catalog entry), but a hosted endpoint such as
|
|
1048
|
-
// Inception/Mercury is exactly an OpenAI-compatible cloud API, so the wizard
|
|
1049
|
-
// also surfaces them under the Cloud API path.
|
|
1050
|
-
const PROTOCOL_COMPAT_RUNTIME_IDS: ReadonlySet<string> = new Set(["openai-compat", "anthropic-compat"]);
|
|
1051
|
-
|
|
1052
|
-
function runtimesForCategory(
|
|
1053
|
-
entries: ReadonlyArray<ProviderSupportEntry>,
|
|
1054
|
-
category: ConfigureCategory,
|
|
1055
|
-
): ProviderSupportEntry[] {
|
|
1056
|
-
switch (category) {
|
|
1057
|
-
case "local-app":
|
|
1058
|
-
return entries.filter((entry) => LOCAL_APP_RUNTIME_IDS.has(entry.runtimeId));
|
|
1059
|
-
case "local-http":
|
|
1060
|
-
return entries.filter((entry) => entry.group === "local-http" && !LOCAL_APP_RUNTIME_IDS.has(entry.runtimeId));
|
|
1061
|
-
case "chatgpt":
|
|
1062
|
-
return entries.filter((entry) => entry.runtimeId === "openai-codex");
|
|
1063
|
-
case "cloud-api": {
|
|
1064
|
-
const byLabel = (a: ProviderSupportEntry, b: ProviderSupportEntry) =>
|
|
1065
|
-
a.label.localeCompare(b.label) || a.runtimeId.localeCompare(b.runtimeId);
|
|
1066
|
-
const named = entries
|
|
1067
|
-
.filter((entry) => entry.group === "cloud-api")
|
|
1068
|
-
.slice()
|
|
1069
|
-
.sort(byLabel);
|
|
1070
|
-
// Surface the generic OpenAI/Anthropic-compatible runtimes here too,
|
|
1071
|
-
// shown last and under the cloud heading rather than their native group.
|
|
1072
|
-
const compat = entries
|
|
1073
|
-
.filter((entry) => PROTOCOL_COMPAT_RUNTIME_IDS.has(entry.runtimeId))
|
|
1074
|
-
.map((entry) => ({ ...entry, group: "cloud-api" as const }))
|
|
1075
|
-
.sort(byLabel);
|
|
1076
|
-
return [...named, ...compat];
|
|
1077
|
-
}
|
|
1078
|
-
case "all":
|
|
1079
|
-
return entries.slice();
|
|
1080
|
-
}
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
648
|
async function pickCategory(rl: ReturnType<typeof createInterface>): Promise<ConfigureCategory | null> {
|
|
1084
649
|
process.stdout.write("\nHow will you connect Clio to a model?\n\n");
|
|
1085
650
|
for (const line of formatCategoryMenu(terminalColumns())) process.stdout.write(`${line}\n`);
|
|
@@ -1147,16 +712,11 @@ async function maybeSteerToNativeRuntime(
|
|
|
1147
712
|
return native;
|
|
1148
713
|
}
|
|
1149
714
|
|
|
1150
|
-
async function
|
|
715
|
+
async function runTargetSetupInteractive(
|
|
1151
716
|
rl: ReturnType<typeof createInterface>,
|
|
1152
|
-
|
|
717
|
+
runtime: RuntimeDescriptor,
|
|
1153
718
|
defaults: ParsedArgs,
|
|
1154
719
|
): Promise<number> {
|
|
1155
|
-
let runtime = preselectedRuntime ?? (await pickRuntimeViaCategory(rl));
|
|
1156
|
-
if (!runtime) {
|
|
1157
|
-
printError("configuration cancelled");
|
|
1158
|
-
return 130;
|
|
1159
|
-
}
|
|
1160
720
|
const auth = openAuthStorage();
|
|
1161
721
|
const settings = readSettings();
|
|
1162
722
|
let support = buildProviderSupportEntry(runtime);
|
|
@@ -1293,9 +853,10 @@ async function runInteractive(
|
|
|
1293
853
|
...(defaults.maxTokens !== undefined ? { maxTokens: defaults.maxTokens } : {}),
|
|
1294
854
|
...(defaults.reasoning !== undefined ? { reasoning: defaults.reasoning } : {}),
|
|
1295
855
|
...(existing?.lmstudio ? { lmstudio: existing.lmstudio } : {}),
|
|
856
|
+
...(existing?.litellm ? { litellm: existing.litellm } : {}),
|
|
1296
857
|
});
|
|
1297
858
|
|
|
1298
|
-
if (runtime.kind === "http") {
|
|
859
|
+
if (runtime.kind === "http" || runtime.externalAgentLoop?.modelCatalog === "live-authoritative") {
|
|
1299
860
|
inventory = await resolveSupportedWireModels(runtime, tentative, existing ?? undefined);
|
|
1300
861
|
}
|
|
1301
862
|
const wireModels = inventory.models;
|
|
@@ -1303,12 +864,21 @@ async function runInteractive(
|
|
|
1303
864
|
// --model here the way the non-interactive path does. It does need to stop
|
|
1304
865
|
// offering the alphabetically first id as though it were the recommended one.
|
|
1305
866
|
const catalogOrdered = support.modelSource === "catalog";
|
|
1306
|
-
model =
|
|
867
|
+
model =
|
|
868
|
+
model ??
|
|
869
|
+
existing?.defaultModel ??
|
|
870
|
+
(inventory.source === "probe" ? wireModels[0] : support.defaultModel) ??
|
|
871
|
+
(catalogOrdered ? undefined : wireModels[0]);
|
|
1307
872
|
if (wireModels.length > 0) {
|
|
1308
873
|
process.stdout.write("\nSelectable models:\n");
|
|
1309
874
|
for (const [index, wireModel] of wireModels.entries()) {
|
|
1310
|
-
|
|
875
|
+
const label = inventory.labels?.[wireModel];
|
|
876
|
+
process.stdout.write(
|
|
877
|
+
` ${index + 1}. ${wireModel}${label && label !== wireModel ? ` — ${label}` : ""}${wireModel === model ? " [default]" : ""}\n`,
|
|
878
|
+
);
|
|
1311
879
|
}
|
|
880
|
+
if (inventory.source === "cache") process.stdout.write(" cached model snapshot (not verified in this run)\n");
|
|
881
|
+
if (inventory.probeError) process.stdout.write(` live probe unavailable: ${inventory.probeError}\n`);
|
|
1312
882
|
if (!model && catalogOrdered) {
|
|
1313
883
|
process.stdout.write(` listed in provider catalog order, which recommends none of them; pick one.\n`);
|
|
1314
884
|
}
|
|
@@ -1379,6 +949,7 @@ async function runInteractive(
|
|
|
1379
949
|
...(defaults.maxTokens !== undefined ? { maxTokens: defaults.maxTokens } : {}),
|
|
1380
950
|
...(reasoningChoice !== undefined ? { reasoning: reasoningChoice } : {}),
|
|
1381
951
|
...(existing?.lmstudio ? { lmstudio: existing.lmstudio } : {}),
|
|
952
|
+
...(existing?.litellm ? { litellm: existing.litellm } : {}),
|
|
1382
953
|
});
|
|
1383
954
|
try {
|
|
1384
955
|
assertOrchestratorReplacementEligible(settings, descriptor);
|
|
@@ -1464,7 +1035,7 @@ async function runInteractive(
|
|
|
1464
1035
|
|
|
1465
1036
|
printSummary(settings, descriptor, probe);
|
|
1466
1037
|
printOk(`target ${targetId} saved`);
|
|
1467
|
-
await runInteropReview(rl);
|
|
1038
|
+
await runInteropReview({ rl });
|
|
1468
1039
|
return 0;
|
|
1469
1040
|
}
|
|
1470
1041
|
|
|
@@ -1514,7 +1085,980 @@ export function runTargetRename(oldId: string, newId: string): number {
|
|
|
1514
1085
|
return 0;
|
|
1515
1086
|
}
|
|
1516
1087
|
|
|
1517
|
-
|
|
1088
|
+
/**
|
|
1089
|
+
* The runtime configuration screens.
|
|
1090
|
+
*
|
|
1091
|
+
* Every screen is one `SectionSpec`: a title, the values it shows, and the
|
|
1092
|
+
* actions that change them. Rendering goes through the lifecycle presenter, so
|
|
1093
|
+
* `configure` degrades on a pipe and under NO_COLOR exactly as `reset`,
|
|
1094
|
+
* `upgrade`, and `uninstall` do. Navigation goes through `pickEntry`, which
|
|
1095
|
+
* uses the arrow-key selector on a terminal and the numbered readline prompt
|
|
1096
|
+
* everywhere else, so Escape leaves a screen without the screen having to teach
|
|
1097
|
+
* a token for it, and a scripted run still has a way to answer.
|
|
1098
|
+
*/
|
|
1099
|
+
|
|
1100
|
+
interface SectionIo {
|
|
1101
|
+
rl: ReturnType<typeof createInterface>;
|
|
1102
|
+
out: NodeJS.WritableStream;
|
|
1103
|
+
/** Both ends of this run, for an action that opens a picker of its own. */
|
|
1104
|
+
streams: ConfigureStreams;
|
|
1105
|
+
/** Confirm one change on the transcript, in the presenter's voice. */
|
|
1106
|
+
ok: (text: string) => void;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
interface SectionAction {
|
|
1110
|
+
label: string;
|
|
1111
|
+
hint?: string;
|
|
1112
|
+
run: (io: SectionIo) => Promise<void>;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
interface SectionSpec {
|
|
1116
|
+
/** Canonical `--section` name. */
|
|
1117
|
+
id: string;
|
|
1118
|
+
title: string;
|
|
1119
|
+
/** What the main menu says this screen covers. */
|
|
1120
|
+
summary: string;
|
|
1121
|
+
/** Other spellings `--section` accepts. */
|
|
1122
|
+
aliases?: ReadonlyArray<string>;
|
|
1123
|
+
fields: () => Array<readonly [string, string]>;
|
|
1124
|
+
actions: ReadonlyArray<SectionAction>;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
const NONE = "(none)";
|
|
1128
|
+
const onOff = (value: boolean): string => (value ? "enabled" : "disabled");
|
|
1129
|
+
const listOr = (values: ReadonlyArray<string>, fallback = NONE): string =>
|
|
1130
|
+
values.length > 0 ? values.join(", ") : fallback;
|
|
1131
|
+
|
|
1132
|
+
/** Read a bounded integer, leaving the setting alone when the answer is not one. */
|
|
1133
|
+
async function askInteger(
|
|
1134
|
+
io: SectionIo,
|
|
1135
|
+
label: string,
|
|
1136
|
+
current: number,
|
|
1137
|
+
apply: (value: number) => void,
|
|
1138
|
+
min = 0,
|
|
1139
|
+
): Promise<void> {
|
|
1140
|
+
const answer = await ask(io.rl, label, String(current));
|
|
1141
|
+
if (answer === null) return;
|
|
1142
|
+
const parsed = Number(answer);
|
|
1143
|
+
if (!Number.isFinite(parsed) || parsed < min) {
|
|
1144
|
+
io.out.write(` ${label} must be a number of at least ${min}; left at ${current}\n`);
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
const value = Math.floor(parsed);
|
|
1148
|
+
apply(value);
|
|
1149
|
+
io.ok(`${label} set to ${value}`);
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
/** Read one of a fixed set of words, leaving the setting alone otherwise. */
|
|
1153
|
+
async function askChoice(
|
|
1154
|
+
io: SectionIo,
|
|
1155
|
+
label: string,
|
|
1156
|
+
allowed: ReadonlyArray<string>,
|
|
1157
|
+
current: string,
|
|
1158
|
+
apply: (value: string) => void,
|
|
1159
|
+
): Promise<void> {
|
|
1160
|
+
const answer = await ask(io.rl, `${label} [${allowed.join("|")}]`, current);
|
|
1161
|
+
if (answer === null) return;
|
|
1162
|
+
const value = answer.trim().toLowerCase();
|
|
1163
|
+
if (!allowed.includes(value)) {
|
|
1164
|
+
io.out.write(` ${label} must be one of ${allowed.join(", ")}; left at ${current}\n`);
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
apply(value);
|
|
1168
|
+
io.ok(`${label} set to ${value}`);
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
const SECTIONS: ReadonlyArray<SectionSpec> = [
|
|
1172
|
+
{
|
|
1173
|
+
id: "targets",
|
|
1174
|
+
title: "Targets & Auth",
|
|
1175
|
+
summary: "providers, endpoints, credentials, models",
|
|
1176
|
+
aliases: ["auth", "target", "providers"],
|
|
1177
|
+
fields: () => {
|
|
1178
|
+
const settings = readSettings();
|
|
1179
|
+
const rows: Array<readonly [string, string]> = [
|
|
1180
|
+
["Chat target", `${settings.chat.target ?? NONE} (model: ${settings.chat.model ?? "target default"})`],
|
|
1181
|
+
[
|
|
1182
|
+
"Fleet target",
|
|
1183
|
+
`${settings.fleet.default.target ?? NONE} (model: ${settings.fleet.default.model ?? "target default"})`,
|
|
1184
|
+
],
|
|
1185
|
+
];
|
|
1186
|
+
if (settings.targets.length === 0) rows.push(["Registered", "(no targets registered)"]);
|
|
1187
|
+
else
|
|
1188
|
+
for (const target of settings.targets) {
|
|
1189
|
+
rows.push([
|
|
1190
|
+
` ${target.id}`,
|
|
1191
|
+
`${target.runtime}, model ${target.defaultModel ?? "unset"}${
|
|
1192
|
+
settings.chat.target === target.id ? " [chat]" : ""
|
|
1193
|
+
}${settings.fleet.default.target === target.id ? " [fleet]" : ""}`,
|
|
1194
|
+
]);
|
|
1195
|
+
}
|
|
1196
|
+
return rows;
|
|
1197
|
+
},
|
|
1198
|
+
actions: [
|
|
1199
|
+
{
|
|
1200
|
+
label: "Add a target",
|
|
1201
|
+
hint: "register a provider endpoint",
|
|
1202
|
+
run: async (io) => {
|
|
1203
|
+
const runtime = await pickRuntimeViaCategory(io.rl);
|
|
1204
|
+
if (runtime) await runTargetSetupInteractive(io.rl, runtime, emptyArgs());
|
|
1205
|
+
},
|
|
1206
|
+
},
|
|
1207
|
+
{
|
|
1208
|
+
label: "Set the chat target",
|
|
1209
|
+
hint: "which target answers in chat",
|
|
1210
|
+
run: async (io) => {
|
|
1211
|
+
await assignTarget(io, "chat");
|
|
1212
|
+
},
|
|
1213
|
+
},
|
|
1214
|
+
{
|
|
1215
|
+
label: "Set the fleet default target",
|
|
1216
|
+
hint: "which target dispatched workers use",
|
|
1217
|
+
run: async (io) => {
|
|
1218
|
+
await assignTarget(io, "fleet");
|
|
1219
|
+
},
|
|
1220
|
+
},
|
|
1221
|
+
{
|
|
1222
|
+
label: "Remove a target",
|
|
1223
|
+
run: async (io) => {
|
|
1224
|
+
const settings = readSettings();
|
|
1225
|
+
const ids = settings.targets.map((target) => target.id);
|
|
1226
|
+
if (ids.length === 0) {
|
|
1227
|
+
io.out.write(" No targets to remove.\n");
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
io.out.write(` Registered: ${ids.join(", ")}\n`);
|
|
1231
|
+
const chosen = await ask(io.rl, "Target id to remove");
|
|
1232
|
+
if (chosen === null || !ids.includes(chosen)) return;
|
|
1233
|
+
if (await askYesNo(io.rl, `Remove target '${chosen}'?`, false)) {
|
|
1234
|
+
runTargetRemove(chosen);
|
|
1235
|
+
}
|
|
1236
|
+
},
|
|
1237
|
+
},
|
|
1238
|
+
],
|
|
1239
|
+
},
|
|
1240
|
+
{
|
|
1241
|
+
id: "models",
|
|
1242
|
+
title: "Models & Thinking",
|
|
1243
|
+
summary: "default model, thinking level, favorites",
|
|
1244
|
+
aliases: ["model", "thinking"],
|
|
1245
|
+
fields: () => {
|
|
1246
|
+
const settings = readSettings();
|
|
1247
|
+
return [
|
|
1248
|
+
["Chat model", settings.chat.model ?? "(target default)"],
|
|
1249
|
+
["Chat thinking", settings.chat.thinkingLevel],
|
|
1250
|
+
["Fleet thinking", settings.fleet.default.thinkingLevel],
|
|
1251
|
+
["Favorites", listOr(settings.chat.modelPicker.favorites)],
|
|
1252
|
+
["Cycle set (Alt+J/K)", listOr(settings.chat.modelPicker.cycleSet)],
|
|
1253
|
+
];
|
|
1254
|
+
},
|
|
1255
|
+
actions: [
|
|
1256
|
+
{
|
|
1257
|
+
label: "Chat thinking level",
|
|
1258
|
+
hint: THINKING_LEVELS.join(" | "),
|
|
1259
|
+
run: async (io) => {
|
|
1260
|
+
await askChoice(io, "Chat thinking level", THINKING_LEVELS, readSettings().chat.thinkingLevel, (value) => {
|
|
1261
|
+
updateSettings((draft) => {
|
|
1262
|
+
draft.chat.thinkingLevel = value as ThinkingLevel;
|
|
1263
|
+
});
|
|
1264
|
+
});
|
|
1265
|
+
},
|
|
1266
|
+
},
|
|
1267
|
+
{
|
|
1268
|
+
label: "Fleet thinking level",
|
|
1269
|
+
hint: THINKING_LEVELS.join(" | "),
|
|
1270
|
+
run: async (io) => {
|
|
1271
|
+
await askChoice(
|
|
1272
|
+
io,
|
|
1273
|
+
"Fleet thinking level",
|
|
1274
|
+
THINKING_LEVELS,
|
|
1275
|
+
readSettings().fleet.default.thinkingLevel,
|
|
1276
|
+
(value) => {
|
|
1277
|
+
updateSettings((draft) => {
|
|
1278
|
+
draft.fleet.default.thinkingLevel = value as ThinkingLevel;
|
|
1279
|
+
});
|
|
1280
|
+
},
|
|
1281
|
+
);
|
|
1282
|
+
},
|
|
1283
|
+
},
|
|
1284
|
+
{
|
|
1285
|
+
label: "Chat default model",
|
|
1286
|
+
hint: "blank clears the override",
|
|
1287
|
+
run: async (io) => {
|
|
1288
|
+
const model = await ask(
|
|
1289
|
+
io.rl,
|
|
1290
|
+
"Chat default model (blank for the target default)",
|
|
1291
|
+
readSettings().chat.model ?? "",
|
|
1292
|
+
);
|
|
1293
|
+
if (model === null) return;
|
|
1294
|
+
updateSettings((draft) => {
|
|
1295
|
+
draft.chat.model = model.length > 0 ? model : null;
|
|
1296
|
+
});
|
|
1297
|
+
io.ok(model.length > 0 ? `Chat default model set to ${model}` : "Chat default model cleared");
|
|
1298
|
+
},
|
|
1299
|
+
},
|
|
1300
|
+
{
|
|
1301
|
+
label: "Model favorites",
|
|
1302
|
+
hint: "comma-separated",
|
|
1303
|
+
run: async (io) => {
|
|
1304
|
+
await askList(io, "Model favorites", readSettings().chat.modelPicker.favorites, (values) => {
|
|
1305
|
+
updateSettings((draft) => {
|
|
1306
|
+
draft.chat.modelPicker.favorites = values;
|
|
1307
|
+
});
|
|
1308
|
+
});
|
|
1309
|
+
},
|
|
1310
|
+
},
|
|
1311
|
+
{
|
|
1312
|
+
label: "Cycle set",
|
|
1313
|
+
hint: "comma-separated, Alt+J/K walks it",
|
|
1314
|
+
run: async (io) => {
|
|
1315
|
+
await askList(io, "Cycle set", readSettings().chat.modelPicker.cycleSet, (values) => {
|
|
1316
|
+
updateSettings((draft) => {
|
|
1317
|
+
draft.chat.modelPicker.cycleSet = values;
|
|
1318
|
+
});
|
|
1319
|
+
});
|
|
1320
|
+
},
|
|
1321
|
+
},
|
|
1322
|
+
],
|
|
1323
|
+
},
|
|
1324
|
+
{
|
|
1325
|
+
id: "chat",
|
|
1326
|
+
title: "Chat Defaults",
|
|
1327
|
+
summary: "streaming, progress, compaction",
|
|
1328
|
+
fields: () => {
|
|
1329
|
+
const settings = readSettings();
|
|
1330
|
+
return [
|
|
1331
|
+
["Smooth streaming", settings.interface.smoothStreaming],
|
|
1332
|
+
["Terminal progress", onOff(settings.interface.terminalProgress)],
|
|
1333
|
+
[
|
|
1334
|
+
"Max output tokens",
|
|
1335
|
+
settings.chat.maxOutputTokens > 0 ? String(settings.chat.maxOutputTokens) : "(runtime default)",
|
|
1336
|
+
],
|
|
1337
|
+
["Prompt prewarm", onOff(settings.chat.prewarm)],
|
|
1338
|
+
[
|
|
1339
|
+
"Auto-compaction",
|
|
1340
|
+
settings.context.compaction.auto
|
|
1341
|
+
? `enabled at ${Math.round(settings.context.compaction.threshold * 100)}%`
|
|
1342
|
+
: "disabled",
|
|
1343
|
+
],
|
|
1344
|
+
[
|
|
1345
|
+
"Working-set eviction",
|
|
1346
|
+
settings.context.workingSet.enabled ? `enabled (${settings.context.workingSet.policy})` : "disabled",
|
|
1347
|
+
],
|
|
1348
|
+
];
|
|
1349
|
+
},
|
|
1350
|
+
actions: [
|
|
1351
|
+
{
|
|
1352
|
+
label: "Smooth streaming",
|
|
1353
|
+
hint: "off | auto | on",
|
|
1354
|
+
run: async (io) => {
|
|
1355
|
+
await askChoice(
|
|
1356
|
+
io,
|
|
1357
|
+
"Smooth streaming",
|
|
1358
|
+
["off", "auto", "on"],
|
|
1359
|
+
readSettings().interface.smoothStreaming,
|
|
1360
|
+
(value) => {
|
|
1361
|
+
updateSettings((draft) => {
|
|
1362
|
+
draft.interface.smoothStreaming = value as SmoothStreaming;
|
|
1363
|
+
});
|
|
1364
|
+
},
|
|
1365
|
+
);
|
|
1366
|
+
},
|
|
1367
|
+
},
|
|
1368
|
+
{
|
|
1369
|
+
label: "Terminal progress indicator",
|
|
1370
|
+
hint: "toggle",
|
|
1371
|
+
run: async (io) => {
|
|
1372
|
+
let next = false;
|
|
1373
|
+
updateSettings((draft) => {
|
|
1374
|
+
draft.interface.terminalProgress = !draft.interface.terminalProgress;
|
|
1375
|
+
next = draft.interface.terminalProgress;
|
|
1376
|
+
});
|
|
1377
|
+
io.ok(`Terminal progress ${onOff(next)}`);
|
|
1378
|
+
},
|
|
1379
|
+
},
|
|
1380
|
+
{
|
|
1381
|
+
label: "Max output tokens",
|
|
1382
|
+
hint: "0 for the runtime default",
|
|
1383
|
+
run: async (io) => {
|
|
1384
|
+
await askInteger(io, "Max output tokens", readSettings().chat.maxOutputTokens, (value) => {
|
|
1385
|
+
updateSettings((draft) => {
|
|
1386
|
+
draft.chat.maxOutputTokens = value;
|
|
1387
|
+
});
|
|
1388
|
+
});
|
|
1389
|
+
},
|
|
1390
|
+
},
|
|
1391
|
+
{
|
|
1392
|
+
label: "Prompt prewarm",
|
|
1393
|
+
hint: "toggle",
|
|
1394
|
+
run: async (io) => {
|
|
1395
|
+
let next = false;
|
|
1396
|
+
updateSettings((draft) => {
|
|
1397
|
+
draft.chat.prewarm = !draft.chat.prewarm;
|
|
1398
|
+
next = draft.chat.prewarm;
|
|
1399
|
+
});
|
|
1400
|
+
io.ok(`Prompt prewarm ${onOff(next)}`);
|
|
1401
|
+
},
|
|
1402
|
+
},
|
|
1403
|
+
{
|
|
1404
|
+
label: "Auto-compaction",
|
|
1405
|
+
hint: "toggle",
|
|
1406
|
+
run: async (io) => {
|
|
1407
|
+
let next = false;
|
|
1408
|
+
updateSettings((draft) => {
|
|
1409
|
+
draft.context.compaction.auto = !draft.context.compaction.auto;
|
|
1410
|
+
next = draft.context.compaction.auto;
|
|
1411
|
+
});
|
|
1412
|
+
io.ok(`Auto-compaction ${onOff(next)}`);
|
|
1413
|
+
},
|
|
1414
|
+
},
|
|
1415
|
+
],
|
|
1416
|
+
},
|
|
1417
|
+
{
|
|
1418
|
+
id: "fleet",
|
|
1419
|
+
title: "Fleet",
|
|
1420
|
+
summary: "concurrency, retries, worker timeouts",
|
|
1421
|
+
fields: () => {
|
|
1422
|
+
const settings = readSettings();
|
|
1423
|
+
return [
|
|
1424
|
+
["Concurrency limit", String(settings.fleet.concurrency)],
|
|
1425
|
+
["Max retries", String(settings.fleet.retry.maxRetries)],
|
|
1426
|
+
["Tool calls per run", String(settings.fleet.limits.toolCallsPerRun)],
|
|
1427
|
+
["Run timeout", `${settings.fleet.limits.internalRunTimeoutMs / 1000}s`],
|
|
1428
|
+
["Worker profiles", listOr(Object.keys(settings.fleet.profiles))],
|
|
1429
|
+
[
|
|
1430
|
+
"Subagent pins",
|
|
1431
|
+
listOr(Object.entries(settings.fleet.agentProfiles).map(([agent, profile]) => `${agent} -> ${profile}`)),
|
|
1432
|
+
],
|
|
1433
|
+
["Remote nodes", listOr(settings.fleet.nodes.map((node) => node.id))],
|
|
1434
|
+
];
|
|
1435
|
+
},
|
|
1436
|
+
actions: [
|
|
1437
|
+
{
|
|
1438
|
+
label: "Concurrency limit",
|
|
1439
|
+
hint: "auto, or a positive integer",
|
|
1440
|
+
run: async (io) => {
|
|
1441
|
+
const current = String(readSettings().fleet.concurrency);
|
|
1442
|
+
const answer = await ask(io.rl, "Concurrency limit [auto or a number]", current);
|
|
1443
|
+
if (answer === null) return;
|
|
1444
|
+
if (answer.trim().toLowerCase() === "auto") {
|
|
1445
|
+
updateSettings((draft) => {
|
|
1446
|
+
draft.fleet.concurrency = "auto";
|
|
1447
|
+
});
|
|
1448
|
+
io.ok("Concurrency limit set to auto");
|
|
1449
|
+
return;
|
|
1450
|
+
}
|
|
1451
|
+
const parsed = Number(answer);
|
|
1452
|
+
if (!Number.isFinite(parsed) || parsed < 1) {
|
|
1453
|
+
io.out.write(` Concurrency limit must be auto or at least 1; left at ${current}\n`);
|
|
1454
|
+
return;
|
|
1455
|
+
}
|
|
1456
|
+
updateSettings((draft) => {
|
|
1457
|
+
draft.fleet.concurrency = Math.floor(parsed);
|
|
1458
|
+
});
|
|
1459
|
+
io.ok(`Concurrency limit set to ${Math.floor(parsed)}`);
|
|
1460
|
+
},
|
|
1461
|
+
},
|
|
1462
|
+
{
|
|
1463
|
+
label: "Max retries",
|
|
1464
|
+
run: async (io) => {
|
|
1465
|
+
await askInteger(io, "Max retries", readSettings().fleet.retry.maxRetries, (value) => {
|
|
1466
|
+
updateSettings((draft) => {
|
|
1467
|
+
draft.fleet.retry.maxRetries = value;
|
|
1468
|
+
});
|
|
1469
|
+
});
|
|
1470
|
+
},
|
|
1471
|
+
},
|
|
1472
|
+
{
|
|
1473
|
+
label: "Tool calls per run",
|
|
1474
|
+
run: async (io) => {
|
|
1475
|
+
await askInteger(
|
|
1476
|
+
io,
|
|
1477
|
+
"Tool calls per run",
|
|
1478
|
+
readSettings().fleet.limits.toolCallsPerRun,
|
|
1479
|
+
(value) => {
|
|
1480
|
+
updateSettings((draft) => {
|
|
1481
|
+
draft.fleet.limits.toolCallsPerRun = value;
|
|
1482
|
+
});
|
|
1483
|
+
},
|
|
1484
|
+
1,
|
|
1485
|
+
);
|
|
1486
|
+
},
|
|
1487
|
+
},
|
|
1488
|
+
{
|
|
1489
|
+
label: "Run timeout",
|
|
1490
|
+
hint: "seconds",
|
|
1491
|
+
run: async (io) => {
|
|
1492
|
+
const current = readSettings().fleet.limits.internalRunTimeoutMs / 1000;
|
|
1493
|
+
const answer = await ask(io.rl, "Run timeout in seconds", String(current));
|
|
1494
|
+
if (answer === null) return;
|
|
1495
|
+
const parsed = Number(answer);
|
|
1496
|
+
if (!Number.isFinite(parsed) || parsed < 1) {
|
|
1497
|
+
io.out.write(` Run timeout must be at least 1 second; left at ${current}s\n`);
|
|
1498
|
+
return;
|
|
1499
|
+
}
|
|
1500
|
+
updateSettings((draft) => {
|
|
1501
|
+
draft.fleet.limits.internalRunTimeoutMs = Math.floor(parsed * 1000);
|
|
1502
|
+
});
|
|
1503
|
+
io.ok(`Run timeout set to ${Math.floor(parsed)}s`);
|
|
1504
|
+
},
|
|
1505
|
+
},
|
|
1506
|
+
],
|
|
1507
|
+
},
|
|
1508
|
+
{
|
|
1509
|
+
id: "permissions",
|
|
1510
|
+
title: "Permissions & Autonomy",
|
|
1511
|
+
summary: "autonomy level, worker permissions, cost limits",
|
|
1512
|
+
aliases: ["autonomy", "safety"],
|
|
1513
|
+
fields: () => {
|
|
1514
|
+
const settings = readSettings();
|
|
1515
|
+
return [
|
|
1516
|
+
["Autonomy level", settings.safety.autonomy],
|
|
1517
|
+
["Worker permissions", settings.fleet.permissions.mode],
|
|
1518
|
+
["Session cost limit", `$${settings.safety.limits.sessionCostUsd} USD`],
|
|
1519
|
+
["Turn tool budget", String(settings.safety.limits.chatToolCallsPerTurn)],
|
|
1520
|
+
["Review watchdog", onOff(settings.safety.review.enabled)],
|
|
1521
|
+
];
|
|
1522
|
+
},
|
|
1523
|
+
actions: [
|
|
1524
|
+
{
|
|
1525
|
+
label: "Autonomy level",
|
|
1526
|
+
hint: "interactive | assisted | auto-edit | full",
|
|
1527
|
+
run: async (io) => {
|
|
1528
|
+
io.out.write(
|
|
1529
|
+
" interactive prompts for everything, assisted auto-reads and confirms writes,\n auto-edit confirms only bash, full runs unattended.\n",
|
|
1530
|
+
);
|
|
1531
|
+
await askChoice(
|
|
1532
|
+
io,
|
|
1533
|
+
"Autonomy level",
|
|
1534
|
+
["interactive", "assisted", "auto-edit", "full"],
|
|
1535
|
+
readSettings().safety.autonomy,
|
|
1536
|
+
(value) => {
|
|
1537
|
+
updateSettings((draft) => {
|
|
1538
|
+
draft.safety.autonomy = value as AutonomyLevel;
|
|
1539
|
+
});
|
|
1540
|
+
},
|
|
1541
|
+
);
|
|
1542
|
+
},
|
|
1543
|
+
},
|
|
1544
|
+
{
|
|
1545
|
+
label: "Worker permission mode",
|
|
1546
|
+
hint: "deny | fail | escalate",
|
|
1547
|
+
run: async (io) => {
|
|
1548
|
+
await askChoice(
|
|
1549
|
+
io,
|
|
1550
|
+
"Worker permission mode",
|
|
1551
|
+
["deny", "fail", "escalate"],
|
|
1552
|
+
readSettings().fleet.permissions.mode,
|
|
1553
|
+
(value) => {
|
|
1554
|
+
updateSettings((draft) => {
|
|
1555
|
+
draft.fleet.permissions.mode = value as WorkerPermissionMode;
|
|
1556
|
+
});
|
|
1557
|
+
},
|
|
1558
|
+
);
|
|
1559
|
+
},
|
|
1560
|
+
},
|
|
1561
|
+
{
|
|
1562
|
+
label: "Session cost limit",
|
|
1563
|
+
hint: "USD",
|
|
1564
|
+
run: async (io) => {
|
|
1565
|
+
const current = readSettings().safety.limits.sessionCostUsd;
|
|
1566
|
+
const answer = await ask(io.rl, "Session cost limit in USD", String(current));
|
|
1567
|
+
if (answer === null) return;
|
|
1568
|
+
const parsed = Number(answer);
|
|
1569
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
1570
|
+
io.out.write(` Session cost limit must be greater than 0; left at $${current}\n`);
|
|
1571
|
+
return;
|
|
1572
|
+
}
|
|
1573
|
+
updateSettings((draft) => {
|
|
1574
|
+
draft.safety.limits.sessionCostUsd = parsed;
|
|
1575
|
+
});
|
|
1576
|
+
io.ok(`Session cost limit set to $${parsed} USD`);
|
|
1577
|
+
},
|
|
1578
|
+
},
|
|
1579
|
+
{
|
|
1580
|
+
label: "Turn tool budget",
|
|
1581
|
+
run: async (io) => {
|
|
1582
|
+
await askInteger(
|
|
1583
|
+
io,
|
|
1584
|
+
"Turn tool budget",
|
|
1585
|
+
readSettings().safety.limits.chatToolCallsPerTurn,
|
|
1586
|
+
(value) => {
|
|
1587
|
+
updateSettings((draft) => {
|
|
1588
|
+
draft.safety.limits.chatToolCallsPerTurn = value;
|
|
1589
|
+
});
|
|
1590
|
+
},
|
|
1591
|
+
1,
|
|
1592
|
+
);
|
|
1593
|
+
},
|
|
1594
|
+
},
|
|
1595
|
+
{
|
|
1596
|
+
label: "Turn-end review watchdog",
|
|
1597
|
+
hint: "toggle",
|
|
1598
|
+
run: async (io) => {
|
|
1599
|
+
let next = false;
|
|
1600
|
+
updateSettings((draft) => {
|
|
1601
|
+
draft.safety.review.enabled = !draft.safety.review.enabled;
|
|
1602
|
+
next = draft.safety.review.enabled;
|
|
1603
|
+
});
|
|
1604
|
+
io.ok(`Review watchdog ${onOff(next)}`);
|
|
1605
|
+
},
|
|
1606
|
+
},
|
|
1607
|
+
],
|
|
1608
|
+
},
|
|
1609
|
+
{
|
|
1610
|
+
id: "panes",
|
|
1611
|
+
title: "Panes & Layout",
|
|
1612
|
+
summary: "terminal panes, startup layout, display mode",
|
|
1613
|
+
aliases: ["layout", "interface", "pane"],
|
|
1614
|
+
fields: () => {
|
|
1615
|
+
const settings = readSettings();
|
|
1616
|
+
return [
|
|
1617
|
+
["Panes capability", settings.interface.panes.enabled],
|
|
1618
|
+
["Startup layout", settings.interface.panes.layout],
|
|
1619
|
+
["TUI mode", settings.interface.mode],
|
|
1620
|
+
["Output detail", settings.interface.outputDetail],
|
|
1621
|
+
["Desktop notifications", onOff(settings.interface.desktopNotifications)],
|
|
1622
|
+
["Git commit attribution", onOff(settings.integrations.git.commitAttribution)],
|
|
1623
|
+
];
|
|
1624
|
+
},
|
|
1625
|
+
actions: [
|
|
1626
|
+
{
|
|
1627
|
+
label: "Panes capability",
|
|
1628
|
+
hint: "off | auto | embedded",
|
|
1629
|
+
run: async (io) => {
|
|
1630
|
+
await askChoice(
|
|
1631
|
+
io,
|
|
1632
|
+
"Panes capability",
|
|
1633
|
+
["off", "auto", "embedded"],
|
|
1634
|
+
readSettings().interface.panes.enabled,
|
|
1635
|
+
(value) => {
|
|
1636
|
+
updateSettings((draft) => {
|
|
1637
|
+
draft.interface.panes.enabled = value as PanesSettings["enabled"];
|
|
1638
|
+
});
|
|
1639
|
+
},
|
|
1640
|
+
);
|
|
1641
|
+
},
|
|
1642
|
+
},
|
|
1643
|
+
{
|
|
1644
|
+
label: "Startup layout",
|
|
1645
|
+
hint: "off | workers | cockpit",
|
|
1646
|
+
run: async (io) => {
|
|
1647
|
+
await askChoice(
|
|
1648
|
+
io,
|
|
1649
|
+
"Startup layout",
|
|
1650
|
+
["off", "workers", "cockpit"],
|
|
1651
|
+
readSettings().interface.panes.layout,
|
|
1652
|
+
(value) => {
|
|
1653
|
+
updateSettings((draft) => {
|
|
1654
|
+
draft.interface.panes.layout = value as PanesSettings["layout"];
|
|
1655
|
+
});
|
|
1656
|
+
},
|
|
1657
|
+
);
|
|
1658
|
+
},
|
|
1659
|
+
},
|
|
1660
|
+
{
|
|
1661
|
+
label: "TUI mode",
|
|
1662
|
+
hint: "regular | fullscreen",
|
|
1663
|
+
run: async (io) => {
|
|
1664
|
+
await askChoice(io, "TUI mode", ["regular", "fullscreen"], readSettings().interface.mode, (value) => {
|
|
1665
|
+
updateSettings((draft) => {
|
|
1666
|
+
draft.interface.mode = value as TuiMode;
|
|
1667
|
+
});
|
|
1668
|
+
});
|
|
1669
|
+
},
|
|
1670
|
+
},
|
|
1671
|
+
{
|
|
1672
|
+
label: "Output detail",
|
|
1673
|
+
hint: "minimal | default | verbose",
|
|
1674
|
+
run: async (io) => {
|
|
1675
|
+
await askChoice(
|
|
1676
|
+
io,
|
|
1677
|
+
"Output detail",
|
|
1678
|
+
["minimal", "default", "verbose"],
|
|
1679
|
+
readSettings().interface.outputDetail,
|
|
1680
|
+
(value) => {
|
|
1681
|
+
updateSettings((draft) => {
|
|
1682
|
+
draft.interface.outputDetail = value as OutputVerbosity;
|
|
1683
|
+
});
|
|
1684
|
+
},
|
|
1685
|
+
);
|
|
1686
|
+
},
|
|
1687
|
+
},
|
|
1688
|
+
{
|
|
1689
|
+
label: "Desktop notifications",
|
|
1690
|
+
hint: "toggle",
|
|
1691
|
+
run: async (io) => {
|
|
1692
|
+
let next = false;
|
|
1693
|
+
updateSettings((draft) => {
|
|
1694
|
+
draft.interface.desktopNotifications = !draft.interface.desktopNotifications;
|
|
1695
|
+
next = draft.interface.desktopNotifications;
|
|
1696
|
+
});
|
|
1697
|
+
io.ok(`Desktop notifications ${onOff(next)}`);
|
|
1698
|
+
},
|
|
1699
|
+
},
|
|
1700
|
+
{
|
|
1701
|
+
label: "Git commit attribution",
|
|
1702
|
+
hint: "toggle",
|
|
1703
|
+
run: async (io) => {
|
|
1704
|
+
let next = false;
|
|
1705
|
+
updateSettings((draft) => {
|
|
1706
|
+
draft.integrations.git.commitAttribution = !draft.integrations.git.commitAttribution;
|
|
1707
|
+
next = draft.integrations.git.commitAttribution;
|
|
1708
|
+
});
|
|
1709
|
+
io.ok(`Git commit attribution ${onOff(next)}`);
|
|
1710
|
+
},
|
|
1711
|
+
},
|
|
1712
|
+
],
|
|
1713
|
+
},
|
|
1714
|
+
{
|
|
1715
|
+
id: "skills",
|
|
1716
|
+
title: "Skills & Extensions",
|
|
1717
|
+
summary: "project imports, ACP peers, plugins",
|
|
1718
|
+
aliases: ["extensions", "skill", "interop"],
|
|
1719
|
+
fields: () => {
|
|
1720
|
+
const settings = readSettings();
|
|
1721
|
+
const rows: Array<readonly [string, string]> = [
|
|
1722
|
+
["Trust project imports", settings.integrations.projectResources.trustProjectImports ? "trusted" : "untrusted"],
|
|
1723
|
+
["External ACP agents", String(settings.integrations.externalAgents.entries.length)],
|
|
1724
|
+
];
|
|
1725
|
+
for (const agent of settings.integrations.externalAgents.entries) {
|
|
1726
|
+
rows.push([` ${agent.id}`, `${agent.command} (governance: ${agent.toolGovernance ?? "default"})`]);
|
|
1727
|
+
}
|
|
1728
|
+
rows.push(
|
|
1729
|
+
["Runtime plugins", listOr(settings.integrations.runtimePlugins)],
|
|
1730
|
+
["Library remote sync", onOff(settings.integrations.library.sync)],
|
|
1731
|
+
);
|
|
1732
|
+
return rows;
|
|
1733
|
+
},
|
|
1734
|
+
actions: [
|
|
1735
|
+
{
|
|
1736
|
+
label: "Trust project imports",
|
|
1737
|
+
hint: "toggle",
|
|
1738
|
+
run: async (io) => {
|
|
1739
|
+
let next = false;
|
|
1740
|
+
updateSettings((draft) => {
|
|
1741
|
+
draft.integrations.projectResources.trustProjectImports =
|
|
1742
|
+
!draft.integrations.projectResources.trustProjectImports;
|
|
1743
|
+
next = draft.integrations.projectResources.trustProjectImports;
|
|
1744
|
+
});
|
|
1745
|
+
io.ok(`Project imports ${next ? "trusted" : "untrusted"}`);
|
|
1746
|
+
},
|
|
1747
|
+
},
|
|
1748
|
+
{
|
|
1749
|
+
label: "Review external ACP agents",
|
|
1750
|
+
hint: "runs the interop review",
|
|
1751
|
+
run: async (io) => {
|
|
1752
|
+
await runInteropReview({ rl: io.rl, streams: io.streams });
|
|
1753
|
+
},
|
|
1754
|
+
},
|
|
1755
|
+
{
|
|
1756
|
+
label: "Library remote sync",
|
|
1757
|
+
hint: "toggle",
|
|
1758
|
+
run: async (io) => {
|
|
1759
|
+
let next = false;
|
|
1760
|
+
updateSettings((draft) => {
|
|
1761
|
+
draft.integrations.library.sync = !draft.integrations.library.sync;
|
|
1762
|
+
next = draft.integrations.library.sync;
|
|
1763
|
+
});
|
|
1764
|
+
io.ok(`Library remote sync ${onOff(next)}`);
|
|
1765
|
+
},
|
|
1766
|
+
},
|
|
1767
|
+
],
|
|
1768
|
+
},
|
|
1769
|
+
{
|
|
1770
|
+
id: "diagnostics",
|
|
1771
|
+
title: "Diagnostics",
|
|
1772
|
+
summary: "version, directories, doctor, raw settings",
|
|
1773
|
+
aliases: ["doctor", "diag"],
|
|
1774
|
+
fields: () => {
|
|
1775
|
+
const dirs = resolveClioDirs();
|
|
1776
|
+
const info = getVersionInfo();
|
|
1777
|
+
return [
|
|
1778
|
+
["Clio Coder", info.clio],
|
|
1779
|
+
["Node.js", info.node],
|
|
1780
|
+
["Platform", info.platform],
|
|
1781
|
+
["Config dir", shortenPath(dirs.config)],
|
|
1782
|
+
["Data dir", shortenPath(dirs.data)],
|
|
1783
|
+
["State dir", shortenPath(dirs.state)],
|
|
1784
|
+
["Cache dir", shortenPath(dirs.cache)],
|
|
1785
|
+
];
|
|
1786
|
+
},
|
|
1787
|
+
actions: [
|
|
1788
|
+
{
|
|
1789
|
+
label: "Run doctor",
|
|
1790
|
+
hint: "read-only health check",
|
|
1791
|
+
run: async () => {
|
|
1792
|
+
const { runDoctorCommand } = await import("./doctor.js");
|
|
1793
|
+
await runDoctorCommand([]);
|
|
1794
|
+
},
|
|
1795
|
+
},
|
|
1796
|
+
{
|
|
1797
|
+
label: "Show raw settings.yaml",
|
|
1798
|
+
run: async (io) => {
|
|
1799
|
+
const file = settingsPath();
|
|
1800
|
+
if (!existsSync(file)) {
|
|
1801
|
+
io.out.write(" settings.yaml does not exist yet.\n");
|
|
1802
|
+
return;
|
|
1803
|
+
}
|
|
1804
|
+
io.out.write(`\n--- ${shortenPath(file)} ---\n${readFileSync(file, "utf8")}--- end ---\n`);
|
|
1805
|
+
},
|
|
1806
|
+
},
|
|
1807
|
+
],
|
|
1808
|
+
},
|
|
1809
|
+
];
|
|
1810
|
+
|
|
1811
|
+
/** A `ParsedArgs` with nothing set, for the target wizard called from a section. */
|
|
1812
|
+
function emptyArgs(): ParsedArgs {
|
|
1813
|
+
return {
|
|
1814
|
+
positional: [],
|
|
1815
|
+
help: false,
|
|
1816
|
+
list: false,
|
|
1817
|
+
all: false,
|
|
1818
|
+
interop: false,
|
|
1819
|
+
json: false,
|
|
1820
|
+
force: false,
|
|
1821
|
+
gateway: false,
|
|
1822
|
+
setOrchestrator: false,
|
|
1823
|
+
setBackground: false,
|
|
1824
|
+
setWorkerDefault: false,
|
|
1825
|
+
};
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
async function askList(
|
|
1829
|
+
io: SectionIo,
|
|
1830
|
+
label: string,
|
|
1831
|
+
current: ReadonlyArray<string>,
|
|
1832
|
+
apply: (values: string[]) => void,
|
|
1833
|
+
): Promise<void> {
|
|
1834
|
+
const answer = await ask(io.rl, `${label} (comma-separated, blank clears)`, current.join(", "));
|
|
1835
|
+
if (answer === null) return;
|
|
1836
|
+
const values = answer
|
|
1837
|
+
.split(",")
|
|
1838
|
+
.map((entry) => entry.trim())
|
|
1839
|
+
.filter((entry) => entry.length > 0);
|
|
1840
|
+
apply(values);
|
|
1841
|
+
io.ok(values.length === 0 ? `${label} cleared` : `${label} set to ${values.join(", ")}`);
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
async function assignTarget(io: SectionIo, role: "chat" | "fleet"): Promise<void> {
|
|
1845
|
+
const settings = readSettings();
|
|
1846
|
+
const ids = settings.targets.map((target) => target.id);
|
|
1847
|
+
if (ids.length === 0) {
|
|
1848
|
+
io.out.write(" No targets registered yet. Add one first.\n");
|
|
1849
|
+
return;
|
|
1850
|
+
}
|
|
1851
|
+
io.out.write(` Registered: ${ids.join(", ")}\n`);
|
|
1852
|
+
const currentTarget = role === "chat" ? settings.chat.target : settings.fleet.default.target;
|
|
1853
|
+
const chosen = await ask(io.rl, `${role === "chat" ? "Chat" : "Fleet"} target id`, currentTarget ?? ids[0] ?? "");
|
|
1854
|
+
if (chosen === null) return;
|
|
1855
|
+
if (!ids.includes(chosen)) {
|
|
1856
|
+
io.out.write(` ${chosen} is not a registered target; nothing changed.\n`);
|
|
1857
|
+
return;
|
|
1858
|
+
}
|
|
1859
|
+
const target = settings.targets.find((entry) => entry.id === chosen);
|
|
1860
|
+
const model = await ask(io.rl, "Model (blank for the target default)", target?.defaultModel ?? "");
|
|
1861
|
+
if (model === null) return;
|
|
1862
|
+
updateSettings((draft) => {
|
|
1863
|
+
if (role === "chat") {
|
|
1864
|
+
draft.chat.target = chosen;
|
|
1865
|
+
draft.chat.model = model.length > 0 ? model : null;
|
|
1866
|
+
} else {
|
|
1867
|
+
draft.fleet.default.target = chosen;
|
|
1868
|
+
draft.fleet.default.model = model.length > 0 ? model : null;
|
|
1869
|
+
}
|
|
1870
|
+
});
|
|
1871
|
+
io.ok(`${role === "chat" ? "Chat" : "Fleet"} target set to ${chosen}`);
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
interface MenuEntry {
|
|
1875
|
+
label: string;
|
|
1876
|
+
hint?: string;
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1879
|
+
type MenuChoice = { kind: "index"; index: number } | { kind: "back" } | { kind: "quit" };
|
|
1880
|
+
|
|
1881
|
+
/**
|
|
1882
|
+
* Pick one entry, by arrow keys on a terminal and by number everywhere else.
|
|
1883
|
+
* The numbered fallback is not a lesser path: it is what a recorded session, a
|
|
1884
|
+
* pipe, and a dumb terminal get, and it accepts the same `b`/`q` tokens the rest
|
|
1885
|
+
* of this CLI uses.
|
|
1886
|
+
*/
|
|
1887
|
+
async function pickEntry(
|
|
1888
|
+
rl: ReturnType<typeof createInterface>,
|
|
1889
|
+
io: ConfigureStreams,
|
|
1890
|
+
entries: ReadonlyArray<MenuEntry>,
|
|
1891
|
+
backLabel: string,
|
|
1892
|
+
plain: boolean,
|
|
1893
|
+
): Promise<MenuChoice> {
|
|
1894
|
+
const out = io.out;
|
|
1895
|
+
if (canSelect(io.in as NodeJS.ReadStream, out as NodeJS.WriteStream)) {
|
|
1896
|
+
const result = await promptSelect({
|
|
1897
|
+
choices: entries.map((entry, index) => ({
|
|
1898
|
+
value: index,
|
|
1899
|
+
label: entry.label,
|
|
1900
|
+
...(entry.hint === undefined ? {} : { hint: entry.hint }),
|
|
1901
|
+
})),
|
|
1902
|
+
railPrefix: railPrefix(plain),
|
|
1903
|
+
backLabel,
|
|
1904
|
+
input: io.in as NodeJS.ReadStream,
|
|
1905
|
+
output: out as NodeJS.WriteStream,
|
|
1906
|
+
});
|
|
1907
|
+
if (result.kind === "selected") return { kind: "index", index: result.value };
|
|
1908
|
+
return result.kind === "quit" ? { kind: "quit" } : { kind: "back" };
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
const rail = railPrefix(plain);
|
|
1912
|
+
entries.forEach((entry, index) => {
|
|
1913
|
+
out.write(`${rail}${index + 1}. ${entry.label}${entry.hint === undefined ? "" : ` (${entry.hint})`}\n`);
|
|
1914
|
+
});
|
|
1915
|
+
out.write(`${rail}b. ${backLabel}\n${rail}q. Quit\n`);
|
|
1916
|
+
const answer = await ask(rl, "\nSelection", "b");
|
|
1917
|
+
if (answer === null) return { kind: "quit" };
|
|
1918
|
+
const trimmed = answer.trim().toLowerCase();
|
|
1919
|
+
if (trimmed === "b" || trimmed === "back" || trimmed.length === 0) return { kind: "back" };
|
|
1920
|
+
if (trimmed === "q" || trimmed === "quit") return { kind: "quit" };
|
|
1921
|
+
const index = Number.parseInt(trimmed, 10) - 1;
|
|
1922
|
+
if (Number.isInteger(index) && index >= 0 && index < entries.length) return { kind: "index", index };
|
|
1923
|
+
out.write(`${rail}Not one of the choices: ${answer}\n`);
|
|
1924
|
+
return { kind: "back" };
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
/** Header, source file, and current values for one screen. */
|
|
1928
|
+
function renderSection(spec: SectionSpec, out: NodeJS.WritableStream): LifecyclePresenter {
|
|
1929
|
+
const presenter = createLifecyclePresenter({ stream: out });
|
|
1930
|
+
presenter.header(spec.title, "configure");
|
|
1931
|
+
presenter.note(`Source: ${shortenPath(settingsPath())}`);
|
|
1932
|
+
presenter.fields(spec.fields());
|
|
1933
|
+
return presenter;
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
/** Show one screen and stay on it until the operator leaves or quits. */
|
|
1937
|
+
async function runSection(
|
|
1938
|
+
rl: ReturnType<typeof createInterface>,
|
|
1939
|
+
streams: ConfigureStreams,
|
|
1940
|
+
spec: SectionSpec,
|
|
1941
|
+
backLabel: string,
|
|
1942
|
+
): Promise<"back" | "quit"> {
|
|
1943
|
+
for (;;) {
|
|
1944
|
+
const presenter = renderSection(spec, streams.out);
|
|
1945
|
+
const io: SectionIo = {
|
|
1946
|
+
rl,
|
|
1947
|
+
out: streams.out,
|
|
1948
|
+
streams,
|
|
1949
|
+
ok: (text) => {
|
|
1950
|
+
presenter.completedStep(text);
|
|
1951
|
+
},
|
|
1952
|
+
};
|
|
1953
|
+
presenter.blank();
|
|
1954
|
+
const choice = await pickEntry(rl, streams, spec.actions, backLabel, presenter.isPlain());
|
|
1955
|
+
if (choice.kind !== "index") return choice.kind;
|
|
1956
|
+
const action = spec.actions[choice.index];
|
|
1957
|
+
if (action !== undefined) await action.run(io);
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
/** The top-level screen. Leaving it is an ordinary exit, not a cancellation. */
|
|
1962
|
+
async function runConfigSectionsMenu(
|
|
1963
|
+
rl: ReturnType<typeof createInterface>,
|
|
1964
|
+
streams: ConfigureStreams,
|
|
1965
|
+
): Promise<number> {
|
|
1966
|
+
for (;;) {
|
|
1967
|
+
const presenter = createLifecyclePresenter({ stream: streams.out });
|
|
1968
|
+
presenter.header("Clio Coder Configuration", "configure");
|
|
1969
|
+
presenter.note(`Source: ${shortenPath(settingsPath())}`);
|
|
1970
|
+
presenter.blank();
|
|
1971
|
+
const choice = await pickEntry(
|
|
1972
|
+
rl,
|
|
1973
|
+
streams,
|
|
1974
|
+
SECTIONS.map((section) => ({ label: section.title, hint: section.summary })),
|
|
1975
|
+
"quit",
|
|
1976
|
+
presenter.isPlain(),
|
|
1977
|
+
);
|
|
1978
|
+
// Escape and q both leave the top screen, and leaving a settings menu you
|
|
1979
|
+
// only looked at is not a failure: this used to exit 130 and print
|
|
1980
|
+
// "error: configuration cancelled" over a run that did exactly what was
|
|
1981
|
+
// asked. The first-run wizard still exits 130, because there a cancel
|
|
1982
|
+
// really does leave Clio unconfigured.
|
|
1983
|
+
if (choice.kind !== "index") return 0;
|
|
1984
|
+
const spec = SECTIONS[choice.index];
|
|
1985
|
+
if (spec === undefined) continue;
|
|
1986
|
+
if ((await runSection(rl, streams, spec, "back")) === "quit") return 0;
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
/** Resolve a `--section` value to exactly one screen, by id or listed alias. */
|
|
1991
|
+
function findSection(name: string): SectionSpec | undefined {
|
|
1992
|
+
const wanted = name.trim().toLowerCase();
|
|
1993
|
+
return SECTIONS.find((section) => section.id === wanted || (section.aliases ?? []).includes(wanted));
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1996
|
+
const SECTION_IDS: ReadonlyArray<string> = SECTIONS.map((section) => section.id);
|
|
1997
|
+
|
|
1998
|
+
/** The streams this run reads from and writes to, which the tests replace. */
|
|
1999
|
+
interface ConfigureStreams {
|
|
2000
|
+
in: NodeJS.ReadableStream;
|
|
2001
|
+
out: NodeJS.WritableStream;
|
|
2002
|
+
}
|
|
2003
|
+
|
|
2004
|
+
async function runInteractive(
|
|
2005
|
+
rl: ReturnType<typeof createInterface>,
|
|
2006
|
+
preselectedRuntime: RuntimeDescriptor | null,
|
|
2007
|
+
defaults: ParsedArgs,
|
|
2008
|
+
streams: ConfigureStreams,
|
|
2009
|
+
): Promise<number> {
|
|
2010
|
+
if (preselectedRuntime) {
|
|
2011
|
+
return await runTargetSetupInteractive(rl, preselectedRuntime, defaults);
|
|
2012
|
+
}
|
|
2013
|
+
|
|
2014
|
+
if (defaults.section !== undefined) {
|
|
2015
|
+
const spec = findSection(defaults.section);
|
|
2016
|
+
if (spec === undefined) {
|
|
2017
|
+
printError(`unknown section: ${defaults.section}. Sections: ${SECTION_IDS.join(", ")}`);
|
|
2018
|
+
return 2;
|
|
2019
|
+
}
|
|
2020
|
+
// Without a terminal there is nobody to answer, and printing a prompt into
|
|
2021
|
+
// a pipe is a dead end that reads as a hang. The screen is still worth
|
|
2022
|
+
// showing, so a scripted `configure --section fleet` is a read of that
|
|
2023
|
+
// section rather than a refusal.
|
|
2024
|
+
if ((streams.in as { isTTY?: boolean }).isTTY !== true) {
|
|
2025
|
+
renderSection(spec, streams.out).done("Done");
|
|
2026
|
+
return 0;
|
|
2027
|
+
}
|
|
2028
|
+
await runSection(rl, streams, spec, "quit");
|
|
2029
|
+
return 0;
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
if (readSettings().targets.length === 0) {
|
|
2033
|
+
// The first run is its own screen flow. It only exists where a keypress can
|
|
2034
|
+
// be read; a pipe, a recorded session, and a dumb terminal keep the
|
|
2035
|
+
// numbered readline path below, which is still the only way to answer them.
|
|
2036
|
+
//
|
|
2037
|
+
// The readline interface is closed first. Left open it stays subscribed to
|
|
2038
|
+
// stdin's keypresses and echoes each one, which puts a stray line under
|
|
2039
|
+
// every menu the wizard draws and breaks the erase that replaces the menu
|
|
2040
|
+
// with its answer. The wizard opens its own where a browser sign-in needs
|
|
2041
|
+
// one.
|
|
2042
|
+
if (canRunOnboarding(streams)) {
|
|
2043
|
+
rl.close();
|
|
2044
|
+
return await runOnboardingWizard(streams);
|
|
2045
|
+
}
|
|
2046
|
+
const runtime = await pickRuntimeViaCategory(rl);
|
|
2047
|
+
if (!runtime) {
|
|
2048
|
+
printError("configuration cancelled");
|
|
2049
|
+
return 130;
|
|
2050
|
+
}
|
|
2051
|
+
return await runTargetSetupInteractive(rl, runtime, defaults);
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2054
|
+
return await runConfigSectionsMenu(rl, streams);
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
export async function runConfigureCommand(
|
|
2058
|
+
argv: ReadonlyArray<string>,
|
|
2059
|
+
inStream: NodeJS.ReadableStream = input,
|
|
2060
|
+
outStream: NodeJS.WritableStream = output,
|
|
2061
|
+
): Promise<number> {
|
|
1518
2062
|
let args: ParsedArgs;
|
|
1519
2063
|
try {
|
|
1520
2064
|
args = parseSetupArgs(argv);
|
|
@@ -1530,15 +2074,21 @@ export async function runConfigureCommand(argv: ReadonlyArray<string>): Promise<
|
|
|
1530
2074
|
initializeClioHome();
|
|
1531
2075
|
ensureRegistryPopulated();
|
|
1532
2076
|
|
|
2077
|
+
if (args.json) {
|
|
2078
|
+
const settings = readSettings();
|
|
2079
|
+
outStream.write(`${JSON.stringify(settings, null, 2)}\n`);
|
|
2080
|
+
return 0;
|
|
2081
|
+
}
|
|
2082
|
+
|
|
1533
2083
|
if (args.list) {
|
|
1534
2084
|
printRuntimeList(args.all);
|
|
1535
2085
|
return 0;
|
|
1536
2086
|
}
|
|
1537
2087
|
if (args.interop) {
|
|
1538
|
-
if (!input.isTTY) return runInteropReview(null);
|
|
2088
|
+
if (!input.isTTY) return runInteropReview({ rl: null });
|
|
1539
2089
|
const rl = createInterface({ input, output });
|
|
1540
2090
|
try {
|
|
1541
|
-
return await runInteropReview(rl);
|
|
2091
|
+
return await runInteropReview({ rl, streams: { in: input, out: output } });
|
|
1542
2092
|
} finally {
|
|
1543
2093
|
rl.close();
|
|
1544
2094
|
}
|
|
@@ -1599,9 +2149,9 @@ export async function runConfigureCommand(argv: ReadonlyArray<string>): Promise<
|
|
|
1599
2149
|
return 2;
|
|
1600
2150
|
}
|
|
1601
2151
|
|
|
1602
|
-
const rl = createInterface({ input, output });
|
|
2152
|
+
const rl = createInterface({ input: inStream, output: outStream });
|
|
1603
2153
|
try {
|
|
1604
|
-
return await runInteractive(rl, runtime, args);
|
|
2154
|
+
return await runInteractive(rl, runtime, args, { in: inStream, out: outStream });
|
|
1605
2155
|
} catch (err) {
|
|
1606
2156
|
printError(err instanceof Error ? err.message : String(err));
|
|
1607
2157
|
return 1;
|