prompt-language-shell 0.9.8 → 1.0.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 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 build project
37
+ $ pls convert video.mp4
41
38
 
42
39
  Here's my plan.
43
40
 
44
- - Navigate to project directory
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 install deps, run tests and build
47
+ $ pls backup photos, compress and upload
52
48
 
53
49
  Here's what I'll do.
54
50
 
55
- - Install dependencies
56
- - Run tests
57
- - Build the project
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:
@@ -129,6 +125,46 @@ Skills let you teach `pls` about your project-specific workflows. Create
129
125
  markdown files in `~/.pls/skills/` to define custom operations that
130
126
  `pls` can understand and execute.
131
127
 
128
+ ### Creating Skills
129
+
130
+ The easiest way to create a new skill is with the guided walkthrough:
131
+
132
+ ```
133
+ $ pls learn
134
+
135
+ Creating a new skill...
136
+
137
+ Skill name (e.g., "Deploy Project"):
138
+ > Build Frontend
139
+
140
+ Description (min 20 characters):
141
+ > Build the frontend application using npm
142
+
143
+ Step 1 - What does this step do?
144
+ > Install dependencies
145
+
146
+ How should this step be executed?
147
+ > shell command
148
+
149
+ Enter the shell command:
150
+ > npm install
151
+
152
+ Add another step?
153
+ > yes
154
+
155
+ ...
156
+ ```
157
+
158
+ The walkthrough guides you through defining:
159
+ - **Name**: A unique name for the skill
160
+ - **Description**: What the skill does (min 20 characters)
161
+ - **Aliases**: Example commands that invoke the skill (optional)
162
+ - **Config**: Configuration properties needed (optional)
163
+ - **Steps**: Each step with either a shell command or reference to another skill
164
+
165
+ Skills are saved to `~/.pls/skills/` as markdown files. File names use
166
+ kebab-case (e.g., "Build Frontend" becomes `build-frontend.md`).
167
+
132
168
  For complete documentation, see [docs/SKILLS.md](./docs/SKILLS.md).
133
169
 
134
170
  ### Structure
@@ -138,60 +174,121 @@ Each skill file uses a simple markdown format:
138
174
  - **Name**: What you call this workflow (e.g., "Build Project")
139
175
  - **Description**: What it does and any variants or options
140
176
  - **Steps**: What needs to happen, in order
141
- - **Execution** (optional): The actual shell commands to run
177
+ - **Execution**: The actual shell commands to run
142
178
 
143
179
  ### Example
144
180
 
145
- Here's a skill that builds different project variants:
181
+ Here's a skill for building a product from source:
146
182
 
147
183
  ```markdown
148
184
  ### Name
149
- Build Project
185
+ Build Product
150
186
 
151
187
  ### Description
152
- Build a project in different configurations:
153
- - dev (debug build with source maps)
154
- - prod (optimized build)
155
- - test (with test coverage)
188
+ Build a product from source. Handles the full compilation pipeline.
189
+
190
+ The company maintains two product lines:
191
+ - Stable: the flagship product
192
+ - Beta: the experimental product
193
+
194
+ If the user says "just compile" or "recompile", dependency installation and
195
+ tests MUST be skipped. Tests MUST also be skipped if the user says "without
196
+ tests". Deployment MUST only run if the user explicitly asks. Compile and
197
+ package steps are MANDATORY.
156
198
 
157
199
  ### Steps
158
200
  - Navigate to the project directory
159
- - Install dependencies if needed
160
- - Run the {ENV} build script
161
- - Generate build artifacts
201
+ - Install build dependencies
202
+ - Run the test suite
203
+ - Compile source code
204
+ - Package build artifacts
205
+ - Deploy to server
162
206
 
163
207
  ### Execution
164
- - cd ~/projects/next
165
- - npm install
166
- - npm run build:{ENV}
167
- - cp -r dist/ builds/{ENV}/
208
+ - [ Navigate To Project ]
209
+ - ./configure && make deps
210
+ - make test
211
+ - make build
212
+ - make package
213
+ - ./scripts/deploy.sh
168
214
  ```
169
215
 
