@crouton-kit/crouter 0.3.36 → 0.3.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build-root.js +2 -6
- package/dist/builtin-memory/internal/nodes-and-canvas.md +1 -1
- package/dist/builtin-personas/runtime-base.md +1 -1
- package/dist/builtin-pi-packages/pi-mode-switch/extensions/index.ts +4 -4
- package/dist/builtin-views/canvas/core.mjs +3 -2
- package/dist/clients/attach/__tests__/crtr-output-render.test.js +21 -205
- package/dist/clients/attach/attach-cmd.js +383 -386
- package/dist/clients/web/web-cmd.js +7 -7
- package/dist/commands/__tests__/human.test.js +1 -15
- package/dist/commands/attention.js +9 -6
- package/dist/commands/canvas-history/search.js +2 -2
- package/dist/commands/canvas-history/show.js +3 -3
- package/dist/commands/canvas-history.js +1 -1
- package/dist/commands/canvas-issue.js +1 -0
- package/dist/commands/canvas-snapshot.js +1 -0
- package/dist/commands/canvas.js +10 -11
- package/dist/commands/chord.js +1 -0
- package/dist/commands/human/prompts.js +0 -1
- package/dist/commands/human/queue.js +1 -0
- package/dist/commands/node-snapshot.js +1 -0
- package/dist/commands/node.js +12 -11
- package/dist/commands/push.d.ts +0 -1
- package/dist/commands/push.js +2 -228
- package/dist/commands/revive.js +2 -2
- package/dist/commands/{canvas-tmux-spread.js → surface-tmux-spread.js} +9 -6
- package/dist/commands/surface.js +3 -2
- package/dist/commands/sys/daemon.d.ts +2 -0
- package/dist/commands/{daemon.js → sys/daemon.js} +12 -12
- package/dist/commands/sys/doctor.js +1 -0
- package/dist/commands/sys.js +4 -3
- package/dist/core/__tests__/daemon-boot.test.js +1 -1
- package/dist/core/__tests__/daemon-wedge.test.d.ts +1 -0
- package/dist/core/__tests__/daemon-wedge.test.js +87 -0
- package/dist/core/__tests__/hearth-bootstrap.test.js +2 -2
- package/dist/core/__tests__/history-inbox.test.d.ts +1 -0
- package/dist/core/__tests__/history-inbox.test.js +105 -0
- package/dist/core/__tests__/kickoff.test.js +3 -3
- package/dist/core/__tests__/revive.test.js +39 -1
- package/dist/core/canvas/history.d.ts +1 -1
- package/dist/core/canvas/history.js +90 -3
- package/dist/core/canvas/paths.d.ts +6 -0
- package/dist/core/canvas/paths.js +9 -0
- package/dist/core/canvas/status-glyph.js +1 -0
- package/dist/core/fault-classifier.d.ts +2 -2
- package/dist/core/feed/inbox.d.ts +7 -12
- package/dist/core/feed/inbox.js +45 -29
- package/dist/core/hearth/providers/blaxel-bootstrap.js +1 -1
- package/dist/core/preview-registry.js +14 -112
- package/dist/core/render.js +1 -1
- package/dist/core/runtime/busy.d.ts +15 -0
- package/dist/core/runtime/busy.js +32 -1
- package/dist/core/runtime/close.d.ts +10 -0
- package/dist/core/runtime/close.js +18 -13
- package/dist/core/runtime/fault-recovery.js +4 -0
- package/dist/core/runtime/kickoff.js +5 -7
- package/dist/core/runtime/placement.js +1 -1
- package/dist/core/runtime/revive.js +9 -1
- package/dist/core/runtime/tmux.d.ts +2 -2
- package/dist/core/runtime/tmux.js +2 -2
- package/dist/daemon/crtrd-cli.js +1 -1
- package/dist/daemon/crtrd.d.ts +24 -0
- package/dist/daemon/crtrd.js +213 -5
- package/dist/daemon/manage.js +2 -2
- package/dist/pi-extensions/canvas-nav.js +2 -2
- package/dist/pi-extensions/canvas-stophook.d.ts +1 -1
- package/dist/pi-extensions/canvas-stophook.js +59 -2
- package/dist/web-client/assets/{index-IAJVtuVe.js → index-MSKSWwV0.js} +1 -1
- package/dist/web-client/index.html +1 -1
- package/package.json +1 -1
- package/dist/commands/daemon.d.ts +0 -2
- /package/dist/commands/{canvas-tmux-spread.d.ts → surface-tmux-spread.d.ts} +0 -0
package/dist/build-root.js
CHANGED
|
@@ -2,14 +2,11 @@ import { defineRoot } from './core/command.js';
|
|
|
2
2
|
const TAGLINE = 'crtr: agentic runtime.';
|
|
3
3
|
/** Lazy registry: subtree name → dynamic importer that builds its BranchDef.
|
|
4
4
|
* The whole point is that each `import()` only compiles that one subtree's
|
|
5
|
-
* module graph. The hot path (`crtr node focus …`, `crtr
|
|
5
|
+
* module graph. The hot path (`crtr node focus …`, `crtr push …`, …) dispatches
|
|
6
6
|
* into a single leaf, so it must load ONE subtree — not the attach-TUI
|
|
7
7
|
* (babel/highlight.js), the web/vite command, and every other subtree it never
|
|
8
8
|
* touches. `crtr` cold-start is dominated by Node module loading; keeping the
|
|
9
|
-
* other subtrees off the path is the biggest, lowest-risk win.
|
|
10
|
-
*
|
|
11
|
-
* Naming note: `push` and `feed` are two subtrees from one module — importing
|
|
12
|
-
* it once yields both registers. */
|
|
9
|
+
* other subtrees off the path is the biggest, lowest-risk win. */
|
|
13
10
|
const SUBTREE_LOADERS = {
|
|
14
11
|
memory: async () => (await import('./commands/memory.js')).registerMemory(),
|
|
15
12
|
search: async () => (await import('./commands/search.js')).registerSearch(),
|
|
@@ -18,7 +15,6 @@ const SUBTREE_LOADERS = {
|
|
|
18
15
|
sys: async () => (await import('./commands/sys.js')).registerSys(),
|
|
19
16
|
node: async () => (await import('./commands/node.js')).registerNode(),
|
|
20
17
|
push: async () => (await import('./commands/push.js')).registerPush(),
|
|
21
|
-
feed: async () => (await import('./commands/push.js')).registerFeed(),
|
|
22
18
|
canvas: async () => (await import('./commands/canvas.js')).registerCanvas(),
|
|
23
19
|
surface: async () => (await import('./commands/surface.js')).registerSurface(),
|
|
24
20
|
};
|
|
@@ -29,7 +29,7 @@ Nothing is reported automatically — the feed contains only what a node **pushe
|
|
|
29
29
|
- `push urgent` — force-wakes every subscriber (you're blocked, scope changed, an error derails the plan).
|
|
30
30
|
- `push final` — the ONLY way a terminal node finishes: writes the canonical result, marks the node done, and tears down its broker. Stopping without it is not finishing. Resident/user-facing nodes stay dormant instead of finishing unless explicitly forced.
|
|
31
31
|
|
|
32
|
-
A push fans a ~30-token **pointer** (a ref path), not the content; subscribers dereference lazily. When a subscriber push wakes you, **the wake message already IS the coalesced digest** —
|
|
32
|
+
A push fans a ~30-token **pointer** (a ref path), not the content; subscribers dereference lazily. When a subscriber push wakes you, **the wake message already IS the coalesced digest** — dereference the refs that matter rather than trying to re-read it. An empty feed while workers run is normal — a worker that hasn't pushed yet leaves no pointer; the wake it fires when it does push is automatic, so there's nothing to poll or check in the meantime.
|
|
33
33
|
|
|
34
34
|
## Lifecycle
|
|
35
35
|
|
|
@@ -8,7 +8,7 @@ Hand any self-contained unit of work to a child instead of doing it inline — t
|
|
|
8
8
|
|
|
9
9
|
crtr node new "<task>" --kind <kind> # `crtr node -h` lists the kinds + the delegate→feed loop
|
|
10
10
|
|
|
11
|
-
You auto-subscribe to every child you spawn, so you're woken when it finishes;
|
|
11
|
+
You auto-subscribe to every child you spawn, so you're woken when it finishes; the wake message already carries the digest — dereference the reports that matter. Prefer delegating over grinding it out yourself.
|
|
12
12
|
|
|
13
13
|
When the work depends on context you already hold — an explore report, a design, a spec, earlier findings — name those files by path in the task itself: a child starts blind and inherits only what you point it at, so unreferenced context you have is context it won't find.
|
|
14
14
|
|
|
@@ -107,8 +107,8 @@ How to operate:
|
|
|
107
107
|
it tight and reviewable.
|
|
108
108
|
- Then validate the spec with a reviewer node first: spawn one with
|
|
109
109
|
\`crtr node new "Review the spec at <absolute-path>" --kind review\` — you
|
|
110
|
-
auto-subscribe to it and are woken when it finishes
|
|
111
|
-
|
|
110
|
+
auto-subscribe to it and are woken when it finishes — the wake message
|
|
111
|
+
already carries the digest; dereference the report path it points to. It flags
|
|
112
112
|
completeness gaps, contradictions, ambiguity, scope creep, and unspecified
|
|
113
113
|
failure modes on critical paths. Resolve every Issue it raises before showing
|
|
114
114
|
the spec to anyone;
|
|
@@ -147,8 +147,8 @@ How to operate:
|
|
|
147
147
|
which tests to write, plus risks and dependencies.
|
|
148
148
|
- Then validate the plan with a reviewer node first: spawn one with
|
|
149
149
|
\`crtr node new "Review the plan at <absolute-path>" --kind review\` — you
|
|
150
|
-
auto-subscribe to it and are woken when it finishes
|
|
151
|
-
|
|
150
|
+
auto-subscribe to it and are woken when it finishes — the wake message
|
|
151
|
+
already carries the digest; dereference the report path it points to. It flags
|
|
152
152
|
completeness gaps, spec misalignment, unresolved decisions, buildability
|
|
153
153
|
problems, quality smells
|
|
154
154
|
(timelines, "for now" shortcuts, magic values, fallbacks, missing type
|
|
@@ -60,9 +60,9 @@
|
|
|
60
60
|
*/
|
|
61
61
|
/**
|
|
62
62
|
* @typedef {Object} Fault
|
|
63
|
-
* @property {'pi→provider'|'viewer↔broker'|'relay↔broker'|'browser↔relay'|'cli→exa'} link
|
|
63
|
+
* @property {'pi→provider'|'viewer↔broker'|'relay↔broker'|'browser↔relay'|'cli→exa'|'daemon→node'} link
|
|
64
64
|
* @property {string} op
|
|
65
|
-
* @property {'rate-limit'|'overloaded'|'connection'|'auth'|'protocol'|'other'} kind
|
|
65
|
+
* @property {'rate-limit'|'overloaded'|'connection'|'auth'|'protocol'|'other'|'wedged'} kind
|
|
66
66
|
* @property {FaultRetry} retry
|
|
67
67
|
* @property {string} message The raw engine error (capped).
|
|
68
68
|
* @property {string} since ISO 8601 — when the fault was first observed.
|
|
@@ -188,6 +188,7 @@ export function hangingLabel(kind) {
|
|
|
188
188
|
if (kind === 'connection') return 'down';
|
|
189
189
|
if (kind === 'auth') return 'auth';
|
|
190
190
|
if (kind === 'protocol') return 'protocol';
|
|
191
|
+
if (kind === 'wedged') return 'wedged';
|
|
191
192
|
return 'other';
|
|
192
193
|
}
|
|
193
194
|
|
|
@@ -19,7 +19,7 @@ function stripAnsi(text) {
|
|
|
19
19
|
test('detects crtr in bash command position, not heredoc body prose', () => {
|
|
20
20
|
assert.equal(isCrtrBashCommand('crtr node new -h'), true);
|
|
21
21
|
assert.equal(isCrtrBashCommand('crtr node inspect snapshot --node abc'), true);
|
|
22
|
-
assert.equal(isCrtrBashCommand('crtr
|
|
22
|
+
assert.equal(isCrtrBashCommand('crtr sys daemon status'), true);
|
|
23
23
|
assert.equal(isCrtrBashCommand('npm test && /usr/local/bin/crouter surface view run inbox'), true);
|
|
24
24
|
assert.equal(isCrtrBashCommand("cat <<'EOF'\nask about crtr node new\nEOF\necho done"), false);
|
|
25
25
|
assert.equal(isCrtrBashCommand('echo crtr node new'), false);
|
|
@@ -161,14 +161,14 @@ test('a branch help preview with more subcommands than the line cap truncates wi
|
|
|
161
161
|
// Contract: outer bash redirects are shell plumbing, not crtr argv, so the
|
|
162
162
|
// displayed crtr path and help detection include only the command segment.
|
|
163
163
|
test('a shell redirect on the outer bash call never leaks into the displayed crtr path or help detection', () => {
|
|
164
|
-
const def = createCrtrBashToolDefinition({ command: 'crtr
|
|
165
|
-
const rendered = def.renderCall({ command: 'crtr
|
|
164
|
+
const def = createCrtrBashToolDefinition({ command: 'crtr push -h 2>&1' });
|
|
165
|
+
const rendered = def.renderCall({ command: 'crtr push -h 2>&1' }, theme).render(80).join('\n');
|
|
166
166
|
assert.match(rendered, new RegExp(HELP_ICON));
|
|
167
167
|
assert.match(rendered, /crtr help/);
|
|
168
|
-
assert.match(rendered, /<b>
|
|
168
|
+
assert.match(rendered, /<b>push<\/b>/);
|
|
169
169
|
assert.doesNotMatch(rendered, /2>/);
|
|
170
170
|
assert.doesNotMatch(rendered, />&1/);
|
|
171
|
-
assert.equal(isCrtrHelpInvocation('crtr
|
|
171
|
+
assert.equal(isCrtrHelpInvocation('crtr push -h 2>&1'), true);
|
|
172
172
|
// Other redirect shapes on the outer call, and mid-argv redirects, must
|
|
173
173
|
// never survive into the path either.
|
|
174
174
|
const other = createCrtrBashToolDefinition({ command: 'crtr node new --name foo > /tmp/log 2>&1' });
|
|
@@ -177,9 +177,9 @@ test('a shell redirect on the outer bash call never leaks into the displayed crt
|
|
|
177
177
|
assert.doesNotMatch(otherRendered, /tmp\/log/);
|
|
178
178
|
// Redirect operators glued to their target with NO space (`2>/tmp/out`,
|
|
179
179
|
// `>/tmp/log`) are still shell plumbing and never displayed as path text.
|
|
180
|
-
const noSpace = createCrtrBashToolDefinition({ command: 'crtr
|
|
181
|
-
const noSpaceRendered = noSpace.renderCall({ command: 'crtr
|
|
182
|
-
assert.match(noSpaceRendered, /<b>
|
|
180
|
+
const noSpace = createCrtrBashToolDefinition({ command: 'crtr push -h 2>/tmp/out' });
|
|
181
|
+
const noSpaceRendered = noSpace.renderCall({ command: 'crtr push -h 2>/tmp/out' }, theme).render(80).join('\n');
|
|
182
|
+
assert.match(noSpaceRendered, /<b>push<\/b>/);
|
|
183
183
|
assert.doesNotMatch(noSpaceRendered, /2>/);
|
|
184
184
|
assert.doesNotMatch(noSpaceRendered, /tmp\/out/);
|
|
185
185
|
const noSpaceMid = createCrtrBashToolDefinition({ command: 'crtr node new --name foo >/tmp/log' });
|
|
@@ -206,12 +206,12 @@ test('a collapsed help card never shows the help label twice (header + body)', (
|
|
|
206
206
|
// (redirects and all) plus the exact, unsummarized output, for both help and
|
|
207
207
|
// normal commands.
|
|
208
208
|
test('expanded view shows the original raw command (redirects included) plus the raw output', () => {
|
|
209
|
-
const def = createCrtrBashToolDefinition({ command: 'crtr
|
|
209
|
+
const def = createCrtrBashToolDefinition({ command: 'crtr push -h 2>&1' });
|
|
210
210
|
const expanded = def
|
|
211
211
|
.renderResult({ content: [{ type: 'text', text: LEAF_SCHEMA }] }, { expanded: true }, theme, {})
|
|
212
212
|
.render(80)
|
|
213
213
|
.join('\n');
|
|
214
|
-
assert.match(expanded, /\$ crtr
|
|
214
|
+
assert.match(expanded, /\$ crtr push -h 2>&1/);
|
|
215
215
|
assert.match(expanded, /render the canvas as an ASCII subscription/);
|
|
216
216
|
assert.match(expanded, /Read-only: queries canvas\.db/);
|
|
217
217
|
// Normal (non-help) command: expanded still carries the raw invocation.
|
|
@@ -236,15 +236,15 @@ test('a running or failed crtr -h invocation never gets the muted help status',
|
|
|
236
236
|
// the command and the output is right there. Success (help or normal) carries
|
|
237
237
|
// no body status; only running/failed do.
|
|
238
238
|
test('a successful crtr call shows no "crtr done" body status line', () => {
|
|
239
|
-
const def = createCrtrBashToolDefinition({ command: 'crtr
|
|
239
|
+
const def = createCrtrBashToolDefinition({ command: 'crtr push update' });
|
|
240
240
|
const collapsed = def
|
|
241
|
-
.renderResult({ content: [{ type: 'text', text: 'some
|
|
241
|
+
.renderResult({ content: [{ type: 'text', text: 'some push output' }] }, { expanded: false }, theme, {})
|
|
242
242
|
.render(80)
|
|
243
243
|
.join('\n');
|
|
244
244
|
assert.doesNotMatch(collapsed, /crtr done/);
|
|
245
|
-
assert.match(collapsed, /some
|
|
245
|
+
assert.match(collapsed, /some push output/);
|
|
246
246
|
const expanded = def
|
|
247
|
-
.renderResult({ content: [{ type: 'text', text: 'some
|
|
247
|
+
.renderResult({ content: [{ type: 'text', text: 'some push output' }] }, { expanded: true }, theme, {})
|
|
248
248
|
.render(80)
|
|
249
249
|
.join('\n');
|
|
250
250
|
assert.doesNotMatch(expanded, /crtr done/);
|
|
@@ -428,7 +428,7 @@ test('renderCall uses a mapped Nerd Font glyph and one line for every mapped roo
|
|
|
428
428
|
const cases = [
|
|
429
429
|
{ command: 'crtr memory lint', iconTokens: ['memory', 'lint'], path: 'memory lint' },
|
|
430
430
|
{ command: 'crtr canvas dashboard', iconTokens: ['canvas', 'dashboard'], path: 'canvas dashboard' },
|
|
431
|
-
{ command: 'crtr
|
|
431
|
+
{ command: 'crtr node yield', iconTokens: ['node', 'yield'], path: 'node yield' },
|
|
432
432
|
{ command: 'crtr human ask', iconTokens: ['human', 'ask'], path: 'human ask' },
|
|
433
433
|
{ command: 'crtr node config set foo bar', iconTokens: ['node', 'config'], path: 'node config set foo bar' },
|
|
434
434
|
{ command: 'crtr pkg plugin list', iconTokens: ['pkg', 'plugin', 'list'], path: 'pkg plugin list' },
|
|
@@ -583,7 +583,7 @@ test('icon lookup uses per-leaf glyphs before family glyphs, with help unchanged
|
|
|
583
583
|
assert.equal(HELP_ICON, String.fromCodePoint(0xf059));
|
|
584
584
|
});
|
|
585
585
|
// ---------------------------------------------------------------------------
|
|
586
|
-
// canvas/node
|
|
586
|
+
// canvas/node runtime cockpit summaries (first non-memory batch, Silas's
|
|
587
587
|
// 2026-07-01 reframe: collapsed cards are computed status digests, not
|
|
588
588
|
// decorated stdout). Fixtures below are literal renderResult() output shapes
|
|
589
589
|
// (core/render.ts: a `- field: N` scalar bullet, an `N <label>:` count
|
|
@@ -715,7 +715,7 @@ test('node inspect show is command-owned suppressOutput, never on failure', () =
|
|
|
715
715
|
assert.match(failed.render(80).join('\n'), /huge dump on failure too/);
|
|
716
716
|
});
|
|
717
717
|
// ---------------------------------------------------------------------------
|
|
718
|
-
// Batch B: canvas/node/
|
|
718
|
+
// Batch B: canvas/node/push runtime cockpit summaries (Silas's
|
|
719
719
|
// 2026-07-01 reframe continued). Fixtures below are literal render() output
|
|
720
720
|
// shapes copied from the real command source (see preview-registry.ts's
|
|
721
721
|
// per-summarizer doc comments for the exact file:line), not the live command
|
|
@@ -961,8 +961,8 @@ test('a crtr canvas history show card collapses to a node line + type-count dige
|
|
|
961
961
|
.join('\n');
|
|
962
962
|
assert.match(emptyExpanded, /0 artifacts\./);
|
|
963
963
|
});
|
|
964
|
-
test('crtr
|
|
965
|
-
const start = createCrtrBashToolDefinition({ command: 'crtr
|
|
964
|
+
test('crtr sys daemon start/status/stop cards collapse to a one-line pid status', () => {
|
|
965
|
+
const start = createCrtrBashToolDefinition({ command: 'crtr sys daemon start' });
|
|
966
966
|
assert.match(start.renderResult({ content: [{ type: 'text', text: '- started: true\n- pid: 4242' }] }, { expanded: false }, theme, {}).render(120).join('\n'), /<success>started · pid 4242<\/success>/);
|
|
967
967
|
assert.match(start.renderResult({ content: [{ type: 'text', text: '- started: false\n- existing_pid: 99' }] }, { expanded: false }, theme, {}).render(120).join('\n'), /<muted>already running · pid 99<\/muted>/);
|
|
968
968
|
assert.match(start.renderResult({ content: [{ type: 'text', text: '- started: false\n- existing_pid: 99' }] }, { expanded: true }, theme, {}).render(120).join('\n'), /existing_pid: 99/);
|
|
@@ -972,7 +972,7 @@ test('crtr canvas daemon start/status/stop cards collapse to a one-line pid stat
|
|
|
972
972
|
.join('\n');
|
|
973
973
|
assert.match(startExpanded, /started: true/);
|
|
974
974
|
assert.match(startExpanded, /pid: 4242/);
|
|
975
|
-
const status = createCrtrBashToolDefinition({ command: 'crtr
|
|
975
|
+
const status = createCrtrBashToolDefinition({ command: 'crtr sys daemon status' });
|
|
976
976
|
assert.match(status.renderResult({ content: [{ type: 'text', text: '- running: true\n- pid: 4242' }] }, { expanded: false }, theme, {}).render(120).join('\n'), /<success>running · pid 4242<\/success>/);
|
|
977
977
|
assert.match(status.renderResult({ content: [{ type: 'text', text: '- running: false' }] }, { expanded: false }, theme, {}).render(120).join('\n'), /<error>not running<\/error>/);
|
|
978
978
|
assert.match(status.renderResult({ content: [{ type: 'text', text: '- running: false' }] }, { expanded: true }, theme, {}).render(120).join('\n'), /running: false/);
|
|
@@ -982,7 +982,7 @@ test('crtr canvas daemon start/status/stop cards collapse to a one-line pid stat
|
|
|
982
982
|
.join('\n');
|
|
983
983
|
assert.match(statusExpanded, /running: true/);
|
|
984
984
|
assert.match(statusExpanded, /pid: 4242/);
|
|
985
|
-
const stop = createCrtrBashToolDefinition({ command: 'crtr
|
|
985
|
+
const stop = createCrtrBashToolDefinition({ command: 'crtr sys daemon stop' });
|
|
986
986
|
assert.match(stop.renderResult({ content: [{ type: 'text', text: '- stopped: true\n- pid: 4242' }] }, { expanded: false }, theme, {}).render(120).join('\n'), /stopped · pid 4242/);
|
|
987
987
|
assert.match(stop.renderResult({ content: [{ type: 'text', text: '- stopped: false' }] }, { expanded: false }, theme, {}).render(120).join('\n'), /<muted>was not running<\/muted>/);
|
|
988
988
|
assert.match(stop.renderResult({ content: [{ type: 'text', text: '- stopped: false' }] }, { expanded: true }, theme, {}).render(120).join('\n'), /stopped: false/);
|
|
@@ -1274,190 +1274,6 @@ test('a crtr node triggers list card collapses to a pending-count + top rows, no
|
|
|
1274
1274
|
.join('\n');
|
|
1275
1275
|
assert.match(emptyExpanded, /No pending triggers in scope self\./);
|
|
1276
1276
|
});
|
|
1277
|
-
test('a crtr feed read card collapses to a drained-pointer digest, not the raw coalesced sections', () => {
|
|
1278
|
-
const def = createCrtrBashToolDefinition({ command: 'crtr feed read' });
|
|
1279
|
-
const drained = def
|
|
1280
|
-
.renderResult({
|
|
1281
|
-
content: [
|
|
1282
|
-
{
|
|
1283
|
-
type: 'text',
|
|
1284
|
-
text: [
|
|
1285
|
-
"3 unread pointers drained from mq123's inbox.",
|
|
1286
|
-
'',
|
|
1287
|
-
'From worker-a — 2 updates:',
|
|
1288
|
-
' [push] did a thing (ref: worker-a:reports/x.md)',
|
|
1289
|
-
' [push] did another (ref: worker-a:reports/y.md)',
|
|
1290
|
-
'',
|
|
1291
|
-
'From worker-b — 1 update:',
|
|
1292
|
-
' [push] finished (ref: worker-b:reports/z.md)',
|
|
1293
|
-
].join('\n'),
|
|
1294
|
-
},
|
|
1295
|
-
],
|
|
1296
|
-
}, { expanded: false }, theme, {})
|
|
1297
|
-
.render(140)
|
|
1298
|
-
.join('\n');
|
|
1299
|
-
assert.match(drained, /3 pointers drained from 2 nodes/);
|
|
1300
|
-
assert.match(drained, /<muted>worker-a, worker-b<\/muted>/);
|
|
1301
|
-
assert.doesNotMatch(drained, /ref: worker-a/);
|
|
1302
|
-
const drainedExpanded = def
|
|
1303
|
-
.renderResult({
|
|
1304
|
-
content: [
|
|
1305
|
-
{
|
|
1306
|
-
type: 'text',
|
|
1307
|
-
text: [
|
|
1308
|
-
"3 unread pointers drained from mq123's inbox.",
|
|
1309
|
-
'',
|
|
1310
|
-
'From worker-a — 2 updates:',
|
|
1311
|
-
' [push] did a thing (ref: worker-a:reports/x.md)',
|
|
1312
|
-
' [push] did another (ref: worker-a:reports/y.md)',
|
|
1313
|
-
'',
|
|
1314
|
-
'From worker-b — 1 update:',
|
|
1315
|
-
' [push] finished (ref: worker-b:reports/z.md)',
|
|
1316
|
-
].join('\n'),
|
|
1317
|
-
},
|
|
1318
|
-
],
|
|
1319
|
-
}, { expanded: true }, theme, {})
|
|
1320
|
-
.render(140)
|
|
1321
|
-
.join('\n');
|
|
1322
|
-
assert.match(drainedExpanded, /ref: worker-a:reports\/x\.md/);
|
|
1323
|
-
const empty = def
|
|
1324
|
-
.renderResult({ content: [{ type: 'text', text: 'No unread pointers for mq123 (unread=0).\n\nYour inbox is empty — nothing has arrived yet.' }] }, { expanded: false }, theme, {})
|
|
1325
|
-
.render(120)
|
|
1326
|
-
.join('\n');
|
|
1327
|
-
assert.match(empty, /<muted>nothing unread<\/muted>/);
|
|
1328
|
-
const emptyExpanded = def
|
|
1329
|
-
.renderResult({ content: [{ type: 'text', text: 'No unread pointers for mq123 (unread=0).\n\nYour inbox is empty — nothing has arrived yet.' }] }, { expanded: true }, theme, {})
|
|
1330
|
-
.render(120)
|
|
1331
|
-
.join('\n');
|
|
1332
|
-
assert.match(emptyExpanded, /Your inbox is empty — nothing has arrived yet\./);
|
|
1333
|
-
});
|
|
1334
|
-
test('a crtr feed message card previews an identity line + body teaser, not the raw label', () => {
|
|
1335
|
-
const def = createCrtrBashToolDefinition({ command: 'crtr feed message m1' });
|
|
1336
|
-
const found = def
|
|
1337
|
-
.renderResult({ content: [{ type: 'text', text: ['Full message m1 (from mq123):', '', 'Line one', 'Line two', 'Line three', 'Line four'].join('\n') }] }, { expanded: false }, theme, {})
|
|
1338
|
-
.render(120)
|
|
1339
|
-
.join('\n');
|
|
1340
|
-
assert.match(found, /m1 · from mq123/);
|
|
1341
|
-
assert.match(found, /Line one/);
|
|
1342
|
-
assert.match(found, /…/);
|
|
1343
|
-
assert.doesNotMatch(found, /Line four/);
|
|
1344
|
-
const foundExpanded = def
|
|
1345
|
-
.renderResult({ content: [{ type: 'text', text: ['Full message m1 (from mq123):', '', 'Line one', 'Line two', 'Line three', 'Line four'].join('\n') }] }, { expanded: true }, theme, {})
|
|
1346
|
-
.render(120)
|
|
1347
|
-
.join('\n');
|
|
1348
|
-
assert.match(foundExpanded, /Line four/);
|
|
1349
|
-
const notFound = def
|
|
1350
|
-
.renderResult({ content: [{ type: 'text', text: "No inbox entry with id m1 in mq123's inbox. Re-read the digest with `crtr feed read --all`." }] }, { expanded: false }, theme, {})
|
|
1351
|
-
.render(120)
|
|
1352
|
-
.join('\n');
|
|
1353
|
-
assert.match(notFound, /No inbox entry with id m1/);
|
|
1354
|
-
const notFoundExpanded = def
|
|
1355
|
-
.renderResult({ content: [{ type: 'text', text: "No inbox entry with id m1 in mq123's inbox. Re-read the digest with `crtr feed read --all`." }] }, { expanded: true }, theme, {})
|
|
1356
|
-
.render(120)
|
|
1357
|
-
.join('\n');
|
|
1358
|
-
assert.match(notFoundExpanded, /Re-read the digest with `crtr feed read --all`\./);
|
|
1359
|
-
});
|
|
1360
|
-
test('a crtr feed peek card collapses to a verdict + working/done/dead counts, not the raw rows', () => {
|
|
1361
|
-
const def = createCrtrBashToolDefinition({ command: 'crtr feed peek' });
|
|
1362
|
-
const zeroClear = def
|
|
1363
|
-
.renderResult({ content: [{ type: 'text', text: 'No nodes below you (0 subscriptions, 0 unread). Nothing will wake you.' }] }, { expanded: false }, theme, {})
|
|
1364
|
-
.render(120)
|
|
1365
|
-
.join('\n');
|
|
1366
|
-
assert.match(zeroClear, /<success>PASS · nothing below<\/success>/);
|
|
1367
|
-
const zeroClearExpanded = def
|
|
1368
|
-
.renderResult({ content: [{ type: 'text', text: 'No nodes below you (0 subscriptions, 0 unread). Nothing will wake you.' }] }, { expanded: true }, theme, {})
|
|
1369
|
-
.render(120)
|
|
1370
|
-
.join('\n');
|
|
1371
|
-
assert.match(zeroClearExpanded, /Nothing will wake you\./);
|
|
1372
|
-
const zeroUnread = def
|
|
1373
|
-
.renderResult({ content: [{ type: 'text', text: 'No nodes below you (0 subscriptions, 4 unread). 4 unread reports sit in your inbox.' }] }, { expanded: false }, theme, {})
|
|
1374
|
-
.render(120)
|
|
1375
|
-
.join('\n');
|
|
1376
|
-
assert.match(zeroUnread, /<warning>0 below · 4 unread<\/warning>/);
|
|
1377
|
-
const zeroUnreadExpanded = def
|
|
1378
|
-
.renderResult({ content: [{ type: 'text', text: 'No nodes below you (0 subscriptions, 4 unread). 4 unread reports sit in your inbox.' }] }, { expanded: true }, theme, {})
|
|
1379
|
-
.render(120)
|
|
1380
|
-
.join('\n');
|
|
1381
|
-
assert.match(zeroUnreadExpanded, /4 unread reports sit in your inbox\./);
|
|
1382
|
-
const withDead = def
|
|
1383
|
-
.renderResult({
|
|
1384
|
-
content: [
|
|
1385
|
-
{
|
|
1386
|
-
type: 'text',
|
|
1387
|
-
text: [
|
|
1388
|
-
'⚠ 1 below you is dead and will NOT wake you.',
|
|
1389
|
-
'',
|
|
1390
|
-
'3 nodes below you — 1 working, 1 done, 1 dead, 2 unread:',
|
|
1391
|
-
'- ● worker-a (mq1) · developer · active · spawned 5m ago · cyc 2 · pushed 1m ago [push]',
|
|
1392
|
-
'- ✓ worker-b (mq2) · developer · done · spawned 10m ago · cyc 3 · pushed 2m ago [push]',
|
|
1393
|
-
'- ✗ worker-c (mq3) · developer · dead · spawned 20m ago · cyc 1 · no push yet',
|
|
1394
|
-
].join('\n'),
|
|
1395
|
-
},
|
|
1396
|
-
],
|
|
1397
|
-
}, { expanded: false }, theme, {})
|
|
1398
|
-
.render(140)
|
|
1399
|
-
.join('\n');
|
|
1400
|
-
assert.match(withDead, /1 working · 1 done · 1 dead/);
|
|
1401
|
-
assert.match(withDead, /<warning>1 dead — will not wake you<\/warning>/);
|
|
1402
|
-
assert.doesNotMatch(withDead, /pushed 1m ago/);
|
|
1403
|
-
const withDeadExpanded = def
|
|
1404
|
-
.renderResult({
|
|
1405
|
-
content: [
|
|
1406
|
-
{
|
|
1407
|
-
type: 'text',
|
|
1408
|
-
text: [
|
|
1409
|
-
'⚠ 1 below you is dead and will NOT wake you.',
|
|
1410
|
-
'',
|
|
1411
|
-
'3 nodes below you — 1 working, 1 done, 1 dead, 2 unread:',
|
|
1412
|
-
'- ● worker-a (mq1) · developer · active · spawned 5m ago · cyc 2 · pushed 1m ago [push]',
|
|
1413
|
-
'- ✓ worker-b (mq2) · developer · done · spawned 10m ago · cyc 3 · pushed 2m ago [push]',
|
|
1414
|
-
'- ✗ worker-c (mq3) · developer · dead · spawned 20m ago · cyc 1 · no push yet',
|
|
1415
|
-
].join('\n'),
|
|
1416
|
-
},
|
|
1417
|
-
],
|
|
1418
|
-
}, { expanded: true }, theme, {})
|
|
1419
|
-
.render(140)
|
|
1420
|
-
.join('\n');
|
|
1421
|
-
assert.match(withDeadExpanded, /pushed 1m ago/);
|
|
1422
|
-
const noDead = def
|
|
1423
|
-
.renderResult({
|
|
1424
|
-
content: [
|
|
1425
|
-
{
|
|
1426
|
-
type: 'text',
|
|
1427
|
-
text: [
|
|
1428
|
-
'Safe to yield — 1 worker running async will wake you.',
|
|
1429
|
-
'',
|
|
1430
|
-
'2 nodes below you — 1 working, 1 done, 0 dead, 3 unread:',
|
|
1431
|
-
'- ● worker-a (mq1) · developer · active · spawned 5m ago · cyc 2 · pushed 1m ago [push]',
|
|
1432
|
-
'- ✓ worker-b (mq2) · developer · done · spawned 10m ago · cyc 3 · pushed 2m ago [push]',
|
|
1433
|
-
].join('\n'),
|
|
1434
|
-
},
|
|
1435
|
-
],
|
|
1436
|
-
}, { expanded: false }, theme, {})
|
|
1437
|
-
.render(140)
|
|
1438
|
-
.join('\n');
|
|
1439
|
-
assert.match(noDead, /1 working · 1 done/);
|
|
1440
|
-
assert.doesNotMatch(noDead, /0 dead/);
|
|
1441
|
-
assert.match(noDead, /<warning>3 unread<\/warning>/);
|
|
1442
|
-
const noDeadExpanded = def
|
|
1443
|
-
.renderResult({
|
|
1444
|
-
content: [
|
|
1445
|
-
{
|
|
1446
|
-
type: 'text',
|
|
1447
|
-
text: [
|
|
1448
|
-
'Safe to yield — 1 worker running async will wake you.',
|
|
1449
|
-
'',
|
|
1450
|
-
'2 nodes below you — 1 working, 1 done, 0 dead, 3 unread:',
|
|
1451
|
-
'- ● worker-a (mq1) · developer · active · spawned 5m ago · cyc 2 · pushed 1m ago [push]',
|
|
1452
|
-
'- ✓ worker-b (mq2) · developer · done · spawned 10m ago · cyc 3 · pushed 2m ago [push]',
|
|
1453
|
-
].join('\n'),
|
|
1454
|
-
},
|
|
1455
|
-
],
|
|
1456
|
-
}, { expanded: true }, theme, {})
|
|
1457
|
-
.render(140)
|
|
1458
|
-
.join('\n');
|
|
1459
|
-
assert.match(noDeadExpanded, /Safe to yield — 1 worker running async will wake you\./);
|
|
1460
|
-
});
|
|
1461
1277
|
test('crtr push urgent/final cards collapse to a one-line outcome + report basename', () => {
|
|
1462
1278
|
const urgentDef = createCrtrBashToolDefinition({ command: 'crtr push urgent' });
|
|
1463
1279
|
const urgent = urgentDef
|