prompt-language-shell 0.7.8 → 0.8.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.
@@ -83,12 +83,17 @@ settings", type "configure", params { query: "app" }
83
83
 
84
84
  Before creating tasks, evaluate the request type:
85
85
 
86
- 1. **Information requests** (questions) - Use question keywords:
86
+ 1. **Introspection requests** - User asks about your capabilities:
87
+ - "list your skills", "what can you do", "flex", "show off", "list
88
+ capabilities", "show skills"
89
+ - Example: "flex" → introspect type
90
+
91
+ 2. **Information requests** (questions) - Use question keywords:
87
92
  - "explain", "describe", "tell me", "what is", "how does", "find",
88
93
  "search"
89
94
  - Example: "explain docker" → answer type
90
95
 
91
- 2. **Action requests** (commands) - Must match available skills:
96
+ 3. **Action requests** (commands) - Must match available skills:
92
97
  - Action verbs like "compile", "deploy", "process", "validate"
93
98
  - If verb matches a skill → extract skill steps as subtasks
94
99
  - If verb does NOT match any skill → ignore type with action
@@ -98,7 +103,7 @@ Before creating tasks, evaluate the request type:
98
103
  - Example: "validate" with no skill → action "Ignore unknown
99
104
  'validate' request"
100
105
 
101
- 3. **Vague/ambiguous requests** without clear verb:
106
+ 4. **Vague/ambiguous requests** without clear verb:
102
107
  - Phrases like "do something", "handle it" → ignore type
103
108
  - Action format: "Ignore unknown 'X' request" where X is the phrase
104
109
 
