@iowarp/clio-coder 0.4.6 → 0.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +283 -0
- package/CHANGELOG.md +50 -0
- package/CONTRIBUTING.md +102 -29
- package/NOTICE +8 -0
- package/README.md +37 -5
- package/dist/{acp-UVEVWSDL.js → acp-W3Y3TRHD.js} +12 -12
- package/dist/{agents-VWY37DTK.js → agents-J4ICNLQ3.js} +52 -52
- package/dist/assets/codewiki.json +1 -1
- package/dist/assets/tui-notices/get-east-asian-width-LICENSE +9 -0
- package/dist/assets/tui-notices/marked-LICENSE +44 -0
- package/dist/assets/tui-notices/pi-tui-LICENSE +21 -0
- package/dist/{auth-PCV2XQDV.js → auth-X2UJUMOC.js} +24 -24
- package/dist/{builtins-PD652MTK.js → builtins-XLXWWSRF.js} +6 -6
- package/dist/{chunk-DZK63TZJ.js → chunk-24S3DN22.js} +336 -612
- package/dist/{chunk-NTK3WTHM.js → chunk-24TOGHPO.js} +2 -2
- package/dist/{chunk-M6TAYK66.js → chunk-2O2LVQQK.js} +11 -4
- package/dist/{chunk-OQM5IVJY.js → chunk-34XHRVKG.js} +3 -3
- package/dist/{chunk-LY4N4TTL.js → chunk-3LXJOICS.js} +2 -2
- package/dist/{chunk-3BJGMT2D.js → chunk-3XE4NRLF.js} +1 -1
- package/dist/{chunk-N2OKBREN.js → chunk-4MFBBY2Z.js} +3 -3
- package/dist/{chunk-5VTJP36Q.js → chunk-4N2RZ4TO.js} +2 -2
- package/dist/{chunk-WNGF5AJX.js → chunk-4QKAITQT.js} +3 -3
- package/dist/chunk-4UHVS5T7.js +207 -0
- package/dist/{chunk-GLYBJ4RQ.js → chunk-5AJPUR72.js} +6 -6
- package/dist/{chunk-5CY4Q6QI.js → chunk-5EQO5NWP.js} +105 -3
- package/dist/{chunk-6HAO6X5B.js → chunk-65BGHOUR.js} +8 -5
- package/dist/{chunk-6VRRJT7L.js → chunk-6CA2BHOR.js} +2 -2
- package/dist/{chunk-HL26ADLH.js → chunk-6CB2IL4N.js} +2 -1
- package/dist/{chunk-E7RF4KBO.js → chunk-6LRLOJHQ.js} +3 -3
- package/dist/{chunk-HXHFDHB6.js → chunk-6TAATXXH.js} +6 -6
- package/dist/{chunk-P6M7RKMI.js → chunk-6YHPAAZR.js} +3 -3
- package/dist/{chunk-5QRX7SLS.js → chunk-7EEGBKV4.js} +7 -7
- package/dist/{chunk-BS4GAMUB.js → chunk-A67NR5AT.js} +2 -2
- package/dist/{chunk-WJIMKGD6.js → chunk-BAU36233.js} +8 -8
- package/dist/{chunk-PZN2LQY6.js → chunk-BO4XV5VX.js} +4 -4
- package/dist/{chunk-I2K2NCZQ.js → chunk-BQQ5MXUC.js} +6 -6
- package/dist/{chunk-X3BI7HTV.js → chunk-C3G5PNUO.js} +21 -21
- package/dist/{chunk-SRZOW5L2.js → chunk-C6GEXX4L.js} +1 -1
- package/dist/{chunk-6RYDAVQT.js → chunk-CE64C5K5.js} +219 -64
- package/dist/chunk-CP3HIR5H.js +2119 -0
- package/dist/{chunk-BBV5TKE6.js → chunk-CPXGJI2A.js} +2 -2
- package/dist/{chunk-IGLVVNEG.js → chunk-CULVKYTK.js} +4 -4
- package/dist/{chunk-DXLUVUD4.js → chunk-D2T66PDV.js} +5 -5
- package/dist/{chunk-NF3BK5WR.js → chunk-DACXIVVF.js} +5 -5
- package/dist/{chunk-IKRWLEDA.js → chunk-DLIYANB7.js} +20 -15
- package/dist/{chunk-RGZNQ5CT.js → chunk-DO7M3LSX.js} +9 -9
- package/dist/{chunk-2QPI3TR3.js → chunk-DVJL77SO.js} +2 -2
- package/dist/{chunk-ODUTYZIU.js → chunk-EUEXGXXR.js} +5 -5
- package/dist/{chunk-WKUYQBCG.js → chunk-EUZRMP4Y.js} +18 -16
- package/dist/{chunk-JMPL7NUB.js → chunk-G4LGUTOE.js} +4 -4
- package/dist/{chunk-CBSKDFWL.js → chunk-GEVQNST4.js} +2 -2
- package/dist/{chunk-NNWJYUNO.js → chunk-GFQ7P2UF.js} +7 -7
- package/dist/{chunk-SXQKNKRA.js → chunk-GI5RRMAH.js} +14 -14
- package/dist/{chunk-HPTJF6DG.js → chunk-GVZFCJHE.js} +336 -83
- package/dist/{chunk-ACCKVSHU.js → chunk-GXIWHXDB.js} +3 -3
- package/dist/{chunk-LUL5Z66S.js → chunk-HOX4YZ2K.js} +6 -5
- package/dist/chunk-HRAKUPIH.js +57 -0
- package/dist/{chunk-F5Z7EWMN.js → chunk-IJMW64DX.js} +50 -50
- package/dist/{chunk-QNWRHOMF.js → chunk-IMOD2YPV.js} +16 -9
- package/dist/{chunk-HZ2T2YED.js → chunk-IQCNVLO6.js} +28 -8
- package/dist/{chunk-HZFBVU5X.js → chunk-ISNCCX42.js} +8 -8
- package/dist/{chunk-7JSSKXG2.js → chunk-IZYTT3CD.js} +7 -3
- package/dist/chunk-KKEYGFX4.js +915 -0
- package/dist/{chunk-HHVJNVUJ.js → chunk-KS7ETA3P.js} +2 -2
- package/dist/{chunk-BLTEAUCB.js → chunk-L3Z5ANCV.js} +7 -7
- package/dist/{chunk-4VMV6OS6.js → chunk-LBFQMYS3.js} +2 -2
- package/dist/{chunk-URBGEOH3.js → chunk-M6Z2V6YU.js} +5 -5
- package/dist/{chunk-BT7HGDOX.js → chunk-MBJO5VNZ.js} +12 -12
- package/dist/chunk-MNQN3X6Y.js +595 -0
- package/dist/{chunk-3QKXHCGS.js → chunk-MZHXENL7.js} +5 -5
- package/dist/chunk-MZNWZUJM.js +1801 -0
- package/dist/{chunk-7YJWEF2L.js → chunk-N37QC5FV.js} +5 -5
- package/dist/{chunk-TJFDL26W.js → chunk-NCCQ6DVS.js} +7 -7
- package/dist/{chunk-P5JMBPDJ.js → chunk-NO7J45X6.js} +162 -47
- package/dist/chunk-OH3YYA3B.js +15 -0
- package/dist/{chunk-22PQ4TX4.js → chunk-P4DL2LM5.js} +16 -77
- package/dist/{chunk-LIN2K3EL.js → chunk-PFLOULP3.js} +4 -4
- package/dist/{chunk-QXFBABFJ.js → chunk-QKWL7SYE.js} +11 -11
- package/dist/chunk-RFCKPS2V.js +26 -0
- package/dist/{chunk-ZS43Y2FT.js → chunk-RTLNFZMD.js} +12 -4
- package/dist/{chunk-U7H5WRG5.js → chunk-RXFPB32E.js} +8 -6
- package/dist/{chunk-Q7NNUHQV.js → chunk-SEDI5K5B.js} +16 -16
- package/dist/{chunk-WV6TDAPN.js → chunk-SELG4SUR.js} +7 -7
- package/dist/{chunk-3WBALRQH.js → chunk-SER7RRAE.js} +27 -832
- package/dist/{chunk-K2HL3GBL.js → chunk-T4TY5GG3.js} +165 -15
- package/dist/chunk-T7QQBNLH.js +691 -0
- package/dist/{chunk-6OII3LOK.js → chunk-TLTGZTXI.js} +3 -3
- package/dist/chunk-TZ6UVP2H.js +637 -0
- package/dist/{chunk-3JLFK5MW.js → chunk-TZHN5ITX.js} +13 -41
- package/dist/{chunk-VANN3GDP.js → chunk-U3TPUXOQ.js} +399 -234
- package/dist/{chunk-VHMX7SXF.js → chunk-UA3W4FGC.js} +6 -6
- package/dist/{chunk-3O2ZAUGQ.js → chunk-UA6W4XFJ.js} +2 -2
- package/dist/chunk-UWD3JD5X.js +709 -0
- package/dist/chunk-V4ZKOKRB.js +13433 -0
- package/dist/{chunk-NC4KV7EL.js → chunk-VCRVEDUC.js} +5 -5
- package/dist/{chunk-4DU2TVI4.js → chunk-VMATUZB6.js} +12 -10
- package/dist/{chunk-J2FYEQ5L.js → chunk-VTYHHQ57.js} +2 -2
- package/dist/chunk-WB7POC6M.js +891 -0
- package/dist/{chunk-3HQVBIKD.js → chunk-WBVKKTUT.js} +2 -2
- package/dist/{chunk-EHOSNVVM.js → chunk-WLHX3MM5.js} +6 -2
- package/dist/{chunk-24T2YZ7A.js → chunk-WN552PKN.js} +553 -298
- package/dist/{chunk-4U4TKZFR.js → chunk-WT5TRMO4.js} +51 -26
- package/dist/{chunk-XWMSPJYN.js → chunk-WURGVOZJ.js} +56 -12
- package/dist/{chunk-7WUZEB2M.js → chunk-XR3XJN2M.js} +2 -2
- package/dist/chunk-XW2UZICE.js +372 -0
- package/dist/{chunk-2AKYFREM.js → chunk-XZ6UFQ52.js} +11 -11
- package/dist/chunk-Y3JU5RPK.js +8 -0
- package/dist/chunk-YKRJTYNP.js +1143 -0
- package/dist/{chunk-PWWSIB2A.js → chunk-ZGIGW4ZG.js} +5 -5
- package/dist/cli/index.js +34 -36
- package/dist/{clio-QQCMTUZE.js → clio-L2P7S2R2.js} +9 -9
- package/dist/{code-nav-GUPM6766.js → code-nav-B5LNX4KU.js} +25 -25
- package/dist/{components-TNIKXCBH.js → components-63MMQKUT.js} +6 -6
- package/dist/{config-IIM7YOD3.js → config-YXARKGTX.js} +70 -67
- package/dist/{configure-4ZM5HWJX.js → configure-LMVQJ46J.js} +47 -35
- package/dist/{context-OL5P3HEP.js → context-7HJEWTTP.js} +87 -44
- package/dist/{context-44BZLJ3J.js → context-XSZKKFBW.js} +14 -14
- package/dist/{context-AIDVQKMW.js → context-YG2LZDYZ.js} +60 -58
- package/dist/{context-clear-OQV76MYP.js → context-clear-AC5NXHM2.js} +57 -55
- package/dist/{context-index-HP2JFUWA.js → context-index-KKTPRV5G.js} +3 -3
- package/dist/{context-map-INTXD54D.js → context-map-T7ROR5D5.js} +4 -4
- package/dist/{context-working-set-ZUW3EGLS.js → context-working-set-F4Q4J3NS.js} +12 -10
- package/dist/{detail-UXPWRANG.js → detail-H73BEF2N.js} +58 -56
- package/dist/{dispatch-runner-P3ZW6G77.js → dispatch-runner-3RY5FSJC.js} +74 -72
- package/dist/{doctor-JJVGPZWT.js → doctor-EQYJOGUG.js} +44 -30
- package/dist/{eval-V2RTN3UH.js → eval-4P7O6CCE.js} +109 -46
- package/dist/{evidence-35PYWGSO.js → evidence-D6GUSYJW.js} +57 -55
- package/dist/{evidence-CIGGKI6J.js → evidence-O76QF7TZ.js} +62 -59
- package/dist/{evolve-33SM3NJP.js → evolve-H47BCETS.js} +58 -56
- package/dist/{extensions-YVRHX6ZI.js → extensions-NNL3ZOCU.js} +98 -20
- package/dist/{fleet-LFO7SIPC.js → fleet-KMNS6NOM.js} +106 -99
- package/dist/{fleet-commands-RLK72GU3.js → fleet-commands-DOWSYCBX.js} +14 -14
- package/dist/{fleet-decisions-3SBSCTCW.js → fleet-decisions-S3PTZV2D.js} +9 -9
- package/dist/{fleet-graph-JZIBCGMW.js → fleet-graph-F2DE2TME.js} +26 -24
- package/dist/{fleet-inspect-2L5YGGZJ.js → fleet-inspect-EN2Q6IVB.js} +60 -57
- package/dist/{fleet-preflight-QRSHIIDE.js → fleet-preflight-FRLETDD3.js} +3 -3
- package/dist/{fleet-validate-LIAVKDUU.js → fleet-validate-B2XDLS57.js} +28 -29
- package/dist/{fleet-verify-JIK3OQZR.js → fleet-verify-4JKUWGJ7.js} +57 -55
- package/dist/{fleet-view-IWHYGQKK.js → fleet-view-BHJFW26B.js} +60 -57
- package/dist/{init-NJEDOTQW.js → init-XAT5N5YM.js} +79 -74
- package/dist/interop-FVECHFKG.js +246 -0
- package/dist/{inventory-74UL7UHU.js → inventory-7KFQGLCV.js} +60 -57
- package/dist/library-NY2XOT75.js +492 -0
- package/dist/library-QAPAO23R.js +306 -0
- package/dist/library-import-5Y6MAS3R.js +183 -0
- package/dist/library-inventory-JT6HWITQ.js +56 -0
- package/dist/library-validation-EQUH25DC.js +42 -0
- package/dist/{memory-I76I5VUH.js → memory-DYVTTCP6.js} +58 -56
- package/dist/{models-D4YDIQZQ.js → models-MASFJ6Y2.js} +48 -36
- package/dist/{monitor-NKVD55HX.js → monitor-YMP4NXJJ.js} +64 -61
- package/dist/{orchestrator-I6OATVPK.js → orchestrator-AFQRRTEI.js} +3685 -1841
- package/dist/{panes-7BTYH3XQ.js → panes-D3Y2KFG5.js} +4 -4
- package/dist/{panes-WPOMEMRH.js → panes-NICTHQCV.js} +7 -7
- package/dist/{paths-J3KN7UAO.js → paths-PSFAUIGO.js} +5 -5
- package/dist/{preload-EJAWJEIO.js → preload-5Q7VXRFJ.js} +57 -55
- package/dist/{reset-4XMQKDNZ.js → reset-7NKZK7JN.js} +9 -9
- package/dist/{resources-TVYVXK3Q.js → resources-NILSIZAT.js} +94 -38
- package/dist/{run-OWQNNSJU.js → run-BPZGEBZV.js} +121 -105
- package/dist/{share-NPZZMUXG.js → share-EZ6ERBMH.js} +31 -28
- package/dist/skills-BTVVNYVM.js +100 -0
- package/dist/{skills-eval-J5TCKSJA.js → skills-eval-UY34MNK7.js} +110 -72
- package/dist/{skills-inventory-7II4DM57.js → skills-inventory-JDHSIA7I.js} +30 -25
- package/dist/{slash-commands-MDSDSWYF.js → slash-commands-K4FNQ2AT.js} +49 -46
- package/dist/{steer-S7D7IKPU.js → steer-2B3EQGFH.js} +4 -4
- package/dist/{support-5VAEC32W.js → support-L2SOL3IE.js} +7 -7
- package/dist/{targets-KILZEOCE.js → targets-44VKGFLN.js} +64 -58
- package/dist/{tasks-R6P6OV22.js → tasks-3KODKFU7.js} +12 -12
- package/dist/{terminal-lease-LZCG2KN3.js → terminal-lease-LXA7MMPO.js} +32 -19
- package/dist/{tools-6OCQ6SVB.js → tools-3UB7TEVR.js} +7 -7
- package/dist/{trace-JHLXWUMN.js → trace-TAMIU4OJ.js} +5 -4
- package/dist/{tui-primitives-RCY2HN7G.js → tui-primitives-A6MUW32V.js} +3 -2
- package/dist/{uninstall-FXYMFFRT.js → uninstall-QYVRPEM4.js} +9 -9
- package/dist/{upgrade-D2OZH6IQ.js → upgrade-7PMKM2JH.js} +15 -16
- package/dist/{usage-PIASNUS4.js → usage-6EQV7EQK.js} +70 -67
- package/dist/{verifiers-LTHUY2PX.js → verifiers-JLITWSHU.js} +15 -15
- package/dist/{verify-HYF7KBVE.js → verify-T7NAEVIG.js} +13 -13
- package/dist/{web-fetch-MQN4FH3X.js → web-fetch-W4TNW4Q7.js} +4 -4
- package/dist/{wiki-generate-RK7JQ5NP.js → wiki-generate-T2TJQJUA.js} +81 -76
- package/dist/{with-panes-OFTNAR4Y.js → with-panes-CHFC7HGI.js} +4 -4
- package/dist/worker/entry.js +283 -76
- package/docs/README.md +43 -0
- package/docs/architecture/architecture.md +8 -7
- package/docs/architecture/artifact-versions.md +3 -3
- package/docs/architecture/context-engine.md +35 -1
- package/docs/architecture/library.md +32 -0
- package/docs/architecture/middleware-and-components.md +1 -1
- package/docs/architecture/model-catalog.md +21 -1
- package/docs/architecture/pi-boundary.md +18 -0
- package/docs/architecture/prompt-envelope-and-tools.md +5 -5
- package/docs/architecture/safety-model.md +21 -2
- package/docs/architecture/session-lifecycle.md +12 -1
- package/docs/architecture/tui-design.md +108 -9
- package/docs/architecture/worker-context.md +106 -0
- package/docs/guide/authoring-plugins.md +160 -0
- package/docs/guide/built-in-agents.md +5 -5
- package/docs/guide/commands-and-modes.md +161 -56
- package/docs/guide/configuration-and-targets.md +1 -1
- package/docs/guide/configuration-reference.md +27 -34
- package/docs/guide/environment-variables.md +2 -2
- package/docs/guide/exit-codes-and-output.md +1 -1
- package/docs/guide/extensions-and-sharing.md +93 -53
- package/docs/guide/fleet-dispatch.md +31 -4
- package/docs/guide/glossary.md +1 -1
- package/docs/guide/harness-extensions.md +291 -0
- package/docs/guide/installation-and-lifecycle.md +9 -9
- package/docs/guide/interop.md +243 -0
- package/docs/guide/panes-and-files.md +3 -3
- package/docs/guide/plugins.md +80 -0
- package/docs/guide/resource-library.md +111 -45
- package/docs/guide/skills-marketplace.md +35 -72
- package/docs/guide/tool-usage.md +19 -7
- package/docs/guide/troubleshooting.md +1 -1
- package/docs/process/development-pipeline.md +14 -14
- package/docs/process/documentation-coverage.md +28 -1
- package/docs/process/documentation-guide.md +15 -7
- package/docs/process/eval-runner.md +8 -3
- package/docs/process/performance-methodology.md +1 -1
- package/docs/process/release-cut-checklist.md +4 -0
- package/docs/process/scientific-validation.md +23 -0
- package/evals/scalar-decision-machinery.ts +1 -1
- package/examples/extensions/README.md +67 -0
- package/examples/extensions/lab-status/clio-coder-extension.json +14 -0
- package/examples/extensions/lab-status/extension.mjs +68 -0
- package/examples/extensions/lab-status/jobs.synthetic.json +9 -0
- package/examples/extensions/measurements/clio-coder-extension.json +28 -0
- package/examples/extensions/measurements/summarize.cjs +23 -0
- package/library/README.md +109 -0
- package/library/_authoring/templates/agent/README.md +28 -0
- package/library/_authoring/templates/agent/agents/benchmark-scout.md +30 -0
- package/library/_authoring/templates/agent/plugin.json +23 -0
- package/library/_authoring/templates/fleet/README.md +29 -0
- package/library/_authoring/templates/fleet/fleets/pipeline-review.md +31 -0
- package/library/_authoring/templates/fleet/plugin.json +23 -0
- package/library/_authoring/templates/plugin/README.md +37 -0
- package/library/_authoring/templates/plugin/agents/data-curator.md +31 -0
- package/library/_authoring/templates/plugin/assets/curation-guide.md +22 -0
- package/library/_authoring/templates/plugin/plugin.json +42 -0
- package/library/_authoring/templates/plugin/prompts/inspect-dataset.md +15 -0
- package/library/_authoring/templates/plugin/skills/dataset-curation/SKILL.md +36 -0
- package/library/_authoring/templates/plugin/skills/dataset-curation/evals.md +17 -0
- package/library/_authoring/templates/prompt/README.md +28 -0
- package/library/_authoring/templates/prompt/plugin.json +23 -0
- package/library/_authoring/templates/prompt/prompts/paper-summary.md +26 -0
- package/library/_authoring/templates/skill/README.md +24 -0
- package/library/_authoring/templates/skill/SKILL.md +39 -0
- package/library/_authoring/templates/skill/evals.md +30 -0
- package/library/_authoring/templates/skill/plugin.json +23 -0
- package/library/agents/README.md +7 -0
- package/library/fleets/README.md +7 -0
- package/library/plugins/materio/README.md +137 -0
- package/library/plugins/materio/ai.iowarp.clio/agents/materio-lab-definer.md +208 -0
- package/library/plugins/materio/ai.iowarp.clio/agents/materio-literature-reviewer.md +308 -0
- package/library/plugins/materio/ai.iowarp.clio/agents/materio-research-explorer.md +249 -0
- package/library/plugins/materio/ai.iowarp.clio/agents/materio-task-executor.md +416 -0
- package/library/plugins/materio/ai.iowarp.clio/agents/materio-task-verifier.md +60 -0
- package/library/plugins/materio/ai.iowarp.clio/agents/materio-workflow-planner.md +238 -0
- package/library/plugins/materio/ai.iowarp.clio/fleets/materio-execute-task.md +50 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/add-task.md +66 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/archive-task.md +65 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/checkpoint.md +26 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/define-research-tasks.md +230 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/define-virtual-lab.md +181 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/execute-task.md +292 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/help.md +129 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/identify-research.md +206 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/literature-review.md +201 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/pause-research.md +12 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/progress.md +105 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/remove-task.md +63 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/resume-research.md +64 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/settings.md +16 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/status.md +92 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/upload-data.md +104 -0
- package/library/plugins/materio/ai.iowarp.clio/prompts/materio/wtfp.md +145 -0
- package/library/plugins/materio/ai.iowarp.clio/references/execution.md +56 -0
- package/library/plugins/materio/ai.iowarp.portability/provenance.json +9 -0
- package/library/plugins/materio/assets/actions/add-task.md +86 -0
- package/library/plugins/materio/assets/actions/archive-task.md +84 -0
- package/library/plugins/materio/assets/actions/checkpoint.md +47 -0
- package/library/plugins/materio/assets/actions/define-research-tasks.md +228 -0
- package/library/plugins/materio/assets/actions/define-virtual-lab.md +179 -0
- package/library/plugins/materio/assets/actions/execute-task.md +290 -0
- package/library/plugins/materio/assets/actions/help.md +140 -0
- package/library/plugins/materio/assets/actions/identify-research.md +204 -0
- package/library/plugins/materio/assets/actions/literature-review.md +199 -0
- package/library/plugins/materio/assets/actions/pause-research.md +34 -0
- package/library/plugins/materio/assets/actions/progress.md +125 -0
- package/library/plugins/materio/assets/actions/remove-task.md +82 -0
- package/library/plugins/materio/assets/actions/resume-research.md +84 -0
- package/library/plugins/materio/assets/actions/settings.md +38 -0
- package/library/plugins/materio/assets/actions/status.md +112 -0
- package/library/plugins/materio/assets/actions/upload-data.md +123 -0
- package/library/plugins/materio/assets/actions/wtfp.md +179 -0
- package/library/plugins/materio/assets/references/research-domains.md +134 -0
- package/library/plugins/materio/assets/references/research-policy.md +77 -0
- package/library/plugins/materio/assets/references/traditional-workflows.md +146 -0
- package/library/plugins/materio/assets/scripts/check_physics.py +271 -0
- package/library/plugins/materio/assets/scripts/check_scripts.py +248 -0
- package/library/plugins/materio/assets/scripts/project_plugin.py +165 -0
- package/library/plugins/materio/assets/scripts/research_state.py +400 -0
- package/library/plugins/materio/assets/scripts/verify_citations.py +616 -0
- package/library/plugins/materio/assets/templates/RESEARCH.md +39 -0
- package/library/plugins/materio/assets/templates/VIRTUAL-LAB.md +120 -0
- package/library/plugins/materio/assets/templates/WORKFLOW.md +42 -0
- package/library/plugins/materio/assets/templates/config.json +5 -0
- package/library/plugins/materio/evals/scripts.yaml +29 -0
- package/library/plugins/materio/plugin.json +530 -0
- package/library/plugins/materio/skills/materio-lab-definer/SKILL.md +74 -0
- package/library/plugins/materio/skills/materio-literature-reviewer/SKILL.md +64 -0
- package/library/plugins/materio/skills/materio-research-explorer/SKILL.md +74 -0
- package/library/plugins/materio/skills/materio-task-executor/SKILL.md +214 -0
- package/library/plugins/materio/skills/materio-task-verifier/SKILL.md +46 -0
- package/library/plugins/materio/skills/materio-workflow-planner/SKILL.md +79 -0
- package/library/plugins/materio/tests/test_check_physics.py +139 -0
- package/library/plugins/materio/tests/test_check_scripts.py +140 -0
- package/library/plugins/materio/tests/test_research_state.py +291 -0
- package/library/plugins/materio/tests/test_verify_citations.py +337 -0
- package/library/prompts/README.md +7 -0
- package/library/registry.yaml +921 -0
- package/library/skills/README.md +120 -0
- package/{skills → library/skills}/coding/ast-grep/SKILL.md +1 -1
- package/{skills → library/skills}/coding/ast-grep/evals.md +1 -1
- package/library/skills/coding/ast-grep/plugin.json +23 -0
- package/{skills → library/skills}/coding/coding-standards/SKILL.md +1 -1
- package/{skills → library/skills}/coding/coding-standards/evals.md +1 -1
- package/library/skills/coding/coding-standards/plugin.json +23 -0
- package/{skills → library/skills}/coding/prototype/SKILL.md +1 -1
- package/{skills → library/skills}/coding/prototype/evals.md +1 -1
- package/library/skills/coding/prototype/plugin.json +23 -0
- package/{skills → library/skills}/coding/tdd/SKILL.md +1 -1
- package/{skills → library/skills}/coding/tdd/evals.md +1 -1
- package/library/skills/coding/tdd/plugin.json +23 -0
- package/{skills → library/skills}/context/context-handoff/SKILL.md +1 -1
- package/{skills → library/skills}/context/context-handoff/evals.md +1 -1
- package/library/skills/context/context-handoff/plugin.json +23 -0
- package/{skills → library/skills}/context/context-prime/SKILL.md +1 -1
- package/{skills → library/skills}/context/context-prime/evals.md +1 -1
- package/library/skills/context/context-prime/plugin.json +23 -0
- package/{skills → library/skills}/git/branch-closeout/SKILL.md +1 -1
- package/library/skills/git/branch-closeout/plugin.json +23 -0
- package/{skills → library/skills}/git/file-ticket/SKILL.md +1 -1
- package/library/skills/git/file-ticket/plugin.json +23 -0
- package/{skills → library/skills}/git/fix-issue/SKILL.md +1 -1
- package/library/skills/git/fix-issue/plugin.json +23 -0
- package/{skills → library/skills}/git/resolve-merge-conflicts/SKILL.md +1 -1
- package/{skills → library/skills}/git/resolve-merge-conflicts/evals.md +1 -1
- package/library/skills/git/resolve-merge-conflicts/plugin.json +23 -0
- package/{skills → library/skills}/git/ship/SKILL.md +1 -1
- package/library/skills/git/ship/plugin.json +23 -0
- package/{skills → library/skills}/git/worktree-create/SKILL.md +1 -1
- package/library/skills/git/worktree-create/plugin.json +23 -0
- package/{skills → library/skills}/git/worktree-merge/SKILL.md +1 -1
- package/library/skills/git/worktree-merge/plugin.json +23 -0
- package/{skills → library/skills}/meta/clio-coder-dev/SKILL.md +1 -1
- package/{skills → library/skills}/meta/clio-coder-dev/evals.md +1 -1
- package/library/skills/meta/clio-coder-dev/plugin.json +23 -0
- package/{skills → library/skills}/meta/clio-coder-test/SKILL.md +1 -1
- package/{skills → library/skills}/meta/clio-coder-test/evals.md +1 -1
- package/library/skills/meta/clio-coder-test/plugin.json +23 -0
- package/{skills → library/skills}/meta/credentials/SKILL.md +1 -1
- package/{skills → library/skills}/meta/credentials/evals.md +1 -1
- package/library/skills/meta/credentials/plugin.json +23 -0
- package/library/skills/meta/find-skills/SKILL.md +85 -0
- package/library/skills/meta/find-skills/evals.md +65 -0
- package/library/skills/meta/find-skills/plugin.json +23 -0
- package/{skills → library/skills}/meta/herdr/SKILL.md +1 -1
- package/{skills → library/skills}/meta/herdr/evals.md +1 -1
- package/library/skills/meta/herdr/plugin.json +23 -0
- package/{skills → library/skills}/meta/skill-craft/SKILL.md +23 -9
- package/{skills → library/skills}/meta/skill-craft/evals.md +11 -3
- package/library/skills/meta/skill-craft/plugin.json +23 -0
- package/{skills → library/skills}/planning/archify/SKILL.md +13 -13
- package/library/skills/planning/archify/plugin.json +23 -0
- package/{skills → library/skills}/planning/architecture/SKILL.md +1 -1
- package/{skills → library/skills}/planning/architecture/evals.md +1 -1
- package/library/skills/planning/architecture/plugin.json +23 -0
- package/{skills → library/skills}/planning/backlog/SKILL.md +1 -1
- package/{skills → library/skills}/planning/backlog/evals.md +1 -1
- package/library/skills/planning/backlog/plugin.json +23 -0
- package/{skills → library/skills}/planning/prd/SKILL.md +1 -1
- package/{skills → library/skills}/planning/prd/evals.md +1 -1
- package/library/skills/planning/prd/plugin.json +23 -0
- package/{skills → library/skills}/planning/product-intent/SKILL.md +1 -1
- package/{skills → library/skills}/planning/product-intent/evals.md +1 -1
- package/library/skills/planning/product-intent/plugin.json +23 -0
- package/{skills → library/skills}/planning/tech-spec/SKILL.md +1 -1
- package/{skills → library/skills}/planning/tech-spec/evals.md +1 -1
- package/library/skills/planning/tech-spec/plugin.json +23 -0
- package/{skills → library/skills}/registry.yaml +7 -7
- package/{skills → library/skills}/remote.yaml +1 -1
- package/{skills → library/skills}/research/arxiv-literature/SKILL.md +1 -1
- package/{skills → library/skills}/research/arxiv-literature/evals.md +1 -1
- package/library/skills/research/arxiv-literature/plugin.json +23 -0
- package/{skills → library/skills}/research/experiment-protocol/SKILL.md +1 -1
- package/{skills → library/skills}/research/experiment-protocol/evals.md +1 -1
- package/library/skills/research/experiment-protocol/plugin.json +23 -0
- package/{skills → library/skills}/research/scientific-debugging/SKILL.md +1 -1
- package/{skills → library/skills}/research/scientific-debugging/evals.md +1 -1
- package/library/skills/research/scientific-debugging/plugin.json +23 -0
- package/{skills → library/skills}/research/scientific-modernization/SKILL.md +1 -1
- package/{skills → library/skills}/research/scientific-modernization/evals.md +1 -1
- package/library/skills/research/scientific-modernization/plugin.json +23 -0
- package/{skills → library/skills}/skill-marketplace.json +37 -37
- package/{skills → library/skills}/workflow/cut-it/SKILL.md +1 -1
- package/{skills → library/skills}/workflow/cut-it/evals.md +1 -1
- package/library/skills/workflow/cut-it/plugin.json +23 -0
- package/{skills → library/skills}/workflow/design-council/SKILL.md +1 -1
- package/{skills → library/skills}/workflow/design-council/evals.md +1 -1
- package/library/skills/workflow/design-council/plugin.json +23 -0
- package/{skills → library/skills}/workflow/grill-me/SKILL.md +1 -1
- package/{skills → library/skills}/workflow/grill-me/evals.md +1 -1
- package/library/skills/workflow/grill-me/plugin.json +23 -0
- package/{skills → library/skills}/workflow/workflow-distiller/SKILL.md +14 -11
- package/{skills → library/skills}/workflow/workflow-distiller/evals.md +14 -6
- package/library/skills/workflow/workflow-distiller/plugin.json +23 -0
- package/package.json +12 -5
- package/src/cli/agents.ts +1 -1
- package/src/cli/config-inspect.ts +1 -1
- package/src/cli/configure.ts +2 -2
- package/src/cli/eval.ts +40 -6
- package/src/cli/extensions.ts +74 -8
- package/src/cli/fleet-preflight.ts +2 -0
- package/src/cli/fleet.ts +2 -0
- package/src/cli/index.ts +1 -3
- package/src/cli/interop-inspect.ts +61 -33
- package/src/cli/interop.ts +79 -9
- package/src/cli/library-import.ts +139 -0
- package/src/cli/library.ts +418 -120
- package/src/cli/run.ts +26 -9
- package/src/cli/skills-eval.ts +43 -6
- package/src/cli/skills-inventory.ts +6 -2
- package/src/cli/skills.ts +44 -491
- package/src/core/bus-events.ts +5 -0
- package/src/core/defaults.ts +1 -1
- package/src/core/recent-models.ts +2 -2
- package/src/core/session-routing.ts +3 -3
- package/src/core/state-file-lock.ts +1 -1
- package/src/core/tool-names.ts +5 -0
- package/src/domains/agents/extension.ts +42 -6
- package/src/domains/agents/fleet-commands.ts +92 -1
- package/src/domains/agents/fleet-contract.ts +87 -19
- package/src/domains/agents/recipe.ts +5 -4
- package/src/domains/agents/registry.ts +80 -24
- package/src/domains/config/keybindings.ts +155 -95
- package/src/domains/context/worker/contract.ts +91 -0
- package/src/domains/context/worker/pressure.ts +120 -0
- package/src/domains/context/worker/recall.ts +62 -0
- package/src/domains/context/worker/select.ts +164 -0
- package/src/domains/context/worker/snapshot.ts +35 -0
- package/src/domains/context/worker/store.ts +16 -0
- package/src/domains/context/working-set/replay/synthetic.ts +2 -1
- package/src/domains/dispatch/active-route-planner.ts +2 -0
- package/src/domains/dispatch/backoff.ts +1 -0
- package/src/domains/dispatch/code-step.ts +12 -2
- package/src/domains/dispatch/contract.ts +3 -0
- package/src/domains/dispatch/execution-plan.ts +2 -0
- package/src/domains/dispatch/extension.ts +109 -27
- package/src/domains/dispatch/fleet-plan.ts +10 -0
- package/src/domains/dispatch/fleet-run.ts +1 -0
- package/src/domains/dispatch/orphan-recovery.ts +1 -0
- package/src/domains/dispatch/receipt-integrity.ts +4 -0
- package/src/domains/dispatch/state.ts +1 -0
- package/src/domains/dispatch/types.ts +5 -0
- package/src/domains/dispatch/validation.ts +12 -0
- package/src/domains/dispatch/workspace-checkpoint.ts +1 -1
- package/src/domains/evidence/failure-attribution.ts +1 -0
- package/src/domains/evidence/trust-status.ts +22 -3
- package/src/domains/extensions/command-schema.ts +145 -0
- package/src/domains/extensions/command-tools.ts +15 -0
- package/src/domains/extensions/compatibility.ts +7 -0
- package/src/domains/extensions/contract.ts +0 -4
- package/src/domains/extensions/discovery.ts +69 -205
- package/src/domains/extensions/extension.ts +0 -4
- package/src/domains/extensions/index.ts +1 -5
- package/src/domains/extensions/manager.ts +1 -6
- package/src/domains/extensions/operator-commands.ts +29 -0
- package/src/domains/extensions/operator-runtime.ts +483 -0
- package/src/domains/extensions/public-api.ts +53 -0
- package/src/domains/extensions/runtime-child.mjs +114 -0
- package/src/domains/extensions/runtime-process.ts +306 -0
- package/src/domains/extensions/runtime-schema.ts +146 -0
- package/src/domains/extensions/{resources.ts → snapshot-access.ts} +1 -17
- package/src/domains/extensions/snapshot.ts +4 -32
- package/src/domains/extensions/state.ts +5 -83
- package/src/domains/extensions/types.ts +33 -34
- package/src/domains/interop/adopt.ts +546 -0
- package/src/domains/interop/detect.ts +50 -12
- package/src/domains/interop/foreign.ts +554 -0
- package/src/domains/interop/import.ts +277 -0
- package/src/domains/interop/index.ts +24 -0
- package/src/domains/interop/install.ts +6 -0
- package/src/domains/interop/inventory.ts +351 -0
- package/src/domains/interop/projection.ts +360 -0
- package/src/domains/interop/registry.ts +106 -3
- package/src/domains/interop/types.ts +51 -0
- package/src/domains/lifecycle/migrations/index.ts +0 -2
- package/src/domains/lifecycle/naming-resources.ts +1 -1
- package/src/domains/middleware/marketplace-offer.ts +1 -1
- package/src/domains/mux/contract.ts +1 -1
- package/src/domains/plugins/catalog.ts +204 -0
- package/src/domains/plugins/contract.ts +24 -0
- package/src/domains/plugins/discovery.ts +316 -0
- package/src/domains/plugins/extension.ts +40 -0
- package/src/domains/plugins/index.ts +77 -0
- package/src/domains/plugins/integrity.ts +6 -0
- package/src/domains/plugins/manifest.ts +3 -0
- package/src/domains/plugins/resources.ts +102 -0
- package/src/domains/plugins/state.ts +753 -0
- package/src/domains/plugins/types.ts +217 -0
- package/src/domains/prompts/extension.ts +9 -2
- package/src/domains/prompts/fragments/operating/skills.md +10 -10
- package/src/domains/resources/common-loader.ts +9 -6
- package/src/domains/resources/index.ts +67 -0
- package/src/domains/resources/library-actions.ts +836 -0
- package/src/domains/resources/library-evals.ts +38 -0
- package/src/domains/resources/library-inventory.ts +1048 -0
- package/src/domains/resources/library-types.ts +50 -0
- package/src/domains/resources/library-validation.ts +634 -0
- package/src/domains/resources/library.ts +437 -156
- package/src/domains/resources/package-references.ts +66 -0
- package/src/domains/resources/prompts/loader.ts +75 -41
- package/src/domains/resources/skills/install.ts +2 -2
- package/src/domains/resources/skills/loader.ts +77 -22
- package/src/domains/resources/skills/marketplace.ts +48 -57
- package/src/domains/resources/skills/promotion.ts +2 -2
- package/src/domains/resources/skills/provenance-pin.ts +17 -4
- package/src/domains/safety/action-classifier.ts +2 -2
- package/src/domains/safety/default-path-policy.ts +3 -1
- package/src/domains/safety/policy-engine.ts +1 -1
- package/src/domains/safety/protected-artifacts.ts +21 -5
- package/src/domains/safety/skill-authority.ts +5 -2
- package/src/domains/share/archive.ts +1 -1
- package/src/engine/apis/openai-completions.ts +52 -2
- package/src/engine/notices/pi-tui-LICENSE +21 -0
- package/src/engine/tui.ts +3 -0
- package/src/engine/worker-runtime.ts +58 -14
- package/src/engine/worker-tools.ts +5 -0
- package/src/entry/orchestrator.ts +26 -5
- package/src/entry/plugin-reload.ts +18 -0
- package/src/interactive/application-controller.ts +67 -21
- package/src/interactive/chat-loop.ts +32 -5
- package/src/interactive/chat-panel.ts +5 -1
- package/src/interactive/clio-editor.ts +53 -21
- package/src/interactive/command-output.ts +56 -1
- package/src/interactive/editor-submit.ts +65 -28
- package/src/interactive/fleet-run-preview.ts +2 -0
- package/src/interactive/follow-up-queue-panel.ts +3 -3
- package/src/interactive/footer/dashboard.ts +4 -1
- package/src/interactive/footer/notifications.ts +2 -2
- package/src/interactive/footer/widgets.ts +2 -2
- package/src/interactive/interactive-application.ts +209 -21
- package/src/interactive/interactive-input-runtime.ts +254 -7
- package/src/interactive/interactive-presentation.ts +41 -12
- package/src/interactive/interactive-slash-runtime.ts +37 -8
- package/src/interactive/keybinding-manager.ts +156 -79
- package/src/interactive/keyboard-owner.ts +37 -0
- package/src/interactive/leader-key.ts +49 -102
- package/src/interactive/leader-menu.ts +45 -0
- package/src/interactive/overlay-ask-user-lifecycle.ts +9 -1
- package/src/interactive/overlay-frame.ts +15 -0
- package/src/interactive/overlay-key-routing.ts +46 -47
- package/src/interactive/overlay-lifecycle.ts +30 -15
- package/src/interactive/overlay-resource-openers.ts +56 -46
- package/src/interactive/overlays/ask-user.ts +630 -556
- package/src/interactive/overlays/extension-panel.ts +70 -0
- package/src/interactive/overlays/extensions.ts +28 -5
- package/src/interactive/overlays/fleet-run-approval.ts +7 -3
- package/src/interactive/overlays/help-reference.ts +89 -40
- package/src/interactive/overlays/interop.ts +122 -3
- package/src/interactive/overlays/keybinding-detail.ts +2 -2
- package/src/interactive/overlays/library-lifecycle.ts +90 -0
- package/src/interactive/overlays/library-model.ts +574 -0
- package/src/interactive/overlays/library-review.ts +557 -0
- package/src/interactive/overlays/library-tabs.ts +3 -15
- package/src/interactive/overlays/library.ts +602 -0
- package/src/interactive/overlays/list-overlay.ts +218 -39
- package/src/interactive/overlays/model-selector.ts +28 -25
- package/src/interactive/overlays/session-selector.ts +9 -3
- package/src/interactive/overlays/settings.ts +25 -18
- package/src/interactive/overlays/tree-selector.ts +16 -11
- package/src/interactive/permission-hint.ts +13 -0
- package/src/interactive/permission-overlay.ts +77 -28
- package/src/interactive/prompt-source-label.ts +21 -0
- package/src/interactive/renderers/reference-card.ts +35 -0
- package/src/interactive/renderers/worker-entry.ts +35 -3
- package/src/interactive/slash-autocomplete.ts +99 -1
- package/src/interactive/slash-commands.ts +390 -67
- package/src/interactive/slash-spec.ts +0 -1
- package/src/interactive/terminal-lease.ts +44 -12
- package/src/interactive/turn-queues.ts +8 -7
- package/src/interactive/turn-runtime.ts +1 -1
- package/src/interactive/view/view-overlay.ts +19 -14
- package/src/interactive/welcome-dashboard.ts +525 -408
- package/src/tools/bootstrap.ts +8 -0
- package/src/tools/builtin-tool-catalog.ts +7 -2
- package/src/tools/context/index.ts +58 -5
- package/src/tools/context/library.ts +465 -0
- package/src/tools/context/surface.ts +20 -6
- package/src/tools/core-bootstrap.ts +3 -0
- package/src/tools/dispatch-admission.ts +24 -1
- package/src/tools/dispatch-arguments.ts +8 -0
- package/src/tools/dispatch-plan.ts +32 -0
- package/src/tools/dispatch-schema.ts +31 -1
- package/src/tools/dispatch-types.ts +3 -0
- package/src/tools/dispatch.ts +1 -1
- package/src/tools/harness-extensions.ts +169 -0
- package/src/tools/observation.ts +4 -0
- package/src/tools/policy.ts +7 -1
- package/src/tools/registry.ts +19 -1
- package/src/tools/worker-evidence.ts +5 -0
- package/src/worker/context-seed.ts +192 -0
- package/src/worker/entry.ts +1 -0
- package/src/worker/spec-contract.ts +8 -1
- package/dist/chunk-3UW3KU4A.js +0 -21
- package/dist/chunk-536HXRFE.js +0 -1257
- package/dist/chunk-B3MOMLJ4.js +0 -433
- package/dist/chunk-CXQ37WJH.js +0 -45
- package/dist/chunk-KTFSFCVN.js +0 -46
- package/dist/chunk-QCOV5DFY.js +0 -911
- package/dist/chunk-QRFXEO6Y.js +0 -17
- package/dist/chunk-VAHLPXPY.js +0 -399
- package/dist/interop-RVCAYECG.js +0 -115
- package/dist/library-CTKZA7K3.js +0 -226
- package/dist/skills-OXFEAWMT.js +0 -510
- package/skills/README.md +0 -418
- package/skills/meta/find-skills/SKILL.md +0 -78
- package/skills/meta/find-skills/evals.md +0 -47
- package/src/domains/extensions/resource-path.ts +0 -27
- package/src/domains/lifecycle/migrations/2026-09-01-extension-install-digests.ts +0 -27
- package/src/interactive/overlays/agents.ts +0 -70
- package/src/interactive/overlays/library-install-confirm.ts +0 -152
- package/src/interactive/overlays/prompts.ts +0 -89
- package/src/interactive/overlays/skills-hub.ts +0 -615
- /package/{skills → library/skills}/coding/ast-grep/references/rule_reference.md +0 -0
- /package/{skills → library/skills}/coding/prototype/references/LOGIC.md +0 -0
- /package/{skills → library/skills}/coding/prototype/references/UI.md +0 -0
- /package/{skills → library/skills}/coding/tdd/references/mocking.md +0 -0
- /package/{skills → library/skills}/coding/tdd/references/tests.md +0 -0
- /package/{skills → library/skills}/context/context-handoff/scripts/new-handoff.sh +0 -0
- /package/{skills → library/skills}/git/branch-closeout/evals.md +0 -0
- /package/{skills → library/skills}/git/branch-closeout/references/closeout-checklist.md +0 -0
- /package/{skills → library/skills}/git/file-ticket/assets/issue-template.md +0 -0
- /package/{skills → library/skills}/git/file-ticket/evals.md +0 -0
- /package/{skills → library/skills}/git/file-ticket/references/issue-discovery.md +0 -0
- /package/{skills → library/skills}/git/fix-issue/evals.md +0 -0
- /package/{skills → library/skills}/git/fix-issue/references/diagnosis-and-rca.md +0 -0
- /package/{skills → library/skills}/git/resolve-merge-conflicts/references/conflict-matrix.md +0 -0
- /package/{skills → library/skills}/git/ship/assets/pr-template.md +0 -0
- /package/{skills → library/skills}/git/ship/evals.md +0 -0
- /package/{skills → library/skills}/git/ship/references/remote-and-branch-policy.md +0 -0
- /package/{skills → library/skills}/git/worktree-create/evals.md +0 -0
- /package/{skills → library/skills}/git/worktree-create/references/worktree-setup.md +0 -0
- /package/{skills → library/skills}/git/worktree-merge/evals.md +0 -0
- /package/{skills → library/skills}/git/worktree-merge/references/merge-strategies.md +0 -0
- /package/{skills → library/skills}/meta/clio-coder-test/references/harness.md +0 -0
- /package/{skills → library/skills}/meta/clio-coder-test/references/test-map.md +0 -0
- /package/{skills → library/skills}/planning/archify/evals.md +0 -0
|
@@ -1,102 +1,122 @@
|
|
|
1
|
-
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
2
|
-
import { basename, join } from "node:path";
|
|
3
1
|
import type { ClioSettings } from "../core/config.js";
|
|
4
2
|
import { readClioVersion } from "../core/package-root.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { listWikiPages } from "../domains/context/wiki/layout.js";
|
|
9
|
-
import {
|
|
10
|
-
type WikiStaleness,
|
|
11
|
-
wikiCompleteness,
|
|
12
|
-
wikiStaleness,
|
|
13
|
-
wikiStalenessAsync,
|
|
14
|
-
} from "../domains/context/wiki/staleness.js";
|
|
15
|
-
import type { TaskMemoryOperatorStatus } from "../domains/memory/index.js";
|
|
16
|
-
import type { ObservabilityContract } from "../domains/observability/index.js";
|
|
17
|
-
import {
|
|
18
|
-
type CapabilityFlags,
|
|
19
|
-
type ProvidersContract,
|
|
20
|
-
resolveModelCapabilities,
|
|
21
|
-
resolveModelRuntimeCapabilitiesForProviders,
|
|
22
|
-
type TargetStatus,
|
|
23
|
-
} from "../domains/providers/index.js";
|
|
24
|
-
import type { ContextUsageSnapshot } from "../domains/session/context-accounting.js";
|
|
3
|
+
import type { ContextState } from "../domains/context/index.js";
|
|
4
|
+
import type { ProvidersContract, TargetStatus } from "../domains/providers/index.js";
|
|
5
|
+
import { sanitizeCallTargetText } from "../domains/safety/call-target.js";
|
|
25
6
|
import type { WorkspaceSnapshot } from "../domains/session/workspace/index.js";
|
|
26
|
-
import { type Component, truncateToWidth } from "../engine/tui.js";
|
|
27
|
-
import {
|
|
28
|
-
|
|
7
|
+
import { type Component, truncateToWidth, visibleWidth } from "../engine/tui.js";
|
|
8
|
+
import {
|
|
9
|
+
brandMark,
|
|
10
|
+
type ClioTheme,
|
|
11
|
+
type ClioToken,
|
|
12
|
+
clioTheme,
|
|
13
|
+
collapseHomePath,
|
|
14
|
+
formatTargetLabel,
|
|
15
|
+
GLYPH,
|
|
16
|
+
padAnsi,
|
|
17
|
+
} from "./theme/index.js";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* What the route can honestly be said to be.
|
|
21
|
+
*
|
|
22
|
+
* `checking` is its own state and is never spelled as ready or unavailable. A
|
|
23
|
+
* target that is configured but not yet probed reports
|
|
24
|
+
* `health.status === "unknown"` with `available` still true, and printing that
|
|
25
|
+
* as either verdict is a claim the process has not earned. `available === false`
|
|
26
|
+
* is a different thing: it is a configuration or auth-level refusal that holds
|
|
27
|
+
* without any probe (see `authStatusFor` in domains/providers/extension.ts), so
|
|
28
|
+
* it is reported as unavailable immediately.
|
|
29
|
+
*/
|
|
30
|
+
export type WelcomeRouteState = "checking" | "ready" | "degraded" | "unavailable" | "unset";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Project-context state, mirroring `ContextState["clioMd"]` plus the honest
|
|
34
|
+
* fourth answer for "the authoritative read has not happened yet". `stale` is
|
|
35
|
+
* carried through rather than folded into `ok`, because a stale CLIO-CODER.md
|
|
36
|
+
* has a different next step than a current one.
|
|
37
|
+
*/
|
|
38
|
+
export type WelcomeProjectContextState = "checking" | "ok" | "stale" | "none" | "malformed";
|
|
29
39
|
|
|
30
40
|
export interface WelcomeDashboardDeps {
|
|
31
|
-
providers: ProvidersContract
|
|
32
|
-
observability: ObservabilityContract;
|
|
33
|
-
getContextUsage?: () => ContextUsageSnapshot;
|
|
41
|
+
providers: Pick<ProvidersContract, "list">;
|
|
34
42
|
getSettings?: () => Readonly<ClioSettings>;
|
|
35
43
|
getWorkspaceSnapshot?: () => WorkspaceSnapshot | null;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
/**
|
|
45
|
+
* The authoritative project-context reader from the context domain. It is
|
|
46
|
+
* synchronous on a cache miss (it walks ancestors for CLIO-CODER.md), so it
|
|
47
|
+
* is never called from `render`; the dashboard refreshes it off the frame and
|
|
48
|
+
* paints `checking` until the first reading lands.
|
|
49
|
+
*/
|
|
50
|
+
getContextState?: (cwd: string) => ContextState;
|
|
51
|
+
/** Effective label for the submit binding; null when unbound or disabled. */
|
|
52
|
+
getSubmitKeyLabel?: () => string | null;
|
|
53
|
+
/** Called when an off-render refresh lands, so the frame owner can ask for one. */
|
|
41
54
|
onFactsRefreshed?: () => void;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* The part of the banner that costs subprocesses and disk reads rather than a
|
|
46
|
-
* map lookup: CLIO-CODER.md presence, the codewiki, wiki staleness and completeness,
|
|
47
|
-
* and the handoff directory. None of it can change as a result of drawing a
|
|
48
|
-
* frame, and re-deriving it per frame ran three `git` subprocesses and a full
|
|
49
|
-
* codewiki parse inside the render timer.
|
|
50
|
-
*/
|
|
51
|
-
export interface WelcomeRepositoryFacts {
|
|
52
|
-
clioMdStatus: string;
|
|
53
|
-
hasCodewiki: boolean;
|
|
54
|
-
codewikiCount: number;
|
|
55
|
-
wikiPageCount: number;
|
|
56
|
-
wikiStatus: string;
|
|
57
|
-
wikiDigestExcerpt: string[];
|
|
58
|
-
handoffCount: number;
|
|
59
|
-
handoffFreshness: string;
|
|
60
55
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* to clear and repaint the entire buffer once the transcript has scrolled.
|
|
56
|
+
* How the off-render refresh is deferred. Production yields to the event loop
|
|
57
|
+
* so no filesystem work ever runs on the render call stack; tests inject a
|
|
58
|
+
* synchronous runner for determinism.
|
|
65
59
|
*/
|
|
66
|
-
|
|
60
|
+
scheduleRefresh?: (run: () => void) => void;
|
|
61
|
+
now?: () => number;
|
|
67
62
|
}
|
|
68
63
|
|
|
69
64
|
export interface WelcomeDashboardStats {
|
|
70
|
-
|
|
71
|
-
|
|
65
|
+
cwd: string;
|
|
66
|
+
workspace: WorkspaceSnapshot | null;
|
|
72
67
|
/** Null when unset. `formatTargetLabel` owns the one spelling for that. */
|
|
73
68
|
targetLabel: string | null;
|
|
74
69
|
modelLabel: string | null;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
handoffCount: number;
|
|
92
|
-
handoffFreshness: string;
|
|
93
|
-
taskMemory: TaskMemoryOperatorStatus | null;
|
|
94
|
-
/** Mirrors `WelcomeRepositoryFacts.pending`; drives the dim placeholder rows. */
|
|
95
|
-
factsPending?: boolean;
|
|
70
|
+
route: WelcomeRouteState;
|
|
71
|
+
/** The actionable half of a failure. Null whenever the route is fine. */
|
|
72
|
+
routeReason: string | null;
|
|
73
|
+
/** `checking` in session mode: the collapsed row does not show it, so it is not read. */
|
|
74
|
+
projectContext: WelcomeProjectContextState;
|
|
75
|
+
/** Null in session mode: the collapsed row prints no key hint. */
|
|
76
|
+
submitKeyLabel: string | null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export type WelcomeDashboardMode = "launchpad" | "session";
|
|
80
|
+
|
|
81
|
+
export interface WelcomeDashboardComponent extends Component {
|
|
82
|
+
collapseToSessionHeader(): boolean;
|
|
83
|
+
resetToLaunchpad(): boolean;
|
|
84
|
+
/** Stop off-render refreshes from requesting frames after teardown. */
|
|
85
|
+
dispose(): void;
|
|
96
86
|
}
|
|
97
87
|
|
|
98
|
-
|
|
99
|
-
|
|
88
|
+
/**
|
|
89
|
+
* How long a project-context reading is trusted, and how soon a reading that
|
|
90
|
+
* could not be taken is retried. The retry is shorter than the trust window so a
|
|
91
|
+
* transient failure resolves quickly, and longer than a frame so a permanently
|
|
92
|
+
* failing read cannot become a per-frame scan.
|
|
93
|
+
*
|
|
94
|
+
* A failure never renews the trust window. Once reads have been failing for
|
|
95
|
+
* longer than the window, the header stops asserting the last value and says
|
|
96
|
+
* `checking` instead, so a stale `ok` cannot outlive the evidence for it.
|
|
97
|
+
*/
|
|
98
|
+
export const WELCOME_PROJECT_CONTEXT_TTL_MS = 10_000;
|
|
99
|
+
export const WELCOME_PROJECT_CONTEXT_RETRY_MS = 2_000;
|
|
100
|
+
|
|
101
|
+
/** Columns the route's failure reason is allowed before it is cut. */
|
|
102
|
+
const ROUTE_REASON_MAX = 48;
|
|
103
|
+
/** The route text is shrunk to this before a failure reason is cut instead. */
|
|
104
|
+
const ROUTE_TARGET_FLOOR = 12;
|
|
105
|
+
/** Below this a reason is dropped; the action row still names the fault. */
|
|
106
|
+
const ROUTE_REASON_FLOOR = 8;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Neutralize a string that came from outside this process before it is styled.
|
|
110
|
+
*
|
|
111
|
+
* Provider reasons, upstream error bodies, branch names, model ids and paths all
|
|
112
|
+
* reach the header from somewhere the header does not control, and a header is a
|
|
113
|
+
* bad place to discover that a payload can move the cursor. `sanitizeCallTargetText`
|
|
114
|
+
* is the project's existing one-line sanitizer (OSC and CSI stripped, C0 and DEL
|
|
115
|
+
* neutralized, whitespace collapsed); this is a thin alias so the intent reads at
|
|
116
|
+
* every call site.
|
|
117
|
+
*/
|
|
118
|
+
function plainOneLine(value: string): string {
|
|
119
|
+
return sanitizeCallTargetText(value);
|
|
100
120
|
}
|
|
101
121
|
|
|
102
122
|
function findCurrentStatus(
|
|
@@ -108,363 +128,387 @@ function findCurrentStatus(
|
|
|
108
128
|
return statuses.find((status) => status.target.id === targetId) ?? null;
|
|
109
129
|
}
|
|
110
130
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (
|
|
120
|
-
|
|
121
|
-
|
|
131
|
+
/**
|
|
132
|
+
* One short line an operator can act on. Reasons arrive as anything from
|
|
133
|
+
* `ECONNREFUSED 127.0.0.1:1234` to a multi-line upstream body, so the text is
|
|
134
|
+
* sanitized to one line and then cut by terminal columns — not by code units,
|
|
135
|
+
* which would split a surrogate pair or a combining sequence.
|
|
136
|
+
*/
|
|
137
|
+
function shortRouteReason(status: TargetStatus): string | null {
|
|
138
|
+
const cleaned = plainOneLine(status.health.lastError ?? status.reason ?? "");
|
|
139
|
+
if (cleaned.length === 0) return null;
|
|
140
|
+
return visibleWidth(cleaned) > ROUTE_REASON_MAX
|
|
141
|
+
? truncateToWidth(cleaned, ROUTE_REASON_MAX, GLYPH.ellipsis, false)
|
|
142
|
+
: cleaned;
|
|
122
143
|
}
|
|
123
144
|
|
|
124
|
-
function
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
):
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
145
|
+
function deriveRoute(
|
|
146
|
+
current: TargetStatus | null,
|
|
147
|
+
targetLabel: string | null,
|
|
148
|
+
modelLabel: string | null,
|
|
149
|
+
): { route: WelcomeRouteState; routeReason: string | null } {
|
|
150
|
+
// A route needs both halves. A configured target with no model and no default
|
|
151
|
+
// model cannot answer a prompt, so it is unset rather than ready: the previous
|
|
152
|
+
// header advertised `dynamo · no model · ready`, which was never true.
|
|
153
|
+
if (targetLabel === null || modelLabel === null) return { route: "unset", routeReason: null };
|
|
154
|
+
// A target id that matches no listed target is a real misconfiguration, not an
|
|
155
|
+
// unprobed one.
|
|
156
|
+
if (current === null) return { route: "unavailable", routeReason: "no such target in settings" };
|
|
157
|
+
// `available: false` is decided from configuration and auth without probing.
|
|
158
|
+
if (!current.available || current.health.status === "down")
|
|
159
|
+
return { route: "unavailable", routeReason: shortRouteReason(current) };
|
|
160
|
+
if (current.health.status === "degraded") return { route: "degraded", routeReason: shortRouteReason(current) };
|
|
161
|
+
if (current.health.status === "healthy") return { route: "ready", routeReason: null };
|
|
162
|
+
// "unknown": configured and permitted, but no probe has returned a verdict.
|
|
163
|
+
return { route: "checking", routeReason: null };
|
|
136
164
|
}
|
|
137
165
|
|
|
138
|
-
function
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
? ` ${Math.round(status.health.latencyMs)}ms`
|
|
143
|
-
: "";
|
|
144
|
-
if (activeStatus(status)) return `${status.health.status}${latency}`;
|
|
145
|
-
const reason = status.health.lastError ?? status.reason;
|
|
146
|
-
return reason && reason !== status.health.status ? `${status.health.status}: ${reason}` : status.health.status;
|
|
166
|
+
function normalizeProjectContext(state: ContextState | null | undefined): WelcomeProjectContextState | null {
|
|
167
|
+
const value = state?.clioMd;
|
|
168
|
+
if (value === "ok" || value === "stale" || value === "none" || value === "malformed") return value;
|
|
169
|
+
return null;
|
|
147
170
|
}
|
|
148
171
|
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
// Width fitting. Every measurement goes through pi's `visibleWidth`, so ANSI,
|
|
174
|
+
// CJK, emoji and combining sequences are counted in terminal columns rather
|
|
175
|
+
// than in code units.
|
|
176
|
+
// ---------------------------------------------------------------------------
|
|
177
|
+
|
|
149
178
|
/**
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
* summaries, so only the paths survive.
|
|
179
|
+
* Head-truncate a path so the leaf stays whole: `…/iowarp/clio-coder`. The leaf
|
|
180
|
+
* identifies a workspace, and two sibling worktrees differ only there, so it is
|
|
181
|
+
* the last thing given up.
|
|
154
182
|
*/
|
|
155
|
-
function
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
if (
|
|
164
|
-
|
|
165
|
-
const parenIndex = item.indexOf(" (");
|
|
166
|
-
out.push(parenIndex === -1 ? item : item.slice(0, parenIndex));
|
|
183
|
+
function fitPathTail(path: string, max: number): string {
|
|
184
|
+
if (max <= 0) return "";
|
|
185
|
+
if (visibleWidth(path) <= max) return path;
|
|
186
|
+
const segments = path.split("/").filter((segment) => segment.length > 0);
|
|
187
|
+
const leaf = segments.at(-1) ?? path;
|
|
188
|
+
let best = leaf;
|
|
189
|
+
for (let index = segments.length - 2; index >= 0; index -= 1) {
|
|
190
|
+
const candidate = `${segments[index]}/${best}`;
|
|
191
|
+
if (visibleWidth(`${GLYPH.ellipsis}/${candidate}`) > max) break;
|
|
192
|
+
best = candidate;
|
|
167
193
|
}
|
|
168
|
-
|
|
194
|
+
const marked = `${GLYPH.ellipsis}/${best}`;
|
|
195
|
+
if (visibleWidth(marked) <= max) return marked;
|
|
196
|
+
return truncateToWidth(leaf, max, GLYPH.ellipsis, false);
|
|
169
197
|
}
|
|
170
198
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
if (loaded.errors.length > 0) return "malformed";
|
|
176
|
-
return loaded.files.length > 0 ? "ok" : "none";
|
|
199
|
+
interface Unit {
|
|
200
|
+
text: string;
|
|
201
|
+
/** Lower survives longer. Units at or below `protectRank` are never dropped. */
|
|
202
|
+
rank: number;
|
|
177
203
|
}
|
|
178
204
|
|
|
179
|
-
/**
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
205
|
+
/**
|
|
206
|
+
* Fit `units` into `maxWidth` by dropping whole low-priority units rather than
|
|
207
|
+
* tail-truncating the joined string. Display order is the order given and is
|
|
208
|
+
* independent of rank, so a unit can be shown first and still be the first to go.
|
|
209
|
+
* Protected units that still overflow are truncated with an ellipsis, so the most
|
|
210
|
+
* important fact is partly visible rather than absent — which is what the previous
|
|
211
|
+
* header got wrong: it appended everything and clipped, making the route the first
|
|
212
|
+
* casualty of a long path.
|
|
213
|
+
*/
|
|
214
|
+
function fitByPriority(theme: ClioTheme, units: ReadonlyArray<Unit>, maxWidth: number, protectRank = 0): string {
|
|
215
|
+
const separator = " · ";
|
|
216
|
+
const sep = theme.fg("dim", separator);
|
|
217
|
+
const sepWidth = visibleWidth(separator);
|
|
218
|
+
const kept = units.map((unit) => ({ ...unit, dropped: false }));
|
|
219
|
+
const total = (): number => {
|
|
220
|
+
const live = kept.filter((unit) => !unit.dropped);
|
|
221
|
+
if (live.length === 0) return 0;
|
|
222
|
+
return live.reduce((sum, unit) => sum + visibleWidth(unit.text), 0) + sepWidth * (live.length - 1);
|
|
196
223
|
};
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
const files = readdirSync(handoffsDir).filter((f) => f.startsWith("handoff-") && f.endsWith(".md"));
|
|
204
|
-
if (files.length === 0) return { handoffCount: 0, handoffFreshness: "none" };
|
|
205
|
-
let newestMtime = 0;
|
|
206
|
-
for (const file of files) {
|
|
207
|
-
const mtime = statSync(join(handoffsDir, file)).mtimeMs;
|
|
208
|
-
if (mtime > newestMtime) newestMtime = mtime;
|
|
209
|
-
}
|
|
210
|
-
return {
|
|
211
|
-
handoffCount: files.length,
|
|
212
|
-
handoffFreshness: newestMtime > 0 ? relative(newestMtime) : "none",
|
|
213
|
-
};
|
|
214
|
-
} catch {
|
|
215
|
-
return { handoffCount: 0, handoffFreshness: "none" };
|
|
224
|
+
while (total() > maxWidth) {
|
|
225
|
+
const droppable = kept.filter((unit) => !unit.dropped && unit.rank > protectRank);
|
|
226
|
+
let worst = droppable[0];
|
|
227
|
+
if (worst === undefined) break;
|
|
228
|
+
for (const unit of droppable) if (unit.rank > worst.rank) worst = unit;
|
|
229
|
+
worst.dropped = true;
|
|
216
230
|
}
|
|
231
|
+
const joined = kept
|
|
232
|
+
.filter((unit) => !unit.dropped)
|
|
233
|
+
.map((unit) => unit.text)
|
|
234
|
+
.join(sep);
|
|
235
|
+
return visibleWidth(joined) <= maxWidth ? joined : truncateToWidth(joined, maxWidth, GLYPH.ellipsis, false);
|
|
217
236
|
}
|
|
218
237
|
|
|
219
|
-
function
|
|
220
|
-
|
|
221
|
-
const wiki = codewiki ? wikiFactsFrom(cwd, wikiStaleness(cwd)) : { wikiPageCount: 0, wikiStatus: NO_WIKI_STATUS };
|
|
222
|
-
return {
|
|
223
|
-
clioMdStatus: clioMdStatusFor(cwd),
|
|
224
|
-
hasCodewiki: codewiki !== null,
|
|
225
|
-
codewikiCount: codewiki ? codewiki.files.filter((file) => file.lang !== "config").length : 0,
|
|
226
|
-
...wiki,
|
|
227
|
-
wikiDigestExcerpt: codewiki ? entryPointExcerpt(renderCodewikiDigest(codewiki)) : [],
|
|
228
|
-
...handoffFacts(cwd),
|
|
229
|
-
};
|
|
238
|
+
function workspacePath(stats: WelcomeDashboardStats): string {
|
|
239
|
+
return plainOneLine(collapseHomePath(stats.cwd));
|
|
230
240
|
}
|
|
231
241
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
async function readWelcomeRepositoryFactsAsync(cwd: string): Promise<WelcomeRepositoryFacts> {
|
|
238
|
-
const codewiki = await readCodewikiAsync(cwd);
|
|
239
|
-
const wiki = codewiki
|
|
240
|
-
? wikiFactsFrom(cwd, await wikiStalenessAsync(cwd))
|
|
241
|
-
: { wikiPageCount: 0, wikiStatus: NO_WIKI_STATUS };
|
|
242
|
-
return {
|
|
243
|
-
clioMdStatus: clioMdStatusFor(cwd),
|
|
244
|
-
hasCodewiki: codewiki !== null,
|
|
245
|
-
codewikiCount: codewiki ? codewiki.files.filter((file) => file.lang !== "config").length : 0,
|
|
246
|
-
...wiki,
|
|
247
|
-
wikiDigestExcerpt: codewiki ? entryPointExcerpt(renderCodewikiDigest(codewiki)) : [],
|
|
248
|
-
...handoffFacts(cwd),
|
|
249
|
-
};
|
|
242
|
+
function workspaceBranch(stats: WelcomeDashboardStats): string | null {
|
|
243
|
+
const workspace = stats.workspace;
|
|
244
|
+
if (workspace?.isGit !== true || !workspace.branch) return null;
|
|
245
|
+
const branch = plainOneLine(workspace.branch);
|
|
246
|
+
return branch.length === 0 ? null : branch;
|
|
250
247
|
}
|
|
251
248
|
|
|
252
249
|
/**
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
250
|
+
* The workspace as `path · branch`, giving up the branch before the path's leaf.
|
|
251
|
+
* A branch name can be far longer than the directory it belongs to, and a header
|
|
252
|
+
* showing `feature/some-very-long-branch` while hiding which checkout it belongs
|
|
253
|
+
* to has kept the less useful half.
|
|
257
254
|
*/
|
|
258
|
-
function
|
|
259
|
-
return
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
255
|
+
function workspaceLabel(theme: ClioTheme, stats: WelcomeDashboardStats, room: number): string {
|
|
256
|
+
if (room <= 0) return "";
|
|
257
|
+
const path = workspacePath(stats);
|
|
258
|
+
const branch = workspaceBranch(stats);
|
|
259
|
+
const paint = (text: string): string => theme.fg("muted", text);
|
|
260
|
+
if (branch === null) return paint(fitPathTail(path, room));
|
|
261
|
+
|
|
262
|
+
const dirty = stats.workspace?.dirty === true;
|
|
263
|
+
const branchText = `${theme.fg("info", branch)}${dirty ? theme.fg("warning", "*") : ""}`;
|
|
264
|
+
const pathRoom = room - visibleWidth(branch) - (dirty ? 1 : 0) - 3;
|
|
265
|
+
// Keep the branch only while the path's leaf survives whole beside it.
|
|
266
|
+
if (pathRoom > 0) {
|
|
267
|
+
const fitted = fitPathTail(path, pathRoom);
|
|
268
|
+
const leaf =
|
|
269
|
+
path
|
|
270
|
+
.split("/")
|
|
271
|
+
.filter((segment) => segment.length > 0)
|
|
272
|
+
.at(-1) ?? path;
|
|
273
|
+
if (fitted.endsWith(leaf)) return `${paint(fitted)}${theme.fg("dim", " · ")}${branchText}`;
|
|
274
|
+
}
|
|
275
|
+
return paint(fitPathTail(path, room));
|
|
270
276
|
}
|
|
271
277
|
|
|
272
|
-
function
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
const targetLabel = current?.target.id ?? settings?.chat?.target ?? null;
|
|
278
|
-
const modelLabel = settings?.chat?.model ?? current?.target.defaultModel ?? null;
|
|
279
|
-
const workspace = deps.getWorkspaceSnapshot?.() ?? null;
|
|
280
|
-
const cwd = workspace?.cwd ?? process.cwd();
|
|
281
|
-
const currentAvailable = current ? activeStatus(current) : false;
|
|
282
|
-
const activeCapabilities = capabilityLabels(selectedModelCapabilities(current, settings, deps.providers));
|
|
283
|
-
const thinkingLevel =
|
|
284
|
-
resolveModelRuntimeCapabilitiesForProviders(
|
|
285
|
-
deps.providers,
|
|
286
|
-
settings?.chat?.target,
|
|
287
|
-
settings?.chat?.model,
|
|
288
|
-
settings?.chat?.thinkingLevel ?? "off",
|
|
289
|
-
)?.thinking.display ??
|
|
290
|
-
settings?.chat?.thinkingLevel ??
|
|
291
|
-
"off";
|
|
292
|
-
|
|
293
|
-
const autonomy = settings?.safety.autonomy ?? "auto-edit";
|
|
294
|
-
const toolProfile = settings?.integrations.externalAgents?.defaults?.toolGovernance ?? "clio-coder-policy";
|
|
295
|
-
const threshold = settings?.context.compaction?.threshold;
|
|
296
|
-
const compactionThreshold =
|
|
297
|
-
typeof threshold === "number" && Number.isFinite(threshold) ? `${Math.round(threshold * 100)}%` : "80%";
|
|
298
|
-
|
|
299
|
-
const {
|
|
300
|
-
clioMdStatus,
|
|
301
|
-
hasCodewiki,
|
|
302
|
-
codewikiCount,
|
|
303
|
-
wikiPageCount,
|
|
304
|
-
wikiStatus,
|
|
305
|
-
wikiDigestExcerpt,
|
|
306
|
-
handoffCount,
|
|
307
|
-
handoffFreshness,
|
|
308
|
-
pending,
|
|
309
|
-
} = (deps.readRepositoryFacts ?? readWelcomeRepositoryFacts)(cwd);
|
|
310
|
-
|
|
311
|
-
return {
|
|
312
|
-
activeTargets: statuses.filter(activeStatus).length,
|
|
313
|
-
totalTargets: statuses.length,
|
|
314
|
-
targetLabel,
|
|
315
|
-
modelLabel,
|
|
316
|
-
thinkingLevel,
|
|
317
|
-
cwd,
|
|
318
|
-
workspace,
|
|
319
|
-
currentAvailable,
|
|
320
|
-
targetHealthLabel: healthReadout(current),
|
|
321
|
-
activeCapabilities,
|
|
322
|
-
extensions: deps.getExtensionStats?.() ?? null,
|
|
323
|
-
autonomy,
|
|
324
|
-
toolProfile,
|
|
325
|
-
compactionThreshold,
|
|
326
|
-
clioMdStatus,
|
|
327
|
-
hasCodewiki,
|
|
328
|
-
codewikiCount,
|
|
329
|
-
wikiPageCount,
|
|
330
|
-
wikiStatus,
|
|
331
|
-
wikiDigestExcerpt,
|
|
332
|
-
handoffCount,
|
|
333
|
-
handoffFreshness,
|
|
334
|
-
taskMemory: deps.getTaskMemoryStatus?.() ?? null,
|
|
335
|
-
...(pending ? { factsPending: true } : {}),
|
|
336
|
-
};
|
|
278
|
+
function routeToken(route: WelcomeRouteState): ClioToken {
|
|
279
|
+
if (route === "ready") return "success";
|
|
280
|
+
if (route === "degraded") return "warning";
|
|
281
|
+
if (route === "unavailable") return "error";
|
|
282
|
+
return "dim";
|
|
337
283
|
}
|
|
338
284
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
285
|
+
function routeGlyph(route: WelcomeRouteState): string {
|
|
286
|
+
if (route === "ready") return GLYPH.ok;
|
|
287
|
+
if (route === "degraded") return GLYPH.warnInline;
|
|
288
|
+
if (route === "unavailable") return GLYPH.error;
|
|
289
|
+
return GLYPH.queued;
|
|
290
|
+
}
|
|
342
291
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
292
|
+
/**
|
|
293
|
+
* The route Clio will use, and — only when there is one — why it cannot answer.
|
|
294
|
+
*
|
|
295
|
+
* A failure reason is given room by shrinking the route text down to a floor
|
|
296
|
+
* before the reason is cut at all, because a failure is least useful exactly
|
|
297
|
+
* where the old header made it least visible. Below the floor the reason is
|
|
298
|
+
* truncated rather than dropped, and only when neither fits does the row fall
|
|
299
|
+
* back to the route alone; the action row still names the fault there.
|
|
300
|
+
*/
|
|
301
|
+
function routeRow(theme: ClioTheme, stats: WelcomeDashboardStats, room: number): string {
|
|
302
|
+
const target = plainOneLine(formatTargetLabel(stats.targetLabel, stats.modelLabel));
|
|
303
|
+
if (stats.route === "unset") return theme.fg("warning", truncateToWidth(target, room, GLYPH.ellipsis, false));
|
|
304
|
+
const token = routeToken(stats.route);
|
|
305
|
+
// A glyph rather than a word, so the verdicts stay distinct with no color.
|
|
306
|
+
const prefix = `${theme.fg(token, routeGlyph(stats.route))} `;
|
|
307
|
+
const available = room - 2;
|
|
308
|
+
if (available <= 0) return theme.fg(token, routeGlyph(stats.route));
|
|
309
|
+
const muted = (text: string): string => theme.fg("muted", text);
|
|
310
|
+
const reason = stats.routeReason;
|
|
311
|
+
if (reason === null) return `${prefix}${muted(truncateToWidth(target, available, GLYPH.ellipsis, false))}`;
|
|
312
|
+
|
|
313
|
+
const reasonWidth = visibleWidth(reason);
|
|
314
|
+
if (visibleWidth(target) + 1 + reasonWidth <= available) return `${prefix}${muted(target)} ${theme.fg(token, reason)}`;
|
|
315
|
+
const targetRoom = available - reasonWidth - 1;
|
|
316
|
+
if (targetRoom >= ROUTE_TARGET_FLOOR)
|
|
317
|
+
return `${prefix}${muted(truncateToWidth(target, targetRoom, GLYPH.ellipsis, false))} ${theme.fg(token, reason)}`;
|
|
318
|
+
const floor = Math.min(ROUTE_TARGET_FLOOR, available);
|
|
319
|
+
const reasonRoom = available - floor - 1;
|
|
320
|
+
if (reasonRoom >= ROUTE_REASON_FLOOR) {
|
|
321
|
+
return `${prefix}${muted(truncateToWidth(target, floor, GLYPH.ellipsis, false))} ${theme.fg(token, truncateToWidth(reason, reasonRoom, GLYPH.ellipsis, false))}`;
|
|
322
|
+
}
|
|
323
|
+
return `${prefix}${muted(truncateToWidth(target, available, GLYPH.ellipsis, false))}`;
|
|
346
324
|
}
|
|
347
325
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
326
|
+
/**
|
|
327
|
+
* The one next step, chosen by what actually blocks work.
|
|
328
|
+
*
|
|
329
|
+
* Route faults outrank project-context state: a task cannot be answered by a
|
|
330
|
+
* route that does not respond. A missing CLIO-CODER.md is guidance rather than a
|
|
331
|
+
* blocker, so it keeps the invitation to work alongside the suggestion. A
|
|
332
|
+
* malformed one is a real fault and points at the read-only `/context` view,
|
|
333
|
+
* never at a command that would regenerate the file the operator still has to
|
|
334
|
+
* repair by hand.
|
|
335
|
+
*/
|
|
336
|
+
function actionRow(theme: ClioTheme, stats: WelcomeDashboardStats, room: number): string {
|
|
337
|
+
const say = (token: ClioToken, text: string): string =>
|
|
338
|
+
theme.fg(token, truncateToWidth(text, room, GLYPH.ellipsis, false));
|
|
339
|
+
if (stats.route === "unset") {
|
|
340
|
+
// Name the half that is missing; both are fixed at the same surface.
|
|
341
|
+
const detail = stats.targetLabel === null ? "no route selected" : "no model selected";
|
|
342
|
+
return say("warning", `${detail} · /model`);
|
|
343
|
+
}
|
|
344
|
+
if (stats.route === "unavailable") return say("error", "route unavailable · /settings targets");
|
|
345
|
+
if (stats.route === "degraded") return say("warning", "route degraded · /settings targets");
|
|
346
|
+
if (stats.projectContext === "malformed") return say("warning", "CLIO-CODER.md malformed · /context to inspect");
|
|
347
|
+
if (stats.projectContext === "none") return say("dim", "describe a task · /context init to index this repo");
|
|
348
|
+
if (stats.projectContext === "stale") return say("dim", "describe a task · /context refresh to update it");
|
|
349
|
+
// A printed key must be a key that works, so an unbound submit says nothing.
|
|
350
|
+
const send = stats.submitKeyLabel === null ? null : `${stats.submitKeyLabel} to send`;
|
|
351
|
+
return say("dim", ["describe a task", send, "/ for commands"].filter((part) => part !== null).join(" · "));
|
|
354
352
|
}
|
|
355
353
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
354
|
+
/**
|
|
355
|
+
* The masthead: identity flush left, workspace flush right, a frame-colored rule
|
|
356
|
+
* between them. Exactly one line at every width. The identity gives up its
|
|
357
|
+
* version before its name, because `Clio Coder` identifies the tool and `v0.4.7`
|
|
358
|
+
* only dates it, and gives up its name before its wordmark.
|
|
359
|
+
*/
|
|
360
|
+
function mastheadRow(theme: ClioTheme, stats: WelcomeDashboardStats, version: string, width: number): string {
|
|
361
|
+
const mark = brandMark(theme);
|
|
362
|
+
const name = theme.style("title", "Clio Coder", { bold: true });
|
|
363
|
+
const identities = [`${mark} ${name} ${theme.fg("dim", `v${version}`)}`, `${mark} ${name}`, mark];
|
|
364
|
+
for (const identity of identities) {
|
|
365
|
+
const identityWidth = visibleWidth(identity);
|
|
366
|
+
// one space, at least two fill columns, one space
|
|
367
|
+
const room = width - identityWidth - 4;
|
|
368
|
+
if (room < 6) continue;
|
|
369
|
+
const workspace = workspaceLabel(theme, stats, room);
|
|
370
|
+
if (workspace.length === 0) continue;
|
|
371
|
+
const fill = width - identityWidth - visibleWidth(workspace) - 2;
|
|
372
|
+
if (fill < 2) continue;
|
|
373
|
+
return `${identity} ${theme.fg("frame", "─".repeat(fill))} ${workspace}`;
|
|
374
|
+
}
|
|
375
|
+
// Too narrow to pair them. The wordmark holds column 0 and the workspace takes
|
|
376
|
+
// what is left; below that, the wordmark alone.
|
|
377
|
+
const markWidth = visibleWidth(mark);
|
|
378
|
+
if (width >= markWidth + 3) {
|
|
379
|
+
const workspace = workspaceLabel(theme, stats, width - markWidth - 1);
|
|
380
|
+
if (workspace.length > 0) return `${mark} ${workspace}`;
|
|
361
381
|
}
|
|
362
|
-
return
|
|
382
|
+
return truncateToWidth(mark, width, "", false);
|
|
363
383
|
}
|
|
364
384
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
385
|
+
/**
|
|
386
|
+
* The collapsed session header: one live line naming where Clio is working and
|
|
387
|
+
* which route answers.
|
|
388
|
+
*
|
|
389
|
+
* It stays live rather than freezing at first submit, because the operator can
|
|
390
|
+
* change model mid-session and an unlabeled frozen route would be read as the
|
|
391
|
+
* current one. Readiness, latency and onboarding are dropped: the footer and the
|
|
392
|
+
* composer rail own those, and this line's job after the first prompt is
|
|
393
|
+
* identity. Identity sits next to the wordmark when it fits and is the first
|
|
394
|
+
* thing dropped when it does not — display order and drop order are separate.
|
|
395
|
+
*/
|
|
396
|
+
function sessionRow(theme: ClioTheme, stats: WelcomeDashboardStats, version: string, width: number): string {
|
|
397
|
+
const mark = brandMark(theme);
|
|
398
|
+
const markWidth = visibleWidth(mark);
|
|
399
|
+
if (width <= markWidth + 1) return truncateToWidth(mark, width, "", false);
|
|
400
|
+
const room = width - markWidth - 1;
|
|
401
|
+
const branch = workspaceBranch(stats);
|
|
402
|
+
const units: Unit[] = [
|
|
403
|
+
{ text: `${theme.style("title", "Clio Coder", { bold: true })} ${theme.fg("dim", `v${version}`)}`, rank: 3 },
|
|
404
|
+
{ text: theme.fg("muted", plainOneLine(formatTargetLabel(stats.targetLabel, stats.modelLabel))), rank: 0 },
|
|
405
|
+
{ text: theme.fg("muted", fitPathTail(workspacePath(stats), Math.max(8, Math.floor(room * 0.5)))), rank: 1 },
|
|
406
|
+
...(branch
|
|
407
|
+
? [
|
|
408
|
+
{
|
|
409
|
+
text: `${theme.fg("info", branch)}${stats.workspace?.dirty === true ? theme.fg("warning", "*") : ""}`,
|
|
410
|
+
rank: 2,
|
|
411
|
+
},
|
|
412
|
+
]
|
|
413
|
+
: []),
|
|
414
|
+
];
|
|
415
|
+
return `${mark} ${fitByPriority(theme, units, room)}`;
|
|
369
416
|
}
|
|
370
417
|
|
|
371
418
|
/**
|
|
372
|
-
*
|
|
373
|
-
*
|
|
374
|
-
*
|
|
375
|
-
*
|
|
376
|
-
*
|
|
377
|
-
* in the README, not in a line the operator reads on every turn.
|
|
419
|
+
* Three rows in launchpad mode and one in session mode, at every width and in
|
|
420
|
+
* every fact state. The row count never moves, because the banner sits at line 0
|
|
421
|
+
* and a height change there forces pi-tui to clear and repaint the whole buffer
|
|
422
|
+
* once the transcript has scrolled — and because a header that grows a row when
|
|
423
|
+
* a probe lands makes the first seconds of a session jump.
|
|
378
424
|
*/
|
|
379
425
|
function buildWelcomeDashboardLines(
|
|
380
426
|
stats: WelcomeDashboardStats,
|
|
427
|
+
version: string,
|
|
381
428
|
width: number,
|
|
382
|
-
mode: WelcomeDashboardMode
|
|
429
|
+
mode: WelcomeDashboardMode,
|
|
383
430
|
): string[] {
|
|
384
431
|
const theme = clioTheme();
|
|
385
432
|
const safeWidth = Math.max(1, width);
|
|
386
|
-
|
|
387
|
-
const
|
|
388
|
-
const
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
const header = [title, workspace, route, theme.fg("muted", nextValue(stats))].join(" · ");
|
|
395
|
-
return [truncateToWidth(header, safeWidth, GLYPH.ellipsis, true)];
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
const body = [row("WORKSPACE", workspace), row("ROUTE", route), row("NEXT", next)];
|
|
399
|
-
if (safeWidth >= MID_MIN) return frame(theme, title, body, safeWidth);
|
|
400
|
-
return [title, ...body].map((line) => truncateToWidth(line, safeWidth, GLYPH.ellipsis, true));
|
|
433
|
+
if (mode === "session") return [padAnsi(sessionRow(theme, stats, version, safeWidth), safeWidth)];
|
|
434
|
+
const indent = safeWidth >= 12 ? " " : "";
|
|
435
|
+
const room = Math.max(1, safeWidth - indent.length);
|
|
436
|
+
return [
|
|
437
|
+
mastheadRow(theme, stats, version, safeWidth),
|
|
438
|
+
`${indent}${routeRow(theme, stats, room)}`,
|
|
439
|
+
`${indent}${actionRow(theme, stats, room)}`,
|
|
440
|
+
].map((line) => padAnsi(line, safeWidth));
|
|
401
441
|
}
|
|
402
442
|
|
|
403
443
|
/**
|
|
404
|
-
*
|
|
405
|
-
*
|
|
406
|
-
*
|
|
407
|
-
*
|
|
408
|
-
* that
|
|
409
|
-
* shorter than a person's patience for a stale `changed files` count, and any
|
|
410
|
-
* command that can actually change these facts drops the cache outright.
|
|
411
|
-
*/
|
|
412
|
-
export const WELCOME_REPOSITORY_FACTS_TTL_MS = 10_000;
|
|
413
|
-
|
|
414
|
-
/**
|
|
415
|
-
* Everything in `WelcomeDashboardStats` that can change what the banner prints,
|
|
416
|
-
* flattened to one comparable string. Cheap enough to build per frame; the
|
|
417
|
-
* alternative was rebuilding seven framed rows, which cost 1.67 ms/frame and was
|
|
418
|
-
* 94% of all time spent inside the root container's render.
|
|
444
|
+
* Everything that can change what the banner prints, flattened to one comparable
|
|
445
|
+
* string. Deliberately short: the previous signature folded in extension counts,
|
|
446
|
+
* task-memory size, capabilities and wiki facts that no render path read, so
|
|
447
|
+
* unrelated background activity invalidated the render cache and rebuilt rows
|
|
448
|
+
* that could not have differed.
|
|
419
449
|
*/
|
|
420
450
|
function statsSignature(stats: WelcomeDashboardStats): string {
|
|
421
|
-
const
|
|
451
|
+
const workspace = stats.workspace;
|
|
422
452
|
return [
|
|
423
|
-
stats.
|
|
424
|
-
|
|
453
|
+
stats.cwd,
|
|
454
|
+
workspace && `${workspace.isGit}\x01${workspace.branch}\x01${workspace.dirty}`,
|
|
425
455
|
stats.targetLabel,
|
|
426
456
|
stats.modelLabel,
|
|
427
|
-
stats.
|
|
428
|
-
stats.
|
|
429
|
-
|
|
430
|
-
stats.
|
|
431
|
-
stats.targetHealthLabel,
|
|
432
|
-
stats.activeCapabilities.join(","),
|
|
433
|
-
stats.extensions && `${stats.extensions.active}/${stats.extensions.installed}`,
|
|
434
|
-
stats.autonomy,
|
|
435
|
-
stats.toolProfile,
|
|
436
|
-
stats.compactionThreshold,
|
|
437
|
-
stats.clioMdStatus,
|
|
438
|
-
stats.hasCodewiki,
|
|
439
|
-
stats.codewikiCount,
|
|
440
|
-
stats.wikiPageCount,
|
|
441
|
-
stats.wikiStatus,
|
|
442
|
-
stats.wikiDigestExcerpt.join(","),
|
|
443
|
-
stats.handoffCount,
|
|
444
|
-
stats.handoffFreshness,
|
|
445
|
-
stats.taskMemory && `${stats.taskMemory.enabled}\x01${stats.taskMemory.tier}\x01${stats.taskMemory.size}`,
|
|
446
|
-
stats.factsPending === true,
|
|
457
|
+
stats.route,
|
|
458
|
+
stats.routeReason,
|
|
459
|
+
stats.projectContext,
|
|
460
|
+
stats.submitKeyLabel,
|
|
447
461
|
].join("\0");
|
|
448
462
|
}
|
|
449
463
|
|
|
464
|
+
interface ProjectContextReading {
|
|
465
|
+
cwd: string;
|
|
466
|
+
state: WelcomeProjectContextState;
|
|
467
|
+
/** When the last *successful* read landed. Never renewed by a failure. */
|
|
468
|
+
readAt: number;
|
|
469
|
+
/** When the last attempt finished, successful or not. Paces retries. */
|
|
470
|
+
attemptedAt: number;
|
|
471
|
+
/** When the current failure streak began; null while reads succeed. */
|
|
472
|
+
failingSince: number | null;
|
|
473
|
+
}
|
|
474
|
+
|
|
450
475
|
export class WelcomeDashboard implements WelcomeDashboardComponent {
|
|
451
|
-
private cachedFacts: { cwd: string; at: number; facts: WelcomeRepositoryFacts } | null = null;
|
|
452
476
|
private cachedRender: { width: number; signature: string; lines: string[] } | null = null;
|
|
453
|
-
|
|
454
|
-
|
|
477
|
+
private context: ProjectContextReading | null = null;
|
|
478
|
+
/** Single-flight latch: a read slower than the TTL must not stack refreshes. */
|
|
479
|
+
private contextRefreshing = false;
|
|
480
|
+
private pendingTimer: ReturnType<typeof setTimeout> | null = null;
|
|
481
|
+
private disposed = false;
|
|
455
482
|
private mode: WelcomeDashboardMode = "launchpad";
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
483
|
+
private readonly now: () => number;
|
|
484
|
+
private readonly schedule: (run: () => void) => void;
|
|
485
|
+
/**
|
|
486
|
+
* Read once, here, rather than inside a frame: resolving it walks up for
|
|
487
|
+
* package.json and parses it, which is filesystem work the render path must
|
|
488
|
+
* not do even once.
|
|
489
|
+
*/
|
|
490
|
+
private readonly version: string;
|
|
491
|
+
|
|
492
|
+
constructor(private readonly deps: WelcomeDashboardDeps) {
|
|
493
|
+
this.now = deps.now ?? ((): number => Date.now());
|
|
494
|
+
this.schedule =
|
|
495
|
+
deps.scheduleRefresh ??
|
|
496
|
+
((run: () => void): void => {
|
|
497
|
+
this.pendingTimer = setTimeout(() => {
|
|
498
|
+
this.pendingTimer = null;
|
|
499
|
+
run();
|
|
500
|
+
}, 0);
|
|
501
|
+
this.pendingTimer.unref?.();
|
|
502
|
+
});
|
|
503
|
+
this.version = readClioVersion();
|
|
504
|
+
}
|
|
461
505
|
|
|
462
506
|
render(width: number): string[] {
|
|
463
|
-
const stats =
|
|
507
|
+
const stats = this.stats();
|
|
464
508
|
const signature = `${this.mode}\0${statsSignature(stats)}`;
|
|
465
509
|
const cached = this.cachedRender;
|
|
466
510
|
if (cached !== null && cached.width === width && cached.signature === signature) return cached.lines;
|
|
467
|
-
const lines = buildWelcomeDashboardLines(stats, width, this.mode);
|
|
511
|
+
const lines = buildWelcomeDashboardLines(stats, this.version, width, this.mode);
|
|
468
512
|
this.cachedRender = { width, signature, lines };
|
|
469
513
|
return lines;
|
|
470
514
|
}
|
|
@@ -485,50 +529,123 @@ export class WelcomeDashboard implements WelcomeDashboardComponent {
|
|
|
485
529
|
return true;
|
|
486
530
|
}
|
|
487
531
|
|
|
488
|
-
/**
|
|
532
|
+
/**
|
|
533
|
+
* Drops the rendered lines only. The project-context reading is not
|
|
534
|
+
* width-dependent, and pi cascades `invalidate` on terminal cell-dimension
|
|
535
|
+
* reports; dropping the reading there would flash the header back to
|
|
536
|
+
* `checking` for a fact that had not changed.
|
|
537
|
+
*/
|
|
489
538
|
invalidate(): void {
|
|
490
|
-
this.cachedFacts = null;
|
|
491
539
|
this.cachedRender = null;
|
|
492
540
|
}
|
|
493
541
|
|
|
494
542
|
/**
|
|
495
|
-
*
|
|
496
|
-
*
|
|
497
|
-
*
|
|
543
|
+
* After this, a refresh already in flight lands on the floor: it neither
|
|
544
|
+
* writes state nor asks for a frame from a presentation that is being torn
|
|
545
|
+
* down.
|
|
546
|
+
*/
|
|
547
|
+
dispose(): void {
|
|
548
|
+
this.disposed = true;
|
|
549
|
+
if (this.pendingTimer !== null) {
|
|
550
|
+
clearTimeout(this.pendingTimer);
|
|
551
|
+
this.pendingTimer = null;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
private stats(): WelcomeDashboardStats {
|
|
556
|
+
const settings = this.deps.getSettings?.();
|
|
557
|
+
const statuses = this.deps.providers.list();
|
|
558
|
+
const current = findCurrentStatus(statuses, settings);
|
|
559
|
+
const targetLabel = current?.target.id ?? settings?.chat?.target ?? null;
|
|
560
|
+
const modelLabel = settings?.chat?.model ?? current?.target.defaultModel ?? null;
|
|
561
|
+
const workspace = this.deps.getWorkspaceSnapshot?.() ?? null;
|
|
562
|
+
const cwd = workspace?.cwd ?? process.cwd();
|
|
563
|
+
const { route, routeReason } = deriveRoute(current, targetLabel, modelLabel);
|
|
564
|
+
// The collapsed row prints neither of these, so it does not pay for them:
|
|
565
|
+
// no background context read, and no cache invalidation when either
|
|
566
|
+
// changes while the header cannot show it. `/new` returns to the launchpad
|
|
567
|
+
// and the check resumes on the next frame.
|
|
568
|
+
const launchpad = this.mode === "launchpad";
|
|
569
|
+
return {
|
|
570
|
+
cwd,
|
|
571
|
+
workspace,
|
|
572
|
+
targetLabel,
|
|
573
|
+
modelLabel,
|
|
574
|
+
route,
|
|
575
|
+
routeReason,
|
|
576
|
+
projectContext: launchpad ? this.projectContext(cwd) : "checking",
|
|
577
|
+
submitKeyLabel: launchpad ? (this.deps.getSubmitKeyLabel?.() ?? null) : null,
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* Returns immediately, always, and never touches the filesystem. On a miss it
|
|
583
|
+
* reports `checking` and schedules the authoritative read off the frame. A
|
|
584
|
+
* reading is only ever served for the cwd it was taken in, so a directory
|
|
585
|
+
* change reports `checking` again rather than the previous repository's answer.
|
|
498
586
|
*/
|
|
499
|
-
private
|
|
587
|
+
private projectContext(cwd: string): WelcomeProjectContextState {
|
|
588
|
+
const cached = this.context;
|
|
589
|
+
if (cached === null || cached.cwd !== cwd) {
|
|
590
|
+
this.scheduleContextRefresh(cwd);
|
|
591
|
+
return "checking";
|
|
592
|
+
}
|
|
500
593
|
const at = this.now();
|
|
501
|
-
|
|
502
|
-
//
|
|
503
|
-
|
|
504
|
-
if (
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
if (override) {
|
|
508
|
-
const facts = override(cwd);
|
|
509
|
-
this.cachedFacts = { cwd, at, facts };
|
|
510
|
-
return facts;
|
|
594
|
+
// Reads that have been failing for longer than the trust window stop
|
|
595
|
+
// standing in for a current one. Without this a stale `ok` survives every
|
|
596
|
+
// failed refresh forever.
|
|
597
|
+
if (cached.failingSince !== null && at - cached.failingSince >= WELCOME_PROJECT_CONTEXT_TTL_MS) {
|
|
598
|
+
if (at - cached.attemptedAt >= WELCOME_PROJECT_CONTEXT_RETRY_MS) this.scheduleContextRefresh(cwd);
|
|
599
|
+
return "checking";
|
|
511
600
|
}
|
|
512
|
-
|
|
513
|
-
|
|
601
|
+
if (at - cached.readAt < WELCOME_PROJECT_CONTEXT_TTL_MS) return cached.state;
|
|
602
|
+
const due = cached.failingSince === null ? cached.attemptedAt : cached.attemptedAt + WELCOME_PROJECT_CONTEXT_RETRY_MS;
|
|
603
|
+
if (at >= due) this.scheduleContextRefresh(cwd);
|
|
604
|
+
// A refresh is in flight or pending; keep showing the last real reading
|
|
605
|
+
// rather than flickering to `checking` on every TTL boundary.
|
|
606
|
+
return cached.state;
|
|
514
607
|
}
|
|
515
608
|
|
|
516
|
-
private
|
|
517
|
-
|
|
518
|
-
this.
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
this.
|
|
523
|
-
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
609
|
+
private scheduleContextRefresh(cwd: string): void {
|
|
610
|
+
const read = this.deps.getContextState;
|
|
611
|
+
if (read === undefined || this.contextRefreshing || this.disposed) return;
|
|
612
|
+
this.contextRefreshing = true;
|
|
613
|
+
this.schedule(() => {
|
|
614
|
+
if (this.disposed) {
|
|
615
|
+
this.contextRefreshing = false;
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
let resolved: WelcomeProjectContextState | null = null;
|
|
619
|
+
try {
|
|
620
|
+
resolved = normalizeProjectContext(read(cwd));
|
|
621
|
+
} catch {
|
|
622
|
+
// A read that threw proves nothing about the workspace. Never turn it
|
|
623
|
+
// into `ok` or `none`.
|
|
624
|
+
resolved = null;
|
|
625
|
+
} finally {
|
|
626
|
+
this.contextRefreshing = false;
|
|
627
|
+
}
|
|
628
|
+
if (this.disposed) return;
|
|
629
|
+
const at = this.now();
|
|
630
|
+
// A reading belongs to the directory it was taken in. If cwd moved while
|
|
631
|
+
// this was in flight, the entry is still filed under its own cwd and the
|
|
632
|
+
// next frame will report `checking` for the new one.
|
|
633
|
+
const previous = this.context !== null && this.context.cwd === cwd ? this.context : null;
|
|
634
|
+
this.context =
|
|
635
|
+
resolved === null
|
|
636
|
+
? {
|
|
637
|
+
cwd,
|
|
638
|
+
state: previous?.state ?? "checking",
|
|
639
|
+
// Deliberately not renewed: a failure must not extend the trust
|
|
640
|
+
// window of the value it failed to confirm.
|
|
641
|
+
readAt: previous?.readAt ?? 0,
|
|
642
|
+
attemptedAt: at,
|
|
643
|
+
failingSince: previous?.failingSince ?? at,
|
|
644
|
+
}
|
|
645
|
+
: { cwd, state: resolved, readAt: at, attemptedAt: at, failingSince: null };
|
|
646
|
+
this.cachedRender = null;
|
|
647
|
+
this.deps.onFactsRefreshed?.();
|
|
648
|
+
});
|
|
532
649
|
}
|
|
533
650
|
}
|
|
534
651
|
|