@vincemakes/kiso-code 0.5.0 → 0.7.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,11 @@ 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
+ // KC3: and so does the @ picker — the row is where a gesture is
34
+ // taught, and the row costs nothing.
35
+ 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 · @ files")}`);
33
36
  ctx.input.prompt();
34
37
  });
35
38
  return;
package/dist/index.js CHANGED
@@ -26,14 +26,14 @@ import { readFileSync, realpathSync, rmSync } from "node:fs";
26
26
  import { createInterface } from "node:readline";
27
27
  import { fileURLToPath } from "node:url";
28
28
  import { join } from "node:path";
29
- import { Body, Editor, bannerLines, escapeTerminal, palette, renderSessionLine } from "@vincemakes/kiso-tui";
29
+ import { Body, Editor, bannerLines, escapeTerminal, interactivePrompt, palette, renderSessionLine } from "@vincemakes/kiso-tui";
30
30
  import { createAgent, disposeExtensions, loadExtensions, loadProjectExtensions, SessionStore, } from "@vincemakes/kiso-runtime";
31
31
  import { createFauxProvider } from "@vincemakes/kiso-evals";
32
32
  import { createCodingTools } from "@vincemakes/kiso-tools-node";
33
33
  import { MODES, modeExtensions, modeFromEnv, modeSystemPrompt, setMode } from "./mode.js";
34
34
  import { builtInLayer } from "./builtin.js";
35
- import { body, bodyLog, builtInExtensions, currentFaux, dock, extensionsDir, loadedExtensions, mergedConfig, mergedTempPaths, projectExtensions, sessionsDir, setAgentModel, setBody, setConfigModels, setConfiguredWindow, setCurrentAgentExtensions, setCurrentFaux, setCurrentModelName, setExtensionLists, setMergedConfig, userExtensions, VERSION } from "./state.js";
36
- import { interactivePrompt, resolveProjectTrust } from "./trust-ui.js";
35
+ import { atFiles, body, bodyLog, builtInExtensions, currentFaux, dock, extensionsDir, loadedExtensions, mergedConfig, mergedTempPaths, projectExtensions, sessionsDir, setAgentModel, setBody, setConfigModels, setConfiguredWindow, setCurrentAgentExtensions, setCurrentFaux, setCurrentModelName, setExtensionLists, setMergedConfig, userExtensions, VERSION } from "./state.js";
36
+ import { resolveProjectTrust } from "./trust-ui.js";
37
37
  import { isFirstRun, scaffoldFirstRun } from "./first-run.js";
38
38
  import { fauxSkip, readFauxScript } from "./faux-glue.js";
39
39
  import { autoCompactFromEnv, chat, contextWindowTokens } from "./chat.js";
@@ -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
  },
@@ -191,6 +196,8 @@ function makeLineInput() {
191
196
  // pipe bytes do not change)
192
197
  dock.bindInput(() => editor.dockState(), "› ");
193
198
  dock.bindMenu(() => editor.menuState()); // v3 §04: the slash-command menu
199
+ editor.bindAtItems(atFiles); // KC3 §5: the file source — listed per OPEN
200
+ dock.bindAt(() => editor.atState()); // KC3 §4: the picker's band
194
201
  dock.bindApproval(() => editor.panelState()); // W21: the panel's bound state
195
202
  return editorInput(editor);
196
203
  }
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
@@ -5,7 +5,7 @@
5
5
  * creates the mutable ones (setBody / setAgentModel / setExtensionLists);
6
6
  * the moved modules read and mutate at call time.
7
7
  */
8
- import { Dock, type Body, type PanelVerdict, type PanelView } from "@vincemakes/kiso-tui";
8
+ import { Dock, type AtItem, type Body, type PanelVerdict, type PanelView } from "@vincemakes/kiso-tui";
9
9
  import type { KisoExtension } from "@vincemakes/kiso-runtime";
10
10
  /** finding #11: KISO_HOME is the ONE root — every default path derives from
11
11
  * it (sessions, trust, extensions, mcp config, skills). The dedicated
@@ -15,6 +15,30 @@ export declare function kisoHome(): string;
15
15
  export declare function sessionsDir(): string;
16
16
  /** E1: the extension scan directory — KISO_EXTENSIONS_DIR overrides. */
17
17
  export declare function extensionsDir(): string;
