prompt-language-shell 0.8.2 → 0.8.6

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.
Files changed (45) hide show
  1. package/dist/configuration/io.js +85 -0
  2. package/dist/configuration/messages.js +30 -0
  3. package/dist/configuration/schema.js +167 -0
  4. package/dist/configuration/transformation.js +55 -0
  5. package/dist/configuration/types.js +30 -0
  6. package/dist/configuration/validation.js +52 -0
  7. package/dist/execution/handlers.js +135 -0
  8. package/dist/execution/processing.js +35 -0
  9. package/dist/execution/reducer.js +148 -0
  10. package/dist/execution/types.js +12 -0
  11. package/dist/execution/validation.js +12 -0
  12. package/dist/index.js +1 -1
  13. package/dist/services/anthropic.js +43 -56
  14. package/dist/services/colors.js +2 -1
  15. package/dist/services/components.js +40 -24
  16. package/dist/services/config-labels.js +15 -15
  17. package/dist/services/filesystem.js +114 -0
  18. package/dist/services/loader.js +8 -5
  19. package/dist/services/logger.js +26 -1
  20. package/dist/services/messages.js +32 -1
  21. package/dist/services/parser.js +3 -1
  22. package/dist/services/refinement.js +10 -10
  23. package/dist/services/router.js +43 -27
  24. package/dist/services/skills.js +12 -11
  25. package/dist/services/validator.js +4 -3
  26. package/dist/types/guards.js +4 -6
  27. package/dist/types/handlers.js +1 -0
  28. package/dist/types/schemas.js +103 -0
  29. package/dist/types/types.js +1 -0
  30. package/dist/ui/Answer.js +38 -16
  31. package/dist/ui/Command.js +48 -22
  32. package/dist/ui/Component.js +147 -33
  33. package/dist/ui/Config.js +69 -78
  34. package/dist/ui/Confirm.js +34 -21
  35. package/dist/ui/Execute.js +151 -178
  36. package/dist/ui/Feedback.js +1 -0
  37. package/dist/ui/Introspect.js +54 -25
  38. package/dist/ui/Label.js +1 -1
  39. package/dist/ui/Main.js +10 -6
  40. package/dist/ui/Refinement.js +8 -1
  41. package/dist/ui/Schedule.js +76 -53
  42. package/dist/ui/Validate.js +77 -77
  43. package/dist/ui/Workflow.js +60 -61
  44. package/package.json +3 -2
  45. package/dist/services/configuration.js +0 -409
package/dist/ui/Config.js CHANGED
@@ -6,7 +6,7 @@ import { ComponentStatus } from '../types/components.js';
6
6
  import { FeedbackType } from '../types/types.js';
7
7
  import { Colors } from '../services/colors.js';
8
8
  import { createFeedback } from '../services/components.js';
9
- import { DebugLevel } from '../services/configuration.js';
9
+ import { DebugLevel } from '../configuration/types.js';
10
10
  import { useInput } from '../services/keyboard.js';
