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.
- package/dist/configuration/io.js +85 -0
- package/dist/configuration/messages.js +30 -0
- package/dist/configuration/schema.js +167 -0
- package/dist/configuration/transformation.js +55 -0
- package/dist/configuration/types.js +30 -0
- package/dist/configuration/validation.js +52 -0
- package/dist/execution/handlers.js +135 -0
- package/dist/execution/processing.js +36 -0
- package/dist/execution/reducer.js +148 -0
- package/dist/execution/types.js +12 -0
- package/dist/execution/validation.js +12 -0
- package/dist/index.js +1 -1
- package/dist/services/anthropic.js +2 -1
- package/dist/services/colors.js +22 -12
- package/dist/services/components.js +35 -11
- package/dist/services/config-labels.js +15 -15
- package/dist/services/logger.js +2 -1
- package/dist/services/messages.js +53 -1
- package/dist/services/refinement.js +11 -6
- package/dist/services/router.js +92 -52
- package/dist/skills/execute.md +79 -9
- package/dist/skills/schedule.md +121 -29
- package/dist/tools/execute.tool.js +4 -0
- package/dist/types/schemas.js +1 -0
- package/dist/ui/Answer.js +36 -15
- package/dist/ui/Command.js +43 -23
- package/dist/ui/Component.js +147 -33
- package/dist/ui/Config.js +73 -79
- package/dist/ui/Confirm.js +34 -21
- package/dist/ui/Execute.js +129 -329
- package/dist/ui/Feedback.js +2 -1
- package/dist/ui/Introspect.js +51 -24
- package/dist/ui/Label.js +4 -3
- package/dist/ui/List.js +3 -2
- package/dist/ui/Main.js +5 -1
- package/dist/ui/Refinement.js +8 -1
- package/dist/ui/Schedule.js +89 -61
- package/dist/ui/Validate.js +75 -77
- package/dist/ui/Workflow.js +47 -123
- package/package.json +1 -1
- package/dist/services/configuration.js +0 -409
package/dist/ui/Feedback.js
CHANGED
|
@@ -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,
|
|
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
|
}
|
package/dist/ui/Introspect.js
CHANGED
|
@@ -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 '../
|
|
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
|
|
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
|
-
|
|
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
|
|
52
|
+
workflowHandlers.addToTimeline(...result.debug);
|
|
40
53
|
}
|
|
41
54
|
// Capabilities come directly from result - no parsing needed
|
|
42
|
-
let
|
|
55
|
+
let caps = result.capabilities;
|
|
43
56
|
// Filter out internal capabilities when not in debug mode
|
|
44
57
|
if (debug === DebugLevel.None) {
|
|
45
|
-
|
|
58
|
+
caps = caps.filter((cap) => cap.name.toUpperCase() !== 'SCHEDULE' &&
|
|
46
59
|
cap.name.toUpperCase() !== 'VALIDATE' &&
|
|
47
60
|
cap.name.toUpperCase() !== 'REPORT');
|
|
48
61
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
71
|
+
workflowHandlers.addToQueue(createReportDefinition(result.message, caps));
|
|
56
72
|
// Signal completion
|
|
57
|
-
lifecycleHandlers
|
|
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
|
-
|
|
66
|
-
stateHandlers?.updateState({
|
|
81
|
+
const finalState = {
|
|
67
82
|
error: errorMessage,
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
}, [
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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 '../
|
|
4
|
+
import { DebugLevel } from '../configuration/types.js';
|
|
5
5
|
import { Separator } from './Separator.js';
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
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';
|
package/dist/ui/Refinement.js
CHANGED
|
@@ -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
|
|
22
|
+
return _jsx(RefinementView, { text: text, status: status });
|
|
16
23
|
};
|
package/dist/ui/Schedule.js
CHANGED
|
@@ -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 '../
|
|
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,
|
|
12
|
-
const taskColors = getTaskColors(task.type,
|
|
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:
|
|
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,
|
|
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,
|
|
37
|
-
const planColors = getTaskColors(TaskType.Schedule,
|
|
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,
|
|
64
|
+
const subtaskColors = getTaskColors(subtask.type, status);
|
|
60
65
|
return {
|
|
61
66
|
description: {
|
|
62
67
|
text: subtask.action,
|
|
63
|
-
color:
|
|
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
|
|
79
|
+
export const ScheduleView = ({ message, tasks, state, status, debug = DebugLevel.None, }) => {
|
|
75
80
|
const isActive = status === ComponentStatus.Active;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
238
|
+
lifecycleHandlers.completeActive();
|
|
174
239
|
void onSelectionConfirmed(refinedTasks);
|
|
175
240
|
}
|
|
176
241
|
else {
|
|
177
242
|
// No selection callback, just complete normally
|
|
178
|
-
lifecycleHandlers
|
|
243
|
+
lifecycleHandlers.completeActive();
|
|
179
244
|
}
|
|
180
245
|
}
|
|
181
246
|
}
|
|
182
247
|
}, { isActive: isActive && defineTask !== null });
|
|
183
|
-
//
|
|
184
|
-
|
|
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
|
-
|
|
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
|
}
|