prompt-language-shell 0.8.2 → 0.8.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.
- package/dist/services/anthropic.js +42 -55
- package/dist/services/colors.js +1 -0
- package/dist/services/components.js +5 -13
- package/dist/services/configuration.js +23 -23
- package/dist/services/filesystem.js +114 -0
- package/dist/services/loader.js +8 -5
- package/dist/services/logger.js +24 -0
- package/dist/services/parser.js +3 -1
- package/dist/services/refinement.js +9 -9
- package/dist/services/router.js +25 -25
- package/dist/services/skills.js +12 -11
- package/dist/services/validator.js +4 -3
- package/dist/types/guards.js +4 -6
- package/dist/types/handlers.js +1 -0
- package/dist/types/schemas.js +103 -0
- package/dist/types/types.js +1 -0
- package/dist/ui/Answer.js +10 -9
- package/dist/ui/Command.js +20 -14
- package/dist/ui/Config.js +8 -7
- package/dist/ui/Confirm.js +4 -4
- package/dist/ui/Execute.js +263 -69
- package/dist/ui/Feedback.js +1 -0
- package/dist/ui/Introspect.js +12 -10
- package/dist/ui/Main.js +5 -5
- package/dist/ui/Schedule.js +8 -8
- package/dist/ui/Validate.js +10 -8
- package/dist/ui/Workflow.js +102 -30
- package/package.json +3 -2
package/dist/ui/Schedule.js
CHANGED
|
@@ -71,7 +71,7 @@ function taskToListItem(task, highlightedChildIndex = null, isDefineTaskWithoutS
|
|
|
71
71
|
}
|
|
72
72
|
return item;
|
|
73
73
|
}
|
|
74
|
-
export function Schedule({ message, tasks, state, status, debug = DebugLevel.None,
|
|
74
|
+
export function Schedule({ message, tasks, state, status, debug = DebugLevel.None, stateHandlers, lifecycleHandlers, errorHandlers, onSelectionConfirmed, }) {
|
|
75
75
|
const isActive = status === ComponentStatus.Active;
|
|
76
76
|
// isActive passed as prop
|
|
77
77
|
const [highlightedIndex, setHighlightedIndex] = useState(state?.highlightedIndex ?? null);
|
|
@@ -95,7 +95,7 @@ export function Schedule({ message, tasks, state, status, debug = DebugLevel.Non
|
|
|
95
95
|
const concreteTasks = tasks.filter((task) => task.type !== TaskType.Ignore && task.type !== TaskType.Discard);
|
|
96
96
|
// Complete the selection phase - it goes to timeline
|
|
97
97
|
// Callback will create a new Plan showing refined tasks (pending) + Confirm (active)
|
|
98
|
-
|
|
98
|
+
lifecycleHandlers?.completeActive();
|
|
99
99
|
void onSelectionConfirmed(concreteTasks);
|
|
100
100
|
}
|
|
101
101
|
}, [
|
|
@@ -103,7 +103,7 @@ export function Schedule({ message, tasks, state, status, debug = DebugLevel.Non
|
|
|
103
103
|
defineTaskIndices.length,
|
|
104
104
|
tasks,
|
|
105
105
|
onSelectionConfirmed,
|
|
106
|
-
|
|
106
|
+
lifecycleHandlers,
|
|
107
107
|
]);
|
|
108
108
|
useInput((input, key) => {
|
|
109
109
|
// Don't handle input if not active or no define task
|
|
@@ -111,7 +111,7 @@ export function Schedule({ message, tasks, state, status, debug = DebugLevel.Non
|
|
|
111
111
|
return;
|
|
112
112
|
}
|
|
113
113
|
if (key.escape) {
|
|
114
|
-
|
|
114
|
+
errorHandlers?.onAborted('task selection');
|
|
115
115
|
return;
|
|
116
116
|
}
|
|
117
117
|
if (key.downArrow) {
|
|
@@ -170,12 +170,12 @@ export function Schedule({ message, tasks, state, status, debug = DebugLevel.Non
|
|
|
170
170
|
if (onSelectionConfirmed) {
|
|
171
171
|
// Complete the selection phase - it goes to timeline
|
|
172
172
|
// Callback will create a new Plan showing refined tasks (pending) + Confirm (active)
|
|
173
|
-
|
|
173
|
+
lifecycleHandlers?.completeActive();
|
|
174
174
|
void onSelectionConfirmed(refinedTasks);
|
|
175
175
|
}
|
|
176
176
|
else {
|
|
177
177
|
// No selection callback, just complete normally
|
|
178
|
-
|
|
178
|
+
lifecycleHandlers?.completeActive();
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
181
|
}
|
|
@@ -183,7 +183,7 @@ export function Schedule({ message, tasks, state, status, debug = DebugLevel.Non
|
|
|
183
183
|
// Sync state back to component definition
|
|
184
184
|
// This ensures timeline can render historical state and tests can validate behavior
|
|
185
185
|
useEffect(() => {
|
|
186
|
-
|
|
186
|
+
stateHandlers?.updateState({
|
|
187
187
|
highlightedIndex,
|
|
188
188
|
currentDefineGroupIndex,
|
|
189
189
|
completedSelections,
|
|
@@ -192,7 +192,7 @@ export function Schedule({ message, tasks, state, status, debug = DebugLevel.Non
|
|
|
192
192
|
highlightedIndex,
|
|
193
193
|
currentDefineGroupIndex,
|
|
194
194
|
completedSelections,
|
|
195
|
-
|
|
195
|
+
stateHandlers,
|
|
196
196
|
]);
|
|
197
197
|
const listItems = tasks.map((task, idx) => {
|
|
198
198
|
// Find which define group this task belongs to (if any)
|
package/dist/ui/Validate.js
CHANGED
|
@@ -4,7 +4,7 @@ import { Box, Text } from 'ink';
|
|
|
4
4
|
import { ComponentStatus } from '../types/components.js';
|
|
5
5
|
import { TaskType } from '../types/types.js';
|
|
6
6
|
import { Colors, getTextColor } from '../services/colors.js';
|
|
7
|
-
import {
|
|
7
|
+
import { createConfigStepsFromSchema } from '../services/components.js';
|
|
8
8
|
import { DebugLevel, saveConfig, unflattenConfig, } from '../services/configuration.js';
|
|
9
9
|
import { saveConfigLabels } from '../services/config-labels.js';
|
|
10
10
|
import { useInput } from '../services/keyboard.js';
|
|
@@ -13,7 +13,7 @@ import { ensureMinimumTime } from '../services/timing.js';
|
|
|
13
13
|
import { Config } from './Config.js';
|
|
14
14
|
import { Spinner } from './Spinner.js';
|
|
15
15
|
const MIN_PROCESSING_TIME = 1000;
|
|
16
|
-
export function Validate({ missingConfig, userRequest, state, status, service, children, debug = DebugLevel.None, onError, onComplete, onAborted,
|
|
16
|
+
export function Validate({ missingConfig, userRequest, state, status, service, children, debug = DebugLevel.None, onError, onComplete, onAborted, stateHandlers, lifecycleHandlers, workflowHandlers, }) {
|
|
17
17
|
const isActive = status === ComponentStatus.Active;
|
|
18
18
|
const [error, setError] = useState(state?.error ?? null);
|
|
19
19
|
const [completionMessage, setCompletionMessage] = useState(state?.completionMessage ?? null);
|
|
@@ -39,7 +39,9 @@ export function Validate({ missingConfig, userRequest, state, status, service, c
|
|
|
39
39
|
await ensureMinimumTime(startTime, MIN_PROCESSING_TIME);
|
|
40
40
|
if (mounted) {
|
|
41
41
|
// Add debug components to timeline if present
|
|
42
|
-
|
|
42
|
+
if (result.debug?.length) {
|
|
43
|
+
workflowHandlers?.addToTimeline(...result.debug);
|
|
44
|
+
}
|
|
43
45
|
// Extract CONFIG tasks with descriptions from result
|
|
44
46
|
const configTasks = result.tasks.filter((task) => task.type === TaskType.Config);
|
|
45
47
|
// Build ConfigRequirements with descriptions
|
|
@@ -68,7 +70,7 @@ export function Validate({ missingConfig, userRequest, state, status, service, c
|
|
|
68
70
|
setCompletionMessage(message);
|
|
69
71
|
setConfigRequirements(withDescriptions);
|
|
70
72
|
// Save state after validation completes
|
|
71
|
-
|
|
73
|
+
stateHandlers?.updateState({
|
|
72
74
|
completionMessage: message,
|
|
73
75
|
configRequirements: withDescriptions,
|
|
74
76
|
validated: true,
|
|
@@ -82,7 +84,7 @@ export function Validate({ missingConfig, userRequest, state, status, service, c
|
|
|
82
84
|
const errorMessage = formatErrorMessage(err);
|
|
83
85
|
setError(errorMessage);
|
|
84
86
|
// Save error state
|
|
85
|
-
|
|
87
|
+
stateHandlers?.updateState({
|
|
86
88
|
error: errorMessage,
|
|
87
89
|
completionMessage: null,
|
|
88
90
|
configRequirements: null,
|
|
@@ -142,7 +144,7 @@ export function Validate({ missingConfig, userRequest, state, status, service, c
|
|
|
142
144
|
}
|
|
143
145
|
// Mark validation component as complete before invoking callback
|
|
144
146
|
// This allows the workflow to proceed to execution
|
|
145
|
-
|
|
147
|
+
lifecycleHandlers?.completeActive();
|
|
146
148
|
// Invoke callback which will queue the Execute component
|
|
147
149
|
if (configRequirements) {
|
|
148
150
|
onComplete(configRequirements);
|
|
@@ -150,10 +152,10 @@ export function Validate({ missingConfig, userRequest, state, status, service, c
|
|
|
150
152
|
};
|
|
151
153
|
const handleConfigAborted = (operation) => {
|
|
152
154
|
// Mark validation component as complete when aborted
|
|
153
|
-
|
|
155
|
+
lifecycleHandlers?.completeActive();
|
|
154
156
|
onAborted(operation);
|
|
155
157
|
};
|
|
156
|
-
return (_jsxs(Box, { alignSelf: "flex-start", flexDirection: "column", children: [isActive && !completionMessage && !error && (_jsxs(Box, { marginLeft: 1, children: [_jsxs(Text, { color: getTextColor(isActive), children: ["Validating configuration requirements.", ' '] }), _jsx(Spinner, {})] })), completionMessage && (_jsx(Box, { marginLeft: 1, children: _jsx(Text, { color: getTextColor(isActive), children: completionMessage }) })), error && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: Colors.Status.Error, children: ["Error: ", error] }) })), configSteps && configSteps.length > 0 && !error && (_jsx(Box, { marginTop: 1, children: _jsx(Config, { steps: configSteps, status: status, debug: debug, onFinished: handleConfigFinished, onAborted: handleConfigAborted
|
|
158
|
+
return (_jsxs(Box, { alignSelf: "flex-start", flexDirection: "column", children: [isActive && !completionMessage && !error && (_jsxs(Box, { marginLeft: 1, children: [_jsxs(Text, { color: getTextColor(isActive), children: ["Validating configuration requirements.", ' '] }), _jsx(Spinner, {})] })), completionMessage && (_jsx(Box, { marginLeft: 1, children: _jsx(Text, { color: getTextColor(isActive), children: completionMessage }) })), error && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: Colors.Status.Error, children: ["Error: ", error] }) })), configSteps && configSteps.length > 0 && !error && (_jsx(Box, { marginTop: 1, children: _jsx(Config, { steps: configSteps, status: status, debug: debug, onFinished: handleConfigFinished, onAborted: handleConfigAborted }) })), configSteps && configSteps.length === 0 && !error && (_jsx(Box, { marginTop: 1, marginLeft: 1, children: _jsx(Text, { color: Colors.Status.Error, children: "Error: No configuration steps generated. Please try again." }) })), children] }));
|
|
157
159
|
}
|
|
158
160
|
/**
|
|
159
161
|
* Build prompt for VALIDATE tool
|
package/dist/ui/Workflow.js
CHANGED
|
@@ -5,6 +5,7 @@ import { ComponentStatus, } from '../types/components.js';
|
|
|
5
5
|
import { ComponentName, FeedbackType } from '../types/types.js';
|
|
6
6
|
import { createFeedback, isStateless, markAsDone, } from '../services/components.js';
|
|
7
7
|
import { DebugLevel } from '../services/configuration.js';
|
|
8
|
+
import { getWarnings } from '../services/logger.js';
|
|
8
9
|
import { getCancellationMessage } from '../services/messages.js';
|
|
9
10
|
import { exitApp } from '../services/process.js';
|
|
10
11
|
import { Component } from './Component.js';
|
|
@@ -35,11 +36,8 @@ export const Workflow = ({ initialQueue, debug }) => {
|
|
|
35
36
|
return { active: null, pending };
|
|
36
37
|
});
|
|
37
38
|
}, []);
|
|
38
|
-
//
|
|
39
|
-
const
|
|
40
|
-
addToQueue: (...items) => {
|
|
41
|
-
setQueue((queue) => [...queue, ...items]);
|
|
42
|
-
},
|
|
39
|
+
// Focused handler instances - segregated by responsibility
|
|
40
|
+
const stateHandlers = useMemo(() => ({
|
|
43
41
|
updateState: (newState) => {
|
|
44
42
|
setCurrent((curr) => {
|
|
45
43
|
const { active, pending } = curr;
|
|
@@ -56,12 +54,41 @@ export const Workflow = ({ initialQueue, debug }) => {
|
|
|
56
54
|
return { active: updated, pending };
|
|
57
55
|
});
|
|
58
56
|
},
|
|
57
|
+
}), []);
|
|
58
|
+
const lifecycleHandlers = useMemo(() => ({
|
|
59
59
|
completeActive: (...items) => {
|
|
60
60
|
moveActiveToPending();
|
|
61
61
|
if (items.length > 0) {
|
|
62
62
|
setQueue((queue) => [...items, ...queue]);
|
|
63
63
|
}
|
|
64
64
|
},
|
|
65
|
+
}), [moveActiveToPending]);
|
|
66
|
+
const queueHandlers = useMemo(() => ({
|
|
67
|
+
addToQueue: (...items) => {
|
|
68
|
+
setQueue((queue) => [...queue, ...items]);
|
|
69
|
+
},
|
|
70
|
+
}), []);
|
|
71
|
+
const errorHandlers = useMemo(() => ({
|
|
72
|
+
onAborted: (operation) => {
|
|
73
|
+
moveActiveToTimeline();
|
|
74
|
+
// Add feedback to queue
|
|
75
|
+
const message = getCancellationMessage(operation);
|
|
76
|
+
setQueue((queue) => [
|
|
77
|
+
...queue,
|
|
78
|
+
createFeedback(FeedbackType.Aborted, message),
|
|
79
|
+
]);
|
|
80
|
+
},
|
|
81
|
+
onError: (error) => {
|
|
82
|
+
moveActiveToTimeline();
|
|
83
|
+
// Add feedback to queue
|
|
84
|
+
setQueue((queue) => [
|
|
85
|
+
...queue,
|
|
86
|
+
createFeedback(FeedbackType.Failed, error),
|
|
87
|
+
]);
|
|
88
|
+
},
|
|
89
|
+
}), [moveActiveToTimeline]);
|
|
90
|
+
// Workflow handlers - used for timeline/queue management
|
|
91
|
+
const workflowHandlers = useMemo(() => ({
|
|
65
92
|
completeActiveAndPending: (...items) => {
|
|
66
93
|
setCurrent((curr) => {
|
|
67
94
|
const { active, pending } = curr;
|
|
@@ -83,24 +110,7 @@ export const Workflow = ({ initialQueue, debug }) => {
|
|
|
83
110
|
addToTimeline: (...items) => {
|
|
84
111
|
setTimeline((prev) => [...prev, ...items]);
|
|
85
112
|
},
|
|
86
|
-
|
|
87
|
-
moveActiveToTimeline();
|
|
88
|
-
// Add feedback to queue
|
|
89
|
-
const message = getCancellationMessage(operation);
|
|
90
|
-
setQueue((queue) => [
|
|
91
|
-
...queue,
|
|
92
|
-
createFeedback(FeedbackType.Aborted, message),
|
|
93
|
-
]);
|
|
94
|
-
},
|
|
95
|
-
onError: (error) => {
|
|
96
|
-
moveActiveToTimeline();
|
|
97
|
-
// Add feedback to queue
|
|
98
|
-
setQueue((queue) => [
|
|
99
|
-
...queue,
|
|
100
|
-
createFeedback(FeedbackType.Failed, error),
|
|
101
|
-
]);
|
|
102
|
-
},
|
|
103
|
-
}), [moveActiveToPending, moveActiveToTimeline]);
|
|
113
|
+
}), []);
|
|
104
114
|
// Global Esc handler removed - components handle their own Esc individually
|
|
105
115
|
// Move next item from queue to active
|
|
106
116
|
useEffect(() => {
|
|
@@ -138,6 +148,14 @@ export const Workflow = ({ initialQueue, debug }) => {
|
|
|
138
148
|
}
|
|
139
149
|
// Stateful components stay in active until handlers move them to pending
|
|
140
150
|
}, [current]);
|
|
151
|
+
// Check for accumulated warnings and add them to timeline
|
|
152
|
+
useEffect(() => {
|
|
153
|
+
const warningMessages = getWarnings();
|
|
154
|
+
if (warningMessages.length > 0) {
|
|
155
|
+
const warningComponents = warningMessages.map((msg) => markAsDone(createFeedback(FeedbackType.Warning, msg)));
|
|
156
|
+
setTimeline((prev) => [...prev, ...warningComponents]);
|
|
157
|
+
}
|
|
158
|
+
}, [timeline, current]);
|
|
141
159
|
// Move final pending to timeline and exit when all done
|
|
142
160
|
useEffect(() => {
|
|
143
161
|
const { active, pending } = current;
|
|
@@ -171,23 +189,77 @@ export const Workflow = ({ initialQueue, debug }) => {
|
|
|
171
189
|
if (isStateless(active)) {
|
|
172
190
|
return _jsx(Component, { def: active, debug: debug }, active.id);
|
|
173
191
|
}
|
|
174
|
-
// For stateful components, inject
|
|
192
|
+
// For stateful components, inject focused handlers
|
|
175
193
|
const statefulActive = active;
|
|
176
194
|
const wrappedDef = {
|
|
177
195
|
...statefulActive,
|
|
178
196
|
props: {
|
|
179
197
|
...statefulActive.props,
|
|
180
|
-
|
|
198
|
+
stateHandlers,
|
|
199
|
+
lifecycleHandlers,
|
|
200
|
+
queueHandlers,
|
|
201
|
+
errorHandlers,
|
|
202
|
+
workflowHandlers,
|
|
181
203
|
},
|
|
182
204
|
};
|
|
183
205
|
return _jsx(Component, { def: wrappedDef, debug: debug }, active.id);
|
|
184
|
-
}, [
|
|
206
|
+
}, [
|
|
207
|
+
current,
|
|
208
|
+
debug,
|
|
209
|
+
stateHandlers,
|
|
210
|
+
lifecycleHandlers,
|
|
211
|
+
queueHandlers,
|
|
212
|
+
errorHandlers,
|
|
213
|
+
workflowHandlers,
|
|
214
|
+
]);
|
|
185
215
|
const pendingComponent = useMemo(() => {
|
|
186
216
|
const { pending } = current;
|
|
187
217
|
if (!pending)
|
|
188
218
|
return null;
|
|
189
|
-
//
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
219
|
+
// For stateless components, render as-is
|
|
220
|
+
if (isStateless(pending)) {
|
|
221
|
+
return _jsx(Component, { def: pending, debug: debug }, pending.id);
|
|
222
|
+
}
|
|
223
|
+
// For stateful components, inject focused handlers (they may have useEffect hooks)
|
|
224
|
+
const statefulPending = pending;
|
|
225
|
+
const wrappedDef = {
|
|
226
|
+
...statefulPending,
|
|
227
|
+
props: {
|
|
228
|
+
...statefulPending.props,
|
|
229
|
+
stateHandlers,
|
|
230
|
+
lifecycleHandlers,
|
|
231
|
+
queueHandlers,
|
|
232
|
+
errorHandlers,
|
|
233
|
+
workflowHandlers,
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
return _jsx(Component, { def: wrappedDef, debug: debug }, pending.id);
|
|
237
|
+
}, [
|
|
238
|
+
current,
|
|
239
|
+
debug,
|
|
240
|
+
stateHandlers,
|
|
241
|
+
lifecycleHandlers,
|
|
242
|
+
queueHandlers,
|
|
243
|
+
errorHandlers,
|
|
244
|
+
workflowHandlers,
|
|
245
|
+
]);
|
|
246
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Static, { items: timeline, children: (item) => {
|
|
247
|
+
// For stateful timeline components, inject handlers (useEffect hooks may still run)
|
|
248
|
+
let def = item;
|
|
249
|
+
if (!isStateless(item)) {
|
|
250
|
+
const statefulItem = item;
|
|
251
|
+
def = {
|
|
252
|
+
...statefulItem,
|
|
253
|
+
props: {
|
|
254
|
+
...statefulItem.props,
|
|
255
|
+
stateHandlers,
|
|
256
|
+
lifecycleHandlers,
|
|
257
|
+
queueHandlers,
|
|
258
|
+
errorHandlers,
|
|
259
|
+
workflowHandlers,
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
return (_jsx(Box, { marginTop: 1, children: _jsx(Component, { def: def, debug: DebugLevel.None }) }, item.id));
|
|
264
|
+
} }, "timeline"), pendingComponent && _jsx(Box, { marginTop: 1, children: pendingComponent }), activeComponent && _jsx(Box, { marginTop: 1, children: activeComponent })] }));
|
|
193
265
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prompt-language-shell",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.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",
|
|
@@ -51,7 +51,8 @@
|
|
|
51
51
|
"ink": "^6.6.0",
|
|
52
52
|
"ink-text-input": "^6.0.0",
|
|
53
53
|
"react": "^19.2.3",
|
|
54
|
-
"yaml": "^2.8.2"
|
|
54
|
+
"yaml": "^2.8.2",
|
|
55
|
+
"zod": "^4.2.1"
|
|
55
56
|
},
|
|
56
57
|
"devDependencies": {
|
|
57
58
|
"@types/node": "^25.0.3",
|