@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 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, kUnit, palette, renderEvent, renderRecap, toolTarget, } from "@vincemakes/kiso-tui";
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
- const GLYPHS = ["▖", "▘", "▝", "▗"];
102
+ // KC2 §5: the family itself moved to the tui's status formatters.
103
103
  let i = 0;
104
- const timer = setInterval(() => onTick(GLYPHS[i++ % GLYPHS.length]), 200);
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 (!dock.active)
622
- return;
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 (!dock.active)
630
- return;
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
- return turn(line);
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
- bodyLog(`${cmd("exit", "leave the session")}\n${cmd("keys", "enter sends · ctrl+J inserts a newline (shift+enter where the terminal encodes it)")}`);
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 { kUnit } from "@vincemakes/kiso-tui";
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 (!dock.active)
29
- return;
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 (!dock.active)
36
- return;
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.5.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.5.0",
22
- "@vincemakes/kiso-evals": "0.5.0",
23
- "@vincemakes/kiso-mcp-ext": "0.5.0",
24
- "@vincemakes/kiso-provider-anthropic": "0.5.0",
25
- "@vincemakes/kiso-provider-openai": "0.5.0",
26
- "@vincemakes/kiso-runtime": "0.5.0",
27
- "@vincemakes/kiso-skills-ext": "0.5.0",
28
- "@vincemakes/kiso-subagent-ext": "0.5.0",
29
- "@vincemakes/kiso-task-ext": "0.5.0",
30
- "@vincemakes/kiso-tools-node": "0.5.0",
31
- "@vincemakes/kiso-tui": "0.5.0",
32
- "@vincemakes/kiso-tui-cells": "0.5.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",