prompt-language-shell 0.9.0 → 0.9.4

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 (64) hide show
  1. package/dist/{ui/Main.js → Main.js} +24 -17
  2. package/dist/{ui → components}/Component.js +31 -26
  3. package/dist/{ui → components}/Workflow.js +23 -7
  4. package/dist/{ui → components/controllers}/Answer.js +18 -17
  5. package/dist/{ui → components/controllers}/Command.js +21 -24
  6. package/dist/{ui → components/controllers}/Config.js +17 -119
  7. package/dist/components/controllers/Confirm.js +42 -0
  8. package/dist/components/controllers/Execute.js +288 -0
  9. package/dist/{ui → components/controllers}/Introspect.js +22 -39
  10. package/dist/components/controllers/Refinement.js +18 -0
  11. package/dist/{ui → components/controllers}/Schedule.js +8 -124
  12. package/dist/{ui → components/controllers}/Validate.js +37 -50
  13. package/dist/components/views/Answer.js +28 -0
  14. package/dist/components/views/Command.js +11 -0
  15. package/dist/components/views/Config.js +115 -0
  16. package/dist/components/views/Confirm.js +24 -0
  17. package/dist/components/views/Execute.js +60 -0
  18. package/dist/{ui → components/views}/Feedback.js +3 -3
  19. package/dist/components/views/Introspect.js +17 -0
  20. package/dist/{ui → components/views}/Label.js +3 -3
  21. package/dist/{ui → components/views}/List.js +3 -3
  22. package/dist/{ui → components/views}/Output.js +2 -2
  23. package/dist/components/views/Refinement.js +9 -0
  24. package/dist/{ui → components/views}/Report.js +1 -1
  25. package/dist/components/views/Schedule.js +120 -0
  26. package/dist/{ui → components/views}/Separator.js +1 -1
  27. package/dist/{ui → components/views}/Spinner.js +1 -1
  28. package/dist/{ui → components/views}/Subtask.js +10 -7
  29. package/dist/components/views/Task.js +18 -0
  30. package/dist/components/views/Upcoming.js +30 -0
  31. package/dist/{ui → components/views}/UserQuery.js +1 -1
  32. package/dist/components/views/Validate.js +17 -0
  33. package/dist/{ui → components/views}/Welcome.js +1 -1
  34. package/dist/{services/config-labels.js → configuration/labels.js} +1 -1
  35. package/dist/configuration/schema.js +2 -2
  36. package/dist/configuration/steps.js +171 -0
  37. package/dist/configuration/transformation.js +17 -0
  38. package/dist/execution/handlers.js +20 -60
  39. package/dist/execution/processing.js +3 -1
  40. package/dist/execution/reducer.js +34 -44
  41. package/dist/execution/runner.js +99 -0
  42. package/dist/execution/types.js +4 -4
  43. package/dist/execution/utils.js +23 -1
  44. package/dist/index.js +1 -1
  45. package/dist/services/components.js +109 -394
  46. package/dist/services/logger.js +3 -3
  47. package/dist/services/messages.js +19 -0
  48. package/dist/services/refinement.js +5 -2
  49. package/dist/services/router.js +136 -55
  50. package/dist/services/shell.js +26 -6
  51. package/dist/services/timing.js +1 -0
  52. package/dist/skills/execute.md +40 -14
  53. package/dist/tools/execute.tool.js +0 -4
  54. package/dist/types/schemas.js +0 -1
  55. package/package.json +1 -1
  56. package/dist/parser.js +0 -13
  57. package/dist/services/config-utils.js +0 -20
  58. package/dist/ui/Confirm.js +0 -62
  59. package/dist/ui/Execute.js +0 -294
  60. package/dist/ui/Refinement.js +0 -23
  61. package/dist/ui/Task.js +0 -175
  62. /package/dist/{ui → components/views}/Debug.js +0 -0
  63. /package/dist/{ui → components/views}/Message.js +0 -0
  64. /package/dist/{ui → components/views}/Panel.js +0 -0
