@aragon-agent/cli 0.6.7 → 0.6.9
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/CHANGELOG.md +27 -0
- package/README.md +107 -77
- package/dist/agent/controller.d.ts +11 -7
- package/dist/agent/controller.js +35 -48
- package/dist/agent/headless.d.ts +2 -1
- package/dist/agent/prompt-options.d.ts +15 -0
- package/dist/agent/prompt-options.js +1 -0
- package/dist/agent/reducer.d.ts +7 -113
- package/dist/agent/reducer.js +0 -62
- package/dist/cli.js +111 -182
- package/dist/commands/builtins.js +3 -1
- package/dist/commands/perf.d.ts +32 -2
- package/dist/commands/perf.js +32 -2
- package/dist/commands/registry.d.ts +4 -5
- package/dist/config/env.js +0 -130
- package/dist/config/load.d.ts +0 -2
- package/dist/config/load.js +2 -31
- package/dist/config/schema.d.ts +1 -14
- package/dist/config/schema.js +0 -1
- package/dist/config/store.js +2 -0
- package/dist/input/interrupt-gesture.d.ts +18 -0
- package/dist/input/interrupt-gesture.js +19 -0
- package/dist/input/pointer-router.d.ts +11 -0
- package/dist/input/pointer-router.js +14 -0
- package/dist/logging/install.d.ts +2 -2
- package/dist/logging/install.js +2 -2
- package/dist/todo/follow-through.d.ts +0 -4
- package/dist/todo/follow-through.js +0 -4
- package/dist/todo/limits.d.ts +0 -10
- package/dist/todo/limits.js +0 -10
- package/dist/todo/prompt.d.ts +0 -15
- package/dist/todo/store.d.ts +8 -13
- package/dist/todo/store.js +13 -14
- package/dist/todo/types.d.ts +2 -1
- package/dist/ui/ActivityLine.d.ts +26 -34
- package/dist/ui/ActivityLine.js +30 -19
- package/dist/ui/App.d.ts +3 -41
- package/dist/ui/App.js +223 -247
- package/dist/ui/BottomStatusRow.d.ts +1 -3
- package/dist/ui/BottomStatusRow.js +4 -8
- package/dist/ui/Composer.d.ts +38 -5
- package/dist/ui/Composer.js +45 -23
- package/dist/ui/Header.d.ts +2 -16
- package/dist/ui/Header.js +3 -18
- package/dist/ui/Logo.d.ts +1 -17
- package/dist/ui/Logo.js +0 -16
- package/dist/ui/ModeChip.d.ts +7 -18
- package/dist/ui/ModeChip.js +9 -0
- package/dist/ui/PromptCaret.d.ts +11 -0
- package/dist/ui/PromptCaret.js +31 -0
- package/dist/ui/PromptInput.d.ts +10 -33
- package/dist/ui/PromptInput.js +37 -20
- package/dist/ui/StatusBar.d.ts +1 -11
- package/dist/ui/StatusBar.js +14 -8
- package/dist/ui/ToastStack.d.ts +2 -18
- package/dist/ui/ToastStack.js +4 -11
- package/dist/ui/Transcript.d.ts +5 -71
- package/dist/ui/Transcript.js +10 -199
- package/dist/ui/composer-rows.js +50 -9
- package/dist/ui/entries/AssistantEntry.d.ts +0 -23
- package/dist/ui/entries/AssistantEntry.js +3 -4
- package/dist/ui/entries/DiffView.d.ts +1 -7
- package/dist/ui/entries/DiffView.js +2 -2
- package/dist/ui/entries/ToolCard.d.ts +0 -6
- package/dist/ui/entries/ToolCard.js +4 -13
- package/dist/ui/frame-differ.d.ts +13 -0
- package/dist/ui/frame-differ.js +37 -24
- package/dist/ui/frame-observer.d.ts +17 -0
- package/dist/ui/frame-observer.js +81 -0
- package/dist/ui/frame-parser.d.ts +7 -0
- package/dist/ui/frame-parser.js +18 -0
- package/dist/ui/ink-runtime.d.ts +1 -0
- package/dist/ui/ink-runtime.js +21 -0
- package/dist/ui/layout/AppShell.d.ts +4 -58
- package/dist/ui/layout/AppShell.js +3 -6
- package/dist/ui/layout/OverlayFrame.d.ts +1 -25
- package/dist/ui/layout/OverlayFrame.js +0 -24
- package/dist/ui/layout/ScrollIndicator.d.ts +4 -3
- package/dist/ui/layout/ScrollIndicator.js +4 -4
- package/dist/ui/layout/ScrollViewport.d.ts +16 -66
- package/dist/ui/layout/ScrollViewport.js +195 -185
- package/dist/ui/layout/budget.d.ts +3 -51
- package/dist/ui/layout/budget.js +4 -53
- package/dist/ui/layout/document-layout.d.ts +13 -0
- package/dist/ui/layout/document-layout.js +11 -0
- package/dist/ui/layout/follow-state.d.ts +1 -0
- package/dist/ui/layout/follow-state.js +1 -3
- package/dist/ui/layout/frame.d.ts +0 -22
- package/dist/ui/layout/frame.js +0 -36
- package/dist/ui/layout/scroll-indicator.d.ts +1 -1
- package/dist/ui/layout/scroll-indicator.js +3 -2
- package/dist/ui/layout/scrollbar-geometry.d.ts +17 -0
- package/dist/ui/layout/scrollbar-geometry.js +13 -0
- package/dist/ui/layout/todo-layout.d.ts +2 -3
- package/dist/ui/layout/todo-layout.js +9 -9
- package/dist/ui/layout/viewport-geometry.d.ts +2 -1
- package/dist/ui/layout/viewport-geometry.js +2 -1
- package/dist/ui/layout/virtual-window.d.ts +1 -0
- package/dist/ui/layout/virtual-window.js +1 -1
- package/dist/ui/live-clamp.d.ts +1 -29
- package/dist/ui/live-clamp.js +1 -29
- package/dist/ui/overlays/HelpOverlay.js +6 -2
- package/dist/ui/run-status-row.d.ts +61 -0
- package/dist/ui/run-status-row.js +70 -0
- package/dist/ui/screen.d.ts +1 -1
- package/dist/ui/screen.js +4 -6
- package/dist/ui/scrollbar-controller.d.ts +30 -0
- package/dist/ui/scrollbar-controller.js +124 -0
- package/dist/ui/transcript-text.d.ts +0 -8
- package/dist/ui/transcript-text.js +0 -8
- package/dist/ui/use-startup-notices.d.ts +1 -57
- package/dist/ui/use-startup-notices.js +3 -54
- package/dist/ui/use-wheel-routing.d.ts +3 -1
- package/dist/ui/use-wheel-routing.js +6 -0
- package/package.json +2 -2
- package/dist/ui/TodoStrip.d.ts +0 -36
- package/dist/ui/TodoStrip.js +0 -32
package/dist/cli.js
CHANGED
|
@@ -15,7 +15,8 @@ import { dirname, join } from 'node:path';
|
|
|
15
15
|
import { fileURLToPath } from 'node:url';
|
|
16
16
|
import process from 'node:process';
|
|
17
17
|
import '../runtime/insecure-tls-warning.cjs';
|
|
18
|
-
|
|
18
|
+
// Must precede every other dependency that imports Ink: CI detection is cached.
|
|
19
|
+
import { render } from './ui/ink-runtime.js';
|
|
19
20
|
import { Command } from 'commander';
|
|
20
21
|
import { loadConfig } from './config/load.js';
|
|
21
22
|
import { getConfigPath, readConfigFile, updatePersistedConfig } from './config/store.js';
|
|
@@ -40,11 +41,13 @@ import { registerExecCommand } from './exec/cli-commands.js';
|
|
|
40
41
|
import { registerSessionsCommand } from './session/cli-commands.js';
|
|
41
42
|
import { registerDoctorCommand, registerInfoCommand } from './diagnostics/cli-commands.js';
|
|
42
43
|
import { App } from './ui/App.js';
|
|
43
|
-
import { decideRenderMode } from './ui/layout/frame.js';
|
|
44
44
|
import { createFrameDiffer } from './ui/frame-differ.js';
|
|
45
45
|
import { wrapStdoutForFrames } from './ui/stdout-frame-writer.js';
|
|
46
46
|
import { setFrameStatsProvider } from './commands/perf.js';
|
|
47
47
|
import { tryCreateStdinFilter } from './input/stdin-filter.js';
|
|
48
|
+
import { createScrollbarBridge } from './ui/scrollbar-controller.js';
|
|
49
|
+
import { createPointerRouter } from './input/pointer-router.js';
|
|
50
|
+
import { createFrameObserver } from './ui/frame-observer.js';
|
|
48
51
|
import { enterAltScreen, writeExitTranscript } from './ui/screen.js';
|
|
49
52
|
import { supportsWindowsVtInput } from './ui/win-vt-input.js';
|
|
50
53
|
import { forceWindowsVtInput } from './ui/win-vt-force.js';
|
|
@@ -93,7 +96,6 @@ function toFlags(opts) {
|
|
|
93
96
|
confirm: opts.confirm,
|
|
94
97
|
toolTimeout: opts.toolTimeout,
|
|
95
98
|
idleTimeout: opts.idleTimeout,
|
|
96
|
-
fullscreen: opts.fullscreen,
|
|
97
99
|
exitTranscript: opts.exitTranscript,
|
|
98
100
|
compact: opts.compact,
|
|
99
101
|
hints: opts.hints,
|
|
@@ -227,14 +229,8 @@ function makeController(flags, opts = { interactive: true }) {
|
|
|
227
229
|
// resolved a bootstrap level — it ran before commander and before `.env` were
|
|
228
230
|
// readable — and without this hand-off the fully resolved one never applies.
|
|
229
231
|
getLogger().reconfigure(config.log);
|
|
230
|
-
//
|
|
231
|
-
|
|
232
|
-
// is pure and `runInteractive` calls it again with the same inputs, so
|
|
233
|
-
// computing it here rather than threading it back costs nothing and keeps the
|
|
234
|
-
// controller's construction self-contained.
|
|
235
|
-
const todoPanelCapable = opts.interactive &&
|
|
236
|
-
decideRenderMode({ fullscreen: config.fullscreen }, process.env, process.stdout) ===
|
|
237
|
-
'fullscreen';
|
|
232
|
+
// Headless controllers never expose the task panel.
|
|
233
|
+
const todoPanelCapable = opts.interactive;
|
|
238
234
|
const confirmBridge = { handler: null };
|
|
239
235
|
// `cancelPending` is replaced by the App's own implementation on mount; this
|
|
240
236
|
// no-op keeps the object total so nothing has to null-check it.
|
|
@@ -386,11 +382,15 @@ function forceVtInputForThisConsole() {
|
|
|
386
382
|
* renders Ink, `runOneShot()` goes to `runHeadless()`, which never does.
|
|
387
383
|
*/
|
|
388
384
|
function runInteractive(flags, extras = {}) {
|
|
385
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
386
|
+
process.stderr.write('Interactive UI requires a TTY. Use -p "<prompt>", exec, or config list / config set.\n');
|
|
387
|
+
process.exitCode = 2;
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
389
390
|
const { controller, confirmBridge, humanInputBridge } = makeController(flags);
|
|
390
391
|
const config = controller.getConfig();
|
|
391
|
-
const mode = decideRenderMode({ fullscreen: config.fullscreen }, process.env, process.stdout);
|
|
392
392
|
const logger = getLogger();
|
|
393
|
-
logger.info('cli', 'run_interactive', { mode, provider: config.provider, model: config.model });
|
|
393
|
+
logger.info('cli', 'run_interactive', { mode: 'fullscreen', provider: config.provider, model: config.model });
|
|
394
394
|
attachAgentEvents(logger, controller);
|
|
395
395
|
// The team stream is CLI-local (D-10), so `attachAgentEvents` cannot see it.
|
|
396
396
|
// Without this a dispatch leaves NO trace in the log file at all — the wrong
|
|
@@ -439,8 +439,7 @@ function runInteractive(flags, extras = {}) {
|
|
|
439
439
|
// a console this process just repaired.
|
|
440
440
|
const vtInputByNode = supportsWindowsVtInput(process.platform, process.versions.node);
|
|
441
441
|
const vtInputSupported = vtInputByNode || forceVtInputForThisConsole();
|
|
442
|
-
const wantMouse =
|
|
443
|
-
config.mouse &&
|
|
442
|
+
const wantMouse = config.mouse &&
|
|
444
443
|
!!process.stdout.isTTY &&
|
|
445
444
|
!!process.stdin.isTTY &&
|
|
446
445
|
vtInputSupported;
|
|
@@ -476,33 +475,26 @@ function runInteractive(flags, extras = {}) {
|
|
|
476
475
|
const disposeStdinFilter = () => filter?.dispose();
|
|
477
476
|
// --- Drag-select (tui-selection-and-scroll-follow §4.4). ------------------
|
|
478
477
|
//
|
|
479
|
-
//
|
|
480
|
-
//
|
|
481
|
-
// controller, the differ's `decorate` hook and the startup notice must never
|
|
482
|
-
// disagree about whether drag-select is running. With it false the session is
|
|
483
|
-
// byte-identical to a pre-feature build — no extra escape on the wire (AC-8),
|
|
484
|
-
// no controller, no `decorate`, no mirror.
|
|
478
|
+
// Selection owns its controller, decoration and notice. Button motion follows
|
|
479
|
+
// mouseOn independently so --no-mouse-select still permits scrollbar dragging.
|
|
485
480
|
const wantSelect = mouseOn && config.mouseSelect;
|
|
481
|
+
let mouseCaptured = mouseOn;
|
|
482
|
+
const scrollbar = createScrollbarBridge(() => mouseCaptured);
|
|
483
|
+
const pointerRouter = mouseOn && filter ? createPointerRouter({
|
|
484
|
+
source: filter.source, handle: (event) => scrollbar.controller?.handle(event) ?? false,
|
|
485
|
+
}) : null;
|
|
486
486
|
const selectionBridge = {
|
|
487
487
|
controller: null,
|
|
488
488
|
onCopied: null,
|
|
489
489
|
requestRedraw: null,
|
|
490
490
|
};
|
|
491
|
-
|
|
492
|
-
//
|
|
493
|
-
// FULL-SCREEN ONLY, and that is invariant I-3 rather than caution: inline mode
|
|
494
|
-
// prints settled entries through Ink's `<Static>`, starts wherever the shell's
|
|
495
|
-
// cursor happened to be and scrolls, so row k of a frame is not absolute row k
|
|
496
|
-
// there and never will be. `--no-diff-render` skips construction entirely,
|
|
497
|
-
// which is what makes rung 2 of the fail-safe ladder byte-identical to a
|
|
498
|
-
// pre-fix build rather than merely equivalent.
|
|
499
|
-
//
|
|
500
|
-
// `rows` is a THUNK, not a snapshot: it is the only input to the I-9 geometry
|
|
501
|
-
// guard and it changes under the differ's feet on every resize.
|
|
502
|
-
const frameWriter = mode === 'fullscreen' && config.diffRender
|
|
491
|
+
const frameWriter = config.diffRender
|
|
503
492
|
? wrapStdoutForFrames(process.stdout, createFrameDiffer({
|
|
504
493
|
sync: config.syncOutput && process.env.TERM !== 'dumb',
|
|
505
494
|
rows: () => process.stdout.rows,
|
|
495
|
+
// Rows that fill the line skip `CSI K` so the scrollbar's last column
|
|
496
|
+
// is not erased on pending-wrap terminals (frame-differ I-4).
|
|
497
|
+
cols: () => process.stdout.columns,
|
|
506
498
|
// The console bridge (`App.tsx`, full-screen only) turns this into
|
|
507
499
|
// `dispatch({type:'notice', level:'warn'})`, which is the only legal
|
|
508
500
|
// user-visible channel with the TUI mounted — a direct write would
|
|
@@ -535,14 +527,20 @@ function runInteractive(flags, extras = {}) {
|
|
|
535
527
|
// than printing zeroes that read like a writer doing nothing (§5.4).
|
|
536
528
|
if (frameWriter)
|
|
537
529
|
setFrameStatsProvider(frameWriter.stats);
|
|
530
|
+
const frameObserver = createFrameObserver({
|
|
531
|
+
stdout: frameWriter?.stdout ?? process.stdout, terminal: process.stdout, scrollbar,
|
|
532
|
+
});
|
|
538
533
|
const disposeFrameWriter = () => {
|
|
534
|
+
frameObserver.dispose();
|
|
535
|
+
pointerRouter?.dispose();
|
|
536
|
+
scrollbar.controller?.dispose();
|
|
539
537
|
if (!frameWriter)
|
|
540
538
|
return;
|
|
541
539
|
setFrameStatsProvider(null);
|
|
542
540
|
frameWriter.dispose();
|
|
543
541
|
};
|
|
544
542
|
const replayTranscript = () => {
|
|
545
|
-
if (replayed ||
|
|
543
|
+
if (replayed || !config.exitTranscript)
|
|
546
544
|
return;
|
|
547
545
|
replayed = true;
|
|
548
546
|
const snapshot = readExitSnapshot();
|
|
@@ -561,7 +559,7 @@ function runInteractive(flags, extras = {}) {
|
|
|
561
559
|
...(snapshot.droppedEntries ? { droppedEntries: snapshot.droppedEntries } : {}),
|
|
562
560
|
}));
|
|
563
561
|
};
|
|
564
|
-
|
|
562
|
+
{
|
|
565
563
|
// `mouse` here means "a mouse-parsing filter is installed", NOT "the user
|
|
566
564
|
// wants mouse support" — see `AltScreenOptions`. `screen.ts` must never
|
|
567
565
|
// derive it for itself, or a later refactor re-opens the gap I-8 closes.
|
|
@@ -574,8 +572,8 @@ function runInteractive(flags, extras = {}) {
|
|
|
574
572
|
// keep their terminal's own selection.
|
|
575
573
|
screen = enterAltScreen(process.stdout, {
|
|
576
574
|
mouse: mouseOn,
|
|
577
|
-
motion:
|
|
578
|
-
bracketedPaste: pasteOn
|
|
575
|
+
motion: mouseOn,
|
|
576
|
+
bracketedPaste: pasteOn,
|
|
579
577
|
});
|
|
580
578
|
const restore = () => screen?.restore();
|
|
581
579
|
// Four idempotent restore paths (§4.4). The signal hook is NOT redundant:
|
|
@@ -600,44 +598,8 @@ function runInteractive(flags, extras = {}) {
|
|
|
600
598
|
process.exit(128 + signo);
|
|
601
599
|
});
|
|
602
600
|
}
|
|
603
|
-
// --- Auto-update (cli-auto-update section 3.8). --------------------------
|
|
604
|
-
//
|
|
605
|
-
// THE ONLY CONSTRUCTION SITE. `runOneShot` / `runHeadless` never reach this
|
|
606
|
-
// function, so `aragon -p` executes zero update code and its stdout is
|
|
607
|
-
// byte-identical to a pre-feature build (AC-1); `aragon config <subcommand>`,
|
|
608
|
-
// `aragon logs`, `aragon skills` and `aragon --version` likewise. Bare
|
|
609
|
-
// `aragon config` DOES construct it, because it is a full interactive session
|
|
610
|
-
// with a settings overlay on top - which is correct, and is why AC-1's claim
|
|
611
|
-
// names the SUBCOMMAND form.
|
|
612
|
-
//
|
|
613
|
-
// `CI` is excluded on top of the two TTY tests because a CI runner with a pty
|
|
614
|
-
// allocated is otherwise a machine that silently mutates its own global
|
|
615
|
-
// toolchain mid-pipeline (D-11).
|
|
616
|
-
// --- The foreign-write door (§4.4.5). ------------------------------------
|
|
617
|
-
//
|
|
618
|
-
// Where bytes that are OURS but are NOT a frame go — today, the OSC 52
|
|
619
|
-
// clipboard sequence. THREE SHAPES, ONE DOOR, and the third one is the reason
|
|
620
|
-
// this is a resolved value rather than an inline arrow:
|
|
621
|
-
//
|
|
622
|
-
// * with the differ in front of stdout it MUST be `frameWriter.writeForeign`,
|
|
623
|
-
// which invalidates the cache and skips the fallback accounting — through
|
|
624
|
-
// the proxy the same bytes are an unrecognised chunk, which is
|
|
625
|
-
// `passThrough(true)`, which prints `FRAME_FALLBACK_NOTICE` at a user who
|
|
626
|
-
// did nothing but copy something (P1-6);
|
|
627
|
-
// * without one — `--no-diff-render`, and inline mode, which never builds a
|
|
628
|
-
// differ at all — stdout IS the real stream, so a direct write is both safe
|
|
629
|
-
// and what §4.4.5 prescribes. OSC 52 emits no newline, so Ink's own line
|
|
630
|
-
// accounting is untouched and there is nothing to invalidate;
|
|
631
|
-
// * on a non-TTY there is no terminal to accept it, so the door is ABSENT.
|
|
632
|
-
//
|
|
633
|
-
// It must never be a function that accepts a chunk and writes NOWHERE.
|
|
634
|
-
// `copyText` reports `'osc52'` whenever its door took the text, so a silent
|
|
635
|
-
// no-op door — which `frameWriter?.writeForeign(chunk)` is under
|
|
636
|
-
// `--no-diff-render` — makes the toast name a mechanism that was never
|
|
637
|
-
// attempted. Naming the mechanism honestly is the entire reason that return
|
|
638
|
-
// value exists (R-7), and a door that lies is worse than no door.
|
|
639
601
|
const writeForeign = frameWriter
|
|
640
|
-
? (text) => frameWriter.writeForeign(text)
|
|
602
|
+
? (text) => { frameObserver?.invalidate(); frameWriter.writeForeign(text); }
|
|
641
603
|
: process.stdout.isTTY
|
|
642
604
|
? (text) => {
|
|
643
605
|
try {
|
|
@@ -659,10 +621,9 @@ function runInteractive(flags, extras = {}) {
|
|
|
659
621
|
// than inside `screen.ts`'s closure because `App` needs to READ it and the
|
|
660
622
|
// handle's own flag is private (it has to be — `restore()` reads it, and
|
|
661
623
|
// exposing a setter would be a second way to get the two out of step).
|
|
662
|
-
let mouseCaptured = mouseOn;
|
|
663
624
|
if (wantSelect && filter) {
|
|
664
625
|
selectionBridge.controller = createSelectionController({
|
|
665
|
-
source: filter.source,
|
|
626
|
+
source: pointerRouter?.selectionSource ?? filter.source,
|
|
666
627
|
// The frame writer is absent under `--no-diff-render`, where there is no
|
|
667
628
|
// cache to repaint from at all; the controller then falls back to asking
|
|
668
629
|
// `App` for a React redraw on every drag flush, which is slower and
|
|
@@ -686,79 +647,82 @@ function runInteractive(flags, extras = {}) {
|
|
|
686
647
|
selectionBridge.controller?.dispose();
|
|
687
648
|
selectionBridge.controller = null;
|
|
688
649
|
};
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
if (!on)
|
|
714
|
-
selectionBridge.controller?.clear();
|
|
715
|
-
},
|
|
716
|
-
isMouseCaptured: () => mouseCaptured,
|
|
717
|
-
}
|
|
718
|
-
: {}),
|
|
719
|
-
// The same door the controller copies through, so `/copy` and a
|
|
720
|
-
// drag-release can never disagree about which stream OSC 52 goes to.
|
|
721
|
-
...(writeForeign ? { writeForeign } : {}),
|
|
722
|
-
...(selectionBridge.controller ? { selection: selectionBridge } : {}),
|
|
723
|
-
}
|
|
724
|
-
: undefined;
|
|
650
|
+
const terminalBridge = {
|
|
651
|
+
mouseSelect: wantSelect,
|
|
652
|
+
scrollbar,
|
|
653
|
+
...(mouseOn
|
|
654
|
+
? {
|
|
655
|
+
setMouseCapture: (on) => {
|
|
656
|
+
screen?.setMouseCapture(on);
|
|
657
|
+
mouseCaptured = on;
|
|
658
|
+
if (!on)
|
|
659
|
+
scrollbar.controller?.cancel();
|
|
660
|
+
// A released mouse cannot be dragging, and a selection left
|
|
661
|
+
// painted over a screen the terminal is now selecting on its
|
|
662
|
+
// own would be two highlights claiming the same rows.
|
|
663
|
+
if (!on)
|
|
664
|
+
selectionBridge.controller?.clear();
|
|
665
|
+
},
|
|
666
|
+
isMouseCaptured: () => mouseCaptured,
|
|
667
|
+
}
|
|
668
|
+
: {}),
|
|
669
|
+
// The same door the controller copies through, so `/copy` and a
|
|
670
|
+
// drag-release can never disagree about which stream OSC 52 goes to.
|
|
671
|
+
...(writeForeign ? { writeForeign } : {}),
|
|
672
|
+
...(selectionBridge.controller ? { selection: selectionBridge } : {}),
|
|
673
|
+
};
|
|
725
674
|
const updateBridge = { service: null, onAttach: null };
|
|
726
675
|
let updateService = null;
|
|
676
|
+
let interactiveClosed = false;
|
|
677
|
+
const failInteractive = (error) => {
|
|
678
|
+
// Ink consumes render exceptions; retain failure even if a disposer throws.
|
|
679
|
+
process.exitCode = 1;
|
|
680
|
+
interactiveClosed = true;
|
|
681
|
+
logger.error('cli', 'render_failed', { error: String(error) });
|
|
682
|
+
for (const cleanup of [
|
|
683
|
+
() => screen?.restore(), disposeSelection, disposeStdinFilter, disposeFrameWriter,
|
|
684
|
+
() => controller.abort(), () => controller.dispose(), () => updateService?.dispose(),
|
|
685
|
+
() => { process.stdin.pause(); process.stdin.unref?.(); },
|
|
686
|
+
]) {
|
|
687
|
+
try {
|
|
688
|
+
cleanup();
|
|
689
|
+
}
|
|
690
|
+
catch (cleanupError) {
|
|
691
|
+
logger.error('cli', 'render_cleanup_failed', { error: String(cleanupError) });
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
};
|
|
727
695
|
const updateEligible = !!process.stdout.isTTY &&
|
|
728
696
|
!!process.stdin.isTTY &&
|
|
729
697
|
!process.env.CI &&
|
|
730
698
|
config.update.mode !== 'off';
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
//
|
|
744
|
-
//
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
// with --no-diff-render, inline mode and `aragon -p`") a statement about
|
|
759
|
-
// the code rather than about Ink's defaulting.
|
|
760
|
-
...(frameWriter ? { stdout: frameWriter.stdout } : {}),
|
|
761
|
-
});
|
|
699
|
+
let instance;
|
|
700
|
+
try {
|
|
701
|
+
instance = render(_jsx(App, { controller: controller, version: VERSION, confirmBridge: confirmBridge, humanInputBridge: humanInputBridge, initialPrompt: extras.initialPrompt, initialOverlay: extras.initialOverlay, mouseSource: mouseOn && filter ? filter.source : undefined,
|
|
702
|
+
// Present ONLY when this console swallows `CSI Z` and mouse reports.
|
|
703
|
+
// Passed in rather than probed inside `App` so that every test renders
|
|
704
|
+
// the same tree on every machine — reading `process.versions.node` from
|
|
705
|
+
// a component would make the notice appear or vanish according to which
|
|
706
|
+
// Node the developer happened to have installed.
|
|
707
|
+
vtInputWarning: vtInputSupported ? undefined : { nodeVersion: process.versions.node }, updateBridge: updateEligible ? updateBridge : undefined,
|
|
708
|
+
// Absent when nothing can refuse a paste, so a `--no-paste` tree is
|
|
709
|
+
// byte-identical to a build without this feature.
|
|
710
|
+
pasteBridge: pasteOn ? pasteBridge : undefined, terminal: terminalBridge }),
|
|
711
|
+
// Full-screen owns console.* itself (I-4): Ink's patchConsole writes
|
|
712
|
+
// straight to stdout and permanently shifts the fixed frame's accounting.
|
|
713
|
+
{
|
|
714
|
+
exitOnCtrlC: false,
|
|
715
|
+
patchConsole: false,
|
|
716
|
+
// I-5: with mouse support off the REAL stdin is handed over unwrapped, so
|
|
717
|
+
// every existing path is provably unchanged.
|
|
718
|
+
stdin: filter?.stdin ?? process.stdin,
|
|
719
|
+
stdout: frameObserver.stdout,
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
catch (error) {
|
|
723
|
+
failInteractive(error);
|
|
724
|
+
throw error;
|
|
725
|
+
}
|
|
762
726
|
// H1's primary disarm, and this is the earliest moment at which "this build
|
|
763
727
|
// STARTS" is proven: the module graph loaded, config resolved, Ink mounted
|
|
764
728
|
// (cli-auto-update-hardening section 5.1.4). It is what makes a long session
|
|
@@ -780,6 +744,8 @@ function runInteractive(flags, extras = {}) {
|
|
|
780
744
|
// must degrade to "no updater", never to a crashed TUI.
|
|
781
745
|
void import('./update/service.js')
|
|
782
746
|
.then(({ UpdateService }) => {
|
|
747
|
+
if (interactiveClosed)
|
|
748
|
+
return;
|
|
783
749
|
const service = new UpdateService({ config: config.update, currentVersion: VERSION });
|
|
784
750
|
updateService = service;
|
|
785
751
|
updateBridge.service = service;
|
|
@@ -810,6 +776,7 @@ function runInteractive(flags, extras = {}) {
|
|
|
810
776
|
void instance
|
|
811
777
|
.waitUntilExit()
|
|
812
778
|
.then(() => {
|
|
779
|
+
interactiveClosed = true;
|
|
813
780
|
screen?.restore();
|
|
814
781
|
// BEFORE `disposeStdinFilter()`, which clears the listener set it subscribed to,
|
|
815
782
|
// and after `restore()` for the reason that call records: the terminal must
|
|
@@ -838,18 +805,7 @@ function runInteractive(flags, extras = {}) {
|
|
|
838
805
|
updateService?.dispose();
|
|
839
806
|
replayTranscript();
|
|
840
807
|
})
|
|
841
|
-
.catch(
|
|
842
|
-
screen?.restore();
|
|
843
|
-
// BEFORE `disposeStdinFilter()`, which clears the listener set it subscribed to,
|
|
844
|
-
// and after `restore()` for the reason that call records: the terminal must
|
|
845
|
-
// stop reporting first, and anything already in flight is still consumed by
|
|
846
|
-
// a live filter.
|
|
847
|
-
disposeSelection();
|
|
848
|
-
disposeStdinFilter();
|
|
849
|
-
disposeFrameWriter();
|
|
850
|
-
controller.dispose();
|
|
851
|
-
updateService?.dispose();
|
|
852
|
-
});
|
|
808
|
+
.catch(failInteractive);
|
|
853
809
|
}
|
|
854
810
|
async function runOneShot(flags, prompt, quiet) {
|
|
855
811
|
// Headless: no App is ever rendered, so there is no human to approve a skill
|
|
@@ -938,7 +894,6 @@ const CONFIG_SET_KEYS = new Set([
|
|
|
938
894
|
'confirmTools',
|
|
939
895
|
'toolTimeoutMs',
|
|
940
896
|
'idleTimeoutMs',
|
|
941
|
-
'fullscreen',
|
|
942
897
|
'exitTranscript',
|
|
943
898
|
'transcriptWindow',
|
|
944
899
|
// tui-render-performance L1 / L4. Listed here AND cased in the switch
|
|
@@ -1159,9 +1114,6 @@ function runConfigSet(key, value) {
|
|
|
1159
1114
|
case 'idleTimeoutMs':
|
|
1160
1115
|
patch.idleTimeoutMs = coercePositiveInt(value, 210_000);
|
|
1161
1116
|
break;
|
|
1162
|
-
case 'fullscreen':
|
|
1163
|
-
patch.fullscreen = value === 'true' || value === '1';
|
|
1164
|
-
break;
|
|
1165
1117
|
case 'exitTranscript':
|
|
1166
1118
|
patch.exitTranscript = value === 'true' || value === '1';
|
|
1167
1119
|
break;
|
|
@@ -1450,30 +1402,16 @@ function buildProgram() {
|
|
|
1450
1402
|
.option('--tool-timeout <ms>', 'Per-tool executor ceiling (default 180000)')
|
|
1451
1403
|
.option('--idle-timeout <ms>', 'Watchdog idle timeout (auto-raised to >= tool-timeout+30s)')
|
|
1452
1404
|
.option('--theme <name>', 'auto|warm|cool|light ("dark" is an alias for "cool")')
|
|
1453
|
-
// Same tri-state shape as `--fullscreen`: declaring the positive form first
|
|
1454
|
-
// keeps the default `undefined`, so "no opinion" stays distinguishable from
|
|
1455
|
-
// an explicit choice and the config layer below can still win.
|
|
1456
1405
|
.option('--compact', 'Compact transcript density (no blank rows between turns)')
|
|
1457
1406
|
.option('--no-compact', 'Comfortable transcript density')
|
|
1458
1407
|
.option('--hints', 'Always show the composer hint row')
|
|
1459
1408
|
.option('--no-hints', 'Hide the composer hint row')
|
|
1460
|
-
// Positive form first, same tri-state reason as `--fullscreen` above.
|
|
1461
1409
|
.option('--show-thinking', 'Show the reasoning blocks the model returns')
|
|
1462
1410
|
.option('--no-show-thinking', 'Hide reasoning blocks (the default)')
|
|
1463
|
-
// Positive form first, same tri-state reason as `--fullscreen` above. The
|
|
1464
|
-
// pair matters more here than for its neighbour because this key defaults to
|
|
1465
|
-
// ON: a lone negative form would let commander synthesize `true` and
|
|
1466
|
-
// overwrite a stored `false` on every run that passed no flag at all.
|
|
1467
1411
|
.option('--live-tool-output', 'Show a running command output tail (the default)')
|
|
1468
1412
|
.option('--no-live-tool-output', 'Keep a running tool card to one line')
|
|
1469
|
-
// Positive form first, same tri-state reason as `--fullscreen` above.
|
|
1470
1413
|
.option('--mouse', 'Wheel scrolls the transcript (full-screen mode)')
|
|
1471
1414
|
.option('--no-mouse', 'Leave the mouse to the terminal (the wheel does nothing)')
|
|
1472
|
-
// Positive form FIRST, for the tri-state reason `--fullscreen` records
|
|
1473
|
-
// below: `mouseSelect` is persisted and defaults to `true`, so a lone
|
|
1474
|
-
// `--no-mouse-select` would make commander default `opts.mouseSelect` to
|
|
1475
|
-
// `true` — indistinguishable from silence, and silently overriding a stored
|
|
1476
|
-
// `false` on every run that passes no flag at all.
|
|
1477
1415
|
.option('--mouse-select', 'Drag with the mouse to select text; releasing copies it')
|
|
1478
1416
|
.option('--no-mouse-select', 'Keep wheel scrolling, but leave drag-select off')
|
|
1479
1417
|
// Positive form FIRST, same tri-state reason as `--mouse-select` above:
|
|
@@ -1482,16 +1420,7 @@ function buildProgram() {
|
|
|
1482
1420
|
// silence, and silently overriding a stored `false` on every run.
|
|
1483
1421
|
.option('--paste', 'Collapse large pastes into a placeholder (the default)')
|
|
1484
1422
|
.option('--no-paste', 'Treat pasted bytes as keystrokes (pre-0.6.3 behavior)')
|
|
1485
|
-
// Declaring `--fullscreen` BEFORE `--no-fullscreen` keeps the default
|
|
1486
|
-
// `undefined` instead of `true`, which is what makes the tri-state work:
|
|
1487
|
-
// "no opinion" must stay distinguishable from "force it".
|
|
1488
|
-
.option('--fullscreen', 'Force the full-screen TUI (overrides auto-downgrade, except non-TTY)')
|
|
1489
|
-
.option('--no-fullscreen', 'Force the inline renderer (v0.2.0 behavior)')
|
|
1490
1423
|
.option('--no-exit-transcript', 'Do not replay the session summary after exiting')
|
|
1491
|
-
// Same tri-state shape as `--fullscreen`: the positive form is declared
|
|
1492
|
-
// FIRST so the default stays `undefined` and "no opinion" remains
|
|
1493
|
-
// distinguishable from an explicit `--no-plan`, which is what lets the
|
|
1494
|
-
// config layer win when neither flag was given.
|
|
1495
1424
|
.option('--plan', 'Start the session in PLAN mode (read-only research + review)')
|
|
1496
1425
|
.option('--no-plan', 'Start the session in BUILD mode (overrides planModeDefault)')
|
|
1497
1426
|
// BOTH FORMS, positive first, and the pair is mandatory (team-subagents
|
|
@@ -724,7 +724,9 @@ const COMMANDS = [
|
|
|
724
724
|
// slash history already has. Putting this canned paragraph under the
|
|
725
725
|
// composer's up-arrow as well would be the same event recorded twice, in
|
|
726
726
|
// the more verbose of the two forms (AC-17).
|
|
727
|
-
ctx.submit(buildContinuationMessage(snapshot), {
|
|
727
|
+
ctx.submit(buildContinuationMessage(snapshot), {
|
|
728
|
+
userInitiated: false, todoPolicy: 'continue',
|
|
729
|
+
});
|
|
728
730
|
return;
|
|
729
731
|
}
|
|
730
732
|
ctx.notify('warn', `Unknown argument "${verb}" - use ` +
|
package/dist/commands/perf.d.ts
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* display would be self-defeating.
|
|
17
17
|
*/
|
|
18
18
|
import type { FrameWriterStats } from '../ui/frame-differ.js';
|
|
19
|
+
import type { ScrollbarBridge } from '../ui/scrollbar-controller.js';
|
|
19
20
|
import type { SlashCommand } from './registry.js';
|
|
20
21
|
export interface PerfSnapshot {
|
|
21
22
|
/** Governor rung, its resolved interval, and the last measured commit cost. */
|
|
@@ -33,10 +34,39 @@ export interface PerfSnapshot {
|
|
|
33
34
|
heightsEstimated: number;
|
|
34
35
|
cols: number;
|
|
35
36
|
/** Geometry. */
|
|
36
|
-
mode: 'fullscreen'
|
|
37
|
+
mode: 'fullscreen';
|
|
37
38
|
viewportRows: number;
|
|
38
39
|
offset: number;
|
|
40
|
+
/** Right-edge scrollbar diagnostics; read lazily so it reflects the live bridge. */
|
|
41
|
+
scrollbar?: ScrollbarPerf;
|
|
39
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Why the right-edge scrollbar is or is not usable, in one record
|
|
45
|
+
* (tui-scrollbar-edge-and-run-row section 3.2). "Cannot see it" and "cannot drag
|
|
46
|
+
* it" had no self-check before; each field below separates one cause.
|
|
47
|
+
*/
|
|
48
|
+
export interface ScrollbarPerf {
|
|
49
|
+
/** 1-based terminal column of the track, or `null` before the first layout. */
|
|
50
|
+
trackCol: number | null;
|
|
51
|
+
trackRows: number | null;
|
|
52
|
+
/** Mouse capture is on. Off keeps the track visible but not draggable. */
|
|
53
|
+
mouseOn: boolean;
|
|
54
|
+
/** A full frame matching the geometry was observed, so pointer hits are trusted. */
|
|
55
|
+
frameReady: boolean;
|
|
56
|
+
thumb: {
|
|
57
|
+
start: number;
|
|
58
|
+
size: number;
|
|
59
|
+
} | null;
|
|
60
|
+
unicode: boolean;
|
|
61
|
+
/** `skipped`: the differ omits CSI K on full-width rows; `n/a`: diff render is off. */
|
|
62
|
+
edgeEl: 'skipped' | 'always' | 'n/a';
|
|
63
|
+
}
|
|
64
|
+
/** Build the diagnostics record from the live bridge (called at `/perf` time). */
|
|
65
|
+
export declare function readScrollbarPerf(opts: {
|
|
66
|
+
bridge: ScrollbarBridge | undefined;
|
|
67
|
+
diffRender: boolean;
|
|
68
|
+
unicode: boolean;
|
|
69
|
+
}): ScrollbarPerf | undefined;
|
|
40
70
|
/** Called by `App`. Passing `null` detaches. */
|
|
41
71
|
export declare function setPerfSnapshotProvider(fn: (() => PerfSnapshot | null) | null): void;
|
|
42
72
|
export declare function readPerfSnapshot(): PerfSnapshot | null;
|
|
@@ -44,6 +74,6 @@ export declare function setPerfResetHook(fn: (() => void) | null): void;
|
|
|
44
74
|
/** Called by `cli.tsx` after building the writer. Passing `null` detaches. */
|
|
45
75
|
export declare function setFrameStatsProvider(fn: (() => FrameWriterStats) | null): void;
|
|
46
76
|
export declare function readFrameStats(): FrameWriterStats | null;
|
|
47
|
-
/** Format a snapshot as the six-line block documented in §5.4. */
|
|
77
|
+
/** Format a snapshot as the six-line block documented in §5.4 (plus the scrollbar line). */
|
|
48
78
|
export declare function formatPerfReport(snap: PerfSnapshot): string;
|
|
49
79
|
export declare const perfCommand: SlashCommand;
|
package/dist/commands/perf.js
CHANGED
|
@@ -16,6 +16,23 @@
|
|
|
16
16
|
* display would be self-defeating.
|
|
17
17
|
*/
|
|
18
18
|
import { clearRenderCaches, renderCacheStats } from '../ui/render-cache.js';
|
|
19
|
+
/** Build the diagnostics record from the live bridge (called at `/perf` time). */
|
|
20
|
+
export function readScrollbarPerf(opts) {
|
|
21
|
+
const { bridge, diffRender, unicode } = opts;
|
|
22
|
+
if (!bridge)
|
|
23
|
+
return undefined;
|
|
24
|
+
const geometry = bridge.geometry;
|
|
25
|
+
return {
|
|
26
|
+
trackCol: geometry?.trackCol ?? null,
|
|
27
|
+
trackRows: geometry?.trackRows ?? null,
|
|
28
|
+
mouseOn: bridge.isEnabled(),
|
|
29
|
+
frameReady: bridge.frameReady,
|
|
30
|
+
thumb: geometry?.thumb ? { start: geometry.thumb.start, size: geometry.thumb.size } : null,
|
|
31
|
+
unicode,
|
|
32
|
+
// `cli.tsx` always wires `cols` when it builds the differ.
|
|
33
|
+
edgeEl: diffRender ? 'skipped' : 'n/a',
|
|
34
|
+
};
|
|
35
|
+
}
|
|
19
36
|
/** Cleared on unmount, so `/perf` in a torn-down tree says so instead of lying. */
|
|
20
37
|
let provider = null;
|
|
21
38
|
/** Called by `App`. Passing `null` detaches. */
|
|
@@ -39,7 +56,7 @@ export function setPerfResetHook(fn) {
|
|
|
39
56
|
* render — so routing them through a 1900-line component's props would buy
|
|
40
57
|
* nothing and cost `App.tsx` plus `app.test.tsx` churn (P1-2).
|
|
41
58
|
*
|
|
42
|
-
* `null` means the writer was never built — `--no-diff-render
|
|
59
|
+
* `null` means the writer was never built — `--no-diff-render` or
|
|
43
60
|
* a non-interactive path. `/perf` says so in words, so "the flag is off" and
|
|
44
61
|
* "the writer crashed" never look alike.
|
|
45
62
|
*/
|
|
@@ -74,11 +91,23 @@ function formatWriterLine() {
|
|
|
74
91
|
`lines/frame ${ratio(stats.linesWritten, stats.framesTotal)} - ` +
|
|
75
92
|
`bytes/frame ${bytesPerFrame} - fallbacks ${stats.fallbacks}`);
|
|
76
93
|
}
|
|
77
|
-
|
|
94
|
+
function formatScrollbarLine(bar) {
|
|
95
|
+
const thumb = bar.thumb ? `${bar.thumb.start}+${bar.thumb.size}` : 'none';
|
|
96
|
+
const line = `scrollbar col=${bar.trackCol ?? '-'} rows=${bar.trackRows ?? '-'} ` +
|
|
97
|
+
`mouse=${bar.mouseOn ? 'on' : 'off'} frameReady=${bar.frameReady ? 'yes' : 'no'} ` +
|
|
98
|
+
`thumb=${thumb} glyphs=${bar.unicode ? 'unicode' : 'ascii'} edge-el=${bar.edgeEl}`;
|
|
99
|
+
if (!bar.mouseOn || bar.frameReady)
|
|
100
|
+
return line;
|
|
101
|
+
const size = bar.trackCol !== null && bar.trackRows !== null
|
|
102
|
+
? `${bar.trackCol}x${bar.trackRows + 4}` : 'the terminal size';
|
|
103
|
+
return `${line}\n waiting for a full frame matching ${size}`;
|
|
104
|
+
}
|
|
105
|
+
/** Format a snapshot as the six-line block documented in §5.4 (plus the scrollbar line). */
|
|
78
106
|
export function formatPerfReport(snap) {
|
|
79
107
|
const caches = renderCacheStats();
|
|
80
108
|
const eco = snap.rung > 0 ? ' - eco' : '';
|
|
81
109
|
const governor = snap.governorEnabled ? '' : ' - governor off';
|
|
110
|
+
const scrollbar = snap.scrollbar ? [formatScrollbarLine(snap.scrollbar)] : [];
|
|
82
111
|
return [
|
|
83
112
|
`render rung ${snap.rung} - interval ${snap.intervalMs}ms - last commit ` +
|
|
84
113
|
`${Math.round(snap.lastCommitMs)}ms${eco}${governor}`,
|
|
@@ -91,6 +120,7 @@ export function formatPerfReport(snap) {
|
|
|
91
120
|
`lines ${caches.lineEntries}`,
|
|
92
121
|
`mode ${snap.mode} - viewport ${snap.viewportRows} rows - offset ${snap.offset}`,
|
|
93
122
|
formatWriterLine(),
|
|
123
|
+
...scrollbar,
|
|
94
124
|
].join('\n');
|
|
95
125
|
}
|
|
96
126
|
export const perfCommand = {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Slash-command registry + dispatcher (spec §5.2). A leading `/` in the prompt
|
|
3
3
|
* input is routed here instead of to the agent; `//` is the literal-`/` escape.
|
|
4
4
|
*/
|
|
5
|
+
import type { SubmitMessageOptions } from '../agent/prompt-options.js';
|
|
5
6
|
import type { AgentController } from '../agent/controller.js';
|
|
6
7
|
import type { AgentMode } from '../agent/agent-mode.js';
|
|
7
8
|
import type { NoticeLevel, Overlay, ToastLevel, ViewAction, ViewState } from '../agent/reducer.js';
|
|
@@ -38,9 +39,7 @@ export interface CommandContext {
|
|
|
38
39
|
* (P1-5): `builtins.ts` reaches `submitMessage` only through here, so
|
|
39
40
|
* `/todo continue` cannot opt out of prompt history without it.
|
|
40
41
|
*/
|
|
41
|
-
submit: (text: string, opts?:
|
|
42
|
-
userInitiated?: boolean;
|
|
43
|
-
}) => void;
|
|
42
|
+
submit: (text: string, opts?: SubmitMessageOptions) => void;
|
|
44
43
|
/**
|
|
45
44
|
* The live auto-continue budget, for `/todo status`'s `(2/25 used)` clause
|
|
46
45
|
* (todo-plan-followthrough §4.3).
|
|
@@ -90,7 +89,7 @@ export interface CommandContext {
|
|
|
90
89
|
/**
|
|
91
90
|
* Mouse capture, for `/mouse` (tui-selection-and-scroll-follow §6.2 / G3).
|
|
92
91
|
*
|
|
93
|
-
* OPTIONAL, and legitimately absent:
|
|
92
|
+
* OPTIONAL, and legitimately absent: `--no-mouse`, a non-TTY, a
|
|
94
93
|
* Windows console that cannot deliver reports, and every test that builds this
|
|
95
94
|
* context by literal. `/mouse` has to have something honest to say when it is
|
|
96
95
|
* missing, which is the same reason `update` above it is optional.
|
|
@@ -102,7 +101,7 @@ export interface CommandContext {
|
|
|
102
101
|
* NOT `process.stdout.write`. In full-screen mode stdout is a `Proxy` in front
|
|
103
102
|
* of the frame differ, and an unrecognised chunk there is counted as a foreign
|
|
104
103
|
* write and prints a diagnostic at the user the first time they copy anything
|
|
105
|
-
* (P1-6). Absent
|
|
104
|
+
* (P1-6). Absent with `--no-diff-render`, where the real
|
|
106
105
|
* stream is handed over directly.
|
|
107
106
|
*/
|
|
108
107
|
writeForeign?: (text: string) => void;
|