prompt-language-shell 0.9.0 → 0.9.4

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 (64) hide show
  1. package/dist/{ui/Main.js → Main.js} +24 -17
  2. package/dist/{ui → components}/Component.js +31 -26
  3. package/dist/{ui → components}/Workflow.js +23 -7
  4. package/dist/{ui → components/controllers}/Answer.js +18 -17
  5. package/dist/{ui → components/controllers}/Command.js +21 -24
  6. package/dist/{ui → components/controllers}/Config.js +17 -119
  7. package/dist/components/controllers/Confirm.js +42 -0
  8. package/dist/components/controllers/Execute.js +288 -0
  9. package/dist/{ui → components/controllers}/Introspect.js +22 -39
  10. package/dist/components/controllers/Refinement.js +18 -0
  11. package/dist/{ui → components/controllers}/Schedule.js +8 -124
  12. package/dist/{ui → components/controllers}/Validate.js +37 -50
  13. package/dist/components/views/Answer.js +28 -0
  14. package/dist/components/views/Command.js +11 -0
  15. package/dist/components/views/Config.js +115 -0
  16. package/dist/components/views/Confirm.js +24 -0
  17. package/dist/components/views/Execute.js +60 -0
  18. package/dist/{ui → components/views}/Feedback.js +3 -3
  19. package/dist/components/views/Introspect.js +17 -0
  20. package/dist/{ui → components/views}/Label.js +3 -3
  21. package/dist/{ui → components/views}/List.js +3 -3
  22. package/dist/{ui → components/views}/Output.js +2 -2
  23. package/dist/components/views/Refinement.js +9 -0
  24. package/dist/{ui → components/views}/Report.js +1 -1
  25. package/dist/components/views/Schedule.js +120 -0
  26. package/dist/{ui → components/views}/Separator.js +1 -1
  27. package/dist/{ui → components/views}/Spinner.js +1 -1
  28. package/dist/{ui → components/views}/Subtask.js +10 -7
  29. package/dist/components/views/Task.js +18 -0
  30. package/dist/components/views/Upcoming.js +30 -0
  31. package/dist/{ui → components/views}/UserQuery.js +1 -1
  32. package/dist/components/views/Validate.js +17 -0
  33. package/dist/{ui → components/views}/Welcome.js +1 -1
  34. package/dist/{services/config-labels.js → configuration/labels.js} +1 -1
  35. package/dist/configuration/schema.js +2 -2
  36. package/dist/configuration/steps.js +171 -0
  37. package/dist/configuration/transformation.js +17 -0
  38. package/dist/execution/handlers.js +20 -60
  39. package/dist/execution/processing.js +3 -1
  40. package/dist/execution/reducer.js +34 -44
  41. package/dist/execution/runner.js +99 -0
  42. package/dist/execution/types.js +4 -4
  43. package/dist/execution/utils.js +23 -1
  44. package/dist/index.js +1 -1
  45. package/dist/services/components.js +109 -394
  46. package/dist/services/logger.js +3 -3
  47. package/dist/services/messages.js +19 -0
  48. package/dist/services/refinement.js +5 -2
  49. package/dist/services/router.js +136 -55
  50. package/dist/services/shell.js +26 -6
  51. package/dist/services/timing.js +1 -0
  52. package/dist/skills/execute.md +40 -14
  53. package/dist/tools/execute.tool.js +0 -4
  54. package/dist/types/schemas.js +0 -1
  55. package/package.json +1 -1
  56. package/dist/parser.js +0 -13
  57. package/dist/services/config-utils.js +0 -20
  58. package/dist/ui/Confirm.js +0 -62
  59. package/dist/ui/Execute.js +0 -294
  60. package/dist/ui/Refinement.js +0 -23
  61. package/dist/ui/Task.js +0 -175
  62. /package/dist/{ui → components/views}/Debug.js +0 -0
  63. /package/dist/{ui → components/views}/Message.js +0 -0
  64. /package/dist/{ui → components/views}/Panel.js +0 -0
