@aragon-agent/cli 0.6.28 → 0.6.31
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 +2123 -1975
- package/README.md +81 -14
- package/dist/agent/project-guidance-prompt.js +20 -20
- package/dist/agent/reducer.js +47 -2
- package/dist/cli.js +169 -56
- package/dist/commands/builtins.js +4 -0
- package/dist/commands/perf.d.ts +25 -1
- package/dist/commands/perf.js +26 -2
- package/dist/commands/registry.d.ts +6 -0
- package/dist/config/env.js +4 -0
- package/dist/config/load.d.ts +3 -1
- package/dist/config/load.js +12 -1
- package/dist/config/schema.d.ts +27 -0
- package/dist/config/schema.js +9 -0
- package/dist/config/ui-state.d.ts +9 -0
- package/dist/config/ui-state.js +9 -0
- package/dist/input/limits.d.ts +20 -0
- package/dist/input/limits.js +20 -0
- package/dist/input/stdin-filter.js +68 -5
- package/dist/ui/App.d.ts +11 -1
- package/dist/ui/App.js +155 -67
- package/dist/ui/AutocompletePopup.js +1 -1
- package/dist/ui/Header.js +7 -2
- package/dist/ui/Markdown.js +30 -3
- package/dist/ui/PromptInput.js +41 -3
- package/dist/ui/ScrollbackShell.d.ts +28 -0
- package/dist/ui/ScrollbackShell.js +10 -0
- package/dist/ui/Transcript.js +7 -0
- package/dist/ui/frame-differ.d.ts +12 -0
- package/dist/ui/layout/Gutter.js +1 -1
- package/dist/ui/layout/OverlayFrame.d.ts +15 -0
- package/dist/ui/layout/OverlayFrame.js +18 -0
- package/dist/ui/layout/ScrollIndicator.js +10 -3
- package/dist/ui/layout/budget.d.ts +15 -0
- package/dist/ui/layout/budget.js +15 -0
- package/dist/ui/layout/header-hints.d.ts +9 -0
- package/dist/ui/layout/header-hints.js +14 -1
- package/dist/ui/layout/status-layout.js +63 -2
- package/dist/ui/markdown-blocks.d.ts +26 -5
- package/dist/ui/markdown-blocks.js +110 -33
- package/dist/ui/overlays/HelpOverlay.d.ts +1 -1
- package/dist/ui/overlays/HelpOverlay.js +21 -3
- package/dist/ui/screen.d.ts +16 -0
- package/dist/ui/screen.js +32 -0
- package/dist/ui/scrollback/budget.d.ts +43 -0
- package/dist/ui/scrollback/budget.js +44 -0
- package/dist/ui/scrollback/drain.d.ts +32 -0
- package/dist/ui/scrollback/drain.js +64 -0
- package/dist/ui/scrollback/exit-summary.d.ts +23 -0
- package/dist/ui/scrollback/exit-summary.js +25 -0
- package/dist/ui/scrollback/limits.d.ts +25 -0
- package/dist/ui/scrollback/limits.js +25 -0
- package/dist/ui/scrollback/live-preview.d.ts +26 -0
- package/dist/ui/scrollback/live-preview.js +106 -0
- package/dist/ui/scrollback/renderer.d.ts +38 -0
- package/dist/ui/scrollback/renderer.js +158 -0
- package/dist/ui/scrollback/scrollback-writer.d.ts +74 -0
- package/dist/ui/scrollback/scrollback-writer.js +263 -0
- package/dist/ui/scrollback/settled-boundary.d.ts +42 -0
- package/dist/ui/scrollback/settled-boundary.js +106 -0
- package/dist/ui/scrollback/use-scrollback-view.d.ts +92 -0
- package/dist/ui/scrollback/use-scrollback-view.js +263 -0
- package/dist/ui/scrollback/writer-shapes.d.ts +61 -0
- package/dist/ui/scrollback/writer-shapes.js +114 -0
- package/dist/ui/stdout-frame-writer.js +12 -2
- package/dist/ui/terminal-plan.d.ts +52 -0
- package/dist/ui/terminal-plan.js +49 -0
- package/dist/ui/transcript-text.d.ts +11 -0
- package/dist/ui/transcript-text.js +21 -7
- package/dist/ui/use-startup-notices.d.ts +12 -1
- package/dist/ui/use-startup-notices.js +28 -2
- package/dist/ui/use-wheel-routing.d.ts +10 -7
- package/dist/ui/use-wheel-routing.js +11 -6
- package/package.json +2 -2
- package/skills/project-indexer/SKILL.md +163 -163
- package/skills/project-indexer/templates/cleancode-overrides.md +46 -46
- package/skills/project-indexer/templates/cleancode-template.md +94 -94
- package/skills/project-indexer/templates/config-template.md +69 -69
- package/skills/project-indexer/templates/index-template.md +129 -129
- package/skills/skill-creator/SKILL.md +96 -96
|
@@ -84,6 +84,12 @@ export interface UiState {
|
|
|
84
84
|
* older file, which is the behaviour wanted here anyway.
|
|
85
85
|
*/
|
|
86
86
|
vtInputNoticeVersion: number;
|
|
87
|
+
/**
|
|
88
|
+
* Which revision of the "scrolling now uses your terminal" notice this user has
|
|
89
|
+
* been shown (`0` = none; tui-native-terminal-scroll D12). A scalar beside the
|
|
90
|
+
* others for the same reasons the two above record, and NOT a schema bump.
|
|
91
|
+
*/
|
|
92
|
+
scrollModeNoticeVersion: number;
|
|
87
93
|
}
|
|
88
94
|
/** Read the state, caching it for the life of the process. Never throws. */
|
|
89
95
|
export declare function loadUiState(): UiState;
|
|
@@ -98,6 +104,9 @@ export declare function setMouseNoticeVersion(version: number): void;
|
|
|
98
104
|
/** `0` on a file written before the field existed — see the field's comment. */
|
|
99
105
|
export declare function getVtInputNoticeVersion(): number;
|
|
100
106
|
export declare function setVtInputNoticeVersion(version: number): void;
|
|
107
|
+
/** `0` on a file written before the field existed. */
|
|
108
|
+
export declare function getScrollModeNoticeVersion(): number;
|
|
109
|
+
export declare function setScrollModeNoticeVersion(version: number): void;
|
|
101
110
|
/**
|
|
102
111
|
* Migration-only: fill in fields that are still at their default, and never
|
|
103
112
|
* overwrite a value already here. A second import — after a half-finished
|
package/dist/config/ui-state.js
CHANGED
|
@@ -37,6 +37,7 @@ const DEFAULT_UI_STATE = {
|
|
|
37
37
|
mouseNoticeSeen: false,
|
|
38
38
|
mouseNoticeVersion: 0,
|
|
39
39
|
vtInputNoticeVersion: 0,
|
|
40
|
+
scrollModeNoticeVersion: 0,
|
|
40
41
|
};
|
|
41
42
|
let memory = null;
|
|
42
43
|
/** Non-negative integer or `fallback`; shared by both numeric fields. */
|
|
@@ -69,6 +70,7 @@ function readFromDisk() {
|
|
|
69
70
|
: DEFAULT_UI_STATE.mouseNoticeSeen,
|
|
70
71
|
mouseNoticeVersion: coerceCount(parsed.mouseNoticeVersion, DEFAULT_UI_STATE.mouseNoticeVersion),
|
|
71
72
|
vtInputNoticeVersion: coerceCount(parsed.vtInputNoticeVersion, DEFAULT_UI_STATE.vtInputNoticeVersion),
|
|
73
|
+
scrollModeNoticeVersion: coerceCount(parsed.scrollModeNoticeVersion, DEFAULT_UI_STATE.scrollModeNoticeVersion),
|
|
72
74
|
};
|
|
73
75
|
}
|
|
74
76
|
catch {
|
|
@@ -142,6 +144,13 @@ export function getVtInputNoticeVersion() {
|
|
|
142
144
|
export function setVtInputNoticeVersion(version) {
|
|
143
145
|
update({ vtInputNoticeVersion: version });
|
|
144
146
|
}
|
|
147
|
+
/** `0` on a file written before the field existed. */
|
|
148
|
+
export function getScrollModeNoticeVersion() {
|
|
149
|
+
return loadUiState().scrollModeNoticeVersion;
|
|
150
|
+
}
|
|
151
|
+
export function setScrollModeNoticeVersion(version) {
|
|
152
|
+
update({ scrollModeNoticeVersion: version });
|
|
153
|
+
}
|
|
145
154
|
/**
|
|
146
155
|
* Migration-only: fill in fields that are still at their default, and never
|
|
147
156
|
* overwrite a value already here. A second import — after a half-finished
|
package/dist/input/limits.d.ts
CHANGED
|
@@ -39,6 +39,26 @@ export declare const PASTE_BURST_MS = 15;
|
|
|
39
39
|
* which is the rule `MAX_PENDING_MOUSE_CHARS` already encodes for mouse reports.
|
|
40
40
|
*/
|
|
41
41
|
export declare const PASTE_ASSEMBLY_MAX_MS = 2000;
|
|
42
|
+
/**
|
|
43
|
+
* How long a paste-marker head that was ALREADY released to the pass-through as
|
|
44
|
+
* ordinary text may still be recognised as the head of a marker (defect 2: the
|
|
45
|
+
* torn-marker rescue in `stdin-filter.ts`).
|
|
46
|
+
*
|
|
47
|
+
* STRUCTURAL, NOT POLICY: no config key exposes it, because it bounds what the
|
|
48
|
+
* byte stream can physically do -- a marker split across two reads -- and not
|
|
49
|
+
* what a user prefers. What it does NOT do is delay a keystroke: the 12 ms
|
|
50
|
+
* pending-flush window in `stdin-filter.ts` still releases a lone ESC exactly as
|
|
51
|
+
* before, and this window only governs whether that release may be spliced back
|
|
52
|
+
* in front of the next chunk so an assembled marker is consumed, not typed.
|
|
53
|
+
*
|
|
54
|
+
* THE FALSE POSITIVE IS THE TRADE-OFF, AND IT IS ACCEPTED. Firing the rescue
|
|
55
|
+
* wrongly needs the user to press Esc and then TYPE the literal characters
|
|
56
|
+
* `[200~` or `[201~` inside this window -- a sequence no keyboard produces, and
|
|
57
|
+
* at worst five characters of lost typing. Paying that buys a fix for visible
|
|
58
|
+
* `[200~` / `[201~` garbage in the draft, which is the failure the whole paste
|
|
59
|
+
* feature exists to prevent.
|
|
60
|
+
*/
|
|
61
|
+
export declare const PASTE_MARKER_RESCAN_TTL_MS = 250;
|
|
42
62
|
/**
|
|
43
63
|
* The inline frame the filter writes into the stream Ink reads (D-3 / I-5).
|
|
44
64
|
*
|
package/dist/input/limits.js
CHANGED
|
@@ -39,6 +39,26 @@ export const PASTE_BURST_MS = 15;
|
|
|
39
39
|
* which is the rule `MAX_PENDING_MOUSE_CHARS` already encodes for mouse reports.
|
|
40
40
|
*/
|
|
41
41
|
export const PASTE_ASSEMBLY_MAX_MS = 2_000;
|
|
42
|
+
/**
|
|
43
|
+
* How long a paste-marker head that was ALREADY released to the pass-through as
|
|
44
|
+
* ordinary text may still be recognised as the head of a marker (defect 2: the
|
|
45
|
+
* torn-marker rescue in `stdin-filter.ts`).
|
|
46
|
+
*
|
|
47
|
+
* STRUCTURAL, NOT POLICY: no config key exposes it, because it bounds what the
|
|
48
|
+
* byte stream can physically do -- a marker split across two reads -- and not
|
|
49
|
+
* what a user prefers. What it does NOT do is delay a keystroke: the 12 ms
|
|
50
|
+
* pending-flush window in `stdin-filter.ts` still releases a lone ESC exactly as
|
|
51
|
+
* before, and this window only governs whether that release may be spliced back
|
|
52
|
+
* in front of the next chunk so an assembled marker is consumed, not typed.
|
|
53
|
+
*
|
|
54
|
+
* THE FALSE POSITIVE IS THE TRADE-OFF, AND IT IS ACCEPTED. Firing the rescue
|
|
55
|
+
* wrongly needs the user to press Esc and then TYPE the literal characters
|
|
56
|
+
* `[200~` or `[201~` inside this window -- a sequence no keyboard produces, and
|
|
57
|
+
* at worst five characters of lost typing. Paying that buys a fix for visible
|
|
58
|
+
* `[200~` / `[201~` garbage in the draft, which is the failure the whole paste
|
|
59
|
+
* feature exists to prevent.
|
|
60
|
+
*/
|
|
61
|
+
export const PASTE_MARKER_RESCAN_TTL_MS = 250;
|
|
42
62
|
/**
|
|
43
63
|
* The inline frame the filter writes into the stream Ink reads (D-3 / I-5).
|
|
44
64
|
*
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
*/
|
|
34
34
|
import { PassThrough } from 'node:stream';
|
|
35
35
|
import { MAX_PENDING_MOUSE_CHARS, splitMouseEvents, isMousePrefix, } from './mouse-events.js';
|
|
36
|
-
import { ENTER_NEWLINE_FRAME, INDEX_KEY_FRAME, PASTE_ASSEMBLY_MAX_MS, PASTE_BURST_MS, PASTE_CLOSE, PASTE_MAX_BYTES, PASTE_OPEN, formatPasteSize, } from './limits.js';
|
|
37
|
-
import { PASTE_BEGIN_MARK, PASTE_END_MARK, classifyChunk, sanitisePaste, trailingPastePrefixLength, } from './paste-parse.js';
|
|
36
|
+
import { ENTER_NEWLINE_FRAME, INDEX_KEY_FRAME, PASTE_ASSEMBLY_MAX_MS, PASTE_BURST_MS, PASTE_CLOSE, PASTE_MARKER_RESCAN_TTL_MS, PASTE_MAX_BYTES, PASTE_OPEN, formatPasteSize, } from './limits.js';
|
|
37
|
+
import { PASTE_BEGIN_MARK, PASTE_END_MARK, classifyChunk, isPastePrefix, sanitisePaste, trailingPastePrefixLength, } from './paste-parse.js';
|
|
38
38
|
import { hasPasteOnlySequence } from './control-sequences.js';
|
|
39
39
|
import { splitEnterSequences, trailingEnterPrefixLength } from './enter-sequences.js';
|
|
40
40
|
import { splitCsiUKeys, trailingCsiUPrefixLength, translateCsiUKey } from './csiu-keys.js';
|
|
@@ -97,6 +97,23 @@ export function createStdinFilter(real, features, bridge, onWarn) {
|
|
|
97
97
|
let paste = { kind: 'idle' };
|
|
98
98
|
let discardBracketed = false;
|
|
99
99
|
let outputBatch = null;
|
|
100
|
+
/**
|
|
101
|
+
* A marker head that has ALREADY been released to Ink, kept for one more
|
|
102
|
+
* chunk (defect 2: the torn-marker rescue in `onData`).
|
|
103
|
+
*
|
|
104
|
+
* WHY IT EXISTS. `flushPending` releases a lone ESC after 12 ms because the
|
|
105
|
+
* Esc key must not be held hostage. When the split point lands inside a paste
|
|
106
|
+
* marker -- `ESC` in one read and `[200~hello` in the next, which is ordinary
|
|
107
|
+
* for a Windows console, SSH or tmux under load -- `feed` can no longer find
|
|
108
|
+
* the marker and `[200~` is typed into the draft. Nothing downstream can undo
|
|
109
|
+
* that: the marker has to be recognised BEFORE its bytes become text.
|
|
110
|
+
*
|
|
111
|
+
* WHAT IT CANNOT DO, STATED HONESTLY. The released ESC has already reached Ink
|
|
112
|
+
* as an Esc key press and this filter cannot retract a write. The rescue
|
|
113
|
+
* guarantees the marker CHARACTERS never become draft text; it does NOT undo
|
|
114
|
+
* that keypress (an armed interrupt gesture, a dismissed popup).
|
|
115
|
+
*/
|
|
116
|
+
let rescuedPrefix = null;
|
|
100
117
|
const writeInput = (text) => {
|
|
101
118
|
if (disposed || outputBatch?.refused)
|
|
102
119
|
return;
|
|
@@ -417,6 +434,18 @@ export function createStdinFilter(real, features, bridge, onWarn) {
|
|
|
417
434
|
rest = rest.slice(first + PASTE_BEGIN_MARK.length);
|
|
418
435
|
}
|
|
419
436
|
};
|
|
437
|
+
/**
|
|
438
|
+
* Hand a held tail to the ordinary text path, remembering it when it could
|
|
439
|
+
* still be the head of a paste marker.
|
|
440
|
+
*
|
|
441
|
+
* THE TAIL IS RELEASED EXACTLY AS BEFORE. Remembering it costs no latency: a
|
|
442
|
+
* real Esc keypress still reaches Ink after `PENDING_FLUSH_MS`, and the
|
|
443
|
+
* remembered head is only ever consulted for the NEXT chunk.
|
|
444
|
+
*/
|
|
445
|
+
const flushTailAsText = (text) => {
|
|
446
|
+
rescuedPrefix = isPastePrefix(text) ? { text, at: Date.now() } : null;
|
|
447
|
+
handleOutsidePaste(text, false);
|
|
448
|
+
};
|
|
420
449
|
const flushPending = () => {
|
|
421
450
|
flushTimer = clearTimer(flushTimer);
|
|
422
451
|
if (pending.length === 0 || disposed)
|
|
@@ -425,20 +454,54 @@ export function createStdinFilter(real, features, bridge, onWarn) {
|
|
|
425
454
|
pending = '';
|
|
426
455
|
// A tail that never completed is ordinary text; it goes through the same
|
|
427
456
|
// scan so a burst already open keeps coalescing it.
|
|
428
|
-
|
|
457
|
+
flushTailAsText(text);
|
|
458
|
+
};
|
|
459
|
+
/**
|
|
460
|
+
* One-shot rescue of a marker whose ESC was already flushed as text.
|
|
461
|
+
*
|
|
462
|
+
* Returns the string `feed` should scan -- the remembered head followed by
|
|
463
|
+
* this chunk -- when the chunk COMPLETES that head into a whole marker, and
|
|
464
|
+
* `null` otherwise. On `null` the chunk travels the normal path: the head has
|
|
465
|
+
* already been emitted, so re-feeding it would type its bytes twice, and
|
|
466
|
+
* holding the chunk instead would swallow characters the user really typed.
|
|
467
|
+
*
|
|
468
|
+
* STARTS WITH, NOT CONTAINS. A marker completed at the junction starts at
|
|
469
|
+
* index 0 of the joined text; one starting anywhere later lies wholly inside
|
|
470
|
+
* `chunk` (the head holds an ESC at index 0 and nothing else that could begin
|
|
471
|
+
* a marker), and `feed` finds that on its own. Only the junction case needs
|
|
472
|
+
* re-scanning, and it is the only case where prepending cannot duplicate a
|
|
473
|
+
* byte that Ink has already seen.
|
|
474
|
+
*
|
|
475
|
+
* ONE-SHOT BY CONSTRUCTION: the remembered head is cleared here on every call,
|
|
476
|
+
* whether or not the rescue fired, so at most one chunk is ever re-scanned
|
|
477
|
+
* against it and no stale head can outlive its `PASTE_MARKER_RESCAN_TTL_MS`.
|
|
478
|
+
*/
|
|
479
|
+
const takeRescuedPrefix = (carried, chunk) => {
|
|
480
|
+
const rescue = rescuedPrefix;
|
|
481
|
+
rescuedPrefix = null;
|
|
482
|
+
if (rescue === null || carried.length > 0 || !features.paste)
|
|
483
|
+
return null;
|
|
484
|
+
if (Date.now() - rescue.at > PASTE_MARKER_RESCAN_TTL_MS)
|
|
485
|
+
return null;
|
|
486
|
+
const joined = rescue.text + chunk;
|
|
487
|
+
const completes = joined.startsWith(PASTE_BEGIN_MARK) || joined.startsWith(PASTE_END_MARK);
|
|
488
|
+
return completes ? joined : null;
|
|
429
489
|
};
|
|
430
490
|
const onData = (chunk) => {
|
|
431
491
|
if (disposed || finished)
|
|
432
492
|
return;
|
|
433
493
|
flushTimer = clearTimer(flushTimer);
|
|
434
494
|
const decoded = typeof chunk === 'string' ? chunk : chunk.toString('utf8');
|
|
495
|
+
// Stripped on BOTH paths, because a NUL in a payload must never be able to
|
|
496
|
+
// forge a `PASTE_OPEN` frame boundary.
|
|
497
|
+
const cleaned = decoded.replace(/\u0000/g, '');
|
|
435
498
|
const carried = pending;
|
|
436
499
|
pending = '';
|
|
437
500
|
// A rejected paste must not leave earlier text or a later Enter executable.
|
|
438
501
|
const batch = { chunks: [], refused: false };
|
|
439
502
|
outputBatch = batch;
|
|
440
503
|
try {
|
|
441
|
-
feed(carried
|
|
504
|
+
feed(takeRescuedPrefix(carried, cleaned) ?? (carried + cleaned));
|
|
442
505
|
}
|
|
443
506
|
finally {
|
|
444
507
|
outputBatch = null;
|
|
@@ -468,7 +531,7 @@ export function createStdinFilter(real, features, bridge, onWarn) {
|
|
|
468
531
|
// input: past the ceiling the tail becomes ordinary text.
|
|
469
532
|
const text = pending;
|
|
470
533
|
pending = '';
|
|
471
|
-
|
|
534
|
+
flushTailAsText(text);
|
|
472
535
|
return;
|
|
473
536
|
}
|
|
474
537
|
flushTimer = arm(flushPending, PENDING_FLUSH_MS);
|
package/dist/ui/App.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ import type { AgentController } from '../agent/controller.js';
|
|
|
10
10
|
import type { ConfirmRequest } from '../tools/index.js';
|
|
11
11
|
import { type Entry, type Overlay, type ToastLevel } from '../agent/reducer.js';
|
|
12
12
|
import type { UpdateBridge } from '../update/types.js';
|
|
13
|
+
import { type ScrollbackBridge } from './scrollback/use-scrollback-view.js';
|
|
14
|
+
import type { ScrollMode } from '../config/schema.js';
|
|
13
15
|
import type { MouseSource } from '../input/stdin-filter.js';
|
|
14
16
|
import { type PasteBridge } from '../input/limits.js';
|
|
15
17
|
import type { CheckpointResult } from '../session/checkpoint.js';
|
|
@@ -86,6 +88,14 @@ export interface AppProps {
|
|
|
86
88
|
*/
|
|
87
89
|
pasteBridge?: PasteBridge;
|
|
88
90
|
terminal?: TerminalBridge;
|
|
91
|
+
/**
|
|
92
|
+
* `'native'` prints settled conversation into the terminal's own scrollback and
|
|
93
|
+
* renders only a bounded live region (tui-native-terminal-scroll). DEFAULTS TO
|
|
94
|
+
* `'app'` so every existing render test and call site is untouched (I-S9).
|
|
95
|
+
*/
|
|
96
|
+
scrollMode?: ScrollMode;
|
|
97
|
+
/** The writer / feed channel `cli.tsx` shares with the tree; required for `'native'`. */
|
|
98
|
+
scrollback?: ScrollbackBridge;
|
|
89
99
|
/**
|
|
90
100
|
* The transcript to seed the first frame with, from a resumed session
|
|
91
101
|
* (tui-session-id-resume 3.12).
|
|
@@ -148,4 +158,4 @@ export interface TerminalBridge {
|
|
|
148
158
|
writeForeign?: (text: string) => void;
|
|
149
159
|
selection?: SelectionBridge;
|
|
150
160
|
}
|
|
151
|
-
export declare function App({ controller, version, initialOverlay, initialPrompt, confirmBridge, humanInputBridge, mouseSource, vtInputWarning, updateBridge, pasteBridge, terminal, initialEntries, session, sessionBridge, sessionNotice, }: AppProps): React.ReactElement;
|
|
161
|
+
export declare function App({ controller, version, initialOverlay, initialPrompt, confirmBridge, humanInputBridge, mouseSource, vtInputWarning, updateBridge, pasteBridge, terminal, scrollMode, scrollback, initialEntries, session, sessionBridge, sessionNotice, }: AppProps): React.ReactElement;
|