@vincemakes/kiso-tui 0.45.3 → 0.46.1

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.d.ts CHANGED
@@ -96,6 +96,9 @@ export declare class Editor {
96
96
  * exactly like ctrl+o because it is the same kind of thing: a switch
97
97
  * the compositor throws, never an interpretation the editor makes. */
98
98
  onThink(cb: () => void): void;
99
+ /** ADR-0058 (3e): the background key (ctrl+b) — move the running
100
+ * foreground command to the background. Forwarded, like ctrl+t. */
101
+ onBackground(cb: () => void): void;
99
102
  /** §2.4: the external-editor key (ctrl+g). */
100
103
  onEditor(cb: () => void): void;
101
104
  /** §2.4 — hand the terminal to an external program, and take it back.
package/dist/editor.js CHANGED
@@ -116,7 +116,8 @@ export const MOUSE_OFF = "\x1b[?1000l\x1b[?1006l";
116
116
  export const PROMPT = "▌ ";
117
117
  export const PROMPT_WIDTH = displayWidth(PROMPT);
118
118
  export const MENU_ITEMS = [
119
- { name: "/mode", desc: "switch the approval tier (default/accept-edits/plan/dontAsk/bypass)" },
119
+ { name: "/mode", desc: "switch the approval tier (default/accept-edits/plan/full-access)" },
120
+ { name: "/dont-ask", desc: "never stop for you: what would ask is refused (on/off)" },
120
121
  { name: "/model", desc: "list model profiles; switch with /model <name|provider/model>" },
121
122
  { name: "/compact", desc: "summarize the older conversation to free context" },
122
123
  // the /resume+/clear mini-spec: the session-navigation pair
@@ -130,6 +131,8 @@ export const MENU_ITEMS = [
130
131
  { name: "/think", desc: "show the last full thinking block" },
131
132
  { name: "/last", desc: "show the most recent tool call's input and output" },
132
133
  { name: "/copy", desc: "copy the last answer (raw markdown) — ctrl+x does the same" },
134
+ // ADR-0058 (3e): the session's tasks — stop one, or look at its output
135
+ { name: "/tasks", desc: "list this session's background tasks; stop one or show its output" },
133
136
  // R4 (C4d): the committed transcript belongs to the terminal and can
134
137
  // never be re-wrapped in place (ADR-0046); this appends it re-folded.
135
138
  { name: "/rewrap", desc: "re-print the recent prose at the current width" },
@@ -351,6 +354,7 @@ export class Editor {
351
354
  // coexist; the editor never interprets the key itself.
352
355
  #expandCbs = [];
353
356
  #thinkCbs = [];
357
+ #backgroundCbs = [];
354
358
  #editorCbs = [];
355
359
  /** E1 §3 — ctrl+x. Same shape as the expand key: the editor owns the
356
360
  * KEY, the CLI owns what it means. */
@@ -502,6 +506,11 @@ export class Editor {
502
506
  onThink(cb) {
503
507
  this.#thinkCbs.push(cb);
504
508
  }
509
+ /** ADR-0058 (3e): the background key (ctrl+b) — move the running
510
+ * foreground command to the background. Forwarded, like ctrl+t. */
511
+ onBackground(cb) {
512
+ this.#backgroundCbs.push(cb);
513
+ }
505
514
  /** §2.4: the external-editor key (ctrl+g). */
506
515
  onEditor(cb) {
507
516
  this.#editorCbs.push(cb);
@@ -1656,6 +1665,17 @@ export class Editor {
1656
1665
  cb();
1657
1666
  i += 1;
1658
1667
  }
1668
+ else if (c === "\x02") {
1669
+ // ADR-0058 (3e) — ctrl+b moves the running foreground command
1670
+ // to the background. `\x02` was unbound in this dispatch. It
1671
+ // works with text in the composer (a steer being typed is
1672
+ // exactly when the person wants the command out of the way);
1673
+ // a panel owns its keys before this point. Inside tmux the
1674
+ // first ctrl+b is tmux's prefix: pressing it twice sends it.
1675
+ for (const cb of [...this.#backgroundCbs])
1676
+ cb();
1677
+ i += 1;
1678
+ }
1659
1679
  else if (c === "\x07") {
1660
1680
  // §2.4 — ctrl+g opens $VISUAL / $EDITOR on the composer.
1661
1681
  //
package/dist/index.d.ts CHANGED
@@ -7,12 +7,12 @@
7
7
  * editor, the diff renderer, and the palette.
8
8
  */
9
9
  export { Body, Dock, CURSOR_MARKER, type BodyOptions } from "./compositor.js";
10
- export { panelAffordance, panelBlockRows, panelLead, panelLeadPlain, panelLeadWidth, panelStatus, PICK_MAX, modePickView, modelPickView, pickAffordance, pickBlockRows, pickLeadPlain, type PickOption, type PickResult, type PickRuntime, type PickSpec, type PanelArgs, type PanelFlavor, type PanelPhase, deletionRiskHint, SAFER_BACK, SAFER_DEGRADED, SAFER_DEGRADED_TRUNCATED, saferDegradedNote, type SaferAnswer, type SaferFailure, type SaferOption, type SaferRuntime, panelOptions, type PanelOption, type PanelOptionKind, type PanelState, type PanelVerdict, type PanelView, } from "./approval-panel.js";
10
+ export { panelAffordance, panelBlockRows, panelLead, panelLeadPlain, panelLeadWidth, panelStatus, PICK_MAX, modePickView, modelPickView, namedPickView, pickAffordance, pickBlockRows, pickLeadPlain, type PickOption, type PickResult, type PickRuntime, type PickSpec, type PanelArgs, type PanelFlavor, type PanelPhase, deletionRiskHint, SAFER_BACK, SAFER_DEGRADED, SAFER_DEGRADED_TRUNCATED, saferDegradedNote, type SaferAnswer, type SaferFailure, type SaferOption, type SaferRuntime, panelOptions, type PanelOption, type PanelOptionKind, type PanelState, type PanelVerdict, type PanelView, } from "./approval-panel.js";
11
11
  export { Container, foldLine, foldWords, visibleWidth, SPINNER, type Component, type FrameCtx } from "./components.js";
12
12
  export { Editor, MENU_ITEMS, PROMPT, PROMPT_WIDTH, displayWidth, charWidth, widthOf, type MenuItem, } from "./editor.js";
13
13
  export { bannerLines, COLOR_OFF, COLOR_ON, currentGround, setGround, 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 "./lines.js";
14
14
  export { editFileDiff, truncateDiff, writeFileDiff, type DiffLine, type DiffResult } from "./diff.js";
15
- export { STATUS_GLYPHS, cacheHitPct, compactingStatus, composeRow, decodeRate, idleStatus, retrySegment, runningStatus, type CompactingProgress, type RetryOnRow, type RowSegment, type StatusMeter } from "./status.js";
15
+ export { STATUS_GLYPHS, cacheHitPct, compactingStatus, composeRow, decodeRate, idleStatus, retrySegment, runningStatus, tasksSegment, type CompactingProgress, type RetryOnRow, type RowSegment, type StatusMeter, type TaskCountsOnRow } from "./status.js";
16
16
  export { contextRows, contextUnavailableRows, type ContextLedger } from "./context-ledger.js";
17
17
  export { interactivePrompt, projectTrustRows, projectTrustView, projectUntrustedNote, uncertainView, type TrustArtifact } from "./strings.js";
18
18
  export { AT_CAP, AT_SKIP, AT_VISIBLE, atEmbed, atFilter, atPanelRows, atWindow, bandHeader, longestRun, type AtItem, type AtMatch } from "./at-picker.js";
package/dist/index.js CHANGED
@@ -12,14 +12,14 @@ export { Body, Dock, CURSOR_MARKER } from "./compositor.js";
12
12
  // approval is pending (the shape authority is the committed preview).
13
13
  export { panelAffordance, panelBlockRows, panelLead, panelLeadPlain, panelLeadWidth, panelStatus,
14
14
  // TUI2-R2 ④: the pick payload — the panel slot's third occupant.
15
- PICK_MAX, modePickView, modelPickView, pickAffordance, pickBlockRows, pickLeadPlain, deletionRiskHint, SAFER_BACK, SAFER_DEGRADED, SAFER_DEGRADED_TRUNCATED, saferDegradedNote, panelOptions, } from "./approval-panel.js";
15
+ PICK_MAX, modePickView, modelPickView, namedPickView, pickAffordance, pickBlockRows, pickLeadPlain, deletionRiskHint, SAFER_BACK, SAFER_DEGRADED, SAFER_DEGRADED_TRUNCATED, saferDegradedNote, panelOptions, } from "./approval-panel.js";
16
16
  export { Container, foldLine, foldWords, visibleWidth, SPINNER } from "./components.js";
17
17
  export { Editor, MENU_ITEMS, PROMPT, PROMPT_WIDTH, displayWidth, charWidth, widthOf, } from "./editor.js";
18
18
  export { bannerLines, COLOR_OFF, COLOR_ON, currentGround, setGround, escapeTerminal, foldResult, foldThinking, kUnit, palette, renderEvent, renderRecap, renderResumeList, renderSessionLine, renderStatusLine, relativeTime, renderTerminalGap, renderToolSummary, TAGLINE, toolTarget, truncateRow, } from "./lines.js";
19
19
  export { editFileDiff, truncateDiff, writeFileDiff } from "./diff.js";
20
20
  // KC2 §5: the status rows' formatters — the CLI keeps the state and the
21
21
  // repaint, the terminal layer owns what the row says.
22
- export { STATUS_GLYPHS, cacheHitPct, compactingStatus, composeRow, decodeRate, idleStatus, retrySegment, runningStatus } from "./status.js";
22
+ export { STATUS_GLYPHS, cacheHitPct, compactingStatus, composeRow, decodeRate, idleStatus, retrySegment, runningStatus, tasksSegment } from "./status.js";
23
23
  // TUI2-R1 (E): /context's attribution rows — a pure function of the
24
24
  // counts the trace sidecar already records (the CLI reads, this renders).
25
25
  export { contextRows, contextUnavailableRows } from "./context-ledger.js";
package/dist/status.d.ts CHANGED
@@ -117,7 +117,18 @@ export interface RetryOnRow {
117
117
  * counts against, what failed, and how long until it is tried. Whole
118
118
  * seconds, rounded UP, so the row never says 0s while still waiting. */
119
119
  export declare function retrySegment(r: RetryOnRow): string;
120
- export declare function runningStatus(glyph: string, since: number, outTokens: number | null, ctxRatio: number, tokPerSec?: number | null, W?: number, retry?: RetryOnRow | null): string;
120
+ /** ADR-0058 (3e): the session's tasks, as the status rows count them. */
121
+ export interface TaskCountsOnRow {
122
+ /** running or starting */
123
+ readonly running: number;
124
+ /** an outcome that cannot be known, not yet looked at in `/tasks` */
125
+ readonly unknown: number;
126
+ }
127
+ /** `● 2 tasks running · ◌ 1 unknown`, `◌ 1 task unknown` — or nothing.
128
+ * An unknown task is never hidden behind the panel: it is the one that
129
+ * needs the person most. */
130
+ export declare function tasksSegment(c: TaskCountsOnRow | undefined): string;
131
+ export declare function runningStatus(glyph: string, since: number, outTokens: number | null, ctxRatio: number, tokPerSec?: number | null, W?: number, retry?: RetryOnRow | null, tasks?: TaskCountsOnRow, detachable?: boolean): string;
121
132
  /**
122
133
  * The COMPACTING row (W18): the covered rounds, the pre-call token
123
134
  * estimate, and the elapsed seconds — all knowable before the one summary
@@ -185,7 +196,7 @@ export interface StatusMeter {
185
196
  * No `W` means no dropping, which is what the callers that do not know
186
197
  * their width should get: today's row, unchanged.
187
198
  */
188
- export declare function idleStatus(tier: string, model: string, ctxRatio: number, meter?: StatusMeter, W?: number, floorOff?: boolean): string;
199
+ export declare function idleStatus(tier: string, model: string, ctxRatio: number, meter?: StatusMeter, W?: number, floorOff?: boolean, tasks?: TaskCountsOnRow): string;
189
200
  /** TUI2-R1 (E) — the cache hit rate the status row shows, from the usage
190
201
  * the CLI already tracks. The denominator is the TOTAL the model was
191
202
  * given (fresh + cacheRead), which is the E2 ruling's own: cacheRead
package/dist/status.js CHANGED
@@ -118,7 +118,25 @@ export function retrySegment(r) {
118
118
  const head = `retrying ${r.attempt}/${r.maxRetries} · ${r.code}`;
119
119
  return r.remainingMs > 0 ? `${head} · ${Math.ceil(r.remainingMs / 1000)}s` : head;
120
120
  }
121
- export function runningStatus(glyph, since, outTokens, ctxRatio, tokPerSec = null, W, retry) {
121
+ /** `● 2 tasks running · ◌ 1 unknown`, `◌ 1 task unknown` — or nothing.
122
+ * An unknown task is never hidden behind the panel: it is the one that
123
+ * needs the person most. */
124
+ export function tasksSegment(c) {
125
+ if (c === undefined)
126
+ return "";
127
+ const n = (k, word) => `${k} ${word}${k === 1 ? "" : "s"}`;
128
+ if (c.running > 0 && c.unknown > 0)
129
+ return `● ${n(c.running, "task")} running · ◌ ${c.unknown} unknown`;
130
+ if (c.running > 0)
131
+ return `● ${n(c.running, "task")} running`;
132
+ if (c.unknown > 0)
133
+ return `◌ ${n(c.unknown, "task")} unknown`;
134
+ return "";
135
+ }
136
+ export function runningStatus(glyph, since, outTokens, ctxRatio, tokPerSec = null, W, retry,
137
+ // 3e: the session's tasks, and whether a running command can be moved
138
+ // to the background now (the row teaches ctrl+b exactly then)
139
+ tasks, detachable = false) {
122
140
  const out = outTokens !== null ? ` ↓ ${kUnit(outTokens)} tokens` : "";
123
141
  const seconds = Math.max(1, Math.round((Date.now() - since) / 1000));
124
142
  return composeRow(`${glyph} working ${elapsedLabel(seconds)}${out}`, [
@@ -132,7 +150,9 @@ export function runningStatus(glyph, since, outTokens, ctxRatio, tokPerSec = nul
132
150
  // honest rule spelled as a default — the recovery flow has no
133
151
  // per-call timing state, so its row says nothing rather than guessing.
134
152
  tokPerSec !== null ? { kind: "fact", text: `${tokPerSec} tok/s` } : null,
153
+ { kind: "fact", text: tasksSegment(tasks) },
135
154
  { kind: "hint", text: "esc stop" },
155
+ detachable ? { kind: "hint", text: "ctrl+b background" } : null,
136
156
  { kind: "hint", text: "alt+⏎ redirect" },
137
157
  { kind: "fact", text: ctxSegment(ctxRatio) },
138
158
  ], W);
@@ -229,12 +249,14 @@ function elideMiddle(text, max) {
229
249
  * No `W` means no dropping, which is what the callers that do not know
230
250
  * their width should get: today's row, unchanged.
231
251
  */
232
- export function idleStatus(tier, model, ctxRatio, meter, W, floorOff = false) {
252
+ export function idleStatus(tier, model, ctxRatio, meter, W, floorOff = false, tasks) {
233
253
  return composeRow(`▸ ${tier}`, [
234
254
  // 0.40.0: the catastrophe floor is on by default and says nothing;
235
255
  // OFF is the state worth seeing, and a fact beside the tier it
236
256
  // changes the meaning of.
237
257
  floorOff ? { kind: "fact", text: "floor off" } : null,
258
+ // 3e: work still going on (or gone unknown) after the turn ended
259
+ { kind: "fact", text: tasksSegment(tasks) },
238
260
  { kind: "hint", text: "/mode to switch" },
239
261
  { kind: "label", text: model },
240
262
  meter?.cacheHitPct != null ? { kind: "fact", text: `CH ${Math.round(meter.cacheHitPct)}%` } : null,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-tui",
3
- "version": "0.45.3",
3
+ "version": "0.46.1",
4
4
  "description": "kiso tui \u2014 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.45.3"
38
+ "@vincemakes/kiso-tui-cells": "0.46.1"
39
39
  }
40
40
  }