@@ -2,10 +2,11 @@ import { asScheduledTasks } from '../types/guards.js';
2
2
  import { FeedbackType, TaskType } from '../types/types.js';
3
3
  import { saveConfig } from '../configuration/io.js';
4
4
  import { getConfigSchema } from '../configuration/schema.js';
5
+ import { createConfigStepsFromSchema } from '../configuration/steps.js';
5
6
  import { unflattenConfig } from '../configuration/transformation.js';
6
- import { saveConfigLabels } from './config-labels.js';
7
- import { createAnswerDefinition, createConfigDefinitionWithKeys, createConfirmDefinition, createExecuteDefinition, createFeedback, createIntrospectDefinition, createMessage, createScheduleDefinition, createValidateDefinition, } from './components.js';
8
- import { getCancellationMessage, getMixedTaskTypesError, getUnknownRequestMessage, } from './messages.js';
7
+ import { saveConfigLabels } from '../configuration/labels.js';
8
+ import { createAnswer, createConfig, createConfirm, createExecute, createFeedback, createIntrospect, createMessage, createSchedule, createValidate, } from './components.js';
9
+ import { getCancellationMessage, getConfirmationMessage, getMixedTaskTypesError, getUnknownRequestMessage, } from './messages.js';
9
10
  import { validateExecuteTasks } from './validator.js';