@@ -1,6 +1,6 @@
1
1
  export const introspectTool = {
2
2
  name: 'introspect',
3
- description: 'Execute a task with type "introspect" to list available capabilities and skills. Called after SCHEDULE has identified an introspection request and user has confirmed. Takes the task action and optional filter parameter to present built-in capabilities and user-defined skills.',
3
+ description: 'Execute a task with type "introspect" to list available capabilities and skills. Called after SCHEDULE has identified an introspection request and user has confirmed. Takes the task action and optional filter parameter to present system capabilities and user-provided skills.',
4
4
  input_schema: {
5
5
  type: 'object',
6
6
  properties: {
@@ -8,25 +8,34 @@ export const introspectTool = {
8
8
  type: 'string',
9
9
  description: 'Introductory reply to display before the capabilities list. Must be a single sentence, maximum 64 characters (including the colon at the end). Vary this naturally - try to use a different phrase each time. Always end with a colon.',
10
10
  },
11
- tasks: {
11
+ capabilities: {
12
12
  type: 'array',
13
- description: 'Array of capabilities, each with type "introspect". Include built-in capabilities (SCHEDULE, INTROSPECT, ANSWER, EXECUTE, REPORT, CONFIGURE) and user-defined skills from the Available Skills section.',
13
+ description: 'Array of capabilities and skills. Include system capabilities (Introspect, Configure, Answer, Execute) with origin "system", meta workflow capabilities (Schedule, Validate, Report) with origin "meta", and user-provided skills from the Available Skills section with origin "user".',
14
14
  items: {
15
15
  type: 'object',
16
16
  properties: {
17
- action: {
17
+ name: {
18
18
  type: 'string',
19
- description: 'Capability name and description. Format: "NAME: Brief description". Maximum 64 characters. Examples: "SCHEDULE: Break down requests into steps", "Deploy App: Build and deploy application".',
19
+ description: 'Capability or skill name. Use title case. Maximum 32 characters. Examples: "Execute", "Deploy Application", "Process Data".',
20
20
  },
21
- type: {
21
+ description: {
22
22
  type: 'string',
23
- description: 'Always "introspect" for capability listings.',
23
+ description: 'Brief description of what this capability does. Start with lowercase letter, no ending punctuation. Maximum 64 characters. Examples: "run shell commands and operations", "build and deploy to production".',
24
+ },
25
+ origin: {
26
+ type: 'string',
27
+ enum: ['system', 'user', 'meta'],
28
+ description: 'Origin of the capability. Use "system" for system capabilities (Introspect, Configure, Answer, Execute), "meta" for meta workflow capabilities (Schedule, Validate, Report), and "user" for user-provided skills.',
29
+ },
30
+ isIncomplete: {
31
+ type: 'boolean',
32
+ description: 'Optional. Set to true if the skill is marked as incomplete.',
24
33
  },
25
34
  },
26
- required: ['action', 'type'],
35
+ required: ['name', 'description', 'origin'],
27
36
  },
28
37
  },
29
38
  },
30
- required: ['message', 'tasks'],
39
+ required: ['message', 'capabilities'],
31
40
  },
32
41
  };
@@ -0,0 +1,25 @@
1
+ import { TaskType } from './types.js';
2
+ /**
3
+ * Type guard to check if a task is a ScheduledTask
4
+ * ScheduledTask has optional subtasks property or is a Group type
5
+ */
6
+ export function isScheduledTask(task) {
7
+ return 'subtasks' in task || task.type === TaskType.Group;
8
+ }
9
+ /**
10
+ * Type-safe conversion of Task array to ScheduledTask array
11
+ * This is safe because Tasks can be treated as ScheduledTask when checking for Groups
12
+ */
13
+ export function asScheduledTasks(tasks) {
14
+ return tasks;
15
+ }
16
+ /**
17
+ * Type guard to check if a value is a valid Task
18
+ */
19
+ export function isTask(value) {
20
+ return (typeof value === 'object' &&
21
+ value !== null &&
22
+ 'action' in value &&
23
+ typeof value.action === 'string' &&
24
+ 'type' in value);
25
+ }
@@ -29,6 +29,12 @@ export var TaskType;
29
29
  TaskType["Discard"] = "discard";
30
30
  TaskType["Group"] = "group";
31
31
  })(TaskType || (TaskType = {}));
32
+ export var Origin;
33
+ (function (Origin) {
34
+ Origin["BuiltIn"] = "system";
35
+ Origin["UserProvided"] = "user";
36
+ Origin["Indirect"] = "meta";
37
+ })(Origin || (Origin = {}));
32
38
  export var FeedbackType;
33
39
  (function (FeedbackType) {
34
40
  FeedbackType["Info"] = "info";
package/dist/ui/Answer.js CHANGED
@@ -23,11 +23,6 @@ export function Answer({ question, state, status, service, handlers, }) {
23
23
  if (!isActive) {
24
24
  return;
25
25
  }
26
- // Skip processing if no service available
27
- if (!service) {
28
- setError('No service available');
29
- return;
30
- }
31
26
  let mounted = true;
32
27
  async function process(svc) {
33
28
  try {
@@ -40,7 +35,9 @@ export function Answer({ question, state, status, service, handlers, }) {
40
35
  const answerText = result.answer || '';
41
36
  setAnswer(answerText);
42
37
  // Update component state so answer persists in timeline
43
- handlers?.updateState({ answer: answerText });
38
+ handlers?.updateState({
39
+ answer: answerText,
40
+ });
44
41
  // Signal completion
45
42
  handlers?.completeActive();
46
43
  }
@@ -49,11 +46,14 @@ export function Answer({ question, state, status, service, handlers, }) {
49
46
  if (mounted) {
50
47
  const errorMessage = formatErrorMessage(err);
51
48
  setError(errorMessage);
49
+ handlers?.updateState({
50
+ error: errorMessage,
51
+ });
52
52
  handlers?.onError(errorMessage);
53
53
  }
54
54
  }
55
55
  }
56
- process(service);
56
+ void process(service);
57
57
  return () => {
58
58
  mounted = false;
59
59
  };
@@ -1,12 +1,12 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useEffect, useState } from 'react';
3
3
  import { Box, Text } from 'ink';
4
- import { ComponentStatus, } from '../types/components.js';
4
+ import { ComponentStatus } from '../types/components.js';
5
5
  import { TaskType } from '../types/types.js';
6
6
  import { Colors } from '../services/colors.js';
7
7
  import { addDebugToTimeline, createScheduleDefinition, } from '../services/components.js';
8
- import { formatErrorMessage } from '../services/messages.js';
9
8
  import { useInput } from '../services/keyboard.js';
9
+ import { formatErrorMessage } from '../services/messages.js';
10
10
  import { handleRefinement } from '../services/refinement.js';
11
11
  import { routeTasksWithConfirm } from '../services/router.js';
12
12
  import { ensureMinimumTime } from '../services/timing.js';
@@ -27,11 +27,6 @@ export function Command({ command, state, status, service, handlers, onAborted,
27
27
  if (!isActive) {
28
28
  return;
29
29
  }
30
- // Skip processing if no service available
31
- if (!service) {
32
- setError('No service available');
33
- return;
34
- }
35
30
  let mounted = true;
36
31
  async function process(svc) {
37
32
  const startTime = Date.now();
@@ -64,6 +59,9 @@ export function Command({ command, state, status, service, handlers, onAborted,
64
59
  });
65
60
  // Check if tasks contain DEFINE type (variant selection needed)
66
61
  const hasDefineTask = result.tasks.some((task) => task.type === TaskType.Define);
62
+ if (!handlers) {
63
+ return;
64
+ }
67
65
  // Create Schedule definition
68
66
  const scheduleDefinition = createScheduleDefinition(result.message, result.tasks, hasDefineTask
69
67
  ? async (selectedTasks) => {
@@ -73,12 +71,12 @@ export function Command({ command, state, status, service, handlers, onAborted,
73
71
  : undefined);
74
72
  if (hasDefineTask) {
75
73
  // DEFINE tasks: Move Command to timeline, add Schedule to queue
76
- handlers?.completeActive();
77
- handlers?.addToQueue(scheduleDefinition);
74
+ handlers.completeActive();
75
+ handlers.addToQueue(scheduleDefinition);
78
76
  }
79
77
  else {
80
78
  // No DEFINE tasks: Complete Command, then route to Confirm flow
81
- handlers?.completeActive();
79
+ handlers.completeActive();
82
80
  routeTasksWithConfirm(result.tasks, result.message, svc, command, handlers, false);
83
81
  }
84
82
  }
@@ -88,11 +86,14 @@ export function Command({ command, state, status, service, handlers, onAborted,
88
86
  if (mounted) {
89
87
  const errorMessage = formatErrorMessage(err);
90
88
  setError(errorMessage);
89
+ handlers?.updateState({
90
+ error: errorMessage,
91
+ });
91
92
  handlers?.onError(errorMessage);
92
93
  }
93
94
  }
94
95
  }
95
- process(service);
96
+ void process(service);
96
97
  return () => {
97
98
  mounted = false;
98
99
  };
@@ -3,16 +3,16 @@ import { memo } from 'react';
3
3
  import { ComponentName } from '../types/types.js';
4
4
  import { Answer } from './Answer.js';
5
5
  import { Command } from './Command.js';
6
- import { Confirm } from './Confirm.js';
7
6
  import { Config } from './Config.js';
7
+ import { Confirm } from './Confirm.js';
8
8
  import { Debug } from './Debug.js';
9
9
  import { Execute } from './Execute.js';
10
10
  import { Feedback } from './Feedback.js';
11
11
  import { Introspect } from './Introspect.js';
12
12
  import { Message } from './Message.js';
13
- import { Schedule } from './Schedule.js';
14
13
  import { Refinement } from './Refinement.js';
15
14
  import { Report } from './Report.js';
15
+ import { Schedule } from './Schedule.js';
16
16
  import { Validate } from './Validate.js';
17
17
  import { Welcome } from './Welcome.js';
18
18
  export const Component = memo(function Component({ def, debug, }) {
package/dist/ui/Config.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState } from 'react';
2
+ import { useEffect, useState } from 'react';
3
3
  import { Box, Text, useFocus } from 'ink';
4
4
  import TextInput from 'ink-text-input';
5
5
  import { ComponentStatus } from '../types/components.js';
@@ -8,6 +8,19 @@ import { Colors } from '../services/colors.js';
8
8
  import { createFeedback } from '../services/components.js';
9
9
  import { DebugLevel } from '../services/configuration.js';
10
10
  import { useInput } from '../services/keyboard.js';
11
+ /**
12
+ * Get postfix with debug brackets if debug is enabled
13
+ * Info: {key} | Verbose: {key} entry
14
+ */
15
+ function getPostfix(text, debugLevel) {
16
+ if (debugLevel === DebugLevel.None || !text) {
17
+ return '';
18
+ }
19
+ if (debugLevel === DebugLevel.Info) {
20
+ return `{${text}}`;
21
+ }
22
+ return `{${text}} entry`;
23
+ }
11
24
  export var StepType;
12
25
  (function (StepType) {
13
26
  StepType["Text"] = "text";
@@ -17,6 +30,10 @@ function TextStep({ value, placeholder, validate, onChange, onSubmit, }) {
17
30
  const [inputValue, setInputValue] = useState(value);
18
31
  const [validationFailed, setValidationFailed] = useState(false);
19
32
  const { isFocused } = useFocus({ autoFocus: true });
33
+ // Sync internal state with prop changes
34
+ useEffect(() => {
35
+ setInputValue(value);
36
+ }, [value]);
20
37
  const handleChange = (newValue) => {
21
38
  setInputValue(newValue);
22
39
  onChange(newValue);
@@ -85,15 +102,27 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
85
102
  stepConfig.options[stepConfig.defaultIndex].value;
86
103
  break;
87
104
  default: {
88
- const exhaustiveCheck = stepConfig;
89
- throw new Error(`Unsupported step type: ${exhaustiveCheck}`);
105
+ const _exhaustiveCheck = stepConfig;
106
+ throw new Error('Unsupported step type');
90
107
  }
91
108
  }
92
109
  });
93
110
  return initial;
94
111
  });
95
- const [inputValue, setInputValue] = useState('');
112
+ const [inputValue, setInputValue] = useState(() => {
113
+ // Initialize with the current step's value if available
114
+ if (isActive && step < steps.length) {
115
+ const stepConfig = steps[step];
116
+ const configKey = stepConfig.path || stepConfig.key;
117
+ return values[configKey] || '';
118
+ }
119
+ return '';
120
+ });
96
121
  const [selectedIndex, setSelectedIndex] = useState(() => {
122
+ // If not active, use saved state
123
+ if (!isActive && state?.selectedIndex !== undefined) {
124
+ return state.selectedIndex;
125
+ }
97
126
  // Initialize selectedIndex based on current step's defaultIndex
98
127
  if (isActive &&
99
128
  step < steps.length &&
@@ -108,35 +137,43 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
108
137
  }
109
138
  return value.replace(/\n/g, '').trim();
110
139
  };
140
+ // Update inputValue when step changes
141
+ useEffect(() => {
142
+ if (isActive && step < steps.length) {
143
+ const stepConfig = steps[step];
144
+ const configKey = stepConfig.path || stepConfig.key;
145
+ const value = values[configKey] || '';
146
+ setInputValue(value);
147
+ }
148
+ }, [step, isActive, steps]);
111
149
  useInput((_, key) => {
112
150
  if (!isActive || step >= steps.length)
113
151
  return;
114
152
  const currentStepConfig = steps[step];
115
153
  if (key.escape) {
116
154
  // Save current value before aborting
117
- if (currentStepConfig) {
118
- const configKey = currentStepConfig.path || currentStepConfig.key;
119
- let currentValue = '';
120
- switch (currentStepConfig.type) {
121
- case StepType.Text:
122
- currentValue = inputValue || values[configKey] || '';
123
- break;
124
- case StepType.Selection:
125
- currentValue = values[configKey] || '';
126
- break;
127
- default: {
128
- const exhaustiveCheck = currentStepConfig;
129
- throw new Error(`Unsupported step type: ${exhaustiveCheck}`);
130
- }
131
- }
132
- if (currentValue) {
133
- setValues({ ...values, [configKey]: currentValue });
155
+ const configKey = currentStepConfig.path || currentStepConfig.key;
156
+ let currentValue = '';
157
+ switch (currentStepConfig.type) {
158
+ case StepType.Text:
159
+ currentValue = inputValue || values[configKey] || '';
160
+ break;
161
+ case StepType.Selection:
162
+ currentValue = values[configKey] || '';
163
+ break;
164
+ default: {
165
+ const _exhaustiveCheck = currentStepConfig;
166
+ throw new Error('Unsupported step type');
134
167
  }
135
168
  }
169
+ if (currentValue) {
170
+ setValues({ ...values, [configKey]: currentValue });
171
+ }
136
172
  // Save state before aborting
137
173
  handlers?.updateState({
138
174
  values,
139
175
  completedStep: step,
176
+ selectedIndex,
140
177
  });
141
178
  if (onAborted) {
142
179
  onAborted('configuration');
@@ -177,8 +214,8 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
177
214
  break;
178
215
  }
179
216
  default: {
180
- const exhaustiveCheck = currentStepConfig;
181
- throw new Error(`Unsupported step type: ${exhaustiveCheck}`);
217
+ const _exhaustiveCheck = currentStepConfig;
218
+ throw new Error('Unsupported step type');
182
219
  }
183
220
  }
184
221
  // Don't allow empty or invalid value
@@ -197,6 +234,7 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
197
234
  const stateUpdate = {
198
235
  values: newValues,
199
236
  completedStep: steps.length,
237
+ selectedIndex,
200
238
  };
201
239
  handlers?.updateState(stateUpdate);
202
240
  // Call onFinished callback and handle result
@@ -219,6 +257,7 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
219
257
  const stateUpdate = {
220
258
  values: newValues,
221
259
  completedStep: step + 1,
260
+ selectedIndex,
222
261
  };
223
262
  handlers?.updateState(stateUpdate);
224
263
  const nextStep = step + 1;
@@ -249,8 +288,8 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
249
288
  return (_jsx(SelectionStep, { options: stepConfig.options, selectedIndex: selectedIndex, isCurrentStep: true }));
250
289
  }
251
290
  default: {
252
- const exhaustiveCheck = stepConfig;
253
- throw new Error(`Unsupported step type: ${exhaustiveCheck}`);
291
+ const _exhaustiveCheck = stepConfig;
292
+ throw new Error('Unsupported step type');
254
293
  }
255
294
  }
256
295
  };
@@ -262,6 +301,7 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
262
301
  if (!shouldShow) {
263
302
  return null;
264
303
  }
265
- return (_jsxs(Box, { flexDirection: "column", marginTop: index === 0 ? 0 : 1, children: [_jsxs(Box, { children: [_jsx(Text, { children: stepConfig.description }), _jsx(Text, { children: ": " }), debug !== DebugLevel.None && stepConfig.path && (_jsxs(Text, { color: Colors.Type.Define, children: ['{', stepConfig.path, '}'] }))] }), _jsxs(Box, { children: [_jsx(Text, { children: " " }), _jsx(Text, { color: Colors.Action.Select, dimColor: !isCurrentStep, children: ">" }), _jsx(Text, { children: " " }), renderStepInput(stepConfig, isCurrentStep)] })] }, stepConfig.path || stepConfig.key));
304
+ const postfix = getPostfix(stepConfig.path, debug);
305
+ return (_jsxs(Box, { flexDirection: "column", marginTop: index === 0 ? 0 : 1, children: [_jsxs(Box, { children: [_jsx(Text, { children: stepConfig.description }), _jsx(Text, { children: ": " }), postfix && _jsx(Text, { color: Colors.Type.Config, children: postfix })] }), _jsxs(Box, { children: [_jsx(Text, { children: " " }), _jsx(Text, { color: Colors.Action.Select, dimColor: !isCurrentStep, children: ">" }), _jsx(Text, { children: " " }), renderStepInput(stepConfig, isCurrentStep)] })] }, stepConfig.path || stepConfig.key));
266
306
  }) }));
267
307
  }
@@ -1,8 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState } from 'react';
3
3
  import { Box, Text } from 'ink';
4
- import { ComponentStatus, } from '../types/components.js';
5
- import { Colors, Palette } from '../services/colors.js';
4
+ import { ComponentStatus } from '../types/components.js';
5
+ import { Colors, getTextColor, Palette } from '../services/colors.js';
6
6
  import { useInput } from '../services/keyboard.js';
7
7
  import { UserQuery } from './UserQuery.js';
8
8
  export function Confirm({ message, state, status, handlers, onConfirmed, onCancelled, }) {
@@ -15,7 +15,7 @@ export function Confirm({ message, state, status, handlers, onConfirmed, onCance
15
15
  // Escape: highlight "No" and cancel
16
16
  setSelectedIndex(1);
17
17
  handlers?.updateState({ selectedIndex: 1 });
18
- onCancelled?.();
18
+ onCancelled();
19
19
  }
20
20
  else if (key.tab) {
21
21
  // Toggle between Yes (0) and No (1)
@@ -27,10 +27,10 @@ export function Confirm({ message, state, status, handlers, onConfirmed, onCance
27
27
  // Confirm selection
28
28
  handlers?.updateState({ selectedIndex, confirmed: true });
29
29
  if (selectedIndex === 0) {
30
- onConfirmed?.();
30
+ onConfirmed();
31
31
  }
32
32
  else {
33
- onCancelled?.();
33
+ onCancelled();
34
34
  }
35
35
  }
36
36
  }, { isActive });
@@ -42,7 +42,7 @@ export function Confirm({ message, state, status, handlers, onConfirmed, onCance
42
42
  // When done, show both the message and user's choice in timeline
43
43
  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] })] }));
44
44
  }
45
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, marginLeft: 1, children: _jsx(Text, { color: isActive ? Colors.Text.Active : Colors.Text.Inactive, children: message }) }), _jsxs(Box, { marginLeft: 1, children: [_jsx(Text, { color: Colors.Action.Select, children: ">" }), _jsx(Text, { children: " " }), _jsx(Box, { children: options.map((option, index) => {
45
+ 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) => {
46
46
  const isSelected = index === selectedIndex;
47
47
  return (_jsx(Box, { marginRight: 2, children: _jsx(Text, { color: isSelected ? option.color : undefined, dimColor: !isSelected, children: option.label }) }, option.value));
48
48
  }) })] })] }));