prompt-language-shell 0.8.4 → 0.8.8

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 (41) 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 +36 -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 +2 -1
  14. package/dist/services/colors.js +22 -12
  15. package/dist/services/components.js +35 -11
  16. package/dist/services/config-labels.js +15 -15
  17. package/dist/services/logger.js +2 -1
  18. package/dist/services/messages.js +53 -1
  19. package/dist/services/refinement.js +11 -6
  20. package/dist/services/router.js +92 -52
  21. package/dist/skills/execute.md +79 -9
  22. package/dist/skills/schedule.md +121 -29
  23. package/dist/tools/execute.tool.js +4 -0
  24. package/dist/types/schemas.js +1 -0
  25. package/dist/ui/Answer.js +36 -15
  26. package/dist/ui/Command.js +43 -23
  27. package/dist/ui/Component.js +147 -33
  28. package/dist/ui/Config.js +73 -79
  29. package/dist/ui/Confirm.js +34 -21
  30. package/dist/ui/Execute.js +129 -329
  31. package/dist/ui/Feedback.js +2 -1
  32. package/dist/ui/Introspect.js +51 -24
  33. package/dist/ui/Label.js +4 -3
  34. package/dist/ui/List.js +3 -2
  35. package/dist/ui/Main.js +5 -1
  36. package/dist/ui/Refinement.js +8 -1
  37. package/dist/ui/Schedule.js +89 -61
  38. package/dist/ui/Validate.js +75 -77
  39. package/dist/ui/Workflow.js +47 -123
  40. package/package.json +1 -1
  41. package/dist/services/configuration.js +0 -409
@@ -1,5 +1,6 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Box, Text } from 'ink';
3
+ import { ComponentStatus } from '../types/components.js';
3
4
  import { FeedbackType } from '../types/types.js';
4
5
  import { getFeedbackColor } from '../services/colors.js';
5
6
  function getSymbol(type) {
@@ -12,7 +13,7 @@ function getSymbol(type) {
12
13
  }[type];
13
14
  }
14
15
  export function Feedback({ type, message }) {
15
- const color = getFeedbackColor(type, false);
16
+ const color = getFeedbackColor(type, ComponentStatus.Done);
16
17
  const symbol = getSymbol(type);
17
18
  return (_jsx(Box, { marginLeft: 1, children: _jsxs(Text, { color: color, children: [symbol, " ", message] }) }));
18
19
  }
@@ -1,22 +1,35 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, 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 } from '../services/colors.js';
6
6
  import { createReportDefinition } from '../services/components.js';
7
- import { DebugLevel } from '../services/configuration.js';
7
+ import { DebugLevel } from '../configuration/types.js';
8
8
  import { useInput } from '../services/keyboard.js';
9
9
  import { formatErrorMessage } from '../services/messages.js';
10
10
  import { ensureMinimumTime } from '../services/timing.js';
11
11
  import { Spinner } from './Spinner.js';
12
12
  const MIN_PROCESSING_TIME = 1000;
