@aragon-agent/cli 0.6.11 → 0.6.13
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 +10 -1
- package/README.md +97 -20
- package/dist/agent/controller.d.ts +38 -3
- package/dist/agent/controller.js +151 -60
- package/dist/agent/model-profile-settings.d.ts +31 -0
- package/dist/agent/model-profile-settings.js +199 -0
- package/dist/agent/queued-messages.d.ts +20 -0
- package/dist/agent/queued-messages.js +46 -0
- package/dist/agent/reducer.d.ts +21 -2
- package/dist/agent/reducer.js +65 -37
- package/dist/cli.js +15 -1
- package/dist/commands/builtins.js +83 -42
- package/dist/commands/queue-command.d.ts +3 -0
- package/dist/commands/queue-command.js +5 -0
- package/dist/commands/registry.d.ts +3 -0
- package/dist/compaction/child.d.ts +4 -2
- package/dist/compaction/child.js +4 -1
- package/dist/compaction/compactor.d.ts +7 -2
- package/dist/compaction/compactor.js +13 -11
- package/dist/compaction/context-command.js +5 -0
- package/dist/compaction/wiring.d.ts +4 -3
- package/dist/compaction/wiring.js +6 -1
- package/dist/config/cli-commands.d.ts +2 -0
- package/dist/config/cli-commands.js +16 -4
- package/dist/config/load.d.ts +2 -1
- package/dist/config/load.js +15 -8
- package/dist/config/model-profile-resolution.d.ts +20 -0
- package/dist/config/model-profile-resolution.js +81 -0
- package/dist/config/model-profile-store.d.ts +67 -0
- package/dist/config/model-profile-store.js +153 -0
- package/dist/config/model-profiles.d.ts +44 -0
- package/dist/config/model-profiles.js +121 -0
- package/dist/config/schema.d.ts +9 -0
- package/dist/config/store.d.ts +11 -1
- package/dist/config/store.js +43 -27
- package/dist/fast/resolve.d.ts +2 -1
- package/dist/fast/resolve.js +6 -4
- package/dist/fast/wiring.d.ts +5 -3
- package/dist/fast/wiring.js +8 -4
- package/dist/input/enter-sequences.d.ts +11 -10
- package/dist/input/enter-sequences.js +29 -27
- package/dist/input/stdin-filter.js +93 -28
- package/dist/logging/install.d.ts +3 -0
- package/dist/logging/install.js +4 -2
- package/dist/logging/secret-registry.d.ts +2 -0
- package/dist/logging/secret-registry.js +18 -0
- package/dist/session/persist.d.ts +6 -7
- package/dist/session/persist.js +6 -9
- package/dist/session/store.d.ts +4 -1
- package/dist/session/validate-session.d.ts +3 -0
- package/dist/session/validate-session.js +207 -0
- package/dist/team/runtime.d.ts +2 -1
- package/dist/team/subagent.d.ts +3 -1
- package/dist/team/subagent.js +3 -1
- package/dist/ui/ActivityLine.d.ts +6 -70
- package/dist/ui/ActivityLine.js +12 -34
- package/dist/ui/App.d.ts +1 -0
- package/dist/ui/App.js +161 -226
- package/dist/ui/BottomStatusRow.d.ts +6 -86
- package/dist/ui/BottomStatusRow.js +11 -26
- package/dist/ui/Composer.d.ts +18 -84
- package/dist/ui/Composer.js +9 -100
- package/dist/ui/PromptInput.d.ts +6 -2
- package/dist/ui/PromptInput.js +91 -105
- package/dist/ui/QueuePanel.d.ts +8 -0
- package/dist/ui/QueuePanel.js +9 -0
- package/dist/ui/QueueStatusRow.d.ts +24 -0
- package/dist/ui/QueueStatusRow.js +51 -0
- package/dist/ui/StatusBar.d.ts +15 -149
- package/dist/ui/StatusBar.js +27 -112
- package/dist/ui/clipboard-task.d.ts +29 -0
- package/dist/ui/clipboard-task.js +66 -0
- package/dist/ui/clipboard.d.ts +29 -50
- package/dist/ui/clipboard.js +141 -75
- package/dist/ui/composer-input.d.ts +28 -0
- package/dist/ui/composer-input.js +46 -0
- package/dist/ui/composer-limits.d.ts +3 -4
- package/dist/ui/composer-limits.js +4 -9
- package/dist/ui/composer-rows.d.ts +1 -1
- package/dist/ui/composer-rows.js +24 -137
- package/dist/ui/editor-navigation.d.ts +45 -0
- package/dist/ui/editor-navigation.js +85 -0
- package/dist/ui/editor-reducer.d.ts +9 -0
- package/dist/ui/editor-reducer.js +31 -15
- package/dist/ui/enter-frames.d.ts +15 -31
- package/dist/ui/enter-frames.js +14 -33
- package/dist/ui/interaction-copy.d.ts +77 -0
- package/dist/ui/interaction-copy.js +40 -0
- package/dist/ui/interaction-hints.d.ts +18 -0
- package/dist/ui/interaction-hints.js +92 -0
- package/dist/ui/layout/budget.js +1 -1
- package/dist/ui/layout/queue-layout.d.ts +19 -0
- package/dist/ui/layout/queue-layout.js +26 -0
- package/dist/ui/layout/status-layout.d.ts +55 -0
- package/dist/ui/layout/status-layout.js +109 -0
- package/dist/ui/layout/todo-layout.d.ts +1 -0
- package/dist/ui/layout/todo-layout.js +1 -1
- package/dist/ui/overlays/HelpOverlay.js +11 -3
- package/dist/ui/overlays/ModelProfileEditor.d.ts +12 -0
- package/dist/ui/overlays/ModelProfileEditor.js +103 -0
- package/dist/ui/overlays/ModelProfilePicker.d.ts +35 -0
- package/dist/ui/overlays/ModelProfilePicker.js +167 -0
- package/dist/ui/overlays/QueueOverlay.d.ts +22 -0
- package/dist/ui/overlays/QueueOverlay.js +79 -0
- package/dist/ui/overlays/SettingsScreen.d.ts +9 -2
- package/dist/ui/overlays/SettingsScreen.js +347 -3
- package/dist/ui/overlays/model-profile-state.d.ts +74 -0
- package/dist/ui/overlays/model-profile-state.js +119 -0
- package/dist/ui/run-status-row.d.ts +7 -19
- package/dist/ui/run-status-row.js +2 -18
- package/dist/ui/selection/selection-controller.d.ts +2 -2
- package/dist/ui/selection/selection-controller.js +58 -20
- package/dist/ui/transcript-text.js +20 -12
- package/dist/ui/use-wheel-routing.js +2 -1
- package/package.json +2 -2
|
@@ -1,95 +1,15 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The bottom status row — the one already-budgeted row above the composer, now
|
|
3
|
-
* shared by two occupants (agent-activity-presentation §3.2.3 / D-17 / P0-1).
|
|
4
|
-
*
|
|
5
|
-
* THE ACTIVITY LINE IS NOT A NEW `AppShell` SLOT, AND THIS FILE IS WHY.
|
|
6
|
-
*
|
|
7
|
-
* `viewportRows()` (`layout/budget.ts`) is the ONE function that says how many
|
|
8
|
-
* rows the viewport gets, and it enumerates exactly `header + toast + composer +
|
|
9
|
-
* status`. In full-screen `AppShell`'s root is `height={frameHeight(rows)}` with
|
|
10
|
-
* `overflow="hidden"`, the bottom chrome box is `flexShrink={0}` and the viewport
|
|
11
|
-
* box is its only `flexShrink={1}` child — so an extra chrome row does not make
|
|
12
|
-
* the frame taller, Yoga takes the row out of the TRANSCRIPT. Meanwhile
|
|
13
|
-
* `viewportRows()` keeps returning the old number to `ScrollViewport`,
|
|
14
|
-
* `selectWindow`'s spacers, `overlayMaxRows`, `popupMaxRows` and `buildTodoRailLayout`.
|
|
15
|
-
* The transcript would draw one row shorter than every consumer believes, ONLY
|
|
16
|
-
* WHILE A RUN IS IN FLIGHT, so the layout would shift on submit and shift back on
|
|
17
|
-
* `agent_end`.
|
|
18
|
-
*
|
|
19
|
-
* `ToastStack.tsx` already legislates this for itself: *"in `fullscreen` this
|
|
20
|
-
* ALWAYS occupies exactly one row — an empty row when there is nothing to say.
|
|
21
|
-
* Rendering conditionally would make the viewport height (and therefore the whole
|
|
22
|
-
* transcript) jump every time a toast appears or expires."* A run starts and ends
|
|
23
|
-
* far more often than a toast appears and expires, so the activity line is the
|
|
24
|
-
* STRONGER case for that rule, not an exception to it.
|
|
25
|
-
*
|
|
26
|
-
* Sharing the row means `budget.ts` and `AppShell.tsx` come out of this round
|
|
27
|
-
* UNMODIFIED — and `budget.test.ts` asserts that they did (AC-8a, DoD #8).
|
|
28
|
-
*
|
|
29
|
-
* PRECEDENCE: toast > activity > update > blank. A toast wins because a
|
|
30
|
-
* transient ack is a RESPONSE TO THE USER, and the row nearest the input belongs
|
|
31
|
-
* to it. The update line is LAST because it is the only one of the three that is
|
|
32
|
-
* PERSISTENT (cli-auto-update D-2): it loses nothing by waiting for the run to
|
|
33
|
-
* end, whereas an activity line deferred is an activity line that never renders.
|
|
34
|
-
* That also means a user who is actively working never sees the updater at all
|
|
35
|
-
* until they stop, which is the whole of the silence requirement.
|
|
36
|
-
*
|
|
37
|
-
* THE TOAST TAKES THE ROW BUT NOT THE SPINNER
|
|
38
|
-
* (`activity-spinner-vanishes-behind-toast`). "Toast wins" used to mean the
|
|
39
|
-
* activity line was never mounted while an ack was up — and because `App`
|
|
40
|
-
* suppresses the other seven animated sites for exactly as long as it BELIEVES
|
|
41
|
-
* this row is showing one, a mid-run toast left the whole frame with ZERO
|
|
42
|
-
* animations for its 2.5 s TTL, indistinguishable from a hung process. Steering
|
|
43
|
-
* and plan approval both ack mid-run, so a long run hit it repeatedly.
|
|
44
|
-
*
|
|
45
|
-
* The row still belongs to the toast: the spinner joins it as a bare glyph, no
|
|
46
|
-
* phrase and no clock, so `bottom-status-row.test.tsx`'s "the ack keeps its
|
|
47
|
-
* words" assertion and the toast's own width are both untouched. A row-direction
|
|
48
|
-
* box holding two one-row children is STILL ONE ROW, which is what the note
|
|
49
|
-
* above actually forbids disturbing — `budget.ts` and `AppShell.tsx` stay out of
|
|
50
|
-
* it. And because the glyph is mounted under precisely the condition `App` uses
|
|
51
|
-
* to suppress the others, "the line is up" and "every other site is still" agree
|
|
52
|
-
* again by construction rather than by enumeration of what can pre-empt the row.
|
|
53
|
-
*/
|
|
1
|
+
/** The permanent action/feedback row. StatusBar owns all activity animation. */
|
|
54
2
|
import React from 'react';
|
|
55
3
|
import type { Theme } from './theme.js';
|
|
56
4
|
import type { Toast } from '../agent/reducer.js';
|
|
5
|
+
import { type ActionHintInput } from './interaction-hints.js';
|
|
57
6
|
export interface BottomStatusRowProps {
|
|
58
7
|
toasts: Toast[];
|
|
59
|
-
|
|
60
|
-
activity
|
|
61
|
-
|
|
62
|
-
* The bare animated spinner (`liveSpinner`), or `null` when it would be a
|
|
63
|
-
* STATIC glyph — an ASCII terminal, or `reducedMotion`.
|
|
64
|
-
*
|
|
65
|
-
* IT IS NOT A SECOND MOUNT CONDITION. `activity` alone decides whether a life
|
|
66
|
-
* signal belongs on this row; this prop only says which FORM the signal takes,
|
|
67
|
-
* and it is read only when `activity` is non-null. So a caller cannot make the
|
|
68
|
-
* two disagree, which is the failure mode that produced this prop.
|
|
69
|
-
*
|
|
70
|
-
* `null` means PREFIX NOTHING, not "prefix the still glyph". A static `·` in
|
|
71
|
-
* front of every toast would be a visible change to ASCII terminals and to
|
|
72
|
-
* users who asked for stillness — neither of whom ever had the dead-frame bug,
|
|
73
|
-
* and both of whom must come out byte-identical.
|
|
74
|
-
*/
|
|
8
|
+
hints?: ActionHintInput;
|
|
9
|
+
/** Legacy assembly props are accepted but never mount an activity spinner. */
|
|
10
|
+
activity?: React.ReactNode;
|
|
75
11
|
activityGlyph?: React.ReactElement | null;
|
|
76
|
-
/**
|
|
77
|
-
* `<UpdateLine>` when the updater has something to say, else `null` — AND THE
|
|
78
|
-
* CALLER DECIDES WHICH (C-15 / cli-auto-update P0-1).
|
|
79
|
-
*
|
|
80
|
-
* `if (update)` below tests a REACT ELEMENT, and an element whose own render
|
|
81
|
-
* returns `null` is still truthy: that branch would emit a box of ZERO rows,
|
|
82
|
-
* the frame would keep its fixed height, Yoga would hand the row to the
|
|
83
|
-
* transcript, and `viewportRows()` would go on reporting the old number to
|
|
84
|
-
* five consumers. So `UpdateLine` is typed `React.ReactElement` and `App`
|
|
85
|
-
* gates it with `shouldRenderUpdateLine(snapshot)`.
|
|
86
|
-
*
|
|
87
|
-
* OPTIONAL, because `bottom-status-row.test.tsx` and the `App.tsx` fixtures
|
|
88
|
-
* construct this component without it and the test tree is inside
|
|
89
|
-
* `tsconfig.test.json` (`typecheck-scope.test.ts`); a required prop turns
|
|
90
|
-
* `npm run typecheck` red for a reason unrelated to this feature (P2-1).
|
|
91
|
-
*/
|
|
92
12
|
update?: React.ReactNode;
|
|
93
13
|
theme: Theme;
|
|
94
14
|
}
|
|
95
|
-
export declare function BottomStatusRow(
|
|
15
|
+
export declare function BottomStatusRow(props: BottomStatusRowProps): React.ReactElement;
|
|
@@ -1,27 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Box, Text } from 'ink';
|
|
3
|
-
import {
|
|
4
|
-
export function BottomStatusRow(
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return (
|
|
13
|
-
// `flexDirection` SPELLED OUT even though `row` is Yoga's default: it is
|
|
14
|
-
// what keeps two children on ONE row, and the header above turns on that
|
|
15
|
-
// being true. Left implicit, it reads as an oversight next to the child
|
|
16
|
-
// below that declares `column` — and "make them match" is a one-word edit
|
|
17
|
-
// that doubles the row, takes it from the transcript, and desynchronises
|
|
18
|
-
// every `viewportRows()` consumer without failing a type or a lint.
|
|
19
|
-
_jsxs(Box, { flexDirection: "row", flexShrink: 0, children: [_jsx(Box, { flexShrink: 0, children: _jsxs(Text, { color: theme.thinking, children: [' ', spinner] }) }), _jsx(Box, { flexDirection: "column", flexGrow: 1, flexShrink: 1, overflow: "hidden", children: _jsx(ToastStack, { toasts: toasts, theme: theme }) })] }));
|
|
20
|
-
}
|
|
21
|
-
if (activity)
|
|
22
|
-
return _jsx(Box, { flexShrink: 0, children: activity });
|
|
23
|
-
if (update)
|
|
24
|
-
return _jsx(Box, { flexShrink: 0, children: update });
|
|
25
|
-
// The blank full-screen row that IS the budget must keep being emitted.
|
|
26
|
-
return _jsx(ToastStack, { toasts: toasts, theme: theme });
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text, useStdout } from 'ink';
|
|
3
|
+
import { buildActionClauses } from './interaction-hints.js';
|
|
4
|
+
export function BottomStatusRow(props) {
|
|
5
|
+
const { stdout } = useStdout();
|
|
6
|
+
const toast = props.toasts[props.toasts.length - 1];
|
|
7
|
+
const hints = props.hints ?? { cols: stdout?.columns ?? 80,
|
|
8
|
+
interactionPhase: props.activity ? 'running' : 'idle' };
|
|
9
|
+
const clauses = buildActionClauses({ ...hints, toast: hints.toast ?? toast?.text,
|
|
10
|
+
updateAvailable: hints.updateAvailable ?? Boolean(props.update) });
|
|
11
|
+
return _jsx(Box, { height: 1, width: hints.cols, flexShrink: 0, children: _jsxs(Text, { color: toast?.level === 'error' ? props.theme.noticeError : props.theme.muted, children: [' ', clauses.join(' | ')] }) });
|
|
27
12
|
}
|
package/dist/ui/Composer.d.ts
CHANGED
|
@@ -1,31 +1,14 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Composer (spec §4.7) — the thin shell around the existing `PromptInput`.
|
|
3
|
-
*
|
|
4
|
-
* Owns exactly two things: the semantic border color and the hint row. The
|
|
5
|
-
* editor's pure helpers (`applyEdit` / `moveVertical` / `slashSuggestions` /
|
|
6
|
-
* `fileTokenAt`) are untouched, so `input.test.ts` keeps passing.
|
|
7
|
-
*
|
|
8
|
-
* The draft flag is held HERE rather than in `App`: the border has to react to
|
|
9
|
-
* "the input is no longer empty", and routing that through App would re-render
|
|
10
|
-
* the transcript on the first keystroke of every message.
|
|
11
|
-
*
|
|
12
|
-
* The fixed status row retains emergency and history-navigation hints while
|
|
13
|
-
* this footer scrolls outside the viewport. Editor state stays mounted.
|
|
14
|
-
*/
|
|
1
|
+
/** Input border and draft shell. Global action hints and activity live below the viewport. */
|
|
15
2
|
import React from 'react';
|
|
16
3
|
import { type DOMElement } from 'ink';
|
|
17
4
|
import type { Theme } from './theme.js';
|
|
18
5
|
import type { TermCapabilities } from './capabilities.js';
|
|
19
6
|
import { pickGlyphs } from './glyphs.js';
|
|
7
|
+
import type { ComposerSubmitResult } from './composer-input.js';
|
|
20
8
|
import { type CommandOption } from './PromptInput.js';
|
|
21
|
-
import {
|
|
9
|
+
import type { RunActivity } from './run-status-row.js';
|
|
22
10
|
import { type AgentMode } from '../agent/agent-mode.js';
|
|
23
|
-
/**
|
|
24
|
-
* The run status row (tui-scrollbar-edge-and-run-row §3.3): the spinner, the phrase
|
|
25
|
-
* and the `queue / interrupt / exit` clauses on ONE row above the input box.
|
|
26
|
-
* `App` passes it only while a run is in flight AND the idle hint row would have
|
|
27
|
-
* been shown, so enabling it never changes the footer's height.
|
|
28
|
-
*/
|
|
11
|
+
/** Legacy caller shape retained for source compatibility; Composer no longer mounts it. */
|
|
29
12
|
export interface RunRowProps {
|
|
30
13
|
activity: RunActivity;
|
|
31
14
|
/** The row is in the viewport with no overlay: draw the animated spinner. */
|
|
@@ -34,9 +17,11 @@ export interface RunRowProps {
|
|
|
34
17
|
rowRef?: React.RefObject<DOMElement>;
|
|
35
18
|
}
|
|
36
19
|
export interface ComposerProps {
|
|
37
|
-
/**
|
|
20
|
+
/** Compatibility prop; activity is now rendered by the global StatusBar. */
|
|
38
21
|
runRow?: RunRowProps | null;
|
|
39
22
|
cols?: number;
|
|
23
|
+
deleteDisambiguated?: boolean;
|
|
24
|
+
onCompletionContextChange?: (context: 'none' | 'slash' | 'file') => void;
|
|
40
25
|
cursorVisible?: boolean;
|
|
41
26
|
onInteraction?: () => void;
|
|
42
27
|
measureRef?: React.RefObject<DOMElement>;
|
|
@@ -51,48 +36,20 @@ export interface ComposerProps {
|
|
|
51
36
|
cwd: string;
|
|
52
37
|
/** Dropped on short terminals to buy back a row (§5.4). */
|
|
53
38
|
showHint: boolean;
|
|
54
|
-
/**
|
|
39
|
+
/** Compatibility prop; submission count no longer hides essential actions. */
|
|
55
40
|
submitCount: number;
|
|
56
|
-
/**
|
|
41
|
+
/** Compatibility prop; App owns secondary teaching-hint visibility. */
|
|
57
42
|
hintsEnabled: boolean;
|
|
58
|
-
/** Mirror of the controller's effective mode; drives the
|
|
43
|
+
/** Mirror of the controller's effective mode; drives the border. */
|
|
59
44
|
agentMode: AgentMode;
|
|
60
|
-
/**
|
|
61
|
-
* Which key this hint should NAME for the mode toggle
|
|
62
|
-
* (shift-tab-mode-toggle-still-dead-on-windows, C3-1).
|
|
63
|
-
*
|
|
64
|
-
* A PROP RATHER THAN A CONSTANT because on some consoles the constant is a
|
|
65
|
-
* lie. This row used to render `shift+tab` unconditionally, with no link to
|
|
66
|
-
* the capability check three files away, so the machines where that key is
|
|
67
|
-
* swallowed were the ones being told to press it - permanently, on every
|
|
68
|
-
* frame, while the one warning they got scrolled out of the transcript
|
|
69
|
-
* minutes earlier (analysis section 3.7). Defaults to the primary key, which
|
|
70
|
-
* is what every unaffected machine renders.
|
|
71
|
-
*/
|
|
45
|
+
/** Compatibility prop; the global action row names the effective mode key. */
|
|
72
46
|
modeToggleKey?: string;
|
|
73
47
|
popupMaxRows?: number;
|
|
74
48
|
popupMaxHeight?: number;
|
|
75
49
|
onPopupRowsChange?: (rows: number) => void;
|
|
76
|
-
/**
|
|
77
|
-
* Rows of newer output below the viewport, forwarded to the input row's chip
|
|
78
|
-
* (tui-selection-and-scroll-follow §4.2 / G7).
|
|
79
|
-
*
|
|
80
|
-
* IT IS STATE, NOT A FADEABLE HINT, and it therefore does NOT pass through the
|
|
81
|
-
* `showHint` / `hintsEnabled` gate below. Those two govern the teaching row —
|
|
82
|
-
* which retires keys the user has demonstrably learned and is dropped outright
|
|
83
|
-
* on a short terminal — and "you are 12 rows behind the newest output" is
|
|
84
|
-
* neither learnable nor optional. Same argument that keeps `esc abort`
|
|
85
|
-
* un-faded in `hintText`.
|
|
86
|
-
*/
|
|
50
|
+
/** Rows of newer output below the viewport, forwarded to the input's scroll chip. */
|
|
87
51
|
scrolledLines?: number;
|
|
88
|
-
/**
|
|
89
|
-
* Live background services (background-service-supervision §3.8), 0 when none.
|
|
90
|
-
*
|
|
91
|
-
* IT IS STATE, NOT A FADEABLE HINT, exactly like `scrolledLines` above: the
|
|
92
|
-
* clause it adds is the only visible way to stop something, and `hintText`
|
|
93
|
-
* below keeps it out of the faded form for the same reason `esc abort` is kept
|
|
94
|
-
* there.
|
|
95
|
-
*/
|
|
52
|
+
/** Compatibility prop; the global status and action rows show live services. */
|
|
96
53
|
services?: number;
|
|
97
54
|
/**
|
|
98
55
|
* The composer's DRAFT row count, forwarded upward (tui-paste-handling
|
|
@@ -109,30 +66,15 @@ export interface ComposerProps {
|
|
|
109
66
|
onNotice?: (level: 'warn' | 'error', text: string) => void;
|
|
110
67
|
theme: Theme;
|
|
111
68
|
caps: TermCapabilities;
|
|
112
|
-
onSubmit: (text: string) =>
|
|
69
|
+
onSubmit: (text: string) => ComposerSubmitResult;
|
|
113
70
|
onHelp?: () => void;
|
|
114
71
|
}
|
|
115
|
-
/**
|
|
116
|
-
* The running-state hint as separate clauses, most urgent first.
|
|
117
|
-
*
|
|
118
|
-
* `hintText` joins them (its output is pinned by AC-43), and the run status row
|
|
119
|
-
* consumes the SAME array so it can drop whole clauses from the tail on a narrow
|
|
120
|
-
* terminal instead of cutting `interrupt` in half. One source, two consumers.
|
|
121
|
-
*/
|
|
72
|
+
/** Legacy helper retained for consumers and contract tests; not rendered by Composer. */
|
|
122
73
|
export declare function runningHintClauses(opts: {
|
|
123
74
|
glyphs: ReturnType<typeof pickGlyphs>;
|
|
124
75
|
services: number;
|
|
125
76
|
}): string[];
|
|
126
|
-
/**
|
|
127
|
-
* Build the hint row.
|
|
128
|
-
*
|
|
129
|
-
* The `running` branch is NEVER abbreviated, no matter how experienced the user
|
|
130
|
-
* is. Once the status bar's hint cluster was removed, the composer became the
|
|
131
|
-
* only place that names the abort key — and fading that out after eight
|
|
132
|
-
* submissions would leave a long-running task with no visible way to stop it.
|
|
133
|
-
* Progressive disclosure is for teaching discoverable things, not for retiring
|
|
134
|
-
* an emergency exit (§4.6 / A-14 / M-10b).
|
|
135
|
-
*/
|
|
77
|
+
/** Compatibility formatter. Product hints use interaction-hints.ts. */
|
|
136
78
|
declare function hintText(opts: {
|
|
137
79
|
running: boolean;
|
|
138
80
|
submitCount: number;
|
|
@@ -142,15 +84,7 @@ declare function hintText(opts: {
|
|
|
142
84
|
/** Live background services; 0 leaves both rows byte-identical to today's. */
|
|
143
85
|
services: number;
|
|
144
86
|
}): string;
|
|
145
|
-
/**
|
|
146
|
-
* `hintText`, for the interrupt-ladder tests (AC-43).
|
|
147
|
-
*
|
|
148
|
-
* EXPORTED UNDER A TEST-FACING NAME rather than exporting `hintText` itself, so
|
|
149
|
-
* the private helper stays private in the module graph and nothing in `src/`
|
|
150
|
-
* grows a second caller by accident. The row is the ONLY visible way to stop a
|
|
151
|
-
* process the agent left running, so it is worth asserting on directly rather
|
|
152
|
-
* than fishing the string out of a rendered frame.
|
|
153
|
-
*/
|
|
87
|
+
/** Compatibility export for existing hint contract tests. */
|
|
154
88
|
export declare const hintTextForTest: typeof hintText;
|
|
155
|
-
export declare function Composer({
|
|
89
|
+
export declare function Composer({ deleteDisambiguated, onCompletionContextChange, cols, cursorVisible, onInteraction, measureRef, isActive, reducedMotion, onEscape, onEscapeDismiss, running, history, commands, cwd, agentMode, popupMaxRows, popupMaxHeight, onPopupRowsChange, scrolledLines, onDraftRows, onNotice, theme, caps, onSubmit, onHelp, }: ComposerProps): React.ReactElement;
|
|
156
90
|
export {};
|
package/dist/ui/Composer.js
CHANGED
|
@@ -1,80 +1,26 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
2
|
-
/**
|
|
3
|
-
* Composer (spec §4.7) — the thin shell around the existing `PromptInput`.
|
|
4
|
-
*
|
|
5
|
-
* Owns exactly two things: the semantic border color and the hint row. The
|
|
6
|
-
* editor's pure helpers (`applyEdit` / `moveVertical` / `slashSuggestions` /
|
|
7
|
-
* `fileTokenAt`) are untouched, so `input.test.ts` keeps passing.
|
|
8
|
-
*
|
|
9
|
-
* The draft flag is held HERE rather than in `App`: the border has to react to
|
|
10
|
-
* "the input is no longer empty", and routing that through App would re-render
|
|
11
|
-
* the transcript on the first keystroke of every message.
|
|
12
|
-
*
|
|
13
|
-
* The fixed status row retains emergency and history-navigation hints while
|
|
14
|
-
* this footer scrolls outside the viewport. Editor state stays mounted.
|
|
15
|
-
*/
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/** Input border and draft shell. Global action hints and activity live below the viewport. */
|
|
16
3
|
import { useCallback, useState } from 'react';
|
|
17
|
-
import { Box
|
|
18
|
-
import stringWidth from 'string-width';
|
|
19
|
-
import { pickGlyphs } from './glyphs.js';
|
|
4
|
+
import { Box } from 'ink';
|
|
20
5
|
import { HINT_FADE_AFTER } from '../config/schema.js';
|
|
21
6
|
import { PromptInput } from './PromptInput.js';
|
|
22
|
-
import {
|
|
23
|
-
|
|
24
|
-
import { RUN_ROW_LEAD, planRunRow } from './run-status-row.js';
|
|
25
|
-
import { MODE_LABEL, MODE_TOGGLE_KEYS, nextMode } from '../agent/agent-mode.js';
|
|
26
|
-
/**
|
|
27
|
-
* The running-state hint as separate clauses, most urgent first.
|
|
28
|
-
*
|
|
29
|
-
* `hintText` joins them (its output is pinned by AC-43), and the run status row
|
|
30
|
-
* consumes the SAME array so it can drop whole clauses from the tail on a narrow
|
|
31
|
-
* terminal instead of cutting `interrupt` in half. One source, two consumers.
|
|
32
|
-
*/
|
|
7
|
+
import { MODE_LABEL, nextMode } from '../agent/agent-mode.js';
|
|
8
|
+
/** Legacy helper retained for consumers and contract tests; not rendered by Composer. */
|
|
33
9
|
export function runningHintClauses(opts) {
|
|
34
10
|
const { glyphs, services } = opts;
|
|
35
|
-
// `queue`, NOT `steer` (tui-shift-enter-copy-queue 5.5): the gesture's
|
|
36
|
-
// consequence now has a name in the transcript (`Queue: ...`), and one
|
|
37
|
-
// behaviour with two names is undiscoverable.
|
|
38
11
|
const clauses = [`${glyphs.enterKey} queue`, `esc${glyphs.times}2 interrupt`];
|
|
39
|
-
// Confirmation is named even when service controls also need space.
|
|
40
12
|
if (services > 0)
|
|
41
13
|
clauses.push(`ctrl+c stop ${services}`);
|
|
42
14
|
clauses.push(`ctrl+c${glyphs.times}2 exit`);
|
|
43
15
|
return clauses;
|
|
44
16
|
}
|
|
45
|
-
/**
|
|
46
|
-
* Build the hint row.
|
|
47
|
-
*
|
|
48
|
-
* The `running` branch is NEVER abbreviated, no matter how experienced the user
|
|
49
|
-
* is. Once the status bar's hint cluster was removed, the composer became the
|
|
50
|
-
* only place that names the abort key — and fading that out after eight
|
|
51
|
-
* submissions would leave a long-running task with no visible way to stop it.
|
|
52
|
-
* Progressive disclosure is for teaching discoverable things, not for retiring
|
|
53
|
-
* an emergency exit (§4.6 / A-14 / M-10b).
|
|
54
|
-
*/
|
|
17
|
+
/** Compatibility formatter. Product hints use interaction-hints.ts. */
|
|
55
18
|
function hintText(opts) {
|
|
56
19
|
const { running, submitCount, glyphs, mode, toggleKey, services } = opts;
|
|
57
20
|
const dot = ` ${glyphs.midDot} `;
|
|
58
|
-
// THE EXIT CLAUSE SURVIVES IN BOTH ROWS (P1-4). The obvious edit here is to
|
|
59
|
-
// replace `ctrl+c*2 exit` with `ctrl+c stop N` — and that would delete the
|
|
60
|
-
// only visible way to quit at exactly the moment Ctrl+C stops meaning "quit",
|
|
61
|
-
// on the screen a user reaches when something has already gone wrong. Both
|
|
62
|
-
// clauses are shown; the row is `wrap="truncate"` and they are ordered
|
|
63
|
-
// most-urgent-first, so a terminal too narrow for all of them drops the exit
|
|
64
|
-
// clause last rather than the abort key.
|
|
65
21
|
if (running)
|
|
66
22
|
return runningHintClauses({ glyphs, services }).join(dot);
|
|
67
|
-
// The hint names the DESTINATION, not the current state — the correct label
|
|
68
|
-
// for a toggle affordance, and the reason the mode word here is the OPPOSITE
|
|
69
|
-
// of the one on the chip beside it.
|
|
70
23
|
const toggle = `${toggleKey} ${MODE_LABEL[nextMode(mode)].toLowerCase()}`;
|
|
71
|
-
// The faded form KEEPS the mode toggle. Progressive disclosure exists to
|
|
72
|
-
// retire things the user has demonstrably learned, and `submitCount` counts
|
|
73
|
-
// submissions — a user can send fifty messages without ever discovering
|
|
74
|
-
// Shift+Tab. Same argument that keeps `esc abort` un-faded above.
|
|
75
|
-
// NEVER FADED, for the reason the props doc gives: it is the only visible way
|
|
76
|
-
// to stop a process the agent left running, and progressive disclosure retires
|
|
77
|
-
// things the user has demonstrably learned — not emergency exits.
|
|
78
24
|
const stop = services > 0 ? [`ctrl+c stop ${services}`] : [];
|
|
79
25
|
if (submitCount >= HINT_FADE_AFTER)
|
|
80
26
|
return [...stop, toggle, '? help'].join(dot);
|
|
@@ -88,31 +34,9 @@ function hintText(opts) {
|
|
|
88
34
|
'? help',
|
|
89
35
|
].join(dot);
|
|
90
36
|
}
|
|
91
|
-
/**
|
|
92
|
-
* `hintText`, for the interrupt-ladder tests (AC-43).
|
|
93
|
-
*
|
|
94
|
-
* EXPORTED UNDER A TEST-FACING NAME rather than exporting `hintText` itself, so
|
|
95
|
-
* the private helper stays private in the module graph and nothing in `src/`
|
|
96
|
-
* grows a second caller by accident. The row is the ONLY visible way to stop a
|
|
97
|
-
* process the agent left running, so it is worth asserting on directly rather
|
|
98
|
-
* than fishing the string out of a rendered frame.
|
|
99
|
-
*/
|
|
37
|
+
/** Compatibility export for existing hint contract tests. */
|
|
100
38
|
export const hintTextForTest = hintText;
|
|
101
|
-
function
|
|
102
|
-
const glyphs = pickGlyphs(caps);
|
|
103
|
-
const separator = ` ${glyphs.midDot} `;
|
|
104
|
-
const clauses = runningHintClauses({ glyphs, services });
|
|
105
|
-
const plan = planRunRow({
|
|
106
|
-
cols,
|
|
107
|
-
// Spinner glyph + the space after it, then the label.
|
|
108
|
-
labelCols: 2 + stringWidth(resolveActivityLabel(runRow.activity, glyphs)),
|
|
109
|
-
hintClauseCols: clauses.map((clause) => stringWidth(clause)),
|
|
110
|
-
separatorCols: stringWidth(separator),
|
|
111
|
-
chipCols: modeChipCols(agentMode),
|
|
112
|
-
});
|
|
113
|
-
return (_jsxs(Box, { ref: runRow.rowRef, flexDirection: "row", flexShrink: 0, children: [_jsx(Box, { width: RUN_ROW_LEAD, flexShrink: 0, children: _jsx(Text, { children: ' ' }) }), _jsx(Box, { width: plan.labelCols, flexShrink: 0, children: _jsx(ActivityLabel, { ...runRow.activity, spinnerLive: runRow.live, theme: theme, caps: caps }) }), plan.hintClauses > 0 && (_jsxs(Text, { wrap: "truncate", color: theme.hintFg ?? theme.muted, children: [separator, clauses.slice(0, plan.hintClauses).join(separator)] })), plan.chip && (_jsxs(_Fragment, { children: [_jsx(Box, { flexGrow: 1 }), _jsx(ModeChip, { mode: agentMode, theme: theme, caps: caps })] }))] }));
|
|
114
|
-
}
|
|
115
|
-
export function Composer({ runRow, cols, cursorVisible, onInteraction, measureRef, isActive, reducedMotion, onEscape, onEscapeDismiss, running, history, commands, cwd, showHint, submitCount, hintsEnabled, agentMode, services = 0, modeToggleKey = MODE_TOGGLE_KEYS.primary, popupMaxRows, popupMaxHeight, onPopupRowsChange, scrolledLines = 0, onDraftRows, onNotice, theme, caps, onSubmit, onHelp, }) {
|
|
39
|
+
export function Composer({ deleteDisambiguated, onCompletionContextChange, cols, cursorVisible, onInteraction, measureRef, isActive, reducedMotion, onEscape, onEscapeDismiss, running, history, commands, cwd, agentMode, popupMaxRows, popupMaxHeight, onPopupRowsChange, scrolledLines = 0, onDraftRows, onNotice, theme, caps, onSubmit, onHelp, }) {
|
|
116
40
|
const [hasDraft, setHasDraft] = useState(false);
|
|
117
41
|
const onDraftChange = useCallback((next) => {
|
|
118
42
|
setHasDraft(next.hasDraft);
|
|
@@ -130,20 +54,5 @@ export function Composer({ runRow, cols, cursorVisible, onInteraction, measureRe
|
|
|
130
54
|
: hasDraft
|
|
131
55
|
? theme.focusBorder ?? theme.primary
|
|
132
56
|
: theme.idleBorder ?? theme.border;
|
|
133
|
-
|
|
134
|
-
// While the run row is up it REPLACES the hint row below the input (same row
|
|
135
|
-
// count, so starting a run never moves the layout).
|
|
136
|
-
const runRowActive = running && !!runRow;
|
|
137
|
-
const visible = showHint && hintsEnabled && !runRowActive;
|
|
138
|
-
return (_jsxs(Box, { ref: measureRef, flexDirection: "column", flexShrink: 0, children: [runRowActive && (_jsx(RunRow, { runRow: runRow, cols: cols ?? process.stdout.columns ?? 80, services: services, agentMode: agentMode, theme: theme, caps: caps })), _jsx(PromptInput, { cols: cols, cursorVisible: cursorVisible, onInteraction: onInteraction, isActive: isActive, reducedMotion: reducedMotion, onEscape: onEscape, onEscapeDismiss: onEscapeDismiss, running: running, history: history, commands: commands, cwd: cwd, theme: theme, caps: caps, popupMaxRows: popupMaxRows, popupMaxHeight: popupMaxHeight, onPopupRowsChange: onPopupRowsChange, onSubmit: onSubmit, onHelp: onHelp, agentMode: agentMode, borderColor: borderColor, onDraftChange: onDraftChange, onNotice: onNotice, scrolledLines: scrolledLines }), visible && (
|
|
139
|
-
// The chip rides the EXISTING hint row rather than adding one, so it
|
|
140
|
-
// costs zero rows on a 24-row terminal.
|
|
141
|
-
_jsxs(Box, { flexDirection: "row", flexShrink: 0, children: [_jsx(Text, { children: ' ' }), _jsx(ModeChip, { mode: agentMode, theme: theme, caps: caps }), _jsx(Text, { wrap: "truncate", color: theme.hintFg ?? theme.muted, children: hintText({
|
|
142
|
-
running,
|
|
143
|
-
submitCount,
|
|
144
|
-
glyphs,
|
|
145
|
-
mode: agentMode,
|
|
146
|
-
toggleKey: modeToggleKey,
|
|
147
|
-
services,
|
|
148
|
-
}) })] }))] }));
|
|
57
|
+
return (_jsx(Box, { ref: measureRef, flexDirection: "column", flexShrink: 0, children: _jsx(PromptInput, { deleteDisambiguated: deleteDisambiguated, onCompletionContextChange: onCompletionContextChange, cols: cols, cursorVisible: cursorVisible, onInteraction: onInteraction, isActive: isActive, reducedMotion: reducedMotion, onEscape: onEscape, onEscapeDismiss: onEscapeDismiss, running: running, history: history, commands: commands, cwd: cwd, theme: theme, caps: caps, popupMaxRows: popupMaxRows, popupMaxHeight: popupMaxHeight, onPopupRowsChange: onPopupRowsChange, onSubmit: onSubmit, onHelp: onHelp, agentMode: agentMode, borderColor: borderColor, onDraftChange: onDraftChange, onNotice: onNotice, scrolledLines: scrolledLines }) }));
|
|
149
58
|
}
|
package/dist/ui/PromptInput.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ import { pickGlyphs } from './glyphs.js';
|
|
|
25
25
|
import { type EditIntent } from '../input/keymap.js';
|
|
26
26
|
import { type Suggestion } from './AutocompletePopup.js';
|
|
27
27
|
import { type EditorState, type InputSegment } from './editor-reducer.js';
|
|
28
|
+
import { type ComposerSubmitResult } from './composer-input.js';
|
|
28
29
|
import type { AgentMode } from '../agent/agent-mode.js';
|
|
29
30
|
export interface CommandOption {
|
|
30
31
|
name: string;
|
|
@@ -51,7 +52,10 @@ interface PromptInputProps {
|
|
|
51
52
|
/** Total menu rows, including borders and the overflow hint. */
|
|
52
53
|
popupMaxHeight?: number;
|
|
53
54
|
onPopupRowsChange?: (rows: number) => void;
|
|
54
|
-
|
|
55
|
+
onCompletionContextChange?: (context: 'none' | 'slash' | 'file') => void;
|
|
56
|
+
/** True only when the stdin filter normalized DEL before Ink parsed it. */
|
|
57
|
+
deleteDisambiguated?: boolean;
|
|
58
|
+
onSubmit: (text: string) => ComposerSubmitResult;
|
|
55
59
|
onHelp?: () => void;
|
|
56
60
|
onEscape?: () => void;
|
|
57
61
|
onEscapeDismiss?: () => void;
|
|
@@ -153,5 +157,5 @@ export declare function fileTokenAt(buffer: string, cursor: number): {
|
|
|
153
157
|
end: number;
|
|
154
158
|
query: string;
|
|
155
159
|
} | null;
|
|
156
|
-
export declare function PromptInput({ cols: colsProp, cursorVisible, onInteraction, isActive, running, history, commands, cwd, theme, caps, popupMaxRows, popupMaxHeight, onPopupRowsChange, agentMode, onSubmit, onHelp, onEscape, onEscapeDismiss, reducedMotion, borderColor, onDraftChange, onNotice, scrolledLines, }: PromptInputProps): React.ReactElement;
|
|
160
|
+
export declare function PromptInput({ cols: colsProp, cursorVisible, onInteraction, isActive, running, history, commands, cwd, theme, caps, popupMaxRows, popupMaxHeight, onPopupRowsChange, onCompletionContextChange, deleteDisambiguated, agentMode, onSubmit, onHelp, onEscape, onEscapeDismiss, reducedMotion, borderColor, onDraftChange, onNotice, scrolledLines, }: PromptInputProps): React.ReactElement;
|
|
157
161
|
export {};
|