18
+ /**
19
+ * KC3 §5 — the @ picker's file source. Computed PER OPEN: no index, no
20
+ * daemon, no watcher, nothing to invalidate and nothing to go stale.
21
+ * (The editor snapshots the result for the life of one open, so this
22
+ * runs once per `@`, not once per keystroke.)
23
+ *
24
+ * In a git repo, git IS the answer. `ls-files -c -o --exclude-standard`
25
+ * is the tracked files AND the untracked ones that are not ignored, in
26
+ * ONE process: the same set the user's own tooling calls "the project",
27
+ * with every .gitignore in the tree already honoured — and honoured by
28
+ * git rather than by a reimplementation of git's rules. stderr is
29
+ * discarded because "not a git repository" must never land in a live
30
+ * TUI frame.
31
+ *
32
+ * Outside a repo — or with no git on PATH — the bounded walk stands
33
+ * in: it prunes AT_SKIP before descending and stops at AT_CAP + 1
34
+ * entries, the extra entry being what lets the panel's counter SAY it
35
+ * truncated instead of showing the first two thousand files as though
36
+ * they were all of them.
37
+ *
38
+ * Paths are forward-slashed on both branches (git's own format), so
39
+ * the fuzzy filter sees one alphabet whichever branch ran.
40
+ */
41
+ export declare function atFiles(): readonly AtItem[];
18
42
  /**
19
43
  * v2c — the interactive input source. TTYs use the raw-mode Editor (the
20
44
  * self-drawn input row — width-aware, the CJK-drift root cause retired,
@@ -30,6 +54,11 @@ export interface LineInput {
30
54
  /** W15: the expand key (ctrl+r) — the chain-level action, never the
31
55
  * editor's own interpretation. */
32
56
  onExpand(cb: () => void): void;
57
+ /** KC2 §2: the redirect gesture (Alt+Enter / Ctrl+Enter) — the
58
+ * buffer's text arrives as a line at the same instant the run is told
59
+ * to stop. OPTIONAL: the pipe path has no raw keys and never wires
60
+ * it, so readline stays exactly as it was. */
61
+ onRedirect?(cb: (line: string) => void): void;
33
62
  question(query: string, cb: (answer: string) => void): void;
34
63
  cancelQuestion(): void;
35
64
  /** W21: open the approval panel — the editor's state machine takes
package/dist/state.js CHANGED
@@ -5,11 +5,12 @@
5
5
  * creates the mutable ones (setBody / setAgentModel / setExtensionLists);
6
6
  * the moved modules read and mutate at call time.
7
7
  */
8
- import { readFileSync } from "node:fs";
8
+ import { execFileSync } from "node:child_process";
9
+ import { readdirSync, readFileSync } from "node:fs";
9
10
  import { homedir } from "node:os";
10
11
  import { dirname, join } from "node:path";
11
12
  import { fileURLToPath } from "node:url";