13
- export function Introspect({ tasks, state: _state, status, service, children, debug = DebugLevel.None, stateHandlers, lifecycleHandlers, queueHandlers, errorHandlers, workflowHandlers, }) {
13
+ export const IntrospectView = ({ state, status, children, }) => {
14
+ const isActive = status === ComponentStatus.Active;
15
+ const { error } = state;
16
+ // Don't render wrapper when done and nothing to show
17
+ if (!isActive && !error && !children) {
18
+ return null;
19
+ }
20
+ return (_jsxs(Box, { alignSelf: "flex-start", flexDirection: "column", children: [isActive && (_jsxs(Box, { marginLeft: 1, children: [_jsx(Text, { color: getTextColor(isActive), children: "Listing capabilities. " }), _jsx(Spinner, {})] })), error && (_jsx(Box, { marginTop: 1, marginLeft: 1, children: _jsxs(Text, { color: Colors.Status.Error, children: ["Error: ", error] }) })), children] }));
21
+ };
22
+ /**
23
+ * Introspect controller: Lists capabilities via LLM
24
+ */
25
+ export function Introspect({ tasks, status, service, children, debug = DebugLevel.None, requestHandlers, lifecycleHandlers, workflowHandlers, }) {
14
26
  const isActive = status === ComponentStatus.Active;
15
- // isActive passed as prop
16
27
  const [error, setError] = useState(null);
28
+ const [capabilities, setCapabilities] = useState(null);
29
+ const [message, setMessage] = useState(null);
17
30
  useInput((input, key) => {
18
31
  if (key.escape && isActive) {
19
- errorHandlers?.onAborted('introspection');
32
+ requestHandlers.onAborted('introspection');
20
33
  }
21
34
  }, { isActive });
22
35
  useEffect(() => {
@@ -36,25 +49,28 @@ export function Introspect({ tasks, state: _state, status, service, children, de
36
49
  if (mounted) {
37
50
  // Add debug components to timeline if present
38
51
  if (result.debug?.length) {
39
- workflowHandlers?.addToTimeline(...result.debug);
52
+ workflowHandlers.addToTimeline(...result.debug);
40
53
  }
41
54
  // Capabilities come directly from result - no parsing needed
42
- let capabilities = result.capabilities;
55
+ let caps = result.capabilities;
43
56
  // Filter out internal capabilities when not in debug mode
44
57
  if (debug === DebugLevel.None) {
45
- capabilities = capabilities.filter((cap) => cap.name.toUpperCase() !== 'SCHEDULE' &&
58
+ caps = caps.filter((cap) => cap.name.toUpperCase() !== 'SCHEDULE' &&
46
59
  cap.name.toUpperCase() !== 'VALIDATE' &&
47
60
  cap.name.toUpperCase() !== 'REPORT');
48
61
  }
49
- // Save state before completing
50
- stateHandlers?.updateState({
51
- capabilities,
62
+ setCapabilities(caps);
63
+ setMessage(result.message);
64
+ const finalState = {
65
+ error: null,
66
+ capabilities: caps,
52
67
  message: result.message,
53
- });
68
+ };
69
+ requestHandlers.onCompleted(finalState);
54
70
  // Add Report component to queue
55
- queueHandlers?.addToQueue(createReportDefinition(result.message, capabilities));
71
+ workflowHandlers.addToQueue(createReportDefinition(result.message, caps));
56
72
  // Signal completion
57
- lifecycleHandlers?.completeActive();
73
+ lifecycleHandlers.completeActive();
58
74
  }
59
75
  }
60
76
  catch (err) {
@@ -62,11 +78,13 @@ export function Introspect({ tasks, state: _state, status, service, children, de
62
78
  if (mounted) {
63
79
  const errorMessage = formatErrorMessage(err);
64
80
  setError(errorMessage);
65
- // Save error state
66
- stateHandlers?.updateState({
81
+ const finalState = {
67
82
  error: errorMessage,
68
- });
69
- errorHandlers?.onError(errorMessage);
83
+ capabilities: [],
84
+ message: null,
85
+ };
86
+ requestHandlers.onCompleted(finalState);
87
+ requestHandlers.onError(errorMessage);
70
88
  }
71
89
  }
72
90
  }
@@ -74,10 +92,19 @@ export function Introspect({ tasks, state: _state, status, service, children, de
74
92
  return () => {
75
93
  mounted = false;
76
94
  };
77
- }, [tasks, isActive, service, debug]);
78
- // Don't render wrapper when done and nothing to show
79
- if (!isActive && !error && !children) {
80
- return null;
81
- }
82
- return (_jsxs(Box, { alignSelf: "flex-start", flexDirection: "column", children: [isActive && (_jsxs(Box, { marginLeft: 1, children: [_jsx(Text, { color: getTextColor(isActive), children: "Listing capabilities. " }), _jsx(Spinner, {})] })), error && (_jsx(Box, { marginTop: 1, marginLeft: 1, children: _jsxs(Text, { color: Colors.Status.Error, children: ["Error: ", error] }) })), children] }));
95
+ }, [
96
+ tasks,
97
+ isActive,
98
+ service,
99
+ debug,
100
+ requestHandlers,
101
+ lifecycleHandlers,
102
+ workflowHandlers,
103
+ ]);
104
+ const state = {
105
+ error,
106
+ capabilities: capabilities || [],
107
+ message,
108
+ };
109
+ return (_jsx(IntrospectView, { state: state, status: status, children: children }));
83
110
  }
package/dist/ui/Label.js CHANGED
@@ -1,9 +1,10 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text } from 'ink';
3
3
  import { getTaskColors, getTaskTypeLabel } from '../services/colors.js';
4
- import { DebugLevel } from '../services/configuration.js';
4
+ import { DebugLevel } from '../configuration/types.js';
5
5
  import { Separator } from './Separator.js';
6
- export function Label({ description, taskType, showType = false, isCurrent = false, debug = DebugLevel.None, }) {
7
- const colors = getTaskColors(taskType, isCurrent);
6
+ import { ComponentStatus } from '../types/components.js';
7
+ export function Label({ description, taskType, showType = false, status = ComponentStatus.Done, debug = DebugLevel.None, }) {
8
+ const colors = getTaskColors(taskType, status);
8
9
  return (_jsxs(Box, { children: [_jsx(Text, { color: colors.description, children: description }), showType && (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsx(Text, { color: colors.type, children: getTaskTypeLabel(taskType, debug) })] }))] }));
9
10
  }
package/dist/ui/List.js CHANGED
@@ -4,7 +4,8 @@ import { Palette } from '../services/colors.js';
4
4
  import { Separator } from './Separator.js';
5
5
  export const List = ({ items, level = 0, highlightedIndex = null, highlightedParentIndex = null, showType = false, }) => {
6
6
  const marginLeft = level > 0 ? 2 : 0;
7
- return (_jsx(Box, { flexDirection: "column", marginLeft: marginLeft, children: items.map((item, index) => {
7
+ const gap = level === 0 ? 1 : 0;
8
+ return (_jsx(Box, { flexDirection: "column", marginLeft: marginLeft, gap: gap, children: items.map((item, index) => {
8
9
  // At level 0, track which parent is active for child highlighting
9
10
  // At level > 0, only highlight if this parent is the active one
10
11
  const shouldHighlightChildren = level === 0 ? highlightedParentIndex === index : false;
@@ -23,6 +24,6 @@ export const List = ({ items, level = 0, highlightedIndex = null, highlightedPar
23
24
  (isHighlighted && item.type.highlightedColor
24
25
  ? item.type.highlightedColor
25
26
  : Palette.White);
26
- return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: markerColor, children: marker }), _jsx(Text, { color: descriptionColor, children: item.description.text }), showType && (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsx(Text, { color: typeColor, children: item.type.text })] }))] }), item.children && item.children.length > 0 && (_jsx(List, { items: item.children, level: level + 1, highlightedIndex: shouldHighlightChildren ? highlightedIndex : null, showType: showType }))] }, index));
27
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: markerColor, children: marker }), _jsx(Text, { color: descriptionColor, children: item.description.text }), showType && (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsx(Text, { color: typeColor, children: item.type.text })] }))] }), item.children && item.children.length > 0 && (_jsx(Box, { marginTop: 1, children: _jsx(List, { items: item.children, level: level + 1, highlightedIndex: shouldHighlightChildren ? highlightedIndex : null, showType: showType }) }))] }, index));
27
28
  }) }));
