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/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();
|
|
@@ -91,10 +241,15 @@ export function Execute({ tasks, state, status, service, handlers, }) {
|
|
|
91
241
|
if (!mounted)
|
|
92
242
|
return;
|
|
93
243
|
// Add debug components to timeline if present
|
|
94
|
-
|
|
244
|
+
if (result.debug?.length) {
|
|
245
|
+
workflowHandlers?.addToTimeline(...result.debug);
|
|
246
|
+
}
|
|
95
247
|
if (!result.commands || result.commands.length === 0) {
|
|
96
|
-
|
|
97
|
-
|
|
248
|
+
dispatch({
|
|
249
|
+
type: 'PROCESSING_COMPLETE',
|
|
250
|
+
payload: { message: result.message },
|
|
251
|
+
});
|
|
252
|
+
stateHandlers?.updateState({
|
|
98
253
|
message: result.message,
|
|
99
254
|
summary: '',
|
|
100
255
|
taskInfos: [],
|
|
@@ -103,14 +258,15 @@ export function Execute({ tasks, state, status, service, handlers, }) {
|
|
|
103
258
|
completionMessage: null,
|
|
104
259
|
error: null,
|
|
105
260
|
});
|
|
106
|
-
|
|
261
|
+
lifecycleHandlers?.completeActive();
|
|
107
262
|
return;
|
|
108
263
|
}
|
|
109
264
|
// Resolve placeholders in command strings
|
|
110
|
-
const resolvedCommands = result.commands.map((cmd) =>
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
+
});
|
|
114
270
|
// Set message, summary, and create task infos
|
|
115
271
|
const newMessage = result.message;
|
|
116
272
|
const newSummary = result.summary || '';
|
|
@@ -118,12 +274,16 @@ export function Execute({ tasks, state, status, service, handlers, }) {
|
|
|
118
274
|
label: tasks[index]?.action,
|
|
119
275
|
command: cmd,
|
|
120
276
|
}));
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
277
|
+
dispatch({
|
|
278
|
+
type: 'COMMANDS_READY',
|
|
279
|
+
payload: {
|
|
280
|
+
message: newMessage,
|
|
281
|
+
summary: newSummary,
|
|
282
|
+
taskInfos: infos,
|
|
283
|
+
},
|
|
284
|
+
});
|
|
125
285
|
// Update state after AI processing
|
|
126
|
-
|
|
286
|
+
stateHandlers?.updateState({
|
|
127
287
|
message: newMessage,
|
|
128
288
|
summary: newSummary,
|
|
129
289
|
taskInfos: infos,
|
|
@@ -137,9 +297,11 @@ export function Execute({ tasks, state, status, service, handlers, }) {
|
|
|
137
297
|
await ensureMinimumTime(startTime, MINIMUM_PROCESSING_TIME);
|
|
138
298
|
if (mounted) {
|
|
139
299
|
const errorMessage = formatErrorMessage(err);
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
300
|
+
dispatch({
|
|
301
|
+
type: 'PROCESSING_ERROR',
|
|
302
|
+
payload: { error: errorMessage },
|
|
303
|
+
});
|
|
304
|
+
stateHandlers?.updateState({
|
|
143
305
|
message: '',
|
|
144
306
|
summary: '',
|
|
145
307
|
taskInfos: [],
|
|
@@ -148,7 +310,7 @@ export function Execute({ tasks, state, status, service, handlers, }) {
|
|
|
148
310
|
completionMessage: null,
|
|
149
311
|
error: errorMessage,
|
|
150
312
|
});
|
|
151
|
-
|
|
313
|
+
errorHandlers?.onError(errorMessage);
|
|
152
314
|
}
|
|
153
315
|
}
|
|
154
316
|
}
|
|
@@ -156,20 +318,27 @@ export function Execute({ tasks, state, status, service, handlers, }) {
|
|
|
156
318
|
return () => {
|
|
157
319
|
mounted = false;
|
|
158
320
|
};
|
|
159
|
-
}, [
|
|
321
|
+
}, [
|
|
322
|
+
tasks,
|
|
323
|
+
isActive,
|
|
324
|
+
service,
|
|
325
|
+
stateHandlers,
|
|
326
|
+
lifecycleHandlers,
|
|
327
|
+
workflowHandlers,
|
|
328
|
+
errorHandlers,
|
|
329
|
+
taskInfos.length,
|
|
330
|
+
hasProcessed,
|
|
331
|
+
]);
|
|
160
332
|
// Handle task completion - move to next task
|
|
161
333
|
const handleTaskComplete = useCallback((index, _output, elapsed) => {
|
|
162
|
-
const updatedTimes = [...taskExecutionTimes, elapsed];
|
|
163
|
-
setTaskExecutionTimes(updatedTimes);
|
|
164
|
-
// Update task with elapsed time and success status
|
|
165
|
-
const updatedTaskInfos = taskInfos.map((task, i) => i === index
|
|
166
|
-
? { ...task, status: ExecutionStatus.Success, elapsed }
|
|
167
|
-
: task);
|
|
168
|
-
setTaskInfos(updatedTaskInfos);
|
|
169
334
|
if (index < taskInfos.length - 1) {
|
|
170
335
|
// More tasks to execute
|
|
171
|
-
|
|
172
|
-
|
|
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({
|
|
173
342
|
message,
|
|
174
343
|
summary,
|
|
175
344
|
taskInfos: updatedTaskInfos,
|
|
@@ -181,11 +350,18 @@ export function Execute({ tasks, state, status, service, handlers, }) {
|
|
|
181
350
|
}
|
|
182
351
|
else {
|
|
183
352
|
// All tasks complete
|
|
184
|
-
const totalElapsed = updatedTimes.reduce((sum, time) => sum + time, 0);
|
|
185
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);
|
|
362
|
+
const totalElapsed = updatedTimes.reduce((sum, time) => sum + time, 0);
|
|
186
363
|
const completion = `${summaryText} in ${formatDuration(totalElapsed)}.`;
|
|
187
|
-
|
|
188
|
-
handlers?.updateState({
|
|
364
|
+
stateHandlers?.updateState({
|
|
189
365
|
message,
|
|
190
366
|
summary,
|
|
191
367
|
taskInfos: updatedTaskInfos,
|
|
@@ -194,21 +370,26 @@ export function Execute({ tasks, state, status, service, handlers, }) {
|
|
|
194
370
|
completionMessage: completion,
|
|
195
371
|
error: null,
|
|
196
372
|
});
|
|
197
|
-
|
|
373
|
+
lifecycleHandlers?.completeActive();
|
|
198
374
|
}
|
|
199
|
-
}, [
|
|
375
|
+
}, [
|
|
376
|
+
taskInfos,
|
|
377
|
+
message,
|
|
378
|
+
lifecycleHandlers,
|
|
379
|
+
taskExecutionTimes,
|
|
380
|
+
summary,
|
|
381
|
+
stateHandlers,
|
|
382
|
+
]);
|
|
200
383
|
const handleTaskError = useCallback((index, error, elapsed) => {
|
|
201
384
|
const task = taskInfos[index];
|
|
202
385
|
const isCritical = task.command.critical !== false; // Default to true
|
|
203
|
-
// Update task with elapsed time and failed status
|
|
204
386
|
const updatedTaskInfos = taskInfos.map((task, i) => i === index
|
|
205
387
|
? { ...task, status: ExecutionStatus.Failed, elapsed }
|
|
206
388
|
: task);
|
|
207
|
-
setTaskInfos(updatedTaskInfos);
|
|
208
389
|
if (isCritical) {
|
|
209
390
|
// Critical failure - stop execution
|
|
210
|
-
|
|
211
|
-
|
|
391
|
+
dispatch({ type: 'TASK_ERROR_CRITICAL', payload: { index, error } });
|
|
392
|
+
stateHandlers?.updateState({
|
|
212
393
|
message,
|
|
213
394
|
summary,
|
|
214
395
|
taskInfos: updatedTaskInfos,
|
|
@@ -217,15 +398,17 @@ export function Execute({ tasks, state, status, service, handlers, }) {
|
|
|
217
398
|
completionMessage: null,
|
|
218
399
|
error,
|
|
219
400
|
});
|
|
220
|
-
|
|
401
|
+
errorHandlers?.onError(error);
|
|
221
402
|
}
|
|
222
403
|
else {
|
|
223
404
|
// Non-critical failure - continue to next task
|
|
224
405
|
const updatedTimes = [...taskExecutionTimes, elapsed];
|
|
225
|
-
setTaskExecutionTimes(updatedTimes);
|
|
226
406
|
if (index < taskInfos.length - 1) {
|
|
227
|
-
|
|
228
|
-
|
|
407
|
+
dispatch({
|
|
408
|
+
type: 'TASK_ERROR_CONTINUE',
|
|
409
|
+
payload: { index, elapsed },
|
|
410
|
+
});
|
|
411
|
+
stateHandlers?.updateState({
|
|
229
412
|
message,
|
|
230
413
|
summary,
|
|
231
414
|
taskInfos: updatedTaskInfos,
|
|
@@ -237,11 +420,14 @@ export function Execute({ tasks, state, status, service, handlers, }) {
|
|
|
237
420
|
}
|
|
238
421
|
else {
|
|
239
422
|
// Last task, complete execution
|
|
240
|
-
const totalElapsed = updatedTimes.reduce((sum, time) => sum + time, 0);
|
|
241
423
|
const summaryText = summary.trim() || 'Execution completed';
|
|
424
|
+
dispatch({
|
|
425
|
+
type: 'LAST_TASK_ERROR',
|
|
426
|
+
payload: { index, elapsed, summaryText },
|
|
427
|
+
});
|
|
428
|
+
const totalElapsed = updatedTimes.reduce((sum, time) => sum + time, 0);
|
|
242
429
|
const completion = `${summaryText} in ${formatDuration(totalElapsed)}.`;
|
|
243
|
-
|
|
244
|
-
handlers?.updateState({
|
|
430
|
+
stateHandlers?.updateState({
|
|
245
431
|
message,
|
|
246
432
|
summary,
|
|
247
433
|
taskInfos: updatedTaskInfos,
|
|
@@ -250,14 +436,22 @@ export function Execute({ tasks, state, status, service, handlers, }) {
|
|
|
250
436
|
completionMessage: completion,
|
|
251
437
|
error: null,
|
|
252
438
|
});
|
|
253
|
-
|
|
439
|
+
lifecycleHandlers?.completeActive();
|
|
254
440
|
}
|
|
255
441
|
}
|
|
256
|
-
}, [
|
|
442
|
+
}, [
|
|
443
|
+
taskInfos,
|
|
444
|
+
message,
|
|
445
|
+
stateHandlers,
|
|
446
|
+
lifecycleHandlers,
|
|
447
|
+
errorHandlers,
|
|
448
|
+
taskExecutionTimes,
|
|
449
|
+
summary,
|
|
450
|
+
]);
|
|
257
451
|
const handleTaskAbort = useCallback((_index) => {
|
|
258
452
|
// Task was aborted - execution already stopped by Escape handler
|
|
259
453
|
// Just update state, don't call onAborted (already called at Execute level)
|
|
260
|
-
|
|
454
|
+
stateHandlers?.updateState({
|
|
261
455
|
message,
|
|
262
456
|
summary,
|
|
263
457
|
taskInfos,
|
|
@@ -266,7 +460,7 @@ export function Execute({ tasks, state, status, service, handlers, }) {
|
|
|
266
460
|
completionMessage: null,
|
|
267
461
|
error: null,
|
|
268
462
|
});
|
|
269
|
-
}, [taskInfos, message, summary, completed, taskExecutionTimes,
|
|
463
|
+
}, [taskInfos, message, summary, completed, taskExecutionTimes, stateHandlers]);
|
|
270
464
|
// Return null only when loading completes with no commands
|
|
271
465
|
if (!isActive && taskInfos.length === 0 && !error) {
|
|
272
466
|
return null;
|
package/dist/ui/Feedback.js
CHANGED
package/dist/ui/Introspect.js
CHANGED
|
@@ -3,20 +3,20 @@ import { useEffect, useState } 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 {
|
|
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
|
-
export function Introspect({ tasks, state: _state, status, service, children, debug = DebugLevel.None,
|
|
13
|
+
export function Introspect({ tasks, state: _state, status, service, children, debug = DebugLevel.None, stateHandlers, lifecycleHandlers, queueHandlers, errorHandlers, workflowHandlers, }) {
|
|
14
14
|
const isActive = status === ComponentStatus.Active;
|
|
15
15
|
// isActive passed as prop
|
|
16
16
|
const [error, setError] = useState(null);
|
|
17
17
|
useInput((input, key) => {
|
|
18
18
|
if (key.escape && isActive) {
|
|
19
|
-
|
|
19
|
+
errorHandlers?.onAborted('introspection');
|
|
20
20
|
}
|
|
21
21
|
}, { isActive });
|
|
22
22
|
useEffect(() => {
|
|
@@ -35,7 +35,9 @@ export function Introspect({ tasks, state: _state, status, service, children, de
|
|
|
35
35
|
await ensureMinimumTime(startTime, MIN_PROCESSING_TIME);
|
|
36
36
|
if (mounted) {
|
|
37
37
|
// Add debug components to timeline if present
|
|
38
|
-
|
|
38
|
+
if (result.debug?.length) {
|
|
39
|
+
workflowHandlers?.addToTimeline(...result.debug);
|
|
40
|
+
}
|
|
39
41
|
// Capabilities come directly from result - no parsing needed
|
|
40
42
|
let capabilities = result.capabilities;
|
|
41
43
|
// Filter out internal capabilities when not in debug mode
|
|
@@ -45,14 +47,14 @@ export function Introspect({ tasks, state: _state, status, service, children, de
|
|
|
45
47
|
cap.name.toUpperCase() !== 'REPORT');
|
|
46
48
|
}
|
|
47
49
|
// Save state before completing
|
|
48
|
-
|
|
50
|
+
stateHandlers?.updateState({
|
|
49
51
|
capabilities,
|
|
50
52
|
message: result.message,
|
|
51
53
|
});
|
|
52
54
|
// Add Report component to queue
|
|
53
|
-
|
|
55
|
+
queueHandlers?.addToQueue(createReportDefinition(result.message, capabilities));
|
|
54
56
|
// Signal completion
|
|
55
|
-
|
|
57
|
+
lifecycleHandlers?.completeActive();
|
|
56
58
|
}
|
|
57
59
|
}
|
|
58
60
|
catch (err) {
|
|
@@ -61,10 +63,10 @@ export function Introspect({ tasks, state: _state, status, service, children, de
|
|
|
61
63
|
const errorMessage = formatErrorMessage(err);
|
|
62
64
|
setError(errorMessage);
|
|
63
65
|
// Save error state
|
|
64
|
-
|
|
66
|
+
stateHandlers?.updateState({
|
|
65
67
|
error: errorMessage,
|
|
66
68
|
});
|
|
67
|
-
|
|
69
|
+
errorHandlers?.onError(errorMessage);
|
|
68
70
|
}
|
|
69
71
|
}
|
|
70
72
|
}
|
|
@@ -72,7 +74,7 @@ export function Introspect({ tasks, state: _state, status, service, children, de
|
|
|
72
74
|
return () => {
|
|
73
75
|
mounted = false;
|
|
74
76
|
};
|
|
75
|
-
}, [tasks, isActive, service, debug
|
|
77
|
+
}, [tasks, isActive, service, debug]);
|
|
76
78
|
// Don't render wrapper when done and nothing to show
|
|
77
79
|
if (!isActive && !error && !children) {
|
|
78
80
|
return null;
|
package/dist/ui/Main.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useState } from 'react';
|
|
3
3
|
import { FeedbackType } from '../types/types.js';
|
|
4
|
-
import { createAnthropicService
|
|
4
|
+
import { createAnthropicService } from '../services/anthropic.js';
|
|
5
5
|
import { createCommandDefinition, createConfigDefinitionWithKeys, createFeedback, createMessage, createWelcomeDefinition, } from '../services/components.js';
|
|
6
6
|
import { DebugLevel, getConfigurationRequiredMessage, getMissingConfigKeys, loadConfig, loadDebugSetting, saveConfig, saveDebugSetting, unflattenConfig, } from '../services/configuration.js';
|
|
7
7
|
import { registerGlobalShortcut } from '../services/keyboard.js';
|
|
8
8
|
import { initializeLogger, setDebugLevel } from '../services/logger.js';
|
|
9
9
|
import { Workflow } from './Workflow.js';
|
|
10
|
-
export const Main = ({ app, command }) => {
|
|
10
|
+
export const Main = ({ app, command, serviceFactory = createAnthropicService, }) => {
|
|
11
11
|
const [service, setService] = useState(null);
|
|
12
12
|
const [initialQueue, setInitialQueue] = useState(null);
|
|
13
13
|
const [debug, setDebugLevelState] = useState(() => loadDebugSetting());
|
|
@@ -44,7 +44,7 @@ export const Main = ({ app, command }) => {
|
|
|
44
44
|
// Config exists - create service immediately
|
|
45
45
|
try {
|
|
46
46
|
const config = loadConfig();
|
|
47
|
-
const newService =
|
|
47
|
+
const newService = serviceFactory(config.anthropic);
|
|
48
48
|
setService(newService);
|
|
49
49
|
}
|
|
50
50
|
catch (error) {
|
|
@@ -56,7 +56,7 @@ export const Main = ({ app, command }) => {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
// If config is missing, service will be created after config completes
|
|
59
|
-
}, [service]);
|
|
59
|
+
}, [service, serviceFactory]);
|
|
60
60
|
// Initialize queue after service is ready
|
|
61
61
|
useEffect(() => {
|
|
62
62
|
// Only set initial queue once
|
|
@@ -75,7 +75,7 @@ export const Main = ({ app, command }) => {
|
|
|
75
75
|
}
|
|
76
76
|
// Load config and create service
|
|
77
77
|
const newConfig = loadConfig();
|
|
78
|
-
const newService =
|
|
78
|
+
const newService = serviceFactory(newConfig.anthropic);
|
|
79
79
|
setService(newService);
|
|
80
80
|
}
|
|
81
81
|
catch (error) {
|