prompt-language-shell 0.9.8 → 1.0.0
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 +97 -40
- package/dist/components/Component.js +5 -3
- package/dist/components/controllers/Command.js +3 -19
- package/dist/components/controllers/Config.js +109 -75
- package/dist/components/controllers/Execute.js +14 -11
- package/dist/components/controllers/Introspect.js +2 -4
- package/dist/components/controllers/Validate.js +3 -2
- package/dist/components/views/Execute.js +1 -1
- package/dist/components/views/Output.js +84 -32
- package/dist/components/views/Subtask.js +12 -7
- package/dist/components/views/Task.js +4 -5
- package/dist/components/views/Upcoming.js +1 -1
- package/dist/execution/runner.js +45 -29
- package/dist/services/anthropic.js +5 -4
- package/dist/services/logger.js +37 -9
- package/dist/services/monitor.js +19 -3
- package/dist/services/refinement.js +1 -7
- package/dist/services/router.js +223 -95
- package/dist/services/shell.js +18 -3
- package/dist/services/utils.js +11 -0
- package/dist/skills/schedule.md +7 -0
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -2,9 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Your personal command-line concierge. Ask politely, and it gets things done.
|
|
4
4
|
|
|
5
|
-
> **Note:** This project is in early preview. Features and APIs will change.
|
|
6
|
-
> See [roadmap](#roadmap).
|
|
7
|
-
|
|
8
5
|
## Installation
|
|
9
6
|
|
|
10
7
|
```bash
|
|
@@ -37,24 +34,23 @@ Skills are custom workflows you can define to teach `pls` about your specific
|
|
|
37
34
|
projects and commands. Once defined, you can use them naturally:
|
|
38
35
|
|
|
39
36
|
```
|
|
40
|
-
$ pls
|
|
37
|
+
$ pls convert video.mp4
|
|
41
38
|
|
|
42
39
|
Here's my plan.
|
|
43
40
|
|
|
44
|
-
-
|
|
45
|
-
- Compile source code
|
|
41
|
+
- Compress video.mp4 with H.264 codec
|
|
46
42
|
```
|
|
47
43
|
|
|
48
44
|
You can provide multiple requests at once:
|
|
49
45
|
|
|
50
46
|
```
|
|
51
|
-
$ pls
|
|
47
|
+
$ pls backup photos, compress and upload
|
|
52
48
|
|
|
53
49
|
Here's what I'll do.
|
|
54
50
|
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
51
|
+
- Copy photos to backup folder
|
|
52
|
+
- Create zip archive
|
|
53
|
+
- Upload to cloud storage
|
|
58
54
|
```
|
|
59
55
|
|
|
60
56
|
When `pls` needs clarification, it will present options to choose from:
|
|
@@ -138,60 +134,121 @@ Each skill file uses a simple markdown format:
|
|
|
138
134
|
- **Name**: What you call this workflow (e.g., "Build Project")
|
|
139
135
|
- **Description**: What it does and any variants or options
|
|
140
136
|
- **Steps**: What needs to happen, in order
|
|
141
|
-
- **Execution
|
|
137
|
+
- **Execution**: The actual shell commands to run
|
|
142
138
|
|
|
143
139
|
### Example
|
|
144
140
|
|
|
145
|
-
Here's a skill
|
|
141
|
+
Here's a skill for building a product from source:
|
|
146
142
|
|
|
147
143
|
```markdown
|
|
148
144
|
### Name
|
|
149
|
-
Build
|
|
145
|
+
Build Product
|
|
150
146
|
|
|
151
147
|
### Description
|
|
152
|
-
Build a
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
-
|
|
148
|
+
Build a product from source. Handles the full compilation pipeline.
|
|
149
|
+
|
|
150
|
+
The company maintains two product lines:
|
|
151
|
+
- Stable: the flagship product
|
|
152
|
+
- Beta: the experimental product
|
|
153
|
+
|
|
154
|
+
If the user says "just compile" or "recompile", dependency installation and
|
|
155
|
+
tests MUST be skipped. Tests MUST also be skipped if the user says "without
|
|
156
|
+
tests". Deployment MUST only run if the user explicitly asks. Compile and
|
|
157
|
+
package steps are MANDATORY.
|
|
156
158
|
|
|
157
159
|
### Steps
|
|
158
160
|
- Navigate to the project directory
|
|
159
|
-
- Install dependencies
|
|
160
|
-
- Run the
|
|
161
|
-
-
|
|
161
|
+
- Install build dependencies
|
|
162
|
+
- Run the test suite
|
|
163
|
+
- Compile source code
|
|
164
|
+
- Package build artifacts
|
|
165
|
+
- Deploy to server
|
|
166
|
+
|
|
167
|
+
### Execution
|
|
168
|
+
- [ Navigate To Project ]
|
|
169
|
+
- ./configure && make deps
|
|
170
|
+
- make test
|
|
171
|
+
- make build
|
|
172
|
+
- make package
|
|
173
|
+
- ./scripts/deploy.sh
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
The `[ Navigate To Project ]` reference invokes another skill by name. When `pls`
|
|
177
|
+
plans this workflow, it expands the reference inline, inserting that skill's
|
|
178
|
+
execution steps at this position. This lets you compose complex workflows from
|
|
179
|
+
simpler, reusable skills. Here's what that skill might look like:
|
|
180
|
+
|
|
181
|
+
```markdown
|
|
182
|
+
### Name
|
|
183
|
+
Navigate To Project
|
|
184
|
+
|
|
185
|
+
### Description
|
|
186
|
+
The company maintains two product lines:
|
|
187
|
+
- Stable: the flagship product
|
|
188
|
+
- Beta: the experimental product
|
|
189
|
+
|
|
190
|
+
### Aliases
|
|
191
|
+
- go to project
|
|
192
|
+
- navigate to repo
|
|
193
|
+
|
|
194
|
+
### Config
|
|
195
|
+
project:
|
|
196
|
+
stable:
|
|
197
|
+
path: string
|
|
198
|
+
beta:
|
|
199
|
+
path: string
|
|
200
|
+
|
|
201
|
+
### Steps
|
|
202
|
+
- Navigate to project directory
|
|
162
203
|
|
|
163
204
|
### Execution
|
|
164
|
-
- cd
|
|
165
|
-
- npm install
|
|
166
|
-
- npm run build:{ENV}
|
|
167
|
-
- cp -r dist/ builds/{ENV}/
|
|
205
|
+
- cd {project.PRODUCT.path}
|
|
168
206
|
```
|
|
169
207
|
|
|
170
|
-
|
|
208
|
+
The `{project.PRODUCT.path}` placeholder uses config values from `~/.plsrc`. The
|
|
209
|
+
PRODUCT is matched from user intent (e.g., "build stable" resolves to
|
|
210
|
+
`project.stable.path`).
|
|
211
|
+
|
|
212
|
+
The Description tells `pls` when to skip optional steps. This lets you say:
|
|
213
|
+
|
|
171
214
|
```
|
|
172
|
-
$ pls build
|
|
173
|
-
|
|
174
|
-
|
|
215
|
+
$ pls build stable
|
|
216
|
+
|
|
217
|
+
- Navigate to the Stable directory
|
|
218
|
+
- Install build dependencies
|
|
219
|
+
- Run the test suite
|
|
220
|
+
- Compile source code
|
|
221
|
+
- Package build artifacts
|
|
175
222
|
```
|
|
176
|
-
The `{ENV}` placeholder gets replaced with the variant you specify.
|
|
177
|
-
Instead of remembering the exact commands and paths for each environment, just
|
|
178
|
-
tell `pls` what you want in plain English. The Execution section ensures the right commands run every time.
|
|
179
223
|
|
|
180
|
-
|
|
224
|
+
Here "stable" matches the PRODUCT, so `pls` looks up `project.stable.path` in your
|
|
225
|
+
config. All steps run except deploy (not requested). When iterating quickly:
|
|
181
226
|
|
|
182
|
-
|
|
183
|
-
|
|
227
|
+
```
|
|
228
|
+
$ pls just recompile experimental
|
|
184
229
|
|
|
230
|
+
- Navigate to the Beta directory
|
|
231
|
+
- Compile source code
|
|
232
|
+
- Package build artifacts
|
|
185
233
|
```
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
234
|
+
|
|
235
|
+
Now "experimental" resolves to `project.beta.path`. And when you're ready to ship:
|
|
236
|
+
|
|
189
237
|
```
|
|
238
|
+
$ pls build and deploy main
|
|
190
239
|
|
|
191
|
-
|
|
240
|
+
- Navigate to the Stable directory
|
|
241
|
+
- Install build dependencies
|
|
242
|
+
- Run the test suite
|
|
243
|
+
- Compile source code
|
|
244
|
+
- Package build artifacts
|
|
245
|
+
- Deploy to server
|
|
246
|
+
```
|
|
192
247
|
|
|
193
|
-
|
|
194
|
-
|
|
248
|
+
The same skill handles all cases based on your intent, something an alias or
|
|
249
|
+
script can't do. Skills are fully dynamic: you can add new variants, change step
|
|
250
|
+
conditions, or introduce new options anytime by editing the markdown file - no
|
|
251
|
+
code changes required.
|
|
195
252
|
|
|
196
253
|
## Development
|
|
197
254
|
|
|
@@ -51,8 +51,8 @@ export const SimpleComponent = memo(function SimpleComponent({ def, }) {
|
|
|
51
51
|
export const ControllerComponent = memo(function ControllerComponent({ def, debug, requestHandlers, lifecycleHandlers, workflowHandlers, }) {
|
|
52
52
|
switch (def.name) {
|
|
53
53
|
case ComponentName.Config: {
|
|
54
|
-
const { props: { steps, onFinished, onAborted }, status, } = def;
|
|
55
|
-
return (_jsx(Config, { steps: steps, onFinished: onFinished, onAborted: onAborted, requestHandlers: requestHandlers, lifecycleHandlers: lifecycleHandlers, status: status, debug: debug }));
|
|
54
|
+
const { props: { steps, query, service, onFinished, onAborted }, status, } = def;
|
|
55
|
+
return (_jsx(Config, { steps: steps, query: query, service: service, onFinished: onFinished, onAborted: onAborted, requestHandlers: requestHandlers, lifecycleHandlers: lifecycleHandlers, workflowHandlers: workflowHandlers, status: status, debug: debug }));
|
|
56
56
|
}
|
|
57
57
|
case ComponentName.Command: {
|
|
58
58
|
const { props: { command, service, onAborted }, status, } = def;
|
|
@@ -100,7 +100,9 @@ export const ViewComponent = memo(function ViewComponent({ def, }) {
|
|
|
100
100
|
return (_jsx(ConfirmView, { status: status, message: message, selectedIndex: state.selectedIndex }));
|
|
101
101
|
}
|
|
102
102
|
case ComponentName.Config: {
|
|
103
|
-
const { props: { steps }, state, status, } = def;
|
|
103
|
+
const { props: { steps: propSteps = [] }, state, status, } = def;
|
|
104
|
+
// Use resolved steps from state if available (for query-based configs)
|
|
105
|
+
const steps = state.steps ?? propSteps;
|
|
104
106
|
return _jsx(ConfigView, { steps: steps, state: state, status: status });
|
|
105
107
|
}
|
|
106
108
|
case ComponentName.Schedule: {
|
|
@@ -34,28 +34,12 @@ export function Command({ command, status, service, requestHandlers, lifecycleHa
|
|
|
34
34
|
async function process(svc) {
|
|
35
35
|
const startTime = Date.now();
|
|
36
36
|
try {
|
|
37
|
-
|
|
38
|
-
// Save schedule debug output before potentially delegating
|
|
39
|
-
const scheduleDebug = result.debug || [];
|
|
40
|
-
// If all tasks are configure type, delegate to CONFIGURE tool
|
|
41
|
-
const allConfig = result.tasks.length > 0 &&
|
|
42
|
-
result.tasks.every((task) => task.type === TaskType.Config);
|
|
43
|
-
if (allConfig) {
|
|
44
|
-
// Extract query from first config task params, default to 'app'
|
|
45
|
-
const query = result.tasks[0].params?.query || 'app';
|
|
46
|
-
// Call CONFIGURE tool to get specific config keys
|
|
47
|
-
result = await svc.processWithTool(query, 'configure');
|
|
48
|
-
}
|
|
37
|
+
const result = await svc.processWithTool(command, 'schedule');
|
|
49
38
|
await ensureMinimumTime(startTime, MIN_PROCESSING_TIME);
|
|
50
39
|
if (mounted) {
|
|
51
40
|
// Add debug components to timeline if present
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const debugComponents = allConfig
|
|
55
|
-
? [...scheduleDebug, ...(result.debug || [])]
|
|
56
|
-
: scheduleDebug;
|
|
57
|
-
if (debugComponents.length > 0) {
|
|
58
|
-
workflowHandlers.addToTimeline(...debugComponents);
|
|
41
|
+
if (result.debug?.length) {
|
|
42
|
+
workflowHandlers.addToTimeline(...result.debug);
|
|
59
43
|
}
|
|
60
44
|
// Update local state
|
|
61
45
|
setMessage(result.message);
|
|
@@ -1,24 +1,57 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useState } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { Box, Text } from 'ink';
|
|
4
|
+
import { ComponentStatus, } from '../../types/components.js';
|
|
5
|
+
import { FeedbackType, TaskType } from '../../types/types.js';
|
|
5
6
|
import { createFeedback } from '../../services/components.js';
|
|
7
|
+
import { createConfigStepsFromSchema } from '../../configuration/steps.js';
|
|
8
|
+
import { saveConfigLabels } from '../../configuration/labels.js';
|
|
6
9
|
import { DebugLevel } from '../../configuration/types.js';
|
|
7
10
|
import { useInput } from '../../services/keyboard.js';
|
|
8
11
|
import { ConfigView, StepType } from '../views/Config.js';
|
|
12
|
+
import { Spinner } from '../views/Spinner.js';
|
|
9
13
|
export { ConfigView, StepType, } from '../views/Config.js';
|
|
14
|
+
/**
|
|
15
|
+
* Resolve query to config steps via CONFIGURE tool
|
|
16
|
+
*/
|
|
17
|
+
async function resolveQueryToSteps(query, service) {
|
|
18
|
+
const result = await service.processWithTool(query, 'configure');
|
|
19
|
+
const configTasks = result.tasks.filter((task) => task.type === TaskType.Config && task.params?.key);
|
|
20
|
+
if (configTasks.length === 0) {
|
|
21
|
+
throw new Error('No configuration settings matched your query.');
|
|
22
|
+
}
|
|
23
|
+
const keys = configTasks.map((task) => task.params?.key);
|
|
24
|
+
const labels = {};
|
|
25
|
+
for (const task of configTasks) {
|
|
26
|
+
const key = task.params?.key;
|
|
27
|
+
if (key && task.action) {
|
|
28
|
+
labels[key] = task.action;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (Object.keys(labels).length > 0) {
|
|
32
|
+
saveConfigLabels(labels);
|
|
33
|
+
}
|
|
34
|
+
const steps = createConfigStepsFromSchema(keys);
|
|
35
|
+
return {
|
|
36
|
+
steps: steps.map((step, i) => ({
|
|
37
|
+
...step,
|
|
38
|
+
description: labels[keys[i]] || step.description,
|
|
39
|
+
})),
|
|
40
|
+
debug: result.debug || [],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
10
43
|
/**
|
|
11
44
|
* Config controller: Multi-step wizard logic
|
|
12
45
|
*/
|
|
13
46
|
export function Config(props) {
|
|
14
|
-
const { steps, status, debug = DebugLevel.None, requestHandlers, lifecycleHandlers, onFinished, onAborted, } = props;
|
|
47
|
+
const { steps: initialSteps, query, service, status, debug = DebugLevel.None, requestHandlers, lifecycleHandlers, workflowHandlers, onFinished, onAborted, } = props;
|
|
15
48
|
const isActive = status === ComponentStatus.Active;
|
|
49
|
+
const [steps, setSteps] = useState(initialSteps || []);
|
|
50
|
+
const [resolving, setResolving] = useState(!initialSteps?.length && !!query);
|
|
16
51
|
const [step, setStep] = useState(0);
|
|
17
52
|
const [values, setValues] = useState(() => {
|
|
18
|
-
// Initialize from step defaults
|
|
19
53
|
const initial = {};
|
|
20
|
-
|
|
21
|
-
// Use full path if available, otherwise use key
|
|
54
|
+
(initialSteps || []).forEach((stepConfig) => {
|
|
22
55
|
const configKey = stepConfig.path || stepConfig.key;
|
|
23
56
|
switch (stepConfig.type) {
|
|
24
57
|
case StepType.Text:
|
|
@@ -30,45 +63,75 @@ export function Config(props) {
|
|
|
30
63
|
initial[configKey] =
|
|
31
64
|
stepConfig.options[stepConfig.defaultIndex].value;
|
|
32
65
|
break;
|
|
33
|
-
default: {
|
|
34
|
-
const _exhaustiveCheck = stepConfig;
|
|
35
|
-
throw new Error('Unsupported step type');
|
|
36
|
-
}
|
|
37
66
|
}
|
|
38
67
|
});
|
|
39
68
|
return initial;
|
|
40
69
|
});
|
|
41
|
-
const [inputValue, setInputValue] = useState(
|
|
42
|
-
// Initialize with the current step's value if available
|
|
43
|
-
if (step < steps.length) {
|
|
44
|
-
const stepConfig = steps[step];
|
|
45
|
-
const configKey = stepConfig.path || stepConfig.key;
|
|
46
|
-
return values[configKey] || '';
|
|
47
|
-
}
|
|
48
|
-
return '';
|
|
49
|
-
});
|
|
70
|
+
const [inputValue, setInputValue] = useState('');
|
|
50
71
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
72
|
+
// Resolve query to steps
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
if (!isActive || !query || !service || initialSteps?.length)
|
|
75
|
+
return;
|
|
76
|
+
resolveQueryToSteps(query, service)
|
|
77
|
+
.then((result) => {
|
|
78
|
+
// Add debug components to timeline if present
|
|
79
|
+
if (result.debug.length) {
|
|
80
|
+
workflowHandlers.addToTimeline(...result.debug);
|
|
81
|
+
}
|
|
82
|
+
setSteps(result.steps);
|
|
83
|
+
setResolving(false);
|
|
84
|
+
// Initialize values for resolved steps
|
|
85
|
+
const initial = {};
|
|
86
|
+
result.steps.forEach((stepConfig) => {
|
|
87
|
+
const configKey = stepConfig.path || stepConfig.key;
|
|
88
|
+
switch (stepConfig.type) {
|
|
89
|
+
case StepType.Text:
|
|
90
|
+
if (stepConfig.value !== null) {
|
|
91
|
+
initial[configKey] = stepConfig.value;
|
|
92
|
+
}
|
|
93
|
+
break;
|
|
94
|
+
case StepType.Selection:
|
|
95
|
+
initial[configKey] =
|
|
96
|
+
stepConfig.options[stepConfig.defaultIndex].value;
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
setValues(initial);
|
|
101
|
+
})
|
|
102
|
+
.catch((err) => {
|
|
103
|
+
setResolving(false);
|
|
104
|
+
lifecycleHandlers.completeActive(createFeedback({
|
|
105
|
+
type: FeedbackType.Failed,
|
|
106
|
+
message: err instanceof Error ? err.message : 'Failed to resolve',
|
|
107
|
+
}));
|
|
108
|
+
});
|
|
109
|
+
}, [
|
|
110
|
+
isActive,
|
|
111
|
+
query,
|
|
112
|
+
service,
|
|
113
|
+
initialSteps,
|
|
114
|
+
lifecycleHandlers,
|
|
115
|
+
workflowHandlers,
|
|
116
|
+
]);
|
|
57
117
|
// Update inputValue when step changes
|
|
58
118
|
useEffect(() => {
|
|
59
119
|
if (isActive && step < steps.length) {
|
|
60
120
|
const stepConfig = steps[step];
|
|
61
121
|
const configKey = stepConfig.path || stepConfig.key;
|
|
62
|
-
|
|
63
|
-
setInputValue(value);
|
|
122
|
+
setInputValue(values[configKey] || '');
|
|
64
123
|
}
|
|
65
|
-
}, [step, isActive, steps]);
|
|
124
|
+
}, [step, isActive, steps, values]);
|
|
125
|
+
const normalizeValue = (value) => {
|
|
126
|
+
if (value === null || value === undefined)
|
|
127
|
+
return '';
|
|
128
|
+
return value.replace(/\n/g, '').trim();
|
|
129
|
+
};
|
|
66
130
|
useInput((_, key) => {
|
|
67
131
|
if (!isActive || step >= steps.length)
|
|
68
132
|
return;
|
|
69
133
|
const currentStepConfig = steps[step];
|
|
70
134
|
if (key.escape) {
|
|
71
|
-
// Save current value before aborting
|
|
72
135
|
const configKey = currentStepConfig.path || currentStepConfig.key;
|
|
73
136
|
let currentValue = '';
|
|
74
137
|
switch (currentStepConfig.type) {
|
|
@@ -78,28 +141,20 @@ export function Config(props) {
|
|
|
78
141
|
case StepType.Selection:
|
|
79
142
|
currentValue = values[configKey] || '';
|
|
80
143
|
break;
|
|
81
|
-
default: {
|
|
82
|
-
const _exhaustiveCheck = currentStepConfig;
|
|
83
|
-
throw new Error('Unsupported step type');
|
|
84
|
-
}
|
|
85
144
|
}
|
|
86
145
|
const finalValues = currentValue
|
|
87
146
|
? { ...values, [configKey]: currentValue }
|
|
88
147
|
: values;
|
|
89
|
-
|
|
90
|
-
const finalState = {
|
|
148
|
+
requestHandlers.onCompleted({
|
|
91
149
|
values: finalValues,
|
|
92
150
|
completedStep: step,
|
|
93
151
|
selectedIndex,
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
// Abort configuration
|
|
152
|
+
steps,
|
|
153
|
+
});
|
|
97
154
|
if (onAborted) {
|
|
98
|
-
// Let Workflow handler complete and add feedback
|
|
99
155
|
onAborted('configuration');
|
|
100
156
|
}
|
|
101
157
|
else {
|
|
102
|
-
// Fallback: complete with abort feedback directly
|
|
103
158
|
lifecycleHandlers.completeActive(createFeedback({
|
|
104
159
|
type: FeedbackType.Aborted,
|
|
105
160
|
message: 'Configuration cancelled.',
|
|
@@ -107,7 +162,6 @@ export function Config(props) {
|
|
|
107
162
|
}
|
|
108
163
|
return;
|
|
109
164
|
}
|
|
110
|
-
// Handle selection step navigation
|
|
111
165
|
if (currentStepConfig.type === StepType.Selection) {
|
|
112
166
|
if (key.tab) {
|
|
113
167
|
setSelectedIndex((prev) => (prev + 1) % currentStepConfig.options.length);
|
|
@@ -122,13 +176,10 @@ export function Config(props) {
|
|
|
122
176
|
let finalValue = '';
|
|
123
177
|
switch (currentStepConfig.type) {
|
|
124
178
|
case StepType.Selection:
|
|
125
|
-
// For selection, value is already validated by options
|
|
126
179
|
finalValue = value;
|
|
127
180
|
break;
|
|
128
181
|
case StepType.Text: {
|
|
129
|
-
// For text input
|
|
130
182
|
const normalizedInput = normalizeValue(value);
|
|
131
|
-
// Try user input first, then fall back to default
|
|
132
183
|
if (normalizedInput && currentStepConfig.validate(normalizedInput)) {
|
|
133
184
|
finalValue = normalizedInput;
|
|
134
185
|
}
|
|
@@ -138,63 +189,46 @@ export function Config(props) {
|
|
|
138
189
|
}
|
|
139
190
|
break;
|
|
140
191
|
}
|
|
141
|
-
default: {
|
|
142
|
-
const _exhaustiveCheck = currentStepConfig;
|
|
143
|
-
throw new Error('Unsupported step type');
|
|
144
|
-
}
|
|
145
192
|
}
|
|
146
|
-
|
|
147
|
-
if (!finalValue) {
|
|
193
|
+
if (!finalValue)
|
|
148
194
|
return;
|
|
149
|
-
}
|
|
150
|
-
// Use full path if available, otherwise use key
|
|
151
195
|
const configKey = currentStepConfig.path || currentStepConfig.key;
|
|
152
196
|
const newValues = { ...values, [configKey]: finalValue };
|
|
153
197
|
setValues(newValues);
|
|
154
198
|
setInputValue('');
|
|
155
199
|
if (step === steps.length - 1) {
|
|
156
|
-
|
|
157
|
-
// Expose final state
|
|
158
|
-
const finalState = {
|
|
200
|
+
requestHandlers.onCompleted({
|
|
159
201
|
values: newValues,
|
|
160
202
|
completedStep: steps.length,
|
|
161
203
|
selectedIndex,
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
// Call onFinished callback and handle result
|
|
204
|
+
steps,
|
|
205
|
+
});
|
|
165
206
|
try {
|
|
166
|
-
|
|
167
|
-
onFinished(newValues);
|
|
168
|
-
}
|
|
169
|
-
// Success - complete with success feedback
|
|
207
|
+
onFinished?.(newValues);
|
|
170
208
|
lifecycleHandlers.completeActive(createFeedback({
|
|
171
209
|
type: FeedbackType.Succeeded,
|
|
172
210
|
message: 'Configuration saved successfully.',
|
|
173
211
|
}));
|
|
174
212
|
}
|
|
175
213
|
catch (error) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
214
|
+
lifecycleHandlers.completeActive(createFeedback({
|
|
215
|
+
type: FeedbackType.Failed,
|
|
216
|
+
message: error instanceof Error ? error.message : 'Configuration failed',
|
|
217
|
+
}));
|
|
179
218
|
}
|
|
180
219
|
setStep(steps.length);
|
|
181
220
|
}
|
|
182
221
|
else {
|
|
183
222
|
const nextStep = step + 1;
|
|
184
223
|
setStep(nextStep);
|
|
185
|
-
// Reset selectedIndex for next step
|
|
186
224
|
if (nextStep < steps.length &&
|
|
187
225
|
steps[nextStep].type === StepType.Selection) {
|
|
188
226
|
setSelectedIndex(steps[nextStep].defaultIndex);
|
|
189
227
|
}
|
|
190
228
|
}
|
|
191
229
|
};
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
completedStep: step,
|
|
197
|
-
selectedIndex,
|
|
198
|
-
};
|
|
199
|
-
return (_jsx(ConfigView, { steps: steps, state: state, status: status, debug: debug, onInputChange: setInputValue, onInputSubmit: handleSubmit }));
|
|
230
|
+
if (resolving) {
|
|
231
|
+
return (_jsxs(Box, { marginLeft: 1, children: [_jsx(Text, { children: "Resolving configuration... " }), _jsx(Spinner, {})] }));
|
|
232
|
+
}
|
|
233
|
+
return (_jsx(ConfigView, { steps: steps, state: { values, completedStep: step, selectedIndex }, status: status, debug: debug, onInputChange: setInputValue, onInputSubmit: handleSubmit }));
|
|
200
234
|
}
|
|
@@ -37,7 +37,10 @@ export function Execute({ tasks: inputTasks, status, service, upcoming, label, r
|
|
|
37
37
|
// Track working directory across commands (persists cd changes)
|
|
38
38
|
const workdirRef = useRef(undefined);
|
|
39
39
|
// Ref to collect live output during execution (dispatched every second)
|
|
40
|
-
const outputRef = useRef({
|
|
40
|
+
const outputRef = useRef({
|
|
41
|
+
chunks: [],
|
|
42
|
+
currentMemory: undefined,
|
|
43
|
+
});
|
|
41
44
|
// Ref to track if current task execution is cancelled
|
|
42
45
|
const cancelledRef = useRef(false);
|
|
43
46
|
const { error, tasks, message, hasProcessed, completionMessage, summary } = localState;
|
|
@@ -62,8 +65,8 @@ export function Execute({ tasks: inputTasks, status, service, upcoming, label, r
|
|
|
62
65
|
index: currentTaskIndex,
|
|
63
66
|
elapsed,
|
|
64
67
|
output: {
|
|
65
|
-
|
|
66
|
-
|
|
68
|
+
chunks: outputRef.current.chunks,
|
|
69
|
+
currentMemory: outputRef.current.currentMemory,
|
|
67
70
|
},
|
|
68
71
|
},
|
|
69
72
|
});
|
|
@@ -83,8 +86,7 @@ export function Execute({ tasks: inputTasks, status, service, upcoming, label, r
|
|
|
83
86
|
...task,
|
|
84
87
|
status: ExecutionStatus.Aborted,
|
|
85
88
|
output: {
|
|
86
|
-
|
|
87
|
-
stderr: outputRef.current.stderr,
|
|
89
|
+
chunks: outputRef.current.chunks,
|
|
88
90
|
},
|
|
89
91
|
};
|
|
90
92
|
}
|
|
@@ -207,7 +209,7 @@ export function Execute({ tasks: inputTasks, status, service, upcoming, label, r
|
|
|
207
209
|
payload: { index: currentTaskIndex, startTime: Date.now() },
|
|
208
210
|
});
|
|
209
211
|
// Reset output ref for new task
|
|
210
|
-
outputRef.current = {
|
|
212
|
+
outputRef.current = { chunks: [], currentMemory: undefined };
|
|
211
213
|
// Merge workdir into command
|
|
212
214
|
const command = workdirRef.current
|
|
213
215
|
? { ...currentTask.command, workdir: workdirRef.current }
|
|
@@ -215,7 +217,10 @@ export function Execute({ tasks: inputTasks, status, service, upcoming, label, r
|
|
|
215
217
|
void executeTask(command, currentTaskIndex, {
|
|
216
218
|
onUpdate: (output) => {
|
|
217
219
|
if (!cancelledRef.current) {
|
|
218
|
-
outputRef.current = {
|
|
220
|
+
outputRef.current = {
|
|
221
|
+
chunks: output.chunks,
|
|
222
|
+
currentMemory: output.currentMemory,
|
|
223
|
+
};
|
|
219
224
|
}
|
|
220
225
|
},
|
|
221
226
|
onComplete: (elapsed, execOutput) => {
|
|
@@ -229,8 +234,7 @@ export function Execute({ tasks: inputTasks, status, service, upcoming, label, r
|
|
|
229
234
|
? {
|
|
230
235
|
...task,
|
|
231
236
|
output: {
|
|
232
|
-
|
|
233
|
-
stderr: execOutput.stderr,
|
|
237
|
+
chunks: execOutput.chunks,
|
|
234
238
|
},
|
|
235
239
|
}
|
|
236
240
|
: task);
|
|
@@ -256,8 +260,7 @@ export function Execute({ tasks: inputTasks, status, service, upcoming, label, r
|
|
|
256
260
|
? {
|
|
257
261
|
...task,
|
|
258
262
|
output: {
|
|
259
|
-
|
|
260
|
-
stderr: execOutput.stderr,
|
|
263
|
+
chunks: execOutput.chunks,
|
|
261
264
|
},
|
|
262
265
|
error: execOutput.error || undefined,
|
|
263
266
|
}
|
|
@@ -54,10 +54,8 @@ export function Introspect({ tasks, status, service, children, debug = DebugLeve
|
|
|
54
54
|
message,
|
|
55
55
|
};
|
|
56
56
|
requestHandlers.onCompleted(finalState);
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
// Signal completion
|
|
60
|
-
lifecycleHandlers.completeActive();
|
|
57
|
+
// Signal completion and add Report to timeline (not queue, to preserve order)
|
|
58
|
+
lifecycleHandlers.completeActive(createReport({ message, capabilities }));
|
|
61
59
|
}
|
|
62
60
|
}
|
|
63
61
|
catch (err) {
|
|
@@ -90,8 +90,9 @@ export function Validate({ missingConfig, userRequest, status, service, onError,
|
|
|
90
90
|
},
|
|
91
91
|
});
|
|
92
92
|
// Override descriptions with LLM-generated ones
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
const configProps = configDef.props;
|
|
94
|
+
if (configProps.steps) {
|
|
95
|
+
configProps.steps = configProps.steps.map((step, index) => ({
|
|
95
96
|
...step,
|
|
96
97
|
description: withDescriptions[index].description ||
|
|
97
98
|
withDescriptions[index].path,
|
|
@@ -56,5 +56,5 @@ export const ExecuteView = ({ isLoading, isExecuting, isActive, error, message,
|
|
|
56
56
|
const isTerminated = upcomingStatus !== ExecutionStatus.Pending;
|
|
57
57
|
// Show upcoming during active execution or when terminated (to show skipped tasks)
|
|
58
58
|
const showUpcoming = upcoming && upcoming.length > 0 && (isActive || isTerminated);
|
|
59
|
-
return (_jsxs(Box, { alignSelf: "flex-start", flexDirection: "column", children: [isLoading && (_jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [label && (_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: getTextColor(isActive), children: label }) })), _jsxs(Box, { marginLeft: label ? 2 : 0, children: [_jsx(Text, { color: Palette.Gray, 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, output: task.output, isFinished: isTaskFinished(task), isActive: isActive }) }, index)))] })), showUpcoming && (_jsx(Box, { marginTop: 1, children: _jsx(Upcoming, { items: upcoming, status: upcomingStatus }) })), 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 }) }))] }));
|
|
59
|
+
return (_jsxs(Box, { alignSelf: "flex-start", flexDirection: "column", children: [isLoading && (_jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [label && (_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: getTextColor(isActive), children: label }) })), _jsxs(Box, { marginLeft: label ? 2 : 0, children: [_jsx(Text, { color: Palette.Gray, 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, currentMemory: task.output?.currentMemory, output: task.output, isFinished: isTaskFinished(task), isActive: isActive }) }, index)))] })), showUpcoming && (_jsx(Box, { marginTop: 1, children: _jsx(Upcoming, { items: upcoming, status: upcomingStatus }) })), 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 }) }))] }));
|
|
60
60
|
};
|