28
29
  };
package/dist/ui/Main.js CHANGED
@@ -1,9 +1,13 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useEffect, useState } from 'react';
3
+ import { DebugLevel } from '../configuration/types.js';
3
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';
4
9
  import { createAnthropicService } from '../services/anthropic.js';
5
10
  import { createCommandDefinition, createConfigDefinitionWithKeys, createFeedback, createMessage, createWelcomeDefinition, } from '../services/components.js';
6
- import { DebugLevel, getConfigurationRequiredMessage, getMissingConfigKeys, loadConfig, loadDebugSetting, saveConfig, saveDebugSetting, unflattenConfig, } from '../services/configuration.js';
7
11
  import { registerGlobalShortcut } from '../services/keyboard.js';
8
12
  import { initializeLogger, setDebugLevel } from '../services/logger.js';
9
13
  import { Workflow } from './Workflow.js';
@@ -4,6 +4,13 @@ import { ComponentStatus } from '../types/components.js';
4
4
  import { useInput } from '../services/keyboard.js';
5
5
  import { Message } from './Message.js';
6
6
  import { Spinner } from './Spinner.js';
7
+ export const RefinementView = ({ text, status }) => {
8
+ const isActive = status === ComponentStatus.Active;
9
+ return (_jsxs(Box, { gap: 1, children: [_jsx(Message, { text: text, status: status }), isActive && _jsx(Spinner, {})] }));
10
+ };
11
+ /**
12
+ * Refinement controller: Handles abort input
13
+ */
7
14
  export const Refinement = ({ text, status, onAborted }) => {
8
15
  const isActive = status === ComponentStatus.Active;
9
16
  useInput((_, key) => {
@@ -12,5 +19,5 @@ export const Refinement = ({ text, status, onAborted }) => {
12
19
  return;
13
20
  }
14
21
  }, { isActive });
15
- return (_jsxs(Box, { gap: 1, children: [_jsx(Message, { text: text }), isActive && _jsx(Spinner, {})] }));
22
+ return _jsx(RefinementView, { text: text, status: status });
16
23
  };
