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.
Files changed (66) hide show
  1. package/README.md +10 -2
  2. package/dist/agent.d.ts +1 -0
  3. package/dist/agent.d.ts.map +1 -1
  4. package/dist/agent.js +252 -67
  5. package/dist/agent.js.map +1 -1
  6. package/dist/cli/commands.d.ts.map +1 -1
  7. package/dist/cli/commands.js +64 -7
  8. package/dist/cli/commands.js.map +1 -1
  9. package/dist/cli/config.d.ts +15 -12
  10. package/dist/cli/config.d.ts.map +1 -1
  11. package/dist/cli/config.js +38 -5
  12. package/dist/cli/config.js.map +1 -1
  13. package/dist/cli/default-skills.d.ts +1 -0
  14. package/dist/cli/default-skills.d.ts.map +1 -1
  15. package/dist/cli/default-skills.js +31 -0
  16. package/dist/cli/default-skills.js.map +1 -1
  17. package/dist/cli/tui.d.ts +16 -1
  18. package/dist/cli/tui.d.ts.map +1 -1
  19. package/dist/cli/tui.js +293 -21
  20. package/dist/cli/tui.js.map +1 -1
  21. package/dist/prolog/bridge.d.ts +15 -0
  22. package/dist/prolog/bridge.d.ts.map +1 -1
  23. package/dist/prolog/bridge.js +68 -0
  24. package/dist/prolog/bridge.js.map +1 -1
  25. package/dist/prolog-src/deepclause_mi.pl +135 -7
  26. package/dist/runner.d.ts +8 -0
  27. package/dist/runner.d.ts.map +1 -1
  28. package/dist/runner.js +79 -0
  29. package/dist/runner.js.map +1 -1
  30. package/dist/system/assets/docs/DML_COMPILER_PROMPT.md +7 -3
  31. package/dist/system/assets/docs/DML_REFERENCE.md +656 -0
  32. package/dist/system/assets/docs/TASK_PROMPT.md +33 -0
  33. package/dist/system/assets/docs/TUI.md +11 -0
  34. package/dist/system/assets/index.d.ts +12 -9
  35. package/dist/system/assets/index.d.ts.map +1 -1
  36. package/dist/system/assets/index.js +38 -9
  37. package/dist/system/assets/index.js.map +1 -1
  38. package/dist/system/assets/recipes/deepclause-coding-workflow/SKILL.md +56 -1
  39. package/dist/system/assets/skills/plan.dml +259 -0
  40. package/dist/system/runtime/catalog-skills.d.ts +1 -1
  41. package/dist/system/runtime/catalog-skills.d.ts.map +1 -1
  42. package/dist/system/runtime/catalog-skills.js +19 -5
  43. package/dist/system/runtime/catalog-skills.js.map +1 -1
  44. package/dist/system/runtime/conductor.d.ts +5 -1
  45. package/dist/system/runtime/conductor.d.ts.map +1 -1
  46. package/dist/system/runtime/conductor.js +22 -0
  47. package/dist/system/runtime/conductor.js.map +1 -1
  48. package/dist/system/runtime/dml-executor.d.ts +1 -1
  49. package/dist/system/runtime/dml-executor.d.ts.map +1 -1
  50. package/dist/system/runtime/dml-executor.js +2 -14
  51. package/dist/system/runtime/dml-executor.js.map +1 -1
  52. package/dist/system/runtime/dml-params.d.ts +2 -2
  53. package/dist/system/runtime/dml-params.d.ts.map +1 -1
  54. package/dist/system/runtime/dml-params.js +3 -0
  55. package/dist/system/runtime/dml-params.js.map +1 -1
  56. package/dist/system/runtime/runtime-tools.d.ts +1 -0
  57. package/dist/system/runtime/runtime-tools.d.ts.map +1 -1
  58. package/dist/system/runtime/runtime-tools.js +95 -0
  59. package/dist/system/runtime/runtime-tools.js.map +1 -1
  60. package/dist/system/runtime/shell-manager.js +1 -1
  61. package/dist/system/runtime/shell-manager.js.map +1 -1
  62. package/dist/system/runtime/skill-creator.d.ts.map +1 -1
  63. package/dist/system/runtime/skill-creator.js +52 -21
  64. package/dist/system/runtime/skill-creator.js.map +1 -1
  65. package/package.json +4 -3
  66. 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 runSkillCommand(this.workspaceRoot, skillName, args, {
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: `!${trimmedCommand}`,
1107
+ content: `${persistOutput ? '!!' : '!'}${trimmedCommand}`,
1042
1108
  }, 'shell');
1043
- activity.pushLine(`shell !${trimmedCommand}`);
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 !${trimmedCommand}...`;
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
- this.finishExecutionPreview(result.success
1061
- ? { persist: true, finalText: formatShellCommandSummary(result) }
1062
- : { persist: true, error: formatShellCommandSummary(result) });
1063
- this.statusLine = result.success ? 'Shell command finished.' : 'Shell command failed.';
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) ? 'Shell command cancelled.' : 'Shell command failed.';
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 runSkillCommand(workspaceRoot, parsed.name, parsed.args, {
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: `/${skillName}${args.length > 0 ? ` ${args.join(' ')}` : ''}`,
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(command.path, args, {
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: skillName, event }),
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',