170
- With this skill defined, you can use natural language like:
216
+ The `[ Navigate To Project ]` reference invokes another skill by name. When `pls`
217
+ plans this workflow, it expands the reference inline, inserting that skill's
218
+ execution steps at this position. This lets you compose complex workflows from
219
+ simpler, reusable skills. Here's what that skill might look like:
220
+
221
+ ```markdown
222
+ ### Name
223
+ Navigate To Project
224
+
225
+ ### Description
226
+ The company maintains two product lines:
227
+ - Stable: the flagship product
228
+ - Beta: the experimental product
229
+
230
+ ### Aliases
231
+ - go to project
232
+ - navigate to repo
233
+
234
+ ### Config
235
+ project:
236
+ stable:
237
+ path: string
238
+ beta:
239
+ path: string
240
+
241
+ ### Steps
242
+ - Navigate to project directory
243
+
244
+ ### Execution
245
+ - cd {project.PRODUCT.path}
171
246
  ```
172
- $ pls build project for production
173
- $ pls build dev environment
174
- $ pls build with testing enabled
247
+
248
+ The `{project.PRODUCT.path}` placeholder uses config values from `~/.plsrc`. The
249
+ PRODUCT is matched from user intent (e.g., "build stable" resolves to
250
+ `project.stable.path`).
251
+
252
+ The Description tells `pls` when to skip optional steps. This lets you say:
253
+
175
254
  ```
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.
255
+ $ pls build stable
179
256
 
180
- ### Keep It Short
257
+ - Navigate to the Stable directory
258
+ - Install build dependencies
259
+ - Run the test suite
260
+ - Compile source code
261
+ - Package build artifacts
262
+ ```
181
263
 
182
- Skills also work with concise commands. Once you've taught `pls` about your
183
- workflow, you can use minimal phrasing:
264
+ Here "stable" matches the PRODUCT, so `pls` looks up `project.stable.path` in your
265
+ config. All steps run except deploy (not requested). When iterating quickly:
266
+
267
+ ```
268
+ $ pls just recompile experimental
184
269
 
270
+ - Navigate to the Beta directory
271
+ - Compile source code
272
+ - Package build artifacts
185
273
  ```
186
- $ pls build prod
187
- $ pls build dev
188
- $ pls build test
274
+
275
+ Now "experimental" resolves to `project.beta.path`. And when you're ready to ship:
276
+
189
277
  ```
278
+ $ pls build and deploy main
190
279
 
191
- ## Roadmap
280
+ - Navigate to the Stable directory
281
+ - Install build dependencies
282
+ - Run the test suite
283
+ - Compile source code
284
+ - Package build artifacts
285
+ - Deploy to server
286
+ ```
192
287
 
193
- - **0.9** - Learn skill, codebase refinement, complex dependency handling
194
- - **1.0** - Production release
288
+ The same skill handles all cases based on your intent, something an alias or
289
+ script can't do. Skills are fully dynamic: you can add new variants, change step
290
+ conditions, or introduce new options anytime by editing the markdown file - no
291
+ code changes required.
195
292
 
196
293
  ## Development
197
294
 
@@ -10,6 +10,7 @@ import { Debug } from './views/Debug.js';
10
10
  import { Execute, ExecuteView, mapStateToViewProps, } from './controllers/Execute.js';
11
11
  import { Feedback } from './views/Feedback.js';
12
12
  import { Introspect, IntrospectView } from './controllers/Introspect.js';
13
+ import { Learn, LearnView } from './controllers/Learn.js';
13
14
  import { Message } from './views/Message.js';
14
15
  import { Refinement, RefinementView } from './controllers/Refinement.js';
15
16
  import { Report } from './views/Report.js';