11
11
  /**
12
12
  * Get postfix with debug brackets if debug is enabled
@@ -78,15 +78,52 @@ function SelectionStep({ options, selectedIndex, isCurrentStep, }) {
78
78
  return (_jsx(Box, { marginRight: 2, children: _jsx(Text, { dimColor: !isSelected || !isCurrentStep, bold: isSelected, children: option.label }) }, option.value));
79
79
  }) }));
80
80
  }
81
- export function Config({ steps, state, status, debug = DebugLevel.None, handlers, onFinished, onAborted, }) {
81
+ export const ConfigView = ({ steps, state, status, debug = DebugLevel.None, onInputChange, onInputSubmit, }) => {
82
82
  const isActive = status === ComponentStatus.Active;
83
- const [step, setStep] = useState(!isActive ? (state?.completedStep ?? steps.length) : 0);
84
- const [values, setValues] = useState(() => {
85
- // If not active and we have saved state values, use those
86
- if (!isActive && state?.values) {
87
- return state.values;
83
+ const { values, completedStep, selectedIndex } = state;
84
+ const renderStepInput = (stepConfig, isCurrentStep) => {
85
+ const configKey = stepConfig.path || stepConfig.key;
86
+ const displayValue = values[configKey];
87
+ switch (stepConfig.type) {
88
+ case StepType.Text:
89
+ if (isCurrentStep && onInputChange && onInputSubmit) {
90
+ return (_jsx(TextStep, { value: values[configKey] || '', placeholder: stepConfig.value || undefined, validate: stepConfig.validate, onChange: onInputChange, onSubmit: onInputSubmit }));
91
+ }
92
+ return (_jsx(Text, { dimColor: true, wrap: "truncate-end", children: displayValue || '' }));
93
+ case StepType.Selection: {
94
+ if (!isCurrentStep) {
95
+ const option = stepConfig.options.find((opt) => opt.value === displayValue);
96
+ return _jsx(Text, { dimColor: true, children: option?.label || '' });
97
+ }
98
+ return (_jsx(SelectionStep, { options: stepConfig.options, selectedIndex: selectedIndex, isCurrentStep: true }));
99
+ }
100
+ default: {
101
+ const _exhaustiveCheck = stepConfig;
102
+ throw new Error('Unsupported step type');
103
+ }
88
104
  }
89
- // Otherwise initialize from step defaults
105
+ };
106
+ return (_jsx(Box, { flexDirection: "column", marginLeft: 1, children: steps.map((stepConfig, index) => {
107
+ const isCurrentStep = index === completedStep && isActive;
108
+ const isCompleted = index < completedStep;
109
+ const wasAborted = index === completedStep && !isActive;
110
+ const shouldShow = isCompleted || isCurrentStep || wasAborted;
111
+ if (!shouldShow) {
112
+ return null;
113
+ }
114
+ const postfix = getPostfix(stepConfig.path, debug);
115
+ return (_jsxs(Box, { flexDirection: "column", marginTop: index === 0 ? 0 : 1, children: [_jsxs(Box, { children: [_jsx(Text, { children: stepConfig.description }), _jsx(Text, { children: ": " }), postfix && _jsx(Text, { color: Colors.Type.Config, children: postfix })] }), _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));
116
+ }) }));
117
+ };
118
+ /**
119
+ * Config controller: Multi-step wizard logic
120
+ */
121
+ export function Config(props) {
122
+ const { steps, status, debug = DebugLevel.None, requestHandlers, lifecycleHandlers, onFinished, onAborted, } = props;
123
+ const isActive = status === ComponentStatus.Active;
124
+ const [step, setStep] = useState(0);
125
+ const [values, setValues] = useState(() => {
126
+ // Initialize from step defaults
90
127
  const initial = {};
91
128
  steps.forEach((stepConfig) => {
92
129
  // Use full path if available, otherwise use key
@@ -111,26 +148,14 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
111
148
  });
112
149
  const [inputValue, setInputValue] = useState(() => {
113
150
  // Initialize with the current step's value if available
114
- if (isActive && step < steps.length) {
151
+ if (step < steps.length) {
115
152
  const stepConfig = steps[step];
116
153
  const configKey = stepConfig.path || stepConfig.key;
117
154
  return values[configKey] || '';
118
155
  }
119
156
  return '';
120
157
  });
121
- const [selectedIndex, setSelectedIndex] = useState(() => {
122
- // If not active, use saved state
123
- if (!isActive && state?.selectedIndex !== undefined) {
124
- return state.selectedIndex;
125
- }
126
- // Initialize selectedIndex based on current step's defaultIndex
127
- if (isActive &&
128
- step < steps.length &&
129
- steps[step].type === StepType.Selection) {
130
- return steps[step].defaultIndex;
131
- }
132
- return 0;
133
- });
158
+ const [selectedIndex, setSelectedIndex] = useState(0);
134
159
  const normalizeValue = (value) => {
135
160
  if (value === null || value === undefined) {
136
161
  return '';
@@ -166,20 +191,21 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
166
191
  throw new Error('Unsupported step type');
167
192
  }
168
193
  }
169
- if (currentValue) {
170
- setValues({ ...values, [configKey]: currentValue });
171
- }
172
- // Save state before aborting
173
- handlers?.updateState({
174
- values,
194
+ const finalValues = currentValue
195
+ ? { ...values, [configKey]: currentValue }
196
+ : values;
197
+ // Expose final state
198
+ const finalState = {
199
+ values: finalValues,
175
200
  completedStep: step,
176
201
  selectedIndex,
177
- });
202
+ };
203
+ requestHandlers.onCompleted(finalState);
178
204
  if (onAborted) {
179
205
  onAborted('configuration');
180
206
  }
181
207
  // Complete with abort feedback
182
- handlers?.completeActive(createFeedback(FeedbackType.Aborted, 'Configuration cancelled.'));
208
+ lifecycleHandlers.completeActive(createFeedback(FeedbackType.Aborted, 'Configuration cancelled.'));
183
209
  return;
184
210
  }
185
211
  // Handle selection step navigation
@@ -191,7 +217,7 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
191
217
  handleSubmit(currentStepConfig.options[selectedIndex].value);
192
218
  }
193
219
  }
194
- });
220
+ }, { isActive });
195
221
  const handleSubmit = (value) => {
196
222
  const currentStepConfig = steps[step];
197
223
  let finalValue = '';
@@ -229,37 +255,29 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
229
255
  setInputValue('');
230
256
  if (step === steps.length - 1) {
231
257
  // Last step completed
232
- // IMPORTANT: Update state BEFORE calling onFinished
233
- // onFinished may call handlers.completeActive(), so state must be saved first
234
- const stateUpdate = {
258
+ // Expose final state
259
+ const finalState = {
235
260
  values: newValues,
236
261
  completedStep: steps.length,
237
262
  selectedIndex,
238
263
  };
239
- handlers?.updateState(stateUpdate);
264
+ requestHandlers.onCompleted(finalState);
240
265
  // Call onFinished callback and handle result
241
266
  try {
242
267
  if (onFinished) {
243
268
  onFinished(newValues);
244
269
  }
245
270
  // Success - complete with success feedback
246
- handlers?.completeActive(createFeedback(FeedbackType.Succeeded, 'Configuration saved successfully.'));
271
+ lifecycleHandlers.completeActive(createFeedback(FeedbackType.Succeeded, 'Configuration saved successfully.'));
247
272
  }
248
273
  catch (error) {
249
274
  // Failure - complete with error feedback
250
275
  const errorMessage = error instanceof Error ? error.message : 'Configuration failed';
251
- handlers?.completeActive(createFeedback(FeedbackType.Failed, errorMessage));
276
+ lifecycleHandlers.completeActive(createFeedback(FeedbackType.Failed, errorMessage));
252
277
  }
253
278
  setStep(steps.length);
254
279
  }
255
280
  else {
256
- // Save state after each step
257
- const stateUpdate = {
258
- values: newValues,
259
- completedStep: step + 1,
260
- selectedIndex,
261
- };
262
- handlers?.updateState(stateUpdate);
263
281
  const nextStep = step + 1;
264
282
  setStep(nextStep);
265
283
  // Reset selectedIndex for next step
@@ -269,39 +287,12 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
269
287
  }
270
288
  }
271
289
  };
