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.
- package/dist/{services/config-labels.js → configuration/labels.js} +1 -1
- package/dist/configuration/schema.js +2 -2
- package/dist/configuration/steps.js +171 -0
- package/dist/configuration/transformation.js +17 -0
- package/dist/execution/handlers.js +1 -7
- package/dist/execution/hooks.js +291 -0
- package/dist/execution/processing.js +3 -1
- package/dist/execution/reducer.js +14 -12
- package/dist/execution/runner.js +81 -0
- package/dist/execution/types.js +1 -0
- package/dist/execution/utils.js +22 -0
- package/dist/services/components.js +109 -394
- package/dist/services/logger.js +3 -3
- package/dist/services/refinement.js +5 -2
- package/dist/services/router.js +69 -46
- package/dist/skills/execute.md +28 -10
- package/dist/ui/Command.js +11 -7
- package/dist/ui/Component.js +5 -2
- package/dist/ui/Config.js +9 -3
- package/dist/ui/Execute.js +211 -148
- package/dist/ui/Introspect.js +13 -14
- package/dist/ui/List.js +2 -2
- package/dist/ui/Main.js +14 -7
- package/dist/ui/Schedule.js +3 -1
- package/dist/ui/Subtask.js +6 -3
- package/dist/ui/Task.js +7 -171
- package/dist/ui/Validate.js +26 -21
- package/dist/ui/Workflow.js +21 -4
- package/package.json +1 -1
- package/dist/parser.js +0 -13
- package/dist/services/config-utils.js +0 -20
package/dist/ui/Task.js
CHANGED
|
@@ -1,175 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useRef, useState } from 'react';
|
|
3
2
|
import { Box } from 'ink';
|
|
4
|
-
import { ExecutionResult, ExecutionStatus, executeCommand, setOutputCallback, } from '../services/shell.js';
|
|
5
|
-
import { calculateElapsed } from '../services/utils.js';
|
|
6
3
|
import { Output } from './Output.js';
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const [stdout, setStdout] = useState(initialOutput?.stdout ?? '');
|
|
15
|
-
const [stderr, setStderr] = useState(initialOutput?.stderr ?? '');
|
|
16
|
-
const [error, setError] = useState(initialOutput?.error ?? '');
|
|
17
|
-
// Refs to track current output for callbacks (avoid stale closure)
|
|
18
|
-
const stdoutRef = useRef(stdout);
|
|
19
|
-
const stderrRef = useRef(stderr);
|
|
20
|
-
const errorRef = useRef(error);
|
|
21
|
-
stdoutRef.current = stdout;
|
|
22
|
-
stderrRef.current = stderr;
|
|
23
|
-
errorRef.current = error;
|
|
24
|
-
// Refs for callbacks to avoid stale closures in async effects
|
|
25
|
-
const onOutputChangeRef = useRef(onOutputChange);
|
|
26
|
-
const onCompleteRef = useRef(onComplete);
|
|
27
|
-
const onErrorRef = useRef(onError);
|
|
28
|
-
const onAbortRef = useRef(onAbort);
|
|
29
|
-
onOutputChangeRef.current = onOutputChange;
|
|
30
|
-
onCompleteRef.current = onComplete;
|
|
31
|
-
onErrorRef.current = onError;
|
|
32
|
-
onAbortRef.current = onAbort;
|
|
33
|
-
// Update elapsed time while running
|
|
34
|
-
useEffect(() => {
|
|
35
|
-
if (status !== ExecutionStatus.Running || !startTime)
|
|
36
|
-
return;
|
|
37
|
-
const interval = setInterval(() => {
|
|
38
|
-
setCurrentElapsed((prev) => {
|
|
39
|
-
const next = Date.now() - startTime;
|
|
40
|
-
return next !== prev ? next : prev;
|
|
41
|
-
});
|
|
42
|
-
}, 1000);
|
|
43
|
-
return () => {
|
|
44
|
-
clearInterval(interval);
|
|
45
|
-
};
|
|
46
|
-
}, [status, startTime]);
|
|
47
|
-
// Execute command when becoming active
|
|
48
|
-
useEffect(() => {
|
|
49
|
-
// Don't execute if task is cancelled or if not active
|
|
50
|
-
if (!isActive ||
|
|
51
|
-
status === ExecutionStatus.Cancelled ||
|
|
52
|
-
status !== ExecutionStatus.Pending) {
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
let mounted = true;
|
|
56
|
-
async function execute() {
|
|
57
|
-
const start = Date.now();
|
|
58
|
-
setStatus(ExecutionStatus.Running);
|
|
59
|
-
setStartTime(start);
|
|
60
|
-
setCurrentElapsed(0);
|
|
61
|
-
setStdout('');
|
|
62
|
-
setStderr('');
|
|
63
|
-
setError('');
|
|
64
|
-
// Set up output callback to capture real-time output
|
|
65
|
-
setOutputCallback((data, stream) => {
|
|
66
|
-
if (!mounted)
|
|
67
|
-
return;
|
|
68
|
-
if (stream === 'stdout') {
|
|
69
|
-
setStdout((prev) => {
|
|
70
|
-
const newStdout = prev + data;
|
|
71
|
-
stdoutRef.current = newStdout;
|
|
72
|
-
// Report output change to parent using refs for current values
|
|
73
|
-
onOutputChangeRef.current?.(index, {
|
|
74
|
-
stdout: newStdout,
|
|
75
|
-
stderr: stderrRef.current,
|
|
76
|
-
error: errorRef.current,
|
|
77
|
-
});
|
|
78
|
-
return newStdout;
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
setStderr((prev) => {
|
|
83
|
-
const newStderr = prev + data;
|
|
84
|
-
stderrRef.current = newStderr;
|
|
85
|
-
// Report output change to parent using refs for current values
|
|
86
|
-
onOutputChangeRef.current?.(index, {
|
|
87
|
-
stdout: stdoutRef.current,
|
|
88
|
-
stderr: newStderr,
|
|
89
|
-
error: errorRef.current,
|
|
90
|
-
});
|
|
91
|
-
return newStderr;
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
try {
|
|
96
|
-
const output = await executeCommand(command, undefined, index);
|
|
97
|
-
setOutputCallback(undefined); // Clear callback
|
|
98
|
-
if (!mounted)
|
|
99
|
-
return;
|
|
100
|
-
const end = Date.now();
|
|
101
|
-
setEndTime(end);
|
|
102
|
-
const taskDuration = calculateElapsed(start);
|
|
103
|
-
setElapsed(taskDuration);
|
|
104
|
-
setStatus(output.result === ExecutionResult.Success
|
|
105
|
-
? ExecutionStatus.Success
|
|
106
|
-
: ExecutionStatus.Failed);
|
|
107
|
-
if (output.result === ExecutionResult.Success) {
|
|
108
|
-
const taskOutput = {
|
|
109
|
-
stdout: output.output,
|
|
110
|
-
stderr: output.errors,
|
|
111
|
-
error: '',
|
|
112
|
-
workdir: output.workdir,
|
|
113
|
-
};
|
|
114
|
-
onCompleteRef.current?.(index, taskDuration, taskOutput);
|
|
115
|
-
}
|
|
116
|
-
else {
|
|
117
|
-
const errorMsg = output.errors || output.error || 'Command failed';
|
|
118
|
-
setError(errorMsg);
|
|
119
|
-
const taskOutput = {
|
|
120
|
-
stdout: output.output,
|
|
121
|
-
stderr: output.errors,
|
|
122
|
-
error: errorMsg,
|
|
123
|
-
workdir: output.workdir,
|
|
124
|
-
};
|
|
125
|
-
onErrorRef.current?.(index, errorMsg, taskDuration, taskOutput);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
catch (err) {
|
|
129
|
-
setOutputCallback(undefined); // Clear callback
|
|
130
|
-
if (!mounted)
|
|
131
|
-
return;
|
|
132
|
-
const end = Date.now();
|
|
133
|
-
setEndTime(end);
|
|
134
|
-
const errorDuration = calculateElapsed(start);
|
|
135
|
-
setElapsed(errorDuration);
|
|
136
|
-
setStatus(ExecutionStatus.Failed);
|
|
137
|
-
const errorMsg = err instanceof Error ? err.message : 'Unknown error';
|
|
138
|
-
setError(errorMsg);
|
|
139
|
-
const taskOutput = {
|
|
140
|
-
stdout: stdoutRef.current,
|
|
141
|
-
stderr: stderrRef.current,
|
|
142
|
-
error: errorMsg,
|
|
143
|
-
};
|
|
144
|
-
// Use try/catch to prevent callback errors from propagating
|
|
145
|
-
try {
|
|
146
|
-
onErrorRef.current?.(index, errorMsg, errorDuration, taskOutput);
|
|
147
|
-
}
|
|
148
|
-
catch {
|
|
149
|
-
// Callback error - already set error state above
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
void execute();
|
|
154
|
-
return () => {
|
|
155
|
-
mounted = false;
|
|
156
|
-
};
|
|
157
|
-
}, [isActive]);
|
|
158
|
-
// Handle abort when task becomes inactive while running
|
|
159
|
-
useEffect(() => {
|
|
160
|
-
if (!isActive && status === ExecutionStatus.Running && startTime) {
|
|
161
|
-
// Task was aborted mid-execution
|
|
162
|
-
const end = Date.now();
|
|
163
|
-
setEndTime(end);
|
|
164
|
-
setElapsed(calculateElapsed(startTime));
|
|
165
|
-
setStatus(ExecutionStatus.Aborted);
|
|
166
|
-
const taskOutput = {
|
|
167
|
-
stdout: stdoutRef.current,
|
|
168
|
-
stderr: stderrRef.current,
|
|
169
|
-
error: errorRef.current,
|
|
170
|
-
};
|
|
171
|
-
onAbortRef.current?.(index, taskOutput);
|
|
172
|
-
}
|
|
173
|
-
}, [isActive, status, startTime, index]);
|
|
174
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Subtask, { label: label, command: command, status: status, isActive: isActive, startTime: startTime, endTime: endTime, elapsed: status === ExecutionStatus.Running ? currentElapsed : elapsed }), _jsx(Output, { stdout: stdout, stderr: stderr, isFinished: isFinished, status: status }, `${stdout.length}-${stderr.length}`)] }));
|
|
4
|
+
import { SubtaskView } from './Subtask.js';
|
|
5
|
+
/**
|
|
6
|
+
* Pure display component for a task.
|
|
7
|
+
* Combines SubtaskView (label/command/status) with Output (stdout/stderr).
|
|
8
|
+
*/
|
|
9
|
+
export function TaskView({ label, command, status, elapsed, stdout, stderr, isFinished, }) {
|
|
10
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(SubtaskView, { label: label, command: command, status: status, elapsed: elapsed }), _jsx(Output, { stdout: stdout, stderr: stderr, isFinished: isFinished, status: status }, `${stdout.length}-${stderr.length}`)] }));
|
|
175
11
|
}
|
package/dist/ui/Validate.js
CHANGED
|
@@ -4,10 +4,11 @@ import { Box, Text } from 'ink';
|
|
|
4
4
|
import { ComponentStatus, } from '../types/components.js';
|
|
5
5
|
import { TaskType } from '../types/types.js';
|
|
6
6
|
import { saveConfig } from '../configuration/io.js';
|
|
7
|
+
import { createConfigStepsFromSchema } from '../configuration/steps.js';
|
|
7
8
|
import { unflattenConfig } from '../configuration/transformation.js';
|
|
8
9
|
import { Colors, getTextColor } from '../services/colors.js';
|
|
9
|
-
import {
|
|
10
|
-
import { saveConfigLabels } from '../
|
|
10
|
+
import { createConfig, createMessage } from '../services/components.js';
|
|
11
|
+
import { saveConfigLabels } from '../configuration/labels.js';
|
|
11
12
|
import { useInput } from '../services/keyboard.js';
|
|
12
13
|
import { formatErrorMessage, getUnresolvedPlaceholdersMessage, } from '../services/messages.js';
|
|
13
14
|
import { ensureMinimumTime } from '../services/timing.js';
|
|
@@ -73,28 +74,32 @@ export function Validate({ missingConfig, userRequest, status, service, onError,
|
|
|
73
74
|
setCompletionMessage(message);
|
|
74
75
|
setConfigRequirements(withDescriptions);
|
|
75
76
|
// Add validation message to timeline before Config component
|
|
76
|
-
workflowHandlers.addToTimeline(createMessage(message));
|
|
77
|
+
workflowHandlers.addToTimeline(createMessage({ text: message }));
|
|
77
78
|
// Create Config component and add to queue
|
|
78
79
|
const keys = withDescriptions.map((req) => req.path);
|
|
79
|
-
const configDef =
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
80
|
+
const configDef = createConfig({
|
|
81
|
+
steps: createConfigStepsFromSchema(keys),
|
|
82
|
+
onFinished: (config) => {
|
|
83
|
+
// Convert flat dotted keys to nested structure grouped by section
|
|
84
|
+
const configBySection = unflattenConfig(config);
|
|
85
|
+
// Extract and save labels to cache
|
|
86
|
+
const labels = {};
|
|
87
|
+
for (const req of withDescriptions) {
|
|
88
|
+
if (req.description) {
|
|
89
|
+
labels[req.path] = req.description;
|
|
90
|
+
}
|
|
87
91
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
92
|
+
saveConfigLabels(labels);
|
|
93
|
+
// Save each section
|
|
94
|
+
for (const [section, sectionConfig] of Object.entries(configBySection)) {
|
|
95
|
+
saveConfig(section, sectionConfig);
|
|
96
|
+
}
|
|
97
|
+
// After config is saved, invoke callback to add Execute component to queue
|
|
98
|
+
onValidationComplete(withDescriptions);
|
|
99
|
+
},
|
|
100
|
+
onAborted: (operation) => {
|
|
101
|
+
onAborted(operation);
|
|
102
|
+
},
|
|
98
103
|
});
|
|
99
104
|
// Override descriptions with LLM-generated ones
|
|
100
105
|
if ('props' in configDef && 'steps' in configDef.props) {
|
package/dist/ui/Workflow.js
CHANGED
|
@@ -3,11 +3,19 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
|
3
3
|
import { Box, Static } from 'ink';
|
|
4
4
|
import { ComponentStatus, } from '../types/components.js';
|
|
5
5
|
import { ComponentName, FeedbackType } from '../types/types.js';
|
|
6
|
-
import { createFeedback
|
|
6
|
+
import { createFeedback } from '../services/components.js';
|
|
7
7
|
import { getWarnings } from '../services/logger.js';
|
|
8
8
|
import { getCancellationMessage } from '../services/messages.js';
|
|
9
9
|
import { exitApp } from '../services/process.js';
|
|
10
10
|
import { SimpleComponent, ControllerComponent, TimelineComponent, } from './Component.js';
|
|
11
|
+
/**
|
|
12
|
+
* Mark a component as done. Returns the component to be added to timeline.
|
|
13
|
+
* Components use handlers.updateState to save their state before completion,
|
|
14
|
+
* so this function sets the status to Done and returns the updated component.
|
|
15
|
+
*/
|
|
16
|
+
function markAsDone(component) {
|
|
17
|
+
return { ...component, status: ComponentStatus.Done };
|
|
18
|
+
}
|
|
11
19
|
export const Workflow = ({ initialQueue, debug }) => {
|
|
12
20
|
const [timeline, setTimeline] = useState([]);
|
|
13
21
|
const [current, setCurrent] = useState({ active: null, pending: null });
|
|
@@ -42,14 +50,14 @@ export const Workflow = ({ initialQueue, debug }) => {
|
|
|
42
50
|
// Add feedback to queue
|
|
43
51
|
setQueue((queue) => [
|
|
44
52
|
...queue,
|
|
45
|
-
createFeedback(FeedbackType.Failed, error),
|
|
53
|
+
createFeedback({ type: FeedbackType.Failed, message: error }),
|
|
46
54
|
]);
|
|
47
55
|
},
|
|
48
56
|
onAborted: (operation) => {
|
|
49
57
|
moveActiveToTimeline();
|
|
50
58
|
// Clear queue and add only feedback to prevent subsequent components from executing
|
|
51
59
|
const message = getCancellationMessage(operation);
|
|
52
|
-
setQueue([createFeedback(FeedbackType.Aborted, message)]);
|
|
60
|
+
setQueue([createFeedback({ type: FeedbackType.Aborted, message })]);
|
|
53
61
|
},
|
|
54
62
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
|
|
55
63
|
onCompleted: (finalState) => {
|
|
@@ -144,7 +152,7 @@ export const Workflow = ({ initialQueue, debug }) => {
|
|
|
144
152
|
useEffect(() => {
|
|
145
153
|
const warningMessages = getWarnings();
|
|
146
154
|
if (warningMessages.length > 0) {
|
|
147
|
-
const warningComponents = warningMessages.map((msg) =>
|
|
155
|
+
const warningComponents = warningMessages.map((msg) => createFeedback({ type: FeedbackType.Warning, message: msg }, ComponentStatus.Done));
|
|
148
156
|
setTimeline((prev) => [...prev, ...warningComponents]);
|
|
149
157
|
}
|
|
150
158
|
}, [timeline, current]);
|
|
@@ -187,3 +195,12 @@ export const Workflow = ({ initialQueue, debug }) => {
|
|
|
187
195
|
const pendingComponent = useMemo(() => renderComponent(current.pending, ComponentStatus.Pending), [current.pending, renderComponent]);
|
|
188
196
|
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Static, { items: timeline, children: (item) => (_jsx(Box, { marginTop: 1, children: _jsx(TimelineComponent, { def: item }) }, item.id)) }, "timeline"), pendingComponent && _jsx(Box, { marginTop: 1, children: pendingComponent }), activeComponent && _jsx(Box, { marginTop: 1, children: activeComponent })] }));
|
|
189
197
|
};
|
|
198
|
+
/**
|
|
199
|
+
* Check if a component is stateless (simple).
|
|
200
|
+
* Stateless components are display-only and complete immediately without
|
|
201
|
+
* tracking internal state. Stateful components manage user interaction
|
|
202
|
+
* and maintain state across their lifecycle.
|
|
203
|
+
*/
|
|
204
|
+
export function isSimple(component) {
|
|
205
|
+
return !('state' in component);
|
|
206
|
+
}
|
package/package.json
CHANGED
package/dist/parser.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Parses a comma-separated list of tasks from command-line prompt
|
|
3
|
-
* Strips exclamation marks and periods from each task
|
|
4
|
-
*
|
|
5
|
-
* @param prompt - Raw command-line input string
|
|
6
|
-
* @returns Array of parsed task strings
|
|
7
|
-
*/
|
|
8
|
-
export function parseCommands(prompt) {
|
|
9
|
-
return prompt
|
|
10
|
-
.split(',')
|
|
11
|
-
.map((task) => task.trim().replace(/[!.]/g, '').trim())
|
|
12
|
-
.filter((task) => task.length > 0);
|
|
13
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utility functions for config manipulation
|
|
3
|
-
*/
|
|
4
|
-
/**
|
|
5
|
-
* Flatten nested config object to dot notation
|
|
6
|
-
* Example: { a: { b: 1 } } => { 'a.b': 1 }
|
|
7
|
-
*/
|
|
8
|
-
export function flattenConfig(obj, prefix = '') {
|
|
9
|
-
const result = {};
|
|
10
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
11
|
-
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
12
|
-
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
13
|
-
Object.assign(result, flattenConfig(value, fullKey));
|
|
14
|
-
}
|
|
15
|
-
else {
|
|
16
|
-
result[fullKey] = value;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
return result;
|
|
20
|
-
}
|