@iowarp/clio-coder 0.3.1 → 0.3.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 +233 -373
- package/CONTRIBUTING.md +23 -23
- package/README.md +284 -613
- package/dist/{acp-FPR54DGL.js → acp-P2AQILE2.js} +43 -53
- package/dist/{agents-OGPIHPJH.js → agents-72W3BI7I.js} +43 -26
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-IC3K6NIZ.js → auth-5TWEIYDN.js} +20 -12
- package/dist/chunk-2DJ2KNFG.js +2095 -0
- package/dist/{chunk-IS3ONKU3.js → chunk-2IR2NMPA.js} +6 -4
- package/dist/chunk-2SFS6XQE.js +122 -0
- package/dist/{chunk-PV4JUBVJ.js → chunk-2TLUCQVG.js} +40 -21
- package/dist/chunk-2VTFPG5O.js +48 -0
- package/dist/chunk-4BJ5BYCE.js +61 -0
- package/dist/{chunk-474KN5II.js → chunk-4BPJXDWC.js} +111 -181
- package/dist/chunk-4VP4KH3K.js +962 -0
- package/dist/chunk-4XUGQOHA.js +797 -0
- package/dist/chunk-4ZG3XFUR.js +77 -0
- package/dist/chunk-5B2AEOW5.js +5407 -0
- package/dist/{chunk-K2ITRMHZ.js → chunk-5TSRNF4G.js} +6 -138
- package/dist/{chunk-4QKXUHSR.js → chunk-5UFT4SUX.js} +70 -20
- package/dist/{chunk-OLBBMFRD.js → chunk-5UUP6MWO.js} +24 -62
- package/dist/chunk-65DEGPJ6.js +52 -0
- package/dist/chunk-6EJMN2Y3.js +17 -0
- package/dist/chunk-6N5PTWMY.js +136 -0
- package/dist/chunk-6SGHMWE3.js +277 -0
- package/dist/chunk-6XLNIQDB.js +27 -0
- package/dist/chunk-7CR24IG7.js +242 -0
- package/dist/chunk-7MNJORFF.js +22 -0
- package/dist/{chunk-KY56HMHH.js → chunk-A3CYT5EX.js} +125 -31
- package/dist/chunk-AGYYIBLL.js +1069 -0
- package/dist/{chunk-GB6QRBXN.js → chunk-APJ265NV.js} +54 -1187
- package/dist/{chunk-673JJUWJ.js → chunk-BMEMKKIT.js} +2 -2
- package/dist/chunk-CBCAPZAA.js +229 -0
- package/dist/chunk-CMZWFGD2.js +352 -0
- package/dist/chunk-COU2UHX6.js +400 -0
- package/dist/chunk-DSELYM6W.js +1077 -0
- package/dist/chunk-DUYJ5IO6.js +644 -0
- package/dist/chunk-ECH6PKUQ.js +39 -0
- package/dist/chunk-ED4KHGC3.js +143 -0
- package/dist/chunk-EKMEHE4H.js +340 -0
- package/dist/chunk-FCSXB6T2.js +338 -0
- package/dist/chunk-FJ3H4MN5.js +48 -0
- package/dist/{chunk-RPTR2H26.js → chunk-FNTMWMX5.js} +21 -15
- package/dist/chunk-FQ4SKYE4.js +29 -0
- package/dist/chunk-G4BMMOKF.js +182 -0
- package/dist/{chunk-ZPY3JZ5E.js → chunk-GGXXDWE4.js} +183 -1233
- package/dist/chunk-HC4CLZ2Y.js +68 -0
- package/dist/{chunk-LU4TK2PR.js → chunk-HFSBBKSQ.js} +5 -56
- package/dist/{chunk-PIUMUEMV.js → chunk-HKIYEGME.js} +10 -6
- package/dist/chunk-I4HZDVNP.js +73 -0
- package/dist/chunk-IKCO5N3L.js +162 -0
- package/dist/chunk-IR4CFBFN.js +56 -0
- package/dist/{chunk-R5KLMSBV.js → chunk-J5Q24KAG.js} +2 -2
- package/dist/chunk-J7CWMCQD.js +255 -0
- package/dist/{chunk-K5XEMXTI.js → chunk-JVCV3ICN.js} +1 -1
- package/dist/chunk-KZWTDYJF.js +217 -0
- package/dist/chunk-LBMZMYH2.js +285 -0
- package/dist/{chunk-G34LV2PF.js → chunk-LM5TQCJZ.js} +84 -170
- package/dist/chunk-LW6DSM3M.js +5135 -0
- package/dist/chunk-LWLEKMDQ.js +3482 -0
- package/dist/{chunk-H6F6BYOH.js → chunk-LZSJBIVT.js} +7003 -7434
- package/dist/{chunk-HQQID6OA.js → chunk-M6SHUN7Q.js} +5 -5
- package/dist/{chunk-FST4FYJB.js → chunk-MFFY33HR.js} +99 -140
- package/dist/{chunk-BSU2YIWB.js → chunk-MVVUPGPW.js} +131 -136
- package/dist/chunk-OAO4GE4M.js +619 -0
- package/dist/{chunk-Q3RUPKEJ.js → chunk-OC7FIQPC.js} +58 -189
- package/dist/chunk-OKGUZO2U.js +34 -0
- package/dist/{chunk-GAEBEQVI.js → chunk-OOJYHWRB.js} +32 -346
- package/dist/{chunk-Q5WJOSJ7.js → chunk-OQ33BKR3.js} +2 -1
- package/dist/chunk-OQE5J4C6.js +73 -0
- package/dist/{chunk-KKNLWXI6.js → chunk-ORBHGJC5.js} +8 -8
- package/dist/{chunk-MAR7Y6HW.js → chunk-PAJK6MAQ.js} +23 -16
- package/dist/{chunk-M5T5VO65.js → chunk-PIWWS5BL.js} +837 -635
- package/dist/chunk-POHLU5DW.js +1186 -0
- package/dist/chunk-QKMUKYO7.js +4961 -0
- package/dist/chunk-SRDMMSEP.js +16405 -0
- package/dist/chunk-SST6Z5JA.js +80 -0
- package/dist/chunk-STBPMHSX.js +2456 -0
- package/dist/chunk-T6YILFSB.js +80 -0
- package/dist/chunk-TZK7PACC.js +174 -0
- package/dist/chunk-TZTZS7QK.js +227 -0
- package/dist/{chunk-ASND7OZK.js → chunk-UFIIWP2H.js} +13 -13
- package/dist/chunk-UOV2BYIW.js +107 -0
- package/dist/{chunk-PFEFKVGL.js → chunk-V6RTAOC2.js} +13 -11
- package/dist/chunk-VAKQQHWR.js +434 -0
- package/dist/chunk-VG7TBQIY.js +128 -0
- package/dist/chunk-VJWL6YS5.js +244 -0
- package/dist/{chunk-EYOKLTMF.js → chunk-VPAYEGVX.js} +17 -3
- package/dist/chunk-WEH5XRJQ.js +32 -0
- package/dist/chunk-X4RCMKVQ.js +641 -0
- package/dist/{chunk-TEKV33Q5.js → chunk-X6IAEBZR.js} +65 -33
- package/dist/chunk-XBXAASKX.js +18 -0
- package/dist/chunk-XN3L4EYL.js +46 -0
- package/dist/{chunk-RDLVBZEO.js → chunk-YCWGATWI.js} +6 -4
- package/dist/chunk-YHZX5GEU.js +193 -0
- package/dist/chunk-YXLYO42X.js +91 -0
- package/dist/{chunk-NMOX6HFD.js → chunk-ZDOOVTXZ.js} +29 -77
- package/dist/chunk-ZI647VB5.js +37 -0
- package/dist/{chunk-C4PTHK7P.js → chunk-ZWLZP4ZT.js} +5 -5
- package/dist/chunk-ZWMF7253.js +1882 -0
- package/dist/cli/index.js +62 -54
- package/dist/clio-JOU4FXVA.js +25 -0
- package/dist/code-nav-7AX6FYE6.js +600 -0
- package/dist/codewiki/build-worker.js +66 -0
- package/dist/compile-cache-CVJMMODC.js +18 -0
- package/dist/{components-DMAOEKFB.js → components-KELWS457.js} +11 -6
- package/dist/{config-IRUQ7SE4.js → config-XCDVKR23.js} +92 -55
- package/dist/configure-4GAP54ZW.js +42 -0
- package/dist/{context-5RADCKTR.js → context-4UOGGLQ5.js} +71 -35
- package/dist/context-5VKGUVJJ.js +866 -0
- package/dist/{context-3KWFLHJG.js → context-77FM5DV5.js} +15 -13
- package/dist/{context-clear-7TSNPAAI.js → context-clear-XXJRLCJJ.js} +54 -28
- package/dist/{context-index-W4RLWOQH.js → context-index-BZ4UYMTC.js} +30 -24
- package/dist/dispatch-runner-QPRDDBDX.js +1997 -0
- package/dist/{docs-5AWSPS37.js → docs-2C2LTVT2.js} +23 -10
- package/dist/{doctor-UC5NAJYQ.js → doctor-HR46URBJ.js} +27 -17
- package/dist/{eval-U6TJHRLX.js → eval-XSSNATB4.js} +29 -16
- package/dist/{evidence-YEGUW4L3.js → evidence-6HG2PY2B.js} +46 -26
- package/dist/{evolve-TXARCTPG.js → evolve-K7YU3NCY.js} +45 -25
- package/dist/{extensions-OZFJ3A3G.js → extensions-QVDOHDGJ.js} +16 -7
- package/dist/{fleet-6G3DHNYE.js → fleet-VY3HHKN6.js} +163 -54
- package/dist/{fleet-preflight-DSNT37JK.js → fleet-preflight-DDN536IT.js} +7 -4
- package/dist/{init-KZ5QTF6M.js → init-JYGXI3FK.js} +69 -32
- package/dist/{memory-73ESV5YC.js → memory-WFZMGYHX.js} +48 -27
- package/dist/{models-A4PVNWJK.js → models-I5QWSEOM.js} +39 -25
- package/dist/monitor-GE4ID3IA.js +661 -0
- package/dist/{chunk-FCIH3BIZ.js → orchestrator-EM5MC3HM.js} +15979 -12407
- package/dist/{paths-C4H6IV77.js → paths-UXLN5YYZ.js} +10 -5
- package/dist/{preload-6WVMHX3A.js → preload-P6DGH2PZ.js} +2 -2
- package/dist/{reset-BGW6OGMV.js → reset-L2FQEE3E.js} +16 -10
- package/dist/{run-YTPEYQOH.js → run-ZU3QMZPZ.js} +101 -61
- package/dist/{share-YIFFV4NQ.js → share-S5BZQC5I.js} +15 -8
- package/dist/{skills-2V6RA3OQ.js → skills-X5VXCRNQ.js} +34 -14
- package/dist/{skills-eval-S2TVJO4F.js → skills-eval-WKIHWTHR.js} +70 -34
- package/dist/steer-GGWFUJUD.js +77 -0
- package/dist/{targets-TYXLPB23.js → targets-SNCPI2NR.js} +43 -27
- package/dist/terminal-lease-BNAHVHBS.js +395 -0
- package/dist/{trace-GGOJ6Q6Z.js → trace-PNCASAXC.js} +41 -16
- package/dist/{chunk-N6F52NLF.js → tree-sitter-HGKH6LG4.js} +28 -2306
- package/dist/{uninstall-LLLT4F4W.js → uninstall-FZCQCDKC.js} +10 -5
- package/dist/{upgrade-33G2LMM5.js → upgrade-JQHHPQ4K.js} +45 -25
- package/dist/{usage-ZAFSXKKG.js → usage-OR4O5SMZ.js} +62 -31
- package/dist/verify-375KUB3Y.js +716 -0
- package/dist/web-fetch-2YHJ3KTG.js +638 -0
- package/dist/{wiki-generate-NUQCVOQ3.js → wiki-generate-UEXP2ARI.js} +74 -34
- package/dist/worker/entry.js +221 -36
- package/dist/workspace-G4ZWUIPR.js +22 -0
- package/docs/README.md +22 -17
- package/docs/acp.md +168 -16
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +136 -7
- package/docs/artifact-versions.md +1 -1
- package/docs/built-in-agents.md +1 -1
- package/docs/capacity-and-scheduling.md +1 -1
- package/docs/commands-and-modes.md +114 -71
- package/docs/config-knobs-audit.md +1 -3
- package/docs/configuration-and-targets.md +174 -46
- package/docs/context-engine.md +29 -6
- package/docs/development-pipeline.md +26 -1
- package/docs/dispatch-architecture-rationale.md +1 -1
- package/docs/documentation-coverage.md +2 -2
- package/docs/documentation-guide.md +1 -1
- package/docs/environment-variables.md +13 -5
- package/docs/eval-runner.md +1 -1
- package/docs/evals-internal.md +1 -1
- package/docs/evidence-and-memory.md +6 -2
- package/docs/evolution.md +2 -2
- package/docs/exit-codes-and-output.md +15 -9
- package/docs/extensions-and-sharing.md +9 -9
- package/docs/fleet-dispatch.md +7 -5
- package/docs/git-commit-provenance.md +120 -0
- package/docs/glossary.md +1 -1
- package/docs/installation-and-lifecycle.md +34 -27
- package/docs/middleware-and-components.md +1 -1
- package/docs/model-catalog.md +45 -14
- package/docs/observability.md +8 -5
- package/docs/performance-methodology.md +491 -0
- package/docs/pi-boundary.md +72 -0
- package/docs/proactive-memory.md +3 -3
- package/docs/prompt-envelope-and-tools.md +24 -3
- package/docs/provider-adapter-cookbook.md +57 -4
- package/docs/release-cut-checklist.md +129 -115
- package/docs/safety-model.md +9 -5
- package/docs/scientific-validation.md +3 -3
- package/docs/session-lifecycle.md +55 -12
- package/docs/skills-marketplace.md +12 -8
- package/docs/time-conventions.md +1 -1
- package/docs/tool-usage.md +3 -3
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +10 -7
- package/docs/tui-design.md +47 -10
- package/docs/worker-dispatch-mechanics.md +1 -1
- package/package.json +19 -22
- package/skills/coding/ast-grep/SKILL.md +136 -0
- package/skills/coding/ast-grep/evals.md +56 -0
- package/skills/coding/ast-grep/references/rule_reference.md +297 -0
- package/skills/coding/coding-standards/SKILL.md +113 -0
- package/skills/coding/coding-standards/evals.md +34 -0
- package/skills/coding/prototype/SKILL.md +86 -0
- package/skills/coding/prototype/evals.md +42 -0
- package/skills/coding/prototype/references/LOGIC.md +67 -0
- package/skills/coding/prototype/references/UI.md +112 -0
- package/skills/coding/tdd/SKILL.md +101 -0
- package/skills/coding/tdd/evals.md +41 -0
- package/skills/coding/tdd/references/mocking.md +59 -0
- package/skills/coding/tdd/references/tests.md +77 -0
- package/skills/context/context-handoff/SKILL.md +126 -0
- package/skills/context/context-handoff/evals.md +57 -0
- package/skills/context/context-handoff/scripts/new-handoff.sh +26 -0
- package/skills/context/context-prime/SKILL.md +95 -0
- package/skills/context/context-prime/evals.md +54 -0
- package/skills/meta/clio-dev/SKILL.md +91 -0
- package/skills/meta/clio-dev/evals.md +45 -0
- package/skills/meta/clio-test/SKILL.md +130 -0
- package/skills/meta/clio-test/evals.md +43 -0
- package/skills/meta/clio-test/references/harness.md +97 -0
- package/skills/meta/clio-test/references/test-map.md +59 -0
- package/skills/meta/credentials/SKILL.md +125 -0
- package/skills/meta/credentials/evals.md +104 -0
- package/skills/meta/find-skills/SKILL.md +72 -0
- package/skills/meta/find-skills/evals.md +47 -0
- package/skills/meta/herdr/SKILL.md +127 -0
- package/skills/meta/herdr/evals.md +38 -0
- package/skills/meta/skill-craft/SKILL.md +102 -0
- package/skills/meta/skill-craft/evals.md +41 -0
- package/skills/planning/architecture/SKILL.md +129 -0
- package/skills/planning/architecture/evals.md +36 -0
- package/skills/planning/backlog/SKILL.md +90 -0
- package/skills/planning/backlog/evals.md +43 -0
- package/skills/planning/prd/SKILL.md +82 -0
- package/skills/planning/prd/evals.md +49 -0
- package/skills/planning/product-intent/SKILL.md +112 -0
- package/skills/planning/product-intent/evals.md +36 -0
- package/skills/planning/tech-spec/SKILL.md +115 -0
- package/skills/planning/tech-spec/evals.md +47 -0
- package/skills/registry.yaml +136 -0
- package/skills/research/arxiv-literature/SKILL.md +104 -0
- package/skills/research/arxiv-literature/evals.md +58 -0
- package/skills/research/experiment-protocol/SKILL.md +122 -0
- package/skills/research/experiment-protocol/evals.md +91 -0
- package/skills/research/scientific-debugging/SKILL.md +119 -0
- package/skills/research/scientific-debugging/evals.md +138 -0
- package/skills/research/scientific-modernization/SKILL.md +138 -0
- package/skills/research/scientific-modernization/evals.md +84 -0
- package/skills/workflow/design-council/SKILL.md +139 -0
- package/skills/workflow/design-council/evals.md +97 -0
- package/skills/workflow/grill-me/SKILL.md +186 -0
- package/skills/workflow/grill-me/evals.md +78 -0
- package/skills/workflow/workflow-distiller/SKILL.md +136 -0
- package/skills/workflow/workflow-distiller/evals.md +107 -0
- package/src/cli/acp.ts +31 -4
- package/src/cli/clio.ts +68 -6
- package/src/cli/config-inspect.ts +28 -22
- package/src/cli/configure.ts +47 -9
- package/src/cli/context-clear.ts +2 -2
- package/src/cli/context-index.ts +21 -23
- package/src/cli/context.ts +13 -8
- package/src/cli/default-target.ts +9 -17
- package/src/cli/docs.ts +11 -5
- package/src/cli/evidence.ts +4 -1
- package/src/cli/extensions.ts +10 -1
- package/src/cli/fleet.ts +47 -6
- package/src/cli/index.ts +55 -26
- package/src/cli/memory.ts +3 -1
- package/src/cli/models.ts +1 -1
- package/src/cli/modes/json-stream.ts +37 -1
- package/src/cli/modes/print.ts +24 -9
- package/src/cli/run.ts +2 -2
- package/src/cli/skills-eval.ts +23 -8
- package/src/cli/skills.ts +19 -4
- package/src/cli/targets.ts +4 -0
- package/src/cli/text-layout.ts +15 -5
- package/src/cli/trace.ts +62 -14
- package/src/cli/upgrade.ts +18 -2
- package/src/cli/usage.ts +10 -3
- package/src/cli/wiki-generate.ts +2 -1
- package/src/core/agent-environment.ts +7 -0
- package/src/core/bash-exec.ts +72 -1
- package/src/core/boot-trace.ts +9 -4
- package/src/core/bus-events.ts +20 -4
- package/src/core/commit-attribution.ts +157 -0
- package/src/core/compile-cache.ts +159 -0
- package/src/core/config.ts +131 -2
- package/src/core/defaults.ts +39 -5
- package/src/core/domain-loader.ts +12 -5
- package/src/core/git-commit-attribution.ts +387 -0
- package/src/core/incomplete-installation.ts +45 -0
- package/src/core/response-schema.ts +1 -1
- package/src/core/safe-exec.ts +13 -1
- package/src/core/settings-layers.ts +155 -21
- package/src/core/skill-activation.ts +1 -1
- package/src/core/startup-timer.ts +3 -3
- package/src/core/state-file-lock.ts +13 -1
- package/src/core/termination.ts +78 -5
- package/src/domains/config/classify.ts +15 -3
- package/src/domains/config/extension.ts +19 -13
- package/src/domains/config/index.ts +10 -0
- package/src/domains/config/keybindings.ts +45 -9
- package/src/domains/context/bootstrap-prompt.ts +1 -1
- package/src/domains/context/bootstrap.ts +111 -18
- package/src/domains/context/clear.ts +16 -11
- package/src/domains/context/clio-md.ts +111 -9
- package/src/domains/context/codewiki/artifact.ts +400 -0
- package/src/domains/context/codewiki/build-worker-protocol.ts +24 -0
- package/src/domains/context/codewiki/build-worker.ts +54 -0
- package/src/domains/context/codewiki/coordinator.ts +182 -0
- package/src/domains/context/codewiki/indexer.ts +59 -144
- package/src/domains/context/codewiki/paths.ts +67 -0
- package/src/domains/context/codewiki/schema.ts +80 -0
- package/src/domains/context/codewiki/tree-sitter.ts +1 -1
- package/src/domains/context/contract.ts +11 -5
- package/src/domains/context/extension.ts +94 -143
- package/src/domains/context/fingerprint.ts +3 -1
- package/src/domains/context/index.ts +12 -22
- package/src/domains/context/project-metadata.ts +19 -0
- package/src/domains/context/prompt-context.ts +9 -10
- package/src/domains/context/refresh.ts +29 -21
- package/src/domains/context/runtime.ts +17 -0
- package/src/domains/context/wiki/generate.ts +39 -34
- package/src/domains/context/wiki/plan.ts +1 -1
- package/src/domains/context/wiki/prompts.ts +21 -8
- package/src/domains/dispatch/code-step.ts +20 -1
- package/src/domains/dispatch/extension.ts +158 -21
- package/src/domains/dispatch/failure-classification.ts +6 -0
- package/src/domains/dispatch/fleet-commit-attribution.ts +56 -0
- package/src/domains/dispatch/orphan-recovery.ts +50 -8
- package/src/domains/dispatch/receipt-integrity.ts +5 -0
- package/src/domains/dispatch/state.ts +31 -6
- package/src/domains/dispatch/transport.ts +2 -1
- package/src/domains/dispatch/types.ts +14 -0
- package/src/domains/dispatch/worker-spawn.ts +21 -2
- package/src/domains/eval/metrics/context.ts +1 -1
- package/src/domains/eval/types.ts +0 -1
- package/src/domains/evidence/build.ts +41 -1
- package/src/domains/lifecycle/migrations/2026-08-18-lmstudio-runtime-id.ts +52 -0
- package/src/domains/lifecycle/migrations/index.ts +24 -4
- package/src/domains/middleware/hooks-io.ts +12 -0
- package/src/domains/middleware/skills-reminder.ts +30 -15
- package/src/domains/prompts/compiler.ts +142 -84
- package/src/domains/prompts/contract.ts +18 -2
- package/src/domains/prompts/extension.ts +39 -7
- package/src/domains/prompts/fragment-loader.ts +0 -1
- package/src/domains/prompts/fragments/identity/clio.md +2 -4
- package/src/domains/prompts/fragments/identity/docs-routing.md +10 -0
- package/src/domains/prompts/fragments/identity/self-awareness.md +1 -45
- package/src/domains/prompts/fragments/operating/contract.md +4 -50
- package/src/domains/prompts/fragments/operating/delegation.md +42 -0
- package/src/domains/prompts/fragments/operating/skills.md +26 -0
- package/src/domains/prompts/fragments/operating/worker.md +16 -0
- package/src/domains/prompts/fragments/safety/auto-edit.md +5 -5
- package/src/domains/prompts/fragments/safety/full-auto.md +3 -3
- package/src/domains/prompts/fragments/safety/read-only.md +4 -4
- package/src/domains/prompts/fragments/safety/suggest.md +2 -2
- package/src/domains/prompts/fragments/wiki/page.md +10 -0
- package/src/domains/prompts/fragments/wiki/plan.md +10 -0
- package/src/domains/prompts/preload.ts +3 -3
- package/src/domains/providers/auth/api-key.ts +1 -1
- package/src/domains/providers/auth/backend-file.ts +20 -10
- package/src/domains/providers/auth/backend-memory.ts +59 -4
- package/src/domains/providers/auth/boot-status.ts +65 -0
- package/src/domains/providers/auth/oauth.ts +2 -1
- package/src/domains/providers/auth/storage.ts +97 -38
- package/src/domains/providers/capabilities.ts +12 -4
- package/src/domains/providers/contract.ts +15 -4
- package/src/domains/providers/extension.ts +18 -6
- package/src/domains/providers/model-runtime-capabilities.ts +15 -4
- package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +118 -35
- package/src/domains/providers/plugins.ts +5 -3
- package/src/domains/providers/probe/fingerprint.ts +25 -5
- package/src/domains/providers/registry.ts +31 -10
- package/src/domains/providers/runtimes/boot-manifest.ts +55 -0
- package/src/domains/providers/runtimes/builtins.ts +2 -2
- package/src/domains/providers/runtimes/common/lmstudio-http.ts +423 -0
- package/src/domains/providers/runtimes/common/local-synth.ts +6 -7
- package/src/domains/providers/runtimes/local-native/lmstudio.ts +241 -0
- package/src/domains/providers/support.ts +6 -3
- package/src/domains/providers/types/local-model-quirks.ts +7 -9
- package/src/domains/providers/types/runtime-descriptor.ts +12 -1
- package/src/domains/providers/types/target-descriptor.ts +22 -0
- package/src/domains/resources/contract.ts +0 -1
- package/src/domains/resources/extension.ts +1 -3
- package/src/domains/resources/loader.ts +3 -4
- package/src/domains/resources/prompts/loader.ts +16 -2
- package/src/domains/resources/prompts/substitute.ts +1 -65
- package/src/domains/resources/skills/content-hash.ts +2 -0
- package/src/domains/resources/skills/install.ts +17 -0
- package/src/domains/resources/skills/loader.ts +17 -10
- package/src/domains/resources/skills/marketplace.ts +55 -9
- package/src/domains/safety/action-classifier.ts +4 -2
- package/src/domains/safety/audit.ts +8 -2
- package/src/domains/safety/extension.ts +1 -1
- package/src/domains/session/compaction/branch-summary.ts +3 -2
- package/src/domains/session/compaction/cut-point.ts +2 -1
- package/src/domains/session/compaction/tokens.ts +2 -1
- package/src/domains/session/context-ledger.ts +14 -0
- package/src/domains/session/contract.ts +15 -0
- package/src/domains/session/decision-board.ts +190 -0
- package/src/domains/session/entries.ts +66 -3
- package/src/domains/session/extension.ts +93 -12
- package/src/domains/session/retry.ts +10 -18
- package/src/domains/session/session-artifacts.ts +107 -0
- package/src/domains/session/task-board.ts +207 -13
- package/src/domains/session/tree/active-path.ts +44 -5
- package/src/domains/session/tree/fork.ts +26 -27
- package/src/domains/session/tree/preview.ts +2 -2
- package/src/domains/session/workspace/git-probe.ts +17 -11
- package/src/domains/user-tasks/store.ts +297 -0
- package/src/engine/acp/errors.ts +96 -0
- package/src/engine/acp/server.ts +1728 -146
- package/src/engine/acp/transport.ts +135 -14
- package/src/engine/acp/types.ts +26 -0
- package/src/engine/agent.ts +3 -3
- package/src/engine/ai.ts +32 -27
- package/src/engine/alcf-oauth.ts +26 -19
- package/src/engine/api-registry.ts +223 -0
- package/src/engine/apis/index.ts +3 -7
- package/src/engine/apis/llamacpp-residency.ts +49 -9
- package/src/engine/apis/lmstudio-residency.ts +5 -21
- package/src/engine/apis/lmstudio.ts +243 -0
- package/src/engine/apis/ollama-native.ts +24 -3
- package/src/engine/apis/openai-completions.ts +170 -91
- package/src/engine/apis/residency.ts +139 -3
- package/src/engine/apis/types.ts +16 -0
- package/src/engine/env-api-keys.ts +98 -0
- package/src/engine/gemma-channel-filter.ts +223 -0
- package/src/engine/instrumented-tui.ts +192 -0
- package/src/engine/messages.ts +14 -0
- package/src/engine/models.ts +42 -0
- package/src/engine/oauth.ts +16 -12
- package/src/engine/prompt-templates.ts +1 -0
- package/src/engine/provider-payload.ts +16 -59
- package/src/engine/strip-tokenizer-sentinels.ts +1 -1
- package/src/engine/truncate.ts +9 -0
- package/src/engine/tui.ts +17 -9
- package/src/engine/types.ts +3 -6
- package/src/engine/worker-runtime-capabilities.ts +5 -0
- package/src/engine/worker-runtime.ts +1 -1
- package/src/engine/worker-tools.ts +9 -4
- package/src/entry/boot-options.ts +50 -0
- package/src/entry/orchestrator.ts +288 -150
- package/src/interactive/application-controller.ts +89 -2
- package/src/interactive/chat-loop.ts +266 -41
- package/src/interactive/chat-panel.ts +715 -278
- package/src/interactive/chat-renderer.ts +306 -85
- package/src/interactive/clio-editor.ts +3 -8
- package/src/interactive/command-fallbacks.ts +2 -2
- package/src/interactive/context-overlay.ts +27 -1
- package/src/interactive/editor-submit.ts +253 -24
- package/src/interactive/export-html/ansi-to-html.ts +161 -0
- package/src/interactive/export-html/index.ts +51 -0
- package/src/interactive/export-html/template.ts +45 -0
- package/src/interactive/export-html/tool-renderer.ts +54 -0
- package/src/interactive/footer/dashboard.ts +4 -0
- package/src/interactive/footer/notifications.ts +1 -1
- package/src/interactive/footer/widgets.ts +42 -22
- package/src/interactive/footer-panel.ts +8 -3
- package/src/interactive/format-time.ts +14 -2
- package/src/interactive/interactive-application.ts +203 -17
- package/src/interactive/interactive-event-projection.ts +18 -1
- package/src/interactive/interactive-input-runtime.ts +50 -4
- package/src/interactive/interactive-presentation.ts +151 -19
- package/src/interactive/interactive-shell.ts +268 -14
- package/src/interactive/interactive-slash-runtime.ts +184 -117
- package/src/interactive/interactive-tickers.ts +38 -7
- package/src/interactive/keybinding-manager.ts +1 -1
- package/src/interactive/layout.ts +40 -3
- package/src/interactive/overlay-frame.ts +1 -1
- package/src/interactive/overlay-general-openers.ts +58 -1
- package/src/interactive/overlay-key-routing.ts +3 -0
- package/src/interactive/overlay-lifecycle.ts +13 -0
- package/src/interactive/overlay-permission-lifecycle.ts +2 -1
- package/src/interactive/overlay-session-lifecycle.ts +69 -12
- package/src/interactive/overlays/ask-user.ts +146 -24
- package/src/interactive/overlays/decisions.ts +300 -0
- package/src/interactive/overlays/help-reference.ts +15 -10
- package/src/interactive/overlays/model-selector.ts +34 -16
- package/src/interactive/overlays/session-selector.ts +18 -0
- package/src/interactive/overlays/settings.ts +105 -17
- package/src/interactive/overlays/skills-hub.ts +4 -4
- package/src/interactive/overlays/tree-selector.ts +41 -6
- package/src/interactive/render-trace.ts +499 -90
- package/src/interactive/renderers/compaction-summary.ts +2 -2
- package/src/interactive/renderers/diff.ts +115 -104
- package/src/interactive/renderers/mermaid.ts +53 -0
- package/src/interactive/renderers/tool-execution.ts +516 -168
- package/src/interactive/renderers/worker-entry.ts +20 -4
- package/src/interactive/session-switch-settlement.ts +10 -0
- package/src/interactive/slash-autocomplete.ts +6 -114
- package/src/interactive/slash-commands.ts +135 -47
- package/src/interactive/slash-spec.ts +9 -38
- package/src/interactive/status/controller.ts +5 -1
- package/src/interactive/status/index.ts +12 -1
- package/src/interactive/status/reasoning.ts +87 -0
- package/src/interactive/status/summary.ts +13 -2
- package/src/interactive/stdout-backpressure.ts +99 -0
- package/src/interactive/stream-pacer.ts +530 -0
- package/src/interactive/stream-pacing-policy.ts +66 -0
- package/src/interactive/tasks-overlay.ts +368 -14
- package/src/interactive/terminal-lease.ts +485 -0
- package/src/interactive/theme/tokens.ts +1 -1
- package/src/interactive/transcript-detail.ts +120 -0
- package/src/interactive/turn-context.ts +4 -3
- package/src/interactive/turn-persistence.ts +30 -13
- package/src/interactive/turn-queues.ts +12 -0
- package/src/interactive/turn-recovery.ts +25 -8
- package/src/interactive/turn-runtime.ts +79 -12
- package/src/interactive/turn-state.ts +10 -0
- package/src/interactive/view/artifacts.ts +114 -4
- package/src/interactive/view/view-overlay.ts +3 -0
- package/src/interactive/welcome-dashboard.ts +17 -16
- package/src/interactive/worker-receipts.ts +52 -3
- package/src/interactive/worker-stream.ts +5 -1
- package/src/tools/agent-tools.ts +23 -3
- package/src/tools/artifact.ts +2 -2
- package/src/tools/ask-user.ts +23 -13
- package/src/tools/bash.ts +30 -2
- package/src/tools/bootstrap.ts +34 -431
- package/src/tools/builtin-tool-catalog.ts +271 -0
- package/src/tools/codewiki/code-nav-surface.ts +29 -0
- package/src/tools/codewiki/code-nav.ts +8 -22
- package/src/tools/codewiki/shared.ts +41 -38
- package/src/tools/context/docs-engine.ts +14 -3
- package/src/tools/context/index.ts +107 -28
- package/src/tools/context/surface.ts +19 -0
- package/src/tools/core-bootstrap.ts +168 -0
- package/src/tools/credential-present.ts +5 -5
- package/src/tools/dispatch-admission.ts +533 -0
- package/src/tools/dispatch-background.ts +54 -0
- package/src/tools/dispatch-event-text.ts +6 -0
- package/src/tools/dispatch-plan.ts +9 -4
- package/src/tools/dispatch-run-events.ts +238 -0
- package/src/tools/dispatch-runner.ts +2370 -0
- package/src/tools/dispatch-scout-admission.ts +295 -0
- package/src/tools/dispatch-types.ts +77 -0
- package/src/tools/dispatch.ts +67 -3161
- package/src/tools/find.ts +4 -2
- package/src/tools/grep.ts +2 -2
- package/src/tools/lazy-tool.ts +60 -0
- package/src/tools/ledger.ts +3 -3
- package/src/tools/monitor-surface.ts +36 -0
- package/src/tools/monitor.ts +2 -32
- package/src/tools/observers.ts +2 -2
- package/src/tools/presentation.ts +107 -0
- package/src/tools/registry.ts +45 -27
- package/src/tools/safe-exec.ts +2 -2
- package/src/tools/steer-surface.ts +17 -0
- package/src/tools/steer.ts +2 -13
- package/src/tools/tasks.ts +108 -11
- package/src/tools/truncate.ts +25 -184
- package/src/tools/verify/frontend.ts +3 -1
- package/src/tools/verify/index.ts +3 -38
- package/src/tools/verify/surface.ts +46 -0
- package/src/tools/web-fetch-surface.ts +23 -0
- package/src/tools/web-fetch.ts +2 -20
- package/src/tools/write.ts +7 -2
- package/src/worker/entry.ts +39 -2
- package/src/worker/spec-contract.ts +26 -5
- package/dist/chunk-7SS2CTV2.js +0 -61361
- package/dist/chunk-DKGKUHFA.js +0 -924
- package/dist/chunk-GEP36Y4X.js +0 -12796
- package/dist/chunk-XYWBQRDM.js +0 -137
- package/dist/clio-BZVGEUFJ.js +0 -58
- package/dist/configure-S7S6F6CL.js +0 -32
- package/docs/html/agents_blueprint.html +0 -936
- package/docs/html/alcf_blueprint.html +0 -324
- package/docs/html/architecture_blueprint.html +0 -850
- package/docs/html/commands_blueprint.html +0 -939
- package/docs/html/config_knobs_audit_blueprint.html +0 -178
- package/docs/html/configuration_blueprint.html +0 -1080
- package/docs/html/context_blueprint.html +0 -603
- package/docs/html/documentation_blueprint.html +0 -832
- package/docs/html/environment_blueprint.html +0 -404
- package/docs/html/eval_blueprint.html +0 -743
- package/docs/html/evals_internal_blueprint.html +0 -190
- package/docs/html/evolution_blueprint.html +0 -674
- package/docs/html/extensions_blueprint.html +0 -2065
- package/docs/html/fleet_dispatch_blueprint.html +0 -286
- package/docs/html/index.html +0 -919
- package/docs/html/lifecycle_blueprint.html +0 -723
- package/docs/html/memory_blueprint.html +0 -699
- package/docs/html/middleware_blueprint.html +0 -664
- package/docs/html/models_blueprint.html +0 -2366
- package/docs/html/observability_blueprint.html +0 -683
- package/docs/html/provider_adapter_blueprint.html +0 -245
- package/docs/html/safety_blueprint.html +0 -1386
- package/docs/html/shared.css +0 -571
- package/docs/html/shared.js +0 -143
- package/docs/html/skills_blueprint.html +0 -671
- package/docs/html/soak_blueprint.html +0 -182
- package/docs/html/tool_usage_blueprint.html +0 -350
- package/docs/html/tools_blueprint.html +0 -2249
- package/docs/html/trace_blueprint.html +0 -235
- package/docs/html/tui_design_blueprint.html +0 -374
- package/docs/html/validation_blueprint.html +0 -961
- package/docs/html/worker_dispatch_blueprint.html +0 -231
- package/src/core/release.ts +0 -2
- package/src/domains/providers/runtimes/common/lmstudio-logger.ts +0 -32
- package/src/domains/providers/runtimes/local-native/lmstudio-native.ts +0 -491
- package/src/engine/apis/lmstudio-native.ts +0 -1438
- package/src/engine/apis/thinking-replay.ts +0 -11
- package/src/tools/string-enum.ts +0 -15
|
@@ -4,6 +4,10 @@ import { loadMemoryRecordsSync, type MemoryRecord } from "../domains/memory/inde
|
|
|
4
4
|
import type { ObservabilityContract } from "../domains/observability/index.js";
|
|
5
5
|
import type { ContextLedger } from "../domains/session/context-ledger.js";
|
|
6
6
|
import type { SessionMeta } from "../domains/session/index.js";
|
|
7
|
+
import { foldSessionArtifacts } from "../domains/session/session-artifacts.js";
|
|
8
|
+
import { foldSessionTaskHistory } from "../domains/session/task-board.js";
|
|
9
|
+
import { filterEntriesToActivePath } from "../domains/session/tree/active-path.js";
|
|
10
|
+
import type { UserTasksStore } from "../domains/user-tasks/store.js";
|
|
7
11
|
import type { TUI } from "../engine/tui.js";
|
|
8
12
|
import { type OpenContextOverlayOptions, openContextOverlay } from "./context-overlay.js";
|
|
9
13
|
import { openCostOverlay } from "./cost-overlay.js";
|
|
@@ -20,7 +24,8 @@ import {
|
|
|
20
24
|
contextResetOptions,
|
|
21
25
|
openContextResetOverlay,
|
|
22
26
|
} from "./overlays/context-reset.js";
|
|
23
|
-
import
|
|
27
|
+
import { formatDecisionCorrectionTurn, openDecisionsOverlay } from "./overlays/decisions.js";
|
|
28
|
+
import { type ContextClearCommandOptions, formatUserTaskHandoff } from "./slash-commands.js";
|
|
24
29
|
import { openTasksOverlay } from "./tasks-overlay.js";
|
|
25
30
|
import { type ArtifactProviderDeps, createDefaultArtifactProviders } from "./view/artifacts.js";
|
|
26
31
|
import { openViewOverlay } from "./view/view-overlay.js";
|
|
@@ -40,6 +45,10 @@ export interface OverlayGeneralOpenersDeps {
|
|
|
40
45
|
renderTaskIsland: () => void;
|
|
41
46
|
requestRender: () => void;
|
|
42
47
|
getTaskBoard?: Parameters<typeof openTasksOverlay>[1];
|
|
48
|
+
userTasks?: UserTasksStore;
|
|
49
|
+
getDecisionBoard?: Parameters<typeof openDecisionsOverlay>[1];
|
|
50
|
+
supersedeDecision?: (interviewId: string, key: string, correction?: string) => unknown;
|
|
51
|
+
submitChat: (text: string) => void;
|
|
43
52
|
getTaskMemoryStatus?: Parameters<typeof openMemoryOverlay>[1];
|
|
44
53
|
dataDir: string;
|
|
45
54
|
notify: (level: "info" | "success" | "warning" | "error", text: string, key?: string) => void;
|
|
@@ -56,6 +65,7 @@ export interface OverlayGeneralOpenersDeps {
|
|
|
56
65
|
openContextOverlay?: typeof openContextOverlay;
|
|
57
66
|
openContextResetOverlay?: typeof openContextResetOverlay;
|
|
58
67
|
openTasksOverlay?: typeof openTasksOverlay;
|
|
68
|
+
openDecisionsOverlay?: typeof openDecisionsOverlay;
|
|
59
69
|
openMemoryOverlay?: typeof openMemoryOverlay;
|
|
60
70
|
openViewOverlay?: typeof openViewOverlay;
|
|
61
71
|
}
|
|
@@ -66,6 +76,7 @@ export interface OverlayGeneralOpeners {
|
|
|
66
76
|
openContextReset(): void;
|
|
67
77
|
toggleFooter(): void;
|
|
68
78
|
openTasks(): void;
|
|
79
|
+
openDecisions(): void;
|
|
69
80
|
openMemory(): void;
|
|
70
81
|
openView(initialFilter?: string): void;
|
|
71
82
|
toggleDispatchBoard(): void;
|
|
@@ -76,6 +87,7 @@ export function createOverlayGeneralOpeners(deps: OverlayGeneralOpenersDeps): Ov
|
|
|
76
87
|
const openContextOverlayFactory = deps.openContextOverlay ?? openContextOverlay;
|
|
77
88
|
const openContextResetOverlayFactory = deps.openContextResetOverlay ?? openContextResetOverlay;
|
|
78
89
|
const openTasksOverlayFactory = deps.openTasksOverlay ?? openTasksOverlay;
|
|
90
|
+
const openDecisionsOverlayFactory = deps.openDecisionsOverlay ?? openDecisionsOverlay;
|
|
79
91
|
const openMemoryOverlayFactory = deps.openMemoryOverlay ?? openMemoryOverlay;
|
|
80
92
|
const openViewOverlayFactory = deps.openViewOverlay ?? openViewOverlay;
|
|
81
93
|
const showOverlayFrameFactory = deps.showOverlayFrame ?? showClioOverlayFrame;
|
|
@@ -137,6 +149,50 @@ export function createOverlayGeneralOpeners(deps: OverlayGeneralOpenersDeps): Ov
|
|
|
137
149
|
deps.transitions.state = "tasks";
|
|
138
150
|
deps.transitions.handle = openTasksOverlayFactory(deps.tui, () => deps.getTaskBoard?.() ?? null, {
|
|
139
151
|
onClose: deps.closeOverlay,
|
|
152
|
+
getSessionSnapshot: () => {
|
|
153
|
+
const meta = deps.getSessionMeta();
|
|
154
|
+
const entries = filterEntriesToActivePath(deps.readSessionEntries?.() ?? [], meta?.pinnedLeafTurnId ?? undefined);
|
|
155
|
+
const workspace = meta?.cwd ?? process.cwd();
|
|
156
|
+
return {
|
|
157
|
+
history: foldSessionTaskHistory(entries),
|
|
158
|
+
artifacts: foldSessionArtifacts(entries, { workspace }),
|
|
159
|
+
};
|
|
160
|
+
},
|
|
161
|
+
...(deps.userTasks
|
|
162
|
+
? {
|
|
163
|
+
getUserTasks: () => deps.userTasks?.snapshot() ?? [],
|
|
164
|
+
onAddUserTask: (title: string) => void deps.userTasks?.add(title),
|
|
165
|
+
onHandUserTask: (id: string) => {
|
|
166
|
+
const task = deps.userTasks?.hand(id, deps.getSessionId?.() ?? undefined);
|
|
167
|
+
if (!task) throw new Error("operator task inbox is unavailable");
|
|
168
|
+
deps.submitChat(formatUserTaskHandoff(task));
|
|
169
|
+
},
|
|
170
|
+
onDoneUserTask: (id: string) => void deps.userTasks?.done(id),
|
|
171
|
+
onDropUserTask: (id: string) => void deps.userTasks?.drop(id),
|
|
172
|
+
}
|
|
173
|
+
: {}),
|
|
174
|
+
onOpenArtifact: (path: string) => openView(`workspace:${path}`),
|
|
175
|
+
requestRender: deps.requestRender,
|
|
176
|
+
workspace: deps.getSessionMeta()?.cwd ?? process.cwd(),
|
|
177
|
+
});
|
|
178
|
+
deps.requestRender();
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const openDecisions = (): void => {
|
|
182
|
+
if (deps.transitions.state !== "closed") return;
|
|
183
|
+
deps.transitions.state = "decisions";
|
|
184
|
+
deps.transitions.handle = openDecisionsOverlayFactory(deps.tui, () => deps.getDecisionBoard?.() ?? [], {
|
|
185
|
+
onSupersede: (selection) => {
|
|
186
|
+
if (!deps.supersedeDecision) throw new Error("decision board is unavailable in this session");
|
|
187
|
+
deps.supersedeDecision(selection.interviewId, selection.key);
|
|
188
|
+
},
|
|
189
|
+
onCorrection: (selection, correction) => {
|
|
190
|
+
if (!deps.supersedeDecision) throw new Error("decision board is unavailable in this session");
|
|
191
|
+
deps.supersedeDecision(selection.interviewId, selection.key, correction);
|
|
192
|
+
deps.closeOverlay();
|
|
193
|
+
deps.submitChat(formatDecisionCorrectionTurn(selection, correction));
|
|
194
|
+
},
|
|
195
|
+
onClose: deps.closeOverlay,
|
|
140
196
|
});
|
|
141
197
|
deps.requestRender();
|
|
142
198
|
};
|
|
@@ -210,6 +266,7 @@ export function createOverlayGeneralOpeners(deps: OverlayGeneralOpenersDeps): Ov
|
|
|
210
266
|
openContextReset,
|
|
211
267
|
toggleFooter,
|
|
212
268
|
openTasks,
|
|
269
|
+
openDecisions,
|
|
213
270
|
openMemory,
|
|
214
271
|
openView,
|
|
215
272
|
toggleDispatchBoard,
|
|
@@ -10,6 +10,7 @@ export type OverlayState =
|
|
|
10
10
|
| "context-view"
|
|
11
11
|
| "context-reset"
|
|
12
12
|
| "tasks"
|
|
13
|
+
| "decisions"
|
|
13
14
|
| "memory"
|
|
14
15
|
| "view"
|
|
15
16
|
| "model"
|
|
@@ -158,6 +159,7 @@ export function routeOverlayKey(
|
|
|
158
159
|
if (overlayState === "closed") return false;
|
|
159
160
|
if (
|
|
160
161
|
(overlayState === "dispatch-board" && matches(data, "clio.dispatchBoard.toggle")) ||
|
|
162
|
+
(overlayState === "tasks" && matches(data, "clio.tasks.open")) ||
|
|
161
163
|
(overlayState === "tree" && matches(data, "clio.session.tree")) ||
|
|
162
164
|
(overlayState === "model" && matches(data, "clio.model.select")) ||
|
|
163
165
|
(overlayState === "help" && matches(data, "clio.leader"))
|
|
@@ -180,6 +182,7 @@ export function routeOverlayKey(
|
|
|
180
182
|
}
|
|
181
183
|
if (overlayState === "context-reset") return false;
|
|
182
184
|
if (overlayState === "tasks") return false;
|
|
185
|
+
if (overlayState === "decisions") return false;
|
|
183
186
|
if (overlayState === "memory") return false;
|
|
184
187
|
if (overlayState === "view") return false;
|
|
185
188
|
if (overlayState === "model") return routeModelOverlayKey(data, deps);
|
|
@@ -33,6 +33,8 @@ export type OverlayLifecycleApplicationDeps = Pick<
|
|
|
33
33
|
| "getSessionId"
|
|
34
34
|
| "getSettings"
|
|
35
35
|
| "getTaskBoard"
|
|
36
|
+
| "userTasks"
|
|
37
|
+
| "getDecisionBoard"
|
|
36
38
|
| "getTaskMemoryStatus"
|
|
37
39
|
| "interop"
|
|
38
40
|
| "observability"
|
|
@@ -46,6 +48,7 @@ export type OverlayLifecycleApplicationDeps = Pick<
|
|
|
46
48
|
| "resources"
|
|
47
49
|
| "session"
|
|
48
50
|
| "stateDir"
|
|
51
|
+
| "supersedeDecision"
|
|
49
52
|
| "toolRegistry"
|
|
50
53
|
| "writeSettings"
|
|
51
54
|
>;
|
|
@@ -94,6 +97,7 @@ export interface OverlayLifecycleRuntimeDeps {
|
|
|
94
97
|
openContextOverlay?: typeof import("./context-overlay.js").openContextOverlay;
|
|
95
98
|
openContextResetOverlay?: typeof import("./overlays/context-reset.js").openContextResetOverlay;
|
|
96
99
|
openTasksOverlay?: typeof import("./tasks-overlay.js").openTasksOverlay;
|
|
100
|
+
openDecisionsOverlay?: typeof import("./overlays/decisions.js").openDecisionsOverlay;
|
|
97
101
|
openMemoryOverlay?: typeof import("./memory-overlay.js").openMemoryOverlay;
|
|
98
102
|
openViewOverlay?: typeof import("./view/view-overlay.js").openViewOverlay;
|
|
99
103
|
openHelpOverlay?: typeof import("./overlays/help-reference.js").openHelpOverlay;
|
|
@@ -118,6 +122,7 @@ export interface OverlayLifecycleController {
|
|
|
118
122
|
openContextResetOverlayState(): void;
|
|
119
123
|
toggleFooterDashboardState(): void;
|
|
120
124
|
openTasksOverlayState(): void;
|
|
125
|
+
openDecisionsOverlayState(): void;
|
|
121
126
|
openMemoryOverlayState(): void;
|
|
122
127
|
openViewOverlayState(initialFilter?: string): void;
|
|
123
128
|
openModelOverlayState(): void;
|
|
@@ -169,6 +174,7 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
|
|
|
169
174
|
openContextOverlay: openContextOverlayFactory,
|
|
170
175
|
openContextResetOverlay: openContextResetOverlayFactory,
|
|
171
176
|
openTasksOverlay: openTasksOverlayFactory,
|
|
177
|
+
openDecisionsOverlay: openDecisionsOverlayFactory,
|
|
172
178
|
openMemoryOverlay: openMemoryOverlayFactory,
|
|
173
179
|
openViewOverlay: openViewOverlayFactory,
|
|
174
180
|
openHelpOverlay: openHelpOverlayFactory,
|
|
@@ -334,6 +340,10 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
|
|
|
334
340
|
renderTaskIsland: () => interactiveTickers.renderTaskIsland(),
|
|
335
341
|
requestRender: () => tui.requestRender(),
|
|
336
342
|
...(deps.app.getTaskBoard ? { getTaskBoard: deps.app.getTaskBoard } : {}),
|
|
343
|
+
...(deps.app.userTasks ? { userTasks: deps.app.userTasks } : {}),
|
|
344
|
+
...(deps.app.getDecisionBoard ? { getDecisionBoard: deps.app.getDecisionBoard } : {}),
|
|
345
|
+
...(deps.app.supersedeDecision ? { supersedeDecision: deps.app.supersedeDecision } : {}),
|
|
346
|
+
submitChat: (text) => deps.getSlashContext().submitChat(text),
|
|
337
347
|
...(deps.app.getTaskMemoryStatus ? { getTaskMemoryStatus: deps.app.getTaskMemoryStatus } : {}),
|
|
338
348
|
dataDir: deps.app.dataDir,
|
|
339
349
|
notify,
|
|
@@ -350,6 +360,7 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
|
|
|
350
360
|
...(openContextOverlayFactory ? { openContextOverlay: openContextOverlayFactory } : {}),
|
|
351
361
|
...(openContextResetOverlayFactory ? { openContextResetOverlay: openContextResetOverlayFactory } : {}),
|
|
352
362
|
...(openTasksOverlayFactory ? { openTasksOverlay: openTasksOverlayFactory } : {}),
|
|
363
|
+
...(openDecisionsOverlayFactory ? { openDecisionsOverlay: openDecisionsOverlayFactory } : {}),
|
|
353
364
|
...(openMemoryOverlayFactory ? { openMemoryOverlay: openMemoryOverlayFactory } : {}),
|
|
354
365
|
...(openViewOverlayFactory ? { openViewOverlay: openViewOverlayFactory } : {}),
|
|
355
366
|
});
|
|
@@ -362,6 +373,7 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
|
|
|
362
373
|
const openContextResetOverlayState = overlayGeneralOpeners.openContextReset;
|
|
363
374
|
const toggleFooterDashboardState = overlayGeneralOpeners.toggleFooter;
|
|
364
375
|
const openTasksOverlayState = overlayGeneralOpeners.openTasks;
|
|
376
|
+
const openDecisionsOverlayState = overlayGeneralOpeners.openDecisions;
|
|
365
377
|
const openMemoryOverlayState = overlayGeneralOpeners.openMemory;
|
|
366
378
|
const openViewOverlayState = overlayGeneralOpeners.openView;
|
|
367
379
|
const toggleDispatchBoardOverlay = overlayGeneralOpeners.toggleDispatchBoard;
|
|
@@ -380,6 +392,7 @@ export function createOverlayLifecycle(deps: OverlayLifecycleRuntimeDeps): Overl
|
|
|
380
392
|
openContextResetOverlayState,
|
|
381
393
|
toggleFooterDashboardState,
|
|
382
394
|
openTasksOverlayState,
|
|
395
|
+
openDecisionsOverlayState,
|
|
383
396
|
openMemoryOverlayState,
|
|
384
397
|
openViewOverlayState,
|
|
385
398
|
openModelOverlayState: overlayModelSelectors.openModelOverlayState,
|
|
@@ -192,6 +192,7 @@ export function createOverlayPermissionLifecycle(deps: OverlayPermissionLifecycl
|
|
|
192
192
|
const unsubscribePermission =
|
|
193
193
|
deps.toolRegistry?.onPermissionRequired((call, decision, meta) => {
|
|
194
194
|
const autonomy = deps.getAutonomy();
|
|
195
|
+
const view = mainApprovalRequestView(call, decision, meta, autonomy, deps.toolRegistry?.parkedCount());
|
|
195
196
|
// The dialog states the tool, the target, the action class, the axis
|
|
196
197
|
// that asked, and the keys that answer it. While it is on screen the
|
|
197
198
|
// notice repeats all of that one line above the transcript, so it is
|
|
@@ -207,13 +208,13 @@ export function createOverlayPermissionLifecycle(deps: OverlayPermissionLifecycl
|
|
|
207
208
|
type: "tool_approval_state",
|
|
208
209
|
toolCallId: meta.toolCallId,
|
|
209
210
|
state: "awaiting-approval",
|
|
211
|
+
view,
|
|
210
212
|
});
|
|
211
213
|
}
|
|
212
214
|
if (deps.getOverlayState() !== "closed") {
|
|
213
215
|
announceParked();
|
|
214
216
|
return;
|
|
215
217
|
}
|
|
216
|
-
const view = mainApprovalRequestView(call, decision, meta, autonomy, deps.toolRegistry?.parkedCount());
|
|
217
218
|
if (!deps.openPermissionOverlay(view)) {
|
|
218
219
|
announceParked();
|
|
219
220
|
return;
|
|
@@ -12,6 +12,7 @@ import { openMessagePickerOverlay } from "./overlays/message-picker.js";
|
|
|
12
12
|
import { openSessionOverlay } from "./overlays/session-selector.js";
|
|
13
13
|
import { openTreeOverlay } from "./overlays/tree-selector.js";
|
|
14
14
|
import { lastTurnSummaryFromLedger } from "./session-last-turn.js";
|
|
15
|
+
import { settleChatBeforeSessionSwitch } from "./session-switch-settlement.js";
|
|
15
16
|
import { reseedSessionUsageFromLedger, type SessionUsageSink } from "./session-usage-reseed.js";
|
|
16
17
|
import type { SlashCommandContext } from "./slash-commands.js";
|
|
17
18
|
import type { TurnSummary } from "./status/index.js";
|
|
@@ -20,7 +21,7 @@ export interface OverlaySessionLifecycleDeps {
|
|
|
20
21
|
tui: TUI;
|
|
21
22
|
transitions: OverlayTransitions;
|
|
22
23
|
session?: SessionContract;
|
|
23
|
-
chat: Pick<ChatLoop, "resetForSession">;
|
|
24
|
+
chat: Pick<ChatLoop, "cancel" | "isStreaming" | "resetForSession" | "whenSettled">;
|
|
24
25
|
chatPanel: ChatPanel;
|
|
25
26
|
/** The one transcript reset: the panel plus every view folded alongside it. */
|
|
26
27
|
resetTranscript(): void;
|
|
@@ -113,20 +114,58 @@ export function createOverlaySessionLifecycle(deps: OverlaySessionLifecycleDeps)
|
|
|
113
114
|
deps.transitions.state = "resume";
|
|
114
115
|
deps.transitions.handle = openResumeOverlay(deps.tui, {
|
|
115
116
|
session,
|
|
116
|
-
onResume: (sessionId) => {
|
|
117
|
+
onResume: async (sessionId) => {
|
|
118
|
+
const settlement = settleChatBeforeSessionSwitch(deps.chat);
|
|
119
|
+
if (settlement) await settlement;
|
|
117
120
|
deps.onResumeSession?.(sessionId);
|
|
121
|
+
// onResumeSession (wired to session.resume) catches and stderr-logs
|
|
122
|
+
// its own failure rather than throwing here, so this is the only
|
|
123
|
+
// signal available: a successful switch always leaves session.current()
|
|
124
|
+
// pointing at the requested id. Without this check, a failed switch
|
|
125
|
+
// still replayed the target's transcript and moved the chat leaf to
|
|
126
|
+
// it while session.current() stayed on the session the operator
|
|
127
|
+
// started on (issue #93), so the next message was appended with a
|
|
128
|
+
// parent turn from a session that was never actually opened.
|
|
129
|
+
if (session.current()?.id !== sessionId) {
|
|
130
|
+
emitCommandNotice(
|
|
131
|
+
deps.getSlashNotice(),
|
|
132
|
+
"error",
|
|
133
|
+
"resume",
|
|
134
|
+
`could not switch to that session; staying on ${preResumeSessionId ?? "no session"}`,
|
|
135
|
+
);
|
|
136
|
+
deps.refreshFooter();
|
|
137
|
+
deps.requestRender();
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
118
140
|
try {
|
|
119
141
|
const turns = deps.readStructuredEntries(sessionId);
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
142
|
+
// The leaf is read before the transcript is rebuilt because it is
|
|
143
|
+
// what the rebuild has to follow. resolveLeafOnOpen prefers a
|
|
144
|
+
// persisted `/tree` pin over the newest turn, so a session resumed
|
|
145
|
+
// on a pin extends from the pinned turn while the file still holds
|
|
146
|
+
// the abandoned branch after it. Replaying the file unfiltered
|
|
147
|
+
// rendered those abandoned turns as ordinary history above the
|
|
148
|
+
// prompt, disagreeing with the branch the next message parents onto
|
|
149
|
+
// and with the tip `/tree` marks (issue #107). The `/tree` switch
|
|
150
|
+
// path below has always scoped its replay to the selected turn;
|
|
151
|
+
// this is the same active-path filter, rooted at the leaf resume
|
|
152
|
+
// actually landed on.
|
|
123
153
|
const leafTurnId = session.tree(sessionId).leafId;
|
|
154
|
+
// activeLeafTurnId, not uptoTurnId: this is a live branch about to
|
|
155
|
+
// be extended, so sidecars anchored to a path turn but written
|
|
156
|
+
// after it (a compaction summary covering the leaf, above all)
|
|
157
|
+
// still belong on screen. uptoTurnId is the historical-truncation
|
|
158
|
+
// variant `/tree` uses.
|
|
159
|
+
const replayOptions = leafTurnId ? { activeLeafTurnId: leafTurnId } : {};
|
|
160
|
+
deps.resetTranscript();
|
|
161
|
+
rehydrateChatPanelFromTurns(deps.chatPanel, turns, replayOptions);
|
|
162
|
+
const replayMessages = buildReplayAgentMessagesFromTurns(turns, replayOptions);
|
|
124
163
|
deps.chat.resetForSession(leafTurnId, replayMessages);
|
|
125
164
|
rescopeToBranch(session, turns, leafTurnId);
|
|
126
165
|
} catch (error) {
|
|
127
166
|
deps.stderr(`[/resume] transcript replay failed: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
128
167
|
}
|
|
129
|
-
if (
|
|
168
|
+
if (sessionId !== preResumeSessionId) {
|
|
130
169
|
deps.announceTaskMemorySeedOffer();
|
|
131
170
|
}
|
|
132
171
|
deps.refreshFooter();
|
|
@@ -160,7 +199,9 @@ export function createOverlaySessionLifecycle(deps: OverlaySessionLifecycleDeps)
|
|
|
160
199
|
deps.transitions.state = "tree";
|
|
161
200
|
deps.transitions.handle = openTreeSelector(deps.tui, {
|
|
162
201
|
session,
|
|
163
|
-
onSwitchTurn: (turnId) => {
|
|
202
|
+
onSwitchTurn: async (turnId) => {
|
|
203
|
+
const settlement = settleChatBeforeSessionSwitch(deps.chat);
|
|
204
|
+
if (settlement) await settlement;
|
|
164
205
|
try {
|
|
165
206
|
session.switchTurn(turnId);
|
|
166
207
|
const sessionId = session.current()?.id ?? null;
|
|
@@ -195,7 +236,8 @@ export function createOverlaySessionLifecycle(deps: OverlaySessionLifecycleDeps)
|
|
|
195
236
|
return;
|
|
196
237
|
}
|
|
197
238
|
const session = deps.session;
|
|
198
|
-
|
|
239
|
+
const preForkSessionId = session.current()?.id ?? null;
|
|
240
|
+
if (preForkSessionId === null) {
|
|
199
241
|
emitCommandNotice(
|
|
200
242
|
deps.getSlashNotice(),
|
|
201
243
|
"warn",
|
|
@@ -207,14 +249,29 @@ export function createOverlaySessionLifecycle(deps: OverlaySessionLifecycleDeps)
|
|
|
207
249
|
deps.transitions.state = "message-picker";
|
|
208
250
|
deps.transitions.handle = openMessagePicker(deps.tui, {
|
|
209
251
|
session,
|
|
210
|
-
onFork: (parentTurnId) => {
|
|
252
|
+
onFork: async (parentTurnId) => {
|
|
253
|
+
const settlement = settleChatBeforeSessionSwitch(deps.chat);
|
|
254
|
+
if (settlement) await settlement;
|
|
211
255
|
try {
|
|
212
256
|
if (deps.onForkSession) deps.onForkSession(parentTurnId);
|
|
213
257
|
else session.fork(parentTurnId);
|
|
214
|
-
deps.resetTranscript();
|
|
215
258
|
const forkedSessionId = session.current()?.id ?? null;
|
|
216
|
-
|
|
217
|
-
|
|
259
|
+
// A successful fork always creates a fresh session id; landing back
|
|
260
|
+
// on the id fork started from means the fork threw and
|
|
261
|
+
// onForkSession swallowed it (orchestrator.ts stderr-logs there).
|
|
262
|
+
// Do not replay: session.current() did not move (issue #93's
|
|
263
|
+
// extension.ts/fork.ts ordering fix keeps it on the session the
|
|
264
|
+
// operator started on instead of orphaning it), so there is
|
|
265
|
+
// nothing new to replay and doing so anyway just re-renders the
|
|
266
|
+
// same transcript while hiding that the fork failed.
|
|
267
|
+
if (forkedSessionId === null || forkedSessionId === preForkSessionId) {
|
|
268
|
+
emitCommandNotice(deps.getSlashNotice(), "error", "fork", `fork failed; staying on ${preForkSessionId}`);
|
|
269
|
+
deps.refreshFooter();
|
|
270
|
+
deps.requestRender();
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
deps.resetTranscript();
|
|
274
|
+
replayFork(forkedSessionId, parentTurnId, session);
|
|
218
275
|
} catch (error) {
|
|
219
276
|
deps.stderr(`[/fork] fork failed: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
220
277
|
}
|
|
@@ -13,8 +13,9 @@ import {
|
|
|
13
13
|
} from "../../engine/tui.js";
|
|
14
14
|
import type { AskUserAnswer, AskUserQuestion, AskUserResult } from "../../tools/ask-user.js";
|
|
15
15
|
import { ASK_USER_OTHER_LABEL, cancelledAskUserResult } from "../../tools/ask-user.js";
|
|
16
|
-
import { buildHint, DEFAULT_SELECT_THEME, showClioOverlayFrame } from "../overlay-frame.js";
|
|
16
|
+
import { buildHint, DEFAULT_SELECT_THEME, type HintEntry, showClioOverlayFrame } from "../overlay-frame.js";
|
|
17
17
|
import { type ClioToken, clioTheme, dotSep, GLYPH, screenTitle } from "../theme/index.js";
|
|
18
|
+
import { nextContentScrollOffset, type ViewScrollAction } from "../view/view-overlay.js";
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* The border and title token while a decision is pending.
|
|
@@ -54,7 +55,7 @@ export const ASK_USER_DEFAULT_SURFACE: AskUserSurface = "panel";
|
|
|
54
55
|
interface AskUserSurfaceGeometry {
|
|
55
56
|
/** Box width in columns; zero fills the row. The frame clamps to the terminal. */
|
|
56
57
|
width: number;
|
|
57
|
-
/** Rows the body may use, before the borders. */
|
|
58
|
+
/** Rows the body may use, before the borders. Zero means every row the frame has. */
|
|
58
59
|
maxInnerRows: number;
|
|
59
60
|
anchor: NonNullable<OverlayOptions["anchor"]>;
|
|
60
61
|
margin: NonNullable<OverlayOptions["margin"]>;
|
|
@@ -74,12 +75,18 @@ interface AskUserSurfaceGeometry {
|
|
|
74
75
|
export const ASK_USER_SURFACE_GEOMETRY: Record<AskUserSurface, AskUserSurfaceGeometry> = {
|
|
75
76
|
compact: { width: 68, maxInnerRows: 10, anchor: "bottom-center", margin: { top: 1, right: 2, bottom: 1, left: 2 } },
|
|
76
77
|
panel: { width: 88, maxInnerRows: 18, anchor: "center", margin: 1 },
|
|
77
|
-
interview
|
|
78
|
+
// The interview is a workspace, not a modal. A cap of 42 turned a 66-row
|
|
79
|
+
// terminal into a centered box with twenty blank rows around it, so the
|
|
80
|
+
// interview takes every row the frame has and lays its own chrome out inside
|
|
81
|
+
// them. The one-row margin keeps the border off the top row and the footer.
|
|
82
|
+
interview: { width: 0, maxInnerRows: 0, anchor: "center", margin: 1 },
|
|
78
83
|
};
|
|
79
84
|
|
|
80
85
|
const ASK_USER_FRAME_ROWS = 2;
|
|
81
86
|
const ASK_USER_FRAME_AND_MARGIN_ROWS = 4;
|
|
82
87
|
const MIN_VISIBLE_OPTIONS = 3;
|
|
88
|
+
/** Interview rows spent on chrome before an option row: strip, header, blanks, status, one question row. */
|
|
89
|
+
const INTERVIEW_CHROME_ROWS = 7;
|
|
83
90
|
const MAX_VISIBLE_OPTIONS = 12;
|
|
84
91
|
const ELLIPSIS = "…";
|
|
85
92
|
|
|
@@ -227,6 +234,14 @@ class AskUserOverlayView implements Component {
|
|
|
227
234
|
private list: SelectList | null = null;
|
|
228
235
|
private input: Input | null = null;
|
|
229
236
|
private resolveCurrent: ((result: AskUserResult) => void) | null = null;
|
|
237
|
+
/** Rows of the interview's scrollable middle already scrolled past. */
|
|
238
|
+
private contentScrollOffset = 0;
|
|
239
|
+
/** Whether the last interview render had more content than the region held. */
|
|
240
|
+
private contentOverflows = false;
|
|
241
|
+
/** Rows the scroll region drew on the last render, for a page-sized jump. */
|
|
242
|
+
private contentRegionHeight = 1;
|
|
243
|
+
/** Rows the scroll region wanted on the last render. */
|
|
244
|
+
private contentTotalRows = 0;
|
|
230
245
|
|
|
231
246
|
constructor(private readonly deps: AskUserOverlayViewDeps) {}
|
|
232
247
|
|
|
@@ -240,6 +255,7 @@ class AskUserOverlayView implements Component {
|
|
|
240
255
|
this.states = this.questions.map((question) => createQuestionState(question));
|
|
241
256
|
this.list = null;
|
|
242
257
|
this.input = null;
|
|
258
|
+
this.resetContentScroll();
|
|
243
259
|
// Before the controls, because how many option rows fit is a property of
|
|
244
260
|
// the box this round is about to be drawn in.
|
|
245
261
|
this.setSurface(askUserSurface(this.questions, this.history.length));
|
|
@@ -283,6 +299,10 @@ class AskUserOverlayView implements Component {
|
|
|
283
299
|
const state = this.currentState();
|
|
284
300
|
if (!question || !state) return;
|
|
285
301
|
|
|
302
|
+
// Scrolling the transcript must not cost the operator their place in the
|
|
303
|
+
// options, so the region has keys of its own that neither control claims.
|
|
304
|
+
if (this.handleScrollInput(data, state.mode)) return;
|
|
305
|
+
|
|
286
306
|
if (state.mode === "text") {
|
|
287
307
|
if (this.isTextModePreviousKey(data)) {
|
|
288
308
|
this.goToRelativeQuestion(-1);
|
|
@@ -331,21 +351,106 @@ class AskUserOverlayView implements Component {
|
|
|
331
351
|
const question = this.currentQuestion();
|
|
332
352
|
if (!question) return [clioTheme().fg("muted", "No questions.")];
|
|
333
353
|
|
|
334
|
-
|
|
354
|
+
if (this.surface === "interview") return this.renderInterview(question, safeWidth, maxRows);
|
|
355
|
+
|
|
356
|
+
this.contentOverflows = false;
|
|
335
357
|
const controlLines = this.renderControlLines(safeWidth);
|
|
336
|
-
const ledgerLines = interview ? this.renderAnswerLedger(safeWidth) : [];
|
|
337
358
|
const statusLines = this.status.length > 0 ? ["", fitLine(clioTheme().fg("dim", this.status), safeWidth)] : [];
|
|
338
|
-
const stripLines = interview ? [this.renderQuestionStrip(safeWidth), ""] : [];
|
|
339
359
|
const headerLine = this.renderQuestionHeader(question, safeWidth);
|
|
340
360
|
const headerLines = headerLine.length > 0 ? [headerLine] : [];
|
|
341
|
-
const spent =
|
|
342
|
-
stripLines.length + headerLines.length + statusLines.length + 1 + controlLines.length + ledgerLines.length;
|
|
361
|
+
const spent = headerLines.length + statusLines.length + 1 + controlLines.length;
|
|
343
362
|
const questionLines = wrapTextWithAnsi(question.question, safeWidth).slice(0, Math.max(1, maxRows - spent));
|
|
344
363
|
|
|
345
|
-
return [...
|
|
346
|
-
|
|
347
|
-
|
|
364
|
+
return [...headerLines, ...questionLines, ...statusLines, "", ...controlLines].slice(0, maxRows);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* The interview, as a workspace with fixed chrome and a scrolling middle.
|
|
369
|
+
*
|
|
370
|
+
* The round strip and the question header hold the top; the status line and
|
|
371
|
+
* the active control hold the bottom, at their full row cost, so the thing the
|
|
372
|
+
* operator has to act on is never the thing that gets clipped. The question
|
|
373
|
+
* text and the answer ledger share whatever is left and scroll through it.
|
|
374
|
+
* Everything is re-derived per render, so a resize is just the next frame.
|
|
375
|
+
*/
|
|
376
|
+
private renderInterview(question: AskUserQuestion, width: number, maxRows: number): string[] {
|
|
377
|
+
const theme = clioTheme();
|
|
378
|
+
const controlLines = this.renderControlLines(width);
|
|
379
|
+
const statusLines = this.status.length > 0 ? ["", fitLine(theme.fg("dim", this.status), width)] : [];
|
|
380
|
+
const headerLine = this.renderQuestionHeader(question, width);
|
|
381
|
+
const headerLines = headerLine.length > 0 ? [headerLine] : [];
|
|
382
|
+
const stripLines = [this.renderQuestionStrip(width), ""];
|
|
383
|
+
const content = [...wrapTextWithAnsi(question.question, width), ...this.renderAnswerLedger(width)];
|
|
384
|
+
|
|
385
|
+
// Chrome sheds from the outside in when the terminal is too short to carry
|
|
386
|
+
// all of it: the round strip first, then the header, then the status, and
|
|
387
|
+
// the control only if a terminal is short enough that nothing else is left.
|
|
388
|
+
let top = [...stripLines, ...headerLines];
|
|
389
|
+
let bottom = [...statusLines, "", ...controlLines];
|
|
390
|
+
const region = (): number => maxRows - top.length - bottom.length;
|
|
391
|
+
if (region() < 1) top = [...headerLines];
|
|
392
|
+
if (region() < 1) top = [];
|
|
393
|
+
if (region() < 1) bottom = ["", ...controlLines];
|
|
394
|
+
if (region() < 1) bottom = [...controlLines];
|
|
395
|
+
const regionRows = Math.max(1, region());
|
|
396
|
+
|
|
397
|
+
const overflows = content.length > regionRows;
|
|
398
|
+
this.contentOverflows = overflows;
|
|
399
|
+
this.contentTotalRows = content.length;
|
|
400
|
+
// One row of the region pays for the indicator when there is more than the
|
|
401
|
+
// region holds, so the marker never covers a row it is reporting on.
|
|
402
|
+
const viewRows = overflows ? Math.max(1, regionRows - 1) : regionRows;
|
|
403
|
+
this.contentRegionHeight = viewRows;
|
|
404
|
+
this.contentScrollOffset = Math.max(0, Math.min(this.contentScrollOffset, Math.max(0, content.length - viewRows)));
|
|
405
|
+
|
|
406
|
+
const visible = content.slice(this.contentScrollOffset, this.contentScrollOffset + viewRows);
|
|
407
|
+
const middle = [...visible];
|
|
408
|
+
if (overflows) middle.push(fitLine(theme.fg("dim", this.scrollIndicator(content.length, viewRows)), width));
|
|
409
|
+
while (middle.length < regionRows) middle.push("");
|
|
410
|
+
|
|
411
|
+
return [...top, ...middle, ...bottom].slice(0, maxRows);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
private scrollIndicator(total: number, viewRows: number): string {
|
|
415
|
+
const above = this.contentScrollOffset;
|
|
416
|
+
const below = Math.max(0, total - viewRows - above);
|
|
417
|
+
const parts: string[] = [];
|
|
418
|
+
if (above > 0) parts.push(`↑ ${above} more`);
|
|
419
|
+
if (below > 0) parts.push(`↓ ${below} more`);
|
|
420
|
+
parts.push(`(${Math.min(total, above + viewRows)}/${total})`);
|
|
421
|
+
return parts.join(" ");
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/** True when the key belonged to the scroll region rather than to a control. */
|
|
425
|
+
private handleScrollInput(data: string, mode: Mode): boolean {
|
|
426
|
+
if (this.surface !== "interview") return false;
|
|
427
|
+
const action = this.scrollAction(data, mode);
|
|
428
|
+
if (!action) return false;
|
|
429
|
+
const next = nextContentScrollOffset(
|
|
430
|
+
this.contentScrollOffset,
|
|
431
|
+
this.contentTotalRows,
|
|
432
|
+
this.contentRegionHeight,
|
|
433
|
+
action,
|
|
348
434
|
);
|
|
435
|
+
if (next !== this.contentScrollOffset) {
|
|
436
|
+
this.contentScrollOffset = next;
|
|
437
|
+
this.deps.requestRender();
|
|
438
|
+
}
|
|
439
|
+
return true;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
private scrollAction(data: string, mode: Mode): ViewScrollAction | null {
|
|
443
|
+
if (matchesKey(data, "pageUp")) return "page-up";
|
|
444
|
+
if (matchesKey(data, "pageDown")) return "page-down";
|
|
445
|
+
// Ctrl+U and Ctrl+D are line-kill and half-page in a text field, so they
|
|
446
|
+
// stay with the input while one is focused.
|
|
447
|
+
if (mode === "select" && matchesKey(data, "ctrl+u")) return "half-up";
|
|
448
|
+
if (mode === "select" && matchesKey(data, "ctrl+d")) return "half-down";
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
private resetContentScroll(): void {
|
|
453
|
+
this.contentScrollOffset = 0;
|
|
349
454
|
}
|
|
350
455
|
|
|
351
456
|
footerHint(): string {
|
|
@@ -357,11 +462,11 @@ class AskUserOverlayView implements Component {
|
|
|
357
462
|
if (!question || !state) return buildHint([]);
|
|
358
463
|
if (state.mode === "text") {
|
|
359
464
|
return this.questions.length > 1
|
|
360
|
-
?
|
|
465
|
+
? this.withScrollHint([
|
|
361
466
|
{ key: "Enter", verb: "submit" },
|
|
362
467
|
{ key: "Alt+Left/Right", verb: "question" },
|
|
363
468
|
])
|
|
364
|
-
:
|
|
469
|
+
: this.withScrollHint([{ key: "Enter", verb: "submit" }]);
|
|
365
470
|
}
|
|
366
471
|
// `accept` rather than `select` or `commit`: the footer of a decision prompt
|
|
367
472
|
// has one job, which is to name the key that answers it. Esc keeps the
|
|
@@ -369,22 +474,32 @@ class AskUserOverlayView implements Component {
|
|
|
369
474
|
// interview together.
|
|
370
475
|
if (question.multi_select === true) {
|
|
371
476
|
return this.questions.length > 1
|
|
372
|
-
?
|
|
477
|
+
? this.withScrollHint([
|
|
373
478
|
{ key: "Left/Right", verb: "question" },
|
|
374
479
|
{ key: "Space", verb: "toggle" },
|
|
375
480
|
{ key: "Enter", verb: "accept" },
|
|
376
481
|
])
|
|
377
|
-
:
|
|
482
|
+
: this.withScrollHint([
|
|
378
483
|
{ key: "Space", verb: "toggle" },
|
|
379
484
|
{ key: "Enter", verb: "accept" },
|
|
380
485
|
]);
|
|
381
486
|
}
|
|
382
487
|
return this.questions.length > 1
|
|
383
|
-
?
|
|
488
|
+
? this.withScrollHint([
|
|
384
489
|
{ key: "Left/Right", verb: "question" },
|
|
385
490
|
{ key: "Enter", verb: "accept" },
|
|
386
491
|
])
|
|
387
|
-
:
|
|
492
|
+
: this.withScrollHint([{ key: "Enter", verb: "accept" }]);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* The keys for this question, plus the scroll keys when there is something to
|
|
497
|
+
* scroll. A footer that always advertised PgUp would be advertising a key that
|
|
498
|
+
* does nothing on most rounds.
|
|
499
|
+
*/
|
|
500
|
+
private withScrollHint(entries: ReadonlyArray<HintEntry>): string {
|
|
501
|
+
if (this.surface !== "interview" || !this.contentOverflows) return buildHint(entries);
|
|
502
|
+
return buildHint([...entries, { key: "PgUp/PgDn", verb: "scroll" }]);
|
|
388
503
|
}
|
|
389
504
|
|
|
390
505
|
private setSurface(next: AskUserSurface): void {
|
|
@@ -443,17 +558,20 @@ class AskUserOverlayView implements Component {
|
|
|
443
558
|
private maxInnerRows(): number {
|
|
444
559
|
const cap = ASK_USER_SURFACE_GEOMETRY[this.surface].maxInnerRows;
|
|
445
560
|
const rows = this.deps.getTerminalRows();
|
|
446
|
-
|
|
447
|
-
|
|
561
|
+
// A zero cap is the interview's "every row the frame has", which is the
|
|
562
|
+
// same number the frame budgets itself when the mount passes no maxHeight.
|
|
563
|
+
if (!Number.isFinite(rows) || rows <= 0) return cap > 0 ? cap : 1;
|
|
564
|
+
const available = Math.max(1, Math.floor(rows) - ASK_USER_FRAME_AND_MARGIN_ROWS);
|
|
565
|
+
return cap > 0 ? Math.min(cap, available) : available;
|
|
448
566
|
}
|
|
449
567
|
|
|
450
568
|
private maxVisibleOptions(): number {
|
|
451
569
|
const inner = this.maxInnerRows();
|
|
452
|
-
// The interview
|
|
453
|
-
//
|
|
454
|
-
//
|
|
570
|
+
// The interview's option list is budgeted off the live row count rather
|
|
571
|
+
// than a fixed heuristic: the strip, header, blank, status, blank and one
|
|
572
|
+
// row of question are what the control shares the workspace with.
|
|
455
573
|
if (this.surface === "interview") {
|
|
456
|
-
return Math.max(MIN_VISIBLE_OPTIONS, Math.min(MAX_VISIBLE_OPTIONS, inner -
|
|
574
|
+
return Math.max(MIN_VISIBLE_OPTIONS, Math.min(MAX_VISIBLE_OPTIONS, inner - INTERVIEW_CHROME_ROWS));
|
|
457
575
|
}
|
|
458
576
|
return Math.max(1, Math.min(MAX_VISIBLE_OPTIONS, inner - 4));
|
|
459
577
|
}
|
|
@@ -809,6 +927,7 @@ class AskUserOverlayView implements Component {
|
|
|
809
927
|
const next = this.nextUnansweredIndex();
|
|
810
928
|
if (next !== null) {
|
|
811
929
|
this.index = next;
|
|
930
|
+
this.resetContentScroll();
|
|
812
931
|
const nextState = this.currentState();
|
|
813
932
|
const nextQuestion = this.currentQuestion();
|
|
814
933
|
if (nextState && nextQuestion && !nextState.answer.trim()) nextState.mode = initialMode(nextQuestion);
|
|
@@ -844,6 +963,7 @@ class AskUserOverlayView implements Component {
|
|
|
844
963
|
this.syncActiveControl();
|
|
845
964
|
this.index = (this.index + delta + this.questions.length) % this.questions.length;
|
|
846
965
|
this.status = "";
|
|
966
|
+
this.resetContentScroll();
|
|
847
967
|
this.rebuildControl();
|
|
848
968
|
this.deps.requestRender();
|
|
849
969
|
}
|
|
@@ -888,7 +1008,9 @@ export function openAskUserOverlay(tui: TUI, deps: OpenAskUserOverlayDeps): AskU
|
|
|
888
1008
|
handle = showClioOverlayFrame(tui, view, {
|
|
889
1009
|
anchor: geometry.anchor,
|
|
890
1010
|
width: geometry.width,
|
|
891
|
-
|
|
1011
|
+
// A zero cap means the frame keeps its own budget, which is every row
|
|
1012
|
+
// the terminal has left after the margins.
|
|
1013
|
+
...(geometry.maxInnerRows > 0 ? { maxHeight: geometry.maxInnerRows + ASK_USER_FRAME_ROWS } : {}),
|
|
892
1014
|
margin: geometry.margin,
|
|
893
1015
|
title: () => (view.isDecisionPending() ? ASK_USER_DECISION_TITLE : ASK_USER_WAITING_TITLE),
|
|
894
1016
|
tone: () => (view.isDecisionPending() ? ASK_USER_DECISION_TONE : undefined),
|