10
11
  /**
11
12
  * Determine the operation name based on task types
@@ -30,7 +31,7 @@ export function routeTasksWithConfirm(tasks, message, service, userRequest, life
30
31
  const validTasks = tasks.filter((task) => task.type !== TaskType.Ignore && task.type !== TaskType.Discard);
31
32
  // Check if no valid tasks remain after filtering
32
33
  if (validTasks.length === 0) {
33
- const msg = createMessage(getUnknownRequestMessage());
34
+ const msg = createMessage({ text: getUnknownRequestMessage() });
34
35
  workflowHandlers.addToQueue(msg);
35
36
  return;
36
37
  }
@@ -45,7 +46,7 @@ export function routeTasksWithConfirm(tasks, message, service, userRequest, life
45
46
  if (hasDefineTask) {
46
47
  // Has DEFINE tasks - add Schedule to queue for user selection
47
48
  // Refinement flow will call this function again with refined tasks
48
- const scheduleDefinition = createScheduleDefinition(message, validTasks);
49
+ const scheduleDefinition = createSchedule({ message, tasks: validTasks });
49
50
  workflowHandlers.addToQueue(scheduleDefinition);
50
51
  }
51
52
  else {
@@ -53,19 +54,27 @@ export function routeTasksWithConfirm(tasks, message, service, userRequest, life
53
54
  // When Schedule activates, Command moves to timeline
54
55
  // When Schedule completes, it moves to pending
55
56
  // When Confirm activates, Schedule stays pending (visible for context)
56
- const scheduleDefinition = createScheduleDefinition(message, validTasks, () => {
57
- // Schedule completed - add Confirm to queue
58
- const confirmDefinition = createConfirmDefinition(() => {
59
- // User confirmed - complete both Confirm and Schedule, then route
60
- lifecycleHandlers.completeActiveAndPending();
61
- executeTasksAfterConfirm(validTasks, context);
62
- }, () => {
63
- // User cancelled - complete both Confirm and Schedule, then show cancellation
64
- lifecycleHandlers.completeActiveAndPending();
65
- const message = getCancellationMessage(operation);
66
- workflowHandlers.addToQueue(createFeedback(FeedbackType.Aborted, message));
67
- });
68
- workflowHandlers.addToQueue(confirmDefinition);
57
+ const scheduleDefinition = createSchedule({
58
+ message,
59
+ tasks: validTasks,
60
+ onSelectionConfirmed: () => {
61
+ // Schedule completed - add Confirm to queue
62
+ const confirmDefinition = createConfirm({
63
+ message: getConfirmationMessage(),
64
+ onConfirmed: () => {
65
+ // User confirmed - complete both Confirm and Schedule, then route
66
+ lifecycleHandlers.completeActiveAndPending();
67
+ executeTasksAfterConfirm(validTasks, context);
68
+ },
69
+ onCancelled: () => {
70
+ // User cancelled - complete both Confirm and Schedule, then show cancellation
71
+ lifecycleHandlers.completeActiveAndPending();
72
+ const message = getCancellationMessage(operation);
73
+ workflowHandlers.addToQueue(createFeedback({ type: FeedbackType.Aborted, message }));
74
+ },
75
+ });
76
+ workflowHandlers.addToQueue(confirmDefinition);
77
+ },
69
78
  });
70
79
  workflowHandlers.addToQueue(scheduleDefinition);
71
80
  }
@@ -134,18 +143,28 @@ function executeTasksAfterConfirm(tasks, context) {
134
143
  .join('\n');
135
144
  return `Invalid skill definition "${error.skill}":\n\n${issuesList}`;
136
145
  });
137
- workflowHandlers.addToQueue(createFeedback(FeedbackType.Failed, errorMessages.join('\n\n')));
146
+ workflowHandlers.addToQueue(createFeedback({
147
+ type: FeedbackType.Failed,
148
+ message: errorMessages.join('\n\n'),
149
+ }));
138
150
  return;
139
151
  }
140
152
  else if (validation.missingConfig.length > 0) {
141
153
  // Missing config detected - create ONE Validate component for ALL missing config
142
- workflowHandlers.addToQueue(createValidateDefinition(validation.missingConfig, userRequest, service, (error) => {
143
- requestHandlers.onError(error);
144
- }, () => {
145
- // After config is complete, resume task routing
146
- routeTasksAfterConfig(scheduledTasks, context);
147
- }, (operation) => {
148
- requestHandlers.onAborted(operation);
154
+ workflowHandlers.addToQueue(createValidate({
155
+ missingConfig: validation.missingConfig,
156
+ userRequest,
157
+ service,
158
+ onError: (error) => {
159
+ requestHandlers.onError(error);
160
+ },
161
+ onValidationComplete: () => {
162
+ // After config is complete, resume task routing
163
+ routeTasksAfterConfig(scheduledTasks, context);
164
+ },
165
+ onAborted: (operation) => {
166
+ requestHandlers.onAborted(operation);
167
+ },
149
168
  }));
150
169
  return;
151
170
  }
@@ -158,11 +177,37 @@ function executeTasksAfterConfirm(tasks, context) {
158
177
  // No missing config - proceed with normal routing
159
178
  routeTasksAfterConfig(scheduledTasks, context);
160
179
  }
180
+ /**
181
+ * Task types that should appear in the upcoming display
182
+ */
183
+ const UPCOMING_TASK_TYPES = [TaskType.Execute, TaskType.Answer];
184
+ /**
185
+ * Collect names of all upcoming execution units (groups and standalone tasks)
186
+ * for display during task execution
187
+ */
188
+ function collectUpcomingNames(scheduledTasks) {
189
+ const names = [];
190
+ for (const task of scheduledTasks) {
191
+ if (task.type === TaskType.Group && task.subtasks?.length) {
192
+ const subtasks = task.subtasks;
193
+ if (UPCOMING_TASK_TYPES.includes(subtasks[0].type)) {
194
+ names.push(task.action);
195
+ }
196
+ }
197
+ else if (UPCOMING_TASK_TYPES.includes(task.type)) {
198
+ names.push(task.action);
199
+ }
200
+ }
201
+ return names;
202
+ }
161
203
  /**
162
204
  * Route tasks after config is complete (or when no config is needed)
163
205
  * Processes tasks in order, grouping by type
164
206
  */
