prompt-language-shell 0.9.0 → 0.9.2

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.
@@ -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
  }
@@ -211,14 +230,14 @@ function routeTasksAfterConfig(scheduledTasks, context) {
211
230
  */
212
231
  function routeAnswerTasks(tasks, context) {
213
232
  for (const task of tasks) {
214
- context.workflowHandlers.addToQueue(createAnswerDefinition(task.action, context.service));
233
+ context.workflowHandlers.addToQueue(createAnswer({ question: task.action, service: context.service }));
215
234
  }
216
235
  }
217
236
  /**
218
237
  * Route Introspect tasks - creates single Introspect component for all tasks
219
238
  */
220
239
  function routeIntrospectTasks(tasks, context) {
221
- context.workflowHandlers.addToQueue(createIntrospectDefinition(tasks, context.service));
240
+ context.workflowHandlers.addToQueue(createIntrospect({ tasks, service: context.service }));
222
241
  }
223
242
  /**
224
243
  * Route Config tasks - extracts keys, caches labels, creates Config component
@@ -240,31 +259,35 @@ function routeConfigTasks(tasks, context) {
240
259
  if (Object.keys(labels).length > 0) {
241
260
  saveConfigLabels(labels);
242
261
  }
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);
262
+ context.workflowHandlers.addToQueue(createConfig({
263
+ steps: createConfigStepsFromSchema(configKeys),
264
+ onFinished: (config) => {
265
+ // Save config - Config component will handle completion and feedback
266
+ try {
267
+ // Convert flat dotted keys to nested structure grouped by section
268
+ const configBySection = unflattenConfig(config);
269
+ // Save each section
270
+ for (const [section, sectionConfig] of Object.entries(configBySection)) {
271
+ saveConfig(section, sectionConfig);
272
+ }
251
273
  }
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);
274
+ catch (error) {
275
+ const errorMessage = error instanceof Error
276
+ ? error.message
277
+ : 'Failed to save configuration';
278
+ throw new Error(errorMessage);
279
+ }
280
+ },
281
+ onAborted: (operation) => {
282
+ context.requestHandlers.onAborted(operation);
283
+ },
261
284
  }));
262
285
  }
263
286
  /**
264
287
  * Route Execute tasks - creates Execute component (validation already done)
265
288
  */
266
289
  function routeExecuteTasks(tasks, context) {
267
- context.workflowHandlers.addToQueue(createExecuteDefinition(tasks, context.service));
290
+ context.workflowHandlers.addToQueue(createExecute({ tasks, service: context.service }));
268
291
  }
269
292
  /**
270
293
  * Registry mapping task types to their route handlers
@@ -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
@@ -424,17 +430,29 @@ Example:
424
430
 
425
431
  Before returning commands:
426
432
 
427
- 1. **CRITICAL: If tasks have params.skill, verify Available Skills
433
+ 1. **CRITICAL: Verify command count matches input task count** - you must
434
+ have exactly one command per input task
435
+ 2. **CRITICAL: If tasks have params.skill, verify Available Skills
428
436
  section exists**
429
- 2. **CRITICAL: If tasks have params.skill, verify the skill exists in
437
+ 3. **CRITICAL: If tasks have params.skill, verify the skill exists in
430
438
  Available Skills section**
431
- 3. **CRITICAL: If tasks have params.skill, verify the skill has both
439
+ 4. **CRITICAL: If tasks have params.skill, verify the skill has both
432
440
  Steps and Execution sections**
433
- 4. **CRITICAL: If any validation fails, return error response with empty
441
+ 5. **CRITICAL: If any validation fails, return error response with empty
434
442
  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
440
- 10. Review for any safety concerns
443
+ 6. Verify each command matches its task description
444
+ 7. Check that all task params are incorporated
445
+ 8. Ensure paths are properly quoted
446
+ 9. Confirm timeouts are reasonable for each operation
447
+ 10. Validate that critical flags are set appropriately
448
+ 11. Review for any safety concerns
449
+
450
+ ## Confirmed Schedule
451
+
452
+ CRITICAL: The user message contains the confirmed schedule that the user
453
+ has reviewed and approved. You MUST generate exactly one command per task
454
+ listed in the confirmed schedule. The number of commands in your response
455
+ MUST equal the number of tasks below. DO NOT add extra commands, DO NOT
456
+ skip tasks, and DO NOT split tasks into multiple commands.
457
+
458
+ Your response MUST contain exactly N commands corresponding to these N tasks.
@@ -4,7 +4,7 @@ import { Box, Text } from 'ink';
4
4
  import { ComponentStatus, } from '../types/components.js';
5
5
  import { TaskType } from '../types/types.js';
6
6
  import { Colors } from '../services/colors.js';
7
- import { createScheduleDefinition } from '../services/components.js';
7
+ import { createSchedule } from '../services/components.js';
8
8
  import { useInput } from '../services/keyboard.js';
9
9
  import { formatErrorMessage } from '../services/messages.js';
10
10
  import { handleRefinement } from '../services/refinement.js';
@@ -77,12 +77,16 @@ export function Command({ command, status, service, requestHandlers, lifecycleHa
77
77
  // Check if tasks contain DEFINE type (variant selection needed)
78
78
  const hasDefineTask = result.tasks.some((task) => task.type === TaskType.Define);
79
79
  // Create Schedule definition
80
- const scheduleDefinition = createScheduleDefinition(result.message, result.tasks, hasDefineTask
81
- ? async (selectedTasks) => {
82
- // Refinement flow for DEFINE tasks
83
- await handleRefinement(selectedTasks, svc, command, lifecycleHandlers, workflowHandlers, requestHandlers);
84
- }
85
- : undefined);
80
+ const scheduleDefinition = createSchedule({
81
+ message: result.message,
82
+ tasks: result.tasks,
83
+ onSelectionConfirmed: hasDefineTask
84
+ ? async (selectedTasks) => {
85
+ // Refinement flow for DEFINE tasks
86
+ await handleRefinement(selectedTasks, svc, command, lifecycleHandlers, workflowHandlers, requestHandlers);
87
+ }
88
+ : undefined,
89
+ });
86
90
  if (hasDefineTask) {
87
91
  // DEFINE tasks: Move Command to timeline, add Schedule to queue
88
92
  lifecycleHandlers.completeActive();
@@ -1,12 +1,13 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { memo } from 'react';
3
+ import { ComponentStatus, } from '../types/components.js';
3
4
  import { ComponentName } from '../types/types.js';
4
5
  import { Answer, AnswerView } from './Answer.js';
5
6
  import { Command, CommandView } from './Command.js';
6
7
  import { Config, ConfigView } from './Config.js';
7
8
  import { Confirm, ConfirmView } from './Confirm.js';
8
9
  import { Debug } from './Debug.js';
9
- import { Execute, ExecuteView } from './Execute.js';
10
+ import { Execute, ExecuteView, mapStateToViewProps } from './Execute.js';
10
11
  import { Feedback } from './Feedback.js';
11
12
  import { Introspect, IntrospectView } from './Introspect.js';
12
13
  import { Message } from './Message.js';
@@ -108,7 +109,9 @@ export const ViewComponent = memo(function ViewComponent({ def, }) {
108
109
  }
109
110
  case ComponentName.Execute: {
110
111
  const { state, status } = def;
111
- return _jsx(ExecuteView, { state: state, status: status });
112
+ const isActive = status === ComponentStatus.Active;
113
+ const viewProps = mapStateToViewProps(state, isActive);
114
+ return _jsx(ExecuteView, { ...viewProps });
112
115
  }
113
116
  case ComponentName.Answer: {
114
117
  const { props: { question }, state, status, } = def;
package/dist/ui/Config.js CHANGED
@@ -208,7 +208,10 @@ export function Config(props) {
208
208
  }
209
209
  else {
210
210
  // Fallback: complete with abort feedback directly
211
- lifecycleHandlers.completeActive(createFeedback(FeedbackType.Aborted, 'Configuration cancelled.'));
211
+ lifecycleHandlers.completeActive(createFeedback({
212
+ type: FeedbackType.Aborted,
213
+ message: 'Configuration cancelled.',
214
+ }));
212
215
  }
213
216
  return;
214
217
  }
@@ -272,12 +275,15 @@ export function Config(props) {
272
275
  onFinished(newValues);
273
276
  }
274
277
  // Success - complete with success feedback
275
- lifecycleHandlers.completeActive(createFeedback(FeedbackType.Succeeded, 'Configuration saved successfully.'));
278
+ lifecycleHandlers.completeActive(createFeedback({
279
+ type: FeedbackType.Succeeded,
280
+ message: 'Configuration saved successfully.',
281
+ }));
276
282
  }
277
283
  catch (error) {
278
284
  // Failure - complete with error feedback
279
285
  const errorMessage = error instanceof Error ? error.message : 'Configuration failed';
280
- lifecycleHandlers.completeActive(createFeedback(FeedbackType.Failed, errorMessage));
286
+ lifecycleHandlers.completeActive(createFeedback({ type: FeedbackType.Failed, message: errorMessage }));
281
287
  }
282
288
  setStep(steps.length);
283
289
  }