@vincemakes/kiso-code 0.7.0 → 0.8.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/builtin.d.ts CHANGED
@@ -1,2 +1,8 @@
1
+ import { type AskUI } from "@vincemakes/kiso-ask-ext";
1
2
  import type { KisoExtension } from "@vincemakes/kiso-runtime";
2
- export declare function builtInLayer(user: readonly KisoExtension[], project: readonly KisoExtension[]): Promise<readonly KisoExtension[]>;
3
+ export declare function builtInLayer(user: readonly KisoExtension[], project: readonly KisoExtension[],
4
+ /** KC3.5: built-in #4 — the ask extension, loaded ONLY when a panel
5
+ * bridge exists (an interactive TTY). No bridge, no fourth built-in:
6
+ * a headless session never pays the rent for a question nobody could
7
+ * answer, and its tool table cannot mention ask_user. */
8
+ ask?: AskUI): Promise<readonly KisoExtension[]>;
package/dist/builtin.js CHANGED
@@ -24,8 +24,14 @@
24
24
  import createMcp from "@vincemakes/kiso-mcp-ext";
25
25
  import createSkills from "@vincemakes/kiso-skills-ext";
26
26
  import createSubagent from "@vincemakes/kiso-subagent-ext";
27
- export async function builtInLayer(user, project) {
28
- const all = await Promise.all([createMcp(), createSkills(), createSubagent()]);
27
+ import createAsk, {} from "@vincemakes/kiso-ask-ext";
28
+ export async function builtInLayer(user, project,
29
+ /** KC3.5: built-in #4 — the ask extension, loaded ONLY when a panel
30
+ * bridge exists (an interactive TTY). No bridge, no fourth built-in:
31
+ * a headless session never pays the rent for a question nobody could
32
+ * answer, and its tool table cannot mention ask_user. */
33
+ ask) {
34
+ const all = await Promise.all([createMcp(), createSkills(), createSubagent(), ...(ask === undefined ? [] : [createAsk(ask)])]);
29
35
  const shadowed = all.filter((b) => user.some((u) => u.name === b.name));
30
36
  for (const s of shadowed) {
31
37
  console.error(`[extensions] user extension "${s.name}" shadows the built-in — the built-in is not loaded`);
package/dist/dispatch.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * turns. The bodies moved verbatim from chat()'s closure; chat provides
4
4
  * the context (the chain, the run state, the prompt arming).
5
5
  */
6
- import { escapeTerminal, kUnit, palette } from "@vincemakes/kiso-tui";
6
+ import { escapeTerminal, helpRows, kUnit, palette } from "@vincemakes/kiso-tui";
7
7
  import { buildAdapter } from "@vincemakes/kiso-runtime/internal";
8
8
  import { MODES, getMode, setMode } from "./mode.js";
9
9
  import { agentModel, body, bodyLog, configModels, dock, setAgentModel, setCurrentModelName } from "./state.js";
@@ -15,24 +15,16 @@ import { directWriteProfile, profileAvailable } from "./config.js";
15
15
  export function dispatch(line, ctx) {
16
16
  const trimmed = line.trim();
17
17
  if (trimmed === "/help") {
18
- // Prints the available commands with one-line descriptions.
19
- // v2a: the command names are the blue identity accent.
20
- const p = palette();
21
- const cmd = (name, desc) => `${p.bold}${name}${p.reset} ${desc}`;
18
+ // KC3.5 slice ⓪ (the extraction): the ROWS moved to the terminal
19
+ // layer's strings module (helpRows the KC3 §1 pattern: what the
20
+ // human reads is presentation). The FLOW is what stays here, and
21
+ // it is unchanged: print on the chain, then re-prompt. The rows
22
+ // are byte-identical to the eight bodyLog calls they replace —
23
+ // the last one still carries its own \n, so `exit` and `keys`
24
+ // land as two rows from one call (bodyLog splits on \n).
22
25
  ctx.chainRef.current = ctx.chainRef.current.then(async () => {
23
- bodyLog(cmd("/help", "print this list of commands"));
24
- bodyLog(cmd("/think", "show the last full thinking block"));
25
- bodyLog(cmd("/last", "show the most recent tool call's input and output"));
26
- bodyLog(cmd("/status", "show session id, event count, and context estimate"));
27
- bodyLog(cmd("/mode", "show the approval tier; /mode <name> switches (manual/default/accept-edits/plan/bypass)"));
28
- bodyLog(cmd("/model", "list model profiles; /model <name|provider/model> switches"));
29
- bodyLog(cmd("/compact", "summarize the older conversation to free context"));
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
- // 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")}`);
26
+ for (const row of helpRows())
27
+ bodyLog(row);
36
28
  ctx.input.prompt();
37
29
  });
38
30
  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, interactivePrompt, palette, renderSessionLine } from "@vincemakes/kiso-tui";
29
+ import { Body, Editor, bannerLines, escapeTerminal, extensionsBannerText, 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
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";
36
+ import { askUi, 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";
@@ -209,20 +209,11 @@ function makeLineInput() {
209
209
  * a fresh install reads `[3 extensions: built-in: mcp, skills, subagent]`
210
210
  * with zero disk setup (E5: the task extension is opt-in). */
211
211
  function bannerExtensionText() {
212
- const total = builtInExtensions.length + userExtensions.length + projectExtensions.length;
213
- if (total === 0)
214
- return "";
215
- const parts = [];
216
- // 0.1.26 (MCP lazy connection): an extension with a live `connecting` flag shows
217
- // its in-flight state in the banner — "mcp (connecting…)".
218
- const label = (e) => e.connecting === true ? `${e.name} (connecting…)` : e.name;
219
- if (builtInExtensions.length > 0)
220
- parts.push(`built-in: ${builtInExtensions.map(label).join(", ")}`);
221
- if (userExtensions.length > 0)
222
- parts.push(userExtensions.map(label).join(", "));
223
- if (projectExtensions.length > 0)
224
- parts.push(`project: ${projectExtensions.map(label).join(", ")}`);
225
- return ` · [${total} extension${total === 1 ? "" : "s"}: ${parts.join(" · ")}]`;
212
+ // KC3.5 slice (the extraction): the composition moved to the
213
+ // terminal layer (extensionsBannerText a pure function of the three
214
+ // name lists, including the "(connecting…)" in-flight label). Which
215
+ // lists exist is the CLI's fact and stays here.
216
+ return extensionsBannerText(builtInExtensions, userExtensions, projectExtensions);
226
217
  }
227
218
  /** E1: the startup banner line(s) — TTY: logo + merged extensions + the
228
219
  * W5 resume list as a LIVE banner cell (W1: the tier re-derives on
@@ -374,7 +365,11 @@ async function makeAgent(sessionId, input, modelFlag) {
374
365
  const proj = project !== null ? await loadProjectExtensions(process.cwd(), user) : [];
375
366
  // R-D 0.1.45: the built-in layer registers by module import (builtin.ts)
376
367
  // — a user extension may shadow a built-in, a project one may not.
377
- const builtIn = await builtInLayer(user, proj);
368
+ // KC3.5: built-in #4 (ask) registers ONLY where a human can answer —
369
+ // the panel bridge is the argument, and a non-TTY session has none to
370
+ // give. A piped run's composed tool table therefore cannot contain
371
+ // ask_user (T-Q3: the bench's structural byte-identity proof).
372
+ const builtIn = await builtInLayer(user, proj, input !== undefined && process.stdin.isTTY ? askUi(input) : undefined);
378
373
  setExtensionLists(builtIn, user, proj, [...builtIn, ...user, ...proj]);
379
374
  // merge round B — the config surface: user config + (trusted) project config,
380
375
  // resolved with flags > env > project > user > default. The CLI never
@@ -6,6 +6,7 @@
6
6
  * decisions. All bodies moved verbatim from index.ts.
7
7
  */
8
8
  import { type PanelVerdict, type PanelView } from "@vincemakes/kiso-tui";
9
+ import type { AskUI } from "@vincemakes/kiso-ask-ext";
9
10
  import { type ProjectArtifacts } from "@vincemakes/kiso-runtime";
10
11
  import type { AgentSession } from "@vincemakes/kiso-runtime";
11
12
  import { type LineInput } from "./state.js";
@@ -34,6 +35,24 @@ import { type LineInput } from "./state.js";
34
35
  */
35
36
  export declare let pendingAsk: (() => void) | null;
36
37
  export declare function askPanel(input: LineInput, view: PanelView): Promise<PanelVerdict>;
38
+ /**
39
+ * KC3.5 — the AskUI bridge: the panel the ask extension asks through.
40
+ *
41
+ * It is deliberately three lines of GLUE. Everything an ask needs was
42
+ * already built for W21 and is reused whole: askPanel owns the
43
+ * abortable ask (the SIGINT/esc path resolves it), the non-interactive
44
+ * refusal (printed loudly, never hung) and the dock-less fallback; the
45
+ * editor owns the keys and the buffer stash; the tui owns the rows and
46
+ * the walk. What is left here is the mapping — the panel's `answers`
47
+ * verdict IS the tool's result, and EVERY other verdict is the decline,
48
+ * which is an honest recorded outcome naming what went unanswered.
49
+ *
50
+ * (A cancel, a non-interactive deny and a dock-less y/n all land in the
51
+ * same place on purpose: none of them is an answer to a multiple-choice
52
+ * question, and inventing one would be the dishonesty the whole round
53
+ * is built to avoid.)
54
+ */
55
+ export declare function askUi(input: LineInput): AskUI;
37
56
  /**
38
57
  * W21/R3 — the "2 Yes, don't ask again for <tool>" rule: a GENERATED
39
58
  * extension, ALLOW-ONLY by construction (it never emits deny or ask —
package/dist/trust-ui.js CHANGED
@@ -9,7 +9,7 @@ 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 { projectTrustRows, projectTrustView, projectUntrustedNote, uncertainView } from "@vincemakes/kiso-tui";
12
+ import { askDeclineAll, askView, projectTrustRows, projectTrustView, projectUntrustedNote, uncertainView, unansweredAskView } 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";
@@ -83,6 +83,31 @@ export function askPanel(input, view) {
83
83
  }
84
84
  });
85
85
  }
86
+ /**
87
+ * KC3.5 — the AskUI bridge: the panel the ask extension asks through.
88
+ *
89
+ * It is deliberately three lines of GLUE. Everything an ask needs was
90
+ * already built for W21 and is reused whole: askPanel owns the
91
+ * abortable ask (the SIGINT/esc path resolves it), the non-interactive
92
+ * refusal (printed loudly, never hung) and the dock-less fallback; the
93
+ * editor owns the keys and the buffer stash; the tui owns the rows and
94
+ * the walk. What is left here is the mapping — the panel's `answers`
95
+ * verdict IS the tool's result, and EVERY other verdict is the decline,
96
+ * which is an honest recorded outcome naming what went unanswered.
97
+ *
98
+ * (A cancel, a non-interactive deny and a dock-less y/n all land in the
99
+ * same place on purpose: none of them is an answer to a multiple-choice
100
+ * question, and inventing one would be the dishonesty the whole round
101
+ * is built to avoid.)
102
+ */
103
+ export function askUi(input) {
104
+ return {
105
+ ask: async (spec) => {
106
+ const verdict = await askPanel(input, askView(spec));
107
+ return verdict.action === "answers" ? verdict.result : askDeclineAll(spec);
108
+ },
109
+ };
110
+ }
86
111
  /**
87
112
  * W21/R3 — the "2 Yes, don't ask again for <tool>" rule: a GENERATED
88
113
  * extension, ALLOW-ONLY by construction (it never emits deny or ask —
@@ -299,7 +324,12 @@ function readdirSyncSafe(dir) {
299
324
  * the body is the single stdout writer, never a stray console.log. */
300
325
  export async function resolveUncertains(session, input, isCancelled) {
301
326
  for (const uncertain of session.uncertainExecutions()) {
302
- const verdict = await askPanel(input, uncertainView(uncertain.name, uncertain.executionId));
327
+ // KC3.5 §4: an interrupted ask_user is not a side effect that may
328
+ // have applied — it is a question nobody answered. The COPY says
329
+ // so; the mechanism is untouched (allow → the runtime's own rerun
330
+ // resolution, whose error-fill text this round never edits).
331
+ const view = uncertain.name === "ask_user" ? unansweredAskView(uncertain.executionId) : uncertainView(uncertain.name, uncertain.executionId);
332
+ const verdict = await askPanel(input, view);
303
333
  if (isCancelled() || verdict.action === "cancel") {
304
334
  // round 10: a cancellation NEVER records a verdict — the execution
305
335
  // 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.7.0",
3
+ "version": "0.8.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,19 @@
18
18
  "test": "vitest run"
19
19
  },
20
20
  "dependencies": {
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"
21
+ "@vincemakes/kiso-ask-ext": "0.8.0",
22
+ "@vincemakes/kiso-core": "0.8.0",
23
+ "@vincemakes/kiso-evals": "0.8.0",
24
+ "@vincemakes/kiso-mcp-ext": "0.8.0",
25
+ "@vincemakes/kiso-provider-anthropic": "0.8.0",
26
+ "@vincemakes/kiso-provider-openai": "0.8.0",
27
+ "@vincemakes/kiso-runtime": "0.8.0",
28
+ "@vincemakes/kiso-skills-ext": "0.8.0",
29
+ "@vincemakes/kiso-subagent-ext": "0.8.0",
30
+ "@vincemakes/kiso-task-ext": "0.8.0",
31
+ "@vincemakes/kiso-tools-node": "0.8.0",
32
+ "@vincemakes/kiso-tui": "0.8.0",
33
+ "@vincemakes/kiso-tui-cells": "0.8.0"
33
34
  },
34
35
  "devDependencies": {
35
36
  "@types/node": "^26.1.2",