@@ -51,8 +52,8 @@ export const SimpleComponent = memo(function SimpleComponent({ def, }) {
51
52
  export const ControllerComponent = memo(function ControllerComponent({ def, debug, requestHandlers, lifecycleHandlers, workflowHandlers, }) {
52
53
  switch (def.name) {
53
54
  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 }));
55
+ const { props: { steps, query, service, onFinished, onAborted }, status, } = def;
56
+ return (_jsx(Config, { steps: steps, query: query, service: service, onFinished: onFinished, onAborted: onAborted, requestHandlers: requestHandlers, lifecycleHandlers: lifecycleHandlers, workflowHandlers: workflowHandlers, status: status, debug: debug }));
56
57
  }
57
58
  case ComponentName.Command: {
58
59
  const { props: { command, service, onAborted }, status, } = def;
@@ -86,6 +87,10 @@ export const ControllerComponent = memo(function ControllerComponent({ def, debu
86
87
  const { props: { tasks, service, upcoming, label }, status, } = def;
87
88
  return (_jsx(Execute, { tasks: tasks, service: service, upcoming: upcoming, label: label, requestHandlers: requestHandlers, lifecycleHandlers: lifecycleHandlers, workflowHandlers: workflowHandlers, status: status }));
88
89
  }
90
+ case ComponentName.Learn: {
91
+ const { props: { suggestedName, onFinished, onAborted }, status, } = def;
92
+ return (_jsx(Learn, { suggestedName: suggestedName, onFinished: onFinished, onAborted: onAborted, requestHandlers: requestHandlers, lifecycleHandlers: lifecycleHandlers, workflowHandlers: workflowHandlers, status: status }));
93
+ }
89
94
  default:
90
95
  throw new Error(`Unknown managed component: ${def.name}`);
91
96
  }
@@ -100,7 +105,9 @@ export const ViewComponent = memo(function ViewComponent({ def, }) {
100
105
  return (_jsx(ConfirmView, { status: status, message: message, selectedIndex: state.selectedIndex }));
101
106
  }
102
107
  case ComponentName.Config: {
103
- const { props: { steps }, state, status, } = def;
108
+ const { props: { steps: propSteps = [] }, state, status, } = def;
109
+ // Use resolved steps from state if available (for query-based configs)
110
+ const steps = state.steps ?? propSteps;
104
111
  return _jsx(ConfigView, { steps: steps, state: state, status: status });
105
112
  }
106
113
  case ComponentName.Schedule: {
@@ -135,6 +142,10 @@ export const ViewComponent = memo(function ViewComponent({ def, }) {
135
142
  const { props: { text }, status, } = def;
136
143
  return _jsx(RefinementView, { text: text, status: status });
137
144
  }
145
+ case ComponentName.Learn: {
146
+ const { state, status } = def;
147
+ return (_jsx(LearnView, { state: state, status: status, onInputChange: () => { }, onInputSubmit: () => { } }));
148
+ }
138
149
  default:
139
150
  throw new Error(`Unknown managed component: ${def.name}`);
140
151
  }
@@ -161,6 +172,7 @@ export const TimelineComponent = ({ def, }) => {
161
172
  case ComponentName.Execute:
162
173
  case ComponentName.Answer:
163
174
  case ComponentName.Introspect:
175
+ case ComponentName.Learn:
164
176
  return _jsx(ViewComponent, { def: def });
165
177
  default:
166
178
  throw new Error('Unknown component type');
@@ -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
- let result = await svc.processWithTool(command, 'schedule');
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
- // If we delegated to configure, include both schedule and configure debug
53
- // If not, only include schedule debug (result.debug is same as scheduleDebug)
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 { ComponentStatus } from '../../types/components.js';
4
- import { FeedbackType } from '../../types/types.js';
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
- steps.forEach((stepConfig) => {
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,83 @@ 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) {
70
+ const [inputValue, setInputValue] = useState('');
71
+ const [selectedIndex, setSelectedIndex] = useState(() => {
72
+ if (!initialSteps?.length)
73
+ return 0;
74
+ const first = initialSteps[0];
75
+ return first.type === StepType.Selection ? first.defaultIndex : 0;
76
+ });
77
+ // Resolve query to steps
78
+ useEffect(() => {
79
+ if (!isActive || !query || !service || initialSteps?.length)
80
+ return;
81
+ resolveQueryToSteps(query, service)
82
+ .then((result) => {
83
+ // Add debug components to timeline if present
84
+ if (result.debug.length) {
85
+ workflowHandlers.addToTimeline(...result.debug);
86
+ }
87
+ setSteps(result.steps);
88
+ setResolving(false);
89
+ // Initialize values for resolved steps
90
+ const initial = {};
91
+ result.steps.forEach((stepConfig) => {
92
+ const configKey = stepConfig.path || stepConfig.key;
93
+ switch (stepConfig.type) {
94
+ case StepType.Text:
95
+ if (stepConfig.value !== null) {
96
+ initial[configKey] = stepConfig.value;
97
+ }
98
+ break;
99
+ case StepType.Selection:
100
+ initial[configKey] =
101
+ stepConfig.options[stepConfig.defaultIndex].value;
102
+ break;
103
+ }
104
+ });
105
+ setValues(initial);
106
+ })
107
+ .catch((err) => {
108
+ setResolving(false);
109
+ lifecycleHandlers.completeActive(createFeedback({
110
+ type: FeedbackType.Failed,
111
+ message: err instanceof Error ? err.message : 'Failed to resolve',
112
+ }));
113
+ });
114
+ }, [
115
+ isActive,
116
+ query,
117
+ service,
118
+ initialSteps,
119
+ lifecycleHandlers,
120
+ workflowHandlers,
121
+ ]);
122
+ // Update inputValue and selectedIndex when step changes
123
+ useEffect(() => {
124
+ if (isActive && step < steps.length) {
44
125
  const stepConfig = steps[step];
45
126
  const configKey = stepConfig.path || stepConfig.key;
46
- return values[configKey] || '';
127
+ setInputValue(values[configKey] || '');
128
+ if (stepConfig.type === StepType.Selection) {
129
+ setSelectedIndex(stepConfig.defaultIndex);
130
+ }
47
131
  }
48
- return '';
49
- });
50
- const [selectedIndex, setSelectedIndex] = useState(0);
132
+ }, [step, isActive, steps, values]);
51
133
  const normalizeValue = (value) => {
52
- if (value === null || value === undefined) {
134
+ if (value === null || value === undefined)
53
135
  return '';
54
- }
55
136
  return value.replace(/\n/g, '').trim();
56
137
  };
57
- // Update inputValue when step changes
58
- useEffect(() => {
59
- if (isActive && step < steps.length) {
60
- const stepConfig = steps[step];
61
- const configKey = stepConfig.path || stepConfig.key;
62
- const value = values[configKey] || '';
63
- setInputValue(value);
64
- }
65
- }, [step, isActive, steps]);
66
138
  useInput((_, key) => {
67
139
  if (!isActive || step >= steps.length)
68
140
  return;
69
141
  const currentStepConfig = steps[step];
70
142
  if (key.escape) {
71
- // Save current value before aborting
72
143
  const configKey = currentStepConfig.path || currentStepConfig.key;
73
144
  let currentValue = '';
74
145
  switch (currentStepConfig.type) {
@@ -78,28 +149,20 @@ export function Config(props) {
78
149
  case StepType.Selection:
79
150
  currentValue = values[configKey] || '';
80
151
  break;
81
- default: {
82
- const _exhaustiveCheck = currentStepConfig;
83
- throw new Error('Unsupported step type');
84
- }
85
152
  }
86
153
  const finalValues = currentValue
87
154
  ? { ...values, [configKey]: currentValue }
88
155
  : values;
89
- // Expose final state
90
- const finalState = {
156
+ requestHandlers.onCompleted({
91
157
  values: finalValues,
92
158
  completedStep: step,
93
159
  selectedIndex,
94
- };
95
- requestHandlers.onCompleted(finalState);
96
- // Abort configuration
160
+ steps,
161
+ });
97
162
  if (onAborted) {
98
- // Let Workflow handler complete and add feedback
99
163
  onAborted('configuration');
100
164
  }
101
165
  else {
102
- // Fallback: complete with abort feedback directly
103
166
  lifecycleHandlers.completeActive(createFeedback({
104
167
  type: FeedbackType.Aborted,
105
168
  message: 'Configuration cancelled.',
@@ -107,7 +170,6 @@ export function Config(props) {
107
170
  }
108
171
  return;
109
172
  }
110
- // Handle selection step navigation
111
173
  if (currentStepConfig.type === StepType.Selection) {
112
174
  if (key.tab) {
113
175
  setSelectedIndex((prev) => (prev + 1) % currentStepConfig.options.length);
@@ -122,13 +184,10 @@ export function Config(props) {
122
184
  let finalValue = '';
123
185
  switch (currentStepConfig.type) {
124
186
  case StepType.Selection:
125
- // For selection, value is already validated by options
126
187
  finalValue = value;
127
188
  break;
128
189
  case StepType.Text: {
129
- // For text input
130
190
  const normalizedInput = normalizeValue(value);
131
- // Try user input first, then fall back to default
132
191
  if (normalizedInput && currentStepConfig.validate(normalizedInput)) {
133
192
  finalValue = normalizedInput;
134
193
  }
@@ -138,63 +197,41 @@ export function Config(props) {
138
197
  }
139
198
  break;
140
199
  }
141
- default: {
142
- const _exhaustiveCheck = currentStepConfig;
143
- throw new Error('Unsupported step type');
144
- }
145
200
  }
146
- // Don't allow empty or invalid value
147
- if (!finalValue) {
201
+ if (!finalValue)
148
202
  return;
149
- }
150
- // Use full path if available, otherwise use key
151
203
  const configKey = currentStepConfig.path || currentStepConfig.key;
152
204
  const newValues = { ...values, [configKey]: finalValue };
153
205
  setValues(newValues);
154
206
  setInputValue('');
155
207
  if (step === steps.length - 1) {
156
- // Last step completed
157
- // Expose final state
158
- const finalState = {
208
+ requestHandlers.onCompleted({
159
209
  values: newValues,
160
210
  completedStep: steps.length,
161
211
  selectedIndex,
162
- };
163
- requestHandlers.onCompleted(finalState);
164
- // Call onFinished callback and handle result
212
+ steps,
213
+ });
165
214
  try {
166
- if (onFinished) {
167
- onFinished(newValues);
168
- }
169
- // Success - complete with success feedback
215
+ onFinished?.(newValues);
170
216
  lifecycleHandlers.completeActive(createFeedback({
171
217
  type: FeedbackType.Succeeded,
172
218
  message: 'Configuration saved successfully.',
173
219
  }));
174
220
  }
175
221
  catch (error) {
176
- // Failure - complete with error feedback
177
- const errorMessage = error instanceof Error ? error.message : 'Configuration failed';
178
- lifecycleHandlers.completeActive(createFeedback({ type: FeedbackType.Failed, message: errorMessage }));
222
+ lifecycleHandlers.completeActive(createFeedback({
223
+ type: FeedbackType.Failed,
224
+ message: error instanceof Error ? error.message : 'Configuration failed',
225
+ }));
179
226
  }
180
227
  setStep(steps.length);
181
228
  }
182
229
  else {
183
- const nextStep = step + 1;
184
- setStep(nextStep);
185
- // Reset selectedIndex for next step
186
- if (nextStep < steps.length &&
187
- steps[nextStep].type === StepType.Selection) {
188
- setSelectedIndex(steps[nextStep].defaultIndex);
189
- }
230
+ setStep(step + 1);
190
231
  }
191
232
  };
192
- // Build current state for View
193
- // Controller always renders View, passing current state and callbacks
194
- const state = {
195
- values,
196
- completedStep: step,
197
- selectedIndex,
198
- };
199
- return (_jsx(ConfigView, { steps: steps, state: state, status: status, debug: debug, onInputChange: setInputValue, onInputSubmit: handleSubmit }));
233
+ if (resolving) {
234
+ return (_jsxs(Box, { marginLeft: 1, children: [_jsx(Text, { children: "Resolving configuration... " }), _jsx(Spinner, {})] }));
235
+ }
236
+ return (_jsx(ConfigView, { steps: steps, state: { values, completedStep: step, selectedIndex }, status: status, debug: debug, onInputChange: setInputValue, onInputSubmit: handleSubmit }));
200
237
  }