prompt-language-shell 0.9.2 → 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 (56) hide show
  1. package/dist/{ui/Main.js → Main.js} +12 -12
  2. package/dist/{ui → components}/Component.js +28 -26
  3. package/dist/{ui → components}/Workflow.js +2 -3
  4. package/dist/{ui → components/controllers}/Answer.js +18 -17
  5. package/dist/{ui → components/controllers}/Command.js +11 -18
  6. package/dist/{ui → components/controllers}/Config.js +8 -116
  7. package/dist/components/controllers/Confirm.js +42 -0
  8. package/dist/{ui → components/controllers}/Execute.js +75 -144
  9. package/dist/{ui → components/controllers}/Introspect.js +12 -28
  10. package/dist/components/controllers/Refinement.js +18 -0
  11. package/dist/components/controllers/Schedule.js +139 -0
  12. package/dist/{ui → components/controllers}/Validate.js +14 -32
  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 +1 -1
  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 +4 -4
  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/configuration/steps.js +1 -1
  35. package/dist/execution/handlers.js +19 -53
  36. package/dist/execution/reducer.js +26 -38
  37. package/dist/execution/runner.js +43 -25
  38. package/dist/execution/types.js +3 -4
  39. package/dist/execution/utils.js +1 -1
  40. package/dist/index.js +1 -1
  41. package/dist/services/messages.js +19 -0
  42. package/dist/services/router.js +69 -11
  43. package/dist/services/shell.js +26 -6
  44. package/dist/services/timing.js +1 -0
  45. package/dist/skills/execute.md +15 -7
  46. package/dist/tools/execute.tool.js +0 -4
  47. package/dist/types/schemas.js +0 -1
  48. package/package.json +1 -1
  49. package/dist/execution/hooks.js +0 -291
  50. package/dist/ui/Confirm.js +0 -62
  51. package/dist/ui/Refinement.js +0 -23
  52. package/dist/ui/Schedule.js +0 -257
  53. package/dist/ui/Task.js +0 -11
  54. /package/dist/{ui → components/views}/Debug.js +0 -0
  55. /package/dist/{ui → components/views}/Message.js +0 -0
  56. /package/dist/{ui → components/views}/Panel.js +0 -0
@@ -139,6 +139,18 @@ Given tasks from this skill:
139
139
  Do NOT invent different commands - use exactly what the skill specifies,
140
140
  with parameter placeholders replaced by actual values.
141
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
+
142
154
  **CRITICAL - VERBATIM EXECUTION**: Run shell commands EXACTLY as written in
143
155
  the ### Execution section. Do NOT:
144
156
  - Modify the command string in any way
@@ -180,7 +192,6 @@ Return a structured response with commands to execute:
180
192
  - **workdir**: Optional working directory for the command (defaults to
181
193
  current)
182
194
  - **timeout**: Optional timeout in milliseconds (defaults to 30000)
183
- - **critical**: Whether failure should stop execution (defaults to true)
184
195
 
185
196
  ## Command Generation Guidelines
186
197
 
@@ -364,14 +375,12 @@ commands:
364
375
 
365
376
  For complex multi-step operations:
366
377
 
367
- 1. **Sequential dependencies**: Mark early commands as critical so failure
368
- stops the chain
378
+ 1. **Sequential dependencies**: Commands execute in order; any failure stops
379
+ the chain
369
380
  2. **Long-running processes**: Set appropriate timeouts (build processes
370
381
  may need 10+ minutes)
371
382
  3. **Working directories**: Use workdir to ensure commands run in the
372
383
  right location
373
- 4. **Error handling**: For non-critical cleanup steps, set critical:
374
- false
375
384
 
376
385
  ## Handling Config Placeholders
377
386
 
@@ -444,8 +453,7 @@ Before returning commands:
444
453
  7. Check that all task params are incorporated
445
454
  8. Ensure paths are properly quoted
446
455
  9. Confirm timeouts are reasonable for each operation
447
- 10. Validate that critical flags are set appropriately
448
- 11. Review for any safety concerns
456
+ 10. Review for any safety concerns
449
457
 
450
458
  ## Confirmed Schedule
451
459
 