@@ -1,16 +1,17 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useEffect, useState } from 'react';
3
- import { DebugLevel } from '../configuration/types.js';
4
- import { FeedbackType } from '../types/types.js';
5
- import { loadConfig, loadDebugSetting, saveConfig, saveDebugSetting, } from '../configuration/io.js';
6
- import { getConfigurationRequiredMessage } from '../configuration/messages.js';
7
- import { getMissingConfigKeys } from '../configuration/schema.js';
8
- import { unflattenConfig } from '../configuration/transformation.js';
9
- import { createAnthropicService } from '../services/anthropic.js';
10
- import { createCommandDefinition, createConfigDefinitionWithKeys, createFeedback, createMessage, createWelcomeDefinition, } from '../services/components.js';
11
- import { registerGlobalShortcut } from '../services/keyboard.js';
12
- import { initializeLogger, setDebugLevel } from '../services/logger.js';
13
- import { Workflow } from './Workflow.js';
3
+ import { DebugLevel } from './configuration/types.js';
4
+ import { FeedbackType } from './types/types.js';
5
+ import { loadConfig, loadDebugSetting, saveConfig, saveDebugSetting, } from './configuration/io.js';
6
+ import { getConfigurationRequiredMessage } from './configuration/messages.js';
7
+ import { getMissingConfigKeys } from './configuration/schema.js';
8
+ import { createConfigStepsFromSchema } from './configuration/steps.js';
9
+ import { unflattenConfig } from './configuration/transformation.js';
10
+ import { createAnthropicService } from './services/anthropic.js';
11
+ import { createCommand, createConfig, createFeedback, createMessage, createWelcome, } from './services/components.js';
12
+ import { registerGlobalShortcut } from './services/keyboard.js';
13
+ import { initializeLogger, setDebugLevel } from './services/logger.js';
14
+ import { Workflow } from './components/Workflow.js';
14
15
  export const Main = ({ app, command, serviceFactory = createAnthropicService, }) => {
15
16
  const [service, setService] = useState(null);
16
17
  const [initialQueue, setInitialQueue] = useState(null);
@@ -56,7 +57,9 @@ export const Main = ({ app, command, serviceFactory = createAnthropicService, })
56
57
  const errorMessage = error instanceof Error
57
58
  ? error.message
58
59
  : 'Failed to initialize service';
59
- setInitialQueue([createFeedback(FeedbackType.Failed, errorMessage)]);
60
+ setInitialQueue([
61
+ createFeedback({ type: FeedbackType.Failed, message: errorMessage }),
62
+ ]);
60
63
  }
61
64
  }
62
65
  // If config is missing, service will be created after config completes
@@ -94,18 +97,22 @@ export const Main = ({ app, command, serviceFactory = createAnthropicService, })
94
97
  // Config was cancelled
95
98
  };
96
99
  setInitialQueue([
97
- createWelcomeDefinition(app),
98
- createMessage(getConfigurationRequiredMessage()),
99
- createConfigDefinitionWithKeys(missingKeys, handleConfigFinished, handleConfigAborted),
100
+ createWelcome({ app }),
101
+ createMessage({ text: getConfigurationRequiredMessage() }),
102
+ createConfig({
103
+ steps: createConfigStepsFromSchema(missingKeys),
104
+ onFinished: handleConfigFinished,
105
+ onAborted: handleConfigAborted,
106
+ }),
100
107
  ]);
101
108
  }
102
109
  else if (service && command) {
103
110
  // Valid service exists and command provided - execute command
104
- setInitialQueue([createCommandDefinition(command, service)]);
111
+ setInitialQueue([createCommand({ command, service })]);
105
112
  }
106
113
  else if (service && !command) {
107
114
  // Valid service exists, no command - show welcome
108
- setInitialQueue([createWelcomeDefinition(app)]);
115
+ setInitialQueue([createWelcome({ app })]);
109
116
  }
110
117
  // Wait for service to be initialized before setting queue
111
118
  }, [app, command, service, initialQueue]);
@@ -1,20 +1,21 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { memo } from 'react';
3
+ import { ComponentStatus, } from '../types/components.js';
3
4
  import { ComponentName } from '../types/types.js';
