@aragon-agent/cli 0.6.35 → 0.6.36
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 +21 -0
- package/README.md +3173 -3071
- package/dist/agent/agent-mode.d.ts +20 -4
- package/dist/agent/agent-mode.js +20 -4
- package/dist/agent/controller.d.ts +38 -3
- package/dist/agent/controller.js +64 -4
- package/dist/agent/headless.js +31 -0
- package/dist/agent/reducer.d.ts +13 -2
- package/dist/agent/reducer.js +1 -0
- package/dist/cli.js +12 -0
- package/dist/commands/agents-command.d.ts +15 -0
- package/dist/commands/agents-command.js +27 -0
- package/dist/commands/builtins.js +131 -4
- package/dist/commands/perf.d.ts +11 -0
- package/dist/commands/perf.js +2 -1
- package/dist/commands/registry.d.ts +14 -0
- package/dist/config/app-paths.d.ts +15 -0
- package/dist/config/app-paths.js +33 -2
- package/dist/config/env.d.ts +4 -1
- package/dist/config/env.js +28 -2
- package/dist/config/load.d.ts +10 -0
- package/dist/config/load.js +8 -1
- package/dist/config/schema.d.ts +68 -0
- package/dist/config/schema.js +39 -0
- package/dist/config/store.js +12 -1
- package/dist/config/ui-state.d.ts +18 -0
- package/dist/config/ui-state.js +9 -0
- package/dist/exec/events.d.ts +25 -0
- package/dist/exec/events.js +4 -0
- package/dist/exec/index.js +16 -1
- package/dist/exec/runner.js +10 -0
- package/dist/proc/limits.d.ts +16 -0
- package/dist/proc/limits.js +16 -0
- package/dist/proc/supervisor.d.ts +20 -1
- package/dist/proc/supervisor.js +42 -14
- package/dist/prompts/command.d.ts +34 -0
- package/dist/prompts/command.js +237 -0
- package/dist/prompts/copy.d.ts +89 -0
- package/dist/prompts/copy.js +131 -0
- package/dist/prompts/fill.d.ts +51 -0
- package/dist/prompts/fill.js +46 -0
- package/dist/prompts/limits.d.ts +53 -0
- package/dist/prompts/limits.js +53 -0
- package/dist/prompts/normalize.d.ts +53 -0
- package/dist/prompts/normalize.js +190 -0
- package/dist/prompts/resolve.d.ts +31 -0
- package/dist/prompts/resolve.js +60 -0
- package/dist/prompts/search.d.ts +58 -0
- package/dist/prompts/search.js +146 -0
- package/dist/prompts/store.d.ts +86 -0
- package/dist/prompts/store.js +453 -0
- package/dist/prompts/types.d.ts +204 -0
- package/dist/prompts/types.js +13 -0
- package/dist/session/checkpoint.d.ts +8 -1
- package/dist/session/checkpoint.js +6 -0
- package/dist/session/persist.d.ts +19 -1
- package/dist/session/persist.js +13 -1
- package/dist/session/startup-session.d.ts +7 -2
- package/dist/session/startup-session.js +5 -2
- package/dist/session/store.js +3 -0
- package/dist/session/validate-session.js +64 -1
- package/dist/team/limits.d.ts +25 -0
- package/dist/team/limits.js +26 -0
- package/dist/team/runtime.js +27 -2
- package/dist/team/settled.d.ts +57 -0
- package/dist/team/settled.js +87 -0
- package/dist/team/subagent.js +48 -2
- package/dist/team/timeline.d.ts +74 -0
- package/dist/team/timeline.js +101 -0
- package/dist/team/types.d.ts +24 -1
- package/dist/todo/acceptance.d.ts +120 -0
- package/dist/todo/acceptance.js +399 -0
- package/dist/todo/follow-through.d.ts +48 -23
- package/dist/todo/follow-through.js +110 -44
- package/dist/todo/limits.d.ts +62 -1
- package/dist/todo/limits.js +62 -1
- package/dist/todo/panel-rows.d.ts +29 -39
- package/dist/todo/panel-rows.js +37 -83
- package/dist/todo/plan-sections.d.ts +60 -0
- package/dist/todo/plan-sections.js +108 -0
- package/dist/todo/prompt.d.ts +17 -12
- package/dist/todo/prompt.js +59 -40
- package/dist/todo/row-window.d.ts +51 -0
- package/dist/todo/row-window.js +107 -0
- package/dist/todo/store.d.ts +139 -31
- package/dist/todo/store.js +271 -73
- package/dist/todo/todo-tool.d.ts +33 -8
- package/dist/todo/todo-tool.js +186 -30
- package/dist/todo/types.d.ts +58 -0
- package/dist/ui/App.js +310 -46
- package/dist/ui/Composer.d.ts +6 -2
- package/dist/ui/Composer.js +2 -2
- package/dist/ui/Header.d.ts +9 -1
- package/dist/ui/Header.js +2 -2
- package/dist/ui/PromptInput.d.ts +21 -1
- package/dist/ui/PromptInput.js +21 -1
- package/dist/ui/ScrollbackShell.d.ts +19 -19
- package/dist/ui/ScrollbackShell.js +8 -1
- package/dist/ui/StatusBar.d.ts +5 -0
- package/dist/ui/StatusBar.js +1 -0
- package/dist/ui/TeamPanel.d.ts +26 -44
- package/dist/ui/TeamPanel.js +46 -135
- package/dist/ui/TodoPanel.d.ts +17 -14
- package/dist/ui/TodoPanel.js +146 -38
- package/dist/ui/Transcript.js +1 -1
- package/dist/ui/composer-input.d.ts +10 -0
- package/dist/ui/composer-input.js +57 -24
- package/dist/ui/entries/TodoCard.d.ts +26 -11
- package/dist/ui/entries/TodoCard.js +56 -15
- package/dist/ui/glyphs.d.ts +19 -0
- package/dist/ui/glyphs.js +4 -0
- package/dist/ui/layout/agents-board.d.ts +102 -0
- package/dist/ui/layout/agents-board.js +389 -0
- package/dist/ui/layout/cells.d.ts +61 -0
- package/dist/ui/layout/cells.js +156 -0
- package/dist/ui/layout/header-hints.d.ts +13 -3
- package/dist/ui/layout/header-hints.js +48 -14
- package/dist/ui/layout/status-detail-layout.js +9 -0
- package/dist/ui/layout/status-layout.d.ts +13 -0
- package/dist/ui/layout/team-row.d.ts +139 -0
- package/dist/ui/layout/team-row.js +246 -0
- package/dist/ui/layout/todo-layout.d.ts +38 -0
- package/dist/ui/layout/todo-layout.js +75 -0
- package/dist/ui/layout/virtual-window.js +23 -3
- package/dist/ui/overlays/AgentsBoardOverlay.d.ts +57 -0
- package/dist/ui/overlays/AgentsBoardOverlay.js +183 -0
- package/dist/ui/overlays/HelpOverlay.js +17 -2
- package/dist/ui/overlays/PromptEditorPage.d.ts +48 -0
- package/dist/ui/overlays/PromptEditorPage.js +227 -0
- package/dist/ui/overlays/PromptLibraryOverlay.d.ts +37 -0
- package/dist/ui/overlays/PromptLibraryOverlay.js +191 -0
- package/dist/ui/overlays/PromptPicker.d.ts +36 -0
- package/dist/ui/overlays/PromptPicker.js +129 -0
- package/dist/ui/overlays/PromptTextArea.d.ts +86 -0
- package/dist/ui/overlays/PromptTextArea.js +141 -0
- package/dist/ui/overlays/prompt-library-state.d.ts +162 -0
- package/dist/ui/overlays/prompt-library-state.js +335 -0
- package/dist/ui/overlays/prompt-picker-state.d.ts +71 -0
- package/dist/ui/overlays/prompt-picker-state.js +98 -0
- package/dist/ui/scrollback/band.js +14 -1
- package/dist/ui/team-status.d.ts +25 -0
- package/dist/ui/team-status.js +31 -3
- package/dist/ui/use-startup-notices.d.ts +35 -1
- package/dist/ui/use-startup-notices.js +29 -2
- package/dist/ui/use-wheel-routing.js +9 -1
- package/package.json +2 -2
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Display-width cell arithmetic (tui-subagent-panel §5 #1).
|
|
3
|
+
*
|
|
4
|
+
* EVERY FUNCTION HERE IS PURE AND TOTAL: no Ink, no clock, no terminal, and no
|
|
5
|
+
* input that throws. The panel row and the Agents board both need to place text
|
|
6
|
+
* in a FIXED number of terminal columns, and `padEnd` cannot do that: it counts
|
|
7
|
+
* code units while a terminal counts cells, so a CJK description padded with
|
|
8
|
+
* `padEnd` overflows its box by one column per wide character and pushes the
|
|
9
|
+
* next cell off the row. That is exactly the defect this round removes from the
|
|
10
|
+
* roster, one layer down.
|
|
11
|
+
*
|
|
12
|
+
* ASCII ONLY: `src/ui/**` is inside `glyphs.test.ts`'s static scan, so the
|
|
13
|
+
* ellipsis and any other symbol a caller wants must be passed IN (the board
|
|
14
|
+
* hands over `glyphs.ellipsis`), never spelled here.
|
|
15
|
+
*/
|
|
16
|
+
import stringWidth from 'string-width';
|
|
17
|
+
/** Invalid geometry becomes zero, exactly like the layout modules' `nonNegativeInt`. */
|
|
18
|
+
function nonNegativeInt(value) {
|
|
19
|
+
return Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
|
|
20
|
+
}
|
|
21
|
+
/** Display columns a string occupies, or 0 for a non-string (defensive at the edge only). */
|
|
22
|
+
export function cellWidth(text) {
|
|
23
|
+
if (typeof text !== 'string' || text.length === 0)
|
|
24
|
+
return 0;
|
|
25
|
+
return stringWidth(text);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Cut `text` to at most `cells` display columns, marking the cut with `ellipsis`.
|
|
29
|
+
*
|
|
30
|
+
* THE ELLIPSIS IS PART OF THE BUDGET, not an extra column: a caller that asks for
|
|
31
|
+
* 8 cells gets a string that MEASURES 8, which is what makes a fixed-width cell
|
|
32
|
+
* a cell rather than a hope. When the budget cannot hold the ellipsis plus one
|
|
33
|
+
* character, the string is hard-cut instead - a marker wider than the cell it
|
|
34
|
+
* marks is the one outcome worse than no marker.
|
|
35
|
+
*
|
|
36
|
+
* Wide characters are never split: a surrogate pair or a double-width glyph that
|
|
37
|
+
* does not fit is dropped whole, so the result can measure one column less than
|
|
38
|
+
* asked but never one more.
|
|
39
|
+
*/
|
|
40
|
+
export function fitCells(text, cells, ellipsis = '...') {
|
|
41
|
+
const budget = nonNegativeInt(cells);
|
|
42
|
+
if (budget === 0)
|
|
43
|
+
return '';
|
|
44
|
+
if (cellWidth(text) <= budget)
|
|
45
|
+
return text;
|
|
46
|
+
const mark = cellWidth(ellipsis) < budget ? ellipsis : '';
|
|
47
|
+
const room = budget - cellWidth(mark);
|
|
48
|
+
let out = '';
|
|
49
|
+
let used = 0;
|
|
50
|
+
// `Array.from` iterates by CODE POINT, so a surrogate pair is one step and can
|
|
51
|
+
// never be cut in half by this loop.
|
|
52
|
+
for (const char of Array.from(text)) {
|
|
53
|
+
const width = cellWidth(char);
|
|
54
|
+
if (used + width > room)
|
|
55
|
+
break;
|
|
56
|
+
out += char;
|
|
57
|
+
used += width;
|
|
58
|
+
}
|
|
59
|
+
return `${out}${mark}`;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Pad `text` to exactly `cells` columns (or leave it alone when it already
|
|
63
|
+
* measures at least that).
|
|
64
|
+
*
|
|
65
|
+
* NOT `padEnd`: see this file's header. A padded cell is what keeps the next
|
|
66
|
+
* column at a stable offset, which is the whole point of the panel's table.
|
|
67
|
+
*/
|
|
68
|
+
export function padCells(text, cells) {
|
|
69
|
+
const budget = nonNegativeInt(cells);
|
|
70
|
+
const width = cellWidth(text);
|
|
71
|
+
if (width >= budget)
|
|
72
|
+
return text;
|
|
73
|
+
return `${text}${' '.repeat(budget - width)}`;
|
|
74
|
+
}
|
|
75
|
+
/** `fitCells` then `padCells`: the result ALWAYS measures exactly `cells`. */
|
|
76
|
+
export function alignRightCells(text, cells, ellipsis = '...') {
|
|
77
|
+
const budget = nonNegativeInt(cells);
|
|
78
|
+
return padCells(fitCells(text, budget, ellipsis), budget);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Fold `text` into at most `maxRows` lines of at most `cells` columns each.
|
|
82
|
+
*
|
|
83
|
+
* BREAKS AT SPACES, HARD-CUTS ONLY WHEN A SINGLE TOKEN CANNOT FIT: a word that is
|
|
84
|
+
* wider than the column is the one case where an honest over-long token beats a
|
|
85
|
+
* line of nothing, so it is cut rather than pushed to a line that still cannot
|
|
86
|
+
* hold it. The last allowed line, when text remains, carries the `(+N lines)`
|
|
87
|
+
* tail so the reader learns that the section was truncated instead of believing
|
|
88
|
+
* the prose ended mid-sentence.
|
|
89
|
+
*
|
|
90
|
+
* Pure and total: `cells <= 0` or `maxRows <= 0` yields no lines, and the caller
|
|
91
|
+
* decides what an empty section means (the board omits it).
|
|
92
|
+
*/
|
|
93
|
+
export function wrapCells(text, cells, maxRows, ellipsis = '...') {
|
|
94
|
+
const width = nonNegativeInt(cells);
|
|
95
|
+
const limit = nonNegativeInt(maxRows);
|
|
96
|
+
const flat = (text ?? '').replace(/\s+/g, ' ').trim();
|
|
97
|
+
if (width === 0 || limit === 0 || flat.length === 0) {
|
|
98
|
+
return { lines: [], dropped: flat.length === 0 ? 0 : limit === 0 ? 1 : 0 };
|
|
99
|
+
}
|
|
100
|
+
const lines = [];
|
|
101
|
+
let rest = flat;
|
|
102
|
+
while (rest.length > 0) {
|
|
103
|
+
if (cellWidth(rest) <= width) {
|
|
104
|
+
lines.push(rest);
|
|
105
|
+
rest = '';
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
const cut = breakCells(rest, width);
|
|
109
|
+
lines.push(cut.line);
|
|
110
|
+
rest = cut.rest;
|
|
111
|
+
}
|
|
112
|
+
if (lines.length <= limit)
|
|
113
|
+
return { lines, dropped: 0 };
|
|
114
|
+
const kept = lines.slice(0, limit);
|
|
115
|
+
const dropped = lines.length - limit;
|
|
116
|
+
const tail = `(+${dropped} lines)`;
|
|
117
|
+
// THE ANNOUNCEMENT IS RESERVED, not appended and then truncated away: a reader who
|
|
118
|
+
// sees prose stop mid-sentence with no marker reads it as the end of the fact, which
|
|
119
|
+
// is exactly the wrong conclusion. When the column is too narrow to hold the marker
|
|
120
|
+
// at all, the line is simply cut - the caller's section is unusable at that width.
|
|
121
|
+
const tailCells = cellWidth(tail) + 1;
|
|
122
|
+
const last = kept[limit - 1] ?? '';
|
|
123
|
+
kept[limit - 1] = tailCells < width
|
|
124
|
+
? `${fitCells(last, width - tailCells, ellipsis)} ${tail}`
|
|
125
|
+
: fitCells(last, width, ellipsis);
|
|
126
|
+
return { lines: kept, dropped };
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* The longest prefix of `text` that fits `width` columns, preferring a space.
|
|
130
|
+
*
|
|
131
|
+
* Returns the consumed line and what is left, so the caller's loop is one
|
|
132
|
+
* assignment per iteration and the "one function does one thing" rule holds.
|
|
133
|
+
*/
|
|
134
|
+
function breakCells(text, width) {
|
|
135
|
+
let used = 0;
|
|
136
|
+
let taken = 0;
|
|
137
|
+
let lastSpace = -1;
|
|
138
|
+
for (const char of Array.from(text)) {
|
|
139
|
+
const charWidth = cellWidth(char);
|
|
140
|
+
if (used + charWidth > width)
|
|
141
|
+
break;
|
|
142
|
+
used += charWidth;
|
|
143
|
+
taken += char.length;
|
|
144
|
+
if (char === ' ')
|
|
145
|
+
lastSpace = taken;
|
|
146
|
+
}
|
|
147
|
+
// A single token wider than the column: hard-cut it rather than emit nothing.
|
|
148
|
+
if (taken === 0) {
|
|
149
|
+
const first = Array.from(text)[0] ?? '';
|
|
150
|
+
return { line: fitCells(first, width), rest: text.slice(first.length) };
|
|
151
|
+
}
|
|
152
|
+
if (lastSpace > 0) {
|
|
153
|
+
return { line: text.slice(0, lastSpace).trimEnd(), rest: text.slice(lastSpace).trimStart() };
|
|
154
|
+
}
|
|
155
|
+
return { line: text.slice(0, taken), rest: text.slice(taken).trimStart() };
|
|
156
|
+
}
|
|
@@ -46,7 +46,7 @@ export declare const HEADER_HINT_LIMITS: {
|
|
|
46
46
|
*/
|
|
47
47
|
readonly separatorCells: 2;
|
|
48
48
|
};
|
|
49
|
-
export type HeaderHintId = 'branch' | 'index' | 'kit' | 'detail';
|
|
49
|
+
export type HeaderHintId = 'branch' | 'prompts' | 'index' | 'kit' | 'detail';
|
|
50
50
|
export interface HeaderHintClause {
|
|
51
51
|
id: HeaderHintId;
|
|
52
52
|
text: string;
|
|
@@ -66,8 +66,9 @@ export interface HeaderHintPlan {
|
|
|
66
66
|
/**
|
|
67
67
|
* Plan the right-hand hints.
|
|
68
68
|
*
|
|
69
|
-
* The
|
|
70
|
-
* does not change, only the arithmetic behind it
|
|
69
|
+
* The inputs are the props `Header` receives plus the two switches that decide which
|
|
70
|
+
* clauses exist at all - the call site does not change, only the arithmetic behind it
|
|
71
|
+
* (spec 3.11 step 5).
|
|
71
72
|
*/
|
|
72
73
|
export declare function planHeaderHints(input: {
|
|
73
74
|
columns: number;
|
|
@@ -84,4 +85,13 @@ export declare function planHeaderHints(input: {
|
|
|
84
85
|
* switch, and it degrades the six expanded rungs back into today's four.
|
|
85
86
|
*/
|
|
86
87
|
kitHint?: boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Whether the `^P prompts` clause exists at all (default `true`).
|
|
90
|
+
*
|
|
91
|
+
* `false` is the `prompts.hint` config key and the only reason the ladder is
|
|
92
|
+
* parameterised: a user who turned the header hint off must get exactly the row
|
|
93
|
+
* they had before this feature, which is what the unfiltered ladder below
|
|
94
|
+
* degenerates into.
|
|
95
|
+
*/
|
|
96
|
+
promptsHint?: boolean;
|
|
87
97
|
}): HeaderHintPlan;
|
|
@@ -58,6 +58,25 @@ const INDEX_CLAUSE = { collapsed: '^I Index', expanded: '^I build index' };
|
|
|
58
58
|
* leftward on every launch for a key pressed twice a session.
|
|
59
59
|
*/
|
|
60
60
|
const KIT_CLAUSE = { expanded: '^K switch kit' };
|
|
61
|
+
/**
|
|
62
|
+
* THE PROMPT LIBRARY KEY HAS EXACTLY ONE FORM, AND IT IS THE SHORT ONE
|
|
63
|
+
* (quick-prompt-library D-15).
|
|
64
|
+
*
|
|
65
|
+
* `^P` is a REQUIREMENT (the user must be able to see that `ctrl+P` exists), and
|
|
66
|
+
* the collapsed row is where that requirement is met. The expanded row's job is to
|
|
67
|
+
* reveal MORE keys than the collapsed one, so inserting a 17-cell
|
|
68
|
+
* `^P prompt library` before `^I` there would trade `^I` and `^K` away on an
|
|
69
|
+
* 80-column terminal - a user who pressed `^G` asking for more would be shown less.
|
|
70
|
+
*
|
|
71
|
+
* The second thing this constant's position encodes is an explicit REVISION of the
|
|
72
|
+
* `RV-P1-2` ordering constraint for the COLLAPSED ladder: `^P` sits before `^I`
|
|
73
|
+
* there, so `^I Index` now appears from 66 columns instead of 54. That is a
|
|
74
|
+
* deliberate, documented loss: `^I` is a once-per-project action and `^P` is the
|
|
75
|
+
* entry point this feature is about. The expanded ladder keeps its full existing
|
|
76
|
+
* order, so `RV-P1-2` still holds there and `^I` never disappears from a row where
|
|
77
|
+
* it used to fit.
|
|
78
|
+
*/
|
|
79
|
+
const PROMPTS_CLAUSE = '^P prompts';
|
|
61
80
|
const DETAIL_CLAUSE = { collapsed: '^G more', expanded: '^G less' };
|
|
62
81
|
/** L3 gives the key name and nothing else: discoverability at the last width. */
|
|
63
82
|
const BRANCH_KEY_ONLY = '^L';
|
|
@@ -69,8 +88,9 @@ const SCROLL_CHIP_MAX = 9999;
|
|
|
69
88
|
/**
|
|
70
89
|
* Plan the right-hand hints.
|
|
71
90
|
*
|
|
72
|
-
* The
|
|
73
|
-
* does not change, only the arithmetic behind it
|
|
91
|
+
* The inputs are the props `Header` receives plus the two switches that decide which
|
|
92
|
+
* clauses exist at all - the call site does not change, only the arithmetic behind it
|
|
93
|
+
* (spec 3.11 step 5).
|
|
74
94
|
*/
|
|
75
95
|
export function planHeaderHints(input) {
|
|
76
96
|
// NORMALISED HERE, NOT TRUSTED (R-14). `Header` defaults to 80, but a test or a
|
|
@@ -86,7 +106,7 @@ export function planHeaderHints(input) {
|
|
|
86
106
|
const detail = input.statusExpanded ? DETAILS_ON : DETAILS_OFF;
|
|
87
107
|
return buildPlan([{ id: 'detail', text: detail }], scrollCells, true);
|
|
88
108
|
}
|
|
89
|
-
const ladder = ladderFor(input.statusExpanded, input.kitHint !== false);
|
|
109
|
+
const ladder = ladderFor(input.statusExpanded, input.kitHint !== false, input.promptsHint !== false);
|
|
90
110
|
// THE SEPARATOR IS SPENT OUT OF THE SAME ROW (defect 12): left half + gap + this
|
|
91
111
|
// column have to add up to `columns`, so the gap comes off the budget instead of
|
|
92
112
|
// being assumed free.
|
|
@@ -114,7 +134,7 @@ export function planHeaderHints(input) {
|
|
|
114
134
|
* `^I` everything from 64 to 78 columns - a permanent loss, paid for a
|
|
115
135
|
* low-frequency key.
|
|
116
136
|
*/
|
|
117
|
-
function ladderFor(statusExpanded, kitHint) {
|
|
137
|
+
function ladderFor(statusExpanded, kitHint, promptsHint) {
|
|
118
138
|
const branch = {
|
|
119
139
|
id: 'branch',
|
|
120
140
|
text: statusExpanded ? BRANCH_CLAUSE.expanded : BRANCH_CLAUSE.collapsed,
|
|
@@ -127,25 +147,39 @@ function ladderFor(statusExpanded, kitHint) {
|
|
|
127
147
|
id: 'detail',
|
|
128
148
|
text: statusExpanded ? DETAIL_CLAUSE.expanded : DETAIL_CLAUSE.collapsed,
|
|
129
149
|
};
|
|
130
|
-
const
|
|
131
|
-
|
|
150
|
+
const branchKey = { id: 'branch', text: BRANCH_KEY_ONLY };
|
|
151
|
+
const prompts = { id: 'prompts', text: PROMPTS_CLAUSE };
|
|
152
|
+
const kit = { id: 'kit', text: KIT_CLAUSE.expanded };
|
|
153
|
+
// THE LADDERS ARE WRITTEN WITH EVERY CLAUSE IN PLACE AND THEN FILTERED, rather
|
|
154
|
+
// than branched per width. Two independently written tables drift: the day one of
|
|
155
|
+
// them gains a rung, the `prompts.hint=false` / `kitHint=false` degradations stop
|
|
156
|
+
// being "the same row minus that clause" and nothing would notice. Filtering a
|
|
157
|
+
// single table makes that property structural.
|
|
158
|
+
const keep = (clause) => (promptsHint || clause.id !== 'prompts') && (kitHint || clause.id !== 'kit');
|
|
159
|
+
// COLLAPSED (six rungs). `^P prompts` sits between the branch and the index, and
|
|
160
|
+
// the third rung is where `^L` loses its label in favour of `^P` - see the note
|
|
161
|
+
// on `PROMPTS_CLAUSE` above for the revision of `RV-P1-2` this encodes.
|
|
162
|
+
const collapsed = [
|
|
163
|
+
[branch, prompts, index, detail],
|
|
164
|
+
[branch, prompts, detail],
|
|
165
|
+
[branchKey, prompts, detail],
|
|
132
166
|
[branch, detail],
|
|
133
|
-
[
|
|
167
|
+
[branchKey, detail],
|
|
134
168
|
[detail],
|
|
135
169
|
];
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return [
|
|
170
|
+
// EXPANDED (seven rungs). `^P` appears in the WIDEST rung only, and AFTER `^K`,
|
|
171
|
+
// so E2..E7 - the rungs that decide what a 34..90 column terminal shows - are
|
|
172
|
+
// byte-identical to the pre-feature answers.
|
|
173
|
+
const expanded = [
|
|
174
|
+
[branch, index, kit, prompts, detail],
|
|
142
175
|
[branch, index, kit, detail],
|
|
143
176
|
[branch, index, detail],
|
|
144
177
|
[branch, kit, detail],
|
|
145
178
|
[branch, detail],
|
|
146
|
-
[
|
|
179
|
+
[branchKey, detail],
|
|
147
180
|
[detail],
|
|
148
181
|
];
|
|
182
|
+
return (statusExpanded ? expanded : collapsed).map((rung) => rung.filter(keep));
|
|
149
183
|
}
|
|
150
184
|
/**
|
|
151
185
|
* Compose the final string. The chip carries its OWN trailing spacing and is the
|
|
@@ -55,6 +55,15 @@ export function planStatusDetail(input) {
|
|
|
55
55
|
add(statusField('team', 'Team ' + status.teamActive.running + '/' + status.teamActive.total));
|
|
56
56
|
if (status.todoActive)
|
|
57
57
|
add(statusField('todo', 'Todo ' + status.todoActive.done + '/' + status.todoActive.total));
|
|
58
|
+
// AFTER `todo`, AND THE ORDER IS THE PRIORITY (D-12): `add()` only appends while
|
|
59
|
+
// the whole row still fits, so a later field is dropped FIRST. `Todo 3/7` is the
|
|
60
|
+
// more important of the two numbers, so it goes first and survives the narrower
|
|
61
|
+
// terminal; `Accept 2/5` degrades before it.
|
|
62
|
+
if (status.acceptanceActive) {
|
|
63
|
+
const acceptance = status.acceptanceActive;
|
|
64
|
+
add(statusField('acceptance', 'Accept ' + acceptance.checked + '/' + acceptance.total
|
|
65
|
+
+ (acceptance.failed > 0 ? ' !' + acceptance.failed : ''), acceptance.failed > 0 ? 'error' : 'normal'));
|
|
66
|
+
}
|
|
58
67
|
add(statusField('model', input.provider + ':' + input.model));
|
|
59
68
|
if (status.fastActive)
|
|
60
69
|
add(statusField('fast', 'Fast' + (status.fastActive.inFlight ? ' active' : '')));
|
|
@@ -51,6 +51,19 @@ export interface StatusLayoutInput {
|
|
|
51
51
|
done: number;
|
|
52
52
|
total: number;
|
|
53
53
|
};
|
|
54
|
+
/**
|
|
55
|
+
* The acceptance counter (todo-acceptance-criteria §6.11).
|
|
56
|
+
*
|
|
57
|
+
* A FALLBACK, exactly like `todoActive`: present only when the rail is NOT already
|
|
58
|
+
* reporting the number, because printing `2/5` twice in one row is the defect
|
|
59
|
+
* RV-P2-4 records. Whether it is present is decided by `acceptanceChip()` in
|
|
60
|
+
* `ui/layout/todo-layout.ts`, which owns "what did the rail actually draw".
|
|
61
|
+
*/
|
|
62
|
+
acceptanceActive?: {
|
|
63
|
+
checked: number;
|
|
64
|
+
total: number;
|
|
65
|
+
failed: number;
|
|
66
|
+
};
|
|
54
67
|
ecoRung?: number;
|
|
55
68
|
fastActive?: {
|
|
56
69
|
inFlight: boolean;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Panel row geometry — the ONE authority for how a subagent row is measured and
|
|
3
|
+
* what it says (tui-subagent-panel §4.2 / §6.1).
|
|
4
|
+
*
|
|
5
|
+
* WHY THIS FILE EXISTS. The roster used to place its right-hand columns by
|
|
6
|
+
* `flexGrow`, and a fixed-height parent box makes Yoga stretch the column to its
|
|
7
|
+
* widest CHILD instead of to the available width: on a 100-column terminal inside
|
|
8
|
+
* the band slot the row measured 48 (spec §0.1, cases 3/4), so `grep 22.1s` sat
|
|
9
|
+
* hard against the description and the right half of the row was blank. Flex
|
|
10
|
+
* cannot fix that, because the number it needs - "how wide is this terminal" - is
|
|
11
|
+
* not part of the layout it is given. So the widths are COMPUTED here, by a total
|
|
12
|
+
* pure function, and the component only places fixed-width boxes at the offsets
|
|
13
|
+
* this module names.
|
|
14
|
+
*
|
|
15
|
+
* THE FIVE CELLS, in drawing order:
|
|
16
|
+
*
|
|
17
|
+
* marker(1+status+1) label(8) description(flex) gap(2) activity(n) clock(8)
|
|
18
|
+
*
|
|
19
|
+
* `description` is the ONLY flexible cell, and it is the one that absorbs every
|
|
20
|
+
* width pressure - the same degradation discipline `StatusBar` documents for its
|
|
21
|
+
* left cluster. The two right-hand cells are fixed and `flexShrink={0}`, so their
|
|
22
|
+
* absolute offsets follow from `rowWidth = parentWidth` and the sum of the fixed
|
|
23
|
+
* cells in front of them, and never from how a particular frame allocates slack.
|
|
24
|
+
*
|
|
25
|
+
* ASCII ONLY: `src/ui/**` is inside `glyphs.test.ts`'s static scan.
|
|
26
|
+
*
|
|
27
|
+
* THE ACTIVITY HELPERS LIVE HERE TOO (v2 implementation note, recorded in the
|
|
28
|
+
* spec's "implementation findings"). `teamRowCells` has to compose the activity
|
|
29
|
+
* text with the supervisor badge, which is what `activityWithBadge` already does;
|
|
30
|
+
* leaving those in `TeamPanel.tsx` would make this module and the component
|
|
31
|
+
* import each other. `TeamPanel` therefore RE-EXPORTS every symbol below, so the
|
|
32
|
+
* existing import sites (three test files) keep their import path unchanged.
|
|
33
|
+
*/
|
|
34
|
+
import type { OverseerAction, SubagentRun } from '../../team/types.js';
|
|
35
|
+
import type { Glyphs } from '../glyphs.js';
|
|
36
|
+
/** The clock cell: wide enough for `125m07s` (7) with one column of headroom. */
|
|
37
|
+
export declare const TEAM_CLOCK_CELLS = 8;
|
|
38
|
+
/** `run.label.padEnd(8).slice(0,8)` — unchanged from the row this replaces. */
|
|
39
|
+
export declare const TEAM_LABEL_CELLS = 8;
|
|
40
|
+
/** The two spaces in front of the activity cell. */
|
|
41
|
+
export declare const TEAM_GAP_CELLS = 2;
|
|
42
|
+
/**
|
|
43
|
+
* The description column's floor: below this the row is unreadable, so the
|
|
44
|
+
* activity cell gives way instead (`activityDropCols`).
|
|
45
|
+
*/
|
|
46
|
+
export declare const TEAM_MIN_DESCRIPTION_CELLS = 12;
|
|
47
|
+
export interface TeamRowGeometry {
|
|
48
|
+
marker: number;
|
|
49
|
+
label: number;
|
|
50
|
+
gap: number;
|
|
51
|
+
activity: number;
|
|
52
|
+
clock: number;
|
|
53
|
+
/** The description column's reserved MINIMUM; the real width comes from flex. */
|
|
54
|
+
minDescription: number;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* How many characters the activity cell may spend at this width.
|
|
58
|
+
*
|
|
59
|
+
* `TEAM_LIMITS.activityChars` is a STORAGE ceiling; a terminal has a COLUMN
|
|
60
|
+
* budget, and the two are not the same number. Below
|
|
61
|
+
* `TEAM_LIMITS.activityDropCols` the cell gives way ENTIRELY (returns 0) rather
|
|
62
|
+
* than shrinking to its 12-character floor: at 40 columns those twelve characters
|
|
63
|
+
* would push the description below its own floor, and "who is doing what" outranks
|
|
64
|
+
* "what they are writing right now" exactly when there is room for one of them.
|
|
65
|
+
* That is a semantic change to a shipped assertion (`activityBudget(40)` used to
|
|
66
|
+
* return 12) and is recorded in the spec's §10 collateral table.
|
|
67
|
+
*
|
|
68
|
+
* `markerCells` defaults to the Unicode tier so the three existing single-argument
|
|
69
|
+
* call sites keep compiling; the panel passes the real tier's width.
|
|
70
|
+
*/
|
|
71
|
+
export declare function activityBudget(cols: number, markerCells?: number): number;
|
|
72
|
+
/**
|
|
73
|
+
* The row's cell widths. A TOTAL function: every cell is a non-negative integer
|
|
74
|
+
* and the five of them never sum past `cols`, for any `cols >= 0` and either
|
|
75
|
+
* glyph tier (I-3, `team-row-geometry.test.ts`'s property test).
|
|
76
|
+
*
|
|
77
|
+
* THE LADDER IS THE SPECIFICATION (§4.2.2), in the order the cells give way:
|
|
78
|
+
* 1. a wide enough terminal gets the business budget;
|
|
79
|
+
* 2. below `activityDropCols` the activity cell is 0;
|
|
80
|
+
* 3. description still negative -> the gap goes;
|
|
81
|
+
* 4. still negative -> the label shrinks to `max(3, cols - marker - clock)`;
|
|
82
|
+
* 5. still negative -> the clock shrinks (`cols < marker + 3 + clock`);
|
|
83
|
+
* 6. the residue (a terminal narrower than the marker itself) is clamped so the
|
|
84
|
+
* sum stays inside `cols` — unreachable on a real device, where native mode
|
|
85
|
+
* degrades to the placeholder below 40x12, but the function is total because
|
|
86
|
+
* the property test runs `cols = 1..400`.
|
|
87
|
+
*/
|
|
88
|
+
export declare function teamRowGeometry(cols: number, glyphs: Glyphs): TeamRowGeometry;
|
|
89
|
+
/**
|
|
90
|
+
* The clock cell's text: this child's elapsed time, or empty when it never
|
|
91
|
+
* started (a `queued` row has no clock to show, and `0ms` would be a lie).
|
|
92
|
+
*/
|
|
93
|
+
export declare function teamClockText(run: SubagentRun, at: number): string;
|
|
94
|
+
/** The past-tense word the supervisor badge and status line render. */
|
|
95
|
+
export declare function interventionWord(action: OverseerAction): string;
|
|
96
|
+
/**
|
|
97
|
+
* The row's supervisor badge (subagent-overseer-v2 AC-5): what the supervisor
|
|
98
|
+
* last did to THIS child and how long ago, e.g. `nudged 2m03s`.
|
|
99
|
+
*
|
|
100
|
+
* `lastIntervention.at` is ABSOLUTE epoch ms (R-P2-3), the same units as
|
|
101
|
+
* `startedAt`, so this is one subtraction - no second clock convention.
|
|
102
|
+
*/
|
|
103
|
+
export declare function overseerBadge(run: SubagentRun, at: number): string;
|
|
104
|
+
/**
|
|
105
|
+
* The middle cell: what this child is doing right now, in a few words.
|
|
106
|
+
*
|
|
107
|
+
* Two width policies, answering different questions. `wide` decides WHICH FACT to
|
|
108
|
+
* show - a whole path or its basename, a whole command line or its program name -
|
|
109
|
+
* mirroring `StatusBar`'s `statusCompactCols` degradation rather than inventing a
|
|
110
|
+
* second threshold. `activityBudget(cols)` decides HOW MUCH OF IT FITS, and it is
|
|
111
|
+
* a continuous function of width rather than a threshold, because the failure it
|
|
112
|
+
* prevents - a row wider than the terminal - is continuous.
|
|
113
|
+
*/
|
|
114
|
+
export declare function activityLine(run: SubagentRun, cols: number, markerCells?: number): string;
|
|
115
|
+
/**
|
|
116
|
+
* The activity cell WITH the badge composed in (AC-5 / risk table): the badge
|
|
117
|
+
* rides the SAME allocation the prose uses, so a row can never grow past its
|
|
118
|
+
* terminal; on a narrow terminal the badge degrades to the action word alone -
|
|
119
|
+
* the who-acted fact outranks the what-they-are-doing tail exactly when there is
|
|
120
|
+
* room for only one.
|
|
121
|
+
*/
|
|
122
|
+
export declare function activityWithBadge(run: SubagentRun, cols: number, at: number, markerCells?: number): string;
|
|
123
|
+
/** The five text fragments of one row, each already clamped to its cell's policy. */
|
|
124
|
+
export interface TeamRowCells {
|
|
125
|
+
marker: string;
|
|
126
|
+
label: string;
|
|
127
|
+
description: string;
|
|
128
|
+
activity: string;
|
|
129
|
+
clock: string;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* The five text fragments of one row.
|
|
133
|
+
*
|
|
134
|
+
* The description is normalized here rather than in the component because
|
|
135
|
+
* truncation preserves explicit newlines and the band reserves exactly ONE row
|
|
136
|
+
* for a child: a description carrying a newline would make the row two rows tall
|
|
137
|
+
* and every row below it would land one row lower than `band.teamRows` allows.
|
|
138
|
+
*/
|
|
139
|
+
export declare function teamRowCells(run: SubagentRun, cols: number, at: number, glyphs: Glyphs): TeamRowCells;
|