prompt-language-shell 0.8.4 → 0.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +35 -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 +1 -1
- package/dist/services/colors.js +1 -1
- 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 +32 -1
- package/dist/services/refinement.js +6 -6
- package/dist/services/router.js +43 -27
- 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 +68 -78
- package/dist/ui/Confirm.js +34 -21
- package/dist/ui/Execute.js +107 -328
- package/dist/ui/Introspect.js +51 -24
- package/dist/ui/Label.js +1 -1
- package/dist/ui/Main.js +5 -1
- package/dist/ui/Refinement.js +8 -1
- package/dist/ui/Schedule.js +75 -52
- package/dist/ui/Validate.js +75 -77
- package/dist/ui/Workflow.js +50 -123
- package/package.json +1 -1
- package/dist/services/configuration.js +0 -409
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,7 +1,7 @@
|
|
|
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
6
|
export function Label({ description, taskType, showType = false, isCurrent = false, debug = DebugLevel.None, }) {
|
|
7
7
|
const colors = getTaskColors(taskType, isCurrent);
|
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,10 +1,10 @@
|
|
|
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
6
|
import { getTaskColors, getTaskTypeLabel } from '../services/colors.js';
|
|
7
|
-
import { DebugLevel } from '../
|
|
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';
|
|
@@ -71,12 +71,54 @@ function taskToListItem(task, highlightedChildIndex = null, isDefineTaskWithoutS
|
|
|
71
71
|
}
|
|
72
72
|
return item;
|
|
73
73
|
}
|
|
74
|
-
export
|
|
74
|
+
export const ScheduleView = ({ message, tasks, state, status, debug = DebugLevel.None, }) => {
|
|
75
75
|
const isActive = status === ComponentStatus.Active;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
76
|
+
const { highlightedIndex, currentDefineGroupIndex, completedSelections } = state;
|
|
77
|
+
// Find all Define tasks
|
|
78
|
+
const defineTaskIndices = tasks
|
|
79
|
+
.map((t, idx) => (t.type === TaskType.Define ? idx : -1))
|
|
80
|
+
.filter((idx) => idx !== -1);
|
|
81
|
+
// Get the current active define task
|
|
82
|
+
const currentDefineTaskIndex = defineTaskIndices[currentDefineGroupIndex] ?? -1;
|
|
83
|
+
const listItems = tasks.map((task, idx) => {
|
|
84
|
+
// Find which define group this task belongs to (if any)
|
|
85
|
+
const defineGroupIndex = defineTaskIndices.indexOf(idx);
|
|
86
|
+
const isDefineTask = defineGroupIndex !== -1;
|
|
87
|
+
// Determine child selection state
|
|
88
|
+
let childIndex = null;
|
|
89
|
+
if (isDefineTask) {
|
|
90
|
+
if (defineGroupIndex < currentDefineGroupIndex) {
|
|
91
|
+
// Previously completed group - show the selection
|
|
92
|
+
childIndex = completedSelections[defineGroupIndex] ?? null;
|
|
93
|
+
}
|
|
94
|
+
else if (defineGroupIndex === currentDefineGroupIndex) {
|
|
95
|
+
// Current active group - show live navigation unless not active
|
|
96
|
+
if (!isActive) {
|
|
97
|
+
// If not active, show the completed selection for this group too
|
|
98
|
+
childIndex = completedSelections[defineGroupIndex] ?? null;
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
childIndex = null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Show arrow on current active define task when no child is highlighted and is active
|
|
106
|
+
const isDefineWithoutSelection = isDefineTask &&
|
|
107
|
+
defineGroupIndex === currentDefineGroupIndex &&
|
|
108
|
+
highlightedIndex === null &&
|
|
109
|
+
isActive;
|
|
110
|
+
return taskToListItem(task, childIndex, isDefineWithoutSelection, isActive, debug);
|
|
111
|
+
});
|
|
112
|
+
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 }) })] }));
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Schedule controller: Manages task selection and navigation
|
|
116
|
+
*/
|
|
117
|
+
export function Schedule({ message, tasks, status, debug = DebugLevel.None, requestHandlers, lifecycleHandlers, onSelectionConfirmed, }) {
|
|
118
|
+
const isActive = status === ComponentStatus.Active;
|
|
119
|
+
const [highlightedIndex, setHighlightedIndex] = useState(null);
|
|
120
|
+
const [currentDefineGroupIndex, setCurrentDefineGroupIndex] = useState(0);
|
|
121
|
+
const [completedSelections, setCompletedSelections] = useState([]);
|
|
80
122
|
// Find all Define tasks
|
|
81
123
|
const defineTaskIndices = tasks
|
|
82
124
|
.map((t, idx) => (t.type === TaskType.Define ? idx : -1))
|
|
@@ -93,9 +135,16 @@ export function Schedule({ message, tasks, state, status, debug = DebugLevel.Non
|
|
|
93
135
|
if (isActive && defineTaskIndices.length === 0 && onSelectionConfirmed) {
|
|
94
136
|
// No selection needed - all tasks are concrete
|
|
95
137
|
const concreteTasks = tasks.filter((task) => task.type !== TaskType.Ignore && task.type !== TaskType.Discard);
|
|
138
|
+
// Expose final state
|
|
139
|
+
const finalState = {
|
|
140
|
+
highlightedIndex,
|
|
141
|
+
currentDefineGroupIndex,
|
|
142
|
+
completedSelections,
|
|
143
|
+
};
|
|
144
|
+
requestHandlers.onCompleted(finalState);
|
|
96
145
|
// Complete the selection phase - it goes to timeline
|
|
97
146
|
// Callback will create a new Plan showing refined tasks (pending) + Confirm (active)
|
|
98
|
-
lifecycleHandlers
|
|
147
|
+
lifecycleHandlers.completeActive();
|
|
99
148
|
void onSelectionConfirmed(concreteTasks);
|
|
100
149
|
}
|
|
101
150
|
}, [
|
|
@@ -104,6 +153,10 @@ export function Schedule({ message, tasks, state, status, debug = DebugLevel.Non
|
|
|
104
153
|
tasks,
|
|
105
154
|
onSelectionConfirmed,
|
|
106
155
|
lifecycleHandlers,
|
|
156
|
+
highlightedIndex,
|
|
157
|
+
currentDefineGroupIndex,
|
|
158
|
+
completedSelections,
|
|
159
|
+
requestHandlers,
|
|
107
160
|
]);
|
|
108
161
|
useInput((input, key) => {
|
|
109
162
|
// Don't handle input if not active or no define task
|
|
@@ -111,7 +164,7 @@ export function Schedule({ message, tasks, state, status, debug = DebugLevel.Non
|
|
|
111
164
|
return;
|
|
112
165
|
}
|
|
113
166
|
if (key.escape) {
|
|
114
|
-
|
|
167
|
+
requestHandlers.onAborted('task selection');
|
|
115
168
|
return;
|
|
116
169
|
}
|
|
117
170
|
if (key.downArrow) {
|
|
@@ -167,61 +220,31 @@ export function Schedule({ message, tasks, state, status, debug = DebugLevel.Non
|
|
|
167
220
|
refinedTasks.push(task);
|
|
168
221
|
}
|
|
169
222
|
});
|
|
223
|
+
// Expose final state
|
|
224
|
+
const finalState = {
|
|
225
|
+
highlightedIndex: null,
|
|
226
|
+
currentDefineGroupIndex,
|
|
227
|
+
completedSelections: newCompletedSelections,
|
|
228
|
+
};
|
|
229
|
+
requestHandlers.onCompleted(finalState);
|
|
170
230
|
if (onSelectionConfirmed) {
|
|
171
231
|
// Complete the selection phase - it goes to timeline
|
|
172
232
|
// Callback will create a new Plan showing refined tasks (pending) + Confirm (active)
|
|
173
|
-
lifecycleHandlers
|
|
233
|
+
lifecycleHandlers.completeActive();
|
|
174
234
|
void onSelectionConfirmed(refinedTasks);
|
|
175
235
|
}
|
|
176
236
|
else {
|
|
177
237
|
// No selection callback, just complete normally
|
|
178
|
-
lifecycleHandlers
|
|
238
|
+
lifecycleHandlers.completeActive();
|
|
179
239
|
}
|
|
180
240
|
}
|
|
181
241
|
}
|
|
182
242
|
}, { isActive: isActive && defineTask !== null });
|
|
183
|
-
//
|
|
184
|
-
|
|
185
|
-
useEffect(() => {
|
|
186
|
-
stateHandlers?.updateState({
|
|
187
|
-
highlightedIndex,
|
|
188
|
-
currentDefineGroupIndex,
|
|
189
|
-
completedSelections,
|
|
190
|
-
});
|
|
191
|
-
}, [
|
|
243
|
+
// Controller always renders View, passing current state
|
|
244
|
+
const state = {
|
|
192
245
|
highlightedIndex,
|
|
193
246
|
currentDefineGroupIndex,
|
|
194
247
|
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 }) })] }));
|
|
248
|
+
};
|
|
249
|
+
return (_jsx(ScheduleView, { message: message, tasks: tasks, state: state, status: status, debug: debug }));
|
|
227
250
|
}
|
package/dist/ui/Validate.js
CHANGED
|
@@ -1,23 +1,35 @@
|
|
|
1
1
|
import { jsxs as _jsxs, jsx as _jsx } 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 { TaskType } from '../types/types.js';
|
|
6
|
+
import { saveConfig } from '../configuration/io.js';
|
|
7
|
+
import { unflattenConfig } from '../configuration/transformation.js';
|
|
6
8
|
import { Colors, getTextColor } from '../services/colors.js';
|
|
7
|
-
import {
|
|
8
|
-
import { DebugLevel, saveConfig, unflattenConfig, } from '../services/configuration.js';
|
|
9
|
+
import { createConfigDefinitionWithKeys, createMessage, } from '../services/components.js';
|
|
9
10
|
import { saveConfigLabels } from '../services/config-labels.js';
|
|
10
11
|
import { useInput } from '../services/keyboard.js';
|
|
11
|
-
import { formatErrorMessage } from '../services/messages.js';
|
|
12
|
+
import { formatErrorMessage, getUnresolvedPlaceholdersMessage, } from '../services/messages.js';
|
|
12
13
|
import { ensureMinimumTime } from '../services/timing.js';
|
|
13
|
-
import { Config } from './Config.js';
|
|
14
14
|
import { Spinner } from './Spinner.js';
|
|
15
15
|
const MIN_PROCESSING_TIME = 1000;
|
|
16
|
-
export
|
|
16
|
+
export const ValidateView = ({ state, status }) => {
|
|
17
17
|
const isActive = status === ComponentStatus.Active;
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
const { error, completionMessage } = state;
|
|
19
|
+
// Don't render when not active and nothing to show
|
|
20
|
+
if (!isActive && !completionMessage && !error) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
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] }) }))] }));
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Validate controller: Validates missing config
|
|
27
|
+
*/
|
|
28
|
+
export function Validate({ missingConfig, userRequest, status, service, onError, requestHandlers, onValidationComplete, onAborted, lifecycleHandlers, workflowHandlers, }) {
|
|
29
|
+
const isActive = status === ComponentStatus.Active;
|
|
30
|
+
const [error, setError] = useState(null);
|
|
31
|
+
const [completionMessage, setCompletionMessage] = useState(null);
|
|
32
|
+
const [configRequirements, setConfigRequirements] = useState([]);
|
|
21
33
|
useInput((_, key) => {
|
|
22
34
|
if (key.escape && isActive) {
|
|
23
35
|
onAborted('validation');
|
|
@@ -40,7 +52,7 @@ export function Validate({ missingConfig, userRequest, state, status, service, c
|
|
|
40
52
|
if (mounted) {
|
|
41
53
|
// Add debug components to timeline if present
|
|
42
54
|
if (result.debug?.length) {
|
|
43
|
-
workflowHandlers
|
|
55
|
+
workflowHandlers.addToTimeline(...result.debug);
|
|
44
56
|
}
|
|
45
57
|
// Extract CONFIG tasks with descriptions from result
|
|
46
58
|
const configTasks = result.tasks.filter((task) => task.type === TaskType.Config);
|
|
@@ -57,25 +69,50 @@ export function Validate({ missingConfig, userRequest, state, status, service, c
|
|
|
57
69
|
};
|
|
58
70
|
});
|
|
59
71
|
// Build completion message showing which config properties are needed
|
|
60
|
-
const
|
|
61
|
-
const propertyWord = count === 1 ? 'property' : 'properties';
|
|
62
|
-
// Shuffle between different message variations
|
|
63
|
-
const messages = [
|
|
64
|
-
`Additional configuration ${propertyWord} required.`,
|
|
65
|
-
`Configuration ${propertyWord} needed.`,
|
|
66
|
-
`Missing configuration ${propertyWord} detected.`,
|
|
67
|
-
`Setup requires configuration ${propertyWord}.`,
|
|
68
|
-
];
|
|
69
|
-
const message = messages[Math.floor(Math.random() * messages.length)];
|
|
72
|
+
const message = getUnresolvedPlaceholdersMessage(withDescriptions.length);
|
|
70
73
|
setCompletionMessage(message);
|
|
71
74
|
setConfigRequirements(withDescriptions);
|
|
72
|
-
//
|
|
73
|
-
|
|
75
|
+
// Add validation message to timeline before Config component
|
|
76
|
+
workflowHandlers.addToTimeline(createMessage(message));
|
|
77
|
+
// Create Config component and add to queue
|
|
78
|
+
const keys = withDescriptions.map((req) => req.path);
|
|
79
|
+
const configDef = createConfigDefinitionWithKeys(keys, (config) => {
|
|
80
|
+
// Convert flat dotted keys to nested structure grouped by section
|
|
81
|
+
const configBySection = unflattenConfig(config);
|
|
82
|
+
// Extract and save labels to cache
|
|
83
|
+
const labels = {};
|
|
84
|
+
for (const req of withDescriptions) {
|
|
85
|
+
if (req.description) {
|
|
86
|
+
labels[req.path] = req.description;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
saveConfigLabels(labels);
|
|
90
|
+
// Save each section
|
|
91
|
+
for (const [section, sectionConfig] of Object.entries(configBySection)) {
|
|
92
|
+
saveConfig(section, sectionConfig);
|
|
93
|
+
}
|
|
94
|
+
// After config is saved, invoke callback to add Execute component to queue
|
|
95
|
+
onValidationComplete(withDescriptions);
|
|
96
|
+
}, (operation) => {
|
|
97
|
+
onAborted(operation);
|
|
98
|
+
});
|
|
99
|
+
// Override descriptions with LLM-generated ones
|
|
100
|
+
if ('props' in configDef && 'steps' in configDef.props) {
|
|
101
|
+
configDef.props.steps = configDef.props.steps.map((step, index) => ({
|
|
102
|
+
...step,
|
|
103
|
+
description: withDescriptions[index].description ||
|
|
104
|
+
withDescriptions[index].path,
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
workflowHandlers.addToQueue(configDef);
|
|
108
|
+
lifecycleHandlers.completeActive();
|
|
109
|
+
const finalState = {
|
|
110
|
+
error: null,
|
|
74
111
|
completionMessage: message,
|
|
75
112
|
configRequirements: withDescriptions,
|
|
76
113
|
validated: true,
|
|
77
|
-
|
|
78
|
-
|
|
114
|
+
};
|
|
115
|
+
requestHandlers.onCompleted(finalState);
|
|
79
116
|
}
|
|
80
117
|
}
|
|
81
118
|
catch (err) {
|
|
@@ -83,13 +120,13 @@ export function Validate({ missingConfig, userRequest, state, status, service, c
|
|
|
83
120
|
if (mounted) {
|
|
84
121
|
const errorMessage = formatErrorMessage(err);
|
|
85
122
|
setError(errorMessage);
|
|
86
|
-
|
|
87
|
-
stateHandlers?.updateState({
|
|
123
|
+
const finalState = {
|
|
88
124
|
error: errorMessage,
|
|
89
125
|
completionMessage: null,
|
|
90
|
-
configRequirements:
|
|
126
|
+
configRequirements: [],
|
|
91
127
|
validated: false,
|
|
92
|
-
}
|
|
128
|
+
};
|
|
129
|
+
requestHandlers.onCompleted(finalState);
|
|
93
130
|
onError(errorMessage);
|
|
94
131
|
}
|
|
95
132
|
}
|
|
@@ -103,59 +140,20 @@ export function Validate({ missingConfig, userRequest, state, status, service, c
|
|
|
103
140
|
userRequest,
|
|
104
141
|
isActive,
|
|
105
142
|
service,
|
|
106
|
-
|
|
143
|
+
requestHandlers,
|
|
107
144
|
onError,
|
|
108
145
|
onAborted,
|
|
146
|
+
onValidationComplete,
|
|
147
|
+
lifecycleHandlers,
|
|
148
|
+
workflowHandlers,
|
|
109
149
|
]);
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
// to load current values from config file, then override descriptions
|
|
116
|
-
const configSteps = configRequirements
|
|
117
|
-
? (() => {
|
|
118
|
-
const keys = configRequirements.map((req) => req.path);
|
|
119
|
-
const steps = createConfigStepsFromSchema(keys);
|
|
120
|
-
// Override descriptions with LLM-generated ones
|
|
121
|
-
return steps.map((step, index) => ({
|
|
122
|
-
...step,
|
|
123
|
-
description: configRequirements[index].description ||
|
|
124
|
-
configRequirements[index].path,
|
|
125
|
-
}));
|
|
126
|
-
})()
|
|
127
|
-
: null;
|
|
128
|
-
const handleConfigFinished = (config) => {
|
|
129
|
-
// Convert flat dotted keys to nested structure grouped by section
|
|
130
|
-
const configBySection = unflattenConfig(config);
|
|
131
|
-
// Extract and save labels to cache
|
|
132
|
-
if (configRequirements) {
|
|
133
|
-
const labels = {};
|
|
134
|
-
for (const req of configRequirements) {
|
|
135
|
-
if (req.description) {
|
|
136
|
-
labels[req.path] = req.description;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
saveConfigLabels(labels);
|
|
140
|
-
}
|
|
141
|
-
// Save each section
|
|
142
|
-
for (const [section, sectionConfig] of Object.entries(configBySection)) {
|
|
143
|
-
saveConfig(section, sectionConfig);
|
|
144
|
-
}
|
|
145
|
-
// Mark validation component as complete before invoking callback
|
|
146
|
-
// This allows the workflow to proceed to execution
|
|
147
|
-
lifecycleHandlers?.completeActive();
|
|
148
|
-
// Invoke callback which will queue the Execute component
|
|
149
|
-
if (configRequirements) {
|
|
150
|
-
onComplete(configRequirements);
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
-
const handleConfigAborted = (operation) => {
|
|
154
|
-
// Mark validation component as complete when aborted
|
|
155
|
-
lifecycleHandlers?.completeActive();
|
|
156
|
-
onAborted(operation);
|
|
150
|
+
const state = {
|
|
151
|
+
error,
|
|
152
|
+
completionMessage,
|
|
153
|
+
configRequirements,
|
|
154
|
+
validated: error === null && completionMessage !== null,
|
|
157
155
|
};
|
|
158
|
-
return
|
|
156
|
+
return _jsx(ValidateView, { state: state, status: status });
|
|
159
157
|
}
|
|
160
158
|
/**
|
|
161
159
|
* Build prompt for VALIDATE tool
|