@@ -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.2",
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",
@@ -1,291 +0,0 @@
1
- import { useCallback, useEffect, useRef, useState, } from 'react';
2
- import { ComponentStatus, } from '../types/components.js';
3
- import { FeedbackType } from '../types/types.js';
4
- import { createFeedback, createMessage } from '../services/components.js';
5
- import { formatErrorMessage, getExecutionErrorMessage, } from '../services/messages.js';
6
- import { ExecutionStatus } from '../services/shell.js';
7
- import { ensureMinimumTime } from '../services/timing.js';
8
- import { handleTaskCompletion, handleTaskFailure } from './handlers.js';
9
- import { processTasks } from './processing.js';
10
- import { executeTask } from './runner.js';
11
- import { ExecuteActionType } from './types.js';
12
- import { getCurrentTaskIndex } from './utils.js';
13
- const ELAPSED_UPDATE_INTERVAL = 1000;
14
- /**
15
- * Track elapsed time from a start timestamp.
16
- * Returns 0 when not active or no start time.
17
- */
18
- export function useElapsedTimer(startTime, isActive) {
19
- const [elapsed, setElapsed] = useState(0);
20
- useEffect(() => {
21
- if (!startTime || !isActive)
22
- return;
23
- const interval = setInterval(() => {
24
- setElapsed(Date.now() - startTime);
25
- }, ELAPSED_UPDATE_INTERVAL);
26
- return () => {
27
- clearInterval(interval);
28
- };
29
- }, [startTime, isActive]);
30
- return elapsed;
31
- }
32
- /**
33
- * Manage live output and timing for the currently executing task.
34
- * Groups related state for tracking a running task's output.
35
- */
36
- export function useLiveTaskOutput() {
37
- const [output, setOutput] = useState({
38
- stdout: '',
39
- stderr: '',
40
- error: '',
41
- });
42
- const [startTime, setStartTime] = useState(null);
43
- const start = useCallback(() => {
44
- setOutput({ stdout: '', stderr: '', error: '' });
45
- setStartTime(Date.now());
46
- }, []);
47
- const stop = useCallback(() => {
48
- setStartTime(null);
49
- }, []);
50
- return {
51
- output,
52
- startTime,
53
- setOutput,
54
- start,
55
- stop,
56
- };
57
- }
58
- /**
59
- * Handle execution cancellation with a ref-based flag.
60
- * The ref is needed because callbacks check the current cancellation state.
61
- */
62
- export function useCancellation() {
63
- const cancelledRef = useRef(false);
64
- const cancel = useCallback(() => {
65
- cancelledRef.current = true;
66
- }, []);
67
- const reset = useCallback(() => {
68
- cancelledRef.current = false;
69
- }, []);
70
- return {
71
- cancelledRef,
72
- cancel,
73
- reset,
74
- };
75
- }
76
- const MINIMUM_PROCESSING_TIME = 400;
77
- /**
78
- * Helper to create ExecuteState with defaults
79
- */
80
- function createExecuteState(overrides = {}) {
81
- return {
82
- message: '',
83
- summary: '',
84
- tasks: [],
85
- completionMessage: null,
86
- error: null,
87
- ...overrides,
88
- };
89
- }
90
- /**
91
- * Process input tasks through AI to generate executable commands.
92
- * Handles the initial phase of task execution.
93
- */
94
- export function useTaskProcessor(config) {
95
- const { inputTasks, service, isActive, hasProcessed, tasksCount, dispatch, requestHandlers, lifecycleHandlers, workflowHandlers, } = config;
96
- useEffect(() => {
97
- if (!isActive || tasksCount > 0 || hasProcessed) {
98
- return;
99
- }
100
- let mounted = true;
101
- async function process(svc) {
102
- const startTime = Date.now();
103
- try {
104
- const result = await processTasks(inputTasks, svc);
105
- await ensureMinimumTime(startTime, MINIMUM_PROCESSING_TIME);
106
- if (!mounted)
107
- return;
108
- // Add debug components to timeline if present
109
- if (result.debug?.length) {
110
- workflowHandlers.addToTimeline(...result.debug);
111
- }
112
- if (result.commands.length === 0) {
113
- if (result.error) {
114
- const errorMessage = getExecutionErrorMessage(result.error);
115
- workflowHandlers.addToTimeline(createMessage({ text: errorMessage }, ComponentStatus.Done));
116
- requestHandlers.onCompleted(createExecuteState({ message: result.message }));
117
- lifecycleHandlers.completeActive();
118
- return;
119
- }
120
- dispatch({
121
- type: ExecuteActionType.ProcessingComplete,
122
- payload: { message: result.message },
123
- });
124
- requestHandlers.onCompleted(createExecuteState({ message: result.message }));
125
- lifecycleHandlers.completeActive();
126
- return;
127
- }
128
- // Create task infos from commands
129
- const taskInfos = result.commands.map((cmd, index) => ({
130
- label: inputTasks[index]?.action ?? cmd.description,
131
- command: cmd,
132
- status: ExecutionStatus.Pending,
133
- elapsed: 0,
134
- }));
135
- dispatch({
136
- type: ExecuteActionType.CommandsReady,
137
- payload: {
138
- message: result.message,
139
- summary: result.summary,
140
- tasks: taskInfos,
141
- },
142
- });
143
- requestHandlers.onCompleted(createExecuteState({
144
- message: result.message,
145
- summary: result.summary,
146
- tasks: taskInfos,
147
- }));
148
- }
149
- catch (err) {
150
- await ensureMinimumTime(startTime, MINIMUM_PROCESSING_TIME);
151
- if (mounted) {
152
- const errorMessage = formatErrorMessage(err);
153
- dispatch({
154
- type: ExecuteActionType.ProcessingError,
155
- payload: { error: errorMessage },
156
- });
157
- requestHandlers.onCompleted(createExecuteState({ error: errorMessage }));
158
- requestHandlers.onError(errorMessage);
159
- }
160
- }
161
- }
162
- void process(service);
163
- return () => {
164
- mounted = false;
165
- };
166
- }, [
167
- inputTasks,
168
- isActive,
169
- service,
170
- requestHandlers,
171
- lifecycleHandlers,
172
- workflowHandlers,
173
- tasksCount,
174
- hasProcessed,
175
- dispatch,
176
- ]);
177
- }
178
- /**
179
- * Execute tasks sequentially, managing state and handling completion/errors.
180
- */
181
- export function useTaskExecutor(config) {
182
- const { isActive, tasks, message, summary, error, workdir, setWorkdir, cancelledRef, liveOutput, dispatch, requestHandlers, lifecycleHandlers, workflowHandlers, } = config;
183
- const currentTaskIndex = getCurrentTaskIndex(tasks);
184
- useEffect(() => {
185
- if (!isActive ||
186
- tasks.length === 0 ||
187
- currentTaskIndex >= tasks.length ||
188
- error) {
189
- return;
190
- }
191
- const currentTask = tasks[currentTaskIndex];
192
- if (currentTask.status !== ExecutionStatus.Pending) {
193
- return;
194
- }
195
- cancelledRef.current = false;
196
- // Mark task as started (running)
197
- dispatch({
198
- type: ExecuteActionType.TaskStarted,
199
- payload: { index: currentTaskIndex },
200
- });
201
- // Reset live state for new task
202
- liveOutput.start();
203
- // Merge workdir into command
204
- const command = workdir
205
- ? { ...currentTask.command, workdir }
206
- : currentTask.command;
207
- void executeTask(command, currentTaskIndex, {
208
- onOutputChange: (output) => {
209
- if (!cancelledRef.current) {
210
- liveOutput.setOutput(output);
211
- }
212
- },
213
- onComplete: (elapsed, output) => {
214
- if (cancelledRef.current)
215
- return;
216
- liveOutput.stop();
217
- // Track working directory
218
- if (output.workdir) {
219
- setWorkdir(output.workdir);
220
- }
221
- const tasksWithOutput = tasks.map((task, i) => i === currentTaskIndex
222
- ? {
223
- ...task,
224
- stdout: output.stdout,
225
- stderr: output.stderr,
226
- error: output.error,
227
- }
228
- : task);
229
- const result = handleTaskCompletion(currentTaskIndex, elapsed, {
230
- tasks: tasksWithOutput,
231
- message,
232
- summary,
233
- });
234
- dispatch(result.action);
235
- requestHandlers.onCompleted(result.finalState);
236
- if (result.shouldComplete) {
237
- lifecycleHandlers.completeActive();
238
- }
239
- },
240
- onError: (errorMsg, elapsed, output) => {
241
- if (cancelledRef.current)
242
- return;
243
- liveOutput.stop();
244
- // Track working directory
245
- if (output.workdir) {
246
- setWorkdir(output.workdir);
247
- }
248
- const tasksWithOutput = tasks.map((task, i) => i === currentTaskIndex
249
- ? {
250
- ...task,
251
- stdout: output.stdout,
252
- stderr: output.stderr,
253
- error: output.error,
254
- }
255
- : task);
256
- const result = handleTaskFailure(currentTaskIndex, errorMsg, elapsed, {
257
- tasks: tasksWithOutput,
258
- message,
259
- summary,
260
- });
261
- dispatch(result.action);
262
- requestHandlers.onCompleted(result.finalState);
263
- if (result.action.type === ExecuteActionType.TaskErrorCritical) {
264
- const criticalErrorMessage = getExecutionErrorMessage(errorMsg);
265
- workflowHandlers.addToQueue(createFeedback({
266
- type: FeedbackType.Failed,
267
- message: criticalErrorMessage,
268
- }));
269
- }
270
- if (result.shouldComplete) {
271
- lifecycleHandlers.completeActive();
272
- }
273
- },
274
- });
275
- }, [
276
- isActive,
277
- tasks,
278
- currentTaskIndex,
279
- message,
280
- summary,
281
- error,
282
- workdir,
283
- setWorkdir,
284
- cancelledRef,
285
- liveOutput,
286
- dispatch,
287
- requestHandlers,
288
- lifecycleHandlers,
289
- workflowHandlers,
290
- ]);
291
- }
@@ -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
- }
@@ -1,23 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Box } from 'ink';
3
- import { ComponentStatus } from '../types/components.js';
4
- import { useInput } from '../services/keyboard.js';
5
- import { Message } from './Message.js';
6
- import { Spinner } from './Spinner.js';
7
- export const RefinementView = ({ text, status }) => {
8
- const isActive = status === ComponentStatus.Active;
9
- return (_jsxs(Box, { gap: 1, children: [_jsx(Message, { text: text, status: status }), isActive && _jsx(Spinner, {})] }));
10
- };
11
- /**
12
- * Refinement controller: Handles abort input
13
- */
14
- export const Refinement = ({ text, status, onAborted }) => {
15
- const isActive = status === ComponentStatus.Active;
16
- useInput((_, key) => {
17
- if (key.escape && isActive) {
18
- onAborted('plan refinement');
19
- return;
20
- }
21
- }, { isActive });
22
- return _jsx(RefinementView, { text: text, status: status });
23
- };