165
207
  function routeTasksAfterConfig(scheduledTasks, context) {
208
+ // Collect all unit names for upcoming display
209
+ const allUnitNames = collectUpcomingNames(scheduledTasks);
210
+ let currentUnitIndex = 0;
166
211
  // Process tasks in order, preserving Group boundaries
167
212
  // Track consecutive standalone tasks to group them by type
168
213
  let consecutiveStandaloneTasks = [];
@@ -182,7 +227,18 @@ function routeTasksAfterConfig(scheduledTasks, context) {
182
227
  const taskType = type;
183
228
  if (typeTasks.length === 0)
184
229
  continue;
185
- routeTasksByType(taskType, typeTasks, context);
230
+ // For tasks that appear in upcoming, calculate from remaining units
231
+ if (UPCOMING_TASK_TYPES.includes(taskType)) {
232
+ // Each task advances the unit index
233
+ for (const task of typeTasks) {
234
+ const upcoming = allUnitNames.slice(currentUnitIndex + 1);
235
+ currentUnitIndex++;
236
+ routeTasksByType(taskType, [task], context, upcoming);
237
+ }
238
+ }
239
+ else {
240
+ routeTasksByType(taskType, typeTasks, context, []);
241
+ }
186
242
  }
187
243
  consecutiveStandaloneTasks = [];
188
244
  };
@@ -195,7 +251,20 @@ function routeTasksAfterConfig(scheduledTasks, context) {
195
251
  if (task.subtasks.length > 0) {
196
252
  const subtasks = task.subtasks;
197
253
  const taskType = subtasks[0].type;
198
- routeTasksByType(taskType, subtasks, context);
254
+ // Calculate upcoming (all units after this one)
255
+ const upcoming = UPCOMING_TASK_TYPES.includes(taskType)
256
+ ? allUnitNames.slice(currentUnitIndex + 1)
257
+ : [];
258
+ if (UPCOMING_TASK_TYPES.includes(taskType)) {
259
+ currentUnitIndex++;
260
+ }
261
+ // Pass group name as label for Execute groups
262
+ if (taskType === TaskType.Execute) {
263
+ routeExecuteTasks(subtasks, context, upcoming, task.action);
264
+ }
265
+ else {
266
+ routeTasksByType(taskType, subtasks, context, upcoming);
267
+ }
199
268
  }
200
269
  }
