prompt-language-shell 0.5.2 → 0.6.0
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/config/ANSWER.md +4 -0
- package/dist/config/PLAN.md +23 -0
- package/dist/config/VALIDATE.md +12 -11
- package/dist/services/components.js +54 -64
- package/dist/services/configuration.js +84 -0
- package/dist/services/messages.js +22 -0
- package/dist/services/queue.js +2 -2
- package/dist/services/refinement.js +36 -0
- package/dist/services/task-router.js +135 -0
- package/dist/types/types.js +0 -1
- package/dist/ui/Answer.js +18 -27
- package/dist/ui/Command.js +45 -27
- package/dist/ui/Component.js +23 -50
- package/dist/ui/Config.js +49 -24
- package/dist/ui/Confirm.js +17 -11
- package/dist/ui/Execute.js +66 -45
- package/dist/ui/Feedback.js +1 -1
- package/dist/ui/Introspect.js +26 -23
- package/dist/ui/Main.js +71 -100
- package/dist/ui/Message.js +1 -1
- package/dist/ui/Plan.js +54 -32
- package/dist/ui/Refinement.js +6 -7
- package/dist/ui/Report.js +1 -1
- package/dist/ui/UserQuery.js +6 -0
- package/dist/ui/Validate.js +49 -19
- package/dist/ui/Welcome.js +1 -1
- package/dist/ui/Workflow.js +119 -0
- package/package.json +1 -1
- package/dist/handlers/answer.js +0 -21
- package/dist/handlers/command.js +0 -34
- package/dist/handlers/config.js +0 -88
- package/dist/handlers/execute.js +0 -46
- package/dist/handlers/execution.js +0 -140
- package/dist/handlers/introspect.js +0 -21
- package/dist/handlers/plan.js +0 -79
- package/dist/types/handlers.js +0 -1
- package/dist/ui/AnswerDisplay.js +0 -8
- package/dist/ui/Column.js +0 -7
package/dist/ui/Plan.js
CHANGED
|
@@ -49,11 +49,11 @@ function taskToListItem(task, highlightedChildIndex = null, isDefineTaskWithoutS
|
|
|
49
49
|
}
|
|
50
50
|
return item;
|
|
51
51
|
}
|
|
52
|
-
export function Plan({ message, tasks, state, debug = false,
|
|
52
|
+
export function Plan({ message, tasks, state, isActive = true, debug = false, handlers, onSelectionConfirmed, }) {
|
|
53
|
+
// isActive passed as prop
|
|
53
54
|
const [highlightedIndex, setHighlightedIndex] = useState(state?.highlightedIndex ?? null);
|
|
54
55
|
const [currentDefineGroupIndex, setCurrentDefineGroupIndex] = useState(state?.currentDefineGroupIndex ?? 0);
|
|
55
56
|
const [completedSelections, setCompletedSelections] = useState(state?.completedSelections ?? []);
|
|
56
|
-
const [isDone, setIsDone] = useState(state?.done ?? false);
|
|
57
57
|
// Find all Define tasks
|
|
58
58
|
const defineTaskIndices = tasks
|
|
59
59
|
.map((t, idx) => (t.type === TaskType.Define ? idx : -1))
|
|
@@ -65,13 +65,29 @@ export function Plan({ message, tasks, state, debug = false, onSelectionConfirme
|
|
|
65
65
|
? defineTask.params.options.length
|
|
66
66
|
: 0;
|
|
67
67
|
const hasMoreGroups = currentDefineGroupIndex < defineTaskIndices.length - 1;
|
|
68
|
+
// If no DEFINE tasks, immediately confirm with all tasks
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (isActive && defineTaskIndices.length === 0 && onSelectionConfirmed) {
|
|
71
|
+
// No selection needed - all tasks are concrete
|
|
72
|
+
const concreteTasks = tasks.filter((task) => task.type !== TaskType.Ignore && task.type !== TaskType.Discard);
|
|
73
|
+
onSelectionConfirmed(concreteTasks);
|
|
74
|
+
// Signal Plan completion after adding Confirm to queue
|
|
75
|
+
handlers?.onComplete();
|
|
76
|
+
}
|
|
77
|
+
}, [
|
|
78
|
+
isActive,
|
|
79
|
+
defineTaskIndices.length,
|
|
80
|
+
tasks,
|
|
81
|
+
onSelectionConfirmed,
|
|
82
|
+
handlers,
|
|
83
|
+
]);
|
|
68
84
|
useInput((input, key) => {
|
|
69
|
-
// Don't handle input if
|
|
70
|
-
if (
|
|
85
|
+
// Don't handle input if not active or no define task
|
|
86
|
+
if (!isActive || !defineTask) {
|
|
71
87
|
return;
|
|
72
88
|
}
|
|
73
89
|
if (key.escape) {
|
|
74
|
-
onAborted();
|
|
90
|
+
handlers?.onAborted('task selection');
|
|
75
91
|
return;
|
|
76
92
|
}
|
|
77
93
|
if (key.downArrow) {
|
|
@@ -97,16 +113,13 @@ export function Plan({ message, tasks, state, debug = false, onSelectionConfirme
|
|
|
97
113
|
setCompletedSelections(newCompletedSelections);
|
|
98
114
|
if (hasMoreGroups) {
|
|
99
115
|
// Advance to next group
|
|
100
|
-
|
|
116
|
+
const newGroupIndex = currentDefineGroupIndex + 1;
|
|
117
|
+
setCurrentDefineGroupIndex(newGroupIndex);
|
|
101
118
|
setHighlightedIndex(null);
|
|
102
119
|
}
|
|
103
120
|
else {
|
|
104
|
-
//
|
|
105
|
-
|
|
106
|
-
setHighlightedIndex(null); // Clear highlight to show Execute color
|
|
107
|
-
if (state) {
|
|
108
|
-
state.done = true;
|
|
109
|
-
}
|
|
121
|
+
// Clear highlight to show Execute color
|
|
122
|
+
setHighlightedIndex(null);
|
|
110
123
|
// Build refined task list with only selected options (no discarded or ignored ones)
|
|
111
124
|
const refinedTasks = [];
|
|
112
125
|
tasks.forEach((task, idx) => {
|
|
@@ -116,8 +129,10 @@ export function Plan({ message, tasks, state, debug = false, onSelectionConfirme
|
|
|
116
129
|
// This is a Define task - only include the selected option
|
|
117
130
|
const options = task.params.options;
|
|
118
131
|
const selectedIndex = newCompletedSelections[defineGroupIndex];
|
|
132
|
+
const selectedOption = String(options[selectedIndex]);
|
|
133
|
+
// Use Execute as default - LLM will properly classify during refinement
|
|
119
134
|
refinedTasks.push({
|
|
120
|
-
action:
|
|
135
|
+
action: selectedOption,
|
|
121
136
|
type: TaskType.Execute,
|
|
122
137
|
});
|
|
123
138
|
}
|
|
@@ -127,26 +142,33 @@ export function Plan({ message, tasks, state, debug = false, onSelectionConfirme
|
|
|
127
142
|
refinedTasks.push(task);
|
|
128
143
|
}
|
|
129
144
|
});
|
|
130
|
-
onSelectionConfirmed
|
|
145
|
+
if (onSelectionConfirmed) {
|
|
146
|
+
// Callback will handle the entire flow (Refinement, refined Plan, Confirm)
|
|
147
|
+
// So we need to complete the Plan first
|
|
148
|
+
handlers?.onComplete();
|
|
149
|
+
onSelectionConfirmed(refinedTasks);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
// No selection callback, just complete normally
|
|
153
|
+
handlers?.onComplete();
|
|
154
|
+
}
|
|
131
155
|
}
|
|
132
156
|
}
|
|
133
|
-
}, { isActive:
|
|
134
|
-
// Sync state back to
|
|
157
|
+
}, { isActive: isActive && defineTask !== null });
|
|
158
|
+
// Sync state back to component definition
|
|
159
|
+
// This ensures timeline can render historical state and tests can validate behavior
|
|
135
160
|
useEffect(() => {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
161
|
+
handlers?.updateState({
|
|
162
|
+
highlightedIndex,
|
|
163
|
+
currentDefineGroupIndex,
|
|
164
|
+
completedSelections,
|
|
165
|
+
});
|
|
142
166
|
}, [
|
|
143
167
|
highlightedIndex,
|
|
144
168
|
currentDefineGroupIndex,
|
|
145
169
|
completedSelections,
|
|
146
|
-
|
|
147
|
-
state,
|
|
170
|
+
handlers,
|
|
148
171
|
]);
|
|
149
|
-
const isCurrent = isDone === false;
|
|
150
172
|
const listItems = tasks.map((task, idx) => {
|
|
151
173
|
// Find which define group this task belongs to (if any)
|
|
152
174
|
const defineGroupIndex = defineTaskIndices.indexOf(idx);
|
|
@@ -159,9 +181,9 @@ export function Plan({ message, tasks, state, debug = false, onSelectionConfirme
|
|
|
159
181
|
childIndex = completedSelections[defineGroupIndex] ?? null;
|
|
160
182
|
}
|
|
161
183
|
else if (defineGroupIndex === currentDefineGroupIndex) {
|
|
162
|
-
// Current active group - show live navigation unless
|
|
163
|
-
if (
|
|
164
|
-
// If
|
|
184
|
+
// Current active group - show live navigation unless not active
|
|
185
|
+
if (!isActive) {
|
|
186
|
+
// If not active, show the completed selection for this group too
|
|
165
187
|
childIndex = completedSelections[defineGroupIndex] ?? null;
|
|
166
188
|
}
|
|
167
189
|
else {
|
|
@@ -169,12 +191,12 @@ export function Plan({ message, tasks, state, debug = false, onSelectionConfirme
|
|
|
169
191
|
}
|
|
170
192
|
}
|
|
171
193
|
}
|
|
172
|
-
// Show arrow on current active define task when no child is highlighted and
|
|
194
|
+
// Show arrow on current active define task when no child is highlighted and is active
|
|
173
195
|
const isDefineWithoutSelection = isDefineTask &&
|
|
174
196
|
defineGroupIndex === currentDefineGroupIndex &&
|
|
175
197
|
highlightedIndex === null &&
|
|
176
|
-
|
|
177
|
-
return taskToListItem(task, childIndex, isDefineWithoutSelection,
|
|
198
|
+
isActive;
|
|
199
|
+
return taskToListItem(task, childIndex, isDefineWithoutSelection, isActive);
|
|
178
200
|
});
|
|
179
|
-
return (_jsxs(Box, { flexDirection: "column", children: [message && (_jsx(Box, { marginBottom: 1, children: _jsx(Label, { description: message, taskType: TaskType.Plan, showType: debug, isCurrent:
|
|
201
|
+
return (_jsxs(Box, { flexDirection: "column", children: [message && (_jsx(Box, { marginBottom: 1, marginLeft: 1, children: _jsx(Label, { description: message, taskType: TaskType.Plan, showType: debug, isCurrent: isActive }) })), _jsx(Box, { marginLeft: 1, children: _jsx(List, { items: listItems, highlightedIndex: currentDefineTaskIndex >= 0 ? highlightedIndex : null, highlightedParentIndex: currentDefineTaskIndex, showType: debug }) })] }));
|
|
180
202
|
}
|
package/dist/ui/Refinement.js
CHANGED
|
@@ -3,13 +3,12 @@ import { Box } from 'ink';
|
|
|
3
3
|
import { useInput } from '../services/keyboard.js';
|
|
4
4
|
import { Message } from './Message.js';
|
|
5
5
|
import { Spinner } from './Spinner.js';
|
|
6
|
-
export const Refinement = ({ text,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
onAborted();
|
|
6
|
+
export const Refinement = ({ text, isActive = true, onAborted, }) => {
|
|
7
|
+
useInput((_, key) => {
|
|
8
|
+
if (key.escape && isActive) {
|
|
9
|
+
onAborted('plan refinement');
|
|
11
10
|
return;
|
|
12
11
|
}
|
|
13
|
-
}, { isActive
|
|
14
|
-
return (_jsxs(Box, { gap: 1, children: [_jsx(Message, { text: text }),
|
|
12
|
+
}, { isActive });
|
|
13
|
+
return (_jsxs(Box, { gap: 1, children: [_jsx(Message, { text: text }), isActive && _jsx(Spinner, {})] }));
|
|
15
14
|
};
|
package/dist/ui/Report.js
CHANGED
|
@@ -10,5 +10,5 @@ function CapabilityItem({ name, description, isBuiltIn, isIndirect, }) {
|
|
|
10
10
|
return (_jsxs(Box, { children: [_jsx(Text, { children: "- " }), _jsx(Text, { color: color, children: name }), _jsxs(Text, { children: [" - ", description] })] }));
|
|
11
11
|
}
|
|
12
12
|
export function Report({ message, capabilities }) {
|
|
13
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: message }), _jsx(Box, { flexDirection: "column", marginLeft:
|
|
13
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginLeft: 1, children: _jsx(Text, { children: message }) }), _jsx(Box, { flexDirection: "column", marginLeft: 3, marginTop: 1, children: capabilities.map((capability, index) => (_jsx(CapabilityItem, { name: capability.name, description: capability.description, isBuiltIn: capability.isBuiltIn, isIndirect: capability.isIndirect }, index))) })] }));
|
|
14
14
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { Colors } from '../services/colors.js';
|
|
4
|
+
export function UserQuery({ children }) {
|
|
5
|
+
return (_jsx(Box, { paddingX: 1, alignSelf: "flex-start", backgroundColor: Colors.Background.UserQuery, children: _jsx(Text, { color: Colors.Text.UserQuery, children: children }) }));
|
|
6
|
+
}
|
package/dist/ui/Validate.js
CHANGED
|
@@ -6,29 +6,29 @@ import { Colors, getTextColor } from '../services/colors.js';
|
|
|
6
6
|
import { useInput } from '../services/keyboard.js';
|
|
7
7
|
import { formatErrorMessage } from '../services/messages.js';
|
|
8
8
|
import { ensureMinimumTime } from '../services/timing.js';
|
|
9
|
+
import { saveConfig, unflattenConfig } from '../services/configuration.js';
|
|
10
|
+
import { Config, StepType } from './Config.js';
|
|
9
11
|
import { Spinner } from './Spinner.js';
|
|
10
12
|
const MIN_PROCESSING_TIME = 1000;
|
|
11
|
-
export function Validate({ missingConfig, userRequest, state, service, children, onError, onComplete, onAborted, }) {
|
|
12
|
-
|
|
13
|
-
const isCurrent = done === false;
|
|
13
|
+
export function Validate({ missingConfig, userRequest, state, isActive = true, service, children, debug, onError, onComplete, onAborted, handlers, }) {
|
|
14
|
+
// isActive passed as prop
|
|
14
15
|
const [error, setError] = useState(null);
|
|
15
|
-
const [isLoading, setIsLoading] = useState(state?.isLoading ?? !done);
|
|
16
16
|
const [completionMessage, setCompletionMessage] = useState(null);
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
const [configRequirements, setConfigRequirements] = useState(null);
|
|
18
|
+
const [showConfig, setShowConfig] = useState(false);
|
|
19
|
+
useInput((_, key) => {
|
|
20
|
+
if (key.escape && isActive && !showConfig) {
|
|
21
|
+
onAborted('validation');
|
|
21
22
|
}
|
|
22
|
-
}, { isActive:
|
|
23
|
+
}, { isActive: isActive && !showConfig });
|
|
23
24
|
useEffect(() => {
|
|
24
|
-
// Skip processing if
|
|
25
|
-
if (
|
|
25
|
+
// Skip processing if not active
|
|
26
|
+
if (!isActive) {
|
|
26
27
|
return;
|
|
27
28
|
}
|
|
28
29
|
// Skip processing if no service available
|
|
29
30
|
if (!service) {
|
|
30
31
|
setError('No service available');
|
|
31
|
-
setIsLoading(false);
|
|
32
32
|
return;
|
|
33
33
|
}
|
|
34
34
|
let mounted = true;
|
|
@@ -67,15 +67,22 @@ export function Validate({ missingConfig, userRequest, state, service, children,
|
|
|
67
67
|
];
|
|
68
68
|
const message = messages[Math.floor(Math.random() * messages.length)];
|
|
69
69
|
setCompletionMessage(message);
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
setConfigRequirements(withDescriptions);
|
|
71
|
+
// Save state after validation completes
|
|
72
|
+
handlers?.updateState({
|
|
73
|
+
configRequirements: withDescriptions,
|
|
74
|
+
validated: true,
|
|
75
|
+
});
|
|
72
76
|
}
|
|
73
77
|
}
|
|
74
78
|
catch (err) {
|
|
75
79
|
await ensureMinimumTime(startTime, MIN_PROCESSING_TIME);
|
|
76
80
|
if (mounted) {
|
|
77
81
|
const errorMessage = formatErrorMessage(err);
|
|
78
|
-
|
|
82
|
+
// Save error state
|
|
83
|
+
handlers?.updateState({
|
|
84
|
+
error: errorMessage,
|
|
85
|
+
});
|
|
79
86
|
if (onError) {
|
|
80
87
|
onError(errorMessage);
|
|
81
88
|
}
|
|
@@ -92,17 +99,40 @@ export function Validate({ missingConfig, userRequest, state, service, children,
|
|
|
92
99
|
}, [
|
|
93
100
|
missingConfig,
|
|
94
101
|
userRequest,
|
|
95
|
-
|
|
102
|
+
isActive,
|
|
96
103
|
service,
|
|
97
104
|
onComplete,
|
|
98
105
|
onError,
|
|
99
106
|
onAborted,
|
|
100
107
|
]);
|
|
101
|
-
// Don't render when
|
|
102
|
-
if (
|
|
108
|
+
// Don't render when not active and nothing to show
|
|
109
|
+
if (!isActive && !completionMessage && !error && !children) {
|
|
103
110
|
return null;
|
|
104
111
|
}
|
|
105
|
-
|
|
112
|
+
// Create ConfigSteps from requirements
|
|
113
|
+
const configSteps = configRequirements
|
|
114
|
+
? configRequirements.map((req) => ({
|
|
115
|
+
description: req.description || req.path,
|
|
116
|
+
key: req.path,
|
|
117
|
+
path: req.path,
|
|
118
|
+
type: StepType.Text,
|
|
119
|
+
value: null,
|
|
120
|
+
validate: () => true,
|
|
121
|
+
}))
|
|
122
|
+
: null;
|
|
123
|
+
const handleConfigFinished = (config) => {
|
|
124
|
+
// Convert flat dotted keys to nested structure grouped by section
|
|
125
|
+
const configBySection = unflattenConfig(config);
|
|
126
|
+
// Save each section
|
|
127
|
+
for (const [section, sectionConfig] of Object.entries(configBySection)) {
|
|
128
|
+
saveConfig(section, sectionConfig);
|
|
129
|
+
}
|
|
130
|
+
onComplete?.(configRequirements);
|
|
131
|
+
};
|
|
132
|
+
const handleConfigAborted = (operation) => {
|
|
133
|
+
onAborted(operation);
|
|
134
|
+
};
|
|
135
|
+
return (_jsxs(Box, { alignSelf: "flex-start", flexDirection: "column", children: [isActive && !completionMessage && !error && (_jsxs(Box, { marginLeft: 1, children: [_jsxs(Text, { color: getTextColor(isActive), children: ["Validating configuration requirements.", ' '] }), _jsx(Spinner, {})] })), completionMessage && (_jsx(Box, { marginLeft: 1, children: _jsx(Text, { color: getTextColor(isActive), children: completionMessage }) })), error && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: Colors.Status.Error, children: ["Error: ", error] }) })), configSteps && !error && (_jsx(Box, { marginTop: 1, children: _jsx(Config, { steps: configSteps, isActive: isActive, debug: debug, onFinished: handleConfigFinished, onAborted: handleConfigAborted, handlers: handlers }) })), children] }));
|
|
106
136
|
}
|
|
107
137
|
/**
|
|
108
138
|
* Build prompt for VALIDATE tool
|
package/dist/ui/Welcome.js
CHANGED
|
@@ -21,7 +21,7 @@ function Description({ description }) {
|
|
|
21
21
|
function Usage() {
|
|
22
22
|
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, gap: 1, children: [_jsx(Section, { title: "Get started:", children: _jsx(Example, { children: "list skills" }) }), _jsx(Section, { title: "Usage:", children: _jsx(Example, { children: "[describe your request]" }) })] }));
|
|
23
23
|
}
|
|
24
|
-
function Section({ title, children
|
|
24
|
+
function Section({ title, children }) {
|
|
25
25
|
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: Palette.White, children: title }), children] }));
|
|
26
26
|
}
|
|
27
27
|
function Example({ children }) {
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { Box, Static } from 'ink';
|
|
4
|
+
import { ComponentName, FeedbackType } from '../types/types.js';
|
|
5
|
+
import { createFeedback, isStateless, markAsDone, } from '../services/components.js';
|
|
6
|
+
import { exitApp } from '../services/process.js';
|
|
7
|
+
import { getCancellationMessage } from '../services/messages.js';
|
|
8
|
+
import { Component } from './Component.js';
|
|
9
|
+
export const Workflow = ({ initialQueue, debug }) => {
|
|
10
|
+
const [timeline, setTimeline] = useState([]);
|
|
11
|
+
const [active, setActive] = useState(null);
|
|
12
|
+
const [queue, setQueue] = useState(initialQueue);
|
|
13
|
+
// Function to move active component to timeline
|
|
14
|
+
const moveActiveToTimeline = useCallback(() => {
|
|
15
|
+
setActive((curr) => {
|
|
16
|
+
if (!curr)
|
|
17
|
+
return null;
|
|
18
|
+
const doneComponent = markAsDone(curr);
|
|
19
|
+
setTimeline((prev) => [...prev, doneComponent]);
|
|
20
|
+
return null;
|
|
21
|
+
});
|
|
22
|
+
}, []);
|
|
23
|
+
// Global handlers for all stateful components
|
|
24
|
+
const handlers = useMemo(() => ({
|
|
25
|
+
onComplete: () => {
|
|
26
|
+
moveActiveToTimeline();
|
|
27
|
+
},
|
|
28
|
+
onAborted: (operation) => {
|
|
29
|
+
moveActiveToTimeline();
|
|
30
|
+
// Add feedback to queue and exit
|
|
31
|
+
const message = getCancellationMessage(operation);
|
|
32
|
+
setQueue((queue) => [
|
|
33
|
+
...queue,
|
|
34
|
+
createFeedback(FeedbackType.Aborted, message),
|
|
35
|
+
]);
|
|
36
|
+
},
|
|
37
|
+
onError: (error) => {
|
|
38
|
+
moveActiveToTimeline();
|
|
39
|
+
// Add feedback to queue and exit with error code
|
|
40
|
+
setQueue((queue) => [
|
|
41
|
+
...queue,
|
|
42
|
+
createFeedback(FeedbackType.Failed, error),
|
|
43
|
+
]);
|
|
44
|
+
},
|
|
45
|
+
addToQueue: (...items) => {
|
|
46
|
+
setQueue((queue) => [...queue, ...items]);
|
|
47
|
+
},
|
|
48
|
+
addToTimeline: (...items) => {
|
|
49
|
+
setTimeline((prev) => [...prev, ...items]);
|
|
50
|
+
},
|
|
51
|
+
completeActive: () => {
|
|
52
|
+
moveActiveToTimeline();
|
|
53
|
+
},
|
|
54
|
+
updateState: (newState) => {
|
|
55
|
+
setActive((curr) => {
|
|
56
|
+
if (!curr || !('state' in curr))
|
|
57
|
+
return curr;
|
|
58
|
+
const stateful = curr;
|
|
59
|
+
return {
|
|
60
|
+
...stateful,
|
|
61
|
+
state: {
|
|
62
|
+
...stateful.state,
|
|
63
|
+
...newState,
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
},
|
|
68
|
+
}), [moveActiveToTimeline]);
|
|
69
|
+
// Global Esc handler removed - components handle their own Esc individually
|
|
70
|
+
// Move next item from queue to active
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
if (queue.length > 0 && active === null) {
|
|
73
|
+
const [first, ...rest] = queue;
|
|
74
|
+
setQueue(rest);
|
|
75
|
+
setActive(first);
|
|
76
|
+
}
|
|
77
|
+
}, [queue, active]);
|
|
78
|
+
// Process active component - stateless components auto-move to timeline
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
if (!active)
|
|
81
|
+
return;
|
|
82
|
+
if (isStateless(active)) {
|
|
83
|
+
const doneComponent = markAsDone(active);
|
|
84
|
+
setTimeline((prev) => [...prev, doneComponent]);
|
|
85
|
+
setActive(null);
|
|
86
|
+
}
|
|
87
|
+
// Stateful components stay in active until handlers move them to timeline
|
|
88
|
+
}, [active]);
|
|
89
|
+
// Exit when all done
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (active === null && queue.length === 0 && timeline.length > 0) {
|
|
92
|
+
// Check if last item in timeline is a failed feedback
|
|
93
|
+
const lastItem = timeline[timeline.length - 1];
|
|
94
|
+
const isFailed = lastItem.name === ComponentName.Feedback &&
|
|
95
|
+
lastItem.props.type === FeedbackType.Failed;
|
|
96
|
+
exitApp(isFailed ? 1 : 0);
|
|
97
|
+
}
|
|
98
|
+
}, [active, queue, timeline]);
|
|
99
|
+
// Inject global handlers into active component
|
|
100
|
+
const activeComponent = useMemo(() => {
|
|
101
|
+
if (!active)
|
|
102
|
+
return null;
|
|
103
|
+
// For stateless components, render as-is with isActive=true
|
|
104
|
+
if (isStateless(active)) {
|
|
105
|
+
return (_jsx(Component, { def: active, isActive: true, debug: debug }, active.id));
|
|
106
|
+
}
|
|
107
|
+
// For stateful components, inject global handlers
|
|
108
|
+
const statefulActive = active;
|
|
109
|
+
const wrappedDef = {
|
|
110
|
+
...statefulActive,
|
|
111
|
+
props: {
|
|
112
|
+
...statefulActive.props,
|
|
113
|
+
handlers,
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
return (_jsx(Component, { def: wrappedDef, isActive: true, debug: debug }, active.id));
|
|
117
|
+
}, [active, debug, handlers]);
|
|
118
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Static, { items: timeline, children: (item) => (_jsx(Box, { marginTop: 1, children: _jsx(Component, { def: item, isActive: false, debug: debug }) }, item.id)) }, "timeline"), _jsx(Box, { marginTop: 1, children: activeComponent })] }));
|
|
119
|
+
};
|
package/package.json
CHANGED
package/dist/handlers/answer.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { ComponentName } from '../types/types.js';
|
|
2
|
-
import { createAnswerDisplayDefinition } from '../services/components.js';
|
|
3
|
-
import { createErrorHandler, withQueueHandler } from '../services/queue.js';
|
|
4
|
-
/**
|
|
5
|
-
* Creates all answer handlers
|
|
6
|
-
*/
|
|
7
|
-
export function createAnswerHandlers(ops, handleAborted) {
|
|
8
|
-
const onError = (error) => {
|
|
9
|
-
ops.setQueue(createErrorHandler(ComponentName.Answer, ops.addToTimeline)(error));
|
|
10
|
-
};
|
|
11
|
-
const onComplete = (answer) => {
|
|
12
|
-
ops.setQueue(withQueueHandler(ComponentName.Answer, () => {
|
|
13
|
-
ops.addToTimeline(createAnswerDisplayDefinition(answer));
|
|
14
|
-
return undefined;
|
|
15
|
-
}, true, 0));
|
|
16
|
-
};
|
|
17
|
-
const onAborted = () => {
|
|
18
|
-
handleAborted('Answer');
|
|
19
|
-
};
|
|
20
|
-
return { onError, onComplete, onAborted };
|
|
21
|
-
}
|
package/dist/handlers/command.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { ComponentName, TaskType } from '../types/types.js';
|
|
2
|
-
import { createConfirmDefinition, createPlanDefinition, markAsDone, } from '../services/components.js';
|
|
3
|
-
import { createErrorHandler, withQueueHandler } from '../services/queue.js';
|
|
4
|
-
/**
|
|
5
|
-
* Creates all command handlers
|
|
6
|
-
*/
|
|
7
|
-
export function createCommandHandlers(ops, handleAborted, planHandlers, executionHandlers) {
|
|
8
|
-
const onError = (error) => {
|
|
9
|
-
ops.setQueue(createErrorHandler(ComponentName.Command, ops.addToTimeline)(error));
|
|
10
|
-
};
|
|
11
|
-
const onComplete = (message, tasks) => {
|
|
12
|
-
ops.setQueue(withQueueHandler(ComponentName.Command, (first) => {
|
|
13
|
-
const hasDefineTask = tasks.some((task) => task.type === TaskType.Define);
|
|
14
|
-
const planDefinition = createPlanDefinition(message, tasks, planHandlers.createAbortHandler(tasks), hasDefineTask ? planHandlers.onSelectionConfirmed : undefined);
|
|
15
|
-
if (hasDefineTask) {
|
|
16
|
-
ops.addToTimeline(markAsDone(first));
|
|
17
|
-
return [planDefinition];
|
|
18
|
-
}
|
|
19
|
-
else {
|
|
20
|
-
const confirmDefinition = createConfirmDefinition(() => {
|
|
21
|
-
executionHandlers.onConfirmed(tasks);
|
|
22
|
-
}, () => {
|
|
23
|
-
executionHandlers.onCancelled(tasks);
|
|
24
|
-
});
|
|
25
|
-
ops.addToTimeline(markAsDone(first), planDefinition);
|
|
26
|
-
return [confirmDefinition];
|
|
27
|
-
}
|
|
28
|
-
}, false, 0));
|
|
29
|
-
};
|
|
30
|
-
const onAborted = () => {
|
|
31
|
-
handleAborted('Request');
|
|
32
|
-
};
|
|
33
|
-
return { onError, onComplete, onAborted };
|
|
34
|
-
}
|
package/dist/handlers/config.js
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { ComponentName, FeedbackType } from '../types/types.js';
|
|
2
|
-
import { createAnthropicService, } from '../services/anthropic.js';
|
|
3
|
-
import { createCommandDefinition, createExecuteDefinition, createFeedback, markAsDone, } from '../services/components.js';
|
|
4
|
-
import { saveAnthropicConfig, saveConfig } from '../services/configuration.js';
|
|
5
|
-
import { FeedbackMessages } from '../services/messages.js';
|
|
6
|
-
import { exitApp } from '../services/process.js';
|
|
7
|
-
import { withQueueHandler } from '../services/queue.js';
|
|
8
|
-
/**
|
|
9
|
-
* Creates all config handlers
|
|
10
|
-
*/
|
|
11
|
-
export function createConfigHandlers(ops, handleAborted, command, commandHandlers, setService) {
|
|
12
|
-
const onFinished = (config) => {
|
|
13
|
-
const anthropicConfig = config;
|
|
14
|
-
saveAnthropicConfig(anthropicConfig);
|
|
15
|
-
const newService = createAnthropicService(anthropicConfig);
|
|
16
|
-
setService(newService);
|
|
17
|
-
ops.setQueue(withQueueHandler(ComponentName.Config, (first, rest) => {
|
|
18
|
-
ops.addToTimeline(markAsDone(first), createFeedback(FeedbackType.Succeeded, FeedbackMessages.ConfigurationComplete));
|
|
19
|
-
if (command) {
|
|
20
|
-
return [
|
|
21
|
-
...rest,
|
|
22
|
-
createCommandDefinition(command, newService, commandHandlers.onError, commandHandlers.onComplete, commandHandlers.onAborted),
|
|
23
|
-
];
|
|
24
|
-
}
|
|
25
|
-
exitApp(0);
|
|
26
|
-
return rest;
|
|
27
|
-
}, false, 0));
|
|
28
|
-
};
|
|
29
|
-
const onAborted = () => {
|
|
30
|
-
handleAborted('Configuration');
|
|
31
|
-
};
|
|
32
|
-
return { onFinished, onAborted };
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Creates config execution finished handler for CONFIG skill
|
|
36
|
-
* Saves arbitrary config keys and optionally continues with execution
|
|
37
|
-
*/
|
|
38
|
-
export function createConfigExecutionFinishedHandler(addToTimeline, keys, tasks, service, executeHandlers) {
|
|
39
|
-
return (config) => {
|
|
40
|
-
// Group by top-level section
|
|
41
|
-
const sections = {};
|
|
42
|
-
for (const fullKey of keys) {
|
|
43
|
-
const parts = fullKey.split('.');
|
|
44
|
-
const shortKey = parts[parts.length - 1];
|
|
45
|
-
const topSection = parts[0];
|
|
46
|
-
// Initialize section if needed
|
|
47
|
-
sections[topSection] = sections[topSection] ?? {};
|
|
48
|
-
if (shortKey in config) {
|
|
49
|
-
const value = config[shortKey];
|
|
50
|
-
// Build nested structure recursively
|
|
51
|
-
let current = sections[topSection];
|
|
52
|
-
for (let i = 1; i < parts.length - 1; i++) {
|
|
53
|
-
current[parts[i]] = current[parts[i]] ?? {};
|
|
54
|
-
current = current[parts[i]];
|
|
55
|
-
}
|
|
56
|
-
current[parts[parts.length - 1]] = value;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
for (const [section, sectionConfig] of Object.entries(sections)) {
|
|
60
|
-
saveConfig(section, sectionConfig);
|
|
61
|
-
}
|
|
62
|
-
return withQueueHandler(ComponentName.Config, (first, rest) => {
|
|
63
|
-
addToTimeline(markAsDone(first), createFeedback(FeedbackType.Succeeded, FeedbackMessages.ConfigurationComplete));
|
|
64
|
-
// If tasks are provided, continue with execution
|
|
65
|
-
if (tasks && service && executeHandlers) {
|
|
66
|
-
return [
|
|
67
|
-
...rest,
|
|
68
|
-
createExecuteDefinition(tasks, service, executeHandlers.onError, executeHandlers.onComplete, executeHandlers.onAborted),
|
|
69
|
-
];
|
|
70
|
-
}
|
|
71
|
-
// Otherwise, exit (legacy behavior for initial setup)
|
|
72
|
-
exitApp(0);
|
|
73
|
-
return rest;
|
|
74
|
-
}, false, 0);
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Creates config execution aborted handler for CONFIG skill
|
|
79
|
-
*/
|
|
80
|
-
export function createConfigExecutionAbortedHandler(addToTimeline) {
|
|
81
|
-
return () => {
|
|
82
|
-
return withQueueHandler(ComponentName.Config, (first, rest) => {
|
|
83
|
-
addToTimeline(markAsDone(first), createFeedback(FeedbackType.Aborted, 'Configuration cancelled.'));
|
|
84
|
-
exitApp(0);
|
|
85
|
-
return rest;
|
|
86
|
-
}, false, 0);
|
|
87
|
-
};
|
|
88
|
-
}
|
package/dist/handlers/execute.js
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { ComponentName, FeedbackType } from '../types/types.js';
|
|
2
|
-
import { createFeedback, createMessage, markAsDone, } from '../services/components.js';
|
|
3
|
-
import { formatDuration } from '../services/messages.js';
|
|
4
|
-
import { exitApp } from '../services/process.js';
|
|
5
|
-
import { ExecutionResult } from '../services/shell.js';
|
|
6
|
-
import { withQueueHandler } from '../services/queue.js';
|
|
7
|
-
/**
|
|
8
|
-
* Creates all execute handlers
|
|
9
|
-
*/
|
|
10
|
-
export function createExecuteHandlers(ops, handleAborted) {
|
|
11
|
-
void handleAborted;
|
|
12
|
-
const onError = (error) => {
|
|
13
|
-
ops.setQueue(withQueueHandler(ComponentName.Execute, (first) => {
|
|
14
|
-
ops.addToTimeline(markAsDone(first), createFeedback(FeedbackType.Failed, error));
|
|
15
|
-
exitApp(1);
|
|
16
|
-
return [];
|
|
17
|
-
}));
|
|
18
|
-
};
|
|
19
|
-
const onComplete = (outputs, totalElapsed) => {
|
|
20
|
-
ops.setQueue(withQueueHandler(ComponentName.Execute, (first) => {
|
|
21
|
-
const failed = outputs.find((out) => out.result !== ExecutionResult.Success);
|
|
22
|
-
if (failed) {
|
|
23
|
-
const errorMessage = failed.error
|
|
24
|
-
? `${failed.description}: ${failed.error}`
|
|
25
|
-
: `${failed.description} failed`;
|
|
26
|
-
ops.addToTimeline(markAsDone(first), createFeedback(FeedbackType.Failed, errorMessage));
|
|
27
|
-
exitApp(1);
|
|
28
|
-
return [];
|
|
29
|
-
}
|
|
30
|
-
ops.addToTimeline(markAsDone(first), createMessage(`Execution completed in ${formatDuration(totalElapsed)}.`));
|
|
31
|
-
exitApp(0);
|
|
32
|
-
return [];
|
|
33
|
-
}));
|
|
34
|
-
};
|
|
35
|
-
const onAborted = (elapsedTime) => {
|
|
36
|
-
ops.setQueue(withQueueHandler(ComponentName.Execute, (first) => {
|
|
37
|
-
const message = elapsedTime > 0
|
|
38
|
-
? `The execution was cancelled after ${formatDuration(elapsedTime)}.`
|
|
39
|
-
: 'The execution was cancelled.';
|
|
40
|
-
ops.addToTimeline(markAsDone(first), createFeedback(FeedbackType.Aborted, message));
|
|
41
|
-
exitApp(0);
|
|
42
|
-
return [];
|
|
43
|
-
}));
|
|
44
|
-
};
|
|
45
|
-
return { onError, onComplete, onAborted };
|
|
46
|
-
}
|