12
- import { Dock } from "@vincemakes/kiso-tui";
13
+ import { AT_CAP, AT_SKIP, Dock } from "@vincemakes/kiso-tui";
13
14
  /** finding #11: KISO_HOME is the ONE root — every default path derives from
14
15
  * it (sessions, trust, extensions, mcp config, skills). The dedicated
15
16
  * env vars (KISO_EXTENSIONS_DIR / KISO_MCP_CONFIG / KISO_SKILLS_DIR)
@@ -24,6 +25,62 @@ export function sessionsDir() {
24
25
  export function extensionsDir() {
25
26
  return process.env.KISO_EXTENSIONS_DIR ?? join(kisoHome(), "extensions");
26
27
  }
28
+ /**
29
+ * KC3 §5 — the @ picker's file source. Computed PER OPEN: no index, no
30
+ * daemon, no watcher, nothing to invalidate and nothing to go stale.
31
+ * (The editor snapshots the result for the life of one open, so this
32
+ * runs once per `@`, not once per keystroke.)
33
+ *
34
+ * In a git repo, git IS the answer. `ls-files -c -o --exclude-standard`
35
+ * is the tracked files AND the untracked ones that are not ignored, in
36
+ * ONE process: the same set the user's own tooling calls "the project",
37
+ * with every .gitignore in the tree already honoured — and honoured by
38
+ * git rather than by a reimplementation of git's rules. stderr is
39
+ * discarded because "not a git repository" must never land in a live
40
+ * TUI frame.
41
+ *
42
+ * Outside a repo — or with no git on PATH — the bounded walk stands
43
+ * in: it prunes AT_SKIP before descending and stops at AT_CAP + 1
44
+ * entries, the extra entry being what lets the panel's counter SAY it
45
+ * truncated instead of showing the first two thousand files as though
46
+ * they were all of them.
47
+ *
48
+ * Paths are forward-slashed on both branches (git's own format), so
49
+ * the fuzzy filter sees one alphabet whichever branch ran.
50
+ */
51
+ export function atFiles() {
52
+ let paths;
53
+ try {
54
+ paths = execFileSync("git", ["ls-files", "-c", "-o", "--exclude-standard"], { cwd: process.cwd(), encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], maxBuffer: 1 << 24 }).split("\n").filter((p) => p !== "");
55
+ }
56
+ catch {
57
+ paths = atWalk(process.cwd(), "", []);
58
+ }
59
+ return paths.slice(0, AT_CAP + 1).map((path) => ({ path }));
60
+ }
61
+ /** The fallback walk. `prefix` carries the repo-relative directory so
62
+ * the result needs no path arithmetic afterwards. An unreadable
63
+ * directory contributes nothing and ends nothing — the recursion's own
64
+ * try catches it at that level, so one locked subtree cannot stop a
65
+ * file picker from opening. */
66
+ function atWalk(dir, prefix, out) {
67
+ try {
68
+ for (const e of readdirSync(dir, { withFileTypes: true })) {
69
+ if (out.length > AT_CAP)
70
+ break;
71
+ if (AT_SKIP.has(e.name))
72
+ continue;
73
+ if (e.isDirectory())
74
+ atWalk(`${dir}/${e.name}`, `${prefix}${e.name}/`, out);
75
+ else if (e.isFile())
76
+ out.push(`${prefix}${e.name}`);
77
+ }
78
+ }
79
+ catch {
80
+ // unreadable — skipped
81
+ }
82
+ return out;
83
+ }
27
84
  /** v2b: the bottom-anchored UI — docked only on a color TTY; pipes and
28
85
  * NO_COLOR stay the v2a line mode byte-for-byte. Created at load, like
29
86
  * the pre-split module-scope const. */
@@ -9,11 +9,6 @@ import { type PanelVerdict, type PanelView } from "@vincemakes/kiso-tui";
9
9
  import { type ProjectArtifacts } from "@vincemakes/kiso-runtime";
10
10
  import type { AgentSession } from "@vincemakes/kiso-runtime";
11
11
  import { type LineInput } from "./state.js";