@@ -1,19 +1,24 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useState } from 'react';
3
3
  import { Box } from 'ink';
4
- import { ComponentStatus } from '../types/components.js';
4
+ import { ComponentStatus, } from '../types/components.js';
5
5
  import { TaskType } from '../types/types.js';
6
- import { getTaskColors, getTaskTypeLabel } from '../services/colors.js';
7
- import { DebugLevel } from '../services/configuration.js';
6
+ import { getTaskColors, getTaskTypeLabel, Palette, } from '../services/colors.js';
7
+ import { DebugLevel } from '../configuration/types.js';
8
8
  import { useInput } from '../services/keyboard.js';
9
9
  import { Label } from './Label.js';
10
10
  import { List } from './List.js';
11
- function taskToListItem(task, highlightedChildIndex = null, isDefineTaskWithoutSelection = false, isCurrent = false, debug = DebugLevel.None) {
12
- const taskColors = getTaskColors(task.type, isCurrent);
11
+ export function taskToListItem(task, highlightedChildIndex = null, isDefineTaskWithoutSelection = false, status = ComponentStatus.Done, debug = DebugLevel.None) {
12
+ const taskColors = getTaskColors(task.type, status);
13
+ // Determine description color based on status
14
+ let descriptionColor = taskColors.description;
15
+ if (status === ComponentStatus.Pending) {
16
+ descriptionColor = Palette.SoftWhite;
17
+ }
13
18
  const item = {
14
19
  description: {
15
20
  text: task.action,
16
- color: taskColors.description,
21
+ color: descriptionColor,
17
22
  },
18
23
  type: { text: getTaskTypeLabel(task.type, debug), color: taskColors.type },
19
24
  children: [],
@@ -21,7 +26,7 @@ function taskToListItem(task, highlightedChildIndex = null, isDefineTaskWithoutS
21
26
  // Mark define tasks with right arrow when no selection has been made
22
27
  if (isDefineTaskWithoutSelection) {
23
28
  item.marker = ' → ';
24
- item.markerColor = getTaskColors(TaskType.Schedule, isCurrent).type;
29
+ item.markerColor = getTaskColors(TaskType.Schedule, status).type;
25
30
  }
26
31
  // Add children for Define tasks with options
27
32
  if (task.type === TaskType.Define && Array.isArray(task.params?.options)) {
@@ -33,8 +38,8 @@ function taskToListItem(task, highlightedChildIndex = null, isDefineTaskWithoutS
33
38
  childType =
34
39
  index === highlightedChildIndex ? TaskType.Execute : TaskType.Discard;
35
40
  }
36
- const colors = getTaskColors(childType, isCurrent);
37
- const planColors = getTaskColors(TaskType.Schedule, isCurrent);
41
+ const colors = getTaskColors(childType, status);
42
+ const planColors = getTaskColors(TaskType.Schedule, status);
38
43
  return {
39
44
  description: {
40
45
  text: option,
@@ -56,11 +61,11 @@ function taskToListItem(task, highlightedChildIndex = null, isDefineTaskWithoutS
56
61
  Array.isArray(scheduledTask.subtasks) &&
57
62
  scheduledTask.subtasks.length > 0) {
58
63
  item.children = scheduledTask.subtasks.map((subtask) => {
59
- const subtaskColors = getTaskColors(subtask.type, isCurrent);
64
+ const subtaskColors = getTaskColors(subtask.type, status);
60
65
  return {
61
66
  description: {
62
67
  text: subtask.action,
63
- color: subtaskColors.description,
68
+ color: Palette.AshGray,
64
69
  },
65
70
  type: {
66
71
  text: getTaskTypeLabel(subtask.type, debug),
@@ -71,12 +76,54 @@ function taskToListItem(task, highlightedChildIndex = null, isDefineTaskWithoutS
71
76
  }
72
77
  return item;
73
78
  }
74
- export function Schedule({ message, tasks, state, status, debug = DebugLevel.None, stateHandlers, lifecycleHandlers, errorHandlers, onSelectionConfirmed, }) {
79
+ export const ScheduleView = ({ message, tasks, state, status, debug = DebugLevel.None, }) => {
75
80
  const isActive = status === ComponentStatus.Active;
76
- // isActive passed as prop
77
- const [highlightedIndex, setHighlightedIndex] = useState(state?.highlightedIndex ?? null);
78
- const [currentDefineGroupIndex, setCurrentDefineGroupIndex] = useState(state?.currentDefineGroupIndex ?? 0);
79
- const [completedSelections, setCompletedSelections] = useState(state?.completedSelections ?? []);
81
+ const { highlightedIndex, currentDefineGroupIndex, completedSelections } = state;
82
+ // Find all Define tasks
83
+ const defineTaskIndices = tasks
84
+ .map((t, idx) => (t.type === TaskType.Define ? idx : -1))
85
+ .filter((idx) => idx !== -1);
86
+ // Get the current active define task
87
+ const currentDefineTaskIndex = defineTaskIndices[currentDefineGroupIndex] ?? -1;
88
+ const listItems = tasks.map((task, idx) => {
89
+ // Find which define group this task belongs to (if any)
90
+ const defineGroupIndex = defineTaskIndices.indexOf(idx);
91
+ const isDefineTask = defineGroupIndex !== -1;
92
+ // Determine child selection state
93
+ let childIndex = null;
94
+ if (isDefineTask) {
95
+ if (defineGroupIndex < currentDefineGroupIndex) {
96
+ // Previously completed group - show the selection
97
+ childIndex = completedSelections[defineGroupIndex] ?? null;
98
+ }
99
+ else if (defineGroupIndex === currentDefineGroupIndex) {
100
+ // Current active group - show live navigation unless not active
101
+ if (!isActive) {
102
+ // If not active, show the completed selection for this group too
103
+ childIndex = completedSelections[defineGroupIndex] ?? null;
104
+ }
105
+ else {
106
+ childIndex = null;
107
+ }
108
+ }
109
+ }
110
+ // Show arrow on current active define task when no child is highlighted and is active
111
+ const isDefineWithoutSelection = isDefineTask &&
112
+ defineGroupIndex === currentDefineGroupIndex &&
113
+ highlightedIndex === null &&
114
+ isActive;
115
+ return taskToListItem(task, childIndex, isDefineWithoutSelection, status, debug);
116
+ });
117
+ return (_jsxs(Box, { flexDirection: "column", children: [message && (_jsx(Box, { marginBottom: 1, marginLeft: 1, children: _jsx(Label, { description: message, taskType: TaskType.Schedule, showType: debug !== DebugLevel.None, status: status, debug: debug }) })), _jsx(Box, { marginLeft: 1, children: _jsx(List, { items: listItems, highlightedIndex: currentDefineTaskIndex >= 0 ? highlightedIndex : null, highlightedParentIndex: currentDefineTaskIndex, showType: debug !== DebugLevel.None }) })] }));
118
+ };
119
+ /**
120
+ * Schedule controller: Manages task selection and navigation
121
+ */
122
+ export function Schedule({ message, tasks, status, debug = DebugLevel.None, requestHandlers, lifecycleHandlers, onSelectionConfirmed, }) {
123
+ const isActive = status === ComponentStatus.Active;
124
+ const [highlightedIndex, setHighlightedIndex] = useState(null);
125
+ const [currentDefineGroupIndex, setCurrentDefineGroupIndex] = useState(0);
126
+ const [completedSelections, setCompletedSelections] = useState([]);
80
127
  // Find all Define tasks
81
128
  const defineTaskIndices = tasks
82
129
  .map((t, idx) => (t.type === TaskType.Define ? idx : -1))
@@ -93,9 +140,16 @@ export function Schedule({ message, tasks, state, status, debug = DebugLevel.Non
93
140
  if (isActive && defineTaskIndices.length === 0 && onSelectionConfirmed) {
94
141
  // No selection needed - all tasks are concrete
95
142
  const concreteTasks = tasks.filter((task) => task.type !== TaskType.Ignore && task.type !== TaskType.Discard);
143
+ // Expose final state
144
+ const finalState = {
145
+ highlightedIndex,
146
+ currentDefineGroupIndex,
147
+ completedSelections,
148
+ };
149
+ requestHandlers.onCompleted(finalState);
96
150
  // Complete the selection phase - it goes to timeline
97
151
  // Callback will create a new Plan showing refined tasks (pending) + Confirm (active)
98
- lifecycleHandlers?.completeActive();
152
+ lifecycleHandlers.completeActive();
99
153
  void onSelectionConfirmed(concreteTasks);
100
154
  }
101
155
  }, [
@@ -104,6 +158,10 @@ export function Schedule({ message, tasks, state, status, debug = DebugLevel.Non
104
158
  tasks,
105
159
  onSelectionConfirmed,
106
160
  lifecycleHandlers,
161
+ highlightedIndex,
162
+ currentDefineGroupIndex,
163
+ completedSelections,
164
+ requestHandlers,
107
165
  ]);
108
166
  useInput((input, key) => {
109
167
  // Don't handle input if not active or no define task
@@ -111,7 +169,7 @@ export function Schedule({ message, tasks, state, status, debug = DebugLevel.Non
111
169
  return;
112
170
  }
113
171
  if (key.escape) {
114
- errorHandlers?.onAborted('task selection');
172
+ requestHandlers.onAborted('task selection');
115
173
  return;
116
174
  }
117
175
  if (key.downArrow) {
@@ -167,61 +225,31 @@ export function Schedule({ message, tasks, state, status, debug = DebugLevel.Non
167
225
  refinedTasks.push(task);
168
226
  }
169
227
  });
228
+ // Expose final state
229
+ const finalState = {
230
+ highlightedIndex: null,
231
+ currentDefineGroupIndex,
232
+ completedSelections: newCompletedSelections,
233
+ };
234
+ requestHandlers.onCompleted(finalState);
170
235
  if (onSelectionConfirmed) {
171
236
  // Complete the selection phase - it goes to timeline
172
237
  // Callback will create a new Plan showing refined tasks (pending) + Confirm (active)
173
- lifecycleHandlers?.completeActive();
238
+ lifecycleHandlers.completeActive();
174
239
  void onSelectionConfirmed(refinedTasks);
175
240
  }
176
241
  else {
177
242
  // No selection callback, just complete normally
178
- lifecycleHandlers?.completeActive();
243
+ lifecycleHandlers.completeActive();
179
244
  }
180
245
  }
181
246
  }
182
247
  }, { isActive: isActive && defineTask !== null });
183
- // Sync state back to component definition
184
- // This ensures timeline can render historical state and tests can validate behavior
185
- useEffect(() => {
186
- stateHandlers?.updateState({
187
- highlightedIndex,
188
- currentDefineGroupIndex,
189
- completedSelections,
190
- });
191
- }, [
248
+ // Controller always renders View, passing current state
249
+ const state = {
192
250
  highlightedIndex,
193
251
  currentDefineGroupIndex,
194
252
  completedSelections,
195
- stateHandlers,
196
- ]);
197
- const listItems = tasks.map((task, idx) => {
198
- // Find which define group this task belongs to (if any)
199
- const defineGroupIndex = defineTaskIndices.indexOf(idx);
200
- const isDefineTask = defineGroupIndex !== -1;
201
- // Determine child selection state
202
- let childIndex = null;
203
- if (isDefineTask) {
204
- if (defineGroupIndex < currentDefineGroupIndex) {
205
- // Previously completed group - show the selection
206
- childIndex = completedSelections[defineGroupIndex] ?? null;
207
- }
208
- else if (defineGroupIndex === currentDefineGroupIndex) {
209
- // Current active group - show live navigation unless not active
210
- if (!isActive) {
211
- // If not active, show the completed selection for this group too
212
- childIndex = completedSelections[defineGroupIndex] ?? null;
213
- }
214
- else {
215
- childIndex = null;
216
- }
217
- }
218
- }
219
- // Show arrow on current active define task when no child is highlighted and is active
220
- const isDefineWithoutSelection = isDefineTask &&
221
- defineGroupIndex === currentDefineGroupIndex &&
222
- highlightedIndex === null &&
223
- isActive;
224
- return taskToListItem(task, childIndex, isDefineWithoutSelection, isActive, debug);
225
- });
226
- return (_jsxs(Box, { flexDirection: "column", children: [message && (_jsx(Box, { marginBottom: 1, marginLeft: 1, children: _jsx(Label, { description: message, taskType: TaskType.Schedule, showType: debug !== DebugLevel.None, isCurrent: isActive, debug: debug }) })), _jsx(Box, { marginLeft: 1, children: _jsx(List, { items: listItems, highlightedIndex: currentDefineTaskIndex >= 0 ? highlightedIndex : null, highlightedParentIndex: currentDefineTaskIndex, showType: debug !== DebugLevel.None }) })] }));
253
+ };
254
+ return (_jsx(ScheduleView, { message: message, tasks: tasks, state: state, status: status, debug: debug }));
227
255
  }