@vincemakes/kiso-code 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chat.js +59 -19
- package/dist/dispatch.js +3 -2
- package/dist/index.js +5 -0
- package/dist/resume.js +9 -11
- package/dist/state.d.ts +5 -0
- package/package.json +13 -13
package/dist/chat.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* estimates. All bodies moved verbatim from index.ts.
|
|
6
6
|
*/
|
|
7
7
|
import { readFileSync } from "node:fs";
|
|
8
|
-
import { escapeTerminal,
|
|
8
|
+
import { escapeTerminal, idleStatus, palette, renderEvent, renderRecap, runningStatus, toolTarget, STATUS_GLYPHS, } from "@vincemakes/kiso-tui";
|
|
9
9
|
import { editFileDiff, writeFileDiff } from "@vincemakes/kiso-tui";
|
|
10
10
|
import { canonicalTargetPath } from "@vincemakes/kiso-tools-node";
|
|
11
11
|
import { canonicalizeUsage } from "@vincemakes/kiso-runtime";
|
|
@@ -99,9 +99,9 @@ export function startStatusSpinner(onTick) {
|
|
|
99
99
|
return () => { };
|
|
100
100
|
// v3 §03/§05: the working glyph family ▖▘▝▗, 200ms rotation — the
|
|
101
101
|
// callback repaints the running status line with the new glyph.
|
|
102
|
-
|
|
102
|
+
// KC2 §5: the family itself moved to the tui's status formatters.
|
|
103
103
|
let i = 0;
|
|
104
|
-
const timer = setInterval(() => onTick(
|
|
104
|
+
const timer = setInterval(() => onTick(STATUS_GLYPHS[i++ % STATUS_GLYPHS.length]), 200);
|
|
105
105
|
timer.unref();
|
|
106
106
|
return () => clearInterval(timer);
|
|
107
107
|
}
|
|
@@ -587,6 +587,30 @@ export async function chat(session, faux, input, autoCompact) {
|
|
|
587
587
|
currentRun.abort();
|
|
588
588
|
}
|
|
589
589
|
});
|
|
590
|
+
// KC2 §2/§3 — the redirect: "stop, and do THIS instead". No stream
|
|
591
|
+
// injection, no new durable or op state — the run aborts (its terminal
|
|
592
|
+
// is an honest `aborted`) and the buffer's text becomes the next turn.
|
|
593
|
+
// With no run in flight the gesture is simply an Enter, which is what
|
|
594
|
+
// the human means by it: there is nothing to stop.
|
|
595
|
+
input.onRedirect?.((line) => {
|
|
596
|
+
if (currentRun === null)
|
|
597
|
+
return dispatch(line, dispatchCtx);
|
|
598
|
+
console.log("\n[redirecting run]");
|
|
599
|
+
pendingAsk?.();
|
|
600
|
+
currentRun.abort();
|
|
601
|
+
// §3: a correction must run BEFORE the follow-ups queued earlier —
|
|
602
|
+
// it is a correction OF them. The existing slot mechanics compose
|
|
603
|
+
// it: every pending slot leaves through the SAME pop the ↑ key uses
|
|
604
|
+
// (cancelled, so its chain segment skips), then they re-enter
|
|
605
|
+
// BEHIND the correction, in their original order. Ephemeral
|
|
606
|
+
// reordering of ephemeral state; the durable log still just records
|
|
607
|
+
// what ran, in the order it ran.
|
|
608
|
+
const jumped = pendingTurns.map((s) => s.line);
|
|
609
|
+
for (let i = jumped.length; i > 0; i -= 1)
|
|
610
|
+
popQueue();
|
|
611
|
+
for (const text of [line, ...jumped])
|
|
612
|
+
submitTurn(text);
|
|
613
|
+
});
|
|
590
614
|
// round 5 (P1-11): the PERSISTENT line listener is installed BEFORE the
|
|
591
615
|
// startup recovery — a cancelled question's re-emitted "line" needs a
|
|
592
616
|
// listener from the very first instant, or the input is silently lost.
|
|
@@ -617,23 +641,18 @@ export async function chat(session, faux, input, autoCompact) {
|
|
|
617
641
|
let runUsage = { in: null, out: null, cache: null, known: false };
|
|
618
642
|
let runGlyph = "▖";
|
|
619
643
|
let runStart = Date.now();
|
|
644
|
+
// KC2 §5: the STATE (the glyph, the run's start, the usage, the dock)
|
|
645
|
+
// stays here; the ROW's text is the tui's status formatter.
|
|
620
646
|
const paintRunning = () => {
|
|
621
|
-
if (
|
|
622
|
-
|
|
623
|
-
const ratio = estimateCtxRatio(session);
|
|
624
|
-
const pct = Number.isFinite(ratio) ? Math.round((1 - ratio) * 100) : null;
|
|
625
|
-
const out = runUsage.out !== null ? ` ↓ ${kUnit(runUsage.out)} tokens` : "";
|
|
626
|
-
dock.setStatus(`${runGlyph} working ${Math.max(1, Math.round((Date.now() - runStart) / 1000))}s${out} · esc to interrupt · ctx left ~${pct}%`);
|
|
647
|
+
if (dock.active)
|
|
648
|
+
dock.setStatus(runningStatus(runGlyph, runStart, runUsage.out, estimateCtxRatio(session)));
|
|
627
649
|
};
|
|
650
|
+
// W19: under plan the idle row makes the posture unmistakable — the W4
|
|
651
|
+
// parentheses idiom names the read-only constraint. The tier is the
|
|
652
|
+
// CALLER's word (the recovery flow passes the bare mode).
|
|
628
653
|
const paintIdle = () => {
|
|
629
|
-
if (
|
|
630
|
-
|
|
631
|
-
const ratio = estimateCtxRatio(session);
|
|
632
|
-
const pct = Number.isFinite(ratio) ? Math.round((1 - ratio) * 100) : null;
|
|
633
|
-
// W19: under plan the idle row makes the posture unmistakable —
|
|
634
|
-
// the W4 parentheses idiom names the read-only constraint.
|
|
635
|
-
const tier = getMode() === "plan" ? "plan (read-only)" : getMode();
|
|
636
|
-
dock.setStatus(`▸ ${tier} · /mode to switch · ${agentModel} · ctx left ~${pct}%`);
|
|
654
|
+
if (dock.active)
|
|
655
|
+
dock.setStatus(idleStatus(getMode() === "plan" ? "plan (read-only)" : getMode(), agentModel, estimateCtxRatio(session)));
|
|
637
656
|
};
|
|
638
657
|
const statusCb = (u, ctx) => {
|
|
639
658
|
runUsage = u;
|
|
@@ -643,13 +662,34 @@ export async function chat(session, faux, input, autoCompact) {
|
|
|
643
662
|
const slot = { line, cancelled: false };
|
|
644
663
|
pendingTurns.push(slot);
|
|
645
664
|
queued += 1;
|
|
646
|
-
chainRef.current = chainRef.current.then(() => {
|
|
665
|
+
chainRef.current = chainRef.current.then(async () => {
|
|
647
666
|
if (slot.cancelled)
|
|
648
667
|
return; // the pop already dropped it — no double count
|
|
649
668
|
const idx = pendingTurns.indexOf(slot);
|
|
650
669
|
if (idx >= 0)
|
|
651
670
|
pendingTurns.splice(idx, 1); // the chip leaves when the turn STARTS
|
|
652
|
-
|
|
671
|
+
// KC2 §4 — the FRESH-TURN uncertainty gate. The runtime's fresh
|
|
672
|
+
// path checks only the open-run gate before persisting
|
|
673
|
+
// user_input (ResumeBlockedError guards the RESUME derivation
|
|
674
|
+
// alone), and the CLI resolved uncertains at startup recovery
|
|
675
|
+
// only — so a turn queued behind an abort-mid-tool could reach
|
|
676
|
+
// the model before the human said whether the side effect
|
|
677
|
+
// applied. It asks HERE, before the turn starts, with the same
|
|
678
|
+
// recovery UI; a human who declines leaves it uncertain and the
|
|
679
|
+
// turn does not start. The resolution's own model-facing fill
|
|
680
|
+
// also answers the dangling tool_use, so the next request never
|
|
681
|
+
// carries an unanswered call. Composed from existing APIs: zero
|
|
682
|
+
// core lines, zero runtime lines.
|
|
683
|
+
if (session.uncertainExecutions().length > 0)
|
|
684
|
+
await resolveUncertains(session, input, () => cancelled);
|
|
685
|
+
if (session.uncertainExecutions().length === 0)
|
|
686
|
+
return turn(line);
|
|
687
|
+
// The human declined (round 10: a cancelled ask records NOTHING —
|
|
688
|
+
// the execution stays uncertain and durable), so the turn does not
|
|
689
|
+
// start. It is never swallowed in silence: the held text is
|
|
690
|
+
// printed back, so the human can see what is waiting on them.
|
|
691
|
+
queued = Math.max(0, queued - 1);
|
|
692
|
+
body.notice(`[turn held — the interrupted execution is still undecided] ${escapeTerminal(line)}`);
|
|
653
693
|
});
|
|
654
694
|
};
|
|
655
695
|
// W22: the ↑/esc pop — the LAST queued slot leaves the queue
|
package/dist/dispatch.js
CHANGED
|
@@ -28,8 +28,9 @@ export function dispatch(line, ctx) {
|
|
|
28
28
|
bodyLog(cmd("/model", "list model profiles; /model <name|provider/model> switches"));
|
|
29
29
|
bodyLog(cmd("/compact", "summarize the older conversation to free context"));
|
|
30
30
|
// KC1: the composer's keys ride the SAME bodyLog call (it splits
|
|
31
|
-
// on \n) — the help gains a row, the cli source does not
|
|
32
|
-
|
|
31
|
+
// on \n) — the help gains a row, the cli source does not.
|
|
32
|
+
// KC2: the redirect joins the same row for the same reason.
|
|
33
|
+
bodyLog(`${cmd("exit", "leave the session")}\n${cmd("keys", "enter sends · ctrl+J newline (shift+enter where encoded) · esc stops the run · alt+⏎ stops it and sends this instead")}`);
|
|
33
34
|
ctx.input.prompt();
|
|
34
35
|
});
|
|
35
36
|
return;
|
package/dist/index.js
CHANGED
|
@@ -140,6 +140,11 @@ function editorInput(editor) {
|
|
|
140
140
|
onExpand(cb) {
|
|
141
141
|
editor.onExpand(cb);
|
|
142
142
|
},
|
|
143
|
+
// KC2 §2: the redirect gesture — the editor decides WHEN (the
|
|
144
|
+
// same-chunk pair, the precedence gate); chat decides what it MEANS.
|
|
145
|
+
onRedirect(cb) {
|
|
146
|
+
editor.onRedirect(cb);
|
|
147
|
+
},
|
|
143
148
|
question(query, cb) {
|
|
144
149
|
editor.question(query, cb);
|
|
145
150
|
},
|
package/dist/resume.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The ergonomics batch B4 (pure move) — resume: the RECOVERY flow (Area 2/7). The body
|
|
3
3
|
* moved verbatim from index.ts.
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
5
|
+
import { idleStatus, runningStatus } from "@vincemakes/kiso-tui";
|
|
6
6
|
import { getMode } from "./mode.js";
|
|
7
7
|
import { agentModel, dock } from "./state.js";
|
|
8
8
|
import { pendingAsk, resolveUncertains } from "./trust-ui.js";
|
|
@@ -23,20 +23,18 @@ export async function resume(session, prompt, faux, input) {
|
|
|
23
23
|
let runUsage = { in: null, out: null, cache: null, known: false };
|
|
24
24
|
let runGlyph = "▖";
|
|
25
25
|
let runStart = Date.now();
|
|
26
|
+
// KC2 §5: the rows the REPL and this flow used to build separately are
|
|
27
|
+
// ONE formatter now — the running row was duplicated verbatim.
|
|
26
28
|
const statusCb = (u, ctx) => {
|
|
27
29
|
runUsage = u;
|
|
28
|
-
if (
|
|
29
|
-
|
|
30
|
-
const pct = Number.isFinite(ctx) ? Math.round((1 - ctx) * 100) : null;
|
|
31
|
-
const out = runUsage.out !== null ? ` ↓ ${kUnit(runUsage.out)} tokens` : "";
|
|
32
|
-
dock.setStatus(`${runGlyph} working ${Math.max(1, Math.round((Date.now() - runStart) / 1000))}s${out} · esc to interrupt · ctx left ~${pct}%`);
|
|
30
|
+
if (dock.active)
|
|
31
|
+
dock.setStatus(runningStatus(runGlyph, runStart, u.out, ctx));
|
|
33
32
|
};
|
|
33
|
+
// the recovery flow prints the BARE mode (chat spells plan's posture) —
|
|
34
|
+
// the extraction keeps that difference, it was not asked to settle it.
|
|
34
35
|
const paintIdle = () => {
|
|
35
|
-
if (
|
|
36
|
-
|
|
37
|
-
const ratio = estimateCtxRatio(session);
|
|
38
|
-
const pct = Number.isFinite(ratio) ? Math.round((1 - ratio) * 100) : null;
|
|
39
|
-
dock.setStatus(`▸ ${getMode()} · /mode to switch · ${agentModel} · ctx left ~${pct}%`);
|
|
36
|
+
if (dock.active)
|
|
37
|
+
dock.setStatus(idleStatus(getMode(), agentModel, estimateCtxRatio(session)));
|
|
40
38
|
};
|
|
41
39
|
const withRun = async (run) => {
|
|
42
40
|
currentRun = run;
|
package/dist/state.d.ts
CHANGED
|
@@ -30,6 +30,11 @@ export interface LineInput {
|
|
|
30
30
|
/** W15: the expand key (ctrl+r) — the chain-level action, never the
|
|
31
31
|
* editor's own interpretation. */
|
|
32
32
|
onExpand(cb: () => void): void;
|
|
33
|
+
/** KC2 §2: the redirect gesture (Alt+Enter / Ctrl+Enter) — the
|
|
34
|
+
* buffer's text arrives as a line at the same instant the run is told
|
|
35
|
+
* to stop. OPTIONAL: the pipe path has no raw keys and never wires
|
|
36
|
+
* it, so readline stays exactly as it was. */
|
|
37
|
+
onRedirect?(cb: (line: string) => void): void;
|
|
33
38
|
question(query: string, cb: (answer: string) => void): void;
|
|
34
39
|
cancelQuestion(): void;
|
|
35
40
|
/** W21: open the approval panel — the editor's state machine takes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-code",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "kiso CLI — the durable coding agent that survives kill -9: kiso chat / kiso resume / kiso sessions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,18 +18,18 @@
|
|
|
18
18
|
"test": "vitest run"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@vincemakes/kiso-core": "0.
|
|
22
|
-
"@vincemakes/kiso-evals": "0.
|
|
23
|
-
"@vincemakes/kiso-mcp-ext": "0.
|
|
24
|
-
"@vincemakes/kiso-provider-anthropic": "0.
|
|
25
|
-
"@vincemakes/kiso-provider-openai": "0.
|
|
26
|
-
"@vincemakes/kiso-runtime": "0.
|
|
27
|
-
"@vincemakes/kiso-skills-ext": "0.
|
|
28
|
-
"@vincemakes/kiso-subagent-ext": "0.
|
|
29
|
-
"@vincemakes/kiso-task-ext": "0.
|
|
30
|
-
"@vincemakes/kiso-tools-node": "0.
|
|
31
|
-
"@vincemakes/kiso-tui": "0.
|
|
32
|
-
"@vincemakes/kiso-tui-cells": "0.
|
|
21
|
+
"@vincemakes/kiso-core": "0.6.0",
|
|
22
|
+
"@vincemakes/kiso-evals": "0.6.0",
|
|
23
|
+
"@vincemakes/kiso-mcp-ext": "0.6.0",
|
|
24
|
+
"@vincemakes/kiso-provider-anthropic": "0.6.0",
|
|
25
|
+
"@vincemakes/kiso-provider-openai": "0.6.0",
|
|
26
|
+
"@vincemakes/kiso-runtime": "0.6.0",
|
|
27
|
+
"@vincemakes/kiso-skills-ext": "0.6.0",
|
|
28
|
+
"@vincemakes/kiso-subagent-ext": "0.6.0",
|
|
29
|
+
"@vincemakes/kiso-task-ext": "0.6.0",
|
|
30
|
+
"@vincemakes/kiso-tools-node": "0.6.0",
|
|
31
|
+
"@vincemakes/kiso-tui": "0.6.0",
|
|
32
|
+
"@vincemakes/kiso-tui-cells": "0.6.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/node": "^26.1.2",
|