prompt-language-shell 0.9.6 → 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 +134 -46
- 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 +17 -14
- 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 +89 -35
- 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/configuration/io.js +10 -0
- package/dist/configuration/schema.js +6 -0
- package/dist/configuration/validation.js +5 -0
- package/dist/execution/processing.js +45 -14
- package/dist/execution/runner.js +46 -30
- package/dist/index.js +2 -0
- package/dist/services/anthropic.js +5 -4
- package/dist/services/filesystem.js +13 -1
- package/dist/services/logger.js +176 -28
- package/dist/services/messages.js +7 -4
- package/dist/services/monitor.js +304 -0
- package/dist/services/performance.js +14 -0
- package/dist/services/refinement.js +7 -16
- package/dist/services/router.js +223 -95
- package/dist/services/shell.js +49 -16
- package/dist/services/utils.js +11 -0
- package/dist/skills/execute.md +82 -3
- package/dist/skills/schedule.md +7 -0
- package/package.json +11 -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
|
|
@@ -31,30 +28,29 @@ Here's what I can help with:
|
|
|
31
28
|
- Configure - manage and configure system settings
|
|
32
29
|
- Answer - respond to questions and provide information
|
|
33
30
|
- Execute - run shell commands and process operations
|
|
34
|
-
|
|
31
|
+
```
|
|
35
32
|
|
|
36
33
|
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:
|
|
@@ -87,16 +83,47 @@ commands your environment requires.
|
|
|
87
83
|
|
|
88
84
|
## Configuration
|
|
89
85
|
|
|
90
|
-
Your configuration is stored in `~/.plsrc` as a YAML file
|
|
86
|
+
Your configuration is stored in `~/.plsrc` as a YAML file:
|
|
87
|
+
|
|
88
|
+
```yaml
|
|
89
|
+
# Mandatory
|
|
90
|
+
anthropic:
|
|
91
|
+
key: sk-ant-...
|
|
92
|
+
model: claude-...
|
|
93
|
+
|
|
94
|
+
# Optional
|
|
95
|
+
settings:
|
|
96
|
+
memory: 1024 # Child process memory limit (MB)
|
|
97
|
+
debug: none # none | info | verbose
|
|
91
98
|
|
|
92
|
-
|
|
93
|
-
|
|
99
|
+
# Custom
|
|
100
|
+
project:
|
|
101
|
+
path: ~/projects/app
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Skills can define their own configuration properties via a `Config` section. When
|
|
105
|
+
a skill requires config values that don't exist, `pls` prompts you to provide
|
|
106
|
+
them before execution. See [Skills](#skills) for details.
|
|
107
|
+
|
|
108
|
+
## Reference
|
|
109
|
+
|
|
110
|
+
### Debug Mode
|
|
111
|
+
Press `Shift+Tab` during execution to cycle through debug levels
|
|
112
|
+
(none → info → verbose).
|
|
113
|
+
Logs are saved to `~/.pls/logs/` when debug is `info` or `verbose`.
|
|
114
|
+
|
|
115
|
+
### Data Locations
|
|
116
|
+
```
|
|
117
|
+
~/.plsrc # Configuration
|
|
118
|
+
~/.pls/skills/ # Custom skills
|
|
119
|
+
~/.pls/logs/ # Debug logs
|
|
120
|
+
```
|
|
94
121
|
|
|
95
122
|
## Skills
|
|
96
123
|
|
|
97
124
|
Skills let you teach `pls` about your project-specific workflows. Create
|
|
98
|
-
markdown files in `~/.pls/skills/` to define custom operations that
|
|
99
|
-
understand and execute.
|
|
125
|
+
markdown files in `~/.pls/skills/` to define custom operations that
|
|
126
|
+
`pls` can understand and execute.
|
|
100
127
|
|
|
101
128
|
For complete documentation, see [docs/SKILLS.md](./docs/SKILLS.md).
|
|
102
129
|
|
|
@@ -107,60 +134,121 @@ Each skill file uses a simple markdown format:
|
|
|
107
134
|
- **Name**: What you call this workflow (e.g., "Build Project")
|
|
108
135
|
- **Description**: What it does and any variants or options
|
|
109
136
|
- **Steps**: What needs to happen, in order
|
|
110
|
-
- **Execution
|
|
137
|
+
- **Execution**: The actual shell commands to run
|
|
111
138
|
|
|
112
139
|
### Example
|
|
113
140
|
|
|
114
|
-
Here's a skill
|
|
141
|
+
Here's a skill for building a product from source:
|
|
115
142
|
|
|
116
143
|
```markdown
|
|
117
144
|
### Name
|
|
118
|
-
Build
|
|
145
|
+
Build Product
|
|
119
146
|
|
|
120
147
|
### Description
|
|
121
|
-
Build a
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
-
|
|
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.
|
|
125
158
|
|
|
126
159
|
### Steps
|
|
127
160
|
- Navigate to the project directory
|
|
128
|
-
- Install dependencies
|
|
129
|
-
- Run the
|
|
130
|
-
-
|
|
161
|
+
- Install build dependencies
|
|
162
|
+
- Run the test suite
|
|
163
|
+
- Compile source code
|
|
164
|
+
- Package build artifacts
|
|
165
|
+
- Deploy to server
|
|
131
166
|
|
|
132
167
|
### Execution
|
|
133
|
-
-
|
|
134
|
-
-
|
|
135
|
-
-
|
|
136
|
-
-
|
|
168
|
+
- [ Navigate To Project ]
|
|
169
|
+
- ./configure && make deps
|
|
170
|
+
- make test
|
|
171
|
+
- make build
|
|
172
|
+
- make package
|
|
173
|
+
- ./scripts/deploy.sh
|
|
137
174
|
```
|
|
138
175
|
|
|
139
|
-
|
|
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
|
|
203
|
+
|
|
204
|
+
### Execution
|
|
205
|
+
- cd {project.PRODUCT.path}
|
|
206
|
+
```
|
|
207
|
+
|
|
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
|
+
|
|
140
214
|
```
|
|
141
|
-
$ pls build
|
|
142
|
-
|
|
143
|
-
|
|
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
|
|
144
222
|
```
|
|
145
|
-
The `{ENV}` placeholder gets replaced with the variant you specify.
|
|
146
|
-
Instead of remembering the exact commands and paths for each environment, just
|
|
147
|
-
tell `pls` what you want in plain English. The Execution section ensures the right commands run every time.
|
|
148
223
|
|
|
149
|
-
|
|
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:
|
|
150
226
|
|
|
151
|
-
|
|
152
|
-
|
|
227
|
+
```
|
|
228
|
+
$ pls just recompile experimental
|
|
153
229
|
|
|
230
|
+
- Navigate to the Beta directory
|
|
231
|
+
- Compile source code
|
|
232
|
+
- Package build artifacts
|
|
154
233
|
```
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
234
|
+
|
|
235
|
+
Now "experimental" resolves to `project.beta.path`. And when you're ready to ship:
|
|
236
|
+
|
|
158
237
|
```
|
|
238
|
+
$ pls build and deploy main
|
|
159
239
|
|
|
160
|
-
|
|
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
|
+
```
|
|
161
247
|
|
|
162
|
-
|
|
163
|
-
|
|
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.
|
|
164
252
|
|
|
165
253
|
## Development
|
|
166
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
|
}
|