prompt-language-shell 0.5.2 → 0.6.2
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/CONFIG.md +4 -2
- package/dist/config/PLAN.md +23 -0
- package/dist/config/VALIDATE.md +12 -11
- package/dist/services/anthropic.js +49 -4
- package/dist/services/components.js +56 -66
- package/dist/services/configuration.js +169 -13
- package/dist/services/messages.js +22 -0
- package/dist/services/queue.js +2 -2
- package/dist/services/refinement.js +37 -0
- package/dist/services/task-router.js +141 -0
- package/dist/types/types.js +0 -1
- package/dist/ui/Answer.js +18 -27
- package/dist/ui/Command.js +44 -27
- package/dist/ui/Component.js +23 -50
- package/dist/ui/Config.js +77 -55
- 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 +132 -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/Introspect.js
CHANGED
|
@@ -2,6 +2,7 @@ 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
4
|
import { Colors, getTextColor } from '../services/colors.js';
|
|
5
|
+
import { createReportDefinition } from '../services/components.js';
|
|
5
6
|
import { useInput } from '../services/keyboard.js';
|
|
6
7
|
import { formatErrorMessage } from '../services/messages.js';
|
|
7
8
|
import { ensureMinimumTime } from '../services/timing.js';
|
|
@@ -41,26 +42,22 @@ function parseCapabilityFromTask(task) {
|
|
|
41
42
|
isIndirect,
|
|
42
43
|
};
|
|
43
44
|
}
|
|
44
|
-
export function Introspect({ tasks, state, service, children, debug = false,
|
|
45
|
-
|
|
46
|
-
const isCurrent = done === false;
|
|
45
|
+
export function Introspect({ tasks, state, isActive = true, service, children, debug = false, handlers, }) {
|
|
46
|
+
// isActive passed as prop
|
|
47
47
|
const [error, setError] = useState(null);
|
|
48
|
-
const [isLoading, setIsLoading] = useState(state?.isLoading ?? !done);
|
|
49
48
|
useInput((input, key) => {
|
|
50
|
-
if (key.escape &&
|
|
51
|
-
|
|
52
|
-
onAborted();
|
|
49
|
+
if (key.escape && isActive) {
|
|
50
|
+
handlers?.onAborted('introspection');
|
|
53
51
|
}
|
|
54
|
-
}, { isActive
|
|
52
|
+
}, { isActive });
|
|
55
53
|
useEffect(() => {
|
|
56
|
-
// Skip processing if
|
|
57
|
-
if (
|
|
54
|
+
// Skip processing if not active
|
|
55
|
+
if (!isActive) {
|
|
58
56
|
return;
|
|
59
57
|
}
|
|
60
58
|
// Skip processing if no service available
|
|
61
59
|
if (!service) {
|
|
62
60
|
setError('No service available');
|
|
63
|
-
setIsLoading(false);
|
|
64
61
|
return;
|
|
65
62
|
}
|
|
66
63
|
let mounted = true;
|
|
@@ -81,21 +78,27 @@ export function Introspect({ tasks, state, service, children, debug = false, onE
|
|
|
81
78
|
cap.name.toUpperCase() !== 'VALIDATE' &&
|
|
82
79
|
cap.name.toUpperCase() !== 'REPORT');
|
|
83
80
|
}
|
|
84
|
-
|
|
85
|
-
|
|
81
|
+
// Save state before completing
|
|
82
|
+
handlers?.updateState({
|
|
83
|
+
capabilities,
|
|
84
|
+
message: result.message,
|
|
85
|
+
});
|
|
86
|
+
// Add Report component to queue
|
|
87
|
+
handlers?.addToQueue(createReportDefinition(result.message, capabilities));
|
|
88
|
+
// Signal completion
|
|
89
|
+
handlers?.completeActive();
|
|
86
90
|
}
|
|
87
91
|
}
|
|
88
92
|
catch (err) {
|
|
89
93
|
await ensureMinimumTime(startTime, MIN_PROCESSING_TIME);
|
|
90
94
|
if (mounted) {
|
|
91
95
|
const errorMessage = formatErrorMessage(err);
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
96
|
+
setError(errorMessage);
|
|
97
|
+
// Save error state
|
|
98
|
+
handlers?.updateState({
|
|
99
|
+
error: errorMessage,
|
|
100
|
+
});
|
|
101
|
+
handlers?.onError(errorMessage);
|
|
99
102
|
}
|
|
100
103
|
}
|
|
101
104
|
}
|
|
@@ -103,10 +106,10 @@ export function Introspect({ tasks, state, service, children, debug = false, onE
|
|
|
103
106
|
return () => {
|
|
104
107
|
mounted = false;
|
|
105
108
|
};
|
|
106
|
-
}, [tasks,
|
|
109
|
+
}, [tasks, isActive, service, debug, handlers]);
|
|
107
110
|
// Don't render wrapper when done and nothing to show
|
|
108
|
-
if (!
|
|
111
|
+
if (!isActive && !error && !children) {
|
|
109
112
|
return null;
|
|
110
113
|
}
|
|
111
|
-
return (_jsxs(Box, { alignSelf: "flex-start", flexDirection: "column", children: [
|
|
114
|
+
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] }));
|
|
112
115
|
}
|
package/dist/ui/Main.js
CHANGED
|
@@ -1,33 +1,17 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
3
|
import { FeedbackType } from '../types/types.js';
|
|
4
4
|
import { createAnthropicService, } from '../services/anthropic.js';
|
|
5
|
-
import { createCommandDefinition,
|
|
6
|
-
import { getConfigurationRequiredMessage,
|
|
5
|
+
import { createCommandDefinition, createConfigDefinitionWithKeys, createFeedback, createMessage, createWelcomeDefinition, } from '../services/components.js';
|
|
6
|
+
import { getConfigurationRequiredMessage, getMissingConfigKeys, loadConfig, loadDebugSetting, saveAnthropicConfig, saveDebugSetting, } from '../services/configuration.js';
|
|
7
7
|
import { registerGlobalShortcut } from '../services/keyboard.js';
|
|
8
|
-
import {
|
|
9
|
-
import { exitApp } from '../services/process.js';
|
|
10
|
-
import { createAnswerHandlers } from '../handlers/answer.js';
|
|
11
|
-
import { createCommandHandlers } from '../handlers/command.js';
|
|
12
|
-
import { createConfigHandlers } from '../handlers/config.js';
|
|
13
|
-
import { createExecuteHandlers } from '../handlers/execute.js';
|
|
14
|
-
import { createExecutionHandlers } from '../handlers/execution.js';
|
|
15
|
-
import { createIntrospectHandlers } from '../handlers/introspect.js';
|
|
16
|
-
import { createPlanHandlers } from '../handlers/plan.js';
|
|
17
|
-
import { Column } from './Column.js';
|
|
8
|
+
import { Workflow } from './Workflow.js';
|
|
18
9
|
export const Main = ({ app, command }) => {
|
|
19
|
-
const [service, setService] =
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return createAnthropicService(config.anthropic);
|
|
23
|
-
}
|
|
24
|
-
return null;
|
|
25
|
-
});
|
|
26
|
-
const [timeline, setTimeline] = React.useState([]);
|
|
27
|
-
const [queue, setQueue] = React.useState([]);
|
|
28
|
-
const [isDebug, setIsDebug] = React.useState(() => loadDebugSetting());
|
|
10
|
+
const [service, setService] = useState(null);
|
|
11
|
+
const [initialQueue, setInitialQueue] = useState(null);
|
|
12
|
+
const [isDebug, setIsDebug] = useState(() => loadDebugSetting());
|
|
29
13
|
// Register global keyboard shortcuts
|
|
30
|
-
|
|
14
|
+
useEffect(() => {
|
|
31
15
|
registerGlobalShortcut('shift+tab', () => {
|
|
32
16
|
setIsDebug((prev) => {
|
|
33
17
|
const newValue = !prev;
|
|
@@ -36,88 +20,75 @@ export const Main = ({ app, command }) => {
|
|
|
36
20
|
});
|
|
37
21
|
});
|
|
38
22
|
}, []);
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
23
|
+
// Initialize service on mount
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (service !== null) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const missingKeys = getMissingConfigKeys();
|
|
29
|
+
if (missingKeys.length === 0) {
|
|
30
|
+
// Config exists - create service immediately
|
|
31
|
+
try {
|
|
32
|
+
const config = loadConfig();
|
|
33
|
+
const newService = createAnthropicService(config.anthropic);
|
|
34
|
+
setService(newService);
|
|
50
35
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (currentQueue.length === 0)
|
|
58
|
-
return currentQueue;
|
|
59
|
-
const [first] = currentQueue;
|
|
60
|
-
if (!isStateless(first)) {
|
|
61
|
-
addToTimeline(markAsDone(first), createFeedback(FeedbackType.Aborted, getCancellationMessage(operationName)));
|
|
36
|
+
catch (error) {
|
|
37
|
+
// Service creation failed - show error and exit
|
|
38
|
+
const errorMessage = error instanceof Error
|
|
39
|
+
? error.message
|
|
40
|
+
: 'Failed to initialize service';
|
|
41
|
+
setInitialQueue([createFeedback(FeedbackType.Failed, errorMessage)]);
|
|
62
42
|
}
|
|
63
|
-
exitApp(0);
|
|
64
|
-
return [];
|
|
65
|
-
});
|
|
66
|
-
}, [addToTimeline]);
|
|
67
|
-
// Create operations object
|
|
68
|
-
const ops = React.useMemo(() => ({
|
|
69
|
-
addToTimeline,
|
|
70
|
-
setQueue,
|
|
71
|
-
service,
|
|
72
|
-
}), [addToTimeline, service]);
|
|
73
|
-
// Create handlers in dependency order
|
|
74
|
-
const introspectHandlers = React.useMemo(() => createIntrospectHandlers(ops, handleAborted), [ops, handleAborted]);
|
|
75
|
-
const answerHandlers = React.useMemo(() => createAnswerHandlers(ops, handleAborted), [ops, handleAborted]);
|
|
76
|
-
const executeHandlers = React.useMemo(() => createExecuteHandlers(ops, handleAborted), [ops, handleAborted]);
|
|
77
|
-
const executionHandlers = React.useMemo(() => createExecutionHandlers(ops, {
|
|
78
|
-
introspect: introspectHandlers,
|
|
79
|
-
answer: answerHandlers,
|
|
80
|
-
execute: executeHandlers,
|
|
81
|
-
}), [ops, introspectHandlers, answerHandlers, executeHandlers]);
|
|
82
|
-
const planHandlers = React.useMemo(() => createPlanHandlers(ops, handleAborted, executionHandlers), [ops, handleAborted, executionHandlers]);
|
|
83
|
-
const commandHandlers = React.useMemo(() => createCommandHandlers(ops, handleAborted, planHandlers, executionHandlers), [ops, handleAborted, planHandlers, executionHandlers]);
|
|
84
|
-
const configHandlers = React.useMemo(() => createConfigHandlers(ops, handleAborted, command, commandHandlers, setService), [ops, handleAborted, command, commandHandlers]);
|
|
85
|
-
// Initialize queue on mount
|
|
86
|
-
React.useEffect(() => {
|
|
87
|
-
const hasConfig = !!service;
|
|
88
|
-
if (command && hasConfig) {
|
|
89
|
-
setQueue([
|
|
90
|
-
createCommandDefinition(command, service, commandHandlers.onError, commandHandlers.onComplete, commandHandlers.onAborted),
|
|
91
|
-
]);
|
|
92
43
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
44
|
+
// If config is missing, service will be created after config completes
|
|
45
|
+
}, [service]);
|
|
46
|
+
// Initialize queue after service is ready
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
// Only set initial queue once
|
|
49
|
+
if (initialQueue !== null) {
|
|
50
|
+
return;
|
|
98
51
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
52
|
+
const missingKeys = getMissingConfigKeys();
|
|
53
|
+
if (missingKeys.length > 0) {
|
|
54
|
+
// Missing config - show initial configuration flow
|
|
55
|
+
const handleConfigFinished = (config) => {
|
|
56
|
+
// Save config and create service
|
|
57
|
+
try {
|
|
58
|
+
const newConfig = saveAnthropicConfig(config);
|
|
59
|
+
const newService = createAnthropicService(newConfig.anthropic);
|
|
60
|
+
setService(newService);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
// Config creation failed - show error
|
|
64
|
+
const errorMessage = error instanceof Error
|
|
65
|
+
? error.message
|
|
66
|
+
: 'Failed to save configuration';
|
|
67
|
+
setInitialQueue([createFeedback(FeedbackType.Failed, errorMessage)]);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const handleConfigAborted = (operation) => {
|
|
71
|
+
// Config was cancelled - just exit
|
|
72
|
+
};
|
|
73
|
+
setInitialQueue([
|
|
104
74
|
createWelcomeDefinition(app),
|
|
105
|
-
createMessage(getConfigurationRequiredMessage(
|
|
106
|
-
|
|
75
|
+
createMessage(getConfigurationRequiredMessage()),
|
|
76
|
+
createConfigDefinitionWithKeys(missingKeys, handleConfigFinished, handleConfigAborted),
|
|
107
77
|
]);
|
|
108
78
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if (queue.length === 0 && timeline.length > 0) {
|
|
117
|
-
exitApp(0);
|
|
79
|
+
else if (service && command) {
|
|
80
|
+
// Valid service exists and command provided - execute command
|
|
81
|
+
setInitialQueue([createCommandDefinition(command, service)]);
|
|
82
|
+
}
|
|
83
|
+
else if (service && !command) {
|
|
84
|
+
// Valid service exists, no command - show welcome
|
|
85
|
+
setInitialQueue([createWelcomeDefinition(app)]);
|
|
118
86
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
87
|
+
// Wait for service to be initialized before setting queue
|
|
88
|
+
}, [app, command, service, initialQueue]);
|
|
89
|
+
// Don't render until initial queue is ready
|
|
90
|
+
if (initialQueue === null) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
return _jsx(Workflow, { initialQueue: initialQueue, debug: isDebug });
|
|
123
94
|
};
|
package/dist/ui/Message.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
3
|
export const Message = ({ text }) => {
|
|
4
|
-
return (_jsx(Box, { children: _jsx(Text, { children: text }) }));
|
|
4
|
+
return (_jsx(Box, { marginLeft: 1, children: _jsx(Text, { children: text }) }));
|
|
5
5
|
};
|
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?.completeActive();
|
|
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?.completeActive();
|
|
149
|
+
onSelectionConfirmed(refinedTasks);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
// No selection callback, just complete normally
|
|
153
|
+
handlers?.completeActive();
|
|
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 }) {
|