prompt-language-shell 0.8.6 → 0.8.8

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,6 +30,7 @@ export async function processTasks(tasks, service) {
30
30
  message: result.message,
31
31
  summary: result.summary || '',
32
32
  commands: resolvedCommands,
33
+ error: result.error,
33
34
  debug: result.debug,
34
35
  };
35
36
  }
@@ -167,6 +167,7 @@ export class AnthropicService {
167
167
  summary: input.summary,
168
168
  tasks: [],
169
169
  commands: input.commands,
170
+ error: input.error,
170
171
  debug,
171
172
  });
172
173
  if (!validation.success) {
@@ -1,13 +1,16 @@
1
1
  import { DebugLevel } from '../configuration/types.js';
2
2
  import { FeedbackType, Origin, TaskType } from '../types/types.js';
3
3
  import { ExecutionStatus } from './shell.js';
4
+ import { ComponentStatus } from '../types/components.js';
4
5
  /**
5
6
  * Base color palette - raw color values with descriptive names.
6
7
  * All colors used in the interface are defined here.
7
8
  */
8
9
  export const Palette = {
9
10
  White: '#ffffff',
11
+ SoftWhite: '#fafafa',
10
12
  AshGray: '#d0d0d0',
13
+ LightGray: '#aaaaaa',
11
14
  Gray: '#888888',
12
15
  DarkGray: '#666666',
13
16
  CharcoalGray: '#282828',
@@ -32,6 +35,7 @@ export const Palette = {
32
35
  export const Colors = {
33
36
  Text: {
34
37
  Active: Palette.White,
38
+ Pending: Palette.SoftWhite,
35
39
  Inactive: Palette.AshGray,
36
40
  UserQuery: Palette.White,
37
41
  },
@@ -148,12 +152,18 @@ const originColors = {
148
152
  * - Colors.Text.Active for current items
149
153
  * - Colors.Text.Inactive (undefined) for historical items
150
154
  */
151
- function processColor(color, isCurrent) {
152
- return color === null
153
- ? isCurrent
154
- ? Colors.Text.Active
155
- : Colors.Text.Inactive
156
- : color;
155
+ function processColor(color, status) {
156
+ const getColor = () => {
157
+ switch (status) {
158
+ case ComponentStatus.Active:
159
+ return Colors.Text.Active;
160
+ case ComponentStatus.Pending:
161
+ return Colors.Text.Pending;
162
+ default:
163
+ return Colors.Text.Inactive;
164
+ }
165
+ };
166
+ return color || getColor();
157
167
  }
158
168
  /**
159
169
  * Get task colors with current/historical state handling.
@@ -162,11 +172,11 @@ function processColor(color, isCurrent) {
162
172
  * - Colors.Text.Inactive (undefined) for historical items
163
173
  * - Colors.Text.Active for current items
164
174
  */
165
- export function getTaskColors(type, isCurrent) {
175
+ export function getTaskColors(type, status) {
166
176
  const colors = taskColors[type];
167
177
  return {
168
- description: processColor(colors.description, isCurrent),
169
- type: processColor(colors.type, isCurrent),
178
+ description: processColor(colors.description, status),
179
+ type: processColor(colors.type, status),
170
180
  };
171
181
  }
172
182
  /**
@@ -176,8 +186,8 @@ export function getTaskColors(type, isCurrent) {
176
186
  * - Colors.Text.Inactive (undefined) for historical items
177
187
  * - Colors.Text.Active for current items
178
188
  */
179
- export function getFeedbackColor(type, isCurrent) {
180
- return processColor(feedbackColors[type], isCurrent);
189
+ export function getFeedbackColor(type, status = ComponentStatus.Done) {
190
+ return processColor(feedbackColors[type], status);
181
191
  }
182
192
  /**
183
193
  * Get color for capability origin.
@@ -147,3 +147,24 @@ export function formatErrorMessage(error) {
147
147
  }
148
148
  return rawMessage;
149
149
  }
150
+ /**
151
+ * Returns an execution error message with varied phrasing.
152
+ * Randomly selects from variations to sound natural, like a concierge.
153
+ * Format: "[Cannot execute phrase]. [Error details]."
154
+ */
155
+ export function getExecutionErrorMessage(error) {
156
+ const prefixes = [
157
+ "I can't execute this",
158
+ "I'm unable to execute this",
159
+ "I can't proceed with this",
160
+ "I'm unable to proceed with this",
161
+ 'This cannot be executed',
162
+ ];
163
+ const prefix = prefixes[Math.floor(Math.random() * prefixes.length)];
164
+ // Capitalize first letter of error
165
+ const capitalizedError = error.charAt(0).toUpperCase() + error.slice(1);
166
+ const errorWithPeriod = capitalizedError.endsWith('.')
167
+ ? capitalizedError
168
+ : `${capitalizedError}.`;
169
+ return `${prefix}. ${errorWithPeriod}`;
170
+ }
@@ -1,3 +1,4 @@
1
+ import { TaskType } from '../types/types.js';
1
2
  import { createRefinement } from './components.js';
2
3
  import { formatErrorMessage, getRefiningMessage } from './messages.js';
3
4
  import { routeTasksWithConfirm } from './router.js';
@@ -17,6 +18,10 @@ export async function handleRefinement(selectedTasks, service, originalCommand,
17
18
  .map((task) => {
18
19
  const action = task.action.toLowerCase().replace(/,/g, ' -');
19
20
  const type = task.type;
21
+ // For execute/group tasks, use generic hint - let LLM decide based on skill
22
+ if (type === TaskType.Execute || type === TaskType.Group) {
23
+ return `${action} (shell execution)`;
24
+ }
20
25
  return `${action} (type: ${type})`;
21
26
  })
22
27
  .join(', ');
@@ -101,6 +101,60 @@ function executeTasksAfterConfirm(tasks, service, userRequest, workflowHandlers,
101
101
  return;
102
102
  }
103
103
  const scheduledTasks = asScheduledTasks(tasks);
104
+ // Collect ALL Execute tasks (standalone and from groups) for upfront validation
105
+ const allExecuteTasks = [];
106
+ for (const task of scheduledTasks) {
107
+ if (task.type === TaskType.Execute) {
108
+ allExecuteTasks.push(task);
109
+ }
110
+ else if (task.type === TaskType.Group && task.subtasks) {
111
+ const subtasks = task.subtasks;
112
+ if (subtasks.length > 0 && subtasks[0].type === TaskType.Execute) {
113
+ allExecuteTasks.push(...subtasks);
114
+ }
115
+ }
116
+ }
117
+ // Validate ALL Execute tasks together to collect ALL missing config upfront
118
+ if (allExecuteTasks.length > 0) {
119
+ try {
120
+ const validation = validateExecuteTasks(allExecuteTasks);
121
+ if (validation.validationErrors.length > 0) {
122
+ // Show error feedback for invalid skills
123
+ const errorMessages = validation.validationErrors.map((error) => {
124
+ const issuesList = error.issues
125
+ .map((issue) => ` - ${issue}`)
126
+ .join('\n');
127
+ return `Invalid skill definition "${error.skill}":\n\n${issuesList}`;
128
+ });
129
+ workflowHandlers.addToQueue(createFeedback(FeedbackType.Failed, errorMessages.join('\n\n')));
130
+ return;
131
+ }
132
+ else if (validation.missingConfig.length > 0) {
133
+ // Missing config detected - create ONE Validate component for ALL missing config
134
+ workflowHandlers.addToQueue(createValidateDefinition(validation.missingConfig, userRequest, service, (error) => {
135
+ requestHandlers.onError(error);
136
+ }, () => {
137
+ // After config is complete, resume task routing
138
+ routeTasksAfterConfig(scheduledTasks, service, userRequest, workflowHandlers, requestHandlers);
139
+ }, (operation) => {
140
+ requestHandlers.onAborted(operation);
141
+ }));
142
+ return;
143
+ }
144
+ }
145
+ catch (error) {
146
+ requestHandlers.onError(error instanceof Error ? error.message : String(error));
147
+ return;
148
+ }
149
+ }
150
+ // No missing config - proceed with normal routing
151
+ routeTasksAfterConfig(scheduledTasks, service, userRequest, workflowHandlers, requestHandlers);
152
+ }
153
+ /**
154
+ * Route tasks after config is complete (or when no config is needed)
155
+ * Processes tasks in order, grouping by type
156
+ */
157
+ function routeTasksAfterConfig(scheduledTasks, service, userRequest, workflowHandlers, requestHandlers) {
104
158
  // Process tasks in order, preserving Group boundaries
105
159
  // Track consecutive standalone tasks to group them by type
106
160
  let consecutiveStandaloneTasks = [];
@@ -197,37 +251,7 @@ function routeTasksByType(taskType, typeTasks, service, userRequest, workflowHan
197
251
  }));
198
252
  }
199
253
  else if (taskType === TaskType.Execute) {
200
- // Execute tasks with validation
201
- try {
202
- const validation = validateExecuteTasks(typeTasks);
203
- if (validation.validationErrors.length > 0) {
204
- // Show error feedback for invalid skills
205
- const errorMessages = validation.validationErrors.map((error) => {
206
- const issuesList = error.issues
207
- .map((issue) => ` - ${issue}`)
208
- .join('\n');
209
- return `Invalid skill definition "${error.skill}":\n\n${issuesList}`;
210
- });
211
- workflowHandlers.addToQueue(createFeedback(FeedbackType.Failed, errorMessages.join('\n\n')));
212
- }
213
- else if (validation.missingConfig.length > 0) {
214
- workflowHandlers.addToQueue(createValidateDefinition(validation.missingConfig, userRequest, service, (error) => {
215
- requestHandlers.onError(error);
216
- }, () => {
217
- workflowHandlers.addToQueue(createExecuteDefinition(typeTasks, service));
218
- }, (operation) => {
219
- requestHandlers.onAborted(operation);
220
- }));
221
- }
222
- else {
223
- workflowHandlers.addToQueue(createExecuteDefinition(typeTasks, service));
224
- }
225
- }
226
- catch (error) {
227
- // Handle skill reference errors (e.g., unknown skills)
228
- const errorMessage = error instanceof Error ? error.message : String(error);
229
- const message = createMessage(errorMessage);
230
- workflowHandlers.addToQueue(message);
231
- }
254
+ // Execute tasks (validation already happened upfront in executeTasksAfterConfirm)
255
+ workflowHandlers.addToQueue(createExecuteDefinition(typeTasks, service));
232
256
  }
233
257
  }
@@ -26,9 +26,63 @@ You will receive:
26
26
 
27
27
  ## Skill-Based Command Generation
28
28
 
29
+ **CRITICAL**: The "Available Skills" section in the prompt defines the ONLY
30
+ skills you can execute. This is an EXHAUSTIVE and COMPLETE list. Do NOT
31
+ assume skills exist based on examples in these instructions.
32
+
29
33
  **CRITICAL**: When tasks originate from a user-defined skill, you MUST use
30
34
  the skill's **Execution** section to generate commands, NOT invent your own.
31
35
 
36
+ **CRITICAL VALIDATION**: Before generating ANY commands for skill-based
37
+ tasks, perform these checks in order:
38
+
39
+ 1. **Verify "Available Skills" section exists**: If there is no
40
+ "Available Skills" section in the prompt, STOP immediately and return
41
+ an error response.
42
+
43
+ 2. **Verify skill exists**: Check if the skill named in params.skill
44
+ actually exists in the "Available Skills" section below.
45
+
46
+ 3. **Verify skill has Steps section**: Check if the skill definition
47
+ includes a "### Steps" section with step descriptions.
48
+
49
+ 4. **Verify skill has Execution section**: Check if the skill definition
50
+ includes a "### Execution" section with actual commands.
51
+
52
+ 5. **If ANY check fails**: STOP immediately and return an error response.
53
+ DO NOT generate commands. DO NOT invent commands. DO NOT make
54
+ assumptions about what commands should be run.
55
+
56
+ **Error Response Formats** (keep error messages concise):
57
+
58
+ No Available Skills section:
59
+ ```
60
+ message: "Cannot execute:"
61
+ summary: "No skills available"
62
+ commands: []
63
+ error: "No skills available"
64
+ ```
65
+
66
+ Skill not found:
67
+ ```
68
+ message: "Cannot execute:"
69
+ summary: "Skill not found"
70
+ commands: []
71
+ error: "Skill '[skill name]' not found"
72
+ ```
73
+
74
+ Skill missing Steps or Execution:
75
+ ```
76
+ message: "Cannot execute:"
77
+ summary: "Incomplete skill"
78
+ commands: []
79
+ error: "Skill '[skill name]' is incomplete"
80
+ ```
81
+
82
+ **IMPORTANT**: Error messages must be concise (under 50 characters). Avoid
83
+ technical jargon or detailed explanations. The error will be shown to the
84
+ user in a natural, conversational format.
85
+
32
86
  ### Understanding Skill Structure
33
87
 
34
88
  User-defined skills have two key sections:
@@ -42,7 +96,7 @@ position.
42
96
 
43
97
  1. **Identify skill tasks**: Check if tasks have params.skill
44
98
  2. **Find the skill**: Look up the skill in "Available Skills" section
45
- below
99
+ below (REQUIRED - must exist)
46
100
  3. **Match tasks to Execution**: Each task action came from a Steps line;
47
101
  use the corresponding Execution line for the command
48
102
  4. **Substitute parameters**: Replace {PARAM} placeholders with actual
@@ -304,9 +358,13 @@ For complex multi-step operations:
304
358
  ❌ Setting unrealistic timeouts for long operations
305
359
  ❌ Running destructive commands without safeguards
306
360
  ❌ Ignoring task parameters when generating commands
307
- ❌ Inventing commands instead of using skill's Execution section
308
- ❌ Ignoring params.skill and making up your own 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**
309
366
  ❌ Not substituting parameter placeholders in skill commands
367
+ ❌ **CRITICAL: Assuming what commands to run when skill is missing**
310
368
 
311
369
  ✅ Match commands precisely to task descriptions
312
370
  ✅ Use task params to fill in specific values
@@ -314,6 +372,10 @@ For complex multi-step operations:
314
372
  ✅ Set appropriate timeouts for each operation type
315
373
  ✅ Include safety checks for destructive operations
316
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**
317
379
  ✅ Always use skill's Execution section when params.skill is present
318
380
  ✅ Replace all {PARAM} placeholders with values from task params
319
381
 
@@ -321,9 +383,17 @@ For complex multi-step operations:
321
383
 
322
384
  Before returning commands:
323
385
 
324
- 1. Verify each command matches its task description
325
- 2. Check that all task params are incorporated
326
- 3. Ensure paths are properly quoted
327
- 4. Confirm timeouts are reasonable for each operation
328
- 5. Validate that critical flags are set appropriately
329
- 6. Review for any safety concerns
386
+ 1. **CRITICAL: If tasks have params.skill, verify Available Skills
387
+ section exists**
388
+ 2. **CRITICAL: If tasks have params.skill, verify the skill exists in
389
+ Available Skills section**
390
+ 3. **CRITICAL: If tasks have params.skill, verify the skill has both
391
+ Steps and Execution sections**
392
+ 4. **CRITICAL: If any validation fails, return error response with empty
393
+ commands array**
394
+ 5. Verify each command matches its task description
395
+ 6. Check that all task params are incorporated
396
+ 7. Ensure paths are properly quoted
397
+ 8. Confirm timeouts are reasonable for each operation
398
+ 9. Validate that critical flags are set appropriately
399
+ 10. Review for any safety concerns
@@ -4,6 +4,21 @@ You are the scheduling component of "pls" (please), a command-line
4
4
  concierge. Your role is to organize user requests into hierarchical
5
5
  task structures with high-level tasks and their subtasks.
6
6
 
7
+ **CRITICAL - Skill Matching Foundation**:
8
+
9
+ The ONLY skills you can execute are those explicitly listed in the
10
+ "Available Skills" section of the system prompt. This section may be
11
+ present with skills, present but empty, or missing entirely. Your
12
+ behavior must adapt accordingly:
13
+
14
+ - **Skills present**: Match user requests ONLY against listed skills
15
+ - **Empty or missing**: Create "ignore" tasks for ALL action verbs
16
+
17
+ All examples in these instructions (e.g., "build", "deploy", "process")
18
+ are for illustration only. They do NOT represent actual available
19
+ skills unless they appear in the "Available Skills" section of the
20
+ system prompt.
21
+
7
22
  ## Response Format
8
23
 
9
24
  Every response MUST include a brief message (single sentence, max 64
@@ -53,21 +68,38 @@ Every task MUST have a type field. Use the appropriate type:
53
68
  - `answer` - Answering questions, explaining concepts
54
69
  - `introspect` - Listing capabilities when user asks what you can do
55
70
  - `report` - Generating summaries, displaying results
56
- - `define` - Presenting options when request is ambiguous
71
+ - `define` - Presenting options when a matching skill needs variant
72
+ selection
57
73
  - `ignore` - Request has NO matching skill OR is too vague to execute
58
74
 
59
- **CRITICAL**: Use `ignore` type for ANY action verb that does NOT have
60
- a matching skill in the "Available Skills" section. DO NOT create
61
- `execute` tasks without a corresponding skill.
75
+ **CRITICAL SKILL MATCHING RULES**:
76
+
77
+ 1. **ONLY match against skills in "Available Skills" section**: The
78
+ ONLY skills you can execute are those explicitly listed in the
79
+ "Available Skills" section of the prompt. Do NOT assume, infer, or
80
+ create skills based on examples in these instructions.
81
+
82
+ 2. **Examples are illustrative only**: All examples in these
83
+ instructions (including "build", "deploy", etc.) are for
84
+ illustration purposes. They do NOT represent actual available
85
+ skills unless they appear in the "Available Skills" section.
62
86
 
63
- **Define task params**: When creating a `define` type task, include:
87
+ 3. **No Available Skills = No Execute Tasks**: If the "Available
88
+ Skills" section is missing or empty, ALL action verbs must result
89
+ in `ignore` type tasks. You cannot execute ANY commands without
90
+ explicitly defined skills.
91
+
92
+ 4. **Define vs Ignore**:
93
+ - Use `define` ONLY when a skill EXISTS in "Available Skills" but
94
+ needs variant selection
95
+ - Use `ignore` when NO matching skill exists in "Available Skills"
96
+
97
+ **Define task params** (ONLY when skill exists): When creating a
98
+ `define` type task for a skill that EXISTS in "Available Skills",
99
+ include:
64
100
  - `skill`: the skill name that needs variant selection (REQUIRED)
65
101
  - `options`: array of option strings describing each variant (REQUIRED)
66
102
 
67
- Example: User "build" without variant → Task with type "define",
68
- params { skill: "Build Project", options: ["Build project Alpha, the
69
- main variant", "Build project Beta, the experimental variant"] }
70
-
71
103
  ## Configuration Requests
72
104
 
73
105
  When user wants to configure or change settings (e.g., "config",
@@ -93,15 +125,22 @@ Before creating tasks, evaluate the request type:
93
125
  "search"
94
126
  - Example: "explain docker" → answer type
95
127
 
96
- 3. **Action requests** (commands) - Must match available skills:
97
- - Action verbs like "compile", "deploy", "process", "validate"
98
- - If verb matches a skill extract skill steps as subtasks
99
- - If verb does NOT match any skill → ignore type with action
100
- "Ignore unknown 'X' request" where X is the verb/phrase
101
- - Example: "compile" with no skill → action "Ignore unknown
102
- 'compile' request"
103
- - Example: "validate" with no skill action "Ignore unknown
104
- 'validate' request"
128
+ 3. **Action requests** (commands) - Must match skills in "Available
129
+ Skills" section:
130
+ - Check if action verb matches ANY skill in "Available Skills"
131
+ section
132
+ - If verb matches a skill examine the skill's Execution section
133
+ to determine structure:
134
+ - Multiple execution steps → create ONLY a group task with those
135
+ steps as subtasks (never create a flat execute task)
136
+ - Single execution step → can use a leaf execute task
137
+ - If verb does NOT match any skill in "Available Skills" → ignore
138
+ type with action "Ignore unknown 'X' request" where X is the
139
+ verb/phrase
140
+ - Example: "compile" with no matching skill in "Available Skills"
141
+ → action "Ignore unknown 'compile' request"
142
+ - Example: "build" with no matching skill in "Available Skills" →
143
+ action "Ignore unknown 'build' request"
105
144
 
106
145
  4. **Vague/ambiguous requests** without clear verb:
107
146
  - Phrases like "do something", "handle it" → ignore type
@@ -128,6 +167,22 @@ components (e.g., {project.VARIANT.path}, {env.TYPE.config},
128
167
  - Example: "build alpha" → variant is "alpha"
129
168
  - Example: "deploy to staging" → variant is "staging"
130
169
  - Example: "process experimental" → variant is "experimental"
170
+ - **CRITICAL**: If the variant CANNOT be identified from the user's
171
+ request, you MUST create a DEFINE task instead (see step 1a below)
172
+
173
+ 1a. **When variant is unclear** - Create a DEFINE task:
174
+ - **NEVER use placeholder values** like `<UNKNOWN>`, `UNKNOWN`, or any
175
+ other placeholder
176
+ - **NEVER leave variant unresolved** or use temporary values
177
+ - **ALWAYS create a DEFINE task** with type "define" that includes:
178
+ - params.skill: the skill name requiring variant selection
179
+ - params.options: array of descriptive options for each available
180
+ variant
181
+ - Example: User says "deploy" without specifying environment → Create
182
+ DEFINE task with options like "Deploy to staging environment" and
183
+ "Deploy to production environment"
184
+ - The define task will prompt the user to select the variant before
185
+ execution continues
131
186
 
132
187
  2. **Normalize to lowercase**: Convert variant name to lowercase
133
188
  - "Alpha" → "alpha"
@@ -160,6 +215,19 @@ components (e.g., {project.VARIANT.path}, {env.TYPE.config},
160
215
  {project.beta.config}` should include config:
161
216
  ["project.beta.repo", "project.beta.config"]
162
217
 
218
+ 6. **Multi-step skills MUST use group structure**:
219
+ - **CRITICAL**: When a skill has multiple execution steps, it MUST
220
+ be represented as a group task with those steps as subtasks
221
+ - **NEVER use a flat execute task** for multi-step skills
222
+ - Single execution step: Can be represented as a leaf execute task
223
+ - Multiple execution steps: ALWAYS use group structure, never flat
224
+ - Note: The same skill can appear multiple times if the user
225
+ requests it in sequence (e.g., "deploy alpha, test, deploy beta")
226
+ - Each occurrence must still use group structure
227
+ - Example: "deploy alpha" → "Deploy Alpha" (group) with subtasks
228
+ - Example: "deploy alpha, test, deploy alpha" → "Deploy Alpha"
229
+ (group), "Run tests" (execute), "Deploy Alpha" (group)
230
+
163
231
  **Examples**:
164
232
 
165
233
  User request with variant placeholder
@@ -188,6 +256,10 @@ User request with multiple config expressions
188
256
  - Multiple config expressions from the same task's commands
189
257
 
190
258
  **Critical Rules**:
259
+ - **NEVER use placeholder values** like `<UNKNOWN>`, `UNKNOWN`, or
260
+ leave variant unresolved
261
+ - **If variant cannot be determined** from user request, create a
262
+ DEFINE task with options
191
263
  - NEVER leave uppercase placeholder components unresolved
192
264
  - The uppercase word can be ANY name (VARIANT, TARGET, TYPE,
193
265
  PRODUCT, etc.)
@@ -269,20 +341,40 @@ even if they use the same action verb.
269
341
 
270
342
  ## Strict Skill Matching
271
343
 
272
- Skills define the ONLY operations you can execute. If skills are
273
- provided in the "Available Skills" section:
344
+ **CRITICAL - Examples Are NOT Real Skills:**
345
+
346
+ - **All examples in these instructions are for illustration ONLY**:
347
+ Examples like "build", "deploy", "process" are NOT real skills
348
+ - **ONLY the Available Skills section contains real skills**: The
349
+ Available Skills section in the system prompt is the ONLY source of
350
+ truth
351
+ - **Never use example skills**: Do NOT create tasks based on skills
352
+ mentioned in examples unless they appear in Available Skills
353
+ - **When no Available Skills section exists**: ALL action verbs must
354
+ result in "ignore" type tasks
355
+
356
+ **CRITICAL**: Skills in the "Available Skills" section define the ONLY
357
+ operations you can execute. This is an EXHAUSTIVE and COMPLETE list.
274
358
 
275
359
  **EXHAUSTIVE and EXCLUSIVE rules:**
276
360
 
277
- - The list of available skills is COMPLETE
278
- - If an action verb does NOT have a matching skill, it CANNOT be
279
- executed
280
- - You MUST create an "ignore" type task for ANY verb without a matching
281
- skill
282
- - There are NO implicit or assumed operations
283
- - **DO NOT infer follow-up actions based on context**
284
- - **DO NOT assume operations even if they seem logically related to a
285
- matched skill**
361
+ - **ONLY skills in "Available Skills" section exist**: The skills
362
+ listed in the "Available Skills" section are the ONLY skills
363
+ available. Do NOT assume skills exist based on examples in these
364
+ instructions.
365
+ - **Empty or missing "Available Skills" = NO execute tasks**: If there
366
+ is no "Available Skills" section, or if it's empty, you CANNOT
367
+ create ANY execute tasks. ALL action verbs must result in "ignore"
368
+ type tasks.
369
+ - **The list is COMPLETE**: The "Available Skills" list is exhaustive.
370
+ There are no hidden or implicit skills.
371
+ - **No matching skill = ignore task**: If an action verb does NOT have
372
+ a matching skill in "Available Skills", you MUST create an "ignore"
373
+ type task
374
+ - **NO assumptions**: There are NO implicit or assumed operations
375
+ - **NO inference**: DO NOT infer follow-up actions based on context
376
+ - **NO related operations**: DO NOT assume operations even if they
377
+ seem logically related to a matched skill
286
378
 
287
379
  **Common verbs that need skills:**
288
380
 
@@ -42,6 +42,10 @@ export const executeTool = {
42
42
  required: ['description', 'command'],
43
43
  },
44
44
  },
45
+ error: {
46
+ type: 'string',
47
+ description: 'Error message when execution cannot proceed. Only include this field when returning an empty commands array due to validation failure (e.g., skill not found, missing Steps/Execution sections). Describes what went wrong.',
48
+ },
45
49
  },
46
50
  required: ['message', 'summary', 'commands'],
47
51
  },
@@ -90,6 +90,7 @@ export const CommandResultSchema = z.object({
90
90
  tasks: z.array(ScheduledTaskSchema),
91
91
  answer: z.string().optional(),
92
92
  commands: z.array(ExecuteCommandSchema).optional(),
93
+ error: z.string().optional(),
93
94
  debug: z.array(ComponentDefinitionSchema).optional(),
94
95
  });
95
96
  /**
package/dist/ui/Config.js CHANGED
@@ -201,11 +201,15 @@ export function Config(props) {
201
201
  selectedIndex,
202
202
  };
203
203
  requestHandlers.onCompleted(finalState);
204
+ // Abort configuration
204
205
  if (onAborted) {
206
+ // Let Workflow handler complete and add feedback
205
207
  onAborted('configuration');
206
208
  }
207
- // Complete with abort feedback
208
- lifecycleHandlers.completeActive(createFeedback(FeedbackType.Aborted, 'Configuration cancelled.'));
209
+ else {
210
+ // Fallback: complete with abort feedback directly
211
+ lifecycleHandlers.completeActive(createFeedback(FeedbackType.Aborted, 'Configuration cancelled.'));
212
+ }
209
213
  return;
210
214
  }
211
215
  // Handle selection step navigation
@@ -4,13 +4,14 @@ import { Box, Text } from 'ink';
4
4
  import { ComponentStatus, } from '../types/components.js';
5
5
  import { getTextColor } from '../services/colors.js';
6
6
  import { useInput } from '../services/keyboard.js';
7
- import { formatErrorMessage } from '../services/messages.js';
7
+ import { formatErrorMessage, getExecutionErrorMessage, } from '../services/messages.js';
8
8
  import { ExecutionStatus } from '../services/shell.js';
9
9
  import { ensureMinimumTime } from '../services/timing.js';
10
10
  import { buildAbortedState, handleTaskCompletion, handleTaskFailure, } from '../execution/handlers.js';
11
11
  import { processTasks } from '../execution/processing.js';
12
12
  import { executeReducer, initialState } from '../execution/reducer.js';
13
13
  import { ExecuteActionType } from '../execution/types.js';
14
+ import { createMessage, markAsDone } from '../services/components.js';
14
15
  import { Message } from './Message.js';
15
16
  import { Spinner } from './Spinner.js';
16
17
  import { Task } from './Task.js';
@@ -101,6 +102,26 @@ export function Execute({ tasks, status, service, requestHandlers, lifecycleHand
101
102
  workflowHandlers.addToTimeline(...result.debug);
102
103
  }
103
104
  if (result.commands.length === 0) {
105
+ // Check if this is an error response (has error field)
106
+ if (result.error) {
107
+ // Add error message to timeline
108
+ const errorMessage = getExecutionErrorMessage(result.error);
109
+ workflowHandlers.addToTimeline(markAsDone(createMessage(errorMessage)));
110
+ // Complete without error in state (message already in timeline)
111
+ const finalState = {
112
+ message: result.message,
113
+ summary: '',
114
+ taskInfos: [],
115
+ completed: 0,
116
+ taskExecutionTimes: [],
117
+ completionMessage: null,
118
+ error: null,
119
+ };
120
+ requestHandlers.onCompleted(finalState);
121
+ lifecycleHandlers.completeActive();
122
+ return;
123
+ }
124
+ // No commands and no error - just complete
104
125
  dispatch({
105
126
  type: ExecuteActionType.ProcessingComplete,
106
127
  payload: { message: result.message },
@@ -1,5 +1,6 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Box, Text } from 'ink';
3
+ import { ComponentStatus } from '../types/components.js';
3
4
  import { FeedbackType } from '../types/types.js';
4
5
  import { getFeedbackColor } from '../services/colors.js';
5
6
  function getSymbol(type) {
@@ -12,7 +13,7 @@ function getSymbol(type) {
12
13
  }[type];
13
14
  }
14
15
  export function Feedback({ type, message }) {
15
- const color = getFeedbackColor(type, false);
16
+ const color = getFeedbackColor(type, ComponentStatus.Done);
16
17
  const symbol = getSymbol(type);
17
18
  return (_jsx(Box, { marginLeft: 1, children: _jsxs(Text, { color: color, children: [symbol, " ", message] }) }));
18
19
  }
package/dist/ui/Label.js CHANGED
@@ -3,7 +3,8 @@ import { Box, Text } from 'ink';
3
3
  import { getTaskColors, getTaskTypeLabel } from '../services/colors.js';
4
4
  import { DebugLevel } from '../configuration/types.js';
5
5
  import { Separator } from './Separator.js';
6
- export function Label({ description, taskType, showType = false, isCurrent = false, debug = DebugLevel.None, }) {
7
- const colors = getTaskColors(taskType, isCurrent);
6
+ import { ComponentStatus } from '../types/components.js';
7
+ export function Label({ description, taskType, showType = false, status = ComponentStatus.Done, debug = DebugLevel.None, }) {
8
+ const colors = getTaskColors(taskType, status);
8
9
  return (_jsxs(Box, { children: [_jsx(Text, { color: colors.description, children: description }), showType && (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsx(Text, { color: colors.type, children: getTaskTypeLabel(taskType, debug) })] }))] }));
9
10
  }
package/dist/ui/List.js CHANGED
@@ -4,7 +4,8 @@ import { Palette } from '../services/colors.js';
4
4
  import { Separator } from './Separator.js';
5
5
  export const List = ({ items, level = 0, highlightedIndex = null, highlightedParentIndex = null, showType = false, }) => {
6
6
  const marginLeft = level > 0 ? 2 : 0;
7
- return (_jsx(Box, { flexDirection: "column", marginLeft: marginLeft, children: items.map((item, index) => {
7
+ const gap = level === 0 ? 1 : 0;
8
+ return (_jsx(Box, { flexDirection: "column", marginLeft: marginLeft, gap: gap, children: items.map((item, index) => {
8
9
  // At level 0, track which parent is active for child highlighting
9
10
  // At level > 0, only highlight if this parent is the active one
10
11
  const shouldHighlightChildren = level === 0 ? highlightedParentIndex === index : false;
@@ -23,6 +24,6 @@ export const List = ({ items, level = 0, highlightedIndex = null, highlightedPar
23
24
  (isHighlighted && item.type.highlightedColor
24
25
  ? item.type.highlightedColor
25
26
  : Palette.White);
26
- return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: markerColor, children: marker }), _jsx(Text, { color: descriptionColor, children: item.description.text }), showType && (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsx(Text, { color: typeColor, children: item.type.text })] }))] }), item.children && item.children.length > 0 && (_jsx(List, { items: item.children, level: level + 1, highlightedIndex: shouldHighlightChildren ? highlightedIndex : null, showType: showType }))] }, index));
27
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: markerColor, children: marker }), _jsx(Text, { color: descriptionColor, children: item.description.text }), showType && (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsx(Text, { color: typeColor, children: item.type.text })] }))] }), item.children && item.children.length > 0 && (_jsx(Box, { marginTop: 1, children: _jsx(List, { items: item.children, level: level + 1, highlightedIndex: shouldHighlightChildren ? highlightedIndex : null, showType: showType }) }))] }, index));
27
28
  }) }));
28
29
  };
@@ -3,17 +3,22 @@ import { useEffect, useState } from 'react';
3
3
  import { Box } from 'ink';
4
4
  import { ComponentStatus, } from '../types/components.js';
5
5
  import { TaskType } from '../types/types.js';
6
- import { getTaskColors, getTaskTypeLabel } from '../services/colors.js';
6
+ import { getTaskColors, getTaskTypeLabel, Palette, } from '../services/colors.js';
7
7
  import { DebugLevel } from '../configuration/types.js';
8
8
  import { useInput } from '../services/keyboard.js';
9
9
  import { Label } from './Label.js';
10
10
  import { List } from './List.js';
11
- function taskToListItem(task, highlightedChildIndex = null, isDefineTaskWithoutSelection = false, isCurrent = false, debug = DebugLevel.None) {
12
- const taskColors = getTaskColors(task.type, isCurrent);
11
+ export function taskToListItem(task, highlightedChildIndex = null, isDefineTaskWithoutSelection = false, status = ComponentStatus.Done, debug = DebugLevel.None) {
12
+ const taskColors = getTaskColors(task.type, status);
13
+ // Determine description color based on status
14
+ let descriptionColor = taskColors.description;
15
+ if (status === ComponentStatus.Pending) {
16
+ descriptionColor = Palette.SoftWhite;
17
+ }
13
18
  const item = {
14
19
  description: {
15
20
  text: task.action,
16
- color: taskColors.description,
21
+ color: descriptionColor,
17
22
  },
18
23
  type: { text: getTaskTypeLabel(task.type, debug), color: taskColors.type },
19
24
  children: [],
@@ -21,7 +26,7 @@ function taskToListItem(task, highlightedChildIndex = null, isDefineTaskWithoutS
21
26
  // Mark define tasks with right arrow when no selection has been made
22
27
  if (isDefineTaskWithoutSelection) {
23
28
  item.marker = ' → ';
24
- item.markerColor = getTaskColors(TaskType.Schedule, isCurrent).type;
29
+ item.markerColor = getTaskColors(TaskType.Schedule, status).type;
25
30
  }
26
31
  // Add children for Define tasks with options
27
32
  if (task.type === TaskType.Define && Array.isArray(task.params?.options)) {
@@ -33,8 +38,8 @@ function taskToListItem(task, highlightedChildIndex = null, isDefineTaskWithoutS
33
38
  childType =
34
39
  index === highlightedChildIndex ? TaskType.Execute : TaskType.Discard;
35
40
  }
36
- const colors = getTaskColors(childType, isCurrent);
37
- const planColors = getTaskColors(TaskType.Schedule, isCurrent);
41
+ const colors = getTaskColors(childType, status);
42
+ const planColors = getTaskColors(TaskType.Schedule, status);
38
43
  return {
39
44
  description: {
40
45
  text: option,
@@ -56,11 +61,11 @@ function taskToListItem(task, highlightedChildIndex = null, isDefineTaskWithoutS
56
61
  Array.isArray(scheduledTask.subtasks) &&
57
62
  scheduledTask.subtasks.length > 0) {
58
63
  item.children = scheduledTask.subtasks.map((subtask) => {
59
- const subtaskColors = getTaskColors(subtask.type, isCurrent);
64
+ const subtaskColors = getTaskColors(subtask.type, status);
60
65
  return {
61
66
  description: {
62
67
  text: subtask.action,
63
- color: subtaskColors.description,
68
+ color: Palette.AshGray,
64
69
  },
65
70
  type: {
66
71
  text: getTaskTypeLabel(subtask.type, debug),
@@ -107,9 +112,9 @@ export const ScheduleView = ({ message, tasks, state, status, debug = DebugLevel
107
112
  defineGroupIndex === currentDefineGroupIndex &&
108
113
  highlightedIndex === null &&
109
114
  isActive;
110
- return taskToListItem(task, childIndex, isDefineWithoutSelection, isActive, debug);
115
+ return taskToListItem(task, childIndex, isDefineWithoutSelection, status, debug);
111
116
  });
112
- return (_jsxs(Box, { flexDirection: "column", children: [message && (_jsx(Box, { marginBottom: 1, marginLeft: 1, children: _jsx(Label, { description: message, taskType: TaskType.Schedule, showType: debug !== DebugLevel.None, isCurrent: isActive, debug: debug }) })), _jsx(Box, { marginLeft: 1, children: _jsx(List, { items: listItems, highlightedIndex: currentDefineTaskIndex >= 0 ? highlightedIndex : null, highlightedParentIndex: currentDefineTaskIndex, showType: debug !== DebugLevel.None }) })] }));
117
+ return (_jsxs(Box, { flexDirection: "column", children: [message && (_jsx(Box, { marginBottom: 1, marginLeft: 1, children: _jsx(Label, { description: message, taskType: TaskType.Schedule, showType: debug !== DebugLevel.None, status: status, debug: debug }) })), _jsx(Box, { marginLeft: 1, children: _jsx(List, { items: listItems, highlightedIndex: currentDefineTaskIndex >= 0 ? highlightedIndex : null, highlightedParentIndex: currentDefineTaskIndex, showType: debug !== DebugLevel.None }) })] }));
113
118
  };
114
119
  /**
115
120
  * Schedule controller: Manages task selection and navigation
@@ -47,12 +47,9 @@ export const Workflow = ({ initialQueue, debug }) => {
47
47
  },
48
48
  onAborted: (operation) => {
49
49
  moveActiveToTimeline();
50
- // Add feedback to queue
50
+ // Clear queue and add only feedback to prevent subsequent components from executing
51
51
  const message = getCancellationMessage(operation);
52
- setQueue((queue) => [
53
- ...queue,
54
- createFeedback(FeedbackType.Aborted, message),
55
- ]);
52
+ setQueue([createFeedback(FeedbackType.Aborted, message)]);
56
53
  },
57
54
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
58
55
  onCompleted: (finalState) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prompt-language-shell",
3
- "version": "0.8.6",
3
+ "version": "0.8.8",
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",