12
- /** v2a: the interactive prompt — blue, the identity accent. readline owns
13
- * the echo of what the user types; we own the prompt's color. (v2c: the
14
- * readline prompt keeps "you> " — the brick ▌ is the dock's row only;
15
- * pipe bytes must not change.) */
16
- export declare function interactivePrompt(): string;
17
12
  /**
18
13
  * W21 — ask the human with the approval panel: the bounded block that
19
14
  * replaces the running tool's live window while the approval is pending.
package/dist/trust-ui.js CHANGED
@@ -9,19 +9,11 @@ import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, symlinkS
9
9
  import { homedir, tmpdir } from "node:os";
10
10
  import { join } from "node:path";
11
11
  import { pathToFileURL } from "node:url";
12
- import { escapeTerminal, palette } from "@vincemakes/kiso-tui";
12
+ import { projectTrustRows, projectTrustView, projectUntrustedNote, uncertainView } from "@vincemakes/kiso-tui";
13
13
  import { projectArtifacts, recordTrust, trustFor } from "@vincemakes/kiso-runtime";
14
14
  import { bodyLog, currentAgentExtensions, dock, extensionsDir, kisoHome, mergedTempPaths } from "./state.js";
15
15
  import { loadUserConfig, resolveProjectTrustPolicy } from "./config.js";
16
16
  import { getMode } from "./mode.js";
17
- /** v2a: the interactive prompt — blue, the identity accent. readline owns
18
- * the echo of what the user types; we own the prompt's color. (v2c: the
19
- * readline prompt keeps "you> " — the brick ▌ is the dock's row only;
20
- * pipe bytes must not change.) */
21
- export function interactivePrompt() {
22
- const p = palette();
23
- return `${p.bold}you> ${p.reset}`;
24
- }
25
17
  /**
26
18
  * W21 — ask the human with the approval panel: the bounded block that
27
19
  * replaces the running tool's live window while the approval is pending.
@@ -192,7 +184,7 @@ export async function resolveProjectTrust(input) {
192
184
  // First discovery — list every artifact (file name + digest short
193
185
  // prefix) and ask the human ONCE.
194
186
  if (!process.stdin.isTTY) {
195
- console.error(`[project .kiso] found ${artifacts.files.length} artifact(s) in ${artifacts.root} — not trusted, not loaded (run kiso interactively once to decide)`);
187
+ console.error(projectUntrustedNote(artifacts.files.length, artifacts.root));
196
188
  return null;
197
189
  }
198
190
  // v2c: the shared input (the editor on a TTY) reads the answer.
@@ -201,19 +193,9 @@ export async function resolveProjectTrust(input) {
201
193
  // the bodyLog below records, verbatim — the listing still lands in
202
194
  // the scrollback; the panel is a bounded block, the record is not).
203
195
  bodyLog(`[project .kiso] ${artifacts.root}`);
204
- for (const f of artifacts.files) {
205
- bodyLog(` ${f.path} (${f.digest.slice(0, 6)})`);
206
- }
207
- const verdict = await askPanel(input, {
208
- flavor: "simple",
209
- name: "project trust",
210
- title: artifacts.root,
211
- speaker: "kiso",
212
- statusText: "▸ project trust",
213
- args: { kind: "text", lines: artifacts.files.map((f) => `${f.path} (${f.digest.slice(0, 6)})`) },
214
- ruleOverride: "trust this project's .kiso?",
215
- fallbackQuestion: `trust this project's .kiso? (y/n) `,
216
- });
196
+ for (const row of projectTrustRows(artifacts.files, " "))
197
+ bodyLog(row);
198
+ const verdict = await askPanel(input, projectTrustView(artifacts.root, artifacts.files));
217
199
  // A cancel is a "no" HERE — refused is sticky, the project does not
218
200
  // load (re-evaluate by deleting the trust line or changing a file).
219
201
  // The non-TTY branch above returned WITHOUT a record so an interactive
@@ -317,16 +299,7 @@ function readdirSyncSafe(dir) {
317
299
  * the body is the single stdout writer, never a stray console.log. */
318
300
  export async function resolveUncertains(session, input, isCancelled) {
319
301
  for (const uncertain of session.uncertainExecutions()) {
320
- const verdict = await askPanel(input, {
321
- flavor: "simple",
322
- name: "uncertain execution",
323
- title: `${uncertain.name} (${uncertain.executionId})`,
324
- speaker: "kiso",
325
- statusText: "▸ uncertain execution",
326
- args: { kind: "text", lines: [uncertain.executionId] },
327
- ruleOverride: "did the interrupted execution apply? — 1 rerun · 3 abandon",
328
- fallbackQuestion: `⚠ interrupted execution: ${escapeTerminal(uncertain.name)} (${uncertain.executionId}) — did it apply? (y)es / (n)o `,
329
- });
302
+ const verdict = await askPanel(input, uncertainView(uncertain.name, uncertain.executionId));
330
303
  if (isCancelled() || verdict.action === "cancel") {
331
304
  // round 10: a cancellation NEVER records a verdict — the execution
332
305
  // stays uncertain and durable; no rerun/abandoned is fabricated.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-code",
3
- "version": "0.5.0",
3
+ "version": "0.7.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.7.0",
22
+ "@vincemakes/kiso-evals": "0.7.0",
23
+ "@vincemakes/kiso-mcp-ext": "0.7.0",
24
+ "@vincemakes/kiso-provider-anthropic": "0.7.0",
25
+ "@vincemakes/kiso-provider-openai": "0.7.0",
26
+ "@vincemakes/kiso-runtime": "0.7.0",
27
+ "@vincemakes/kiso-skills-ext": "0.7.0",
28
+ "@vincemakes/kiso-subagent-ext": "0.7.0",
29
+ "@vincemakes/kiso-task-ext": "0.7.0",
30
+ "@vincemakes/kiso-tools-node": "0.7.0",
31
+ "@vincemakes/kiso-tui": "0.7.0",
32
+ "@vincemakes/kiso-tui-cells": "0.7.0"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/node": "^26.1.2",