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.
- package/README.md +0 -1
- package/dist/configuration/io.js +22 -1
- package/dist/configuration/types.js +3 -4
- package/dist/execution/handlers.js +20 -29
- package/dist/execution/processing.js +12 -1
- package/dist/execution/reducer.js +17 -37
- package/dist/execution/utils.js +6 -0
- package/dist/services/components.js +1 -2
- package/dist/services/filesystem.js +21 -1
- package/dist/services/messages.js +10 -16
- package/dist/services/process.js +7 -2
- package/dist/services/router.js +87 -57
- package/dist/services/shell.js +179 -10
- package/dist/services/skills.js +2 -1
- package/dist/skills/answer.md +14 -12
- package/dist/skills/execute.md +70 -29
- package/dist/skills/introspect.md +9 -9
- package/dist/skills/schedule.md +0 -6
- package/dist/types/errors.js +47 -0
- package/dist/types/result.js +40 -0
- package/dist/ui/Component.js +2 -2
- package/dist/ui/Execute.js +135 -112
- package/dist/ui/Output.js +54 -0
- package/dist/ui/Task.js +99 -10
- package/package.json +1 -1
package/dist/services/router.js
CHANGED
|
@@ -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
|
|
34
|
-
workflowHandlers.addToQueue(
|
|
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
|
|
59
|
+
// User confirmed - complete both Confirm and Schedule, then route
|
|
53
60
|
lifecycleHandlers.completeActiveAndPending();
|
|
54
|
-
executeTasksAfterConfirm(validTasks,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
203
|
-
|
|
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
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
213
|
-
|
|
240
|
+
if (Object.keys(labels).length > 0) {
|
|
241
|
+
saveConfigLabels(labels);
|
|
214
242
|
}
|
|
215
|
-
|
|
216
|
-
//
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
-
|
|
231
|
-
|
|
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
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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
|
}
|
package/dist/services/shell.js
CHANGED
|
@@ -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
|
-
*
|
|
63
|
-
*
|
|
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
|
-
|
|
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
|
*/
|
package/dist/services/skills.js
CHANGED
|
@@ -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
|
|
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);
|
package/dist/skills/answer.md
CHANGED
|
@@ -110,15 +110,17 @@ They enable cleaner, more reusable component logic.
|
|
|
110
110
|
|
|
111
111
|
## Common Mistakes to Avoid
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
package/dist/skills/execute.md
CHANGED
|
@@ -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
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
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
|
-
|
|
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**
|
|
85
|
-
2. **Configure**
|
|
86
|
-
3. **Answer**
|
|
87
|
-
4. **Execute**
|
|
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**
|
|
94
|
-
6. **Validate**
|
|
95
|
-
7. **Report**
|
|
93
|
+
5. **Schedule**
|
|
94
|
+
6. **Validate**
|
|
95
|
+
7. **Report**
|
|
96
96
|
|
|
97
|
-
###
|
|
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:
|
package/dist/skills/schedule.md
CHANGED
|
@@ -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"
|