prompt-language-shell 0.8.8 → 0.9.0

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.
@@ -30,11 +30,18 @@ export function routeTasksWithConfirm(tasks, message, service, userRequest, life
30
30
  const validTasks = tasks.filter((task) => task.type !== TaskType.Ignore && task.type !== TaskType.Discard);
31
31
  // Check if no valid tasks remain after filtering
32
32
  if (validTasks.length === 0) {
33
- const message = createMessage(getUnknownRequestMessage());
34
- workflowHandlers.addToQueue(message);
33
+ const msg = createMessage(getUnknownRequestMessage());
34
+ workflowHandlers.addToQueue(msg);
35
35
  return;
36
36
  }
37
37
  const operation = getOperationName(validTasks);
38
+ // Create routing context for downstream functions
39
+ const context = {
40
+ service,
41
+ userRequest,
42
+ workflowHandlers,
43
+ requestHandlers: requestHandlers,
44
+ };
38
45
  if (hasDefineTask) {
39
46
  // Has DEFINE tasks - add Schedule to queue for user selection
40
47
  // Refinement flow will call this function again with refined tasks
@@ -49,9 +56,9 @@ export function routeTasksWithConfirm(tasks, message, service, userRequest, life
49
56
  const scheduleDefinition = createScheduleDefinition(message, validTasks, () => {
50
57
  // Schedule completed - add Confirm to queue
51
58
  const confirmDefinition = createConfirmDefinition(() => {
52
- // User confirmed - complete both Confirm and Schedule, then route to appropriate component
59
+ // User confirmed - complete both Confirm and Schedule, then route
53
60
  lifecycleHandlers.completeActiveAndPending();
54
- executeTasksAfterConfirm(validTasks, service, userRequest, workflowHandlers, requestHandlers);
61
+ executeTasksAfterConfirm(validTasks, context);
55
62
  }, () => {
56
63
  // User cancelled - complete both Confirm and Schedule, then show cancellation
57
64
  lifecycleHandlers.completeActiveAndPending();
@@ -91,7 +98,8 @@ function validateTaskTypes(tasks) {
91
98
  * Validates task types and routes each type appropriately
92
99
  * Supports mixed types at top level with Groups
93
100
  */
94
- function executeTasksAfterConfirm(tasks, service, userRequest, workflowHandlers, requestHandlers) {
101
+ function executeTasksAfterConfirm(tasks, context) {
102
+ const { service, userRequest, workflowHandlers, requestHandlers } = context;
95
103
  // Validate task types (Groups must have uniform subtasks)
96
104
  try {
97
105
  validateTaskTypes(tasks);
@@ -135,7 +143,7 @@ function executeTasksAfterConfirm(tasks, service, userRequest, workflowHandlers,
135
143
  requestHandlers.onError(error);
136
144
  }, () => {
137
145
  // After config is complete, resume task routing
138
- routeTasksAfterConfig(scheduledTasks, service, userRequest, workflowHandlers, requestHandlers);
146
+ routeTasksAfterConfig(scheduledTasks, context);
139
147
  }, (operation) => {
140
148
  requestHandlers.onAborted(operation);
141
149
  }));
@@ -148,13 +156,13 @@ function executeTasksAfterConfirm(tasks, service, userRequest, workflowHandlers,
148
156
  }
149
157
  }
150
158
  // No missing config - proceed with normal routing
151
- routeTasksAfterConfig(scheduledTasks, service, userRequest, workflowHandlers, requestHandlers);
159
+ routeTasksAfterConfig(scheduledTasks, context);
152
160
  }
153
161
  /**
154
162
  * Route tasks after config is complete (or when no config is needed)
155
163
  * Processes tasks in order, grouping by type
156
164
  */
157
- function routeTasksAfterConfig(scheduledTasks, service, userRequest, workflowHandlers, requestHandlers) {
165
+ function routeTasksAfterConfig(scheduledTasks, context) {
158
166
  // Process tasks in order, preserving Group boundaries
159
167
  // Track consecutive standalone tasks to group them by type
160
168
  let consecutiveStandaloneTasks = [];
@@ -174,7 +182,7 @@ function routeTasksAfterConfig(scheduledTasks, service, userRequest, workflowHan
174
182
  const taskType = type;
175
183
  if (typeTasks.length === 0)
176
184
  continue;
177
- routeTasksByType(taskType, typeTasks, service, userRequest, workflowHandlers, requestHandlers);
185
+ routeTasksByType(taskType, typeTasks, context);
178
186
  }
179
187
  consecutiveStandaloneTasks = [];
180
188
  };
@@ -187,7 +195,7 @@ function routeTasksAfterConfig(scheduledTasks, service, userRequest, workflowHan
187
195
  if (task.subtasks.length > 0) {
188
196
  const subtasks = task.subtasks;
189
197
  const taskType = subtasks[0].type;
190
- routeTasksByType(taskType, subtasks, service, userRequest, workflowHandlers, requestHandlers);
198
+ routeTasksByType(taskType, subtasks, context);
191
199
  }
192
200
  }
193
201
  else {
@@ -199,59 +207,81 @@ function routeTasksAfterConfig(scheduledTasks, service, userRequest, workflowHan
199
207
  processStandaloneTasks();
200
208
  }
201
209
  /**
202
- * Route tasks by type to appropriate components
203
- * Extracted to allow reuse for both Groups and standalone tasks
210
+ * Route Answer tasks - creates separate Answer component for each question
211
+ */
212
+ function routeAnswerTasks(tasks, context) {
213
+ for (const task of tasks) {
214
+ context.workflowHandlers.addToQueue(createAnswerDefinition(task.action, context.service));
215
+ }
216
+ }
217
+ /**
218
+ * Route Introspect tasks - creates single Introspect component for all tasks
204
219
  */
205
- function routeTasksByType(taskType, typeTasks, service, userRequest, workflowHandlers, requestHandlers) {
206
- if (taskType === TaskType.Answer) {
207
- // Create separate Answer component for each question
208
- for (const task of typeTasks) {
209
- workflowHandlers.addToQueue(createAnswerDefinition(task.action, service));
220
+ function routeIntrospectTasks(tasks, context) {
221
+ context.workflowHandlers.addToQueue(createIntrospectDefinition(tasks, context.service));
222
+ }
223
+ /**
224
+ * Route Config tasks - extracts keys, caches labels, creates Config component
225
+ */
226
+ function routeConfigTasks(tasks, context) {
227
+ const configKeys = tasks
228
+ .map((task) => task.params?.key)
229
+ .filter((key) => key !== undefined);
230
+ // Extract and cache labels from task descriptions
231
+ // Only cache labels for dynamically discovered keys (not in schema)
232
+ const schema = getConfigSchema();
233
+ const labels = {};
234
+ for (const task of tasks) {
235
+ const key = task.params?.key;
236
+ if (key && task.action && !(key in schema)) {
237
+ labels[key] = task.action;
210
238
  }
211
239
  }
212
- else if (taskType === TaskType.Introspect) {
213
- workflowHandlers.addToQueue(createIntrospectDefinition(typeTasks, service));
240
+ if (Object.keys(labels).length > 0) {
241
+ saveConfigLabels(labels);
214
242
  }
215
- else if (taskType === TaskType.Config) {
216
- // Route to Config flow - extract keys and descriptions from task params
217
- const configKeys = typeTasks
218
- .map((task) => task.params?.key)
219
- .filter((key) => key !== undefined);
220
- // Extract and cache labels from task descriptions
221
- // Only cache labels for dynamically discovered keys (not in schema)
222
- const schema = getConfigSchema();
223
- const labels = {};
224
- for (const task of typeTasks) {
225
- const key = task.params?.key;
226
- if (key && task.action && !(key in schema)) {
227
- labels[key] = task.action;
243
+ context.workflowHandlers.addToQueue(createConfigDefinitionWithKeys(configKeys, (config) => {
244
+ // Save config - Config component will handle completion and feedback
245
+ try {
246
+ // Convert flat dotted keys to nested structure grouped by section
247
+ const configBySection = unflattenConfig(config);
248
+ // Save each section
249
+ for (const [section, sectionConfig] of Object.entries(configBySection)) {
250
+ saveConfig(section, sectionConfig);
228
251
  }
229
252
  }
230
- if (Object.keys(labels).length > 0) {
231
- saveConfigLabels(labels);
253
+ catch (error) {
254
+ const errorMessage = error instanceof Error
255
+ ? error.message
256
+ : 'Failed to save configuration';
257
+ throw new Error(errorMessage);
232
258
  }
233
- workflowHandlers.addToQueue(createConfigDefinitionWithKeys(configKeys, (config) => {
234
- // Save config - Config component will handle completion and feedback
235
- try {
236
- // Convert flat dotted keys to nested structure grouped by section
237
- const configBySection = unflattenConfig(config);
238
- // Save each section
239
- for (const [section, sectionConfig] of Object.entries(configBySection)) {
240
- saveConfig(section, sectionConfig);
241
- }
242
- }
243
- catch (error) {
244
- const errorMessage = error instanceof Error
245
- ? error.message
246
- : 'Failed to save configuration';
247
- throw new Error(errorMessage);
248
- }
249
- }, (operation) => {
250
- requestHandlers.onAborted(operation);
251
- }));
252
- }
253
- else if (taskType === TaskType.Execute) {
254
- // Execute tasks (validation already happened upfront in executeTasksAfterConfirm)
255
- workflowHandlers.addToQueue(createExecuteDefinition(typeTasks, service));
259
+ }, (operation) => {
260
+ context.requestHandlers.onAborted(operation);
261
+ }));
262
+ }
263
+ /**
264
+ * Route Execute tasks - creates Execute component (validation already done)
265
+ */
266
+ function routeExecuteTasks(tasks, context) {
267
+ context.workflowHandlers.addToQueue(createExecuteDefinition(tasks, context.service));
268
+ }
269
+ /**
270
+ * Registry mapping task types to their route handlers
271
+ */
272
+ const taskRouteHandlers = {
273
+ [TaskType.Answer]: routeAnswerTasks,
274
+ [TaskType.Introspect]: routeIntrospectTasks,
275
+ [TaskType.Config]: routeConfigTasks,
276
+ [TaskType.Execute]: routeExecuteTasks,
277
+ };
278
+ /**
279
+ * Route tasks by type to appropriate components
280
+ * Uses registry pattern for extensibility
281
+ */
282
+ function routeTasksByType(taskType, tasks, context) {
283
+ const handler = taskRouteHandlers[taskType];
284
+ if (handler) {
285
+ handler(tasks, context);
256
286
  }
257
287
  }
@@ -1,3 +1,4 @@
1
+ import { spawn } from 'child_process';
1
2
  export var ExecutionStatus;
2
3
  (function (ExecutionStatus) {
3
4
  ExecutionStatus["Pending"] = "pending";
@@ -58,18 +59,186 @@ export class DummyExecutor {
58
59
  });
59
60
  }
60
61
  }
62
+ // Marker for extracting pwd from command output
63
+ const PWD_MARKER = '__PWD_MARKER_7x9k2m__';
61
64
  /**
62
- * Default executor uses DummyExecutor for development and testing.
63
- * To implement real shell execution, create a RealExecutor class that:
64
- * - Spawns process with cmd.command in shell mode using child_process.spawn()
65
- * - Sets working directory from cmd.workdir
66
- * - Handles cmd.timeout for command timeout
67
- * - Captures stdout and stderr streams
68
- * - Calls onProgress with Running/Success/Failed status
69
- * - Returns CommandOutput with actual stdout, stderr, exitCode
70
- * - Handles errors (spawn failures, timeouts, non-zero exit codes)
65
+ * Parse stdout to extract workdir and clean output.
66
+ * Returns the cleaned output and the extracted workdir.
71
67
  */
72
- const executor = new DummyExecutor();
68
+ function parseWorkdir(rawOutput) {
69
+ const markerIndex = rawOutput.lastIndexOf(PWD_MARKER);
70
+ if (markerIndex === -1) {
71
+ return { output: rawOutput };
72
+ }
73
+ const output = rawOutput.slice(0, markerIndex).trimEnd();
74
+ const pwdPart = rawOutput.slice(markerIndex + PWD_MARKER.length).trim();
75
+ const lines = pwdPart.split('\n').filter((l) => l.trim());
76
+ const workdir = lines[0];
77
+ return { output, workdir };
78
+ }
79
+ /**
80
+ * Manages streaming output while filtering out the PWD marker.
81
+ * Buffers output to avoid emitting partial markers to the callback.
82
+ */
83
+ class OutputStreamer {
84
+ chunks = [];
85
+ emittedLength = 0;
86
+ callback;
87
+ constructor(callback) {
88
+ this.callback = callback;
89
+ }
90
+ /**
91
+ * Add new stdout data and emit safe content to callback.
92
+ * Buffers data to avoid emitting partial PWD markers.
93
+ */
94
+ pushStdout(data) {
95
+ this.chunks.push(data);
96
+ if (!this.callback)
97
+ return;
98
+ const accumulated = this.chunks.join('');
99
+ const markerIndex = accumulated.indexOf(PWD_MARKER);
100
+ if (markerIndex !== -1) {
101
+ // Marker found - emit everything before it (trimmed)
102
+ this.emitUpTo(accumulated.slice(0, markerIndex).trimEnd().length);
103
+ }
104
+ else {
105
+ // No marker yet - emit all but buffer for potential partial marker
106
+ const bufferSize = PWD_MARKER.length + 5;
107
+ const safeLength = Math.max(this.emittedLength, accumulated.length - bufferSize);
108
+ this.emitUpTo(safeLength);
109
+ }
110
+ }
111
+ /**
112
+ * Emit content up to the specified length if there's new content.
113
+ */
114
+ emitUpTo(length) {
115
+ if (length > this.emittedLength && this.callback) {
116
+ const accumulated = this.chunks.join('');
117
+ const newContent = accumulated.slice(this.emittedLength, length);
118
+ this.callback(newContent, 'stdout');
119
+ this.emittedLength = length;
120
+ }
121
+ }
122
+ /**
123
+ * Get the accumulated raw output.
124
+ */
125
+ getAccumulated() {
126
+ return this.chunks.join('');
127
+ }
128
+ }
129
+ /**
130
+ * Real executor that spawns shell processes and captures output.
131
+ */
132
+ export class RealExecutor {
133
+ outputCallback;
134
+ constructor(outputCallback) {
135
+ this.outputCallback = outputCallback;
136
+ }
137
+ /**
138
+ * Set or update the output callback
139
+ */
140
+ setOutputCallback(callback) {
141
+ this.outputCallback = callback;
142
+ }
143
+ execute(cmd, onProgress, _ = 0) {
144
+ return new Promise((resolve) => {
145
+ onProgress?.(ExecutionStatus.Running);
146
+ const stderr = [];
147
+ // Wrap command to capture final working directory
148
+ const wrappedCommand = `${cmd.command}; __exit=$?; echo ""; echo "${PWD_MARKER}"; pwd; exit $__exit`;
149
+ // Wrap spawn in try/catch to handle synchronous errors
150
+ let child;
151
+ try {
152
+ child = spawn(wrappedCommand, {
153
+ shell: true,
154
+ cwd: cmd.workdir || process.cwd(),
155
+ });
156
+ }
157
+ catch (error) {
158
+ const errorMessage = error instanceof Error ? error.message : 'Failed to spawn process';
159
+ const commandResult = {
160
+ description: cmd.description,
161
+ command: cmd.command,
162
+ output: '',
163
+ errors: errorMessage,
164
+ result: ExecutionResult.Error,
165
+ error: errorMessage,
166
+ };
167
+ onProgress?.(ExecutionStatus.Failed);
168
+ resolve(commandResult);
169
+ return;
170
+ }
171
+ // Handle timeout if specified
172
+ const SIGKILL_GRACE_PERIOD = 3000;
173
+ let timeoutId;
174
+ let killTimeoutId;
175
+ if (cmd.timeout && cmd.timeout > 0) {
176
+ timeoutId = setTimeout(() => {
177
+ child.kill('SIGTERM');
178
+ // Escalate to SIGKILL if process doesn't terminate
179
+ killTimeoutId = setTimeout(() => {
180
+ child.kill('SIGKILL');
181
+ }, SIGKILL_GRACE_PERIOD);
182
+ }, cmd.timeout);
183
+ }
184
+ // Use OutputStreamer for buffered stdout streaming
185
+ const stdoutStreamer = new OutputStreamer(this.outputCallback);
186
+ child.stdout.on('data', (data) => {
187
+ stdoutStreamer.pushStdout(data.toString());
188
+ });
189
+ child.stderr.on('data', (data) => {
190
+ const text = data.toString();
191
+ stderr.push(text);
192
+ this.outputCallback?.(text, 'stderr');
193
+ });
194
+ child.on('error', (error) => {
195
+ if (timeoutId)
196
+ clearTimeout(timeoutId);
197
+ if (killTimeoutId)
198
+ clearTimeout(killTimeoutId);
199
+ const commandResult = {
200
+ description: cmd.description,
201
+ command: cmd.command,
202
+ output: stdoutStreamer.getAccumulated(),
203
+ errors: error.message,
204
+ result: ExecutionResult.Error,
205
+ error: error.message,
206
+ };
207
+ onProgress?.(ExecutionStatus.Failed);
208
+ resolve(commandResult);
209
+ });
210
+ child.on('close', (code) => {
211
+ if (timeoutId)
212
+ clearTimeout(timeoutId);
213
+ if (killTimeoutId)
214
+ clearTimeout(killTimeoutId);
215
+ const success = code === 0;
216
+ const { output, workdir } = parseWorkdir(stdoutStreamer.getAccumulated());
217
+ const commandResult = {
218
+ description: cmd.description,
219
+ command: cmd.command,
220
+ output,
221
+ errors: stderr.join(''),
222
+ result: success ? ExecutionResult.Success : ExecutionResult.Error,
223
+ error: success ? undefined : `Exit code: ${code}`,
224
+ workdir,
225
+ };
226
+ onProgress?.(success ? ExecutionStatus.Success : ExecutionStatus.Failed);
227
+ resolve(commandResult);
228
+ });
229
+ });
230
+ }
231
+ }
232
+ // Create real executor instance
233
+ const realExecutor = new RealExecutor();
234
+ // Default executor for production use
235
+ const executor = realExecutor;
236
+ /**
237
+ * Set a callback to receive command output in real-time
238
+ */
239
+ export function setOutputCallback(callback) {
240
+ realExecutor.setOutputCallback(callback);
241
+ }
73
242
  /**
74
243
  * Execute a single shell command
75
244
  */
@@ -1,5 +1,6 @@
1
1
  import { homedir } from 'os';
2
2
  import { join } from 'path';
3
+ import { AppError, ErrorCode } from '../types/errors.js';
3
4
  import { defaultFileSystem } from './filesystem.js';
4
5
  import { displayWarning } from './logger.js';
5
6
  import { getUnknownSkillMessage } from './messages.js';
@@ -189,7 +190,7 @@ export function expandSkillReferences(execution, skillLookup, visited = new Set(
189
190
  }
190
191
  // Check for circular reference
191
192
  if (visited.has(skillName)) {
192
- throw new Error(`Circular skill reference detected: ${Array.from(visited).join(' → ')} → ${skillName}`);
193
+ throw new AppError(`Circular skill reference detected: ${Array.from(visited).join(' → ')} → ${skillName}`, ErrorCode.CircularReference);
193
194
  }
194
195
  // Second: Match against skill name
195
196
  const skill = skillLookup(skillName);
@@ -110,15 +110,17 @@ They enable cleaner, more reusable component logic.
110
110
 
111
111
  ## Common Mistakes to Avoid
112
112
 
113
- Starting with "Here's the answer:" or "Let me explain:"
114
- Exceeding 4 lines or 80 characters per line
115
- Including unnecessary details
116
- Using overly technical jargon without explanation
117
- Repeating the question in the answer
118
- Using citation tags like `<cite>` or any HTML/XML markup
119
-
120
- ✅ Direct, concise answers
121
- ✅ Proper line breaks at natural phrase boundaries
122
- Essential information only
123
- Clear, accessible language
124
- Plain text only - no markup tags
113
+ **DO NOT:**
114
+ - Start with "Here's the answer:" or "Let me explain:"
115
+ - Exceed 4 lines or 80 characters per line
116
+ - Include unnecessary details
117
+ - Use overly technical jargon without explanation
118
+ - Repeat the question in the answer
119
+ - Use citation tags like `<cite>` or any HTML/XML markup
120
+
121
+ **DO:**
122
+ - Give direct, concise answers
123
+ - Use proper line breaks at natural phrase boundaries
124
+ - Include essential information only
125
+ - Use clear, accessible language
126
+ - Use plain text only - no markup tags
@@ -133,9 +133,26 @@ Given tasks from this skill:
133
133
  Do NOT invent different commands - use exactly what the skill specifies,
134
134
  with parameter placeholders replaced by actual values.
135
135
 
136
- **CRITICAL**: Take the exact command from the ### Execution section. Do
137
- not modify, improve, or rewrite the command in any way. The user wrote
138
- these commands specifically for their environment and workflow.
136
+ **CRITICAL - VERBATIM EXECUTION**: Run shell commands EXACTLY as written in
137
+ the ### Execution section. Do NOT:
138
+ - Modify the command string in any way
139
+ - Optimize or improve the command
140
+ - Add flags or options
141
+ - Change paths or filenames
142
+ - Rewrite using different syntax
143
+ - "Fix" perceived issues in the command
144
+ - Expand aliases or shortcuts
145
+ - Strip or modify escape characters (backslashes, quotes)
146
+ - Convert `\"` to `"` or `\'` to `'`
147
+ - Remove or change any escaping sequences
148
+
149
+ The ONLY allowed change is replacing `{placeholder}` tokens with their
150
+ resolved values. Everything else must remain character-for-character
151
+ identical to what the user wrote in their skill definition.
152
+
153
+ **PRESERVE ALL CHARACTERS**: If the skill has `x=\"y\"`, output `x=\"y\"`.
154
+ If it has `path/to/file\ with\ spaces`, keep it exactly as written.
155
+ Escape sequences are intentional - do not "clean" or "simplify" them.
139
156
 
140
157
  ## Response Format
141
158
 
@@ -350,34 +367,58 @@ For complex multi-step operations:
350
367
  4. **Error handling**: For non-critical cleanup steps, set critical:
351
368
  false
352
369
 
370
+ ## Handling Config Placeholders
371
+
372
+ When substituting parameter placeholders in skill commands:
373
+
374
+ 1. **Known values**: Replace `{PARAM}` with the actual value from task params
375
+ 2. **Unknown values**: If a placeholder value is not available in task params,
376
+ **keep the original `{placeholder}` syntax** in the command. Do NOT replace
377
+ it with `<UNKNOWN>` or any other marker.
378
+
379
+ **CRITICAL**: Never use `<UNKNOWN>`, `<MISSING>`, `<undefined>`, or similar
380
+ markers in commands. The `<` and `>` characters break shell syntax. Always
381
+ preserve the original `{placeholder}` format for unresolved values - this
382
+ allows the system to detect and prompt for missing configuration.
383
+
384
+ Example:
385
+ - Command template: `process.py --output {settings.output}`
386
+ - If `settings.output` is NOT in task params:
387
+ - WRONG: `process.py --output <UNKNOWN>`
388
+ - CORRECT: `process.py --output {settings.output}`
389
+
353
390
  ## Common Mistakes to Avoid
354
391
 
355
- Generating commands that don't match the task description
356
- Using platform-specific commands without consideration
357
- Forgetting to quote paths with spaces
358
- Setting unrealistic timeouts for long operations
359
- Running destructive commands without safeguards
360
- Ignoring task parameters when generating commands
361
- **CRITICAL: Inventing commands instead of using skill's Execution
362
- section**
363
- **CRITICAL: Ignoring params.skill and making up your own commands**
364
- **CRITICAL: Generating commands when the skill doesn't exist in
365
- Available Skills**
366
- Not substituting parameter placeholders in skill commands
367
- **CRITICAL: Assuming what commands to run when skill is missing**
368
-
369
- Match commands precisely to task descriptions
370
- Use task params to fill in specific values
371
- ✅ Quote all file paths properly
372
- ✅ Set appropriate timeouts for each operation type
373
- Include safety checks for destructive operations
374
- Generate portable commands when possible
375
- **CRITICAL: Verify skill exists in Available Skills before generating
376
- commands**
377
- **CRITICAL: Return error response if skill not found, never invent
378
- commands**
379
- Always use skill's Execution section when params.skill is present
380
- ✅ Replace all {PARAM} placeholders with values from task params
392
+ **DO NOT:**
393
+ - Generate commands that don't match the task description
394
+ - Use platform-specific commands without consideration
395
+ - Forget to quote paths with spaces
396
+ - Set unrealistic timeouts for long operations
397
+ - Run destructive commands without safeguards
398
+ - Ignore task parameters when generating commands
399
+ - **CRITICAL: Invent commands instead of using skill's Execution
400
+ section**
401
+ - **CRITICAL: Ignore params.skill and make up your own commands**
402
+ - **CRITICAL: Generate commands when the skill doesn't exist in
403
+ Available Skills**
404
+ - Fail to substitute parameter placeholders in skill commands
405
+ - **CRITICAL: Assume what commands to run when skill is missing**
406
+ - **CRITICAL: Replace unknown placeholders with `<UNKNOWN>` - this breaks
407
+ shell syntax**
408
+
409
+ **DO:**
410
+ - Match commands precisely to task descriptions
411
+ - Use task params to fill in specific values
412
+ - Quote all file paths properly
413
+ - Set appropriate timeouts for each operation type
414
+ - Include safety checks for destructive operations
415
+ - Generate portable commands when possible
416
+ - **CRITICAL: Verify skill exists in Available Skills before generating
417
+ commands**
418
+ - **CRITICAL: Return error response if skill not found, never invent
419
+ commands**
420
+ - Always use skill's Execution section when params.skill is present
421
+ - Replace all {PARAM} placeholders with values from task params
381
422
 
382
423
  ## Final Validation
383
424
 
@@ -64,7 +64,7 @@ capability list.
64
64
 
65
65
  ## Capabilities Structure
66
66
 
67
- **⚠️ CRITICAL ORDERING REQUIREMENT ⚠️**
67
+ **CRITICAL ORDERING REQUIREMENT**
68
68
 
69
69
  You MUST present capabilities in the EXACT order specified below. This is
70
70
  NON-NEGOTIABLE and applies to EVERY response.
@@ -81,20 +81,20 @@ NON-NEGOTIABLE and applies to EVERY response.
81
81
 
82
82
  These MUST appear FIRST, in this EXACT sequence:
83
83
 
84
- 1. **Introspect** ← ALWAYS FIRST
85
- 2. **Configure** ← ALWAYS SECOND
86
- 3. **Answer** ← ALWAYS THIRD
87
- 4. **Execute** ← ALWAYS FOURTH
84
+ 1. **Introspect**
85
+ 2. **Configure**
86
+ 3. **Answer**
87
+ 4. **Execute**
88
88
 
89
89
  ### Position 5-7: meta workflow capabilities (origin: "meta")
90
90
 
91
91
  These MUST appear AFTER Execute and BEFORE user-provided skills:
92
92
 
93
- 5. **Schedule** ← NEVER FIRST, ALWAYS position 5 (after Execute)
94
- 6. **Validate** ← ALWAYS position 6 (after Schedule)
95
- 7. **Report** ← NEVER FIRST, ALWAYS position 7 (after Validate)
93
+ 5. **Schedule**
94
+ 6. **Validate**
95
+ 7. **Report**
96
96
 
97
- ### 3. user-provided skills (origin: "user")
97
+ ### Position 8+: user-provided skills (origin: "user")
98
98
 
99
99
  If skills are provided in the "Available Skills" section below, include
100
100
  them in the response. For each skill:
@@ -325,12 +325,6 @@ even if they use the same action verb.
325
325
  - Task 2: "Process data" (skill-based with subtasks)
326
326
  - Task 3: "Explain Kubernetes" (type: answer)
327
327
 
328
- - "explain tdd, process files, explain tbd" → THREE separate task
329
- groups:
330
- - Task 1: "Explain Test-Driven Development" (type: answer)
331
- - Task 2: "Process files" (skill-based with subtasks)
332
- - Task 3: "Explain TBD" (type: answer)
333
-
334
328
  - "process files and validate" where only "process" has a skill →
335
329
  - Task 1: "Process files" (skill-based with subtasks)
336
330
  - Task 2: type "ignore" for unmatched "validate"