prompt-language-shell 0.8.8 → 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/README.md +0 -1
- package/dist/configuration/io.js +22 -1
- 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/configuration/types.js +3 -4
- package/dist/execution/handlers.js +20 -35
- package/dist/execution/hooks.js +291 -0
- package/dist/execution/processing.js +15 -2
- package/dist/execution/reducer.js +30 -48
- package/dist/execution/runner.js +81 -0
- package/dist/execution/types.js +1 -0
- package/dist/execution/utils.js +28 -0
- package/dist/services/components.js +109 -395
- package/dist/services/filesystem.js +21 -1
- package/dist/services/logger.js +3 -3
- package/dist/services/messages.js +10 -16
- package/dist/services/process.js +7 -2
- package/dist/services/refinement.js +5 -2
- package/dist/services/router.js +120 -67
- package/dist/services/shell.js +179 -10
- package/dist/services/skills.js +2 -1
- package/dist/skills/answer.md +14 -12
- package/dist/skills/execute.md +98 -39
- package/dist/skills/introspect.md +9 -9
- package/dist/skills/schedule.md +0 -6
- package/dist/types/errors.js +47 -0
- package/dist/types/result.js +40 -0
- package/dist/ui/Command.js +11 -7
- package/dist/ui/Component.js +6 -3
- package/dist/ui/Config.js +9 -3
- package/dist/ui/Execute.js +249 -163
- package/dist/ui/Introspect.js +13 -14
- package/dist/ui/List.js +2 -2
- package/dist/ui/Main.js +14 -7
- package/dist/ui/Output.js +54 -0
- package/dist/ui/Schedule.js +3 -1
- package/dist/ui/Subtask.js +6 -3
- package/dist/ui/Task.js +10 -85
- 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/Execute.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback, useEffect, useReducer } from 'react';
|
|
2
|
+
import { useCallback, useEffect, useReducer, useRef, useState } from 'react';
|
|
3
3
|
import { Box, Text } from 'ink';
|
|
4
4
|
import { ComponentStatus, } from '../types/components.js';
|
|
5
5
|
import { getTextColor } from '../services/colors.js';
|
|
@@ -7,93 +7,155 @@ import { useInput } from '../services/keyboard.js';
|
|
|
7
7
|
import { formatErrorMessage, getExecutionErrorMessage, } from '../services/messages.js';
|
|
8
8
|
import { ExecutionStatus } from '../services/shell.js';
|
|
9
9
|
import { ensureMinimumTime } from '../services/timing.js';
|
|
10
|
-
import {
|
|
10
|
+
import { handleTaskCompletion, handleTaskFailure, } from '../execution/handlers.js';
|
|
11
11
|
import { processTasks } from '../execution/processing.js';
|
|
12
12
|
import { executeReducer, initialState } from '../execution/reducer.js';
|
|
13
|
+
import { executeTask } from '../execution/runner.js';
|
|
13
14
|
import { ExecuteActionType } from '../execution/types.js';
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
15
|
+
import { getCurrentTaskIndex } from '../execution/utils.js';
|
|
16
|
+
import { createFeedback, createMessage } from '../services/components.js';
|
|
17
|
+
import { FeedbackType } from '../types/types.js';
|
|
16
18
|
import { Spinner } from './Spinner.js';
|
|
17
|
-
import {
|
|
19
|
+
import { TaskView } from './Task.js';
|
|
18
20
|
const MINIMUM_PROCESSING_TIME = 400;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
const ELAPSED_UPDATE_INTERVAL = 1000;
|
|
22
|
+
/**
|
|
23
|
+
* Check if a task is finished (success, failed, or aborted)
|
|
24
|
+
*/
|
|
25
|
+
function isTaskFinished(task) {
|
|
26
|
+
return (task.status === ExecutionStatus.Success ||
|
|
27
|
+
task.status === ExecutionStatus.Failed ||
|
|
28
|
+
task.status === ExecutionStatus.Aborted);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Map ExecuteState to view props for rendering in timeline
|
|
32
|
+
*/
|
|
33
|
+
export function mapStateToViewProps(state, isActive) {
|
|
34
|
+
const taskViewData = state.tasks.map((task) => {
|
|
35
|
+
return {
|
|
36
|
+
label: task.label,
|
|
37
|
+
command: task.command,
|
|
38
|
+
status: task.status,
|
|
39
|
+
elapsed: task.elapsed,
|
|
40
|
+
stdout: task.stdout ?? '',
|
|
41
|
+
stderr: task.stderr ?? '',
|
|
42
|
+
isActive: false, // In timeline, no task is active
|
|
43
|
+
isFinished: isTaskFinished(task),
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
return {
|
|
47
|
+
isLoading: false,
|
|
48
|
+
isExecuting: false,
|
|
49
|
+
isActive,
|
|
50
|
+
error: state.error,
|
|
51
|
+
message: state.message,
|
|
52
|
+
tasks: taskViewData,
|
|
53
|
+
completionMessage: state.completionMessage,
|
|
54
|
+
showTasks: state.tasks.length > 0,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Create an ExecuteState with defaults
|
|
59
|
+
*/
|
|
60
|
+
function createExecuteState(overrides = {}) {
|
|
61
|
+
return {
|
|
62
|
+
message: '',
|
|
63
|
+
summary: '',
|
|
64
|
+
tasks: [],
|
|
65
|
+
completionMessage: null,
|
|
66
|
+
error: null,
|
|
67
|
+
...overrides,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Execute view: Pure display component for task execution progress
|
|
72
|
+
*/
|
|
73
|
+
export const ExecuteView = ({ isLoading, isExecuting, isActive, error, message, tasks, completionMessage, showTasks, }) => {
|
|
26
74
|
// Return null only when loading completes with no commands
|
|
27
|
-
if (!isActive &&
|
|
75
|
+
if (!isActive && tasks.length === 0 && !error) {
|
|
28
76
|
return null;
|
|
29
77
|
}
|
|
30
|
-
|
|
31
|
-
const showTasks = !isActive && taskInfos.length > 0;
|
|
32
|
-
return (_jsxs(Box, { alignSelf: "flex-start", flexDirection: "column", children: [isLoading && (_jsxs(Box, { marginLeft: 1, children: [_jsx(Text, { color: getTextColor(isActive), children: "Preparing commands. " }), _jsx(Spinner, {})] })), (isExecuting || showTasks) && (_jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [message && (_jsxs(Box, { marginBottom: 1, gap: 1, children: [_jsx(Text, { color: getTextColor(isActive), children: message }), isExecuting && _jsx(Spinner, {})] })), taskInfos.map((taskInfo, index) => (_jsx(Box, { marginBottom: index < taskInfos.length - 1 ? 1 : 0, children: _jsx(Task, { label: taskInfo.label, command: taskInfo.command, isActive: isActive && index === completed, index: index, initialStatus: taskInfo.status, initialElapsed: taskInfo.elapsed, onComplete: onTaskComplete, onAbort: onTaskAbort, onError: onTaskError }) }, index)))] })), completionMessage && !isActive && (_jsx(Box, { marginTop: 1, marginLeft: 1, children: _jsx(Text, { color: getTextColor(false), children: completionMessage }) })), error && _jsx(Message, { text: error, status: status })] }));
|
|
78
|
+
return (_jsxs(Box, { alignSelf: "flex-start", flexDirection: "column", children: [isLoading && (_jsxs(Box, { marginLeft: 1, children: [_jsx(Text, { color: getTextColor(isActive), children: "Preparing commands. " }), _jsx(Spinner, {})] })), (isExecuting || showTasks) && (_jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [message && (_jsxs(Box, { marginBottom: 1, gap: 1, children: [_jsx(Text, { color: getTextColor(isActive), children: message }), isExecuting && _jsx(Spinner, {})] })), tasks.map((task, index) => (_jsx(Box, { marginBottom: index < tasks.length - 1 ? 1 : 0, children: _jsx(TaskView, { label: task.label, command: task.command, status: task.status, elapsed: task.elapsed, stdout: task.stdout, stderr: task.stderr, isFinished: task.isFinished }) }, index)))] })), completionMessage && !isActive && (_jsx(Box, { marginTop: 1, marginLeft: 1, children: _jsx(Text, { color: getTextColor(false), children: completionMessage }) })), error && (_jsx(Box, { marginLeft: 1, children: _jsx(Text, { children: error }) }))] }));
|
|
33
79
|
};
|
|
34
80
|
/**
|
|
35
|
-
* Execute controller: Runs tasks sequentially
|
|
81
|
+
* Execute controller: Runs tasks sequentially and manages all execution state
|
|
36
82
|
*/
|
|
37
|
-
export function Execute({ tasks, status, service, requestHandlers, lifecycleHandlers, workflowHandlers, }) {
|
|
83
|
+
export function Execute({ tasks: inputTasks, status, service, requestHandlers, lifecycleHandlers, workflowHandlers, }) {
|
|
38
84
|
const isActive = status === ComponentStatus.Active;
|
|
39
85
|
const [localState, dispatch] = useReducer(executeReducer, initialState);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
86
|
+
// Live output state for currently executing task
|
|
87
|
+
const [liveOutput, setLiveOutput] = useState({
|
|
88
|
+
stdout: '',
|
|
89
|
+
stderr: '',
|
|
90
|
+
error: '',
|
|
91
|
+
});
|
|
92
|
+
const [liveElapsed, setLiveElapsed] = useState(0);
|
|
93
|
+
const [taskStartTime, setTaskStartTime] = useState(null);
|
|
94
|
+
// Track working directory across commands (persists cd changes)
|
|
95
|
+
const workdirRef = useRef(undefined);
|
|
96
|
+
// Ref to track if current task execution is cancelled
|
|
97
|
+
const cancelledRef = useRef(false);
|
|
98
|
+
const { error, tasks, message, hasProcessed, completionMessage, summary } = localState;
|
|
99
|
+
// Derive current task index from tasks
|
|
100
|
+
const currentTaskIndex = getCurrentTaskIndex(tasks);
|
|
101
|
+
// Derive states
|
|
102
|
+
const isLoading = isActive && tasks.length === 0 && !error && !hasProcessed;
|
|
103
|
+
const isExecuting = isActive && currentTaskIndex < tasks.length;
|
|
104
|
+
const showTasks = !isActive && tasks.length > 0;
|
|
105
|
+
// Update elapsed time while task is running
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
if (!taskStartTime || !isExecuting)
|
|
108
|
+
return;
|
|
109
|
+
const interval = setInterval(() => {
|
|
110
|
+
setLiveElapsed(Date.now() - taskStartTime);
|
|
111
|
+
}, ELAPSED_UPDATE_INTERVAL);
|
|
112
|
+
return () => {
|
|
113
|
+
clearInterval(interval);
|
|
114
|
+
};
|
|
115
|
+
}, [taskStartTime, isExecuting]);
|
|
116
|
+
// Handle cancel
|
|
45
117
|
const handleCancel = useCallback(() => {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
118
|
+
cancelledRef.current = true;
|
|
119
|
+
dispatch({ type: ExecuteActionType.CancelExecution });
|
|
120
|
+
// Build updated task infos with current output for the running task
|
|
121
|
+
const updatedTaskInfos = tasks.map((task) => {
|
|
122
|
+
if (task.status === ExecutionStatus.Running) {
|
|
123
|
+
return {
|
|
124
|
+
...task,
|
|
125
|
+
status: ExecutionStatus.Aborted,
|
|
126
|
+
stdout: liveOutput.stdout,
|
|
127
|
+
stderr: liveOutput.stderr,
|
|
128
|
+
error: liveOutput.error,
|
|
129
|
+
};
|
|
57
130
|
}
|
|
58
|
-
else {
|
|
131
|
+
else if (task.status === ExecutionStatus.Pending) {
|
|
59
132
|
return { ...task, status: ExecutionStatus.Cancelled };
|
|
60
133
|
}
|
|
134
|
+
return task;
|
|
61
135
|
});
|
|
62
|
-
|
|
63
|
-
const finalState = {
|
|
136
|
+
const finalState = createExecuteState({
|
|
64
137
|
message,
|
|
65
138
|
summary,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
taskExecutionTimes,
|
|
69
|
-
completionMessage: null,
|
|
70
|
-
error: null,
|
|
71
|
-
};
|
|
139
|
+
tasks: updatedTaskInfos,
|
|
140
|
+
});
|
|
72
141
|
requestHandlers.onCompleted(finalState);
|
|
73
142
|
requestHandlers.onAborted('execution');
|
|
74
|
-
}, [
|
|
75
|
-
message,
|
|
76
|
-
summary,
|
|
77
|
-
taskInfos,
|
|
78
|
-
completed,
|
|
79
|
-
taskExecutionTimes,
|
|
80
|
-
requestHandlers,
|
|
81
|
-
]);
|
|
143
|
+
}, [message, summary, tasks, liveOutput, requestHandlers]);
|
|
82
144
|
useInput((_, key) => {
|
|
83
|
-
if (key.escape && (isLoading || isExecuting)
|
|
145
|
+
if (key.escape && (isLoading || isExecuting)) {
|
|
84
146
|
handleCancel();
|
|
85
147
|
}
|
|
86
148
|
}, { isActive: (isLoading || isExecuting) && isActive });
|
|
87
149
|
// Process tasks to get commands from AI
|
|
88
150
|
useEffect(() => {
|
|
89
|
-
if (!isActive ||
|
|
151
|
+
if (!isActive || tasks.length > 0 || hasProcessed) {
|
|
90
152
|
return;
|
|
91
153
|
}
|
|
92
154
|
let mounted = true;
|
|
93
155
|
async function process(svc) {
|
|
94
156
|
const startTime = Date.now();
|
|
95
157
|
try {
|
|
96
|
-
const result = await processTasks(
|
|
158
|
+
const result = await processTasks(inputTasks, svc);
|
|
97
159
|
await ensureMinimumTime(startTime, MINIMUM_PROCESSING_TIME);
|
|
98
160
|
if (!mounted)
|
|
99
161
|
return;
|
|
@@ -102,67 +164,41 @@ export function Execute({ tasks, status, service, requestHandlers, lifecycleHand
|
|
|
102
164
|
workflowHandlers.addToTimeline(...result.debug);
|
|
103
165
|
}
|
|
104
166
|
if (result.commands.length === 0) {
|
|
105
|
-
// Check if this is an error response (has error field)
|
|
106
167
|
if (result.error) {
|
|
107
|
-
// Add error message to timeline
|
|
108
168
|
const errorMessage = getExecutionErrorMessage(result.error);
|
|
109
|
-
workflowHandlers.addToTimeline(
|
|
110
|
-
|
|
111
|
-
const finalState = {
|
|
112
|
-
message: result.message,
|
|
113
|
-
summary: '',
|
|
114
|
-
taskInfos: [],
|
|
115
|
-
completed: 0,
|
|
116
|
-
taskExecutionTimes: [],
|
|
117
|
-
completionMessage: null,
|
|
118
|
-
error: null,
|
|
119
|
-
};
|
|
120
|
-
requestHandlers.onCompleted(finalState);
|
|
169
|
+
workflowHandlers.addToTimeline(createMessage({ text: errorMessage }, ComponentStatus.Done));
|
|
170
|
+
requestHandlers.onCompleted(createExecuteState({ message: result.message }));
|
|
121
171
|
lifecycleHandlers.completeActive();
|
|
122
172
|
return;
|
|
123
173
|
}
|
|
124
|
-
// No commands and no error - just complete
|
|
125
174
|
dispatch({
|
|
126
175
|
type: ExecuteActionType.ProcessingComplete,
|
|
127
176
|
payload: { message: result.message },
|
|
128
177
|
});
|
|
129
|
-
|
|
130
|
-
message: result.message,
|
|
131
|
-
summary: '',
|
|
132
|
-
taskInfos: [],
|
|
133
|
-
completed: 0,
|
|
134
|
-
taskExecutionTimes: [],
|
|
135
|
-
completionMessage: null,
|
|
136
|
-
error: null,
|
|
137
|
-
};
|
|
138
|
-
requestHandlers.onCompleted(finalState);
|
|
178
|
+
requestHandlers.onCompleted(createExecuteState({ message: result.message }));
|
|
139
179
|
lifecycleHandlers.completeActive();
|
|
140
180
|
return;
|
|
141
181
|
}
|
|
142
182
|
// Create task infos from commands
|
|
143
|
-
const
|
|
144
|
-
label:
|
|
183
|
+
const taskInfos = result.commands.map((cmd, index) => ({
|
|
184
|
+
label: inputTasks[index]?.action ?? cmd.description,
|
|
145
185
|
command: cmd,
|
|
186
|
+
status: ExecutionStatus.Pending,
|
|
187
|
+
elapsed: 0,
|
|
146
188
|
}));
|
|
147
189
|
dispatch({
|
|
148
190
|
type: ExecuteActionType.CommandsReady,
|
|
149
191
|
payload: {
|
|
150
192
|
message: result.message,
|
|
151
193
|
summary: result.summary,
|
|
152
|
-
|
|
194
|
+
tasks: taskInfos,
|
|
153
195
|
},
|
|
154
196
|
});
|
|
155
|
-
|
|
156
|
-
const finalState = {
|
|
197
|
+
requestHandlers.onCompleted(createExecuteState({
|
|
157
198
|
message: result.message,
|
|
158
199
|
summary: result.summary,
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
taskExecutionTimes: [],
|
|
162
|
-
completionMessage: null,
|
|
163
|
-
error: null,
|
|
164
|
-
};
|
|
165
|
-
requestHandlers.onCompleted(finalState);
|
|
200
|
+
tasks: taskInfos,
|
|
201
|
+
}));
|
|
166
202
|
}
|
|
167
203
|
catch (err) {
|
|
168
204
|
await ensureMinimumTime(startTime, MINIMUM_PROCESSING_TIME);
|
|
@@ -172,16 +208,7 @@ export function Execute({ tasks, status, service, requestHandlers, lifecycleHand
|
|
|
172
208
|
type: ExecuteActionType.ProcessingError,
|
|
173
209
|
payload: { error: errorMessage },
|
|
174
210
|
});
|
|
175
|
-
|
|
176
|
-
message: '',
|
|
177
|
-
summary: '',
|
|
178
|
-
taskInfos: [],
|
|
179
|
-
completed: 0,
|
|
180
|
-
taskExecutionTimes: [],
|
|
181
|
-
completionMessage: null,
|
|
182
|
-
error: errorMessage,
|
|
183
|
-
};
|
|
184
|
-
requestHandlers.onCompleted(finalState);
|
|
211
|
+
requestHandlers.onCompleted(createExecuteState({ error: errorMessage }));
|
|
185
212
|
requestHandlers.onError(errorMessage);
|
|
186
213
|
}
|
|
187
214
|
}
|
|
@@ -191,81 +218,140 @@ export function Execute({ tasks, status, service, requestHandlers, lifecycleHand
|
|
|
191
218
|
mounted = false;
|
|
192
219
|
};
|
|
193
220
|
}, [
|
|
194
|
-
|
|
221
|
+
inputTasks,
|
|
195
222
|
isActive,
|
|
196
223
|
service,
|
|
197
224
|
requestHandlers,
|
|
198
225
|
lifecycleHandlers,
|
|
199
226
|
workflowHandlers,
|
|
200
|
-
|
|
227
|
+
tasks.length,
|
|
201
228
|
hasProcessed,
|
|
202
229
|
]);
|
|
203
|
-
//
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
});
|
|
211
|
-
dispatch(result.action);
|
|
212
|
-
requestHandlers.onCompleted(result.finalState);
|
|
213
|
-
if (result.shouldComplete) {
|
|
214
|
-
lifecycleHandlers.completeActive();
|
|
215
|
-
}
|
|
216
|
-
}, [
|
|
217
|
-
taskInfos,
|
|
218
|
-
message,
|
|
219
|
-
summary,
|
|
220
|
-
taskExecutionTimes,
|
|
221
|
-
requestHandlers,
|
|
222
|
-
lifecycleHandlers,
|
|
223
|
-
]);
|
|
224
|
-
const handleTaskError = useCallback((index, error, elapsed) => {
|
|
225
|
-
const result = handleTaskFailure(index, error, elapsed, {
|
|
226
|
-
taskInfos,
|
|
227
|
-
message,
|
|
228
|
-
summary,
|
|
229
|
-
taskExecutionTimes,
|
|
230
|
-
});
|
|
231
|
-
dispatch(result.action);
|
|
232
|
-
requestHandlers.onCompleted(result.finalState);
|
|
233
|
-
if (result.shouldReportError) {
|
|
234
|
-
requestHandlers.onError(error);
|
|
230
|
+
// Execute current task
|
|
231
|
+
useEffect(() => {
|
|
232
|
+
if (!isActive ||
|
|
233
|
+
tasks.length === 0 ||
|
|
234
|
+
currentTaskIndex >= tasks.length ||
|
|
235
|
+
error) {
|
|
236
|
+
return;
|
|
235
237
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
+
const currentTask = tasks[currentTaskIndex];
|
|
239
|
+
if (currentTask.status !== ExecutionStatus.Pending) {
|
|
240
|
+
return;
|
|
238
241
|
}
|
|
242
|
+
cancelledRef.current = false;
|
|
243
|
+
// Mark task as started (running)
|
|
244
|
+
dispatch({
|
|
245
|
+
type: ExecuteActionType.TaskStarted,
|
|
246
|
+
payload: { index: currentTaskIndex },
|
|
247
|
+
});
|
|
248
|
+
// Reset live state for new task
|
|
249
|
+
setLiveOutput({ stdout: '', stderr: '', error: '' });
|
|
250
|
+
setLiveElapsed(0);
|
|
251
|
+
setTaskStartTime(Date.now());
|
|
252
|
+
// Merge workdir into command
|
|
253
|
+
const command = workdirRef.current
|
|
254
|
+
? { ...currentTask.command, workdir: workdirRef.current }
|
|
255
|
+
: currentTask.command;
|
|
256
|
+
void executeTask(command, currentTaskIndex, {
|
|
257
|
+
onOutputChange: (output) => {
|
|
258
|
+
if (!cancelledRef.current) {
|
|
259
|
+
setLiveOutput(output);
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
onComplete: (elapsed, output) => {
|
|
263
|
+
if (cancelledRef.current)
|
|
264
|
+
return;
|
|
265
|
+
setTaskStartTime(null);
|
|
266
|
+
// Track working directory
|
|
267
|
+
if (output.workdir) {
|
|
268
|
+
workdirRef.current = output.workdir;
|
|
269
|
+
}
|
|
270
|
+
const tasksWithOutput = tasks.map((task, i) => i === currentTaskIndex
|
|
271
|
+
? {
|
|
272
|
+
...task,
|
|
273
|
+
stdout: output.stdout,
|
|
274
|
+
stderr: output.stderr,
|
|
275
|
+
error: output.error,
|
|
276
|
+
}
|
|
277
|
+
: task);
|
|
278
|
+
const result = handleTaskCompletion(currentTaskIndex, elapsed, {
|
|
279
|
+
tasks: tasksWithOutput,
|
|
280
|
+
message,
|
|
281
|
+
summary,
|
|
282
|
+
});
|
|
283
|
+
dispatch(result.action);
|
|
284
|
+
requestHandlers.onCompleted(result.finalState);
|
|
285
|
+
if (result.shouldComplete) {
|
|
286
|
+
lifecycleHandlers.completeActive();
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
onError: (errorMsg, elapsed, output) => {
|
|
290
|
+
if (cancelledRef.current)
|
|
291
|
+
return;
|
|
292
|
+
setTaskStartTime(null);
|
|
293
|
+
// Track working directory
|
|
294
|
+
if (output.workdir) {
|
|
295
|
+
workdirRef.current = output.workdir;
|
|
296
|
+
}
|
|
297
|
+
const tasksWithOutput = tasks.map((task, i) => i === currentTaskIndex
|
|
298
|
+
? {
|
|
299
|
+
...task,
|
|
300
|
+
stdout: output.stdout,
|
|
301
|
+
stderr: output.stderr,
|
|
302
|
+
error: output.error,
|
|
303
|
+
}
|
|
304
|
+
: task);
|
|
305
|
+
const result = handleTaskFailure(currentTaskIndex, errorMsg, elapsed, {
|
|
306
|
+
tasks: tasksWithOutput,
|
|
307
|
+
message,
|
|
308
|
+
summary,
|
|
309
|
+
});
|
|
310
|
+
dispatch(result.action);
|
|
311
|
+
requestHandlers.onCompleted(result.finalState);
|
|
312
|
+
if (result.action.type === ExecuteActionType.TaskErrorCritical) {
|
|
313
|
+
const errorMessage = getExecutionErrorMessage(errorMsg);
|
|
314
|
+
workflowHandlers.addToQueue(createFeedback({ type: FeedbackType.Failed, message: errorMessage }));
|
|
315
|
+
}
|
|
316
|
+
if (result.shouldComplete) {
|
|
317
|
+
lifecycleHandlers.completeActive();
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
});
|
|
239
321
|
}, [
|
|
240
|
-
|
|
322
|
+
isActive,
|
|
323
|
+
tasks,
|
|
324
|
+
currentTaskIndex,
|
|
241
325
|
message,
|
|
242
326
|
summary,
|
|
243
|
-
|
|
327
|
+
error,
|
|
244
328
|
requestHandlers,
|
|
245
329
|
lifecycleHandlers,
|
|
330
|
+
workflowHandlers,
|
|
246
331
|
]);
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
332
|
+
// Build view data for each task
|
|
333
|
+
const taskViewData = tasks.map((task) => {
|
|
334
|
+
const isTaskActive = isActive && task.status === ExecutionStatus.Running;
|
|
335
|
+
const finished = isTaskFinished(task);
|
|
336
|
+
// Use live output for active task, stored output for finished tasks
|
|
337
|
+
const stdout = isTaskActive ? liveOutput.stdout : (task.stdout ?? '');
|
|
338
|
+
const stderr = isTaskActive ? liveOutput.stderr : (task.stderr ?? '');
|
|
339
|
+
// Use live elapsed for active running task
|
|
340
|
+
const elapsed = isTaskActive ? liveElapsed : task.elapsed;
|
|
341
|
+
// Merge workdir for active task
|
|
342
|
+
const command = isTaskActive && workdirRef.current
|
|
343
|
+
? { ...task.command, workdir: workdirRef.current }
|
|
344
|
+
: task.command;
|
|
345
|
+
return {
|
|
346
|
+
label: task.label,
|
|
347
|
+
command,
|
|
348
|
+
status: task.status,
|
|
349
|
+
elapsed,
|
|
350
|
+
stdout,
|
|
351
|
+
stderr,
|
|
352
|
+
isActive: isTaskActive,
|
|
353
|
+
isFinished: finished,
|
|
354
|
+
};
|
|
355
|
+
});
|
|
356
|
+
return (_jsx(ExecuteView, { isLoading: isLoading, isExecuting: isExecuting, isActive: isActive, error: error, message: message, tasks: taskViewData, completionMessage: completionMessage, showTasks: showTasks }));
|
|
271
357
|
}
|
package/dist/ui/Introspect.js
CHANGED
|
@@ -2,8 +2,9 @@ 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
4
|
import { ComponentStatus, } from '../types/components.js';
|
|
5
|
+
import { Origin } from '../types/types.js';
|
|
5
6
|
import { Colors, getTextColor } from '../services/colors.js';
|
|
6
|
-
import {
|
|
7
|
+
import { createReport } from '../services/components.js';
|
|
7
8
|
import { DebugLevel } from '../configuration/types.js';
|
|
8
9
|
import { useInput } from '../services/keyboard.js';
|
|
9
10
|
import { formatErrorMessage } from '../services/messages.js';
|
|
@@ -51,24 +52,22 @@ export function Introspect({ tasks, status, service, children, debug = DebugLeve
|
|
|
51
52
|
if (result.debug?.length) {
|
|
52
53
|
workflowHandlers.addToTimeline(...result.debug);
|
|
53
54
|
}
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
// Filter out
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
setCapabilities(caps);
|
|
63
|
-
setMessage(result.message);
|
|
55
|
+
// Destructure message from result
|
|
56
|
+
const { message } = result;
|
|
57
|
+
// Filter out meta workflow capabilities when not in debug mode
|
|
58
|
+
const capabilities = debug === DebugLevel.None
|
|
59
|
+
? result.capabilities.filter((cap) => cap.origin !== Origin.Indirect)
|
|
60
|
+
: result.capabilities;
|
|
61
|
+
setCapabilities(capabilities);
|
|
62
|
+
setMessage(message);
|
|
64
63
|
const finalState = {
|
|
65
64
|
error: null,
|
|
66
|
-
capabilities
|
|
67
|
-
message
|
|
65
|
+
capabilities,
|
|
66
|
+
message,
|
|
68
67
|
};
|
|
69
68
|
requestHandlers.onCompleted(finalState);
|
|
70
69
|
// Add Report component to queue
|
|
71
|
-
workflowHandlers.addToQueue(
|
|
70
|
+
workflowHandlers.addToQueue(createReport({ message, capabilities }));
|
|
72
71
|
// Signal completion
|
|
73
72
|
lifecycleHandlers.completeActive();
|
|
74
73
|
}
|
package/dist/ui/List.js
CHANGED
|
@@ -2,9 +2,9 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
3
|
import { Palette } from '../services/colors.js';
|
|
4
4
|
import { Separator } from './Separator.js';
|
|
5
|
-
export const List = ({ items, level = 0, highlightedIndex = null, highlightedParentIndex = null, showType = false, }) => {
|
|
5
|
+
export const List = ({ items, level = 0, highlightedIndex = null, highlightedParentIndex = null, showType = false, compact = false, }) => {
|
|
6
6
|
const marginLeft = level > 0 ? 2 : 0;
|
|
7
|
-
const gap = level === 0 ? 1 : 0;
|
|
7
|
+
const gap = level === 0 && !compact ? 1 : 0;
|
|
8
8
|
return (_jsx(Box, { flexDirection: "column", marginLeft: marginLeft, gap: gap, children: items.map((item, index) => {
|
|
9
9
|
// At level 0, track which parent is active for child highlighting
|
|
10
10
|
// At level > 0, only highlight if this parent is the active one
|
package/dist/ui/Main.js
CHANGED
|
@@ -5,9 +5,10 @@ import { FeedbackType } from '../types/types.js';
|
|
|
5
5
|
import { loadConfig, loadDebugSetting, saveConfig, saveDebugSetting, } from '../configuration/io.js';
|
|
6
6
|
import { getConfigurationRequiredMessage } from '../configuration/messages.js';
|
|
7
7
|
import { getMissingConfigKeys } from '../configuration/schema.js';
|
|
8
|
+
import { createConfigStepsFromSchema } from '../configuration/steps.js';
|
|
8
9
|
import { unflattenConfig } from '../configuration/transformation.js';
|
|
9
10
|
import { createAnthropicService } from '../services/anthropic.js';
|
|
10
|
-
import {
|
|
11
|
+
import { createCommand, createConfig, createFeedback, createMessage, createWelcome, } from '../services/components.js';
|
|
11
12
|
import { registerGlobalShortcut } from '../services/keyboard.js';
|
|
12
13
|
import { initializeLogger, setDebugLevel } from '../services/logger.js';
|
|
13
14
|
import { Workflow } from './Workflow.js';
|
|
@@ -56,7 +57,9 @@ export const Main = ({ app, command, serviceFactory = createAnthropicService, })
|
|
|
56
57
|
const errorMessage = error instanceof Error
|
|
57
58
|
? error.message
|
|
58
59
|
: 'Failed to initialize service';
|
|
59
|
-
setInitialQueue([
|
|
60
|
+
setInitialQueue([
|
|
61
|
+
createFeedback({ type: FeedbackType.Failed, message: errorMessage }),
|
|
62
|
+
]);
|
|
60
63
|
}
|
|
61
64
|
}
|
|
62
65
|
// If config is missing, service will be created after config completes
|
|
@@ -94,18 +97,22 @@ export const Main = ({ app, command, serviceFactory = createAnthropicService, })
|
|
|
94
97
|
// Config was cancelled
|
|
95
98
|
};
|
|
96
99
|
setInitialQueue([
|
|
97
|
-
|
|
98
|
-
createMessage(getConfigurationRequiredMessage()),
|
|
99
|
-
|
|
100
|
+
createWelcome({ app }),
|
|
101
|
+
createMessage({ text: getConfigurationRequiredMessage() }),
|
|
102
|
+
createConfig({
|
|
103
|
+
steps: createConfigStepsFromSchema(missingKeys),
|
|
104
|
+
onFinished: handleConfigFinished,
|
|
105
|
+
onAborted: handleConfigAborted,
|
|
106
|
+
}),
|
|
100
107
|
]);
|
|
101
108
|
}
|
|
102
109
|
else if (service && command) {
|
|
103
110
|
// Valid service exists and command provided - execute command
|
|
104
|
-
setInitialQueue([
|
|
111
|
+
setInitialQueue([createCommand({ command, service })]);
|
|
105
112
|
}
|
|
106
113
|
else if (service && !command) {
|
|
107
114
|
// Valid service exists, no command - show welcome
|
|
108
|
-
setInitialQueue([
|
|
115
|
+
setInitialQueue([createWelcome({ app })]);
|
|
109
116
|
}
|
|
110
117
|
// Wait for service to be initialized before setting queue
|
|
111
118
|
}, [app, command, service, initialQueue]);
|