prompt-language-shell 0.5.2 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/ANSWER.md +4 -0
- package/dist/config/CONFIG.md +4 -2
- package/dist/config/PLAN.md +23 -0
- package/dist/config/VALIDATE.md +12 -11
- package/dist/services/anthropic.js +49 -4
- package/dist/services/components.js +56 -66
- package/dist/services/configuration.js +169 -13
- package/dist/services/messages.js +22 -0
- package/dist/services/queue.js +2 -2
- package/dist/services/refinement.js +37 -0
- package/dist/services/task-router.js +141 -0
- package/dist/types/types.js +0 -1
- package/dist/ui/Answer.js +18 -27
- package/dist/ui/Command.js +44 -27
- package/dist/ui/Component.js +23 -50
- package/dist/ui/Config.js +77 -55
- package/dist/ui/Confirm.js +17 -11
- package/dist/ui/Execute.js +66 -45
- package/dist/ui/Feedback.js +1 -1
- package/dist/ui/Introspect.js +26 -23
- package/dist/ui/Main.js +71 -100
- package/dist/ui/Message.js +1 -1
- package/dist/ui/Plan.js +54 -32
- package/dist/ui/Refinement.js +6 -7
- package/dist/ui/Report.js +1 -1
- package/dist/ui/UserQuery.js +6 -0
- package/dist/ui/Validate.js +49 -19
- package/dist/ui/Welcome.js +1 -1
- package/dist/ui/Workflow.js +132 -0
- package/package.json +1 -1
- package/dist/handlers/answer.js +0 -21
- package/dist/handlers/command.js +0 -34
- package/dist/handlers/config.js +0 -88
- package/dist/handlers/execute.js +0 -46
- package/dist/handlers/execution.js +0 -140
- package/dist/handlers/introspect.js +0 -21
- package/dist/handlers/plan.js +0 -79
- package/dist/types/handlers.js +0 -1
- package/dist/ui/AnswerDisplay.js +0 -8
- package/dist/ui/Column.js +0 -7
package/dist/ui/Component.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import
|
|
2
|
+
import { memo } from 'react';
|
|
3
3
|
import { ComponentName } from '../types/types.js';
|
|
4
4
|
import { Answer } from './Answer.js';
|
|
5
|
-
import { AnswerDisplay } from './AnswerDisplay.js';
|
|
6
5
|
import { Command } from './Command.js';
|
|
7
6
|
import { Confirm } from './Confirm.js';
|
|
8
7
|
import { Config } from './Config.js';
|
|
@@ -15,62 +14,36 @@ import { Refinement } from './Refinement.js';
|
|
|
15
14
|
import { Report } from './Report.js';
|
|
16
15
|
import { Validate } from './Validate.js';
|
|
17
16
|
import { Welcome } from './Welcome.js';
|
|
18
|
-
export const Component =
|
|
17
|
+
export const Component = memo(function Component({ def, isActive, debug, }) {
|
|
18
|
+
// For stateless components, always inactive
|
|
19
|
+
const isStatelessComponent = !('state' in def);
|
|
20
|
+
const componentIsActive = isStatelessComponent ? false : isActive;
|
|
19
21
|
switch (def.name) {
|
|
20
22
|
case ComponentName.Welcome:
|
|
21
23
|
return _jsx(Welcome, { ...def.props });
|
|
22
|
-
case ComponentName.Config:
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return _jsx(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const props = def.props;
|
|
29
|
-
const state = def.state;
|
|
30
|
-
return _jsx(Command, { ...props, state: state });
|
|
31
|
-
}
|
|
32
|
-
case ComponentName.Plan: {
|
|
33
|
-
const props = def.props;
|
|
34
|
-
const state = def.state;
|
|
35
|
-
return _jsx(Plan, { ...props, state: state, debug: debug });
|
|
36
|
-
}
|
|
24
|
+
case ComponentName.Config:
|
|
25
|
+
return (_jsx(Config, { ...def.props, state: def.state, isActive: componentIsActive, debug: debug }));
|
|
26
|
+
case ComponentName.Command:
|
|
27
|
+
return _jsx(Command, { ...def.props, state: def.state, isActive: isActive });
|
|
28
|
+
case ComponentName.Plan:
|
|
29
|
+
return (_jsx(Plan, { ...def.props, state: def.state, isActive: componentIsActive, debug: debug }));
|
|
37
30
|
case ComponentName.Feedback:
|
|
38
31
|
return _jsx(Feedback, { ...def.props });
|
|
39
32
|
case ComponentName.Message:
|
|
40
33
|
return _jsx(Message, { ...def.props });
|
|
41
|
-
case ComponentName.Refinement:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return _jsx(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const props = def.props;
|
|
48
|
-
const state = def.state;
|
|
49
|
-
return _jsx(Confirm, { ...props, state: state });
|
|
50
|
-
}
|
|
51
|
-
case ComponentName.Introspect: {
|
|
52
|
-
const props = def.props;
|
|
53
|
-
const state = def.state;
|
|
54
|
-
return _jsx(Introspect, { ...props, state: state, debug: debug });
|
|
55
|
-
}
|
|
34
|
+
case ComponentName.Refinement:
|
|
35
|
+
return (_jsx(Refinement, { ...def.props, state: def.state, isActive: isActive }));
|
|
36
|
+
case ComponentName.Confirm:
|
|
37
|
+
return _jsx(Confirm, { ...def.props, state: def.state, isActive: isActive });
|
|
38
|
+
case ComponentName.Introspect:
|
|
39
|
+
return (_jsx(Introspect, { ...def.props, state: def.state, isActive: componentIsActive, debug: debug }));
|
|
56
40
|
case ComponentName.Report:
|
|
57
41
|
return _jsx(Report, { ...def.props });
|
|
58
|
-
case ComponentName.Answer:
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return _jsx(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
return _jsx(AnswerDisplay, { ...def.props });
|
|
65
|
-
case ComponentName.Execute: {
|
|
66
|
-
const props = def.props;
|
|
67
|
-
const state = def.state;
|
|
68
|
-
return _jsx(Execute, { ...props, state: state });
|
|
69
|
-
}
|
|
70
|
-
case ComponentName.Validate: {
|
|
71
|
-
const props = def.props;
|
|
72
|
-
const state = def.state;
|
|
73
|
-
return _jsx(Validate, { ...props, state: state });
|
|
74
|
-
}
|
|
42
|
+
case ComponentName.Answer:
|
|
43
|
+
return _jsx(Answer, { ...def.props, state: def.state, isActive: isActive });
|
|
44
|
+
case ComponentName.Execute:
|
|
45
|
+
return _jsx(Execute, { ...def.props, state: def.state, isActive: isActive });
|
|
46
|
+
case ComponentName.Validate:
|
|
47
|
+
return (_jsx(Validate, { ...def.props, state: def.state, isActive: isActive, debug: debug }));
|
|
75
48
|
}
|
|
76
49
|
});
|
package/dist/ui/Config.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import
|
|
2
|
+
import { useState } from 'react';
|
|
3
3
|
import { Box, Text, useFocus } from 'ink';
|
|
4
4
|
import TextInput from 'ink-text-input';
|
|
5
5
|
import { Colors } from '../services/colors.js';
|
|
@@ -10,8 +10,8 @@ export var StepType;
|
|
|
10
10
|
StepType["Selection"] = "selection";
|
|
11
11
|
})(StepType || (StepType = {}));
|
|
12
12
|
function TextStep({ value, placeholder, validate, onChange, onSubmit, }) {
|
|
13
|
-
const [inputValue, setInputValue] =
|
|
14
|
-
const [validationFailed, setValidationFailed] =
|
|
13
|
+
const [inputValue, setInputValue] = useState(value);
|
|
14
|
+
const [validationFailed, setValidationFailed] = useState(false);
|
|
15
15
|
const { isFocused } = useFocus({ autoFocus: true });
|
|
16
16
|
const handleChange = (newValue) => {
|
|
17
17
|
setInputValue(newValue);
|
|
@@ -57,20 +57,27 @@ function SelectionStep({ options, selectedIndex, isCurrentStep, }) {
|
|
|
57
57
|
return (_jsx(Box, { marginRight: 2, children: _jsx(Text, { dimColor: !isSelected || !isCurrentStep, bold: isSelected, children: option.label }) }, option.value));
|
|
58
58
|
}) }));
|
|
59
59
|
}
|
|
60
|
-
export function Config({ steps, state, debug, onFinished, onAborted }) {
|
|
61
|
-
|
|
62
|
-
const [step, setStep] =
|
|
63
|
-
const [values, setValues] =
|
|
60
|
+
export function Config({ steps, state, isActive = true, debug, handlers, onFinished, onAborted, }) {
|
|
61
|
+
// isActive passed as prop
|
|
62
|
+
const [step, setStep] = useState(!isActive ? (state?.completedStep ?? steps.length) : 0);
|
|
63
|
+
const [values, setValues] = useState(() => {
|
|
64
|
+
// If not active and we have saved state values, use those
|
|
65
|
+
if (!isActive && state?.values) {
|
|
66
|
+
return state.values;
|
|
67
|
+
}
|
|
68
|
+
// Otherwise initialize from step defaults
|
|
64
69
|
const initial = {};
|
|
65
70
|
steps.forEach((stepConfig) => {
|
|
71
|
+
// Use full path if available, otherwise use key
|
|
72
|
+
const configKey = stepConfig.path || stepConfig.key;
|
|
66
73
|
switch (stepConfig.type) {
|
|
67
74
|
case StepType.Text:
|
|
68
75
|
if (stepConfig.value !== null) {
|
|
69
|
-
initial[
|
|
76
|
+
initial[configKey] = stepConfig.value;
|
|
70
77
|
}
|
|
71
78
|
break;
|
|
72
79
|
case StepType.Selection:
|
|
73
|
-
initial[
|
|
80
|
+
initial[configKey] =
|
|
74
81
|
stepConfig.options[stepConfig.defaultIndex].value;
|
|
75
82
|
break;
|
|
76
83
|
default: {
|
|
@@ -81,10 +88,15 @@ export function Config({ steps, state, debug, onFinished, onAborted }) {
|
|
|
81
88
|
});
|
|
82
89
|
return initial;
|
|
83
90
|
});
|
|
84
|
-
const [inputValue, setInputValue] =
|
|
85
|
-
const [selectedIndex, setSelectedIndex] =
|
|
86
|
-
|
|
87
|
-
|
|
91
|
+
const [inputValue, setInputValue] = useState('');
|
|
92
|
+
const [selectedIndex, setSelectedIndex] = useState(() => {
|
|
93
|
+
// Initialize selectedIndex based on current step's defaultIndex
|
|
94
|
+
if (isActive &&
|
|
95
|
+
step < steps.length &&
|
|
96
|
+
steps[step].type === StepType.Selection) {
|
|
97
|
+
return steps[step].defaultIndex;
|
|
98
|
+
}
|
|
99
|
+
return 0;
|
|
88
100
|
});
|
|
89
101
|
const normalizeValue = (value) => {
|
|
90
102
|
if (value === null || value === undefined) {
|
|
@@ -92,21 +104,21 @@ export function Config({ steps, state, debug, onFinished, onAborted }) {
|
|
|
92
104
|
}
|
|
93
105
|
return value.replace(/\n/g, '').trim();
|
|
94
106
|
};
|
|
95
|
-
useInput((
|
|
96
|
-
if (
|
|
107
|
+
useInput((_, key) => {
|
|
108
|
+
if (!isActive || step >= steps.length)
|
|
109
|
+
return;
|
|
110
|
+
const currentStepConfig = steps[step];
|
|
111
|
+
if (key.escape) {
|
|
97
112
|
// Save current value before aborting
|
|
98
|
-
const currentStepConfig = steps[step];
|
|
99
113
|
if (currentStepConfig) {
|
|
114
|
+
const configKey = currentStepConfig.path || currentStepConfig.key;
|
|
100
115
|
let currentValue = '';
|
|
101
116
|
switch (currentStepConfig.type) {
|
|
102
117
|
case StepType.Text:
|
|
103
|
-
currentValue = inputValue || values[
|
|
118
|
+
currentValue = inputValue || values[configKey] || '';
|
|
104
119
|
break;
|
|
105
120
|
case StepType.Selection:
|
|
106
|
-
currentValue =
|
|
107
|
-
currentStepConfig.options[selectedIndex]?.value ||
|
|
108
|
-
values[currentStepConfig.key] ||
|
|
109
|
-
'';
|
|
121
|
+
currentValue = values[configKey] || '';
|
|
110
122
|
break;
|
|
111
123
|
default: {
|
|
112
124
|
const exhaustiveCheck = currentStepConfig;
|
|
@@ -114,32 +126,21 @@ export function Config({ steps, state, debug, onFinished, onAborted }) {
|
|
|
114
126
|
}
|
|
115
127
|
}
|
|
116
128
|
if (currentValue) {
|
|
117
|
-
setValues({ ...values, [
|
|
129
|
+
setValues({ ...values, [configKey]: currentValue });
|
|
118
130
|
}
|
|
119
131
|
}
|
|
120
132
|
if (onAborted) {
|
|
121
|
-
onAborted();
|
|
133
|
+
onAborted('configuration');
|
|
122
134
|
}
|
|
123
135
|
return;
|
|
124
136
|
}
|
|
125
|
-
|
|
126
|
-
if (
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
else if (key.return) {
|
|
133
|
-
handleSubmit(currentStep.options[selectedIndex].value);
|
|
134
|
-
}
|
|
135
|
-
break;
|
|
136
|
-
case StepType.Text:
|
|
137
|
-
// Text input handled by TextInput component
|
|
138
|
-
break;
|
|
139
|
-
default: {
|
|
140
|
-
const exhaustiveCheck = currentStep;
|
|
141
|
-
throw new Error(`Unsupported step type: ${exhaustiveCheck}`);
|
|
142
|
-
}
|
|
137
|
+
// Handle selection step navigation
|
|
138
|
+
if (currentStepConfig.type === StepType.Selection) {
|
|
139
|
+
if (key.tab) {
|
|
140
|
+
setSelectedIndex((prev) => (prev + 1) % currentStepConfig.options.length);
|
|
141
|
+
}
|
|
142
|
+
else if (key.return) {
|
|
143
|
+
handleSubmit(currentStepConfig.options[selectedIndex].value);
|
|
143
144
|
}
|
|
144
145
|
}
|
|
145
146
|
});
|
|
@@ -173,38 +174,59 @@ export function Config({ steps, state, debug, onFinished, onAborted }) {
|
|
|
173
174
|
if (!finalValue) {
|
|
174
175
|
return;
|
|
175
176
|
}
|
|
176
|
-
|
|
177
|
+
// Use full path if available, otherwise use key
|
|
178
|
+
const configKey = currentStepConfig.path || currentStepConfig.key;
|
|
179
|
+
const newValues = { ...values, [configKey]: finalValue };
|
|
177
180
|
setValues(newValues);
|
|
178
181
|
setInputValue('');
|
|
179
182
|
if (step === steps.length - 1) {
|
|
180
183
|
// Last step completed
|
|
184
|
+
// IMPORTANT: Update state BEFORE calling onFinished
|
|
185
|
+
// onFinished may call handlers.completeActive(), so state must be saved first
|
|
186
|
+
const stateUpdate = {
|
|
187
|
+
values: newValues,
|
|
188
|
+
completedStep: steps.length,
|
|
189
|
+
};
|
|
190
|
+
handlers?.updateState(stateUpdate);
|
|
191
|
+
// Now call onFinished - this may trigger completeActive()
|
|
181
192
|
if (onFinished) {
|
|
182
193
|
onFinished(newValues);
|
|
183
194
|
}
|
|
184
195
|
setStep(steps.length);
|
|
185
196
|
}
|
|
186
197
|
else {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
198
|
+
// Save state after each step
|
|
199
|
+
const stateUpdate = {
|
|
200
|
+
values: newValues,
|
|
201
|
+
completedStep: step + 1,
|
|
202
|
+
};
|
|
203
|
+
handlers?.updateState(stateUpdate);
|
|
204
|
+
const nextStep = step + 1;
|
|
205
|
+
setStep(nextStep);
|
|
206
|
+
// Reset selectedIndex for next step
|
|
207
|
+
if (nextStep < steps.length &&
|
|
208
|
+
steps[nextStep].type === StepType.Selection) {
|
|
209
|
+
setSelectedIndex(steps[nextStep].defaultIndex);
|
|
192
210
|
}
|
|
193
211
|
}
|
|
194
212
|
};
|
|
195
213
|
const renderStepInput = (stepConfig, isCurrentStep) => {
|
|
214
|
+
const configKey = stepConfig.path || stepConfig.key;
|
|
215
|
+
// Use state values when inactive, local values when active
|
|
216
|
+
const displayValue = !isActive && state?.values ? state.values[configKey] : values[configKey];
|
|
196
217
|
switch (stepConfig.type) {
|
|
197
218
|
case StepType.Text:
|
|
198
219
|
if (isCurrentStep) {
|
|
199
220
|
return (_jsx(TextStep, { value: inputValue, placeholder: stepConfig.value || undefined, validate: stepConfig.validate, onChange: setInputValue, onSubmit: handleSubmit }));
|
|
200
221
|
}
|
|
201
|
-
return _jsx(Text, { dimColor: true, children:
|
|
222
|
+
return (_jsx(Text, { dimColor: true, wrap: "truncate-end", children: displayValue || '' }));
|
|
202
223
|
case StepType.Selection: {
|
|
203
224
|
if (!isCurrentStep) {
|
|
204
|
-
|
|
205
|
-
|
|
225
|
+
// Find the option that matches the saved/current value
|
|
226
|
+
const option = stepConfig.options.find((opt) => opt.value === displayValue);
|
|
227
|
+
return _jsx(Text, { dimColor: true, children: option?.label || '' });
|
|
206
228
|
}
|
|
207
|
-
return (_jsx(SelectionStep, { options: stepConfig.options, selectedIndex: selectedIndex, isCurrentStep:
|
|
229
|
+
return (_jsx(SelectionStep, { options: stepConfig.options, selectedIndex: selectedIndex, isCurrentStep: true }));
|
|
208
230
|
}
|
|
209
231
|
default: {
|
|
210
232
|
const exhaustiveCheck = stepConfig;
|
|
@@ -212,14 +234,14 @@ export function Config({ steps, state, debug, onFinished, onAborted }) {
|
|
|
212
234
|
}
|
|
213
235
|
}
|
|
214
236
|
};
|
|
215
|
-
return (_jsx(Box, { flexDirection: "column", children: steps.map((stepConfig, index) => {
|
|
216
|
-
const isCurrentStep = index === step &&
|
|
237
|
+
return (_jsx(Box, { flexDirection: "column", marginLeft: 1, children: steps.map((stepConfig, index) => {
|
|
238
|
+
const isCurrentStep = index === step && isActive;
|
|
217
239
|
const isCompleted = index < step;
|
|
218
|
-
const wasAborted = index === step &&
|
|
240
|
+
const wasAborted = index === step && !isActive;
|
|
219
241
|
const shouldShow = isCompleted || isCurrentStep || wasAborted;
|
|
220
242
|
if (!shouldShow) {
|
|
221
243
|
return null;
|
|
222
244
|
}
|
|
223
|
-
return (_jsxs(Box, { flexDirection: "column", marginTop: index === 0 ? 0 : 1, children: [_jsxs(Box, { children: [_jsx(Text, { children: stepConfig.description }), _jsx(Text, { children: ": " }), debug && stepConfig.path && (_jsxs(Text, { color: Colors.Type.Define, children: ['{', stepConfig.path, '}'] }))] }), _jsxs(Box, { children: [_jsx(Text, { children: " " }), _jsx(Text, { color: Colors.Action.Select, dimColor: !isCurrentStep, children: ">" }), _jsx(Text, { children: " " }), renderStepInput(stepConfig, isCurrentStep)] })] }, stepConfig.key));
|
|
245
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: index === 0 ? 0 : 1, children: [_jsxs(Box, { children: [_jsx(Text, { children: stepConfig.description }), _jsx(Text, { children: ": " }), debug && stepConfig.path && (_jsxs(Text, { color: Colors.Type.Define, children: ['{', stepConfig.path, '}'] }))] }), _jsxs(Box, { children: [_jsx(Text, { children: " " }), _jsx(Text, { color: Colors.Action.Select, dimColor: !isCurrentStep, children: ">" }), _jsx(Text, { children: " " }), renderStepInput(stepConfig, isCurrentStep)] })] }, stepConfig.path || stepConfig.key));
|
|
224
246
|
}) }));
|
|
225
247
|
}
|
package/dist/ui/Confirm.js
CHANGED
|
@@ -1,26 +1,32 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import
|
|
2
|
+
import { useState } from 'react';
|
|
3
3
|
import { Box, Text } from 'ink';
|
|
4
4
|
import { Colors, Palette } from '../services/colors.js';
|
|
5
5
|
import { useInput } from '../services/keyboard.js';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const [selectedIndex, setSelectedIndex] =
|
|
6
|
+
import { UserQuery } from './UserQuery.js';
|
|
7
|
+
export function Confirm({ message, state, isActive = true, handlers, onConfirmed, onCancelled, }) {
|
|
8
|
+
// isActive passed as prop
|
|
9
|
+
const [selectedIndex, setSelectedIndex] = useState(state?.selectedIndex ?? 0); // 0 = Yes, 1 = No
|
|
10
10
|
useInput((input, key) => {
|
|
11
|
-
if (
|
|
11
|
+
if (!isActive)
|
|
12
12
|
return;
|
|
13
13
|
if (key.escape) {
|
|
14
14
|
// Escape: highlight "No" and cancel
|
|
15
15
|
setSelectedIndex(1);
|
|
16
|
+
handlers?.updateState({ selectedIndex: 1 });
|
|
16
17
|
onCancelled?.();
|
|
17
18
|
}
|
|
18
19
|
else if (key.tab) {
|
|
19
20
|
// Toggle between Yes (0) and No (1)
|
|
20
|
-
setSelectedIndex((prev) =>
|
|
21
|
+
setSelectedIndex((prev) => {
|
|
22
|
+
const newIndex = prev === 0 ? 1 : 0;
|
|
23
|
+
handlers?.updateState({ selectedIndex: newIndex });
|
|
24
|
+
return newIndex;
|
|
25
|
+
});
|
|
21
26
|
}
|
|
22
27
|
else if (key.return) {
|
|
23
28
|
// Confirm selection
|
|
29
|
+
handlers?.updateState({ selectedIndex, confirmed: true });
|
|
24
30
|
if (selectedIndex === 0) {
|
|
25
31
|
onConfirmed?.();
|
|
26
32
|
}
|
|
@@ -28,16 +34,16 @@ export function Confirm({ message, state, onConfirmed, onCancelled, }) {
|
|
|
28
34
|
onCancelled?.();
|
|
29
35
|
}
|
|
30
36
|
}
|
|
31
|
-
}, { isActive
|
|
37
|
+
}, { isActive });
|
|
32
38
|
const options = [
|
|
33
39
|
{ label: 'yes', value: 'yes', color: Palette.BrightGreen },
|
|
34
40
|
{ label: 'no', value: 'no', color: Colors.Status.Error },
|
|
35
41
|
];
|
|
36
|
-
if (
|
|
42
|
+
if (!isActive) {
|
|
37
43
|
// When done, show both the message and user's choice in timeline
|
|
38
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: undefined, children: message }) }),
|
|
44
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, marginLeft: 1, children: _jsx(Text, { color: undefined, children: message }) }), _jsxs(UserQuery, { children: ["> ", options[selectedIndex].label] })] }));
|
|
39
45
|
}
|
|
40
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color:
|
|
46
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, marginLeft: 1, children: _jsx(Text, { color: isActive ? Colors.Text.Active : Colors.Text.Inactive, children: message }) }), _jsxs(Box, { marginLeft: 1, children: [_jsx(Text, { color: Colors.Action.Select, children: ">" }), _jsx(Text, { children: " " }), _jsx(Box, { children: options.map((option, index) => {
|
|
41
47
|
const isSelected = index === selectedIndex;
|
|
42
48
|
return (_jsx(Box, { marginRight: 2, children: _jsx(Text, { color: isSelected ? option.color : undefined, dimColor: !isSelected, children: option.label }) }, option.value));
|
|
43
49
|
}) })] })] }));
|
package/dist/ui/Execute.js
CHANGED
|
@@ -85,41 +85,53 @@ function CommandStatusDisplay({ item, elapsed }) {
|
|
|
85
85
|
const elapsedTime = getElapsedTime();
|
|
86
86
|
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { paddingLeft: 2, children: [_jsx(Text, { color: colors.icon, children: STATUS_ICONS[item.status] }), _jsx(Text, { color: colors.description, children: item.label || item.command.description }), elapsedTime !== undefined && (_jsxs(Text, { color: Palette.DarkGray, children: [" (", formatDuration(elapsedTime), ")"] }))] }), _jsxs(Box, { paddingLeft: 5, children: [_jsx(Text, { color: colors.symbol, children: "\u221F " }), _jsx(Text, { color: colors.command, children: item.command.command }), item.status === ExecutionStatus.Running && (_jsxs(Text, { children: [' ', _jsx(Spinner, {})] }))] })] }));
|
|
87
87
|
}
|
|
88
|
-
export function Execute({ tasks, state,
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
const [error, setError] = useState(null);
|
|
92
|
-
const [isLoading, setIsLoading] = useState(state?.isLoading ?? !done);
|
|
88
|
+
export function Execute({ tasks, state, isActive = true, service, handlers, }) {
|
|
89
|
+
// isActive passed as prop
|
|
90
|
+
const [error, setError] = useState(state?.error ?? null);
|
|
93
91
|
const [isExecuting, setIsExecuting] = useState(false);
|
|
94
|
-
const [commandStatuses, setCommandStatuses] = useState([]);
|
|
95
|
-
const [message, setMessage] = useState('');
|
|
92
|
+
const [commandStatuses, setCommandStatuses] = useState(state?.commandStatuses ?? []);
|
|
93
|
+
const [message, setMessage] = useState(state?.message ?? '');
|
|
96
94
|
const [currentElapsed, setCurrentElapsed] = useState(0);
|
|
97
95
|
const [runningIndex, setRunningIndex] = useState(null);
|
|
98
96
|
const [outputs, setOutputs] = useState([]);
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
97
|
+
const [hasProcessed, setHasProcessed] = useState(false);
|
|
98
|
+
// Derive loading state from current conditions
|
|
99
|
+
const isLoading = isActive &&
|
|
100
|
+
commandStatuses.length === 0 &&
|
|
101
|
+
!error &&
|
|
102
|
+
!isExecuting &&
|
|
103
|
+
!hasProcessed;
|
|
104
|
+
useInput((_, key) => {
|
|
105
|
+
if (key.escape && (isLoading || isExecuting) && isActive) {
|
|
102
106
|
setIsExecuting(false);
|
|
103
107
|
setRunningIndex(null);
|
|
104
108
|
// Mark any running command as aborted when cancelled
|
|
105
109
|
const now = Date.now();
|
|
106
|
-
setCommandStatuses((prev) =>
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
110
|
+
setCommandStatuses((prev) => {
|
|
111
|
+
const updated = prev.map((item) => {
|
|
112
|
+
if (item.status === ExecutionStatus.Running) {
|
|
113
|
+
const elapsed = item.startTime
|
|
114
|
+
? Math.floor((now - item.startTime) / 1000) * 1000
|
|
115
|
+
: undefined;
|
|
116
|
+
return {
|
|
117
|
+
...item,
|
|
118
|
+
status: ExecutionStatus.Aborted,
|
|
119
|
+
endTime: now,
|
|
120
|
+
elapsed,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
return item;
|
|
124
|
+
});
|
|
125
|
+
// Save state after updating
|
|
126
|
+
handlers?.updateState({
|
|
127
|
+
commandStatuses: updated,
|
|
128
|
+
message,
|
|
129
|
+
});
|
|
130
|
+
return updated;
|
|
131
|
+
});
|
|
132
|
+
handlers?.onAborted('execution');
|
|
121
133
|
}
|
|
122
|
-
}, { isActive: (isLoading || isExecuting) &&
|
|
134
|
+
}, { isActive: (isLoading || isExecuting) && isActive });
|
|
123
135
|
// Update elapsed time for running command
|
|
124
136
|
useEffect(() => {
|
|
125
137
|
if (runningIndex === null)
|
|
@@ -139,15 +151,20 @@ export function Execute({ tasks, state, service, onError, onComplete, onAborted,
|
|
|
139
151
|
useEffect(() => {
|
|
140
152
|
if (isExecuting || commandStatuses.length === 0 || !outputs.length)
|
|
141
153
|
return;
|
|
142
|
-
|
|
143
|
-
|
|
154
|
+
// Save state before completing
|
|
155
|
+
handlers?.updateState({
|
|
156
|
+
message,
|
|
157
|
+
commandStatuses,
|
|
158
|
+
error,
|
|
159
|
+
});
|
|
160
|
+
handlers?.completeActive();
|
|
161
|
+
}, [isExecuting, commandStatuses, outputs, handlers, message, error]);
|
|
144
162
|
useEffect(() => {
|
|
145
|
-
if (
|
|
163
|
+
if (!isActive) {
|
|
146
164
|
return;
|
|
147
165
|
}
|
|
148
166
|
if (!service) {
|
|
149
167
|
setError('No service available');
|
|
150
|
-
setIsLoading(false);
|
|
151
168
|
return;
|
|
152
169
|
}
|
|
153
170
|
let mounted = true;
|
|
@@ -173,9 +190,14 @@ export function Execute({ tasks, state, service, onError, onComplete, onAborted,
|
|
|
173
190
|
if (!mounted)
|
|
174
191
|
return;
|
|
175
192
|
if (!result.commands || result.commands.length === 0) {
|
|
176
|
-
setIsLoading(false);
|
|
177
193
|
setOutputs([]);
|
|
178
|
-
|
|
194
|
+
setHasProcessed(true);
|
|
195
|
+
// Save state before completing
|
|
196
|
+
handlers?.updateState({
|
|
197
|
+
message: result.message,
|
|
198
|
+
commandStatuses: [],
|
|
199
|
+
});
|
|
200
|
+
handlers?.completeActive();
|
|
179
201
|
return;
|
|
180
202
|
}
|
|
181
203
|
// Resolve placeholders in command strings before execution
|
|
@@ -190,7 +212,6 @@ export function Execute({ tasks, state, service, onError, onComplete, onAborted,
|
|
|
190
212
|
status: ExecutionStatus.Pending,
|
|
191
213
|
label: tasks[index]?.action,
|
|
192
214
|
})));
|
|
193
|
-
setIsLoading(false);
|
|
194
215
|
setIsExecuting(true);
|
|
195
216
|
// Execute commands sequentially
|
|
196
217
|
const outputs = await executeCommands(resolvedCommands, (progress) => {
|
|
@@ -236,14 +257,14 @@ export function Execute({ tasks, state, service, onError, onComplete, onAborted,
|
|
|
236
257
|
await ensureMinimumTime(startTime, MINIMUM_PROCESSING_TIME);
|
|
237
258
|
if (mounted) {
|
|
238
259
|
const errorMessage = formatErrorMessage(err);
|
|
239
|
-
setIsLoading(false);
|
|
240
260
|
setIsExecuting(false);
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}
|
|
261
|
+
setError(errorMessage);
|
|
262
|
+
setHasProcessed(true);
|
|
263
|
+
// Save error state
|
|
264
|
+
handlers?.updateState({
|
|
265
|
+
error: errorMessage,
|
|
266
|
+
});
|
|
267
|
+
handlers?.onError(errorMessage);
|
|
247
268
|
}
|
|
248
269
|
}
|
|
249
270
|
}
|
|
@@ -251,12 +272,12 @@ export function Execute({ tasks, state, service, onError, onComplete, onAborted,
|
|
|
251
272
|
return () => {
|
|
252
273
|
mounted = false;
|
|
253
274
|
};
|
|
254
|
-
}, [tasks,
|
|
275
|
+
}, [tasks, isActive, service, handlers]);
|
|
255
276
|
// Return null only when loading completes with no commands
|
|
256
|
-
if (
|
|
277
|
+
if (!isActive && commandStatuses.length === 0 && !error) {
|
|
257
278
|
return null;
|
|
258
279
|
}
|
|
259
|
-
// Show completed steps when
|
|
260
|
-
const showCompletedSteps =
|
|
261
|
-
return (_jsxs(Box, { alignSelf: "flex-start", flexDirection: "column", children: [isLoading && (_jsxs(Box, { children: [_jsx(Text, { color: getTextColor(
|
|
280
|
+
// Show completed steps when not active
|
|
281
|
+
const showCompletedSteps = !isActive && commandStatuses.length > 0;
|
|
282
|
+
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 || showCompletedSteps) && (_jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [message && (_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { color: getTextColor(isActive), children: message }), isExecuting && (_jsxs(Text, { children: [' ', _jsx(Spinner, {})] }))] })), commandStatuses.map((item, index) => (_jsx(Box, { marginBottom: index < commandStatuses.length - 1 ? 1 : 0, children: _jsx(CommandStatusDisplay, { item: item, elapsed: index === runningIndex ? currentElapsed : undefined }) }, index)))] })), error && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: Colors.Status.Error, children: ["Error: ", error] }) }))] }));
|
|
262
283
|
}
|
package/dist/ui/Feedback.js
CHANGED
|
@@ -13,5 +13,5 @@ function getSymbol(type) {
|
|
|
13
13
|
export function Feedback({ type, message }) {
|
|
14
14
|
const color = getFeedbackColor(type, false);
|
|
15
15
|
const symbol = getSymbol(type);
|
|
16
|
-
return (_jsx(Box, { children: _jsxs(Text, { color: color, children: [symbol, " ", message] }) }));
|
|
16
|
+
return (_jsx(Box, { marginLeft: 1, children: _jsxs(Text, { color: color, children: [symbol, " ", message] }) }));
|
|
17
17
|
}
|