@vincemakes/kiso-tui 0.6.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/editor.js CHANGED
@@ -26,7 +26,10 @@ import { charWidth, displayWidth, leadWidth, widthOf } from "./width.js";
26
26
  // authority) — re-exported so the editor's public surface is unchanged.
27
27
  export { charWidth, displayWidth, widthOf };
28
28
  import { palette } from "./render.js";
29
- import { panelLead } from "./approval-panel.js";
29
+ // KC3.5: the panel-slot dispatchers — the ask branch folded into the
30
+ // W21 lead/rows, so this file keeps ONE panel and one key owner.
31
+ import { askCommitCustom, askKey, askStart, panelLead } from "./ask-panel.js";
32
+ import { AT_VISIBLE, atFilter } from "./at-picker.js";
30
33
  // TUI v4 #16d: the input row is the blue brick + the edit area — the
31
34
  // "you>" text is gone (the brick IS the prompt; the pipe path's readline
32
35
  // prompt keeps its own "you> " — v2a line mode, byte-for-byte).
@@ -44,6 +47,13 @@ export const MENU_ITEMS = [
44
47
  /** KC1 §3 — the newline code point. Every source (paste, Ctrl+J, the
45
48
  * Shift+Enter encodings, a CRLF pair) normalizes to exactly ONE. */
46
49
  const NEWLINE = 0x0a;
50
+ /** KC3 §3 — the picker's sigil, and the two characters that count as a
51
+ * word boundary before it. A `@` anywhere else (vince@example.com) is
52
+ * an ordinary character: the reference is a thing you START, not a
53
+ * thing an address accidentally becomes. */
54
+ const AT = 0x40;
55
+ const SPACE = 0x20;
56
+ const TAB = 0x09;
47
57
  /** KC1 §5 — the composer's CEILING (adjudication A1): at most 6 visible
48
58
  * rows. A ceiling only — N_visible clamps by the terminal's height so
49
59
  * the geometry stays legal down to the compositor's enter gate (H = 4
@@ -101,6 +111,22 @@ export class Editor {
101
111
  #onRender;
102
112
  #menuOpen = false; // v3 §04: the slash-command menu
103
113
  #menuSel = 0;
114
+ // KC3 §3 — the @ file picker. THREE fields and no more: the armed
115
+ // bit, the selection, and the per-open SNAPSHOT of the file list.
116
+ // The query is deliberately NOT stored — it is derived from the
117
+ // buffer and the cursor on every read (the KC1 flat-buffer
118
+ // discipline: never a second mutable model). That is what makes
119
+ // backspacing past the `@` close the picker with no handler
120
+ // anywhere, and what keeps every existing op — the kills, paste,
121
+ // the history stash, the queue-pop replace — correct for free.
122
+ #atOpen = false;
123
+ #atSel = 0;
124
+ // the list is snapshotted AT OPEN and held for that open's lifetime
125
+ // (§4: no index, no watcher, no re-listing per keystroke). An armed
126
+ // bit with no token under the cursor is inert by construction — the
127
+ // next open re-snapshots, so a stale list can never be shown.
128
+ #atList = null;
129
+ #atItems = null;
104
130
  // A2 (the feel): the session-scoped input history — every submitted TURN
105
131
  // line (never a question answer), capped at 100, never persisted. ↑↓
106
132
  // navigate it ONLY from an empty input or while already browsing.
@@ -215,7 +241,7 @@ export class Editor {
215
241
  * the frame's clamp is the authority. */
216
242
  #visibleRows(lineCount) {
217
243
  const H = process.stdout.rows ?? 24;
218
- const bands = (this.#menuOpen ? this.#menuFiltered().length : 0) + this.#queueState().length;
244
+ const bands = (this.#menuOpen ? this.#menuFiltered().length : 0) + this.#atRows() + this.#queueState().length;
219
245
  return Math.max(1, Math.min(lineCount, N_MAX, Math.max(1, H - 3 - bands)));
220
246
  }
221
247
  /** The dock's input-row state — ADDITIVE (§5): `line` + `cursor` keep
@@ -276,6 +302,128 @@ export class Editor {
276
302
  this.#menuSel = 0;
277
303
  this.#onRender();
278
304
  }
305
+ /** KC3 §3 — bind the file source. The tui owns no file list and
306
+ * never touches a disk (input is data, output is bytes): the CLI
307
+ * feeds the paths, and until it does, the picker cannot open at
308
+ * all — which is exactly why every non-@ scenario and every
309
+ * consumer that does not bind (the recovery flow, the existing
310
+ * gates) is byte-identical. */
311
+ bindAtItems(source) {
312
+ this.#atItems = source;
313
+ }
314
+ /**
315
+ * KC3 §3 — the token under the cursor, DERIVED. Scans back from the
316
+ * cursor within the CURSOR'S LINE for the `@` that opens it:
317
+ * - whitespace before finding one → there is no token (the space
318
+ * ended it);
319
+ * - an `@` that is not itself at a word boundary → inert (the
320
+ * email case: the `@` of vince@example.com opens nothing);
321
+ * - otherwise the token runs from that `@` to the CURSOR — never
322
+ * to the end of the line, so `@ra|.js` narrows on "ra".
323
+ * Line-local: the start of any line of a multi-line composer is a
324
+ * boundary, exactly like the start of the buffer.
325
+ */
326
+ #atToken() {
327
+ const b = this.#cursorBounds();
328
+ for (let i = this.#cursor - 1; i >= b.start; i -= 1) {
329
+ const cp = this.#chars[i];
330
+ if (cp === SPACE || cp === TAB)
331
+ return null;
332
+ if (cp !== AT)
333
+ continue;
334
+ const before = i > b.start ? this.#chars[i - 1] : null;
335
+ if (before !== null && before !== SPACE && before !== TAB)
336
+ return null; // mid-word
337
+ return { start: i, query: String.fromCodePoint(...this.#chars.slice(i + 1, this.#cursor)) };
338
+ }
339
+ return null;
340
+ }
341
+ /** KC3 §3 — the picker's full state, or null when it is not up. Up
342
+ * requires ALL of: armed, nobody with higher precedence holding the
343
+ * keys, a live token under the cursor, and at least one match (the
344
+ * menu's precedent — a panel with nothing in it is noise, and the
345
+ * keys fall back to their ordinary meanings). */
346
+ #atView() {
347
+ if (!this.#atOpen || this.#atList === null)
348
+ return null;
349
+ if (this.#panel !== null || this.#menuOpen)
350
+ return null;
351
+ const token = this.#atToken();
352
+ if (token === null)
353
+ return null;
354
+ const { matches, capped } = atFilter(this.#atList, token.query);
355
+ if (matches.length === 0)
356
+ return null;
357
+ // the selection CLAMPS at read time rather than being corrected
358
+ // on every edit — narrowing the query can only ever shrink the
359
+ // list, and a clamp is the whole correction that needs
360
+ return { matches, selected: Math.min(this.#atSel, matches.length - 1), capped, start: token.start };
361
+ }
362
+ #atUp() {
363
+ return this.#atView() !== null;
364
+ }
365
+ /** KC3 §4 — the picker's visible state for the dock; null when
366
+ * closed. The compositor windows it and draws the counter. */
367
+ atState() {
368
+ const view = this.#atView();
369
+ if (view === null)
370
+ return null;
371
+ return { matches: view.matches, selected: view.selected, capped: view.capped };
372
+ }
373
+ /** KC3 §3 — arm the picker at a freshly typed `@`. The gate is the
374
+ * KC2 precedence pattern: the approval panel, the slash menu and a
375
+ * pending question each own the keys first. A paste is literal text
376
+ * (guarded by the caller). The history browse and the queue-pop
377
+ * walk are NOT re-tested here because typing has already ended them
378
+ * — #insert leaves both before a character ever lands. */
379
+ #atArm() {
380
+ if (this.#atItems === null)
381
+ return;
382
+ if (this.#panel !== null || this.#menuOpen || this.#questionCb !== null)
383
+ return;
384
+ if (this.#atToken() === null)
385
+ return; // not at a word boundary
386
+ this.#atOpen = true;
387
+ this.#atSel = 0;
388
+ this.#atList = this.#atItems(); // §5: listed per OPEN, never per keystroke
389
+ }
390
+ #atClose() {
391
+ this.#atOpen = false;
392
+ this.#atSel = 0;
393
+ this.#atList = null;
394
+ }
395
+ /**
396
+ * KC3 §3 — accept: the token becomes `@<path> `.
397
+ *
398
+ * The CANONICAL PATH and a trailing space, and nothing else — the
399
+ * file's CONTENT is never inserted. That is the whole product
400
+ * decision: the model is handed a reference it can choose to read,
401
+ * so an @ mention costs a path's worth of tokens instead of a
402
+ * file's, and the model's own read_file call is what pays for the
403
+ * bytes it actually needs.
404
+ *
405
+ * Only [token.start, cursor) is replaced, so text after the cursor
406
+ * survives and a multi-line buffer keeps every other line.
407
+ */
408
+ #atAccept() {
409
+ const view = this.#atView();
410
+ if (view === null)
411
+ return;
412
+ const insert = [...`@${view.matches[view.selected].path} `].map((ch) => ch.codePointAt(0));
413
+ this.#chars.splice(view.start, this.#cursor - view.start, ...insert);
414
+ this.#cursor = view.start + insert.length;
415
+ this.#atClose();
416
+ this.#reflow();
417
+ this.#onRender();
418
+ }
419
+ /** KC3 §4 — the picker's band height, the editor's honest estimate
420
+ * (the compositor re-applies the clamp against the frame's REAL
421
+ * folded rows, exactly as it does for the menu): the windowed rows
422
+ * plus the counter row. */
423
+ #atRows() {
424
+ const view = this.#atView();
425
+ return view === null ? 0 : Math.min(view.matches.length, AT_VISIBLE) + 1;
426
+ }
279
427
  /** One-shot question mode: the NEXT submit answers, not a turn. */
280
428
  question(_query, cb) {
281
429
  this.#questionCb = cb;
@@ -293,6 +441,7 @@ export class Editor {
293
441
  view,
294
442
  phase: "options",
295
443
  sel: 0,
444
+ ask: view.ask === undefined ? null : askStart(view.ask),
296
445
  amend: "yes",
297
446
  onCommit,
298
447
  stash: { chars: this.#chars, cursor: this.#cursor, scroll: this.#scroll },
@@ -304,6 +453,7 @@ export class Editor {
304
453
  this.#menuOpen = false;
305
454
  this.#menuSel = 0;
306
455
  this.#queuePopMode = false; // W22: the panel owns the keys while up
456
+ this.#atClose(); // KC3 §3: and the picker closes with everything else
307
457
  this.#onRender();
308
458
  }
309
459
  /** W21: cancel the panel — the SIGINT path's pair to beginPanel. */
@@ -316,7 +466,7 @@ export class Editor {
316
466
  const panel = this.#panel;
317
467
  if (panel === null)
318
468
  return null;
319
- return { view: panel.view, phase: panel.phase, sel: panel.sel };
469
+ return { view: panel.view, phase: panel.phase, sel: panel.sel, ...(panel.ask === null ? {} : { ask: panel.ask }) };
320
470
  }
321
471
  enter() {
322
472
  if (this.#entered)
@@ -346,7 +496,7 @@ export class Editor {
346
496
  // W21: the panel's lead owns the row while up (the brick returns
347
497
  // when the panel closes).
348
498
  const panel = this.#panel;
349
- const lead = panel !== null ? panelLead(panel.view, panel.phase, panel.sel) : `${p.bold}${PROMPT}${p.reset}`;
499
+ const lead = panel !== null ? panelLead(panel.view, panel.phase, panel.sel, panel.ask ?? undefined) : `${p.bold}${PROMPT}${p.reset}`;
350
500
  // W23: the ONE width authority — leadWidth(lead), the ANSI-stripped
351
501
  // visible width (the styled panel lead / the styled brick measure
352
502
  // the same as their plain text — a lead can never measure
@@ -373,6 +523,45 @@ export class Editor {
373
523
  // text); ctrl-c still rides the SIGINT handler (which
374
524
  // cancels the panel).
375
525
  const panel = this.#panel;
526
+ // KC3.5: an ASK panel routes its own keys — the digits pick
527
+ // (single-select advances, multi toggles), space toggles at
528
+ // the cursor, `t` opens the type-your-own line (the
529
+ // rule-input phase's shape: the buffer is the editor's, so
530
+ // only esc and enter are intercepted while typing), esc
531
+ // declines the whole call. Everything else falls through to
532
+ // the ordinary editing chain below.
533
+ if (panel.ask !== null) {
534
+ const typing = panel.ask.phase === "custom";
535
+ if (c === "\x1b" && !text.slice(i + 1).startsWith("[") && !text.slice(i + 1).startsWith("O")) {
536
+ this.#askStep("esc");
537
+ i += 1;
538
+ continue;
539
+ }
540
+ if (c === "\x0d" || c === "\x0a") {
541
+ this.#askStep(typing ? "commit" : "enter");
542
+ i += 1;
543
+ continue;
544
+ }
545
+ if (!typing && (c === " " || (c !== undefined && c >= "1" && c <= "4") || c === "t" || c === "T")) {
546
+ this.#askStep(c === " " ? "space" : c === "T" ? "t" : c);
547
+ i += 1;
548
+ continue;
549
+ }
550
+ // an ask at rest swallows stray PRINTABLE keys — the panel
551
+ // owns them, and a typed "/" or "@" must not arm the menu
552
+ // or the picker underneath. Two things are never
553
+ // swallowed: the CSI/SS3 introducer, because ←/↑/↓ are
554
+ // the ask's own keys and the parser below routes them
555
+ // (the T-Q1 red), and the CONTROL characters, because
556
+ // ctrl-c must still reach the SIGINT handler that
557
+ // cancels the panel — W21's own rule, and what the T-Q6
558
+ // race red caught: an abort with the panel up did
559
+ // nothing at all.
560
+ if (!typing && c !== undefined && c >= " " && c !== "\x7f") {
561
+ i += 1;
562
+ continue;
563
+ }
564
+ }
376
565
  if (c === "\x1b" && !text.slice(i + 1).startsWith("[") && !text.slice(i + 1).startsWith("O")) {
377
566
  this.#panelEsc();
378
567
  i += 1;
@@ -443,6 +632,17 @@ export class Editor {
443
632
  this.#refreshMenu();
444
633
  i += 1;
445
634
  }
635
+ else if (this.#atUp()) {
636
+ // KC3 §3: esc closes the picker and leaves the BUFFER
637
+ // ALONE — unlike the menu's esc, which clears it. The
638
+ // sentence around the reference is still being written,
639
+ // and a dismissed picker must not take it away. CA-4:
640
+ // the closing esc consumes its burst, so it can never
641
+ // also abort the run.
642
+ this.#atClose();
643
+ this.#onRender();
644
+ i += 1;
645
+ }
446
646
  else if (this.#queuePopMode) {
447
647
  // W22: esc in the pop-mode — ONE more pop, then the
448
648
  // mode ends: the next esc at rest rides the escapeCbs
@@ -539,6 +739,12 @@ export class Editor {
539
739
  }
540
740
  i += 1;
541
741
  }
742
+ else if (c === "\t" && this.#atUp()) {
743
+ // KC3 §3: Tab accepts the selected path — the token becomes
744
+ // `@<path> `. Never the file's content.
745
+ this.#atAccept();
746
+ i += 1;
747
+ }
542
748
  else if (c === "\x12") {
543
749
  // W15: the expand key (ctrl+r) — rides the chain like a
544
750
  // command, the editor just forwards it.
@@ -596,7 +802,10 @@ export class Editor {
596
802
  // panel owns the keys while up (↑↓ do nothing — the panel has no
597
803
  // ↑↓ role).
598
804
  if (this.#panel !== null) {
599
- /* the panel owns the keys */
805
+ // W21: the panel owns the keys. KC3.5: an ask uses ↑↓ for
806
+ // the option cursor (the approval panel still has no ↑↓ role).
807
+ if (this.#panel.ask !== null && this.#panel.ask.phase === "options")
808
+ this.#askStep(final === "A" ? "up" : "down");
600
809
  }
601
810
  else if (this.#menuOpen) {
602
811
  if (final === "A")
@@ -604,6 +813,14 @@ export class Editor {
604
813
  else
605
814
  this.#menuSel = Math.min(this.#menuFiltered().length - 1, this.#menuSel + 1);
606
815
  }
816
+ else if (this.#atUp()) {
817
+ // KC3 §3: the picker owns ↑↓ while up — the SELECTION, never
818
+ // the cursor and never the composer's line walk. It sits
819
+ // ABOVE the multi-line branch on purpose: a picker opened on
820
+ // line 2 of a composer must still select.
821
+ const view = this.#atView();
822
+ this.#atSel = final === "A" ? Math.max(0, view.selected - 1) : Math.min(view.matches.length - 1, view.selected + 1);
823
+ }
607
824
  else if (this.#chars.includes(NEWLINE)) {
608
825
  // KC1 §4: a MULTI-LINE buffer's ↑↓ walk its lines. The
609
826
  // history and the queue-pop below stay gated on an EMPTY
@@ -626,7 +843,12 @@ export class Editor {
626
843
  this.#onRender();
627
844
  }
628
845
  else if (final === "D") {
629
- this.#move(-1);
846
+ // KC3.5: ← walks the ask BACK a question (the ‹ n/m › walk); at
847
+ // question one it stays put — esc is the decline, never ←.
848
+ if (this.#panel?.ask != null && this.#panel.ask.phase === "options")
849
+ this.#askStep("left");
850
+ else
851
+ this.#move(-1);
630
852
  }
631
853
  else if (final === "C") {
632
854
  this.#move(1);
@@ -741,6 +963,33 @@ export class Editor {
741
963
  else if (panel.sel === 3)
742
964
  this.#panelClose({ action: "deny", reason: "" });
743
965
  }
966
+ /**
967
+ * KC3.5 — one ask key: the pure reducer decides, this method applies.
968
+ * The buffer is cleared on every phase change so the type-your-own
969
+ * line starts empty and its text never leaks back into the options
970
+ * (the rule-input phase's own discipline). A step that produced a
971
+ * RESULT closes the panel with it — the stash/restore is the W21
972
+ * path, identical for an answer and for a decline.
973
+ */
974
+ #askStep(key) {
975
+ const panel = this.#panel;
976
+ if (panel === null || panel.ask === null)
977
+ return;
978
+ const spec = panel.view.ask;
979
+ const before = panel.ask.phase;
980
+ const step = key === "commit" ? askCommitCustom(spec, panel.ask, this.line()) : askKey(spec, panel.ask, key);
981
+ panel.ask = step.state;
982
+ if (step.state.phase !== before) {
983
+ this.#chars = [];
984
+ this.#cursor = 0;
985
+ this.#scroll = 0;
986
+ }
987
+ if (step.result !== undefined) {
988
+ this.#panelClose({ action: "answers", result: step.result });
989
+ return;
990
+ }
991
+ this.#onRender();
992
+ }
744
993
  #panelClose(verdict) {
745
994
  const panel = this.#panel;
746
995
  if (panel === null)
@@ -764,6 +1013,11 @@ export class Editor {
764
1013
  this.#reflow();
765
1014
  if (!this.#pasting)
766
1015
  this.#refreshMenu();
1016
+ // KC3 §3: a TYPED `@` arms the picker; a PASTED one never does —
1017
+ // a paste is content, and content that happens to contain an
1018
+ // address must not open a file browser mid-sentence.
1019
+ if (cp === AT && !this.#pasting)
1020
+ this.#atArm();
767
1021
  }
768
1022
  #backspace() {
769
1023
  if (this.#cursor === 0)
@@ -833,6 +1087,7 @@ export class Editor {
833
1087
  this.#menuOpen = false;
834
1088
  this.#menuSel = 0;
835
1089
  this.#queuePopMode = false;
1090
+ this.#atClose(); // KC3 §3: a departing line takes its picker with it
836
1091
  return line;
837
1092
  }
838
1093
  /** A2: the history remembers submitted TURN lines — never question
@@ -857,6 +1112,7 @@ export class Editor {
857
1112
  #composerIdle() {
858
1113
  return (this.#panel === null &&
859
1114
  !this.#menuOpen &&
1115
+ !this.#atUp() && // KC3 §3: the @ picker owns the keys while up, exactly like the menu
860
1116
  this.#historyIdx === null &&
861
1117
  !this.#queuePopMode &&
862
1118
  !this.#pasting &&
@@ -889,6 +1145,14 @@ export class Editor {
889
1145
  this.#onRender();
890
1146
  }
891
1147
  #submit() {
1148
+ // KC3 §3: Enter ACCEPTS while the picker is up — the same rule the
1149
+ // menu's A1 feel established (complete first, let the user read
1150
+ // what they got, and let the NEXT Enter send it). An @ reference
1151
+ // that submitted on the first Enter would send the fragment.
1152
+ if (this.#atUp()) {
1153
+ this.#atAccept();
1154
+ return;
1155
+ }
892
1156
  if (this.#menuOpen) {
893
1157
  // A1 (the feel): Enter submits the EXACT selection directly; a
894
1158
  // PARTIAL selection COMPLETES the buffer (the Tab semantics)
@@ -975,7 +1239,7 @@ export class Editor {
975
1239
  // W23: the ONE width authority — leadWidth(lead) — the cap follows
976
1240
  // the lead the editor itself renders (the panel lead when the panel
977
1241
  // owns the keys, the brick otherwise): maxW = W − walls − lead.
978
- const lead = this.#panel !== null ? panelLead(this.#panel.view, this.#panel.phase, this.#panel.sel) : PROMPT;
1242
+ const lead = this.#panel !== null ? panelLead(this.#panel.view, this.#panel.phase, this.#panel.sel, this.#panel.ask ?? undefined) : PROMPT;
979
1243
  const leadW = leadWidth(lead);
980
1244
  const maxW = Math.max(1, W - leadW - 4); // W6: the box's walls (2+2) — the visible line fits the box's inner width; the "…" rides inside
981
1245
  // KC1: the scroll is the CURSOR LINE's own offset — a single-line
package/dist/index.d.ts CHANGED
@@ -12,3 +12,7 @@ export { Editor, MENU_ITEMS, PROMPT, PROMPT_WIDTH, displayWidth, charWidth, widt
12
12
  export { bannerLines, COLOR_OFF, COLOR_ON, escapeTerminal, foldResult, foldThinking, kUnit, palette, renderEvent, renderRecap, renderResumeList, renderSessionLine, renderStatusLine, relativeTime, renderTerminalGap, renderToolSummary, TAGLINE, toolTarget, truncateRow, type Palette, type PathResolver, type RecapStats, type ResumeMeta, type RenderInput, type RenderResult, type RunUsage, } from "./render.js";
13
13
  export { editFileDiff, truncateDiff, writeFileDiff, type DiffLine, type DiffResult } from "./diff.js";
14
14
  export { STATUS_GLYPHS, idleStatus, runningStatus } from "./status.js";
15
+ export { interactivePrompt, projectTrustRows, projectTrustView, projectUntrustedNote, uncertainView, type TrustArtifact } from "./strings.js";
16
+ export { AT_CAP, AT_SKIP, AT_VISIBLE, atEmbed, atFilter, atPanelRows, atWindow, longestRun, type AtItem, type AtMatch } from "./at-picker.js";
17
+ export { ASK_HEADER_CAP, ASK_MAX_OPTIONS, ASK_MAX_QUESTIONS, ASK_MIN_OPTIONS, askAffordance, askAnswers, askBlockRows, askCommitCustom, askDeclineAll, askDeclineList, askKey, askLeadPlain, askStart, askStatus, askView, type AskAnswer, type AskOption, type AskQuestion, type AskResult, type AskRuntime, type AskSpec, type AskStep, } from "./ask-panel.js";
18
+ export { extensionsBannerText, helpRows, unansweredAskView, type BannerExtension } from "./strings.js";
package/dist/index.js CHANGED
@@ -17,3 +17,17 @@ export { editFileDiff, truncateDiff, writeFileDiff } from "./diff.js";
17
17
  // KC2 §5: the status rows' formatters — the CLI keeps the state and the
18
18
  // repaint, the terminal layer owns what the row says.
19
19
  export { STATUS_GLYPHS, idleStatus, runningStatus } from "./status.js";
20
+ // KC3 §1 (the extraction): the human-facing strings — the prompt, the
21
+ // project-trust listing/view/note, the uncertain execution's view. The
22
+ // FLOW (who is asked, what a verdict means) stays in the cli.
23
+ export { interactivePrompt, projectTrustRows, projectTrustView, projectUntrustedNote, uncertainView } from "./strings.js";
24
+ // KC3 §3/§5: the @ file picker's pure half — the subsequence filter, the
25
+ // deterministic rank, and the ONE cap the CLI's file source shares.
26
+ export { AT_CAP, AT_SKIP, AT_VISIBLE, atEmbed, atFilter, atPanelRows, atWindow, longestRun } from "./at-picker.js";
27
+ // KC3.5 (the ask round): the ask view — the panel machinery generalized.
28
+ // The cli composes the view and hands the answers to the tool; the keys,
29
+ // the rows and the walk are the terminal layer's.
30
+ export { ASK_HEADER_CAP, ASK_MAX_OPTIONS, ASK_MAX_QUESTIONS, ASK_MIN_OPTIONS, askAffordance, askAnswers, askBlockRows, askCommitCustom, askDeclineAll, askDeclineList, askKey, askLeadPlain, askStart, askStatus, askView, } from "./ask-panel.js";
31
+ // KC3.5 §4: the interrupted-ask copy — the SAME uncertainty gate, said
32
+ // honestly for a question nobody answered (the ① probe's surface).
33
+ export { extensionsBannerText, helpRows, unansweredAskView } from "./strings.js";
@@ -0,0 +1,6 @@
1
+ /**
2
+ * KC3 slice 1 — the strings shim: the human-facing strings moved to the
3
+ * 9th package (the ADR-0043 Amendment 4 hatch); the tui re-exports them
4
+ * so its consumers (the cli) import a stable path.
5
+ */
6
+ export * from "@vincemakes/kiso-tui-cells/strings";
@@ -0,0 +1,6 @@
1
+ /**
2
+ * KC3 slice 1 — the strings shim: the human-facing strings moved to the
3
+ * 9th package (the ADR-0043 Amendment 4 hatch); the tui re-exports them
4
+ * so its consumers (the cli) import a stable path.
5
+ */
6
+ export * from "@vincemakes/kiso-tui-cells/strings";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-tui",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "kiso tui — the pure terminal layer (cell renderer, dock, raw editor, diff, palette). Zero runtime dependencies: input is data, output is bytes.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -35,6 +35,6 @@
35
35
  },
36
36
  "homepage": "https://github.com/vincemakes/kiso/tree/main/packages/tui#readme",
37
37
  "dependencies": {
38
- "@vincemakes/kiso-tui-cells": "0.6.0"
38
+ "@vincemakes/kiso-tui-cells": "0.8.0"
39
39
  }
40
40
  }