201
270
  else {
@@ -209,21 +278,29 @@ function routeTasksAfterConfig(scheduledTasks, context) {
209
278
  /**
210
279
  * Route Answer tasks - creates separate Answer component for each question
211
280
  */
212
- function routeAnswerTasks(tasks, context) {
213
- for (const task of tasks) {
214
- context.workflowHandlers.addToQueue(createAnswerDefinition(task.action, context.service));
281
+ function routeAnswerTasks(tasks, context, upcoming) {
282
+ for (let i = 0; i < tasks.length; i++) {
283
+ const task = tasks[i];
284
+ // Calculate upcoming: remaining answer tasks + original upcoming
285
+ const remainingAnswers = tasks.slice(i + 1).map((t) => t.action);
286
+ const taskUpcoming = [...remainingAnswers, ...upcoming];
287
+ context.workflowHandlers.addToQueue(createAnswer({
288
+ question: task.action,
289
+ service: context.service,
290
+ upcoming: taskUpcoming,
291
+ }));
215
292
  }
216
293
  }
217
294
  /**
218
295
  * Route Introspect tasks - creates single Introspect component for all tasks
219
296
  */
220
- function routeIntrospectTasks(tasks, context) {
221
- context.workflowHandlers.addToQueue(createIntrospectDefinition(tasks, context.service));
297
+ function routeIntrospectTasks(tasks, context, _upcoming) {
298
+ context.workflowHandlers.addToQueue(createIntrospect({ tasks, service: context.service }));
222
299
  }
223
300
  /**
224
301
  * Route Config tasks - extracts keys, caches labels, creates Config component
225
302
  */
226
- function routeConfigTasks(tasks, context) {
303
+ function routeConfigTasks(tasks, context, _upcoming) {
227
304
  const configKeys = tasks
228
305
  .map((task) => task.params?.key)
229
306
  .filter((key) => key !== undefined);
@@ -240,31 +317,35 @@ function routeConfigTasks(tasks, context) {
240
317
  if (Object.keys(labels).length > 0) {
241
318
  saveConfigLabels(labels);
242
319
  }
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);
320
+ context.workflowHandlers.addToQueue(createConfig({
321
+ steps: createConfigStepsFromSchema(configKeys),
322
+ onFinished: (config) => {
323
+ // Save config - Config component will handle completion and feedback
324
+ try {
325
+ // Convert flat dotted keys to nested structure grouped by section
326
+ const configBySection = unflattenConfig(config);
327
+ // Save each section
328
+ for (const [section, sectionConfig] of Object.entries(configBySection)) {
329
+ saveConfig(section, sectionConfig);
330
+ }
251
331
  }
252
- }
253
- catch (error) {
254
- const errorMessage = error instanceof Error
255
- ? error.message
256
- : 'Failed to save configuration';
257
- throw new Error(errorMessage);
258
- }
259
- }, (operation) => {
260
- context.requestHandlers.onAborted(operation);
332
+ catch (error) {
333
+ const errorMessage = error instanceof Error
334
+ ? error.message
335
+ : 'Failed to save configuration';
336
+ throw new Error(errorMessage);
337
+ }
338
+ },
339
+ onAborted: (operation) => {
340
+ context.requestHandlers.onAborted(operation);
341
+ },
261
342
  }));
262
343
  }
263
344
  /**
264
345
  * Route Execute tasks - creates Execute component (validation already done)
265
346
  */
266
- function routeExecuteTasks(tasks, context) {
267
- context.workflowHandlers.addToQueue(createExecuteDefinition(tasks, context.service));
347
+ function routeExecuteTasks(tasks, context, upcoming, label) {
348
+ context.workflowHandlers.addToQueue(createExecute({ tasks, service: context.service, upcoming, label }));
268
349
  }
269
350
  /**
270
351
  * Registry mapping task types to their route handlers
@@ -279,9 +360,9 @@ const taskRouteHandlers = {
279
360
  * Route tasks by type to appropriate components
280
361
  * Uses registry pattern for extensibility
281
362
  */
282
- function routeTasksByType(taskType, tasks, context) {
363
+ function routeTasksByType(taskType, tasks, context, upcoming) {
283
364
  const handler = taskRouteHandlers[taskType];
284
365
  if (handler) {
285
- handler(tasks, context);
366
+ handler(tasks, context, upcoming);
286
367
  }
287
368
  }
@@ -61,6 +61,14 @@ export class DummyExecutor {
61
61
  }
62
62
  // Marker for extracting pwd from command output
63
63
  const PWD_MARKER = '__PWD_MARKER_7x9k2m__';
64
+ const MAX_OUTPUT_LINES = 128;
65
+ /**
66
+ * Limit output to last MAX_OUTPUT_LINES lines.
67
+ */
68
+ function limitLines(output) {
69
+ const lines = output.split('\n');
70
+ return lines.slice(-MAX_OUTPUT_LINES).join('\n');
71
+ }
64
72
  /**
65
73
  * Parse stdout to extract workdir and clean output.
66
74
  * Returns the cleaned output and the extracted workdir.
@@ -93,6 +101,12 @@ class OutputStreamer {
93
101
  */
94
102
  pushStdout(data) {
95
103
  this.chunks.push(data);
104
+ // Collapse when we have too many chunks to prevent memory growth
105
+ if (this.chunks.length > 16) {
106
+ const accumulated = this.chunks.join('');
107
+ this.chunks = [limitLines(accumulated)];
108
+ this.emittedLength = 0;
109
+ }
96
110
  if (!this.callback)
97
111
  return;
98
112
  const accumulated = this.chunks.join('');
@@ -123,7 +137,7 @@ class OutputStreamer {
123
137
  * Get the accumulated raw output.
124
138
  */
125
139
  getAccumulated() {
126
- return this.chunks.join('');
140
+ return limitLines(this.chunks.join(''));
127
141
  }
128
142
  }
129
143
  /**
@@ -189,6 +203,13 @@ export class RealExecutor {
189
203
  child.stderr.on('data', (data) => {
190
204
  const text = data.toString();
191
205
  stderr.push(text);
206
+ // Collapse when we have too many chunks to prevent memory growth
207
+ if (stderr.length > 16) {
208
+ const accumulated = stderr.join('');
209
+ const limited = limitLines(accumulated);
210
+ stderr.length = 0;
211
+ stderr.push(limited);
212
+ }
192
213
  this.outputCallback?.(text, 'stderr');
193
214
  });
194
215
  child.on('error', (error) => {
@@ -200,7 +221,7 @@ export class RealExecutor {
200
221
  description: cmd.description,
201
222
  command: cmd.command,
202
223
  output: stdoutStreamer.getAccumulated(),
203
- errors: error.message,
224
+ errors: limitLines(stderr.join('')) || error.message,
204
225
  result: ExecutionResult.Error,
205
226
  error: error.message,
206
227
  };
@@ -218,7 +239,7 @@ export class RealExecutor {
218
239
  description: cmd.description,
219
240
  command: cmd.command,
220
241
  output,
221
- errors: stderr.join(''),
242
+ errors: limitLines(stderr.join('')),
222
243
  result: success ? ExecutionResult.Success : ExecutionResult.Error,
223
244
  error: success ? undefined : `Exit code: ${code}`,
224
245
  workdir,
@@ -278,9 +299,8 @@ export async function executeCommands(commands, onProgress) {
278
299
  : ExecutionStatus.Failed,
279
300
  output,
280
301
  });
281
- // Stop if critical command failed
282
- const isCritical = cmd.critical !== false;
283
- if (output.result !== ExecutionResult.Success && isCritical) {
302
+ // Stop on failure
303
+ if (output.result !== ExecutionResult.Success) {
284
304
  break;
285
305
  }
286
306
  }
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Timing utilities for UI components
3
3
  */
4
+ export const ELAPSED_UPDATE_INTERVAL = 250;
4
5
  /**
5
6
  * Waits for at least the minimum processing time.
6
7
  * Ensures async operations don't complete too quickly for good UX.
@@ -24,6 +24,12 @@ You will receive:
24
24
  - Tasks from user-defined skills include params.skill (skill name) and
25
25
  parameter values that were substituted into the action
26
26
 
27
+ **CRITICAL - Command Count Rule**: You MUST generate EXACTLY one command
28
+ per input task, no more, no less. The number of commands in your response
29
+ MUST match the number of tasks you received. Do NOT split a single task
30
+ into multiple commands or generate extra commands beyond what was
31
+ scheduled.
32
+
27
33
  ## Skill-Based Command Generation
28
34
 
29
35
  **CRITICAL**: The "Available Skills" section in the prompt defines the ONLY
@@ -133,6 +139,18 @@ Given tasks from this skill:
133
139
  Do NOT invent different commands - use exactly what the skill specifies,
134
140
  with parameter placeholders replaced by actual values.
135
141
 
142
+ ### Handling Skipped Steps
143
+
144
+ **CRITICAL - STEP ORDER PRESERVATION**: When some steps from a skill are
145
+ omitted during scheduling, you MUST maintain alignment with the
146
+ original step positions in both the Steps and Execution sections. Each
147
+ task corresponds to a specific line number in the skill definition, NOT
148
+ to its sequential position in the task list. If you receive tasks for
149
+ steps 1 and 3 (with step 2 skipped), use Execution lines 1 and 3
150
+ (NOT lines 1 and 2). The step numbers in the task actions indicate
151
+ which Execution line to use - always match by original position, never
152
+ by sequential task index.
153
+
136
154
  **CRITICAL - VERBATIM EXECUTION**: Run shell commands EXACTLY as written in
137
155
  the ### Execution section. Do NOT:
138
156
  - Modify the command string in any way
@@ -174,7 +192,6 @@ Return a structured response with commands to execute:
174
192
  - **workdir**: Optional working directory for the command (defaults to
175
193
  current)
176
194
  - **timeout**: Optional timeout in milliseconds (defaults to 30000)
177
- - **critical**: Whether failure should stop execution (defaults to true)
178
195
 
179
196
  ## Command Generation Guidelines
180
197
 
@@ -358,14 +375,12 @@ commands:
358
375
 
359
376
  For complex multi-step operations:
360
377
 
361
- 1. **Sequential dependencies**: Mark early commands as critical so failure
362
- stops the chain
378
+ 1. **Sequential dependencies**: Commands execute in order; any failure stops
379
+ the chain
363
380
  2. **Long-running processes**: Set appropriate timeouts (build processes
364
381
  may need 10+ minutes)
365
382
  3. **Working directories**: Use workdir to ensure commands run in the
366
383
  right location
367
- 4. **Error handling**: For non-critical cleanup steps, set critical:
368
- false
369
384
 
370
385
  ## Handling Config Placeholders
371
386
 
@@ -424,17 +439,28 @@ Example:
424
439
 
425
440
  Before returning commands:
426
441
 
427
- 1. **CRITICAL: If tasks have params.skill, verify Available Skills
442
+ 1. **CRITICAL: Verify command count matches input task count** - you must
443
+ have exactly one command per input task
444
+ 2. **CRITICAL: If tasks have params.skill, verify Available Skills
428
445
  section exists**
429
- 2. **CRITICAL: If tasks have params.skill, verify the skill exists in
446
+ 3. **CRITICAL: If tasks have params.skill, verify the skill exists in
430
447
  Available Skills section**
431
- 3. **CRITICAL: If tasks have params.skill, verify the skill has both
448
+ 4. **CRITICAL: If tasks have params.skill, verify the skill has both
432
449
  Steps and Execution sections**
433
- 4. **CRITICAL: If any validation fails, return error response with empty
450
+ 5. **CRITICAL: If any validation fails, return error response with empty
434
451
  commands array**
435
- 5. Verify each command matches its task description
436
- 6. Check that all task params are incorporated
437
- 7. Ensure paths are properly quoted
438
- 8. Confirm timeouts are reasonable for each operation
439
- 9. Validate that critical flags are set appropriately
452
+ 6. Verify each command matches its task description
453
+ 7. Check that all task params are incorporated
454
+ 8. Ensure paths are properly quoted
455
+ 9. Confirm timeouts are reasonable for each operation
440
456
  10. Review for any safety concerns
457
+
458
+ ## Confirmed Schedule
459
+
460
+ CRITICAL: The user message contains the confirmed schedule that the user
461
+ has reviewed and approved. You MUST generate exactly one command per task
462
+ listed in the confirmed schedule. The number of commands in your response
463
+ MUST equal the number of tasks below. DO NOT add extra commands, DO NOT
464
+ skip tasks, and DO NOT split tasks into multiple commands.
465
+
466
+ Your response MUST contain exactly N commands corresponding to these N tasks.
@@ -34,10 +34,6 @@ export const executeTool = {
34
34
  type: 'number',
35
35
  description: 'Optional timeout in milliseconds. Defaults to 30000 (30 seconds).',
36
36
  },
37
- critical: {
38
- type: 'boolean',
39
- description: 'Whether failure should stop execution of subsequent commands. Defaults to true.',
40
- },
41
37
  },
42
38
  required: ['description', 'command'],
43
39
  },
@@ -56,7 +56,6 @@ export const ExecuteCommandSchema = z.object({
56
56
  command: z.string().min(1),
57
57
  workdir: z.string().optional(),
58
58
  timeout: z.number().int().positive().optional(),
59
- critical: z.boolean().optional(),
60
59
  });
61
60
  /**
62
61
  * Zod schema for Capability type.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prompt-language-shell",
3
- "version": "0.9.0",
3
+ "version": "0.9.4",
4
4
  "description": "Your personal command-line concierge. Ask politely, and it gets things done.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/dist/parser.js DELETED
@@ -1,13 +0,0 @@
1
- /**
2
- * Parses a comma-separated list of tasks from command-line prompt
3
- * Strips exclamation marks and periods from each task
4
- *
5
- * @param prompt - Raw command-line input string
6
- * @returns Array of parsed task strings
7
- */
8
- export function parseCommands(prompt) {
9
- return prompt
10
- .split(',')
11
- .map((task) => task.trim().replace(/[!.]/g, '').trim())
12
- .filter((task) => task.length > 0);
13
- }
@@ -1,20 +0,0 @@
1
- /**
2
- * Utility functions for config manipulation
3
- */
4
- /**
5
- * Flatten nested config object to dot notation
6
- * Example: { a: { b: 1 } } => { 'a.b': 1 }
7
- */
8
- export function flattenConfig(obj, prefix = '') {
9
- const result = {};
10
- for (const [key, value] of Object.entries(obj)) {
11
- const fullKey = prefix ? `${prefix}.${key}` : key;
12
- if (value && typeof value === 'object' && !Array.isArray(value)) {
13
- Object.assign(result, flattenConfig(value, fullKey));
14
- }
15
- else {
16
- result[fullKey] = value;
17
- }
18
- }
19
- return result;
20
- }
@@ -1,62 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState } from 'react';
3
- import { Box, Text } from 'ink';
4
- import { ComponentStatus, } from '../types/components.js';
5
- import { Colors, getTextColor, Palette } from '../services/colors.js';
6
- import { useInput } from '../services/keyboard.js';
7
- import { UserQuery } from './UserQuery.js';
8
- export const ConfirmView = ({ message, state, status }) => {
9
- const isActive = status === ComponentStatus.Active;
10
- const { selectedIndex } = state;
11
- const options = [
12
- { label: 'yes', value: 'yes', color: Palette.BrightGreen },
13
- { label: 'no', value: 'no', color: Colors.Status.Error },
14
- ];
15
- // Timeline rendering (Done status)
16
- if (status === ComponentStatus.Done) {
17
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, marginLeft: 1, children: _jsx(Text, { color: undefined, children: message }) }), _jsxs(UserQuery, { children: ["> ", options[selectedIndex].label] })] }));
18
- }
19
- // Active/Pending rendering
20
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, marginLeft: 1, children: _jsx(Text, { color: getTextColor(isActive), children: message }) }), _jsxs(Box, { marginLeft: 1, children: [_jsx(Text, { color: Colors.Action.Select, children: ">" }), _jsx(Text, { children: " " }), _jsx(Box, { children: options.map((option, index) => {
21
- const isSelected = index === selectedIndex;
22
- return (_jsx(Box, { marginRight: 2, children: _jsx(Text, { color: isSelected ? option.color : undefined, dimColor: !isSelected, children: option.label }) }, option.value));
23
- }) })] })] }));
24
- };
25
- /**
26
- * Confirm controller: Manages yes/no selection
27
- */
28
- export function Confirm({ message, status, requestHandlers, onConfirmed, onCancelled, }) {
29
- const isActive = status === ComponentStatus.Active;
30
- const [selectedIndex, setSelectedIndex] = useState(0); // 0 = Yes, 1 = No
31
- useInput((input, key) => {
32
- if (!isActive)
33
- return;
34
- if (key.escape) {
35
- // Escape: highlight "No" and cancel
36
- const finalState = { selectedIndex: 1, confirmed: false };
37
- requestHandlers.onCompleted(finalState);
38
- onCancelled();
39
- }
40
- else if (key.tab) {
41
- // Toggle between Yes (0) and No (1)
42
- setSelectedIndex((prev) => (prev === 0 ? 1 : 0));
43
- }
44
- else if (key.return) {
45
- // Confirm selection
46
- const finalState = {
47
- selectedIndex,
48
- confirmed: true,
49
- };
50
- requestHandlers.onCompleted(finalState);
51
- if (selectedIndex === 0) {
52
- onConfirmed();
53
- }
54
- else {
55
- onCancelled();
56
- }
57
- }
58
- }, { isActive });
59
- // Controller always renders View, passing current state
60
- const state = { selectedIndex, confirmed: false };
61
- return _jsx(ConfirmView, { message: message, state: state, status: status });
62
- }