deepclause-sdk 0.0.61 → 0.0.62
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/README.md +47 -5
- package/dist/cli/compile.d.ts +10 -0
- package/dist/cli/compile.d.ts.map +1 -1
- package/dist/cli/compile.js +62 -11
- package/dist/cli/compile.js.map +1 -1
- package/dist/cli/config.d.ts +5 -0
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +61 -0
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/index.js +118 -9
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/run.d.ts +1 -0
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +1 -0
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/tui.d.ts +27 -1
- package/dist/cli/tui.d.ts.map +1 -1
- package/dist/cli/tui.js +430 -23
- package/dist/cli/tui.js.map +1 -1
- package/dist/compiler.d.ts +10 -0
- package/dist/compiler.d.ts.map +1 -1
- package/dist/compiler.js +27 -6
- package/dist/compiler.js.map +1 -1
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +26 -19
- package/dist/runner.js.map +1 -1
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +44 -0
- package/dist/sdk.js.map +1 -1
- package/dist/system/assets/docs/CONDUCTOR_PROMPT.md +30 -5
- package/dist/system/assets/docs/TUI.md +139 -0
- package/dist/system/assets/index.d.ts +11 -1
- package/dist/system/assets/index.d.ts.map +1 -1
- package/dist/system/assets/index.js +62 -7
- package/dist/system/assets/index.js.map +1 -1
- package/dist/system/runtime/agentvm-manager.d.ts +28 -1
- package/dist/system/runtime/agentvm-manager.d.ts.map +1 -1
- package/dist/system/runtime/agentvm-manager.js +18 -2
- package/dist/system/runtime/agentvm-manager.js.map +1 -1
- package/dist/system/runtime/conductor.d.ts.map +1 -1
- package/dist/system/runtime/conductor.js +8 -4
- package/dist/system/runtime/conductor.js.map +1 -1
- package/dist/system/runtime/dml-executor.js +58 -54
- package/dist/system/runtime/dml-executor.js.map +1 -1
- package/dist/system/runtime/runtime-tools.d.ts +2 -1
- package/dist/system/runtime/runtime-tools.d.ts.map +1 -1
- package/dist/system/runtime/runtime-tools.js +17 -2
- package/dist/system/runtime/runtime-tools.js.map +1 -1
- package/dist/system/runtime/shell-manager.d.ts +3 -3
- package/dist/system/runtime/shell-manager.d.ts.map +1 -1
- package/dist/system/runtime/shell-manager.js +31 -3
- package/dist/system/runtime/shell-manager.js.map +1 -1
- package/dist/system/runtime/shell-tool-events.d.ts +11 -0
- package/dist/system/runtime/shell-tool-events.d.ts.map +1 -0
- package/dist/system/runtime/shell-tool-events.js +139 -0
- package/dist/system/runtime/shell-tool-events.js.map +1 -0
- package/dist/system/runtime/skill-creator.d.ts +3 -0
- package/dist/system/runtime/skill-creator.d.ts.map +1 -1
- package/dist/system/runtime/skill-creator.js +41 -25
- package/dist/system/runtime/skill-creator.js.map +1 -1
- package/dist/types.d.ts +12 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/cli/tui.js
CHANGED
|
@@ -4,11 +4,15 @@ import { emitKeypressEvents } from 'readline';
|
|
|
4
4
|
import { createInterface } from 'readline/promises';
|
|
5
5
|
import { stdin as input, stdout as output } from 'process';
|
|
6
6
|
import { listCommands } from './commands.js';
|
|
7
|
-
import { getConfigPath, loadConfig, resolveModelSlot } from './config.js';
|
|
7
|
+
import { getConfigPath, loadConfig, resolveModelSlot, setModel } from './config.js';
|
|
8
8
|
import { run } from './run.js';
|
|
9
9
|
import { createLocalSkill, createConductorSession, getConductorSessionDetail, listConductorSessions, runConductorTurn, } from '../system/runtime/conductor.js';
|
|
10
|
+
import { getSystemAssetSourcePaths } from '../system/assets/index.js';
|
|
11
|
+
import { createShellManager } from '../system/runtime/shell-manager.js';
|
|
12
|
+
import { buildToolCompletionEvent, buildToolFailureEvent, buildToolStartEvent, createShellToolEventBridge, } from '../system/runtime/shell-tool-events.js';
|
|
10
13
|
const IGNORED_LIVE_LOG_TOOLS = new Set(['set_result', 'update_memory']);
|
|
11
|
-
const BUILTIN_SLASH_COMMANDS = ['new', 'sessions', 'help', 'compile', 'skill-creator', 'cancel', 'exit', 'quit'];
|
|
14
|
+
const BUILTIN_SLASH_COMMANDS = ['new', 'sessions', 'help', 'compile', 'skill-creator', 'set-model', 'cancel', 'exit', 'quit'];
|
|
15
|
+
const MODEL_SLOTS = ['gateway', 'run', 'compile'];
|
|
12
16
|
const CHILD_EVENT_INDENT = '\t';
|
|
13
17
|
const CHILD_EVENT_TAB_WIDTH = 4;
|
|
14
18
|
const MAX_ACTIVITY_LINES = 400;
|
|
@@ -67,10 +71,7 @@ export class LiveExecutionPrinter {
|
|
|
67
71
|
this.flushStream();
|
|
68
72
|
switch (event.type) {
|
|
69
73
|
case 'tool_call':
|
|
70
|
-
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
this.writeLine(`${formatEventPrefix(logEvent)}tool ${event.toolName}(${formatToolArgs(event.toolArgs)})`);
|
|
74
|
+
this.handleToolCall(logEvent);
|
|
74
75
|
break;
|
|
75
76
|
case 'output':
|
|
76
77
|
if (event.content) {
|
|
@@ -126,9 +127,17 @@ export class LiveExecutionPrinter {
|
|
|
126
127
|
this.streamOpen = false;
|
|
127
128
|
this.activeStreamKey = null;
|
|
128
129
|
}
|
|
130
|
+
handleToolCall(logEvent) {
|
|
131
|
+
const line = formatToolEventLine(logEvent);
|
|
132
|
+
if (!line) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
this.writeLine(line);
|
|
136
|
+
}
|
|
129
137
|
}
|
|
130
|
-
class ActivityBuffer {
|
|
138
|
+
export class ActivityBuffer {
|
|
131
139
|
lines = [];
|
|
140
|
+
activeTools = new Map();
|
|
132
141
|
activeStreamKey = null;
|
|
133
142
|
activeStreamLine = '';
|
|
134
143
|
handle(logEvent) {
|
|
@@ -143,10 +152,7 @@ class ActivityBuffer {
|
|
|
143
152
|
this.flushStream();
|
|
144
153
|
switch (event.type) {
|
|
145
154
|
case 'tool_call':
|
|
146
|
-
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
this.pushLine(`${formatEventPrefix(logEvent)}tool ${event.toolName}(${formatToolArgs(event.toolArgs)})`);
|
|
155
|
+
this.handleToolCall(logEvent);
|
|
150
156
|
break;
|
|
151
157
|
case 'output':
|
|
152
158
|
if (event.content) {
|
|
@@ -185,16 +191,25 @@ class ActivityBuffer {
|
|
|
185
191
|
}
|
|
186
192
|
finish() {
|
|
187
193
|
this.flushStream();
|
|
194
|
+
this.activeTools.clear();
|
|
188
195
|
}
|
|
189
196
|
clear() {
|
|
190
197
|
this.lines.length = 0;
|
|
198
|
+
this.activeTools.clear();
|
|
191
199
|
this.activeStreamKey = null;
|
|
192
200
|
this.activeStreamLine = '';
|
|
193
201
|
}
|
|
194
202
|
snapshot() {
|
|
195
|
-
|
|
203
|
+
const body = this.activeStreamLine
|
|
196
204
|
? [...this.lines, this.activeStreamLine]
|
|
197
205
|
: [...this.lines];
|
|
206
|
+
const activeLines = this.buildActiveToolLines();
|
|
207
|
+
if (activeLines.length === 0) {
|
|
208
|
+
return body;
|
|
209
|
+
}
|
|
210
|
+
return body.length > 0
|
|
211
|
+
? [...activeLines, '', ...body]
|
|
212
|
+
: activeLines;
|
|
198
213
|
}
|
|
199
214
|
handleStream(logEvent) {
|
|
200
215
|
const streamKey = logEvent.scope === 'child' ? `child:${logEvent.childSlug ?? '?'}` : 'main';
|
|
@@ -225,6 +240,43 @@ class ActivityBuffer {
|
|
|
225
240
|
this.lines.splice(0, this.lines.length - MAX_ACTIVITY_LINES);
|
|
226
241
|
}
|
|
227
242
|
}
|
|
243
|
+
handleToolCall(logEvent) {
|
|
244
|
+
const { event } = logEvent;
|
|
245
|
+
if (!event.toolName || IGNORED_LIVE_LOG_TOOLS.has(event.toolName)) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (event.toolState) {
|
|
249
|
+
const scopeKey = toolScopeKey(logEvent);
|
|
250
|
+
if (event.toolState === 'starting' || event.toolState === 'running') {
|
|
251
|
+
this.activeTools.set(scopeKey, {
|
|
252
|
+
scopeKey,
|
|
253
|
+
scopeLabel: toolScopeLabel(logEvent),
|
|
254
|
+
toolName: event.toolName,
|
|
255
|
+
toolState: event.toolState,
|
|
256
|
+
toolPid: event.toolPid,
|
|
257
|
+
toolBackend: event.toolBackend,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
this.activeTools.delete(scopeKey);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
const line = formatToolEventLine(logEvent);
|
|
265
|
+
if (line) {
|
|
266
|
+
this.pushLine(line);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
buildActiveToolLines() {
|
|
270
|
+
if (this.activeTools.size === 0) {
|
|
271
|
+
return [];
|
|
272
|
+
}
|
|
273
|
+
const lines = ['Active Tool Status'];
|
|
274
|
+
const entries = [...this.activeTools.values()].sort((left, right) => left.scopeKey.localeCompare(right.scopeKey));
|
|
275
|
+
for (const entry of entries) {
|
|
276
|
+
lines.push(formatActiveToolStatus(entry));
|
|
277
|
+
}
|
|
278
|
+
return lines;
|
|
279
|
+
}
|
|
228
280
|
}
|
|
229
281
|
class FullscreenTui {
|
|
230
282
|
workspaceRoot;
|
|
@@ -263,6 +315,7 @@ class FullscreenTui {
|
|
|
263
315
|
editorState = null;
|
|
264
316
|
recentFiles = [];
|
|
265
317
|
lastSubmittedInput = null;
|
|
318
|
+
shellWorkspacePath;
|
|
266
319
|
renderTimer = null;
|
|
267
320
|
animationTimer = null;
|
|
268
321
|
closeResolver = null;
|
|
@@ -272,6 +325,7 @@ class FullscreenTui {
|
|
|
272
325
|
constructor(workspaceRoot, options = {}) {
|
|
273
326
|
this.workspaceRoot = workspaceRoot;
|
|
274
327
|
this.options = options;
|
|
328
|
+
this.shellWorkspacePath = path.resolve(workspaceRoot, 'workspace');
|
|
275
329
|
}
|
|
276
330
|
async start() {
|
|
277
331
|
try {
|
|
@@ -428,8 +482,12 @@ class FullscreenTui {
|
|
|
428
482
|
await this.applyCompletion();
|
|
429
483
|
return;
|
|
430
484
|
case 'escape':
|
|
431
|
-
|
|
432
|
-
|
|
485
|
+
{
|
|
486
|
+
const previousInput = this.inputValue;
|
|
487
|
+
this.inputValue = '';
|
|
488
|
+
this.cursor = 0;
|
|
489
|
+
this.refreshCommandBarStatus(previousInput);
|
|
490
|
+
}
|
|
433
491
|
break;
|
|
434
492
|
default:
|
|
435
493
|
if (text && !key.ctrl && !key.meta) {
|
|
@@ -711,7 +769,7 @@ class FullscreenTui {
|
|
|
711
769
|
this.requestRender();
|
|
712
770
|
return;
|
|
713
771
|
}
|
|
714
|
-
const parsed =
|
|
772
|
+
const parsed = parseCommandBarInput(rawInput);
|
|
715
773
|
if (this.busy && !this.pendingPrompt && !canSubmitParsedInputWhileBusy(parsed)) {
|
|
716
774
|
this.statusLine = 'Execution is still running. Use /cancel or Ctrl+C to stop it.';
|
|
717
775
|
this.requestRender();
|
|
@@ -725,6 +783,10 @@ class FullscreenTui {
|
|
|
725
783
|
await this.executeSkill(parsed.name, parsed.args);
|
|
726
784
|
return;
|
|
727
785
|
}
|
|
786
|
+
if (parsed.kind === 'shell') {
|
|
787
|
+
await this.executeShellCommand(parsed.command);
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
728
790
|
await this.executeTask(parsed.prompt);
|
|
729
791
|
}
|
|
730
792
|
async executeBuiltin(parsed) {
|
|
@@ -749,6 +811,9 @@ class FullscreenTui {
|
|
|
749
811
|
case 'skill-creator':
|
|
750
812
|
await this.executeSkillCreator(parsed.name, parsed.rawArgs);
|
|
751
813
|
return;
|
|
814
|
+
case 'set-model':
|
|
815
|
+
await this.executeSetModel(parsed.rawArgs);
|
|
816
|
+
return;
|
|
752
817
|
case 'sessions':
|
|
753
818
|
await this.refreshSessions({ createIfMissing: true, selectedSessionId: this.selectedSessionId });
|
|
754
819
|
await this.refreshCommands();
|
|
@@ -931,6 +996,78 @@ class FullscreenTui {
|
|
|
931
996
|
}
|
|
932
997
|
}
|
|
933
998
|
}
|
|
999
|
+
async executeShellCommand(commandText) {
|
|
1000
|
+
const trimmedCommand = commandText.trim();
|
|
1001
|
+
if (!trimmedCommand) {
|
|
1002
|
+
this.statusLine = 'Enter a shell command after !.';
|
|
1003
|
+
this.requestRender();
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
1006
|
+
const sessionId = this.selectedSessionId || 'global';
|
|
1007
|
+
const activity = this.activityFor(sessionId);
|
|
1008
|
+
this.lastSubmittedInput = { kind: 'shell', command: trimmedCommand };
|
|
1009
|
+
this.focusedPane = 'messages';
|
|
1010
|
+
this.paneScroll.messages = 0;
|
|
1011
|
+
this.paneScroll.process = 0;
|
|
1012
|
+
this.beginExecutionPreview(sessionId, 'shell', {
|
|
1013
|
+
role: 'system',
|
|
1014
|
+
kind: 'output',
|
|
1015
|
+
tag: 'shell',
|
|
1016
|
+
content: `!${trimmedCommand}`,
|
|
1017
|
+
}, 'shell');
|
|
1018
|
+
activity.pushLine(`shell !${trimmedCommand}`);
|
|
1019
|
+
this.busy = true;
|
|
1020
|
+
this.currentAbortController = new AbortController();
|
|
1021
|
+
this.startAnimation();
|
|
1022
|
+
this.statusLine = `Running !${trimmedCommand}...`;
|
|
1023
|
+
this.requestRender();
|
|
1024
|
+
try {
|
|
1025
|
+
const result = await runShellCommand(this.workspaceRoot, trimmedCommand, {
|
|
1026
|
+
sandbox: this.options.sandbox,
|
|
1027
|
+
signal: this.currentAbortController.signal,
|
|
1028
|
+
onEvent: (event) => {
|
|
1029
|
+
activity.handle(event);
|
|
1030
|
+
this.requestRender();
|
|
1031
|
+
},
|
|
1032
|
+
});
|
|
1033
|
+
activity.finish();
|
|
1034
|
+
await this.refreshCommands();
|
|
1035
|
+
this.finishExecutionPreview(result.success
|
|
1036
|
+
? { persist: true, finalText: formatShellCommandSummary(result) }
|
|
1037
|
+
: { persist: true, error: formatShellCommandSummary(result) });
|
|
1038
|
+
this.statusLine = result.success ? 'Shell command finished.' : 'Shell command failed.';
|
|
1039
|
+
}
|
|
1040
|
+
catch (error) {
|
|
1041
|
+
activity.finish();
|
|
1042
|
+
const message = error.message;
|
|
1043
|
+
this.finishExecutionPreview({ persist: true, error: message });
|
|
1044
|
+
this.statusLine = isAbortError(error) ? 'Shell command cancelled.' : 'Shell command failed.';
|
|
1045
|
+
}
|
|
1046
|
+
finally {
|
|
1047
|
+
this.busy = false;
|
|
1048
|
+
this.currentAbortController = null;
|
|
1049
|
+
this.stopAnimation();
|
|
1050
|
+
if (this.exitRequested) {
|
|
1051
|
+
this.close();
|
|
1052
|
+
}
|
|
1053
|
+
else {
|
|
1054
|
+
this.requestRender();
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
async executeSetModel(rawArgs) {
|
|
1059
|
+
try {
|
|
1060
|
+
const { model, slot } = parseSetModelCommandArgs(rawArgs);
|
|
1061
|
+
const result = await setModel(this.workspaceRoot, model, slot);
|
|
1062
|
+
const slotsLabel = result.updatedSlots.join(', ');
|
|
1063
|
+
this.currentProcessActivity().pushLine(`config model ${result.modelId} slots=${slotsLabel}`);
|
|
1064
|
+
this.statusLine = `Model set to ${result.modelId} for ${slotsLabel}. Applies on the next turn.`;
|
|
1065
|
+
}
|
|
1066
|
+
catch (error) {
|
|
1067
|
+
this.statusLine = error.message;
|
|
1068
|
+
}
|
|
1069
|
+
this.requestRender();
|
|
1070
|
+
}
|
|
934
1071
|
async requestClarification(promptText) {
|
|
935
1072
|
this.ensureClarificationVisible(promptText);
|
|
936
1073
|
return this.promptForValue(promptText, 'clarify');
|
|
@@ -1101,7 +1238,8 @@ class FullscreenTui {
|
|
|
1101
1238
|
label: 'Run',
|
|
1102
1239
|
items: [
|
|
1103
1240
|
{ id: 'run.prompt', label: 'Send Prompt…', description: 'Return to the command bar and enter a freeform prompt.', shortcut: 'Enter' },
|
|
1104
|
-
{ id: 'run.
|
|
1241
|
+
{ id: 'run.shell', label: 'Run Shell Command…', description: 'Prefill the command bar for a direct workspace shell command.' },
|
|
1242
|
+
{ id: 'run.repeat', label: 'Repeat Last Command', description: 'Re-run the last prompt, shell command, or skill invocation.', enabled: !!this.lastSubmittedInput },
|
|
1105
1243
|
{ id: 'run.cancel', label: 'Cancel Current Execution', description: 'Abort the running task or skill.', shortcut: '/cancel', enabled: this.busy },
|
|
1106
1244
|
{ id: 'run.clear', label: 'Clear Execution Pane', description: 'Discard the current session execution log.' },
|
|
1107
1245
|
{ id: 'run.refreshSkills', label: 'Refresh Skill Catalog', description: 'Re-scan the workspace for compiled skills.' },
|
|
@@ -1124,7 +1262,7 @@ class FullscreenTui {
|
|
|
1124
1262
|
label: 'Help',
|
|
1125
1263
|
items: [
|
|
1126
1264
|
{ id: 'help.shortcuts', label: 'Keyboard Shortcuts', description: 'Show the fullscreen keyboard map.' },
|
|
1127
|
-
{ id: 'help.slash', label: '
|
|
1265
|
+
{ id: 'help.slash', label: 'Commands', description: 'Show slash commands, shell commands, and direct skill invocation.' },
|
|
1128
1266
|
{ id: 'help.about', label: 'About This TUI', description: 'Show a short explanation of the fullscreen workspace.' },
|
|
1129
1267
|
],
|
|
1130
1268
|
},
|
|
@@ -1204,6 +1342,13 @@ class FullscreenTui {
|
|
|
1204
1342
|
this.closeMenuMode();
|
|
1205
1343
|
this.statusLine = 'Command bar ready.';
|
|
1206
1344
|
return;
|
|
1345
|
+
case 'run.shell':
|
|
1346
|
+
this.closeMenuMode();
|
|
1347
|
+
this.inputValue = '!';
|
|
1348
|
+
this.cursor = this.inputValue.length;
|
|
1349
|
+
this.statusLine = formatShellModeStatus(this.workspaceRoot, this.shellWorkspacePath, this.options.sandbox);
|
|
1350
|
+
this.requestRender();
|
|
1351
|
+
return;
|
|
1207
1352
|
case 'run.repeat':
|
|
1208
1353
|
await this.repeatLastCommand();
|
|
1209
1354
|
return;
|
|
@@ -1245,13 +1390,14 @@ class FullscreenTui {
|
|
|
1245
1390
|
this.openTextViewer('Keyboard Shortcuts', getShortcutHelpLines().join('\n'), { preferredExtension: '.txt' });
|
|
1246
1391
|
return;
|
|
1247
1392
|
case 'help.slash':
|
|
1248
|
-
this.openTextViewer('
|
|
1393
|
+
this.openTextViewer('Commands', getSlashHelpLines().join('\n'), { preferredExtension: '.txt' });
|
|
1249
1394
|
return;
|
|
1250
1395
|
case 'help.about':
|
|
1251
1396
|
this.openTextViewer('About This TUI', [
|
|
1252
1397
|
'DeepClause fullscreen TUI',
|
|
1253
1398
|
'',
|
|
1254
1399
|
'Use F10 or Ctrl+G for the menu bar, Ctrl+W to cycle panes, and /cancel to stop a running execution.',
|
|
1400
|
+
'Type !<command> to run a direct shell command in the configured workspace shell.',
|
|
1255
1401
|
'The Skills and Files menus open searchable pickers, and Ctrl+P opens the action palette directly.',
|
|
1256
1402
|
].join('\n'), { preferredExtension: '.txt' });
|
|
1257
1403
|
return;
|
|
@@ -1787,7 +1933,7 @@ class FullscreenTui {
|
|
|
1787
1933
|
async repeatLastCommand() {
|
|
1788
1934
|
if (!this.lastSubmittedInput) {
|
|
1789
1935
|
this.closeMenuMode();
|
|
1790
|
-
this.statusLine = 'No previous prompt or skill to repeat yet.';
|
|
1936
|
+
this.statusLine = 'No previous prompt, shell command, or skill to repeat yet.';
|
|
1791
1937
|
this.requestRender();
|
|
1792
1938
|
return;
|
|
1793
1939
|
}
|
|
@@ -1802,6 +1948,10 @@ class FullscreenTui {
|
|
|
1802
1948
|
await this.executeSkill(this.lastSubmittedInput.name, this.lastSubmittedInput.args);
|
|
1803
1949
|
return;
|
|
1804
1950
|
}
|
|
1951
|
+
if (this.lastSubmittedInput.kind === 'shell') {
|
|
1952
|
+
await this.executeShellCommand(this.lastSubmittedInput.command);
|
|
1953
|
+
return;
|
|
1954
|
+
}
|
|
1805
1955
|
if (this.lastSubmittedInput.kind === 'builtin') {
|
|
1806
1956
|
await this.executeBuiltin(this.lastSubmittedInput);
|
|
1807
1957
|
}
|
|
@@ -1818,7 +1968,9 @@ class FullscreenTui {
|
|
|
1818
1968
|
kind,
|
|
1819
1969
|
rootTag,
|
|
1820
1970
|
activeChildTag: kind === 'skill' ? rootTag : undefined,
|
|
1821
|
-
entries:
|
|
1971
|
+
entries: kind === 'shell'
|
|
1972
|
+
? [lead]
|
|
1973
|
+
: [lead, { role: 'assistant', content: '', pending: true }],
|
|
1822
1974
|
};
|
|
1823
1975
|
}
|
|
1824
1976
|
updatePreviewFromEvent(logEvent) {
|
|
@@ -1863,6 +2015,9 @@ class FullscreenTui {
|
|
|
1863
2015
|
if (this.currentPreview.kind === 'task') {
|
|
1864
2016
|
return logEvent.scope === 'main';
|
|
1865
2017
|
}
|
|
2018
|
+
if (this.currentPreview.kind === 'shell') {
|
|
2019
|
+
return false;
|
|
2020
|
+
}
|
|
1866
2021
|
return logEvent.scope === 'child' && logEvent.childSlug === this.currentPreview.rootTag;
|
|
1867
2022
|
}
|
|
1868
2023
|
insertPreviewMessage(entry) {
|
|
@@ -1919,6 +2074,23 @@ class FullscreenTui {
|
|
|
1919
2074
|
return;
|
|
1920
2075
|
}
|
|
1921
2076
|
this.currentPreview.activeChildTag = undefined;
|
|
2077
|
+
if (this.currentPreview.kind === 'shell') {
|
|
2078
|
+
const detail = typeof options.error === 'string' && options.error.trim()
|
|
2079
|
+
? { role: 'system', kind: 'output', tag: 'shell', content: options.error, error: true }
|
|
2080
|
+
: typeof options.finalText === 'string' && options.finalText.trim()
|
|
2081
|
+
? { role: 'system', kind: 'output', tag: 'shell', content: options.finalText }
|
|
2082
|
+
: null;
|
|
2083
|
+
if (detail) {
|
|
2084
|
+
this.currentPreview.entries.push(detail);
|
|
2085
|
+
}
|
|
2086
|
+
if (options.persist) {
|
|
2087
|
+
const existing = this.ephemeralMessagesBySessionId.get(this.currentPreview.sessionId) ?? [];
|
|
2088
|
+
existing.push(...this.currentPreview.entries.map((entry) => ({ ...entry, pending: false })));
|
|
2089
|
+
this.ephemeralMessagesBySessionId.set(this.currentPreview.sessionId, existing);
|
|
2090
|
+
}
|
|
2091
|
+
this.currentPreview = null;
|
|
2092
|
+
return;
|
|
2093
|
+
}
|
|
1922
2094
|
const assistant = this.ensurePreviewAssistantMessage();
|
|
1923
2095
|
if (typeof options.finalText === 'string' && options.finalText.trim()) {
|
|
1924
2096
|
assistant.content = options.finalText;
|
|
@@ -2029,24 +2201,49 @@ class FullscreenTui {
|
|
|
2029
2201
|
return this.activityFor(this.selectedSessionId || 'global');
|
|
2030
2202
|
}
|
|
2031
2203
|
insertText(text) {
|
|
2204
|
+
const previousInput = this.inputValue;
|
|
2032
2205
|
this.inputValue = `${this.inputValue.slice(0, this.cursor)}${text}${this.inputValue.slice(this.cursor)}`;
|
|
2033
2206
|
this.cursor += text.length;
|
|
2207
|
+
this.refreshCommandBarStatus(previousInput);
|
|
2034
2208
|
}
|
|
2035
2209
|
deleteBackward() {
|
|
2036
2210
|
if (this.cursor === 0) {
|
|
2037
2211
|
return;
|
|
2038
2212
|
}
|
|
2213
|
+
const previousInput = this.inputValue;
|
|
2039
2214
|
this.inputValue = `${this.inputValue.slice(0, this.cursor - 1)}${this.inputValue.slice(this.cursor)}`;
|
|
2040
2215
|
this.cursor -= 1;
|
|
2216
|
+
this.refreshCommandBarStatus(previousInput);
|
|
2041
2217
|
}
|
|
2042
2218
|
deleteForward() {
|
|
2043
2219
|
if (this.cursor >= this.inputValue.length) {
|
|
2044
2220
|
return;
|
|
2045
2221
|
}
|
|
2222
|
+
const previousInput = this.inputValue;
|
|
2046
2223
|
this.inputValue = `${this.inputValue.slice(0, this.cursor)}${this.inputValue.slice(this.cursor + 1)}`;
|
|
2224
|
+
this.refreshCommandBarStatus(previousInput);
|
|
2047
2225
|
}
|
|
2048
2226
|
async refreshCommands() {
|
|
2049
2227
|
this.commands = await listCommands(this.workspaceRoot);
|
|
2228
|
+
await this.refreshShellContext();
|
|
2229
|
+
}
|
|
2230
|
+
async refreshShellContext() {
|
|
2231
|
+
const config = await loadConfig(this.workspaceRoot);
|
|
2232
|
+
this.shellWorkspacePath = path.resolve(this.workspaceRoot, config.workspace || './workspace');
|
|
2233
|
+
}
|
|
2234
|
+
refreshCommandBarStatus(previousInput = '') {
|
|
2235
|
+
if (this.busy || this.pendingPrompt || this.uiMode !== 'command') {
|
|
2236
|
+
return;
|
|
2237
|
+
}
|
|
2238
|
+
const wasShell = previousInput.trimStart().startsWith('!');
|
|
2239
|
+
const isShell = this.inputValue.trimStart().startsWith('!');
|
|
2240
|
+
if (isShell) {
|
|
2241
|
+
this.statusLine = formatShellModeStatus(this.workspaceRoot, this.shellWorkspacePath, this.options.sandbox);
|
|
2242
|
+
return;
|
|
2243
|
+
}
|
|
2244
|
+
if (wasShell) {
|
|
2245
|
+
this.statusLine = 'Command bar ready.';
|
|
2246
|
+
}
|
|
2050
2247
|
}
|
|
2051
2248
|
async refreshSessions(options = {}) {
|
|
2052
2249
|
let sessions = await listConductorSessions(this.workspaceRoot);
|
|
@@ -2284,6 +2481,7 @@ class FullscreenTui {
|
|
|
2284
2481
|
return ['No session selected.'];
|
|
2285
2482
|
}
|
|
2286
2483
|
const contextSize = summarizeContextSize(this.sessionDetail);
|
|
2484
|
+
const systemAssetSources = getSystemAssetSourcePaths(this.workspaceRoot);
|
|
2287
2485
|
const usageEntries = Object.entries(this.sessionDetail.usageByModel ?? {})
|
|
2288
2486
|
.sort((left, right) => right[1].totalTokens - left[1].totalTokens || left[0].localeCompare(right[0]));
|
|
2289
2487
|
const body = [
|
|
@@ -2294,6 +2492,17 @@ class FullscreenTui {
|
|
|
2294
2492
|
`Assistant Memory ${formatByteSize(contextSize.assistantMemoryBytes)} (~${formatInteger(contextSize.assistantMemoryTokens)} tok)`,
|
|
2295
2493
|
`Total ${formatByteSize(contextSize.totalBytes)} (~${formatInteger(contextSize.totalTokens)} tok)`,
|
|
2296
2494
|
'',
|
|
2495
|
+
'System Asset Sources',
|
|
2496
|
+
`Conductor DML ${formatSystemAssetSourcePath(this.workspaceRoot, systemAssetSources.conductorDml)}`,
|
|
2497
|
+
`Conductor Prompt ${formatSystemAssetSourcePath(this.workspaceRoot, systemAssetSources.conductorPrompt)}`,
|
|
2498
|
+
`Skill Creator DML ${formatSystemAssetSourcePath(this.workspaceRoot, systemAssetSources.skillCreatorDml)}`,
|
|
2499
|
+
`Skill Creator Prompt ${formatSystemAssetSourcePath(this.workspaceRoot, systemAssetSources.skillCreatorPrompt)}`,
|
|
2500
|
+
'Changes apply on the next turn or /compile run. No TUI reload is required.',
|
|
2501
|
+
'',
|
|
2502
|
+
'Shell Execution',
|
|
2503
|
+
`Backend ${this.options.sandbox ? 'sandbox' : 'host'}`,
|
|
2504
|
+
`CWD ${this.shellWorkspacePath}`,
|
|
2505
|
+
'',
|
|
2297
2506
|
'Session Token Usage',
|
|
2298
2507
|
];
|
|
2299
2508
|
if (usageEntries.length === 0) {
|
|
@@ -2553,7 +2762,7 @@ async function startLineTui(workspaceRoot = process.cwd(), options = {}) {
|
|
|
2553
2762
|
if (!line) {
|
|
2554
2763
|
continue;
|
|
2555
2764
|
}
|
|
2556
|
-
const parsed =
|
|
2765
|
+
const parsed = parseCommandBarInput(line);
|
|
2557
2766
|
if (parsed.kind === 'builtin') {
|
|
2558
2767
|
switch (parsed.name) {
|
|
2559
2768
|
case 'cancel':
|
|
@@ -2611,7 +2820,40 @@ async function startLineTui(workspaceRoot = process.cwd(), options = {}) {
|
|
|
2611
2820
|
console.log(divider('-'));
|
|
2612
2821
|
console.log('');
|
|
2613
2822
|
continue;
|
|
2823
|
+
case 'set-model': {
|
|
2824
|
+
try {
|
|
2825
|
+
const { model, slot } = parseSetModelCommandArgs(parsed.rawArgs);
|
|
2826
|
+
const result = await setModel(workspaceRoot, model, slot);
|
|
2827
|
+
console.log(`Model set to ${result.modelId} (${result.updatedSlots.join(', ')}).`);
|
|
2828
|
+
}
|
|
2829
|
+
catch (error) {
|
|
2830
|
+
console.log(`${paint('error', ANSI.red)} ${error.message}`);
|
|
2831
|
+
}
|
|
2832
|
+
continue;
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
2836
|
+
if (parsed.kind === 'shell') {
|
|
2837
|
+
console.log(divider('-'));
|
|
2838
|
+
console.log(`${paint('shell', ANSI.dim)} !${parsed.command}`);
|
|
2839
|
+
console.log(divider('-'));
|
|
2840
|
+
const printer = new LiveExecutionPrinter();
|
|
2841
|
+
try {
|
|
2842
|
+
const result = await runShellCommand(workspaceRoot, parsed.command, {
|
|
2843
|
+
sandbox: options.sandbox,
|
|
2844
|
+
onEvent: (event) => printer.handle(event),
|
|
2845
|
+
});
|
|
2846
|
+
printer.finish();
|
|
2847
|
+
console.log(formatShellCommandSummary(result));
|
|
2614
2848
|
}
|
|
2849
|
+
catch (error) {
|
|
2850
|
+
printer.finish();
|
|
2851
|
+
console.log(`${paint('error', ANSI.red)} ${error.message}`);
|
|
2852
|
+
}
|
|
2853
|
+
console.log('');
|
|
2854
|
+
console.log(divider('-'));
|
|
2855
|
+
console.log('');
|
|
2856
|
+
continue;
|
|
2615
2857
|
}
|
|
2616
2858
|
if (parsed.kind === 'skill') {
|
|
2617
2859
|
console.log(divider('-'));
|
|
@@ -2734,6 +2976,40 @@ async function runSkillCreatorCommand(workspaceRoot, sessionId, spec, options =
|
|
|
2734
2976
|
onEvent: options.onEvent,
|
|
2735
2977
|
});
|
|
2736
2978
|
}
|
|
2979
|
+
async function runShellCommand(workspaceRoot, command, options = {}) {
|
|
2980
|
+
const trimmedCommand = command.trim();
|
|
2981
|
+
if (!trimmedCommand) {
|
|
2982
|
+
throw new Error('command is required');
|
|
2983
|
+
}
|
|
2984
|
+
const toolName = 'shell';
|
|
2985
|
+
const toolArgs = { command: trimmedCommand };
|
|
2986
|
+
const emit = (event) => options.onEvent?.({ scope: 'main', event });
|
|
2987
|
+
emit(buildToolStartEvent(toolName, toolArgs));
|
|
2988
|
+
let shellManager = null;
|
|
2989
|
+
try {
|
|
2990
|
+
const config = await loadConfig(workspaceRoot);
|
|
2991
|
+
const workspacePath = path.resolve(workspaceRoot, config.workspace || './workspace');
|
|
2992
|
+
shellManager = createShellManager({
|
|
2993
|
+
workspacePath,
|
|
2994
|
+
sandbox: options.sandbox,
|
|
2995
|
+
});
|
|
2996
|
+
const observer = createShellToolEventBridge({
|
|
2997
|
+
toolName,
|
|
2998
|
+
toolArgs,
|
|
2999
|
+
emit,
|
|
3000
|
+
});
|
|
3001
|
+
const result = await shellManager.exec(trimmedCommand, options.signal, observer);
|
|
3002
|
+
emit(buildToolCompletionEvent(toolName, toolArgs, result));
|
|
3003
|
+
return result;
|
|
3004
|
+
}
|
|
3005
|
+
catch (error) {
|
|
3006
|
+
emit(buildToolFailureEvent(toolName, toolArgs, error));
|
|
3007
|
+
throw error;
|
|
3008
|
+
}
|
|
3009
|
+
finally {
|
|
3010
|
+
await shellManager?.dispose();
|
|
3011
|
+
}
|
|
3012
|
+
}
|
|
2737
3013
|
function formatSkillCreatorSummary(workspaceRoot, result) {
|
|
2738
3014
|
const slug = typeof result.slug === 'string' ? result.slug : 'unknown-skill';
|
|
2739
3015
|
const outputPath = typeof result.output_path === 'string' ? result.output_path : '';
|
|
@@ -2743,6 +3019,15 @@ function formatSkillCreatorSummary(workspaceRoot, result) {
|
|
|
2743
3019
|
const relativeOutputPath = path.relative(workspaceRoot, outputPath);
|
|
2744
3020
|
return `Published /${slug} to ${relativeOutputPath || outputPath}.`;
|
|
2745
3021
|
}
|
|
3022
|
+
function formatShellCommandSummary(result) {
|
|
3023
|
+
if (result.success) {
|
|
3024
|
+
return result.summary || 'Shell command completed successfully.';
|
|
3025
|
+
}
|
|
3026
|
+
return result.summary || `Shell command failed with exit code ${result.exitCode}.`;
|
|
3027
|
+
}
|
|
3028
|
+
function isAbortError(error) {
|
|
3029
|
+
return error instanceof Error && error.name === 'AbortError';
|
|
3030
|
+
}
|
|
2746
3031
|
export function previewMessageFromEvent(logEvent) {
|
|
2747
3032
|
switch (logEvent.event.type) {
|
|
2748
3033
|
case 'output':
|
|
@@ -2826,7 +3111,14 @@ export function parseCommandArgs(rawArgs) {
|
|
|
2826
3111
|
}
|
|
2827
3112
|
return args;
|
|
2828
3113
|
}
|
|
2829
|
-
export function
|
|
3114
|
+
export function parseCommandBarInput(line) {
|
|
3115
|
+
const trimmedStart = line.trimStart();
|
|
3116
|
+
if (trimmedStart.startsWith('\\!')) {
|
|
3117
|
+
return { kind: 'text', prompt: trimmedStart.slice(1).trim() };
|
|
3118
|
+
}
|
|
3119
|
+
if (trimmedStart.startsWith('!')) {
|
|
3120
|
+
return { kind: 'shell', command: trimmedStart.slice(1).trim() };
|
|
3121
|
+
}
|
|
2830
3122
|
const trimmed = line.trim();
|
|
2831
3123
|
if (!trimmed.startsWith('/')) {
|
|
2832
3124
|
return { kind: 'text', prompt: trimmed };
|
|
@@ -2858,6 +3150,9 @@ export function parseSlashInput(line) {
|
|
|
2858
3150
|
args: parseCommandArgs(rawArgs),
|
|
2859
3151
|
};
|
|
2860
3152
|
}
|
|
3153
|
+
export function parseSlashInput(line) {
|
|
3154
|
+
return parseCommandBarInput(line);
|
|
3155
|
+
}
|
|
2861
3156
|
export function canSubmitParsedInputWhileBusy(parsed) {
|
|
2862
3157
|
return parsed.kind === 'builtin' && (parsed.name === 'cancel' || parsed.name === 'exit' || parsed.name === 'quit');
|
|
2863
3158
|
}
|
|
@@ -2940,11 +3235,56 @@ async function createSessionInteractive(rl, workspaceRoot) {
|
|
|
2940
3235
|
function renderHeader(session) {
|
|
2941
3236
|
console.log(divider('='));
|
|
2942
3237
|
console.log(`${paint('DeepClause', ANSI.bold, ANSI.cyan)} ${paint('interactive conductor', ANSI.dim)}`);
|
|
2943
|
-
console.log(`${paint('commands', ANSI.dim)} /compile <spec> /new /sessions /help /exit /skill args`);
|
|
3238
|
+
console.log(`${paint('commands', ANSI.dim)} /compile <spec> /set-model <model> /new /sessions /help /exit /skill args`);
|
|
2944
3239
|
console.log(`${paint('session', ANSI.dim)} ${paint(session.id, ANSI.cyan)}`);
|
|
2945
3240
|
console.log(divider('='));
|
|
2946
3241
|
console.log('');
|
|
2947
3242
|
}
|
|
3243
|
+
export function parseSetModelCommandArgs(rawArgs) {
|
|
3244
|
+
const args = parseCommandArgs(rawArgs);
|
|
3245
|
+
let model;
|
|
3246
|
+
let slot;
|
|
3247
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
3248
|
+
const arg = args[index];
|
|
3249
|
+
if (arg === '--slot') {
|
|
3250
|
+
const candidate = args[index + 1];
|
|
3251
|
+
if (!candidate) {
|
|
3252
|
+
throw new Error('Provide a slot after --slot.');
|
|
3253
|
+
}
|
|
3254
|
+
if (!isModelSlot(candidate)) {
|
|
3255
|
+
throw new Error('Slot must be one of: gateway, run, compile.');
|
|
3256
|
+
}
|
|
3257
|
+
if (slot) {
|
|
3258
|
+
throw new Error('Provide --slot only once.');
|
|
3259
|
+
}
|
|
3260
|
+
slot = candidate;
|
|
3261
|
+
index += 1;
|
|
3262
|
+
continue;
|
|
3263
|
+
}
|
|
3264
|
+
if (arg.startsWith('--slot=')) {
|
|
3265
|
+
const candidate = arg.slice('--slot='.length);
|
|
3266
|
+
if (!isModelSlot(candidate)) {
|
|
3267
|
+
throw new Error('Slot must be one of: gateway, run, compile.');
|
|
3268
|
+
}
|
|
3269
|
+
if (slot) {
|
|
3270
|
+
throw new Error('Provide --slot only once.');
|
|
3271
|
+
}
|
|
3272
|
+
slot = candidate;
|
|
3273
|
+
continue;
|
|
3274
|
+
}
|
|
3275
|
+
if (arg.startsWith('--')) {
|
|
3276
|
+
throw new Error(`Unknown option: ${arg}`);
|
|
3277
|
+
}
|
|
3278
|
+
if (model) {
|
|
3279
|
+
throw new Error('Provide exactly one model for /set-model.');
|
|
3280
|
+
}
|
|
3281
|
+
model = arg;
|
|
3282
|
+
}
|
|
3283
|
+
if (!model) {
|
|
3284
|
+
throw new Error('Provide a model after /set-model.');
|
|
3285
|
+
}
|
|
3286
|
+
return { model, slot };
|
|
3287
|
+
}
|
|
2948
3288
|
function streamLabel(logEvent) {
|
|
2949
3289
|
return logEvent.scope === 'child'
|
|
2950
3290
|
? `${CHILD_EVENT_INDENT}[${logEvent.childSlug ?? '?'}] llm`
|
|
@@ -2955,6 +3295,16 @@ function formatEventPrefix(logEvent) {
|
|
|
2955
3295
|
? `${CHILD_EVENT_INDENT}[${logEvent.childSlug ?? '?'}] `
|
|
2956
3296
|
: '';
|
|
2957
3297
|
}
|
|
3298
|
+
function toolScopeKey(logEvent) {
|
|
3299
|
+
return logEvent.scope === 'child'
|
|
3300
|
+
? `child:${logEvent.childSlug ?? '?'}`
|
|
3301
|
+
: 'main';
|
|
3302
|
+
}
|
|
3303
|
+
function toolScopeLabel(logEvent) {
|
|
3304
|
+
return logEvent.scope === 'child'
|
|
3305
|
+
? logEvent.childSlug ?? '?'
|
|
3306
|
+
: 'main';
|
|
3307
|
+
}
|
|
2958
3308
|
function parseTaggedPrompt(promptText) {
|
|
2959
3309
|
const match = promptText.match(/^\[([^\]]+)\]\s*(.*)$/);
|
|
2960
3310
|
if (!match) {
|
|
@@ -3010,6 +3360,58 @@ function formatToolArgs(args) {
|
|
|
3010
3360
|
}
|
|
3011
3361
|
return parts.join(', ');
|
|
3012
3362
|
}
|
|
3363
|
+
function formatSystemAssetSourcePath(workspaceRoot, filePath) {
|
|
3364
|
+
const relativePath = path.relative(workspaceRoot, filePath);
|
|
3365
|
+
if (relativePath && !relativePath.startsWith('..') && !path.isAbsolute(relativePath)) {
|
|
3366
|
+
return relativePath || '.';
|
|
3367
|
+
}
|
|
3368
|
+
return filePath;
|
|
3369
|
+
}
|
|
3370
|
+
function formatToolEventLine(logEvent) {
|
|
3371
|
+
const { event } = logEvent;
|
|
3372
|
+
if (!event.toolName || IGNORED_LIVE_LOG_TOOLS.has(event.toolName)) {
|
|
3373
|
+
return null;
|
|
3374
|
+
}
|
|
3375
|
+
const prefix = formatEventPrefix(logEvent);
|
|
3376
|
+
const callLabel = `${event.toolName}(${formatToolArgs(event.toolArgs)})`;
|
|
3377
|
+
if (!event.toolState) {
|
|
3378
|
+
return `${prefix}tool ${callLabel}`;
|
|
3379
|
+
}
|
|
3380
|
+
return `${prefix}tool ${callLabel} ${event.toolState}${formatToolEventDetails(event)}`;
|
|
3381
|
+
}
|
|
3382
|
+
function formatToolEventDetails(event) {
|
|
3383
|
+
const details = [];
|
|
3384
|
+
if (typeof event.toolPid === 'number') {
|
|
3385
|
+
details.push(`pid=${event.toolPid}`);
|
|
3386
|
+
}
|
|
3387
|
+
else if (event.toolBackend === 'sandbox') {
|
|
3388
|
+
details.push('sandbox');
|
|
3389
|
+
}
|
|
3390
|
+
if (typeof event.toolExitCode === 'number') {
|
|
3391
|
+
details.push(`exit=${event.toolExitCode}`);
|
|
3392
|
+
}
|
|
3393
|
+
const message = event.toolError
|
|
3394
|
+
?? (event.toolSummary && event.toolSummary !== 'Command completed successfully' ? event.toolSummary : undefined);
|
|
3395
|
+
if (message) {
|
|
3396
|
+
details.push(message);
|
|
3397
|
+
}
|
|
3398
|
+
return details.length > 0 ? ` ${details.join(' ')}` : '';
|
|
3399
|
+
}
|
|
3400
|
+
function formatActiveToolStatus(entry) {
|
|
3401
|
+
const details = [];
|
|
3402
|
+
if (typeof entry.toolPid === 'number') {
|
|
3403
|
+
details.push(`pid=${entry.toolPid}`);
|
|
3404
|
+
}
|
|
3405
|
+
else if (entry.toolBackend === 'sandbox') {
|
|
3406
|
+
details.push('sandbox');
|
|
3407
|
+
}
|
|
3408
|
+
const detailSuffix = details.length > 0 ? ` ${details.join(' ')}` : '';
|
|
3409
|
+
return `${entry.scopeLabel} ${entry.toolName} ${entry.toolState}${detailSuffix}`;
|
|
3410
|
+
}
|
|
3411
|
+
function formatShellModeStatus(workspaceRoot, shellWorkspacePath, sandbox) {
|
|
3412
|
+
const displayPath = path.relative(workspaceRoot, shellWorkspacePath) || '.';
|
|
3413
|
+
return `Shell mode: ${sandbox ? 'AgentVM sandbox' : 'host shell'} in ${displayPath}. Enter runs, /cancel stops.`;
|
|
3414
|
+
}
|
|
3013
3415
|
function buildHelpLines() {
|
|
3014
3416
|
return [
|
|
3015
3417
|
'commands',
|
|
@@ -3017,10 +3419,12 @@ function buildHelpLines() {
|
|
|
3017
3419
|
'/sessions refresh or choose sessions',
|
|
3018
3420
|
'/compile <spec> compile and publish a new local skill',
|
|
3019
3421
|
'/skill-creator <spec> alias for /compile',
|
|
3422
|
+
'/set-model <model> [--slot <slot>] update the configured model selection',
|
|
3020
3423
|
'/cancel abort the current execution',
|
|
3021
3424
|
'/help show this help',
|
|
3022
3425
|
'/quit exit the TUI',
|
|
3023
3426
|
'/<skill> [args] run a compiled skill directly',
|
|
3427
|
+
'!<command> run a shell command directly in the workspace shell',
|
|
3024
3428
|
'keys',
|
|
3025
3429
|
'F10 / Ctrl+G open the menu bar',
|
|
3026
3430
|
'Ctrl+P open the action palette',
|
|
@@ -3041,6 +3445,9 @@ function getShortcutHelpLines() {
|
|
|
3041
3445
|
const keysIndex = lines.indexOf('keys');
|
|
3042
3446
|
return keysIndex === -1 ? [] : lines.slice(keysIndex);
|
|
3043
3447
|
}
|
|
3448
|
+
function isModelSlot(value) {
|
|
3449
|
+
return MODEL_SLOTS.includes(value);
|
|
3450
|
+
}
|
|
3044
3451
|
function computePaneWidths(columns) {
|
|
3045
3452
|
const available = Math.max(36, columns - 2);
|
|
3046
3453
|
let left = Math.max(18, Math.floor(available * 0.2));
|