deepclause-sdk 0.0.68 → 0.0.70
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 +10 -2
- package/dist/agent.d.ts +1 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +252 -67
- package/dist/agent.js.map +1 -1
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +64 -7
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/config.d.ts +15 -12
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +38 -5
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/default-skills.d.ts +1 -0
- package/dist/cli/default-skills.d.ts.map +1 -1
- package/dist/cli/default-skills.js +31 -0
- package/dist/cli/default-skills.js.map +1 -1
- package/dist/cli/tui.d.ts +16 -1
- package/dist/cli/tui.d.ts.map +1 -1
- package/dist/cli/tui.js +293 -21
- package/dist/cli/tui.js.map +1 -1
- package/dist/prolog/bridge.d.ts +15 -0
- package/dist/prolog/bridge.d.ts.map +1 -1
- package/dist/prolog/bridge.js +68 -0
- package/dist/prolog/bridge.js.map +1 -1
- package/dist/prolog-src/deepclause_mi.pl +135 -7
- package/dist/runner.d.ts +8 -0
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +79 -0
- package/dist/runner.js.map +1 -1
- package/dist/system/assets/docs/DML_COMPILER_PROMPT.md +7 -3
- package/dist/system/assets/docs/DML_REFERENCE.md +656 -0
- package/dist/system/assets/docs/TASK_PROMPT.md +33 -0
- package/dist/system/assets/docs/TUI.md +11 -0
- package/dist/system/assets/index.d.ts +12 -9
- package/dist/system/assets/index.d.ts.map +1 -1
- package/dist/system/assets/index.js +38 -9
- package/dist/system/assets/index.js.map +1 -1
- package/dist/system/assets/recipes/deepclause-coding-workflow/SKILL.md +56 -1
- package/dist/system/assets/skills/plan.dml +259 -0
- package/dist/system/runtime/catalog-skills.d.ts +1 -1
- package/dist/system/runtime/catalog-skills.d.ts.map +1 -1
- package/dist/system/runtime/catalog-skills.js +19 -5
- package/dist/system/runtime/catalog-skills.js.map +1 -1
- package/dist/system/runtime/conductor.d.ts +5 -1
- package/dist/system/runtime/conductor.d.ts.map +1 -1
- package/dist/system/runtime/conductor.js +22 -0
- package/dist/system/runtime/conductor.js.map +1 -1
- package/dist/system/runtime/dml-executor.d.ts +1 -1
- package/dist/system/runtime/dml-executor.d.ts.map +1 -1
- package/dist/system/runtime/dml-executor.js +2 -14
- package/dist/system/runtime/dml-executor.js.map +1 -1
- package/dist/system/runtime/dml-params.d.ts +2 -2
- package/dist/system/runtime/dml-params.d.ts.map +1 -1
- package/dist/system/runtime/dml-params.js +3 -0
- package/dist/system/runtime/dml-params.js.map +1 -1
- package/dist/system/runtime/runtime-tools.d.ts +1 -0
- package/dist/system/runtime/runtime-tools.d.ts.map +1 -1
- package/dist/system/runtime/runtime-tools.js +95 -0
- package/dist/system/runtime/runtime-tools.js.map +1 -1
- package/dist/system/runtime/shell-manager.js +1 -1
- package/dist/system/runtime/shell-manager.js.map +1 -1
- package/dist/system/runtime/skill-creator.d.ts.map +1 -1
- package/dist/system/runtime/skill-creator.js +52 -21
- package/dist/system/runtime/skill-creator.js.map +1 -1
- package/package.json +4 -3
- package/src/prolog-src/deepclause_mi.pl +135 -7
package/dist/cli/tui.js
CHANGED
|
@@ -7,12 +7,12 @@ import { listCommands } from './commands.js';
|
|
|
7
7
|
import { getConfigPath, loadConfig, resolveModelSlot, setModel } from './config.js';
|
|
8
8
|
import { run } from './run.js';
|
|
9
9
|
import { formatToolArgs } from './tool-args.js';
|
|
10
|
-
import { createSessionExecutionLogWriter, createLocalSkill, createConductorSession, getConductorSessionDetail, listConductorSessions, runConductorTurn, } from '../system/runtime/conductor.js';
|
|
10
|
+
import { appendConductorSessionMessages, createSessionExecutionLogWriter, createLocalSkill, createConductorSession, getConductorSessionDetail, listConductorSessions, runConductorTurn, } from '../system/runtime/conductor.js';
|
|
11
11
|
import { getSystemAssetSourcePaths } from '../system/assets/index.js';
|
|
12
12
|
import { createShellManager, describeShellExecutionBackend, } from '../system/runtime/shell-manager.js';
|
|
13
13
|
import { buildToolCompletionEvent, buildToolFailureEvent, buildToolStartEvent, createShellToolEventBridge, } from '../system/runtime/shell-tool-events.js';
|
|
14
14
|
const IGNORED_LIVE_LOG_TOOLS = new Set(['set_result', 'update_memory']);
|
|
15
|
-
const BUILTIN_SLASH_COMMANDS = ['new', 'sessions', 'help', 'compile', 'skill-creator', 'set-model', 'cancel', 'exit', 'quit'];
|
|
15
|
+
const BUILTIN_SLASH_COMMANDS = ['new', 'sessions', 'help', 'run', 'compile', 'skill-creator', 'set-model', 'cancel', 'exit', 'quit'];
|
|
16
16
|
const MODEL_SLOTS = ['gateway', 'run', 'compile'];
|
|
17
17
|
const CHILD_EVENT_INDENT = '\t';
|
|
18
18
|
const CHILD_EVENT_TAB_WIDTH = 4;
|
|
@@ -23,6 +23,7 @@ const BUSY_RENDER_INTERVAL_MS = 40;
|
|
|
23
23
|
const TYPING_RENDER_INTERVAL_MS = 90;
|
|
24
24
|
const COMBINING_MARK_RE = /\p{Mark}/u;
|
|
25
25
|
const EXTENDED_PICTOGRAPHIC_RE = /\p{Extended_Pictographic}/u;
|
|
26
|
+
const MAX_PERSISTED_SHELL_STREAM_CHARS = 4000;
|
|
26
27
|
const TEXT_FILE_EXTENSIONS = new Set([
|
|
27
28
|
'.dml', '.md', '.txt', '.json', '.yaml', '.yml', '.toml', '.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs',
|
|
28
29
|
'.css', '.scss', '.html', '.sql', '.py', '.sh', '.bash', '.zsh', '.pro', '.pl', '.csv', '.env', '.example',
|
|
@@ -828,6 +829,9 @@ class FullscreenTui {
|
|
|
828
829
|
this.requestRender();
|
|
829
830
|
return;
|
|
830
831
|
}
|
|
832
|
+
case 'run':
|
|
833
|
+
await this.executeRunFile(parsed.args);
|
|
834
|
+
return;
|
|
831
835
|
case 'compile':
|
|
832
836
|
case 'skill-creator':
|
|
833
837
|
await this.executeSkillCreator(parsed.name, parsed.rawArgs);
|
|
@@ -923,7 +927,7 @@ class FullscreenTui {
|
|
|
923
927
|
this.statusLine = `Running /${skillName}...`;
|
|
924
928
|
this.requestRender();
|
|
925
929
|
try {
|
|
926
|
-
const result = await
|
|
930
|
+
const result = await runSlashCommand(this.workspaceRoot, skillName, args, {
|
|
927
931
|
sessionId: this.selectedSessionId || undefined,
|
|
928
932
|
sandbox: this.options.sandbox,
|
|
929
933
|
signal: this.currentAbortController.signal,
|
|
@@ -962,6 +966,68 @@ class FullscreenTui {
|
|
|
962
966
|
}
|
|
963
967
|
}
|
|
964
968
|
}
|
|
969
|
+
async executeRunFile(args) {
|
|
970
|
+
if (args.length === 0) {
|
|
971
|
+
this.statusLine = 'Provide a DML file after /run.';
|
|
972
|
+
this.requestRender();
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
const [fileSpec, ...runArgs] = args;
|
|
976
|
+
const sessionId = this.selectedSessionId || 'global';
|
|
977
|
+
const activity = this.activityFor(sessionId);
|
|
978
|
+
const commandText = `/run ${args.join(' ')}`;
|
|
979
|
+
const previewTag = path.basename(fileSpec, path.extname(fileSpec)) || 'run';
|
|
980
|
+
this.lastSubmittedInput = { kind: 'builtin', name: 'run', rawArgs: args.join(' '), args: [...args] };
|
|
981
|
+
this.focusedPane = 'messages';
|
|
982
|
+
this.paneScroll.messages = 0;
|
|
983
|
+
this.paneScroll.process = 0;
|
|
984
|
+
this.beginExecutionPreview(sessionId, 'skill', { role: 'system', content: commandText }, previewTag);
|
|
985
|
+
activity.pushLine(`run ${commandText}`);
|
|
986
|
+
this.busy = true;
|
|
987
|
+
this.currentAbortController = new AbortController();
|
|
988
|
+
this.startAnimation();
|
|
989
|
+
this.statusLine = `Running ${commandText}...`;
|
|
990
|
+
this.requestRender();
|
|
991
|
+
try {
|
|
992
|
+
const result = await runFileCommand(this.workspaceRoot, fileSpec, runArgs, {
|
|
993
|
+
sessionId: this.selectedSessionId || undefined,
|
|
994
|
+
sandbox: this.options.sandbox,
|
|
995
|
+
signal: this.currentAbortController.signal,
|
|
996
|
+
onUserInput: (question) => this.requestClarification(question),
|
|
997
|
+
onEvent: (event) => {
|
|
998
|
+
activity.handle(event);
|
|
999
|
+
this.updatePreviewFromEvent(event);
|
|
1000
|
+
this.requestRender();
|
|
1001
|
+
},
|
|
1002
|
+
});
|
|
1003
|
+
activity.finish();
|
|
1004
|
+
await this.refreshCommands();
|
|
1005
|
+
this.finishExecutionPreview({
|
|
1006
|
+
persist: true,
|
|
1007
|
+
finalText: result.answer || summarizeRunResult(result),
|
|
1008
|
+
error: result.error,
|
|
1009
|
+
});
|
|
1010
|
+
this.statusLine = result.error ? `${commandText} failed.` : `${commandText} finished.`;
|
|
1011
|
+
}
|
|
1012
|
+
catch (error) {
|
|
1013
|
+
activity.finish();
|
|
1014
|
+
const message = error.message;
|
|
1015
|
+
activity.pushLine(`[run] error ${message}`);
|
|
1016
|
+
this.finishExecutionPreview({ persist: true, error: message });
|
|
1017
|
+
this.statusLine = `${commandText} failed.`;
|
|
1018
|
+
}
|
|
1019
|
+
finally {
|
|
1020
|
+
this.busy = false;
|
|
1021
|
+
this.currentAbortController = null;
|
|
1022
|
+
this.stopAnimation();
|
|
1023
|
+
if (this.exitRequested) {
|
|
1024
|
+
this.close();
|
|
1025
|
+
}
|
|
1026
|
+
else {
|
|
1027
|
+
this.requestRender();
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
965
1031
|
async executeSkillCreator(commandName, specText) {
|
|
966
1032
|
const trimmedSpec = specText.trim();
|
|
967
1033
|
if (!trimmedSpec) {
|
|
@@ -1021,16 +1087,16 @@ class FullscreenTui {
|
|
|
1021
1087
|
}
|
|
1022
1088
|
}
|
|
1023
1089
|
}
|
|
1024
|
-
async executeShellCommand(commandText) {
|
|
1090
|
+
async executeShellCommand(commandText, persistOutput = false) {
|
|
1025
1091
|
const trimmedCommand = commandText.trim();
|
|
1026
1092
|
if (!trimmedCommand) {
|
|
1027
|
-
this.statusLine = 'Enter a shell command after
|
|
1093
|
+
this.statusLine = 'Enter a shell command after ! or !!.';
|
|
1028
1094
|
this.requestRender();
|
|
1029
1095
|
return;
|
|
1030
1096
|
}
|
|
1031
1097
|
const sessionId = this.selectedSessionId || 'global';
|
|
1032
1098
|
const activity = this.activityFor(sessionId);
|
|
1033
|
-
this.lastSubmittedInput = { kind: 'shell', command: trimmedCommand };
|
|
1099
|
+
this.lastSubmittedInput = { kind: 'shell', command: trimmedCommand, persistOutput };
|
|
1034
1100
|
this.focusedPane = 'messages';
|
|
1035
1101
|
this.paneScroll.messages = 0;
|
|
1036
1102
|
this.paneScroll.process = 0;
|
|
@@ -1038,14 +1104,15 @@ class FullscreenTui {
|
|
|
1038
1104
|
role: 'system',
|
|
1039
1105
|
kind: 'output',
|
|
1040
1106
|
tag: 'shell',
|
|
1041
|
-
content:
|
|
1107
|
+
content: `${persistOutput ? '!!' : '!'}${trimmedCommand}`,
|
|
1042
1108
|
}, 'shell');
|
|
1043
|
-
activity.pushLine(`shell
|
|
1109
|
+
activity.pushLine(`shell ${persistOutput ? '!!' : '!'}${trimmedCommand}`);
|
|
1044
1110
|
this.busy = true;
|
|
1045
1111
|
this.currentAbortController = new AbortController();
|
|
1046
1112
|
this.startAnimation();
|
|
1047
|
-
this.statusLine = `Running
|
|
1113
|
+
this.statusLine = `Running ${persistOutput ? '!!' : '!'}${trimmedCommand}...`;
|
|
1048
1114
|
this.requestRender();
|
|
1115
|
+
let persistenceStarted = false;
|
|
1049
1116
|
try {
|
|
1050
1117
|
const result = await runShellCommand(this.workspaceRoot, trimmedCommand, {
|
|
1051
1118
|
sandbox: this.options.sandbox,
|
|
@@ -1057,16 +1124,31 @@ class FullscreenTui {
|
|
|
1057
1124
|
});
|
|
1058
1125
|
activity.finish();
|
|
1059
1126
|
await this.refreshCommands();
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1127
|
+
if (persistOutput) {
|
|
1128
|
+
persistenceStarted = true;
|
|
1129
|
+
const persistedSessionId = await this.persistShellCommandResult(trimmedCommand, result);
|
|
1130
|
+
await this.refreshSessions({ createIfMissing: true, selectedSessionId: persistedSessionId });
|
|
1131
|
+
this.finishExecutionPreview({ persist: false });
|
|
1132
|
+
this.statusLine = result.success
|
|
1133
|
+
? 'Shell command finished and was added to the session.'
|
|
1134
|
+
: 'Shell command failed and its output was added to the session.';
|
|
1135
|
+
}
|
|
1136
|
+
else {
|
|
1137
|
+
this.finishExecutionPreview(result.success
|
|
1138
|
+
? { persist: true, finalText: formatShellCommandSummary(result) }
|
|
1139
|
+
: { persist: true, error: formatShellCommandSummary(result) });
|
|
1140
|
+
this.statusLine = result.success ? 'Shell command finished.' : 'Shell command failed.';
|
|
1141
|
+
}
|
|
1064
1142
|
}
|
|
1065
1143
|
catch (error) {
|
|
1066
1144
|
activity.finish();
|
|
1067
1145
|
const message = error.message;
|
|
1068
1146
|
this.finishExecutionPreview({ persist: true, error: message });
|
|
1069
|
-
this.statusLine = isAbortError(error)
|
|
1147
|
+
this.statusLine = isAbortError(error)
|
|
1148
|
+
? 'Shell command cancelled.'
|
|
1149
|
+
: persistOutput && persistenceStarted
|
|
1150
|
+
? 'Shell command finished, but saving its transcript failed.'
|
|
1151
|
+
: 'Shell command failed.';
|
|
1070
1152
|
}
|
|
1071
1153
|
finally {
|
|
1072
1154
|
this.busy = false;
|
|
@@ -1423,6 +1505,7 @@ class FullscreenTui {
|
|
|
1423
1505
|
'',
|
|
1424
1506
|
'Use F10 or Ctrl+G for the menu bar, Ctrl+W to cycle panes, and /cancel to stop a running execution.',
|
|
1425
1507
|
'Type !<command> to run a direct shell command in the configured workspace shell.',
|
|
1508
|
+
'Type !!<command> to run a shell command and append its transcript to the active session.',
|
|
1426
1509
|
'The Skills and Files menus open searchable pickers, and Ctrl+P opens the action palette directly.',
|
|
1427
1510
|
].join('\n'), { preferredExtension: '.txt' });
|
|
1428
1511
|
return;
|
|
@@ -1974,7 +2057,7 @@ class FullscreenTui {
|
|
|
1974
2057
|
return;
|
|
1975
2058
|
}
|
|
1976
2059
|
if (this.lastSubmittedInput.kind === 'shell') {
|
|
1977
|
-
await this.executeShellCommand(this.lastSubmittedInput.command);
|
|
2060
|
+
await this.executeShellCommand(this.lastSubmittedInput.command, this.lastSubmittedInput.persistOutput);
|
|
1978
2061
|
return;
|
|
1979
2062
|
}
|
|
1980
2063
|
if (this.lastSubmittedInput.kind === 'builtin') {
|
|
@@ -2333,6 +2416,18 @@ class FullscreenTui {
|
|
|
2333
2416
|
this.activityBySessionId.set(key, created);
|
|
2334
2417
|
return created;
|
|
2335
2418
|
}
|
|
2419
|
+
async persistShellCommandResult(command, result) {
|
|
2420
|
+
let sessionId = this.selectedSessionId;
|
|
2421
|
+
if (!sessionId) {
|
|
2422
|
+
await this.refreshSessions({ createIfMissing: true });
|
|
2423
|
+
sessionId = this.selectedSessionId;
|
|
2424
|
+
}
|
|
2425
|
+
if (!sessionId) {
|
|
2426
|
+
throw new Error('No conductor session available to persist shell output.');
|
|
2427
|
+
}
|
|
2428
|
+
await appendConductorSessionMessages(this.workspaceRoot, sessionId, buildPersistedShellTranscript(command, result));
|
|
2429
|
+
return sessionId;
|
|
2430
|
+
}
|
|
2336
2431
|
requestRender() {
|
|
2337
2432
|
if (this.renderTimer) {
|
|
2338
2433
|
return;
|
|
@@ -2559,8 +2654,10 @@ class FullscreenTui {
|
|
|
2559
2654
|
'System Asset Sources',
|
|
2560
2655
|
`Conductor DML ${formatSystemAssetSourcePath(this.workspaceRoot, systemAssetSources.conductorDml)}`,
|
|
2561
2656
|
`Conductor Prompt ${formatSystemAssetSourcePath(this.workspaceRoot, systemAssetSources.conductorPrompt)}`,
|
|
2657
|
+
`Plan DML ${formatSystemAssetSourcePath(this.workspaceRoot, systemAssetSources.planDml)}`,
|
|
2562
2658
|
`Skill Creator DML ${formatSystemAssetSourcePath(this.workspaceRoot, systemAssetSources.skillCreatorDml)}`,
|
|
2563
2659
|
`Skill Creator Prompt ${formatSystemAssetSourcePath(this.workspaceRoot, systemAssetSources.skillCreatorPrompt)}`,
|
|
2660
|
+
`Task Prompt ${formatSystemAssetSourcePath(this.workspaceRoot, systemAssetSources.taskPrompt)}`,
|
|
2564
2661
|
'Changes apply on the next turn or /compile run. No TUI reload is required.',
|
|
2565
2662
|
'',
|
|
2566
2663
|
'Shell Execution',
|
|
@@ -2869,6 +2966,38 @@ async function startLineTui(workspaceRoot = process.cwd(), options = {}) {
|
|
|
2869
2966
|
console.log(helpLine);
|
|
2870
2967
|
}
|
|
2871
2968
|
continue;
|
|
2969
|
+
case 'run':
|
|
2970
|
+
if (parsed.args.length === 0) {
|
|
2971
|
+
console.log('Provide a DML file after /run.');
|
|
2972
|
+
continue;
|
|
2973
|
+
}
|
|
2974
|
+
console.log(divider('-'));
|
|
2975
|
+
console.log(`${paint('run', ANSI.dim)} /run ${parsed.args.join(' ')}`);
|
|
2976
|
+
console.log(divider('-'));
|
|
2977
|
+
{
|
|
2978
|
+
const printer = new LiveExecutionPrinter();
|
|
2979
|
+
try {
|
|
2980
|
+
await runFileCommand(workspaceRoot, parsed.args[0], parsed.args.slice(1), {
|
|
2981
|
+
sessionId: session.id,
|
|
2982
|
+
sandbox: options.sandbox,
|
|
2983
|
+
onUserInput: async (question) => {
|
|
2984
|
+
printer.finish();
|
|
2985
|
+
console.log(`${paint('clarify', ANSI.yellow)} ${question}`);
|
|
2986
|
+
return (await rl.question('> ')).trim();
|
|
2987
|
+
},
|
|
2988
|
+
onEvent: (event) => printer.handle(event),
|
|
2989
|
+
});
|
|
2990
|
+
}
|
|
2991
|
+
catch (error) {
|
|
2992
|
+
printer.finish();
|
|
2993
|
+
console.log(`${paint('error', ANSI.red)} ${error.message}`);
|
|
2994
|
+
}
|
|
2995
|
+
printer.finish();
|
|
2996
|
+
}
|
|
2997
|
+
console.log('');
|
|
2998
|
+
console.log(divider('-'));
|
|
2999
|
+
console.log('');
|
|
3000
|
+
continue;
|
|
2872
3001
|
case 'compile':
|
|
2873
3002
|
case 'skill-creator':
|
|
2874
3003
|
if (!parsed.rawArgs.trim()) {
|
|
@@ -2946,7 +3075,7 @@ async function startLineTui(workspaceRoot = process.cwd(), options = {}) {
|
|
|
2946
3075
|
console.log(divider('-'));
|
|
2947
3076
|
const printer = new LiveExecutionPrinter();
|
|
2948
3077
|
try {
|
|
2949
|
-
await
|
|
3078
|
+
await runSlashCommand(workspaceRoot, parsed.name, parsed.args, {
|
|
2950
3079
|
sessionId: session.id,
|
|
2951
3080
|
sandbox: options.sandbox,
|
|
2952
3081
|
onUserInput: async (question) => {
|
|
@@ -3028,13 +3157,57 @@ export async function runSkillCommand(workspaceRoot, skillName, args, options =
|
|
|
3028
3157
|
if (!command) {
|
|
3029
3158
|
throw new Error(`Unknown skill: ${skillName}`);
|
|
3030
3159
|
}
|
|
3160
|
+
return executeRunnableCommand(workspaceRoot, {
|
|
3161
|
+
inputText: `/${skillName}${args.length > 0 ? ` ${args.join(' ')}` : ''}`,
|
|
3162
|
+
runPath: command.path,
|
|
3163
|
+
childSlug: skillName,
|
|
3164
|
+
skillName,
|
|
3165
|
+
}, args, options);
|
|
3166
|
+
}
|
|
3167
|
+
export async function runSlashCommand(workspaceRoot, name, args, options = {}) {
|
|
3168
|
+
const commands = await listCommands(workspaceRoot);
|
|
3169
|
+
const command = commands.find((entry) => entry.name === name);
|
|
3170
|
+
if (command) {
|
|
3171
|
+
return executeRunnableCommand(workspaceRoot, {
|
|
3172
|
+
inputText: `/${name}${args.length > 0 ? ` ${args.join(' ')}` : ''}`,
|
|
3173
|
+
runPath: command.path,
|
|
3174
|
+
childSlug: name,
|
|
3175
|
+
skillName: name,
|
|
3176
|
+
}, args, options);
|
|
3177
|
+
}
|
|
3178
|
+
const resolvedPlanPath = await resolveWorkspaceDmlPath(workspaceRoot, name, { mode: 'plans-only' });
|
|
3179
|
+
if (!resolvedPlanPath) {
|
|
3180
|
+
throw new Error(`Unknown skill or plan: ${name}`);
|
|
3181
|
+
}
|
|
3182
|
+
const childSlug = path.basename(resolvedPlanPath, path.extname(resolvedPlanPath)) || name;
|
|
3183
|
+
return executeRunnableCommand(workspaceRoot, {
|
|
3184
|
+
inputText: `/${name}${args.length > 0 ? ` ${args.join(' ')}` : ''}`,
|
|
3185
|
+
runPath: resolvedPlanPath,
|
|
3186
|
+
childSlug,
|
|
3187
|
+
skillName: childSlug,
|
|
3188
|
+
}, args, options);
|
|
3189
|
+
}
|
|
3190
|
+
export async function runFileCommand(workspaceRoot, fileSpec, args, options = {}) {
|
|
3191
|
+
const resolvedPath = await resolveWorkspaceDmlPath(workspaceRoot, fileSpec, { mode: 'direct' });
|
|
3192
|
+
if (!resolvedPath) {
|
|
3193
|
+
throw new Error(`DML file not found: ${fileSpec}`);
|
|
3194
|
+
}
|
|
3195
|
+
const childSlug = path.basename(resolvedPath, path.extname(resolvedPath)) || 'run';
|
|
3196
|
+
return executeRunnableCommand(workspaceRoot, {
|
|
3197
|
+
inputText: `/run ${[fileSpec, ...args].join(' ')}`.trim(),
|
|
3198
|
+
runPath: resolvedPath,
|
|
3199
|
+
childSlug,
|
|
3200
|
+
skillName: childSlug,
|
|
3201
|
+
}, args, options);
|
|
3202
|
+
}
|
|
3203
|
+
async function executeRunnableCommand(workspaceRoot, target, args, options = {}) {
|
|
3031
3204
|
const executionLog = options.sessionId
|
|
3032
3205
|
? createSessionExecutionLogWriter({
|
|
3033
3206
|
workspaceRoot,
|
|
3034
3207
|
sessionId: options.sessionId,
|
|
3035
3208
|
executionKind: 'skill',
|
|
3036
|
-
inputText:
|
|
3037
|
-
skillName,
|
|
3209
|
+
inputText: target.inputText,
|
|
3210
|
+
skillName: target.skillName,
|
|
3038
3211
|
args,
|
|
3039
3212
|
})
|
|
3040
3213
|
: null;
|
|
@@ -3043,14 +3216,14 @@ export async function runSkillCommand(workspaceRoot, skillName, args, options =
|
|
|
3043
3216
|
options.onEvent?.(event);
|
|
3044
3217
|
};
|
|
3045
3218
|
try {
|
|
3046
|
-
const result = await run(
|
|
3219
|
+
const result = await run(target.runPath, args, {
|
|
3047
3220
|
configRoot: workspaceRoot,
|
|
3048
3221
|
headless: true,
|
|
3049
3222
|
stream: true,
|
|
3050
3223
|
sandbox: options.sandbox,
|
|
3051
3224
|
signal: options.signal,
|
|
3052
3225
|
onUserInput: options.onUserInput,
|
|
3053
|
-
onEvent: (event) => emitLogEvent({ scope: 'child', childSlug:
|
|
3226
|
+
onEvent: (event) => emitLogEvent({ scope: 'child', childSlug: target.childSlug, event }),
|
|
3054
3227
|
onChildEvent: (childSlug, event) => emitLogEvent({ scope: 'child', childSlug, event }),
|
|
3055
3228
|
});
|
|
3056
3229
|
await executionLog?.finish({
|
|
@@ -3069,6 +3242,64 @@ export async function runSkillCommand(workspaceRoot, skillName, args, options =
|
|
|
3069
3242
|
throw error;
|
|
3070
3243
|
}
|
|
3071
3244
|
}
|
|
3245
|
+
async function resolveWorkspaceDmlPath(workspaceRoot, fileSpec, options) {
|
|
3246
|
+
const trimmed = fileSpec.trim();
|
|
3247
|
+
if (!trimmed) {
|
|
3248
|
+
return null;
|
|
3249
|
+
}
|
|
3250
|
+
const candidates = options.mode === 'plans-only'
|
|
3251
|
+
? buildPlanOnlyCandidates(workspaceRoot, trimmed)
|
|
3252
|
+
: buildDirectDmlCandidates(workspaceRoot, trimmed);
|
|
3253
|
+
for (const candidate of candidates) {
|
|
3254
|
+
if (await fileExists(candidate)) {
|
|
3255
|
+
return candidate;
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3258
|
+
return null;
|
|
3259
|
+
}
|
|
3260
|
+
function buildDirectDmlCandidates(workspaceRoot, fileSpec) {
|
|
3261
|
+
const trimmed = fileSpec.trim();
|
|
3262
|
+
const candidates = new Set();
|
|
3263
|
+
const directPath = path.resolve(workspaceRoot, trimmed);
|
|
3264
|
+
candidates.add(directPath);
|
|
3265
|
+
if (!trimmed.endsWith('.dml')) {
|
|
3266
|
+
candidates.add(path.resolve(workspaceRoot, `${trimmed}.dml`));
|
|
3267
|
+
}
|
|
3268
|
+
const normalized = trimLeadingPathSeparators(trimmed).replace(/\\/g, '/');
|
|
3269
|
+
const planRelative = normalized.startsWith('plans/') ? normalized : path.posix.join('plans', normalized);
|
|
3270
|
+
candidates.add(path.resolve(workspaceRoot, planRelative));
|
|
3271
|
+
if (!planRelative.endsWith('.dml')) {
|
|
3272
|
+
candidates.add(path.resolve(workspaceRoot, `${planRelative}.dml`));
|
|
3273
|
+
}
|
|
3274
|
+
return Array.from(candidates);
|
|
3275
|
+
}
|
|
3276
|
+
function buildPlanOnlyCandidates(workspaceRoot, fileSpec) {
|
|
3277
|
+
const normalized = trimLeadingPathSeparators(fileSpec.trim()).replace(/\\/g, '/');
|
|
3278
|
+
const planRelative = normalized.startsWith('plans/') ? normalized : path.posix.join('plans', normalized);
|
|
3279
|
+
const plansRoot = path.resolve(workspaceRoot, 'plans');
|
|
3280
|
+
const candidates = [
|
|
3281
|
+
path.resolve(workspaceRoot, planRelative),
|
|
3282
|
+
...(planRelative.endsWith('.dml') ? [] : [path.resolve(workspaceRoot, `${planRelative}.dml`)]),
|
|
3283
|
+
];
|
|
3284
|
+
return candidates.filter((candidate, index, all) => {
|
|
3285
|
+
if (all.indexOf(candidate) !== index) {
|
|
3286
|
+
return false;
|
|
3287
|
+
}
|
|
3288
|
+
return candidate === plansRoot || candidate.startsWith(`${plansRoot}${path.sep}`);
|
|
3289
|
+
});
|
|
3290
|
+
}
|
|
3291
|
+
function trimLeadingPathSeparators(value) {
|
|
3292
|
+
return value.replace(/^[/\\]+/, '');
|
|
3293
|
+
}
|
|
3294
|
+
async function fileExists(filePath) {
|
|
3295
|
+
try {
|
|
3296
|
+
await fs.access(filePath);
|
|
3297
|
+
return true;
|
|
3298
|
+
}
|
|
3299
|
+
catch {
|
|
3300
|
+
return false;
|
|
3301
|
+
}
|
|
3302
|
+
}
|
|
3072
3303
|
async function runSkillCreatorCommand(workspaceRoot, sessionId, spec, options = {}) {
|
|
3073
3304
|
const trimmedSpec = spec.trim();
|
|
3074
3305
|
if (!trimmedSpec) {
|
|
@@ -3163,6 +3394,41 @@ function formatShellCommandSummary(result) {
|
|
|
3163
3394
|
}
|
|
3164
3395
|
return result.summary || `Shell command failed with exit code ${result.exitCode}.`;
|
|
3165
3396
|
}
|
|
3397
|
+
function buildPersistedShellTranscript(command, result) {
|
|
3398
|
+
const sections = [
|
|
3399
|
+
'[shell result]',
|
|
3400
|
+
`Command: ${command}`,
|
|
3401
|
+
`Summary: ${formatShellCommandSummary(result)}`,
|
|
3402
|
+
`Backend: ${result.backendLabel ?? result.backend}`,
|
|
3403
|
+
`Exit code: ${result.exitCode}`,
|
|
3404
|
+
];
|
|
3405
|
+
const stdout = truncateShellTranscriptStream(result.stdout, 'stdout');
|
|
3406
|
+
const stderr = truncateShellTranscriptStream(result.stderr, 'stderr');
|
|
3407
|
+
if (stdout) {
|
|
3408
|
+
sections.push(`stdout:\n${stdout}`);
|
|
3409
|
+
}
|
|
3410
|
+
if (stderr) {
|
|
3411
|
+
sections.push(`stderr:\n${stderr}`);
|
|
3412
|
+
}
|
|
3413
|
+
if (!stdout && !stderr) {
|
|
3414
|
+
sections.push('No stdout or stderr output.');
|
|
3415
|
+
}
|
|
3416
|
+
return [
|
|
3417
|
+
{ role: 'user', content: `[shell command]\n${command}` },
|
|
3418
|
+
{ role: 'assistant', content: sections.join('\n\n') },
|
|
3419
|
+
];
|
|
3420
|
+
}
|
|
3421
|
+
function truncateShellTranscriptStream(content, label) {
|
|
3422
|
+
const trimmed = content.trim();
|
|
3423
|
+
if (!trimmed) {
|
|
3424
|
+
return '';
|
|
3425
|
+
}
|
|
3426
|
+
if (trimmed.length <= MAX_PERSISTED_SHELL_STREAM_CHARS) {
|
|
3427
|
+
return trimmed;
|
|
3428
|
+
}
|
|
3429
|
+
const omitted = trimmed.length - MAX_PERSISTED_SHELL_STREAM_CHARS;
|
|
3430
|
+
return `${trimmed.slice(0, MAX_PERSISTED_SHELL_STREAM_CHARS).trimEnd()}\n\n[${label} truncated: ${omitted} more chars omitted]`;
|
|
3431
|
+
}
|
|
3166
3432
|
function isAbortError(error) {
|
|
3167
3433
|
return error instanceof Error && error.name === 'AbortError';
|
|
3168
3434
|
}
|
|
@@ -3254,8 +3520,11 @@ export function parseCommandBarInput(line) {
|
|
|
3254
3520
|
if (trimmedStart.startsWith('\\!')) {
|
|
3255
3521
|
return { kind: 'text', prompt: trimmedStart.slice(1).trim() };
|
|
3256
3522
|
}
|
|
3523
|
+
if (trimmedStart.startsWith('!!')) {
|
|
3524
|
+
return { kind: 'shell', command: trimmedStart.slice(2).trim(), persistOutput: true };
|
|
3525
|
+
}
|
|
3257
3526
|
if (trimmedStart.startsWith('!')) {
|
|
3258
|
-
return { kind: 'shell', command: trimmedStart.slice(1).trim() };
|
|
3527
|
+
return { kind: 'shell', command: trimmedStart.slice(1).trim(), persistOutput: false };
|
|
3259
3528
|
}
|
|
3260
3529
|
const trimmed = line.trim();
|
|
3261
3530
|
if (!trimmed.startsWith('/')) {
|
|
@@ -3613,6 +3882,7 @@ function buildHelpLines() {
|
|
|
3613
3882
|
'commands',
|
|
3614
3883
|
'/new [title] create a new conductor session',
|
|
3615
3884
|
'/sessions refresh or choose sessions',
|
|
3885
|
+
'/run <file> [args] run a workspace DML file directly',
|
|
3616
3886
|
'/compile <spec> compile and publish a new local skill',
|
|
3617
3887
|
'/skill-creator <spec> alias for /compile',
|
|
3618
3888
|
'/set-model <model> [--slot <slot>] update the configured model selection',
|
|
@@ -3620,7 +3890,9 @@ function buildHelpLines() {
|
|
|
3620
3890
|
'/help show this help',
|
|
3621
3891
|
'/quit exit the TUI',
|
|
3622
3892
|
'/<skill> [args] run a compiled skill directly',
|
|
3893
|
+
'/<plan> [args] run plans/<plan>.dml when no compiled skill matches',
|
|
3623
3894
|
'!<command> run a shell command directly in the workspace shell',
|
|
3895
|
+
'!!<command> run a shell command and add its result to the session transcript',
|
|
3624
3896
|
'keys',
|
|
3625
3897
|
'F10 / Ctrl+G open the menu bar',
|
|
3626
3898
|
'Ctrl+P open the action palette',
|