prompt-language-shell 0.8.0 → 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 +50 -37
- package/dist/services/colors.js +21 -1
- package/dist/services/components.js +6 -14
- package/dist/services/config-labels.js +75 -0
- package/dist/services/config-utils.js +20 -0
- package/dist/services/configuration.js +38 -59
- 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/registry.js +1 -1
- package/dist/services/router.js +29 -28
- package/dist/services/skills.js +15 -14
- package/dist/services/validator.js +4 -3
- package/dist/skills/introspect.md +52 -43
- package/dist/skills/schedule.md +8 -3
- package/dist/tools/introspect.tool.js +18 -9
- package/dist/types/guards.js +23 -0
- package/dist/types/handlers.js +1 -0
- package/dist/types/schemas.js +103 -0
- package/dist/types/types.js +7 -0
- package/dist/ui/Answer.js +11 -15
- package/dist/ui/Command.js +24 -20
- package/dist/ui/Config.js +44 -32
- package/dist/ui/Confirm.js +9 -9
- package/dist/ui/Execute.js +265 -75
- package/dist/ui/Feedback.js +1 -0
- package/dist/ui/Introspect.js +16 -61
- package/dist/ui/Main.js +6 -6
- package/dist/ui/Report.js +4 -8
- package/dist/ui/Schedule.js +12 -12
- package/dist/ui/Spinner.js +3 -1
- package/dist/ui/Subtask.js +1 -1
- package/dist/ui/Task.js +7 -6
- package/dist/ui/Validate.js +28 -21
- package/dist/ui/Workflow.js +102 -30
- package/package.json +3 -2
package/dist/ui/Execute.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback, useEffect,
|
|
2
|
+
import { useCallback, useEffect, useReducer } from 'react';
|
|
3
3
|
import { Box, Text } from 'ink';
|
|
4
4
|
import { ComponentStatus, } from '../types/components.js';
|
|
5
5
|
import { Colors, getTextColor } from '../services/colors.js';
|
|
6
|
-
import { addDebugToTimeline } from '../services/components.js';
|
|
7
6
|
import { useInput } from '../services/keyboard.js';
|
|
8
7
|
import { loadUserConfig } from '../services/loader.js';
|
|
9
8
|
import { formatErrorMessage } from '../services/messages.js';
|
|
@@ -14,41 +13,184 @@ import { formatDuration } from '../services/utils.js';
|
|
|
14
13
|
import { Spinner } from './Spinner.js';
|
|
15
14
|
import { Task } from './Task.js';
|
|
16
15
|
const MINIMUM_PROCESSING_TIME = 400;
|
|
17
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Validates that all placeholders in a command have been resolved.
|
|
18
|
+
* Throws an error if unresolved placeholders are found.
|
|
19
|
+
*/
|
|
20
|
+
function validatePlaceholderResolution(command, original) {
|
|
21
|
+
const unresolvedPattern = /\{[^}]+\}/g;
|
|
22
|
+
const matches = command.match(unresolvedPattern);
|
|
23
|
+
if (matches && matches.length > 0) {
|
|
24
|
+
throw new Error(`Unresolved placeholders in command: ${matches.join(', ')}\nCommand: ${original}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function executeReducer(state, action) {
|
|
28
|
+
switch (action.type) {
|
|
29
|
+
case 'PROCESSING_COMPLETE':
|
|
30
|
+
return {
|
|
31
|
+
...state,
|
|
32
|
+
message: action.payload.message,
|
|
33
|
+
hasProcessed: true,
|
|
34
|
+
};
|
|
35
|
+
case 'COMMANDS_READY':
|
|
36
|
+
return {
|
|
37
|
+
...state,
|
|
38
|
+
message: action.payload.message,
|
|
39
|
+
summary: action.payload.summary,
|
|
40
|
+
taskInfos: action.payload.taskInfos,
|
|
41
|
+
completed: 0,
|
|
42
|
+
};
|
|
43
|
+
case 'PROCESSING_ERROR':
|
|
44
|
+
return {
|
|
45
|
+
...state,
|
|
46
|
+
error: action.payload.error,
|
|
47
|
+
hasProcessed: true,
|
|
48
|
+
};
|
|
49
|
+
case 'TASK_COMPLETE': {
|
|
50
|
+
const updatedTimes = [
|
|
51
|
+
...state.taskExecutionTimes,
|
|
52
|
+
action.payload.elapsed,
|
|
53
|
+
];
|
|
54
|
+
const updatedTaskInfos = state.taskInfos.map((task, i) => i === action.payload.index
|
|
55
|
+
? {
|
|
56
|
+
...task,
|
|
57
|
+
status: ExecutionStatus.Success,
|
|
58
|
+
elapsed: action.payload.elapsed,
|
|
59
|
+
}
|
|
60
|
+
: task);
|
|
61
|
+
return {
|
|
62
|
+
...state,
|
|
63
|
+
taskInfos: updatedTaskInfos,
|
|
64
|
+
taskExecutionTimes: updatedTimes,
|
|
65
|
+
completed: action.payload.index + 1,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
case 'ALL_TASKS_COMPLETE': {
|
|
69
|
+
const updatedTimes = [
|
|
70
|
+
...state.taskExecutionTimes,
|
|
71
|
+
action.payload.elapsed,
|
|
72
|
+
];
|
|
73
|
+
const updatedTaskInfos = state.taskInfos.map((task, i) => i === action.payload.index
|
|
74
|
+
? {
|
|
75
|
+
...task,
|
|
76
|
+
status: ExecutionStatus.Success,
|
|
77
|
+
elapsed: action.payload.elapsed,
|
|
78
|
+
}
|
|
79
|
+
: task);
|
|
80
|
+
const totalElapsed = updatedTimes.reduce((sum, time) => sum + time, 0);
|
|
81
|
+
const completion = `${action.payload.summaryText} in ${formatDuration(totalElapsed)}.`;
|
|
82
|
+
return {
|
|
83
|
+
...state,
|
|
84
|
+
taskInfos: updatedTaskInfos,
|
|
85
|
+
taskExecutionTimes: updatedTimes,
|
|
86
|
+
completed: action.payload.index + 1,
|
|
87
|
+
completionMessage: completion,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
case 'TASK_ERROR_CRITICAL': {
|
|
91
|
+
const updatedTaskInfos = state.taskInfos.map((task, i) => i === action.payload.index
|
|
92
|
+
? { ...task, status: ExecutionStatus.Failed, elapsed: 0 }
|
|
93
|
+
: task);
|
|
94
|
+
return {
|
|
95
|
+
...state,
|
|
96
|
+
taskInfos: updatedTaskInfos,
|
|
97
|
+
error: action.payload.error,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
case 'TASK_ERROR_CONTINUE': {
|
|
101
|
+
const updatedTimes = [
|
|
102
|
+
...state.taskExecutionTimes,
|
|
103
|
+
action.payload.elapsed,
|
|
104
|
+
];
|
|
105
|
+
const updatedTaskInfos = state.taskInfos.map((task, i) => i === action.payload.index
|
|
106
|
+
? {
|
|
107
|
+
...task,
|
|
108
|
+
status: ExecutionStatus.Failed,
|
|
109
|
+
elapsed: action.payload.elapsed,
|
|
110
|
+
}
|
|
111
|
+
: task);
|
|
112
|
+
return {
|
|
113
|
+
...state,
|
|
114
|
+
taskInfos: updatedTaskInfos,
|
|
115
|
+
taskExecutionTimes: updatedTimes,
|
|
116
|
+
completed: action.payload.index + 1,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
case 'LAST_TASK_ERROR': {
|
|
120
|
+
const updatedTimes = [
|
|
121
|
+
...state.taskExecutionTimes,
|
|
122
|
+
action.payload.elapsed,
|
|
123
|
+
];
|
|
124
|
+
const updatedTaskInfos = state.taskInfos.map((task, i) => i === action.payload.index
|
|
125
|
+
? {
|
|
126
|
+
...task,
|
|
127
|
+
status: ExecutionStatus.Failed,
|
|
128
|
+
elapsed: action.payload.elapsed,
|
|
129
|
+
}
|
|
130
|
+
: task);
|
|
131
|
+
const totalElapsed = updatedTimes.reduce((sum, time) => sum + time, 0);
|
|
132
|
+
const completion = `${action.payload.summaryText} in ${formatDuration(totalElapsed)}.`;
|
|
133
|
+
return {
|
|
134
|
+
...state,
|
|
135
|
+
taskInfos: updatedTaskInfos,
|
|
136
|
+
taskExecutionTimes: updatedTimes,
|
|
137
|
+
completed: action.payload.index + 1,
|
|
138
|
+
completionMessage: completion,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
case 'CANCEL_EXECUTION': {
|
|
142
|
+
const updatedTaskInfos = state.taskInfos.map((task, taskIndex) => {
|
|
143
|
+
if (taskIndex < action.payload.completed) {
|
|
144
|
+
return { ...task, status: ExecutionStatus.Success };
|
|
145
|
+
}
|
|
146
|
+
else if (taskIndex === action.payload.completed) {
|
|
147
|
+
return { ...task, status: ExecutionStatus.Aborted };
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
return { ...task, status: ExecutionStatus.Cancelled };
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
return {
|
|
154
|
+
...state,
|
|
155
|
+
taskInfos: updatedTaskInfos,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
default:
|
|
159
|
+
return state;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
export function Execute({ tasks, state, status, service, stateHandlers, lifecycleHandlers, errorHandlers, workflowHandlers, }) {
|
|
18
163
|
const isActive = status === ComponentStatus.Active;
|
|
19
|
-
const [
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
164
|
+
const [localState, dispatch] = useReducer(executeReducer, {
|
|
165
|
+
error: state?.error ?? null,
|
|
166
|
+
taskInfos: state?.taskInfos ?? [],
|
|
167
|
+
message: state?.message ?? '',
|
|
168
|
+
completed: state?.completed ?? 0,
|
|
169
|
+
hasProcessed: false,
|
|
170
|
+
taskExecutionTimes: state?.taskExecutionTimes ?? [],
|
|
171
|
+
completionMessage: state?.completionMessage ?? null,
|
|
172
|
+
summary: state?.summary ?? '',
|
|
173
|
+
});
|
|
174
|
+
const { error, taskInfos, message, completed, hasProcessed, taskExecutionTimes, completionMessage, summary, } = localState;
|
|
27
175
|
// Derive loading state from current conditions
|
|
28
176
|
const isLoading = isActive && taskInfos.length === 0 && !error && !hasProcessed;
|
|
29
177
|
const isExecuting = completed < taskInfos.length;
|
|
30
178
|
// Handle cancel with useCallback to ensure we capture latest state
|
|
31
179
|
const handleCancel = useCallback(() => {
|
|
32
|
-
|
|
33
|
-
//
|
|
34
|
-
// - At completed: interrupted (Aborted)
|
|
35
|
-
// - After completed: never started (Cancelled)
|
|
180
|
+
dispatch({ type: 'CANCEL_EXECUTION', payload: { completed } });
|
|
181
|
+
// Get updated task infos after cancel
|
|
36
182
|
const updatedTaskInfos = taskInfos.map((task, taskIndex) => {
|
|
37
183
|
if (taskIndex < completed) {
|
|
38
|
-
// Tasks that completed before interruption
|
|
39
184
|
return { ...task, status: ExecutionStatus.Success };
|
|
40
185
|
}
|
|
41
186
|
else if (taskIndex === completed) {
|
|
42
|
-
// Task that was running when interrupted
|
|
43
187
|
return { ...task, status: ExecutionStatus.Aborted };
|
|
44
188
|
}
|
|
45
189
|
else {
|
|
46
|
-
// Tasks that haven't started yet
|
|
47
190
|
return { ...task, status: ExecutionStatus.Cancelled };
|
|
48
191
|
}
|
|
49
192
|
});
|
|
50
|
-
|
|
51
|
-
handlers?.updateState({
|
|
193
|
+
stateHandlers?.updateState({
|
|
52
194
|
message,
|
|
53
195
|
summary,
|
|
54
196
|
taskInfos: updatedTaskInfos,
|
|
@@ -57,8 +199,16 @@ export function Execute({ tasks, state, status, service, handlers, }) {
|
|
|
57
199
|
completionMessage: null,
|
|
58
200
|
error: null,
|
|
59
201
|
});
|
|
60
|
-
|
|
61
|
-
}, [
|
|
202
|
+
errorHandlers?.onAborted('execution');
|
|
203
|
+
}, [
|
|
204
|
+
message,
|
|
205
|
+
summary,
|
|
206
|
+
taskInfos,
|
|
207
|
+
completed,
|
|
208
|
+
taskExecutionTimes,
|
|
209
|
+
stateHandlers,
|
|
210
|
+
errorHandlers,
|
|
211
|
+
]);
|
|
62
212
|
useInput((_, key) => {
|
|
63
213
|
if (key.escape && (isLoading || isExecuting) && isActive) {
|
|
64
214
|
handleCancel();
|
|
@@ -69,10 +219,6 @@ export function Execute({ tasks, state, status, service, handlers, }) {
|
|
|
69
219
|
if (!isActive || taskInfos.length > 0 || hasProcessed) {
|
|
70
220
|
return;
|
|
71
221
|
}
|
|
72
|
-
if (!service) {
|
|
73
|
-
setError('No service available');
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
222
|
let mounted = true;
|
|
77
223
|
async function process(svc) {
|
|
78
224
|
const startTime = Date.now();
|
|
@@ -95,10 +241,15 @@ export function Execute({ tasks, state, status, service, handlers, }) {
|
|
|
95
241
|
if (!mounted)
|
|
96
242
|
return;
|
|
97
243
|
// Add debug components to timeline if present
|
|
98
|
-
|
|
244
|
+
if (result.debug?.length) {
|
|
245
|
+
workflowHandlers?.addToTimeline(...result.debug);
|
|
246
|
+
}
|
|
99
247
|
if (!result.commands || result.commands.length === 0) {
|
|
100
|
-
|
|
101
|
-
|
|
248
|
+
dispatch({
|
|
249
|
+
type: 'PROCESSING_COMPLETE',
|
|
250
|
+
payload: { message: result.message },
|
|
251
|
+
});
|
|
252
|
+
stateHandlers?.updateState({
|
|
102
253
|
message: result.message,
|
|
103
254
|
summary: '',
|
|
104
255
|
taskInfos: [],
|
|
@@ -107,14 +258,15 @@ export function Execute({ tasks, state, status, service, handlers, }) {
|
|
|
107
258
|
completionMessage: null,
|
|
108
259
|
error: null,
|
|
109
260
|
});
|
|
110
|
-
|
|
261
|
+
lifecycleHandlers?.completeActive();
|
|
111
262
|
return;
|
|
112
263
|
}
|
|
113
264
|
// Resolve placeholders in command strings
|
|
114
|
-
const resolvedCommands = result.commands.map((cmd) =>
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
265
|
+
const resolvedCommands = result.commands.map((cmd) => {
|
|
266
|
+
const resolved = replacePlaceholders(cmd.command, userConfig);
|
|
267
|
+
validatePlaceholderResolution(resolved, cmd.command);
|
|
268
|
+
return { ...cmd, command: resolved };
|
|
269
|
+
});
|
|
118
270
|
// Set message, summary, and create task infos
|
|
119
271
|
const newMessage = result.message;
|
|
120
272
|
const newSummary = result.summary || '';
|
|
@@ -122,12 +274,16 @@ export function Execute({ tasks, state, status, service, handlers, }) {
|
|
|
122
274
|
label: tasks[index]?.action,
|
|
123
275
|
command: cmd,
|
|
124
276
|
}));
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
277
|
+
dispatch({
|
|
278
|
+
type: 'COMMANDS_READY',
|
|
279
|
+
payload: {
|
|
280
|
+
message: newMessage,
|
|
281
|
+
summary: newSummary,
|
|
282
|
+
taskInfos: infos,
|
|
283
|
+
},
|
|
284
|
+
});
|
|
129
285
|
// Update state after AI processing
|
|
130
|
-
|
|
286
|
+
stateHandlers?.updateState({
|
|
131
287
|
message: newMessage,
|
|
132
288
|
summary: newSummary,
|
|
133
289
|
taskInfos: infos,
|
|
@@ -141,9 +297,11 @@ export function Execute({ tasks, state, status, service, handlers, }) {
|
|
|
141
297
|
await ensureMinimumTime(startTime, MINIMUM_PROCESSING_TIME);
|
|
142
298
|
if (mounted) {
|
|
143
299
|
const errorMessage = formatErrorMessage(err);
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
300
|
+
dispatch({
|
|
301
|
+
type: 'PROCESSING_ERROR',
|
|
302
|
+
payload: { error: errorMessage },
|
|
303
|
+
});
|
|
304
|
+
stateHandlers?.updateState({
|
|
147
305
|
message: '',
|
|
148
306
|
summary: '',
|
|
149
307
|
taskInfos: [],
|
|
@@ -152,28 +310,35 @@ export function Execute({ tasks, state, status, service, handlers, }) {
|
|
|
152
310
|
completionMessage: null,
|
|
153
311
|
error: errorMessage,
|
|
154
312
|
});
|
|
155
|
-
|
|
313
|
+
errorHandlers?.onError(errorMessage);
|
|
156
314
|
}
|
|
157
315
|
}
|
|
158
316
|
}
|
|
159
|
-
process(service);
|
|
317
|
+
void process(service);
|
|
160
318
|
return () => {
|
|
161
319
|
mounted = false;
|
|
162
320
|
};
|
|
163
|
-
}, [
|
|
321
|
+
}, [
|
|
322
|
+
tasks,
|
|
323
|
+
isActive,
|
|
324
|
+
service,
|
|
325
|
+
stateHandlers,
|
|
326
|
+
lifecycleHandlers,
|
|
327
|
+
workflowHandlers,
|
|
328
|
+
errorHandlers,
|
|
329
|
+
taskInfos.length,
|
|
330
|
+
hasProcessed,
|
|
331
|
+
]);
|
|
164
332
|
// Handle task completion - move to next task
|
|
165
333
|
const handleTaskComplete = useCallback((index, _output, elapsed) => {
|
|
166
|
-
const updatedTimes = [...taskExecutionTimes, elapsed];
|
|
167
|
-
setTaskExecutionTimes(updatedTimes);
|
|
168
|
-
// Update task with elapsed time and success status
|
|
169
|
-
const updatedTaskInfos = taskInfos.map((task, i) => i === index
|
|
170
|
-
? { ...task, status: ExecutionStatus.Success, elapsed }
|
|
171
|
-
: task);
|
|
172
|
-
setTaskInfos(updatedTaskInfos);
|
|
173
334
|
if (index < taskInfos.length - 1) {
|
|
174
335
|
// More tasks to execute
|
|
175
|
-
|
|
176
|
-
|
|
336
|
+
dispatch({ type: 'TASK_COMPLETE', payload: { index, elapsed } });
|
|
337
|
+
const updatedTimes = [...taskExecutionTimes, elapsed];
|
|
338
|
+
const updatedTaskInfos = taskInfos.map((task, i) => i === index
|
|
339
|
+
? { ...task, status: ExecutionStatus.Success, elapsed }
|
|
340
|
+
: task);
|
|
341
|
+
stateHandlers?.updateState({
|
|
177
342
|
message,
|
|
178
343
|
summary,
|
|
179
344
|
taskInfos: updatedTaskInfos,
|
|
@@ -185,11 +350,18 @@ export function Execute({ tasks, state, status, service, handlers, }) {
|
|
|
185
350
|
}
|
|
186
351
|
else {
|
|
187
352
|
// All tasks complete
|
|
353
|
+
const summaryText = summary.trim() || 'Execution completed';
|
|
354
|
+
dispatch({
|
|
355
|
+
type: 'ALL_TASKS_COMPLETE',
|
|
356
|
+
payload: { index, elapsed, summaryText },
|
|
357
|
+
});
|
|
358
|
+
const updatedTimes = [...taskExecutionTimes, elapsed];
|
|
359
|
+
const updatedTaskInfos = taskInfos.map((task, i) => i === index
|
|
360
|
+
? { ...task, status: ExecutionStatus.Success, elapsed }
|
|
361
|
+
: task);
|
|
188
362
|
const totalElapsed = updatedTimes.reduce((sum, time) => sum + time, 0);
|
|
189
|
-
const summaryText = summary?.trim() || 'Execution completed';
|
|
190
363
|
const completion = `${summaryText} in ${formatDuration(totalElapsed)}.`;
|
|
191
|
-
|
|
192
|
-
handlers?.updateState({
|
|
364
|
+
stateHandlers?.updateState({
|
|
193
365
|
message,
|
|
194
366
|
summary,
|
|
195
367
|
taskInfos: updatedTaskInfos,
|
|
@@ -198,21 +370,26 @@ export function Execute({ tasks, state, status, service, handlers, }) {
|
|
|
198
370
|
completionMessage: completion,
|
|
199
371
|
error: null,
|
|
200
372
|
});
|
|
201
|
-
|
|
373
|
+
lifecycleHandlers?.completeActive();
|
|
202
374
|
}
|
|
203
|
-
}, [
|
|
375
|
+
}, [
|
|
376
|
+
taskInfos,
|
|
377
|
+
message,
|
|
378
|
+
lifecycleHandlers,
|
|
379
|
+
taskExecutionTimes,
|
|
380
|
+
summary,
|
|
381
|
+
stateHandlers,
|
|
382
|
+
]);
|
|
204
383
|
const handleTaskError = useCallback((index, error, elapsed) => {
|
|
205
384
|
const task = taskInfos[index];
|
|
206
|
-
const isCritical = task
|
|
207
|
-
// Update task with elapsed time and failed status
|
|
385
|
+
const isCritical = task.command.critical !== false; // Default to true
|
|
208
386
|
const updatedTaskInfos = taskInfos.map((task, i) => i === index
|
|
209
387
|
? { ...task, status: ExecutionStatus.Failed, elapsed }
|
|
210
388
|
: task);
|
|
211
|
-
setTaskInfos(updatedTaskInfos);
|
|
212
389
|
if (isCritical) {
|
|
213
390
|
// Critical failure - stop execution
|
|
214
|
-
|
|
215
|
-
|
|
391
|
+
dispatch({ type: 'TASK_ERROR_CRITICAL', payload: { index, error } });
|
|
392
|
+
stateHandlers?.updateState({
|
|
216
393
|
message,
|
|
217
394
|
summary,
|
|
218
395
|
taskInfos: updatedTaskInfos,
|
|
@@ -221,15 +398,17 @@ export function Execute({ tasks, state, status, service, handlers, }) {
|
|
|
221
398
|
completionMessage: null,
|
|
222
399
|
error,
|
|
223
400
|
});
|
|
224
|
-
|
|
401
|
+
errorHandlers?.onError(error);
|
|
225
402
|
}
|
|
226
403
|
else {
|
|
227
404
|
// Non-critical failure - continue to next task
|
|
228
405
|
const updatedTimes = [...taskExecutionTimes, elapsed];
|
|
229
|
-
setTaskExecutionTimes(updatedTimes);
|
|
230
406
|
if (index < taskInfos.length - 1) {
|
|
231
|
-
|
|
232
|
-
|
|
407
|
+
dispatch({
|
|
408
|
+
type: 'TASK_ERROR_CONTINUE',
|
|
409
|
+
payload: { index, elapsed },
|
|
410
|
+
});
|
|
411
|
+
stateHandlers?.updateState({
|
|
233
412
|
message,
|
|
234
413
|
summary,
|
|
235
414
|
taskInfos: updatedTaskInfos,
|
|
@@ -241,11 +420,14 @@ export function Execute({ tasks, state, status, service, handlers, }) {
|
|
|
241
420
|
}
|
|
242
421
|
else {
|
|
243
422
|
// Last task, complete execution
|
|
423
|
+
const summaryText = summary.trim() || 'Execution completed';
|
|
424
|
+
dispatch({
|
|
425
|
+
type: 'LAST_TASK_ERROR',
|
|
426
|
+
payload: { index, elapsed, summaryText },
|
|
427
|
+
});
|
|
244
428
|
const totalElapsed = updatedTimes.reduce((sum, time) => sum + time, 0);
|
|
245
|
-
const summaryText = summary?.trim() || 'Execution completed';
|
|
246
429
|
const completion = `${summaryText} in ${formatDuration(totalElapsed)}.`;
|
|
247
|
-
|
|
248
|
-
handlers?.updateState({
|
|
430
|
+
stateHandlers?.updateState({
|
|
249
431
|
message,
|
|
250
432
|
summary,
|
|
251
433
|
taskInfos: updatedTaskInfos,
|
|
@@ -254,14 +436,22 @@ export function Execute({ tasks, state, status, service, handlers, }) {
|
|
|
254
436
|
completionMessage: completion,
|
|
255
437
|
error: null,
|
|
256
438
|
});
|
|
257
|
-
|
|
439
|
+
lifecycleHandlers?.completeActive();
|
|
258
440
|
}
|
|
259
441
|
}
|
|
260
|
-
}, [
|
|
442
|
+
}, [
|
|
443
|
+
taskInfos,
|
|
444
|
+
message,
|
|
445
|
+
stateHandlers,
|
|
446
|
+
lifecycleHandlers,
|
|
447
|
+
errorHandlers,
|
|
448
|
+
taskExecutionTimes,
|
|
449
|
+
summary,
|
|
450
|
+
]);
|
|
261
451
|
const handleTaskAbort = useCallback((_index) => {
|
|
262
452
|
// Task was aborted - execution already stopped by Escape handler
|
|
263
453
|
// Just update state, don't call onAborted (already called at Execute level)
|
|
264
|
-
|
|
454
|
+
stateHandlers?.updateState({
|
|
265
455
|
message,
|
|
266
456
|
summary,
|
|
267
457
|
taskInfos,
|
|
@@ -270,7 +460,7 @@ export function Execute({ tasks, state, status, service, handlers, }) {
|
|
|
270
460
|
completionMessage: null,
|
|
271
461
|
error: null,
|
|
272
462
|
});
|
|
273
|
-
}, [taskInfos, message, summary, completed, taskExecutionTimes,
|
|
463
|
+
}, [taskInfos, message, summary, completed, taskExecutionTimes, stateHandlers]);
|
|
274
464
|
// Return null only when loading completes with no commands
|
|
275
465
|
if (!isActive && taskInfos.length === 0 && !error) {
|
|
276
466
|
return null;
|
package/dist/ui/Feedback.js
CHANGED
package/dist/ui/Introspect.js
CHANGED
|
@@ -1,64 +1,22 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useState } from 'react';
|
|
3
3
|
import { Box, Text } from 'ink';
|
|
4
|
-
import { ComponentStatus
|
|
4
|
+
import { ComponentStatus } from '../types/components.js';
|
|
5
5
|
import { Colors, getTextColor } from '../services/colors.js';
|
|
6
|
-
import {
|
|
6
|
+
import { createReportDefinition } from '../services/components.js';
|
|
7
7
|
import { DebugLevel } from '../services/configuration.js';
|
|
8
8
|
import { useInput } from '../services/keyboard.js';
|
|
9
9
|
import { formatErrorMessage } from '../services/messages.js';
|
|
10
10
|
import { ensureMinimumTime } from '../services/timing.js';
|
|
11
11
|
import { Spinner } from './Spinner.js';
|
|
12
12
|
const MIN_PROCESSING_TIME = 1000;
|
|
13
|
-
|
|
14
|
-
'CONFIGURE',
|
|
15
|
-
'SCHEDULE',
|
|
16
|
-
'INTROSPECT',
|
|
17
|
-
'ANSWER',
|
|
18
|
-
'EXECUTE',
|
|
19
|
-
'VALIDATE',
|
|
20
|
-
'REPORT',
|
|
21
|
-
]);
|
|
22
|
-
const INDIRECT_CAPABILITIES = new Set(['SCHEDULE', 'VALIDATE', 'REPORT']);
|
|
23
|
-
function parseCapabilityFromTask(task) {
|
|
24
|
-
// Parse "NAME: Description" format from task.action
|
|
25
|
-
const colonIndex = task.action.indexOf(':');
|
|
26
|
-
if (colonIndex === -1) {
|
|
27
|
-
const upperName = task.action.toUpperCase();
|
|
28
|
-
// Check for status markers
|
|
29
|
-
const isIncomplete = task.action.includes('(INCOMPLETE)');
|
|
30
|
-
const cleanName = task.action.replace(/\s*\(INCOMPLETE\)\s*/gi, '').trim();
|
|
31
|
-
return {
|
|
32
|
-
name: cleanName,
|
|
33
|
-
description: '',
|
|
34
|
-
isBuiltIn: BUILT_IN_CAPABILITIES.has(upperName),
|
|
35
|
-
isIndirect: INDIRECT_CAPABILITIES.has(upperName),
|
|
36
|
-
isIncomplete,
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
const name = task.action.substring(0, colonIndex).trim();
|
|
40
|
-
const description = task.action.substring(colonIndex + 1).trim();
|
|
41
|
-
// Check for status markers
|
|
42
|
-
const isIncomplete = name.includes('(INCOMPLETE)');
|
|
43
|
-
const cleanName = name.replace(/\s*\(INCOMPLETE\)\s*/gi, '').trim();
|
|
44
|
-
const upperName = cleanName.toUpperCase();
|
|
45
|
-
const isBuiltIn = BUILT_IN_CAPABILITIES.has(upperName);
|
|
46
|
-
const isIndirect = INDIRECT_CAPABILITIES.has(upperName);
|
|
47
|
-
return {
|
|
48
|
-
name: cleanName,
|
|
49
|
-
description,
|
|
50
|
-
isBuiltIn,
|
|
51
|
-
isIndirect,
|
|
52
|
-
isIncomplete,
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
export function Introspect({ tasks, state, status, service, children, debug = DebugLevel.None, handlers, }) {
|
|
13
|
+
export function Introspect({ tasks, state: _state, status, service, children, debug = DebugLevel.None, stateHandlers, lifecycleHandlers, queueHandlers, errorHandlers, workflowHandlers, }) {
|
|
56
14
|
const isActive = status === ComponentStatus.Active;
|
|
57
15
|
// isActive passed as prop
|
|
58
16
|
const [error, setError] = useState(null);
|
|
59
17
|
useInput((input, key) => {
|
|
60
18
|
if (key.escape && isActive) {
|
|
61
|
-
|
|
19
|
+
errorHandlers?.onAborted('introspection');
|
|
62
20
|
}
|
|
63
21
|
}, { isActive });
|
|
64
22
|
useEffect(() => {
|
|
@@ -66,11 +24,6 @@ export function Introspect({ tasks, state, status, service, children, debug = De
|
|
|
66
24
|
if (!isActive) {
|
|
67
25
|
return;
|
|
68
26
|
}
|
|
69
|
-
// Skip processing if no service available
|
|
70
|
-
if (!service) {
|
|
71
|
-
setError('No service available');
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
27
|
let mounted = true;
|
|
75
28
|
async function process(svc) {
|
|
76
29
|
const startTime = Date.now();
|
|
@@ -82,9 +35,11 @@ export function Introspect({ tasks, state, status, service, children, debug = De
|
|
|
82
35
|
await ensureMinimumTime(startTime, MIN_PROCESSING_TIME);
|
|
83
36
|
if (mounted) {
|
|
84
37
|
// Add debug components to timeline if present
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
38
|
+
if (result.debug?.length) {
|
|
39
|
+
workflowHandlers?.addToTimeline(...result.debug);
|
|
40
|
+
}
|
|
41
|
+
// Capabilities come directly from result - no parsing needed
|
|
42
|
+
let capabilities = result.capabilities;
|
|
88
43
|
// Filter out internal capabilities when not in debug mode
|
|
89
44
|
if (debug === DebugLevel.None) {
|
|
90
45
|
capabilities = capabilities.filter((cap) => cap.name.toUpperCase() !== 'SCHEDULE' &&
|
|
@@ -92,14 +47,14 @@ export function Introspect({ tasks, state, status, service, children, debug = De
|
|
|
92
47
|
cap.name.toUpperCase() !== 'REPORT');
|
|
93
48
|
}
|
|
94
49
|
// Save state before completing
|
|
95
|
-
|
|
50
|
+
stateHandlers?.updateState({
|
|
96
51
|
capabilities,
|
|
97
52
|
message: result.message,
|
|
98
53
|
});
|
|
99
54
|
// Add Report component to queue
|
|
100
|
-
|
|
55
|
+
queueHandlers?.addToQueue(createReportDefinition(result.message, capabilities));
|
|
101
56
|
// Signal completion
|
|
102
|
-
|
|
57
|
+
lifecycleHandlers?.completeActive();
|
|
103
58
|
}
|
|
104
59
|
}
|
|
105
60
|
catch (err) {
|
|
@@ -108,18 +63,18 @@ export function Introspect({ tasks, state, status, service, children, debug = De
|
|
|
108
63
|
const errorMessage = formatErrorMessage(err);
|
|
109
64
|
setError(errorMessage);
|
|
110
65
|
// Save error state
|
|
111
|
-
|
|
66
|
+
stateHandlers?.updateState({
|
|
112
67
|
error: errorMessage,
|
|
113
68
|
});
|
|
114
|
-
|
|
69
|
+
errorHandlers?.onError(errorMessage);
|
|
115
70
|
}
|
|
116
71
|
}
|
|
117
72
|
}
|
|
118
|
-
process(service);
|
|
73
|
+
void process(service);
|
|
119
74
|
return () => {
|
|
120
75
|
mounted = false;
|
|
121
76
|
};
|
|
122
|
-
}, [tasks, isActive, service, debug
|
|
77
|
+
}, [tasks, isActive, service, debug]);
|
|
123
78
|
// Don't render wrapper when done and nothing to show
|
|
124
79
|
if (!isActive && !error && !children) {
|
|
125
80
|
return null;
|