4
- import { Answer, AnswerView } from './Answer.js';
5
- import { Command, CommandView } from './Command.js';
6
- import { Config, ConfigView } from './Config.js';
7
- import { Confirm, ConfirmView } from './Confirm.js';
8
- import { Debug } from './Debug.js';
9
- import { Execute, ExecuteView } from './Execute.js';
10
- import { Feedback } from './Feedback.js';
11
- import { Introspect, IntrospectView } from './Introspect.js';
12
- import { Message } from './Message.js';
13
- import { Refinement, RefinementView } from './Refinement.js';
14
- import { Report } from './Report.js';
15
- import { Schedule, ScheduleView } from './Schedule.js';
16
- import { Validate, ValidateView } from './Validate.js';
17
- import { Welcome } from './Welcome.js';
5
+ import { Answer, AnswerView } from './controllers/Answer.js';
6
+ import { Command, CommandView } from './controllers/Command.js';
7
+ import { Config, ConfigView } from './controllers/Config.js';
8
+ import { Confirm, ConfirmView } from './controllers/Confirm.js';
9
+ import { Debug } from './views/Debug.js';
10
+ import { Execute, ExecuteView, mapStateToViewProps, } from './controllers/Execute.js';
11
+ import { Feedback } from './views/Feedback.js';
12
+ import { Introspect, IntrospectView } from './controllers/Introspect.js';
13
+ import { Message } from './views/Message.js';
14
+ import { Refinement, RefinementView } from './controllers/Refinement.js';
15
+ import { Report } from './views/Report.js';
16
+ import { Schedule, ScheduleView } from './controllers/Schedule.js';
17
+ import { Validate, ValidateView } from './controllers/Validate.js';
18
+ import { Welcome } from './views/Welcome.js';
18
19
  /**
19
20
  * Render a simple component (no lifecycle management)
20
21
  */