272
- const renderStepInput = (stepConfig, isCurrentStep) => {
273
- const configKey = stepConfig.path || stepConfig.key;
274
- // Use state values when inactive, local values when active
275
- const displayValue = !isActive && state?.values ? state.values[configKey] : values[configKey];
276
- switch (stepConfig.type) {
277
- case StepType.Text:
278
- if (isCurrentStep) {
279
- return (_jsx(TextStep, { value: inputValue, placeholder: stepConfig.value || undefined, validate: stepConfig.validate, onChange: setInputValue, onSubmit: handleSubmit }));
280
- }
281
- return (_jsx(Text, { dimColor: true, wrap: "truncate-end", children: displayValue || '' }));
282
- case StepType.Selection: {
283
- if (!isCurrentStep) {
284
- // Find the option that matches the saved/current value
285
- const option = stepConfig.options.find((opt) => opt.value === displayValue);
286
- return _jsx(Text, { dimColor: true, children: option?.label || '' });
287
- }
288
- return (_jsx(SelectionStep, { options: stepConfig.options, selectedIndex: selectedIndex, isCurrentStep: true }));
289
- }
290
- default: {
291
- const _exhaustiveCheck = stepConfig;
292
- throw new Error('Unsupported step type');
293
- }
294
- }
290
+ // Build current state for View
291
+ // Controller always renders View, passing current state and callbacks
292
+ const state = {
293
+ values,
294
+ completedStep: step,
295
+ selectedIndex,
295
296
  };
296
- return (_jsx(Box, { flexDirection: "column", marginLeft: 1, children: steps.map((stepConfig, index) => {
297
- const isCurrentStep = index === step && isActive;
298
- const isCompleted = index < step;
299
- const wasAborted = index === step && !isActive;
300
- const shouldShow = isCompleted || isCurrentStep || wasAborted;
301
- if (!shouldShow) {
302
- return null;
303
- }
304
- const postfix = getPostfix(stepConfig.path, debug);
305
- return (_jsxs(Box, { flexDirection: "column", marginTop: index === 0 ? 0 : 1, children: [_jsxs(Box, { children: [_jsx(Text, { children: stepConfig.description }), _jsx(Text, { children: ": " }), postfix && _jsx(Text, { color: Colors.Type.Config, children: postfix })] }), _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));
306
- }) }));
297
+ return (_jsx(ConfigView, { steps: steps, state: state, status: status, debug: debug, onInputChange: setInputValue, onInputSubmit: handleSubmit }));
307
298
  }
@@ -1,31 +1,53 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState } from 'react';
3
3
  import { Box, Text } from 'ink';
4
- import { ComponentStatus } from '../types/components.js';
4
+ import { ComponentStatus, } from '../types/components.js';
5
5
  import { Colors, getTextColor, Palette } from '../services/colors.js';
6
6
  import { useInput } from '../services/keyboard.js';
7
7
  import { UserQuery } from './UserQuery.js';
8
- export function Confirm({ message, state, status, handlers, onConfirmed, onCancelled, }) {
8
+ export const ConfirmView = ({ message, state, status }) => {
9
9
  const isActive = status === ComponentStatus.Active;
10
- const [selectedIndex, setSelectedIndex] = useState(state?.selectedIndex ?? 0); // 0 = Yes, 1 = No
10
+ const { selectedIndex } = state;
11
+ const options = [
12
+ { label: 'yes', value: 'yes', color: Palette.BrightGreen },
13
+ { label: 'no', value: 'no', color: Colors.Status.Error },
14
+ ];
15
+ // Timeline rendering (Done status)
16
+ if (status === ComponentStatus.Done) {
17
+ 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] })] }));
18
+ }
19
+ // Active/Pending rendering
20
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, marginLeft: 1, children: _jsx(Text, { color: getTextColor(isActive), children: message }) }), _jsxs(Box, { marginLeft: 1, children: [_jsx(Text, { color: Colors.Action.Select, children: ">" }), _jsx(Text, { children: " " }), _jsx(Box, { children: options.map((option, index) => {
21
+ const isSelected = index === selectedIndex;
22
+ return (_jsx(Box, { marginRight: 2, children: _jsx(Text, { color: isSelected ? option.color : undefined, dimColor: !isSelected, children: option.label }) }, option.value));
23
+ }) })] })] }));
24
+ };
25
+ /**
26
+ * Confirm controller: Manages yes/no selection
27
+ */
28
+ export function Confirm({ message, status, requestHandlers, onConfirmed, onCancelled, }) {
29
+ const isActive = status === ComponentStatus.Active;
30
+ const [selectedIndex, setSelectedIndex] = useState(0); // 0 = Yes, 1 = No
11
31
  useInput((input, key) => {
12
32
  if (!isActive)
13
33
  return;
14
34
  if (key.escape) {
15
35
  // Escape: highlight "No" and cancel
16
- setSelectedIndex(1);
17
- handlers?.updateState({ selectedIndex: 1 });
36
+ const finalState = { selectedIndex: 1, confirmed: false };
37
+ requestHandlers.onCompleted(finalState);
18
38
  onCancelled();
19
39
  }
20
40
  else if (key.tab) {
21
41
  // Toggle between Yes (0) and No (1)
22
- const newIndex = selectedIndex === 0 ? 1 : 0;
23
- setSelectedIndex(newIndex);
24
- handlers?.updateState({ selectedIndex: newIndex });
42
+ setSelectedIndex((prev) => (prev === 0 ? 1 : 0));
25
43
  }
26
44
  else if (key.return) {
27
45
  // Confirm selection
28
- handlers?.updateState({ selectedIndex, confirmed: true });
46
+ const finalState = {
47
+ selectedIndex,
48
+ confirmed: true,
49
+ };
50
+ requestHandlers.onCompleted(finalState);
29
51
  if (selectedIndex === 0) {
30
52
  onConfirmed();
31
53
  }
@@ -34,16 +56,7 @@ export function Confirm({ message, state, status, handlers, onConfirmed, onCance
34
56
  }
35
57
  }
36
58
  }, { isActive });
37
- const options = [
38
- { label: 'yes', value: 'yes', color: Palette.BrightGreen },
39
- { label: 'no', value: 'no', color: Colors.Status.Error },
40
- ];
41
- if (!isActive) {
42
- // When done, show both the message and user's choice in timeline
43
- 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] })] }));
44
- }
45
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, marginLeft: 1, children: _jsx(Text, { color: getTextColor(isActive), children: message }) }), _jsxs(Box, { marginLeft: 1, children: [_jsx(Text, { color: Colors.Action.Select, children: ">" }), _jsx(Text, { children: " " }), _jsx(Box, { children: options.map((option, index) => {
46
- const isSelected = index === selectedIndex;
47
- return (_jsx(Box, { marginRight: 2, children: _jsx(Text, { color: isSelected ? option.color : undefined, dimColor: !isSelected, children: option.label }) }, option.value));
48
- }) })] })] }));
59
+ // Controller always renders View, passing current state
60
+ const state = { selectedIndex, confirmed: false };
61
+ return _jsx(ConfirmView, { message: message, state: state, status: status });
49
62
  }