@north-light/crouter 0.3.158 → 0.3.160
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/api/dto/nodes.d.ts +3 -0
- package/dist/builtin-memory/internal/INDEX.md +1 -0
- package/dist/builtin-memory/internal/storage-tiers.md +2 -0
- package/dist/builtin-memory/internal/workflow-codification.md +82 -0
- package/dist/clients/attach/__tests__/bash-call-width.test.d.ts +1 -0
- package/dist/clients/attach/__tests__/bash-call-width.test.js +38 -0
- package/dist/clients/attach/__tests__/chat-view-snapshot-ordering.test.js +20 -0
- package/dist/clients/attach/__tests__/crtr-output-coverage.test.js +3 -1
- package/dist/clients/attach/chrome/bash-jobs.d.ts +5 -8
- package/dist/clients/attach/chrome/bash-jobs.js +20 -32
- package/dist/clients/attach/chrome/canvas-panels.js +10 -3
- package/dist/clients/attach/chrome/roster.d.ts +10 -4
- package/dist/clients/attach/chrome/roster.js +96 -36
- package/dist/clients/attach/input/controller.d.ts +3 -0
- package/dist/clients/attach/input/controller.js +1 -0
- package/dist/clients/attach/render/assistant-message.d.ts +13 -0
- package/dist/clients/attach/render/assistant-message.js +55 -0
- package/dist/clients/attach/render/chat-view.d.ts +29 -0
- package/dist/clients/attach/render/chat-view.js +233 -12
- package/dist/clients/attach/render/context-message.js +1 -1
- package/dist/clients/attach/render/edit-diff.js +20 -1
- package/dist/clients/attach/render/tool-calls.d.ts +32 -0
- package/dist/clients/attach/render/tool-calls.js +251 -0
- package/dist/clients/attach/session/input-wiring.d.ts +3 -0
- package/dist/clients/attach/session/input-wiring.js +3 -0
- package/dist/clients/attach/slash/dispatch.d.ts +4 -0
- package/dist/clients/attach/slash/dispatch.js +8 -0
- package/dist/clients/attach/viewer.js +568 -566
- package/dist/commands/cron.js +2 -2
- package/dist/commands/node.js +141 -3
- package/dist/commands/search.js +1 -1
- package/dist/commands/surface-inspect.js +4 -3
- package/dist/commands/sys/config.js +10 -2
- package/dist/commands/sys/doctor.js +22 -1
- package/dist/commands/sys/setup-core.d.ts +5 -1
- package/dist/commands/sys/setup-core.js +16 -2
- package/dist/commands/sys/setup-wizard.d.ts +11 -0
- package/dist/commands/sys/setup-wizard.js +111 -6
- package/dist/core/__tests__/canvas-inbox-watcher.test.js +5 -3
- package/dist/core/__tests__/tmux-surface.test.js +5 -2
- package/dist/core/bash-jobs.d.ts +11 -0
- package/dist/core/bash-jobs.js +41 -1
- package/dist/core/canvas/canvas.js +1 -1
- package/dist/core/canvas/labels.d.ts +1 -0
- package/dist/core/canvas/labels.js +3 -1
- package/dist/core/canvas/nav-render.d.ts +6 -0
- package/dist/core/canvas/nav-render.js +8 -0
- package/dist/core/canvas/types.d.ts +6 -0
- package/dist/core/config.js +3 -2
- package/dist/core/feed/inbox.d.ts +3 -3
- package/dist/core/feed/inbox.js +10 -6
- package/dist/core/inspector/core.d.ts +17 -2
- package/dist/core/inspector/core.js +172 -23
- package/dist/core/inspector/model.d.ts +30 -1
- package/dist/core/inspector/model.js +39 -0
- package/dist/core/inspector/text.js +14 -1
- package/dist/core/inspector/tui.js +71 -3
- package/dist/core/keybindings/__tests__/resolve.test.js +2 -2
- package/dist/core/keybindings/catalog.d.ts +3 -3
- package/dist/core/keybindings/catalog.js +5 -2
- package/dist/core/preview-registry.d.ts +5 -0
- package/dist/core/preview-registry.js +10 -0
- package/dist/core/runtime/canvas-extensions.d.ts +4 -0
- package/dist/core/runtime/canvas-extensions.js +4 -0
- package/dist/core/runtime/naming-persist.d.ts +4 -3
- package/dist/core/runtime/naming-persist.js +10 -9
- package/dist/core/runtime/naming.d.ts +28 -8
- package/dist/core/runtime/naming.js +168 -32
- package/dist/core/runtime/nerd-font.d.ts +28 -0
- package/dist/core/runtime/nerd-font.js +127 -0
- package/dist/core/runtime/tmux.js +16 -0
- package/dist/core/tui/host.js +21 -6
- package/dist/daemon/api/map.js +2 -0
- package/dist/pi-extensions/canvas-bash-valve.js +5 -0
- package/dist/pi-extensions/naming-tool.d.ts +24 -0
- package/dist/pi-extensions/naming-tool.js +67 -0
- package/dist/types.d.ts +5 -0
- package/dist/types.js +1 -0
- package/dist/web-client/assets/index-B76ZKfT_.js +79 -0
- package/dist/web-client/assets/{index-CpEl9LTS.css → index-CqLKj8Xu.css} +1 -1
- package/dist/web-client/index.html +2 -2
- package/dist/web-client/sw.js +1 -1
- package/package.json +1 -1
- package/runtime.lock.json +2 -2
- package/dist/web-client/assets/index-CsuwzlcQ.js +0 -79
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
// render/tool-calls.ts — attach-viewer call headers for the plain agent tools.
|
|
2
|
+
//
|
|
3
|
+
// pi's builtin renderers name a tool with a bold word (`read <path>`,
|
|
4
|
+
// `$ <the entire command line>`). In the crtr viewer a tool call is a card the
|
|
5
|
+
// human scans, not a shell echo (inline-ui-placement rule 12): each tool gets
|
|
6
|
+
// its own Nerd Font glyph, and `bash` gets a compact, syntax-colored ONE-LINE
|
|
7
|
+
// summary instead of dumping a multi-line command into the transcript. The
|
|
8
|
+
// full command is still one ctrl+o away (rule 10: terse by default, reveal on
|
|
9
|
+
// demand).
|
|
10
|
+
//
|
|
11
|
+
// Only the CALL is re-skinned here; every result renderer stays pi's builtin,
|
|
12
|
+
// so output, truncation, timing and error chrome are untouched.
|
|
13
|
+
import { createBashToolDefinition, createReadToolDefinition, createWriteToolDefinition } from '@earendil-works/pi-coding-agent';
|
|
14
|
+
import { Text, getCapabilities, hyperlink, truncateToWidth, visibleWidth, wrapTextWithAnsi } from '@earendil-works/pi-tui';
|
|
15
|
+
import { homedir } from 'node:os';
|
|
16
|
+
import { isAbsolute, resolve } from 'node:path';
|
|
17
|
+
import { pathToFileURL } from 'node:url';
|
|
18
|
+
import { TOOL_ICON } from '../../../core/preview-registry.js';
|
|
19
|
+
function str(value) {
|
|
20
|
+
return typeof value === 'string' ? value : '';
|
|
21
|
+
}
|
|
22
|
+
/** Key in a tool's shared render-state bag (`context.state`) carrying the
|
|
23
|
+
* viewer's fold. `MinimizableToolComponent` sets it; the fold-aware renderCalls
|
|
24
|
+
* below read it and emit ONLY their one-line header while it is true, which is
|
|
25
|
+
* what makes a folded card exactly `<icon> <tool> <path>` with no body — the
|
|
26
|
+
* renderer owns its folded form, no post-hoc slicing of rendered rows. */
|
|
27
|
+
export const FOLDED_STATE_KEY = 'crtrFolded';
|
|
28
|
+
export function isFoldedContext(context) {
|
|
29
|
+
return context.state?.[FOLDED_STATE_KEY] === true;
|
|
30
|
+
}
|
|
31
|
+
/** The one-line folded form of a file tool's call: `<icon> <label> <path>`. */
|
|
32
|
+
export function foldedFileCallLine(tool, rawPath, theme, cwd) {
|
|
33
|
+
return `${header(TOOL_ICON[tool], tool, theme)} ${pathDisplay(rawPath, theme, cwd)}`;
|
|
34
|
+
}
|
|
35
|
+
/** `~`-shorten an absolute path the way pi's renderToolPath does (its
|
|
36
|
+
* render-utils module is not exported from the package root). */
|
|
37
|
+
function shortenPath(path) {
|
|
38
|
+
const home = homedir();
|
|
39
|
+
return path.startsWith(home) ? `~${path.slice(home.length)}` : path;
|
|
40
|
+
}
|
|
41
|
+
/** Accent-colored, OSC-8-linked path — the same affordance pi gives file tools. */
|
|
42
|
+
function pathDisplay(rawPath, theme, cwd) {
|
|
43
|
+
if (!rawPath)
|
|
44
|
+
return theme.fg('toolOutput', '...');
|
|
45
|
+
const styled = theme.fg('accent', shortenPath(rawPath));
|
|
46
|
+
if (!getCapabilities().hyperlinks)
|
|
47
|
+
return styled;
|
|
48
|
+
const absolute = isAbsolute(rawPath) ? rawPath : resolve(cwd, rawPath);
|
|
49
|
+
return hyperlink(styled, pathToFileURL(absolute).href);
|
|
50
|
+
}
|
|
51
|
+
/** `<icon> <label>` — the glyph in accent, the tool name in pi's tool title style. */
|
|
52
|
+
function header(icon, label, theme) {
|
|
53
|
+
return `${theme.fg('accent', icon)} ${theme.fg('toolTitle', theme.bold(label))}`;
|
|
54
|
+
}
|
|
55
|
+
/** Prefix a pi-built `Text` in place (its `text` field is TS-private but a plain
|
|
56
|
+
* runtime property). Used where the header line is baked into the same string as
|
|
57
|
+
* a body we want to keep verbatim — write's syntax-highlighted content preview,
|
|
58
|
+
* edit's header inside its own card. Silently no-ops if pi's Text ever stops
|
|
59
|
+
* looking like this, so the worst case is a missing glyph, never a crash. */
|
|
60
|
+
export function prefixToolText(component, prefix) {
|
|
61
|
+
const text = component;
|
|
62
|
+
if (typeof text.text !== 'string' || typeof text.setText !== 'function')
|
|
63
|
+
return;
|
|
64
|
+
if (text.text.startsWith(prefix))
|
|
65
|
+
return;
|
|
66
|
+
text.setText(`${prefix}${text.text}`);
|
|
67
|
+
}
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// read
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
/** Attach-viewer `read`: an icon + the path (plus any `:offset-limit` range).
|
|
72
|
+
* The call is a single line by construction, so a folded read shows exactly
|
|
73
|
+
* what it read and nothing else. */
|
|
74
|
+
export function createCrtrReadToolDefinition(cwd) {
|
|
75
|
+
const builtin = createReadToolDefinition(cwd);
|
|
76
|
+
return {
|
|
77
|
+
name: 'read',
|
|
78
|
+
label: 'read',
|
|
79
|
+
description: 'Render read calls with a crtr icon header in the attach viewer.',
|
|
80
|
+
parameters: {},
|
|
81
|
+
renderCall(args, theme, context) {
|
|
82
|
+
const a = args;
|
|
83
|
+
const raw = str(a?.file_path ?? a?.path);
|
|
84
|
+
let range = '';
|
|
85
|
+
if (a?.offset !== undefined || a?.limit !== undefined) {
|
|
86
|
+
const start = typeof a?.offset === 'number' ? a.offset : 1;
|
|
87
|
+
const end = typeof a?.limit === 'number' ? start + a.limit - 1 : undefined;
|
|
88
|
+
range = theme.fg('warning', `:${start}${end !== undefined ? `-${end}` : ''}`);
|
|
89
|
+
}
|
|
90
|
+
const line = `${header(TOOL_ICON.read, 'read', theme)} ${pathDisplay(raw, theme, context.cwd ?? cwd)}${range}`;
|
|
91
|
+
const component = context.lastComponent ?? new Text('', 0, 0);
|
|
92
|
+
component.setText(line);
|
|
93
|
+
return component;
|
|
94
|
+
},
|
|
95
|
+
renderResult(result, options, theme, context) {
|
|
96
|
+
return builtin.renderResult(result, options, theme, context);
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
// write
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
/** Attach-viewer `write`: pi's builtin call renderer (it owns the
|
|
104
|
+
* syntax-highlighted content preview and its expand hint) with the icon
|
|
105
|
+
* prefixed onto its header line. Folded, only that header survives. */
|
|
106
|
+
export function createCrtrWriteToolDefinition(cwd) {
|
|
107
|
+
const builtin = createWriteToolDefinition(cwd);
|
|
108
|
+
return {
|
|
109
|
+
name: 'write',
|
|
110
|
+
label: 'write',
|
|
111
|
+
description: 'Render write calls with a crtr icon header in the attach viewer.',
|
|
112
|
+
parameters: {},
|
|
113
|
+
renderCall(args, theme, context) {
|
|
114
|
+
if (isFoldedContext(context)) {
|
|
115
|
+
const a = args;
|
|
116
|
+
const line = foldedFileCallLine('write', str(a?.file_path ?? a?.path), theme, context.cwd ?? cwd);
|
|
117
|
+
// Reuse the previous component when it is Text-like (the builtin's is a
|
|
118
|
+
// Text subclass), so fold toggles don't churn component identity.
|
|
119
|
+
const last = context.lastComponent;
|
|
120
|
+
if (last && typeof last.setText === 'function') {
|
|
121
|
+
last.setText(line);
|
|
122
|
+
return last;
|
|
123
|
+
}
|
|
124
|
+
return new Text(line, 0, 0);
|
|
125
|
+
}
|
|
126
|
+
const component = builtin.renderCall(args, theme, context);
|
|
127
|
+
prefixToolText(component, `${theme.fg('accent', TOOL_ICON.write)} `);
|
|
128
|
+
return component;
|
|
129
|
+
},
|
|
130
|
+
renderResult(result, options, theme, context) {
|
|
131
|
+
return builtin.renderResult(result, options, theme, context);
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
// bash (non-crtr commands; `crtr ...` invocations get the command card in
|
|
137
|
+
// render/crtr-output.ts)
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
const OPERATOR_RE = /^(&&|\|\||\||;|>>|>|<|2>&1|&)$/;
|
|
140
|
+
/** Split a command into shell-ish tokens, keeping quoted spans whole. */
|
|
141
|
+
function tokenize(line) {
|
|
142
|
+
return line.match(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|\S+/g) ?? [];
|
|
143
|
+
}
|
|
144
|
+
/** Color a single command line so it reads at a glance: program name first,
|
|
145
|
+
* operators marking the pipeline joints, flags and quoted strings toned to
|
|
146
|
+
* their own hues, everything else plain tool output. */
|
|
147
|
+
function styleCommandLine(line, theme) {
|
|
148
|
+
const tokens = tokenize(line);
|
|
149
|
+
let atSegmentStart = true;
|
|
150
|
+
const styled = tokens.map((token) => {
|
|
151
|
+
if (OPERATOR_RE.test(token)) {
|
|
152
|
+
atSegmentStart = true;
|
|
153
|
+
return theme.fg('syntaxOperator', token);
|
|
154
|
+
}
|
|
155
|
+
if (atSegmentStart && !token.includes('=')) {
|
|
156
|
+
atSegmentStart = false;
|
|
157
|
+
return theme.fg('syntaxFunction', theme.bold(token));
|
|
158
|
+
}
|
|
159
|
+
atSegmentStart = false;
|
|
160
|
+
if (token.startsWith('-'))
|
|
161
|
+
return theme.fg('muted', token);
|
|
162
|
+
if (/^["']/.test(token))
|
|
163
|
+
return theme.fg('syntaxString', token);
|
|
164
|
+
return theme.fg('toolOutput', token);
|
|
165
|
+
});
|
|
166
|
+
return styled.join(' ');
|
|
167
|
+
}
|
|
168
|
+
/** The bash call header. Collapsed it is exactly ONE line — icon, the command's
|
|
169
|
+
* first line styled, truncated to the pane with a muted `+N lines` count when
|
|
170
|
+
* there is more. Expanded (ctrl+o) it prints every line of the command, wrapped
|
|
171
|
+
* (never clipped — expanded is the raw truth) into the pane with continuations
|
|
172
|
+
* hanging under the icon. A line wider than the pane would desync pi-tui's line
|
|
173
|
+
* accounting against the terminal's own wrapping and garble the frame. */
|
|
174
|
+
class BashCallComponent {
|
|
175
|
+
command = '';
|
|
176
|
+
expanded = false;
|
|
177
|
+
timeout;
|
|
178
|
+
theme;
|
|
179
|
+
/** Nothing is cached across paints (render recomputes from the fields), so
|
|
180
|
+
* pi's cache-drop hook has nothing to do here. */
|
|
181
|
+
invalidate() { }
|
|
182
|
+
update(theme, command, expanded, timeout) {
|
|
183
|
+
this.theme = theme;
|
|
184
|
+
this.command = command;
|
|
185
|
+
this.expanded = expanded;
|
|
186
|
+
this.timeout = timeout;
|
|
187
|
+
}
|
|
188
|
+
render(width) {
|
|
189
|
+
const theme = this.theme;
|
|
190
|
+
if (!theme)
|
|
191
|
+
return [];
|
|
192
|
+
const icon = theme.fg('accent', TOOL_ICON.bash);
|
|
193
|
+
const lines = this.command.split('\n').map((line) => line.trim()).filter((line) => line.length > 0);
|
|
194
|
+
const suffix = this.timeout ? theme.fg('muted', ` (timeout ${this.timeout}s)`) : '';
|
|
195
|
+
if (lines.length === 0)
|
|
196
|
+
return [`${icon} ${theme.fg('toolOutput', '...')}`];
|
|
197
|
+
if (this.expanded) {
|
|
198
|
+
const prefixWidth = visibleWidth(`${icon} `);
|
|
199
|
+
const indent = ' '.repeat(prefixWidth);
|
|
200
|
+
const inner = Math.max(1, width - prefixWidth);
|
|
201
|
+
const body = [];
|
|
202
|
+
for (let i = 0; i < lines.length; i++) {
|
|
203
|
+
let styled = styleCommandLine(lines[i], theme);
|
|
204
|
+
if (i === lines.length - 1)
|
|
205
|
+
styled += suffix;
|
|
206
|
+
const wrapped = wrapTextWithAnsi(styled, inner);
|
|
207
|
+
for (let j = 0; j < wrapped.length; j++) {
|
|
208
|
+
body.push(`${i === 0 && j === 0 ? `${icon} ` : indent}${wrapped[j]}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// Final backstop: at a pane narrower than the prefix itself the wrap
|
|
212
|
+
// budget bottoms out at 1 and a wrapped line can still overhang.
|
|
213
|
+
return body.map((line) => truncateToWidth(line, width, ''));
|
|
214
|
+
}
|
|
215
|
+
const more = lines.length > 1 ? theme.fg('muted', ` +${lines.length - 1} lines`) : '';
|
|
216
|
+
const fixed = visibleWidth(`${icon} `) + visibleWidth(more) + visibleWidth(suffix);
|
|
217
|
+
const budget = Math.max(8, width - fixed);
|
|
218
|
+
const first = lines[0];
|
|
219
|
+
const shown = first.length > budget ? `${first.slice(0, Math.max(1, budget - 1))}…` : first;
|
|
220
|
+
return [truncateToWidth(`${icon} ${styleCommandLine(shown, theme)}${more}${suffix}`, width)];
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/** Attach-viewer `bash` for anything that is not a `crtr` invocation. */
|
|
224
|
+
export function createCrtrPlainBashToolDefinition(cwd) {
|
|
225
|
+
const builtin = createBashToolDefinition(cwd);
|
|
226
|
+
return {
|
|
227
|
+
name: 'bash',
|
|
228
|
+
label: 'bash',
|
|
229
|
+
description: 'Render plain bash calls as a compact styled command line in the attach viewer.',
|
|
230
|
+
parameters: {},
|
|
231
|
+
renderCall(args, theme, context) {
|
|
232
|
+
const a = args;
|
|
233
|
+
// pi's builtin bash result renderer reads the elapsed clock off this shared
|
|
234
|
+
// state, which its own renderCall would have started — keep stamping it so a
|
|
235
|
+
// running command still shows its duration.
|
|
236
|
+
const state = context.state;
|
|
237
|
+
if (state && context.executionStarted && state.startedAt === undefined) {
|
|
238
|
+
state.startedAt = Date.now();
|
|
239
|
+
state.endedAt = undefined;
|
|
240
|
+
}
|
|
241
|
+
const component = context.lastComponent instanceof BashCallComponent ? context.lastComponent : new BashCallComponent();
|
|
242
|
+
component.update(theme, str(a?.command),
|
|
243
|
+
// Folded wins over ctrl+o expansion: a folded bash is always its one-line form.
|
|
244
|
+
Boolean(context.expanded) && !isFoldedContext(context), typeof a?.timeout === 'number' ? a.timeout : undefined);
|
|
245
|
+
return component;
|
|
246
|
+
},
|
|
247
|
+
renderResult(result, options, theme, context) {
|
|
248
|
+
return builtin.renderResult(result, options, theme, context);
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
}
|
|
@@ -15,6 +15,9 @@ export interface InputWiringHooks {
|
|
|
15
15
|
lastAssistantText: () => string | undefined;
|
|
16
16
|
/** Ctrl+O — flip global tool-output expansion; returns the new state. */
|
|
17
17
|
toggleToolsExpanded: () => boolean;
|
|
18
|
+
/** `/fold-tools` — flip the fold-finished-tool-calls view setting; returns the
|
|
19
|
+
* new state. */
|
|
20
|
+
toggleFoldTools: () => boolean;
|
|
18
21
|
/** Ctrl+T — flip thinking-block visibility; returns true when now HIDDEN. */
|
|
19
22
|
toggleThinking: () => boolean;
|
|
20
23
|
/** `/graph` and the graph keybinding. */
|
|
@@ -80,6 +80,9 @@ export function createInputController(ctx, km, hooks) {
|
|
|
80
80
|
// state owned by ChatView, surfaced as a footer notice mirroring pi's
|
|
81
81
|
// status line.
|
|
82
82
|
onToggleToolsExpand: () => setNotice(hooks.toggleToolsExpanded() ? 'Tool output: expanded' : 'Tool output: collapsed'),
|
|
83
|
+
onFoldTools: () => setNotice(hooks.toggleFoldTools()
|
|
84
|
+
? 'Finished tool calls: folded to one line'
|
|
85
|
+
: 'Finished tool calls: shown in full'),
|
|
83
86
|
onToggleThinking: () => setNotice(hooks.toggleThinking() ? 'Thinking blocks: hidden' : 'Thinking blocks: visible'),
|
|
84
87
|
// The correlated read-op channel the native pickers (/model, /resume,
|
|
85
88
|
// /fork, /tree, /settings, /scoped-models) fetch their payloads over.
|
|
@@ -71,6 +71,10 @@ export interface SlashContext {
|
|
|
71
71
|
onColor?: (color: string | null) => void;
|
|
72
72
|
/** Open the focused node's progressively disclosed metadata dossier. */
|
|
73
73
|
onNodeMetadata?: () => void;
|
|
74
|
+
/** `/fold-tools` — flip the "fold finished tool calls" view setting. Pure
|
|
75
|
+
* viewer-local render state (ChatView owns it), so it is safe for an observer
|
|
76
|
+
* and for a remote attach. Absent → the command notifies it is unwired. */
|
|
77
|
+
onFoldTools?: () => void;
|
|
74
78
|
}
|
|
75
79
|
/** Canvas slash-commands — implemented NATIVE in the viewer, the ONLY live host:
|
|
76
80
|
* the broker binds mode:'rpc', so broker pi-extensions never run their
|
|
@@ -42,6 +42,7 @@ export const CANVAS_SLASH_COMMANDS = [
|
|
|
42
42
|
{ name: 'mcp', description: 'MCP servers — status from the adapter’s config/cache; Enter (or /mcp <server>) connects, reconnects, or re-auths' },
|
|
43
43
|
{ name: 'rename', description: 'Rename this node everywhere — node row, viewer window, session chip, tmux pane: /rename <name>' },
|
|
44
44
|
{ name: 'color', description: 'Color this node — tmux pane outline + name chip: /color <color> (red, blue, #rrggbb, …) or /color none' },
|
|
45
|
+
{ name: 'fold-tools', description: 'View setting — fold every FINISHED tool call to its single call line (Alt+C → w → f)' },
|
|
45
46
|
];
|
|
46
47
|
const CANVAS_NAMES = new Set(CANVAS_SLASH_COMMANDS.map((c) => c.name));
|
|
47
48
|
/** Native canvas commands that shell a LOCAL `crtr` mutation or read against
|
|
@@ -394,6 +395,13 @@ function dispatchCanvasCommand(name, arg, ctx) {
|
|
|
394
395
|
return true;
|
|
395
396
|
case 'color':
|
|
396
397
|
return colorNode(arg, ctx);
|
|
398
|
+
case 'fold-tools':
|
|
399
|
+
// Viewer-local render state only — no frame, no CLI, no node mutation.
|
|
400
|
+
if (ctx.onFoldTools)
|
|
401
|
+
ctx.onFoldTools();
|
|
402
|
+
else
|
|
403
|
+
ctx.notify('The fold-tools view setting is not wired into this viewer');
|
|
404
|
+
return true;
|
|
397
405
|
default:
|
|
398
406
|
ctx.notify(`/${name} is not available in attach`);
|
|
399
407
|
return true;
|