@@ -74,16 +75,16 @@ export const ControllerComponent = memo(function ControllerComponent({ def, debu
74
75
  return (_jsx(Introspect, { tasks: tasks, service: service, children: children, requestHandlers: requestHandlers, lifecycleHandlers: lifecycleHandlers, workflowHandlers: workflowHandlers, status: status, debug: debug }));
75
76
  }
76
77
  case ComponentName.Answer: {
77
- const { props: { question, service }, status, } = def;
78
- return (_jsx(Answer, { question: question, service: service, requestHandlers: requestHandlers, lifecycleHandlers: lifecycleHandlers, workflowHandlers: workflowHandlers, status: status }));
78
+ const { props: { question, service, upcoming }, status, } = def;
79
+ return (_jsx(Answer, { question: question, service: service, upcoming: upcoming, requestHandlers: requestHandlers, lifecycleHandlers: lifecycleHandlers, workflowHandlers: workflowHandlers, status: status }));
79
80
  }
80
81
  case ComponentName.Validate: {
81
82
  const { props: { missingConfig, userRequest, service, onError, onValidationComplete, onAborted, }, status, } = def;
82
83
  return (_jsx(Validate, { missingConfig: missingConfig, userRequest: userRequest, service: service, onError: onError, onValidationComplete: onValidationComplete, onAborted: onAborted, requestHandlers: requestHandlers, lifecycleHandlers: lifecycleHandlers, workflowHandlers: workflowHandlers, status: status }));
83
84
  }
84
85
  case ComponentName.Execute: {
85
- const { props: { tasks, service }, status, } = def;
86
- return (_jsx(Execute, { tasks: tasks, service: service, requestHandlers: requestHandlers, lifecycleHandlers: lifecycleHandlers, workflowHandlers: workflowHandlers, status: status }));
86
+ const { props: { tasks, service, upcoming, label }, status, } = def;
87
+ return (_jsx(Execute, { tasks: tasks, service: service, upcoming: upcoming, label: label, requestHandlers: requestHandlers, lifecycleHandlers: lifecycleHandlers, workflowHandlers: workflowHandlers, status: status }));
87
88
  }
88
89
  default:
89
90
  throw new Error(`Unknown managed component: ${def.name}`);
@@ -96,7 +97,7 @@ export const ViewComponent = memo(function ViewComponent({ def, }) {
96
97
  switch (def.name) {
97
98
  case ComponentName.Confirm: {
98
99
  const { props: { message }, state, status, } = def;
99
- return _jsx(ConfirmView, { message: message, state: state, status: status });
100
+ return (_jsx(ConfirmView, { status: status, message: message, selectedIndex: state.selectedIndex }));
100
101
  }
101
102
  case ComponentName.Config: {
102
103
  const { props: { steps }, state, status, } = def;
@@ -104,15 +105,18 @@ export const ViewComponent = memo(function ViewComponent({ def, }) {
104
105
  }
105
106
  case ComponentName.Schedule: {
106
107
  const { props: { message, tasks }, state, status, } = def;
107
- return (_jsx(ScheduleView, { message: message, tasks: tasks, state: state, status: status }));
108
+ return (_jsx(ScheduleView, { status: status, message: message, tasks: tasks, highlightedIndex: state.highlightedIndex, currentDefineGroupIndex: state.currentDefineGroupIndex, completedSelections: state.completedSelections }));
108
109
  }
109
110
  case ComponentName.Execute: {
110
- const { state, status } = def;
111
- return _jsx(ExecuteView, { state: state, status: status });
111
+ const { props: { upcoming, label }, state, status, } = def;
112
+ const isActive = status === ComponentStatus.Active;
113
+ const viewProps = mapStateToViewProps(state, isActive, upcoming, label);
114
+ return _jsx(ExecuteView, { ...viewProps });
112
115
  }
113
116
  case ComponentName.Answer: {
114
- const { props: { question }, state, status, } = def;
115
- return _jsx(AnswerView, { question: question, state: state, status: status });
117
+ const { props: { question, upcoming }, state, status, } = def;
118
+ const lines = state.answer ? state.answer.split('\n') : null;
119
+ return (_jsx(AnswerView, { status: status, question: question, lines: lines, error: state.error, upcoming: upcoming, cancelled: state.cancelled }));
116
120
  }
117
121
  case ComponentName.Command: {
118
122
  const { props: { command }, state, status, } = def;
@@ -120,11 +124,12 @@ export const ViewComponent = memo(function ViewComponent({ def, }) {
120
124
  }
121
125
  case ComponentName.Introspect: {
122
126
  const { props: { children }, state, status, } = def;
123
- return (_jsx(IntrospectView, { state: state, status: status, children: children }));
127
+ const hasCapabilities = state.capabilities.length > 0;
128
+ return (_jsx(IntrospectView, { status: status, hasCapabilities: hasCapabilities, error: state.error, children: children }));
124
129
  }
125
130
  case ComponentName.Validate: {
126
131
  const { state, status } = def;
127
- return _jsx(ValidateView, { state: state, status: status });
132
+ return (_jsx(ValidateView, { status: status, completionMessage: state.completionMessage, error: state.error }));
128
133
  }
129
134
  case ComponentName.Refinement: {
130
135
  const { props: { text }, status, } = def;
@@ -3,11 +3,19 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
3
3
  import { Box, Static } from 'ink';
4
4
  import { ComponentStatus, } from '../types/components.js';
5
5
  import { ComponentName, FeedbackType } from '../types/types.js';
6
- import { createFeedback, isSimple, markAsDone, } from '../services/components.js';
6
+ import { createFeedback } from '../services/components.js';
7
7
  import { getWarnings } from '../services/logger.js';
8
8
  import { getCancellationMessage } from '../services/messages.js';
9
9
  import { exitApp } from '../services/process.js';
10
10
  import { SimpleComponent, ControllerComponent, TimelineComponent, } from './Component.js';
11
+ /**
12
+ * Mark a component as done. Returns the component to be added to timeline.
13
+ * Components use handlers.updateState to save their state before completion,
14
+ * so this function sets the status to Done and returns the updated component.
15
+ */
16
+ function markAsDone(component) {
17
+ return { ...component, status: ComponentStatus.Done };
18
+ }
11
19
  export const Workflow = ({ initialQueue, debug }) => {
12
20
  const [timeline, setTimeline] = useState([]);
13
21
  const [current, setCurrent] = useState({ active: null, pending: null });
@@ -39,17 +47,16 @@ export const Workflow = ({ initialQueue, debug }) => {
39
47
  const requestHandlers = useMemo(() => ({
40
48
  onError: (error) => {
41
49
  moveActiveToTimeline();
42
- // Add feedback to queue
43
- setQueue((queue) => [
44
- ...queue,
45
- createFeedback(FeedbackType.Failed, error),
50
+ // Clear queue and add only feedback to prevent subsequent components from executing
51
+ setQueue([
52
+ createFeedback({ type: FeedbackType.Failed, message: error }),
46
53
  ]);
47
54
  },
48
55
  onAborted: (operation) => {
49
56
  moveActiveToTimeline();
50
57
  // Clear queue and add only feedback to prevent subsequent components from executing
51
58
  const message = getCancellationMessage(operation);
52
- setQueue([createFeedback(FeedbackType.Aborted, message)]);
59
+ setQueue([createFeedback({ type: FeedbackType.Aborted, message })]);
53
60
  },
54
61
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
55
62
  onCompleted: (finalState) => {
@@ -144,7 +151,7 @@ export const Workflow = ({ initialQueue, debug }) => {
144
151
  useEffect(() => {
145
152
  const warningMessages = getWarnings();
146
153
  if (warningMessages.length > 0) {
147
- const warningComponents = warningMessages.map((msg) => markAsDone(createFeedback(FeedbackType.Warning, msg)));
154
+ const warningComponents = warningMessages.map((msg) => createFeedback({ type: FeedbackType.Warning, message: msg }, ComponentStatus.Done));
148
155
  setTimeline((prev) => [...prev, ...warningComponents]);
149
156
  }
150
157
  }, [timeline, current]);
@@ -187,3 +194,12 @@ export const Workflow = ({ initialQueue, debug }) => {
187
194
  const pendingComponent = useMemo(() => renderComponent(current.pending, ComponentStatus.Pending), [current.pending, renderComponent]);
188
195
  return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Static, { items: timeline, children: (item) => (_jsx(Box, { marginTop: 1, children: _jsx(TimelineComponent, { def: item }) }, item.id)) }, "timeline"), pendingComponent && _jsx(Box, { marginTop: 1, children: pendingComponent }), activeComponent && _jsx(Box, { marginTop: 1, children: activeComponent })] }));
189
196
  };
197
+ /**
198
+ * Check if a component is stateless (simple).
199
+ * Stateless components are display-only and complete immediately without
200
+ * tracking internal state. Stateful components manage user interaction
201
+ * and maintain state across their lifecycle.
202
+ */
203
+ export function isSimple(component) {
204
+ return !('state' in component);
205
+ }
@@ -1,28 +1,29 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useEffect, useState } from 'react';
3
- import { Box, Text } from 'ink';
4
- import { ComponentStatus, } from '../types/components.js';
5
- import { Colors, getTextColor } from '../services/colors.js';
6
- import { useInput } from '../services/keyboard.js';
7
- import { formatErrorMessage } from '../services/messages.js';
8
- import { withMinimumTime } from '../services/timing.js';
9
- import { Spinner } from './Spinner.js';
3
+ import { ComponentStatus, } from '../../types/components.js';
4
+ import { useInput } from '../../services/keyboard.js';
5
+ import { formatErrorMessage } from '../../services/messages.js';
6
+ import { withMinimumTime } from '../../services/timing.js';
7
+ import { AnswerView } from '../views/Answer.js';
8
+ export { AnswerView } from '../views/Answer.js';
10
9
  const MINIMUM_PROCESSING_TIME = 400;
11
- export const AnswerView = ({ question, state, status }) => {
12
- const isActive = status === ComponentStatus.Active;
13
- const { error, answer } = state;
14
- const lines = answer ? answer.split('\n') : [];
15
- return (_jsxs(Box, { alignSelf: "flex-start", flexDirection: "column", children: [isActive && !answer && !error && (_jsxs(Box, { marginLeft: 1, children: [_jsx(Text, { color: getTextColor(isActive), children: "Finding answer. " }), _jsx(Spinner, {})] })), answer && (_jsxs(_Fragment, { children: [_jsx(Box, { marginLeft: 1, marginBottom: 1, children: _jsx(Text, { color: getTextColor(isActive), children: question }) }), _jsx(Box, { flexDirection: "column", paddingLeft: 3, children: lines.map((line, index) => (_jsx(Text, { color: getTextColor(isActive), children: line }, index))) })] })), error && (_jsx(Box, { marginTop: 1, marginLeft: 1, children: _jsxs(Text, { color: Colors.Status.Error, children: ["Error: ", error] }) }))] }));
16
- };
17
10
  /**
18
11
  * Answer controller: Fetches answer from LLM
19
12
  */
20
- export function Answer({ question, status, service, requestHandlers, lifecycleHandlers, workflowHandlers, }) {
13
+ export function Answer({ question, status, service, upcoming, requestHandlers, lifecycleHandlers, workflowHandlers, }) {
21
14
  const isActive = status === ComponentStatus.Active;
22
15
  const [error, setError] = useState(null);
23
16
  const [answer, setAnswer] = useState(null);
17
+ const [cancelled, setCancelled] = useState(false);
24
18
  useInput((input, key) => {
25
19
  if (key.escape && isActive) {
20
+ setCancelled(true);
21
+ const finalState = {
22
+ answer: null,
23
+ error: null,
24
+ cancelled: true,
25
+ };
26
+ requestHandlers.onCompleted(finalState);
26
27
  requestHandlers.onAborted('answer');
27
28
  }
28
29
  }, { isActive });
@@ -80,6 +81,6 @@ export function Answer({ question, status, service, requestHandlers, lifecycleHa
80
81
  lifecycleHandlers,
81
82
  workflowHandlers,
82
83
  ]);
83
- const state = { error, answer };
84
- return _jsx(AnswerView, { question: question, state: state, status: status });
84
+ const lines = answer ? answer.split('\n') : null;
85
+ return (_jsx(AnswerView, { status: status, question: question, lines: lines, error: error, upcoming: upcoming, cancelled: cancelled }));
85
86
  }
@@ -1,23 +1,16 @@
1
- import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useEffect, useState } from 'react';
3
- import { Box, Text } from 'ink';
4
- import { ComponentStatus, } from '../types/components.js';
5
- import { TaskType } from '../types/types.js';
6
- import { Colors } from '../services/colors.js';
7
- import { createScheduleDefinition } from '../services/components.js';
8
- import { useInput } from '../services/keyboard.js';
9
- import { formatErrorMessage } from '../services/messages.js';
10
- import { handleRefinement } from '../services/refinement.js';
11
- import { routeTasksWithConfirm } from '../services/router.js';
12
- import { ensureMinimumTime } from '../services/timing.js';
13
- import { Spinner } from './Spinner.js';
14
- import { UserQuery } from './UserQuery.js';
3
+ import { ComponentStatus, } from '../../types/components.js';
4
+ import { TaskType } from '../../types/types.js';
5
+ import { createSchedule } from '../../services/components.js';
6
+ import { useInput } from '../../services/keyboard.js';
7
+ import { formatErrorMessage } from '../../services/messages.js';
8
+ import { handleRefinement } from '../../services/refinement.js';
9
+ import { routeTasksWithConfirm } from '../../services/router.js';
10
+ import { ensureMinimumTime } from '../../services/timing.js';
11
+ import { CommandView } from '../views/Command.js';
12
+ export { CommandView } from '../views/Command.js';
15
13
  const MIN_PROCESSING_TIME = 400; // purely for visual effect
16
- export const CommandView = ({ command, state, status }) => {
17
- const isActive = status === ComponentStatus.Active;
18
- const { error } = state;
19
- return (_jsxs(Box, { alignSelf: "flex-start", flexDirection: "column", children: [!isActive ? (_jsxs(UserQuery, { children: ["> pls ", command] })) : (_jsxs(Box, { marginLeft: 1, children: [_jsxs(Text, { color: Colors.Text.Active, children: ["> pls ", command] }), _jsx(Text, { children: " " }), _jsx(Spinner, {})] })), error && (_jsx(Box, { marginTop: 1, marginLeft: 1, children: _jsxs(Text, { color: Colors.Status.Error, children: ["Error: ", error] }) }))] }));
20
- };
21
14
  /**
22
15
  * Command controller: Processes and routes command
23
16
  */
@@ -77,12 +70,16 @@ export function Command({ command, status, service, requestHandlers, lifecycleHa
77
70
  // Check if tasks contain DEFINE type (variant selection needed)
78
71
  const hasDefineTask = result.tasks.some((task) => task.type === TaskType.Define);
79
72
  // Create Schedule definition
80
- const scheduleDefinition = createScheduleDefinition(result.message, result.tasks, hasDefineTask
81
- ? async (selectedTasks) => {
82
- // Refinement flow for DEFINE tasks
83
- await handleRefinement(selectedTasks, svc, command, lifecycleHandlers, workflowHandlers, requestHandlers);
84
- }
85
- : undefined);
73
+ const scheduleDefinition = createSchedule({
74
+ message: result.message,
75
+ tasks: result.tasks,
76
+ onSelectionConfirmed: hasDefineTask
77
+ ? async (selectedTasks) => {
78
+ // Refinement flow for DEFINE tasks
79
+ await handleRefinement(selectedTasks, svc, command, lifecycleHandlers, workflowHandlers, requestHandlers);
80
+ }
81
+ : undefined,
82
+ });
86
83
  if (hasDefineTask) {
87
84
  // DEFINE tasks: Move Command to timeline, add Schedule to queue
88
85
  lifecycleHandlers.completeActive();
@@ -1,120 +1,12 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useEffect, useState } from 'react';
3
- import { Box, Text, useFocus } from 'ink';
4
- import TextInput from 'ink-text-input';
5
- import { ComponentStatus } from '../types/components.js';
6
- import { FeedbackType } from '../types/types.js';
7
- import { Colors } from '../services/colors.js';
8
- import { createFeedback } from '../services/components.js';
9
- import { DebugLevel } from '../configuration/types.js';
10
- import { useInput } from '../services/keyboard.js';
11
- /**
12
- * Get postfix with debug brackets if debug is enabled
13
- * Info: {key} | Verbose: {key} entry
14
- */
15
- function getPostfix(text, debugLevel) {
16
- if (debugLevel === DebugLevel.None || !text) {
17
- return '';
18
- }
19
- if (debugLevel === DebugLevel.Info) {
20
- return `{${text}}`;
21
- }
22
- return `{${text}} entry`;
23
- }
24
- export var StepType;
25
- (function (StepType) {
26
- StepType["Text"] = "text";
27
- StepType["Selection"] = "selection";
28
- })(StepType || (StepType = {}));
29
- function TextStep({ value, placeholder, validate, onChange, onSubmit, }) {
30
- const [inputValue, setInputValue] = useState(value);
31
- const [validationFailed, setValidationFailed] = useState(false);
32
- const { isFocused } = useFocus({ autoFocus: true });
33
- // Sync internal state with prop changes
34
- useEffect(() => {
35
- setInputValue(value);
36
- }, [value]);
37
- const handleChange = (newValue) => {
38
- setInputValue(newValue);
39
- onChange(newValue);
40
- if (validationFailed) {
41
- setValidationFailed(false);
42
- }
43
- };
44
- const handleSubmit = (value) => {
45
- // Use placeholder if input is empty
46
- const finalValue = value || placeholder || '';
47
- if (!validate(finalValue)) {
48
- setValidationFailed(true);
49
- return;
50
- }
51
- onSubmit(finalValue);
52
- };
53
- // Handle input manually when validation fails
54
- useInput((input, key) => {
55
- if (!validationFailed)
56
- return;
57
- if (key.return) {
58
- handleSubmit(inputValue);
59
- }
60
- else if (key.backspace || key.delete) {
61
- const newValue = inputValue.slice(0, -1);
62
- handleChange(newValue);
63
- }
64
- else if (!key.ctrl && !key.meta && input) {
65
- const newValue = inputValue + input;
66
- handleChange(newValue);
67
- }
68
- }, { isActive: validationFailed });
69
- // When validation fails, show colored text
70
- if (validationFailed) {
71
- return (_jsxs(Text, { color: Colors.Status.Error, children: [inputValue || placeholder, isFocused && _jsx(Text, { inverse: true, children: " " })] }));
72
- }
73
- return (_jsx(TextInput, { value: inputValue, onChange: handleChange, onSubmit: handleSubmit, placeholder: placeholder }));
74
- }
75
- function SelectionStep({ options, selectedIndex, isCurrentStep, }) {
76
- return (_jsx(Box, { children: options.map((option, optIndex) => {
77
- const isSelected = optIndex === selectedIndex;
78
- return (_jsx(Box, { marginRight: 2, children: _jsx(Text, { dimColor: !isSelected || !isCurrentStep, bold: isSelected, children: option.label }) }, option.value));
79
- }) }));
80
- }
81
- export const ConfigView = ({ steps, state, status, debug = DebugLevel.None, onInputChange, onInputSubmit, }) => {
82
- const isActive = status === ComponentStatus.Active;
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
- }
104
- }
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
- };
3
+ import { ComponentStatus } from '../../types/components.js';
4
+ import { FeedbackType } from '../../types/types.js';
5
+ import { createFeedback } from '../../services/components.js';
6
+ import { DebugLevel } from '../../configuration/types.js';
7
+ import { useInput } from '../../services/keyboard.js';
8
+ import { ConfigView, StepType } from '../views/Config.js';
9
+ export { ConfigView, StepType, } from '../views/Config.js';
118
10
  /**
119
11
  * Config controller: Multi-step wizard logic
120
12
  */
@@ -208,7 +100,10 @@ export function Config(props) {
208
100
  }
209
101
  else {
210
102
  // Fallback: complete with abort feedback directly
211
- lifecycleHandlers.completeActive(createFeedback(FeedbackType.Aborted, 'Configuration cancelled.'));
103
+ lifecycleHandlers.completeActive(createFeedback({
104
+ type: FeedbackType.Aborted,
105
+ message: 'Configuration cancelled.',
106
+ }));
212
107
  }
213
108
  return;
214
109
  }
@@ -272,12 +167,15 @@ export function Config(props) {
272
167
  onFinished(newValues);
273
168
  }
274
169
  // Success - complete with success feedback
275
- lifecycleHandlers.completeActive(createFeedback(FeedbackType.Succeeded, 'Configuration saved successfully.'));
170
+ lifecycleHandlers.completeActive(createFeedback({
171
+ type: FeedbackType.Succeeded,
172
+ message: 'Configuration saved successfully.',
173
+ }));
276
174
  }
277
175
  catch (error) {
278
176
  // Failure - complete with error feedback
279
177
  const errorMessage = error instanceof Error ? error.message : 'Configuration failed';
280
- lifecycleHandlers.completeActive(createFeedback(FeedbackType.Failed, errorMessage));
178
+ lifecycleHandlers.completeActive(createFeedback({ type: FeedbackType.Failed, message: errorMessage }));
281
179
  }
282
180
  setStep(steps.length);
283
181
  }
@@ -0,0 +1,42 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { ComponentStatus, } from '../../types/components.js';
4
+ import { useInput } from '../../services/keyboard.js';
5
+ import { ConfirmView } from '../views/Confirm.js';
6
+ export { ConfirmView } from '../views/Confirm.js';
7
+ /**
8
+ * Confirm controller: Manages yes/no selection
9
+ */
10
+ export function Confirm({ message, status, requestHandlers, onConfirmed, onCancelled, }) {
11
+ const isActive = status === ComponentStatus.Active;
12
+ const [selectedIndex, setSelectedIndex] = useState(0); // 0 = Yes, 1 = No
13
+ useInput((input, key) => {
14
+ if (!isActive)
15
+ return;
16
+ if (key.escape) {
17
+ // Escape: highlight "No" and cancel
18
+ const finalState = { selectedIndex: 1, confirmed: false };
19
+ requestHandlers.onCompleted(finalState);
20
+ onCancelled();
21
+ }
22
+ else if (key.tab) {
23
+ // Toggle between Yes (0) and No (1)
24
+ setSelectedIndex((prev) => (prev === 0 ? 1 : 0));
25
+ }
26
+ else if (key.return) {
27
+ // Confirm selection
28
+ const finalState = {
29
+ selectedIndex,
30
+ confirmed: true,
31
+ };
32
+ requestHandlers.onCompleted(finalState);
33
+ if (selectedIndex === 0) {
34
+ onConfirmed();
35
+ }
36
+ else {
37
+ onCancelled();
38
+ }
39
+ }
40
+ }, { isActive });
41
+ return (_jsx(ConfirmView, { status: status, message: message, selectedIndex: selectedIndex }));
42
+ }