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/Answer.js
CHANGED
|
@@ -1,20 +1,29 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } 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 { useInput } from '../services/keyboard.js';
|
|
7
7
|
import { formatErrorMessage } from '../services/messages.js';
|
|
8
8
|
import { withMinimumTime } from '../services/timing.js';
|
|
9
9
|
import { Spinner } from './Spinner.js';
|
|
10
10
|
const MINIMUM_PROCESSING_TIME = 400;
|
|
11
|
-
export
|
|
11
|
+
export const AnswerView = ({ question, state, status }) => {
|
|
12
|
+
const isActive = status === ComponentStatus.Active;
|
|
13
|
+
const { error, answer } = state;
|
|
14
|
+
const lines = answer ? answer.split('\n') : [];
|
|
15
|
+
return (_jsxs(Box, { alignSelf: "flex-start", flexDirection: "column", children: [isActive && !answer && !error && (_jsxs(Box, { marginLeft: 1, children: [_jsx(Text, { color: getTextColor(isActive), children: "Finding answer. " }), _jsx(Spinner, {})] })), answer && (_jsxs(_Fragment, { children: [_jsx(Box, { marginLeft: 1, marginBottom: 1, children: _jsx(Text, { color: getTextColor(isActive), children: question }) }), _jsx(Box, { flexDirection: "column", paddingLeft: 3, children: lines.map((line, index) => (_jsx(Text, { color: getTextColor(isActive), children: line }, index))) })] })), error && (_jsx(Box, { marginTop: 1, marginLeft: 1, children: _jsxs(Text, { color: Colors.Status.Error, children: ["Error: ", error] }) }))] }));
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Answer controller: Fetches answer from LLM
|
|
19
|
+
*/
|
|
20
|
+
export function Answer({ question, status, service, requestHandlers, lifecycleHandlers, workflowHandlers, }) {
|
|
12
21
|
const isActive = status === ComponentStatus.Active;
|
|
13
22
|
const [error, setError] = useState(null);
|
|
14
|
-
const [answer, setAnswer] = useState(
|
|
23
|
+
const [answer, setAnswer] = useState(null);
|
|
15
24
|
useInput((input, key) => {
|
|
16
25
|
if (key.escape && isActive) {
|
|
17
|
-
|
|
26
|
+
requestHandlers.onAborted('answer');
|
|
18
27
|
}
|
|
19
28
|
}, { isActive });
|
|
20
29
|
useEffect(() => {
|
|
@@ -30,27 +39,32 @@ export function Answer({ question, state, status, service, stateHandlers, lifecy
|
|
|
30
39
|
if (mounted) {
|
|
31
40
|
// Add debug components to timeline if present
|
|
32
41
|
if (result.debug?.length) {
|
|
33
|
-
workflowHandlers
|
|
42
|
+
workflowHandlers.addToTimeline(...result.debug);
|
|
34
43
|
}
|
|
35
44
|
// Extract answer from result
|
|
36
45
|
const answerText = result.answer || '';
|
|
37
46
|
setAnswer(answerText);
|
|
38
|
-
//
|
|
39
|
-
|
|
47
|
+
// Expose final state
|
|
48
|
+
const finalState = {
|
|
40
49
|
answer: answerText,
|
|
41
|
-
|
|
50
|
+
error: null,
|
|
51
|
+
};
|
|
52
|
+
requestHandlers.onCompleted(finalState);
|
|
42
53
|
// Signal completion
|
|
43
|
-
lifecycleHandlers
|
|
54
|
+
lifecycleHandlers.completeActive();
|
|
44
55
|
}
|
|
45
56
|
}
|
|
46
57
|
catch (err) {
|
|
47
58
|
if (mounted) {
|
|
48
59
|
const errorMessage = formatErrorMessage(err);
|
|
49
60
|
setError(errorMessage);
|
|
50
|
-
|
|
61
|
+
// Expose final state with error
|
|
62
|
+
const finalState = {
|
|
51
63
|
error: errorMessage,
|
|
52
|
-
|
|
53
|
-
|
|
64
|
+
answer: null,
|
|
65
|
+
};
|
|
66
|
+
requestHandlers.onCompleted(finalState);
|
|
67
|
+
requestHandlers.onError(errorMessage);
|
|
54
68
|
}
|
|
55
69
|
}
|
|
56
70
|
}
|
|
@@ -58,7 +72,14 @@ export function Answer({ question, state, status, service, stateHandlers, lifecy
|
|
|
58
72
|
return () => {
|
|
59
73
|
mounted = false;
|
|
60
74
|
};
|
|
61
|
-
}, [
|
|
62
|
-
|
|
63
|
-
|
|
75
|
+
}, [
|
|
76
|
+
question,
|
|
77
|
+
isActive,
|
|
78
|
+
service,
|
|
79
|
+
requestHandlers,
|
|
80
|
+
lifecycleHandlers,
|
|
81
|
+
workflowHandlers,
|
|
82
|
+
]);
|
|
83
|
+
const state = { error, answer };
|
|
84
|
+
return _jsx(AnswerView, { question: question, state: state, status: status });
|
|
64
85
|
}
|
package/dist/ui/Command.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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
6
|
import { Colors } from '../services/colors.js';
|
|
7
7
|
import { createScheduleDefinition } from '../services/components.js';
|
|
@@ -13,12 +13,22 @@ import { ensureMinimumTime } from '../services/timing.js';
|
|
|
13
13
|
import { Spinner } from './Spinner.js';
|
|
14
14
|
import { UserQuery } from './UserQuery.js';
|
|
15
15
|
const MIN_PROCESSING_TIME = 400; // purely for visual effect
|
|
16
|
-
export
|
|
16
|
+
export const CommandView = ({ command, state, status }) => {
|
|
17
17
|
const isActive = status === ComponentStatus.Active;
|
|
18
|
-
const
|
|
18
|
+
const { error } = state;
|
|
19
|
+
return (_jsxs(Box, { alignSelf: "flex-start", flexDirection: "column", children: [!isActive ? (_jsxs(UserQuery, { children: ["> pls ", command] })) : (_jsxs(Box, { marginLeft: 1, children: [_jsxs(Text, { color: Colors.Text.Active, children: ["> pls ", command] }), _jsx(Text, { children: " " }), _jsx(Spinner, {})] })), error && (_jsx(Box, { marginTop: 1, marginLeft: 1, children: _jsxs(Text, { color: Colors.Status.Error, children: ["Error: ", error] }) }))] }));
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Command controller: Processes and routes command
|
|
23
|
+
*/
|
|
24
|
+
export function Command({ command, status, service, requestHandlers, lifecycleHandlers, workflowHandlers, onAborted, }) {
|
|
25
|
+
const isActive = status === ComponentStatus.Active;
|
|
26
|
+
const [error, setError] = useState(null);
|
|
27
|
+
const [message, setMessage] = useState(null);
|
|
28
|
+
const [tasks, setTasks] = useState([]);
|
|
19
29
|
useInput((_, key) => {
|
|
20
30
|
if (key.escape && isActive) {
|
|
21
|
-
|
|
31
|
+
requestHandlers.onAborted('request');
|
|
22
32
|
onAborted?.('request');
|
|
23
33
|
}
|
|
24
34
|
}, { isActive });
|
|
@@ -52,38 +62,36 @@ export function Command({ command, state, status, service, stateHandlers, lifecy
|
|
|
52
62
|
? [...scheduleDebug, ...(result.debug || [])]
|
|
53
63
|
: scheduleDebug;
|
|
54
64
|
if (debugComponents.length > 0) {
|
|
55
|
-
workflowHandlers
|
|
65
|
+
workflowHandlers.addToTimeline(...debugComponents);
|
|
56
66
|
}
|
|
57
|
-
//
|
|
58
|
-
|
|
67
|
+
// Update local state
|
|
68
|
+
setMessage(result.message);
|
|
69
|
+
setTasks(result.tasks);
|
|
70
|
+
// Expose final state
|
|
71
|
+
const finalState = {
|
|
72
|
+
error: null,
|
|
59
73
|
message: result.message,
|
|
60
74
|
tasks: result.tasks,
|
|
61
|
-
}
|
|
75
|
+
};
|
|
76
|
+
requestHandlers.onCompleted(finalState);
|
|
62
77
|
// Check if tasks contain DEFINE type (variant selection needed)
|
|
63
78
|
const hasDefineTask = result.tasks.some((task) => task.type === TaskType.Define);
|
|
64
|
-
// Guard: ensure all required handlers are present
|
|
65
|
-
if (!queueHandlers ||
|
|
66
|
-
!lifecycleHandlers ||
|
|
67
|
-
!workflowHandlers ||
|
|
68
|
-
!errorHandlers) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
79
|
// Create Schedule definition
|
|
72
80
|
const scheduleDefinition = createScheduleDefinition(result.message, result.tasks, hasDefineTask
|
|
73
81
|
? async (selectedTasks) => {
|
|
74
82
|
// Refinement flow for DEFINE tasks
|
|
75
|
-
await handleRefinement(selectedTasks, svc, command,
|
|
83
|
+
await handleRefinement(selectedTasks, svc, command, lifecycleHandlers, workflowHandlers, requestHandlers);
|
|
76
84
|
}
|
|
77
85
|
: undefined);
|
|
78
86
|
if (hasDefineTask) {
|
|
79
87
|
// DEFINE tasks: Move Command to timeline, add Schedule to queue
|
|
80
88
|
lifecycleHandlers.completeActive();
|
|
81
|
-
|
|
89
|
+
workflowHandlers.addToQueue(scheduleDefinition);
|
|
82
90
|
}
|
|
83
91
|
else {
|
|
84
92
|
// No DEFINE tasks: Complete Command, then route to Confirm flow
|
|
85
93
|
lifecycleHandlers.completeActive();
|
|
86
|
-
routeTasksWithConfirm(result.tasks, result.message, svc, command,
|
|
94
|
+
routeTasksWithConfirm(result.tasks, result.message, svc, command, lifecycleHandlers, workflowHandlers, requestHandlers, false);
|
|
87
95
|
}
|
|
88
96
|
}
|
|
89
97
|
}
|
|
@@ -92,10 +100,14 @@ export function Command({ command, state, status, service, stateHandlers, lifecy
|
|
|
92
100
|
if (mounted) {
|
|
93
101
|
const errorMessage = formatErrorMessage(err);
|
|
94
102
|
setError(errorMessage);
|
|
95
|
-
|
|
103
|
+
// Expose final state with error
|
|
104
|
+
const finalState = {
|
|
96
105
|
error: errorMessage,
|
|
97
|
-
|
|
98
|
-
|
|
106
|
+
message: null,
|
|
107
|
+
tasks: [],
|
|
108
|
+
};
|
|
109
|
+
requestHandlers.onCompleted(finalState);
|
|
110
|
+
requestHandlers.onError(errorMessage);
|
|
99
111
|
}
|
|
100
112
|
}
|
|
101
113
|
}
|
|
@@ -103,6 +115,14 @@ export function Command({ command, state, status, service, stateHandlers, lifecy
|
|
|
103
115
|
return () => {
|
|
104
116
|
mounted = false;
|
|
105
117
|
};
|
|
106
|
-
}, [
|
|
107
|
-
|
|
118
|
+
}, [
|
|
119
|
+
command,
|
|
120
|
+
isActive,
|
|
121
|
+
service,
|
|
122
|
+
requestHandlers,
|
|
123
|
+
lifecycleHandlers,
|
|
124
|
+
workflowHandlers,
|
|
125
|
+
]);
|
|
126
|
+
const state = { error, message, tasks };
|
|
127
|
+
return _jsx(CommandView, { command: command, state: state, status: status });
|
|
108
128
|
}
|
package/dist/ui/Component.js
CHANGED
|
@@ -1,49 +1,163 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { memo } from 'react';
|
|
3
3
|
import { ComponentName } from '../types/types.js';
|
|
4
|
-
import { Answer } from './Answer.js';
|
|
5
|
-
import { Command } from './Command.js';
|
|
6
|
-
import { Config } from './Config.js';
|
|
7
|
-
import { Confirm } from './Confirm.js';
|
|
4
|
+
import { Answer, AnswerView } from './Answer.js';
|
|
5
|
+
import { Command, CommandView } from './Command.js';
|
|
6
|
+
import { Config, ConfigView } from './Config.js';
|
|
7
|
+
import { Confirm, ConfirmView } from './Confirm.js';
|
|
8
8
|
import { Debug } from './Debug.js';
|
|
9
|
-
import { Execute } from './Execute.js';
|
|
9
|
+
import { Execute, ExecuteView } from './Execute.js';
|
|
10
10
|
import { Feedback } from './Feedback.js';
|
|
11
|
-
import { Introspect } from './Introspect.js';
|
|
11
|
+
import { Introspect, IntrospectView } from './Introspect.js';
|
|
12
12
|
import { Message } from './Message.js';
|
|
13
|
-
import { Refinement } from './Refinement.js';
|
|
13
|
+
import { Refinement, RefinementView } from './Refinement.js';
|
|
14
14
|
import { Report } from './Report.js';
|
|
15
|
-
import { Schedule } from './Schedule.js';
|
|
16
|
-
import { Validate } from './Validate.js';
|
|
15
|
+
import { Schedule, ScheduleView } from './Schedule.js';
|
|
16
|
+
import { Validate, ValidateView } from './Validate.js';
|
|
17
17
|
import { Welcome } from './Welcome.js';
|
|
18
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Render a simple component (no lifecycle management)
|
|
20
|
+
*/
|
|
21
|
+
export const SimpleComponent = memo(function SimpleComponent({ def, }) {
|
|
19
22
|
switch (def.name) {
|
|
23
|
+
case ComponentName.Welcome: {
|
|
24
|
+
const { props, status } = def;
|
|
25
|
+
return _jsx(Welcome, { ...props, status: status });
|
|
26
|
+
}
|
|
27
|
+
case ComponentName.Feedback: {
|
|
28
|
+
const { props, status } = def;
|
|
29
|
+
return _jsx(Feedback, { ...props, status: status });
|
|
30
|
+
}
|
|
31
|
+
case ComponentName.Message: {
|
|
32
|
+
const { props, status } = def;
|
|
33
|
+
return _jsx(Message, { ...props, status: status });
|
|
34
|
+
}
|
|
35
|
+
case ComponentName.Debug: {
|
|
36
|
+
const { props, status } = def;
|
|
37
|
+
return _jsx(Debug, { ...props, status: status });
|
|
38
|
+
}
|
|
39
|
+
case ComponentName.Report: {
|
|
40
|
+
const { props, status } = def;
|
|
41
|
+
return _jsx(Report, { ...props, status: status });
|
|
42
|
+
}
|
|
43
|
+
default:
|
|
44
|
+
throw new Error(`Unknown simple component: ${def.name}`);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
/**
|
|
48
|
+
* Render a managed component (controller with lifecycle management)
|
|
49
|
+
*/
|
|
50
|
+
export const ControllerComponent = memo(function ControllerComponent({ def, debug, requestHandlers, lifecycleHandlers, workflowHandlers, }) {
|
|
51
|
+
switch (def.name) {
|
|
52
|
+
case ComponentName.Config: {
|
|
53
|
+
const { props: { steps, onFinished, onAborted }, status, } = def;
|
|
54
|
+
return (_jsx(Config, { steps: steps, onFinished: onFinished, onAborted: onAborted, requestHandlers: requestHandlers, lifecycleHandlers: lifecycleHandlers, status: status, debug: debug }));
|
|
55
|
+
}
|
|
56
|
+
case ComponentName.Command: {
|
|
57
|
+
const { props: { command, service, onAborted }, status, } = def;
|
|
58
|
+
return (_jsx(Command, { command: command, service: service, onAborted: onAborted, requestHandlers: requestHandlers, lifecycleHandlers: lifecycleHandlers, workflowHandlers: workflowHandlers, status: status }));
|
|
59
|
+
}
|
|
60
|
+
case ComponentName.Schedule: {
|
|
61
|
+
const { props: { message, tasks, onSelectionConfirmed }, status, } = def;
|
|
62
|
+
return (_jsx(Schedule, { message: message, tasks: tasks, onSelectionConfirmed: onSelectionConfirmed, requestHandlers: requestHandlers, lifecycleHandlers: lifecycleHandlers, status: status, debug: debug }));
|
|
63
|
+
}
|
|
64
|
+
case ComponentName.Refinement: {
|
|
65
|
+
const { props: { text, onAborted }, status, } = def;
|
|
66
|
+
return (_jsx(Refinement, { text: text, onAborted: onAborted, requestHandlers: requestHandlers, status: status }));
|
|
67
|
+
}
|
|
68
|
+
case ComponentName.Confirm: {
|
|
69
|
+
const { props: { message, onConfirmed, onCancelled }, status, } = def;
|
|
70
|
+
return (_jsx(Confirm, { message: message, onConfirmed: onConfirmed, onCancelled: onCancelled, requestHandlers: requestHandlers, status: status }));
|
|
71
|
+
}
|
|
72
|
+
case ComponentName.Introspect: {
|
|
73
|
+
const { props: { tasks, service, children }, status, } = def;
|
|
74
|
+
return (_jsx(Introspect, { tasks: tasks, service: service, children: children, requestHandlers: requestHandlers, lifecycleHandlers: lifecycleHandlers, workflowHandlers: workflowHandlers, status: status, debug: debug }));
|
|
75
|
+
}
|
|
76
|
+
case ComponentName.Answer: {
|
|
77
|
+
const { props: { question, service }, status, } = def;
|
|
78
|
+
return (_jsx(Answer, { question: question, service: service, requestHandlers: requestHandlers, lifecycleHandlers: lifecycleHandlers, workflowHandlers: workflowHandlers, status: status }));
|
|
79
|
+
}
|
|
80
|
+
case ComponentName.Validate: {
|
|
81
|
+
const { props: { missingConfig, userRequest, service, onError, onValidationComplete, onAborted, }, status, } = def;
|
|
82
|
+
return (_jsx(Validate, { missingConfig: missingConfig, userRequest: userRequest, service: service, onError: onError, onValidationComplete: onValidationComplete, onAborted: onAborted, requestHandlers: requestHandlers, lifecycleHandlers: lifecycleHandlers, workflowHandlers: workflowHandlers, status: status }));
|
|
83
|
+
}
|
|
84
|
+
case ComponentName.Execute: {
|
|
85
|
+
const { props: { tasks, service }, status, } = def;
|
|
86
|
+
return (_jsx(Execute, { tasks: tasks, service: service, requestHandlers: requestHandlers, lifecycleHandlers: lifecycleHandlers, workflowHandlers: workflowHandlers, status: status }));
|
|
87
|
+
}
|
|
88
|
+
default:
|
|
89
|
+
throw new Error(`Unknown managed component: ${def.name}`);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
/**
|
|
93
|
+
* Render a managed component as View only (no Controller logic)
|
|
94
|
+
*/
|
|
95
|
+
export const ViewComponent = memo(function ViewComponent({ def, }) {
|
|
96
|
+
switch (def.name) {
|
|
97
|
+
case ComponentName.Confirm: {
|
|
98
|
+
const { props: { message }, state, status, } = def;
|
|
99
|
+
return _jsx(ConfirmView, { message: message, state: state, status: status });
|
|
100
|
+
}
|
|
101
|
+
case ComponentName.Config: {
|
|
102
|
+
const { props: { steps }, state, status, } = def;
|
|
103
|
+
return _jsx(ConfigView, { steps: steps, state: state, status: status });
|
|
104
|
+
}
|
|
105
|
+
case ComponentName.Schedule: {
|
|
106
|
+
const { props: { message, tasks }, state, status, } = def;
|
|
107
|
+
return (_jsx(ScheduleView, { message: message, tasks: tasks, state: state, status: status }));
|
|
108
|
+
}
|
|
109
|
+
case ComponentName.Execute: {
|
|
110
|
+
const { props: { tasks }, state, status, } = def;
|
|
111
|
+
return _jsx(ExecuteView, { tasks: tasks, state: state, status: status });
|
|
112
|
+
}
|
|
113
|
+
case ComponentName.Answer: {
|
|
114
|
+
const { props: { question }, state, status, } = def;
|
|
115
|
+
return _jsx(AnswerView, { question: question, state: state, status: status });
|
|
116
|
+
}
|
|
117
|
+
case ComponentName.Command: {
|
|
118
|
+
const { props: { command }, state, status, } = def;
|
|
119
|
+
return _jsx(CommandView, { command: command, state: state, status: status });
|
|
120
|
+
}
|
|
121
|
+
case ComponentName.Introspect: {
|
|
122
|
+
const { props: { children }, state, status, } = def;
|
|
123
|
+
return (_jsx(IntrospectView, { state: state, status: status, children: children }));
|
|
124
|
+
}
|
|
125
|
+
case ComponentName.Validate: {
|
|
126
|
+
const { state, status } = def;
|
|
127
|
+
return _jsx(ValidateView, { state: state, status: status });
|
|
128
|
+
}
|
|
129
|
+
case ComponentName.Refinement: {
|
|
130
|
+
const { props: { text }, status, } = def;
|
|
131
|
+
return _jsx(RefinementView, { text: text, status: status });
|
|
132
|
+
}
|
|
133
|
+
default:
|
|
134
|
+
throw new Error(`Unknown managed component: ${def.name}`);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
/**
|
|
138
|
+
* Render a component in the timeline (Views only for managed, as-is for simple)
|
|
139
|
+
*/
|
|
140
|
+
export const TimelineComponent = ({ def, }) => {
|
|
141
|
+
switch (def.name) {
|
|
142
|
+
// Simple components render as-is
|
|
20
143
|
case ComponentName.Welcome:
|
|
21
|
-
return _jsx(Welcome, { ...def.props, status: def.status });
|
|
22
|
-
case ComponentName.Config:
|
|
23
|
-
return (_jsx(Config, { ...def.props, state: def.state, status: def.status, debug: debug }));
|
|
24
|
-
case ComponentName.Command:
|
|
25
|
-
return _jsx(Command, { ...def.props, state: def.state, status: def.status });
|
|
26
|
-
case ComponentName.Schedule:
|
|
27
|
-
return (_jsx(Schedule, { ...def.props, state: def.state, status: def.status, debug: debug }));
|
|
28
144
|
case ComponentName.Feedback:
|
|
29
|
-
return _jsx(Feedback, { ...def.props, status: def.status });
|
|
30
145
|
case ComponentName.Message:
|
|
31
|
-
return _jsx(Message, { ...def.props, status: def.status });
|
|
32
146
|
case ComponentName.Debug:
|
|
33
|
-
return _jsx(Debug, { ...def.props, status: def.status });
|
|
34
|
-
case ComponentName.Refinement:
|
|
35
|
-
return (_jsx(Refinement, { ...def.props, state: def.state, status: def.status }));
|
|
36
|
-
case ComponentName.Confirm:
|
|
37
|
-
return _jsx(Confirm, { ...def.props, state: def.state, status: def.status });
|
|
38
|
-
case ComponentName.Introspect:
|
|
39
|
-
return (_jsx(Introspect, { ...def.props, state: def.state, status: def.status, debug: debug }));
|
|
40
147
|
case ComponentName.Report:
|
|
41
|
-
return _jsx(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
case ComponentName.
|
|
45
|
-
|
|
148
|
+
return _jsx(SimpleComponent, { def: def });
|
|
149
|
+
// Managed components render as Views
|
|
150
|
+
case ComponentName.Config:
|
|
151
|
+
case ComponentName.Command:
|
|
152
|
+
case ComponentName.Confirm:
|
|
153
|
+
case ComponentName.Schedule:
|
|
154
|
+
case ComponentName.Refinement:
|
|
46
155
|
case ComponentName.Validate:
|
|
47
|
-
|
|
156
|
+
case ComponentName.Execute:
|
|
157
|
+
case ComponentName.Answer:
|
|
158
|
+
case ComponentName.Introspect:
|
|
159
|
+
return _jsx(ViewComponent, { def: def });
|
|
160
|
+
default:
|
|
161
|
+
throw new Error('Unknown component type');
|
|
48
162
|
}
|
|
49
|
-
}
|
|
163
|
+
};
|
package/dist/ui/Config.js
CHANGED
|
@@ -6,7 +6,7 @@ import { ComponentStatus } from '../types/components.js';
|
|
|
6
6
|
import { FeedbackType } from '../types/types.js';
|
|
7
7
|
import { Colors } from '../services/colors.js';
|
|
8
8
|
import { createFeedback } from '../services/components.js';
|
|
9
|
-
import { DebugLevel } from '../
|
|
9
|
+
import { DebugLevel } from '../configuration/types.js';
|
|
10
10
|
import { useInput } from '../services/keyboard.js';
|
|
11
11
|
/**
|
|
12
12
|
* Get postfix with debug brackets if debug is enabled
|
|
@@ -78,16 +78,52 @@ function SelectionStep({ options, selectedIndex, isCurrentStep, }) {
|
|
|
78
78
|
return (_jsx(Box, { marginRight: 2, children: _jsx(Text, { dimColor: !isSelected || !isCurrentStep, bold: isSelected, children: option.label }) }, option.value));
|
|
79
79
|
}) }));
|
|
80
80
|
}
|
|
81
|
+
export const ConfigView = ({ steps, state, status, debug = DebugLevel.None, onInputChange, onInputSubmit, }) => {
|
|
82
|
+
const isActive = status === ComponentStatus.Active;
|
|
83
|
+
const { values, completedStep, selectedIndex } = state;
|
|
84
|
+
const renderStepInput = (stepConfig, isCurrentStep) => {
|
|
85
|
+
const configKey = stepConfig.path || stepConfig.key;
|
|
86
|
+
const displayValue = values[configKey];
|
|
87
|
+
switch (stepConfig.type) {
|
|
88
|
+
case StepType.Text:
|
|
89
|
+
if (isCurrentStep && onInputChange && onInputSubmit) {
|
|
90
|
+
return (_jsx(TextStep, { value: values[configKey] || '', placeholder: stepConfig.value || undefined, validate: stepConfig.validate, onChange: onInputChange, onSubmit: onInputSubmit }));
|
|
91
|
+
}
|
|
92
|
+
return (_jsx(Text, { dimColor: true, wrap: "truncate-end", children: displayValue || '' }));
|
|
93
|
+
case StepType.Selection: {
|
|
94
|
+
if (!isCurrentStep) {
|
|
95
|
+
const option = stepConfig.options.find((opt) => opt.value === displayValue);
|
|
96
|
+
return _jsx(Text, { dimColor: true, children: option?.label || '' });
|
|
97
|
+
}
|
|
98
|
+
return (_jsx(SelectionStep, { options: stepConfig.options, selectedIndex: selectedIndex, isCurrentStep: true }));
|
|
99
|
+
}
|
|
100
|
+
default: {
|
|
101
|
+
const _exhaustiveCheck = stepConfig;
|
|
102
|
+
throw new Error('Unsupported step type');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
return (_jsx(Box, { flexDirection: "column", marginLeft: 1, children: steps.map((stepConfig, index) => {
|
|
107
|
+
const isCurrentStep = index === completedStep && isActive;
|
|
108
|
+
const isCompleted = index < completedStep;
|
|
109
|
+
const wasAborted = index === completedStep && !isActive;
|
|
110
|
+
const shouldShow = isCompleted || isCurrentStep || wasAborted;
|
|
111
|
+
if (!shouldShow) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
const postfix = getPostfix(stepConfig.path, debug);
|
|
115
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: index === 0 ? 0 : 1, children: [_jsxs(Box, { children: [_jsx(Text, { children: stepConfig.description }), _jsx(Text, { children: ": " }), postfix && _jsx(Text, { color: Colors.Type.Config, children: postfix })] }), _jsxs(Box, { children: [_jsx(Text, { children: " " }), _jsx(Text, { color: Colors.Action.Select, dimColor: !isCurrentStep, children: ">" }), _jsx(Text, { children: " " }), renderStepInput(stepConfig, isCurrentStep)] })] }, stepConfig.path || stepConfig.key));
|
|
116
|
+
}) }));
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Config controller: Multi-step wizard logic
|
|
120
|
+
*/
|
|
81
121
|
export function Config(props) {
|
|
82
|
-
const { steps,
|
|
122
|
+
const { steps, status, debug = DebugLevel.None, requestHandlers, lifecycleHandlers, onFinished, onAborted, } = props;
|
|
83
123
|
const isActive = status === ComponentStatus.Active;
|
|
84
|
-
const [step, setStep] = useState(
|
|
124
|
+
const [step, setStep] = useState(0);
|
|
85
125
|
const [values, setValues] = useState(() => {
|
|
86
|
-
//
|
|
87
|
-
if (!isActive && state?.values) {
|
|
88
|
-
return state.values;
|
|
89
|
-
}
|
|
90
|
-
// Otherwise initialize from step defaults
|
|
126
|
+
// Initialize from step defaults
|
|
91
127
|
const initial = {};
|
|
92
128
|
steps.forEach((stepConfig) => {
|
|
93
129
|
// Use full path if available, otherwise use key
|
|
@@ -112,26 +148,14 @@ export function Config(props) {
|
|
|
112
148
|
});
|
|
113
149
|
const [inputValue, setInputValue] = useState(() => {
|
|
114
150
|
// Initialize with the current step's value if available
|
|
115
|
-
if (
|
|
151
|
+
if (step < steps.length) {
|
|
116
152
|
const stepConfig = steps[step];
|
|
117
153
|
const configKey = stepConfig.path || stepConfig.key;
|
|
118
154
|
return values[configKey] || '';
|
|
119
155
|
}
|
|
120
156
|
return '';
|
|
121
157
|
});
|
|
122
|
-
const [selectedIndex, setSelectedIndex] = useState(
|
|
123
|
-
// If not active, use saved state
|
|
124
|
-
if (!isActive && state?.selectedIndex !== undefined) {
|
|
125
|
-
return state.selectedIndex;
|
|
126
|
-
}
|
|
127
|
-
// Initialize selectedIndex based on current step's defaultIndex
|
|
128
|
-
if (isActive &&
|
|
129
|
-
step < steps.length &&
|
|
130
|
-
steps[step].type === StepType.Selection) {
|
|
131
|
-
return steps[step].defaultIndex;
|
|
132
|
-
}
|
|
133
|
-
return 0;
|
|
134
|
-
});
|
|
158
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
135
159
|
const normalizeValue = (value) => {
|
|
136
160
|
if (value === null || value === undefined) {
|
|
137
161
|
return '';
|
|
@@ -167,20 +191,25 @@ export function Config(props) {
|
|
|
167
191
|
throw new Error('Unsupported step type');
|
|
168
192
|
}
|
|
169
193
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
//
|
|
174
|
-
|
|
175
|
-
values,
|
|
194
|
+
const finalValues = currentValue
|
|
195
|
+
? { ...values, [configKey]: currentValue }
|
|
196
|
+
: values;
|
|
197
|
+
// Expose final state
|
|
198
|
+
const finalState = {
|
|
199
|
+
values: finalValues,
|
|
176
200
|
completedStep: step,
|
|
177
201
|
selectedIndex,
|
|
178
|
-
}
|
|
202
|
+
};
|
|
203
|
+
requestHandlers.onCompleted(finalState);
|
|
204
|
+
// Abort configuration
|
|
179
205
|
if (onAborted) {
|
|
206
|
+
// Let Workflow handler complete and add feedback
|
|
180
207
|
onAborted('configuration');
|
|
181
208
|
}
|
|
182
|
-
|
|
183
|
-
|
|
209
|
+
else {
|
|
210
|
+
// Fallback: complete with abort feedback directly
|
|
211
|
+
lifecycleHandlers.completeActive(createFeedback(FeedbackType.Aborted, 'Configuration cancelled.'));
|
|
212
|
+
}
|
|
184
213
|
return;
|
|
185
214
|
}
|
|
186
215
|
// Handle selection step navigation
|
|
@@ -192,7 +221,7 @@ export function Config(props) {
|
|
|
192
221
|
handleSubmit(currentStepConfig.options[selectedIndex].value);
|
|
193
222
|
}
|
|
194
223
|
}
|
|
195
|
-
});
|
|
224
|
+
}, { isActive });
|
|
196
225
|
const handleSubmit = (value) => {
|
|
197
226
|
const currentStepConfig = steps[step];
|
|
198
227
|
let finalValue = '';
|
|
@@ -230,37 +259,29 @@ export function Config(props) {
|
|
|
230
259
|
setInputValue('');
|
|
231
260
|
if (step === steps.length - 1) {
|
|
232
261
|
// Last step completed
|
|
233
|
-
//
|
|
234
|
-
|
|
235
|
-
const stateUpdate = {
|
|
262
|
+
// Expose final state
|
|
263
|
+
const finalState = {
|
|
236
264
|
values: newValues,
|
|
237
265
|
completedStep: steps.length,
|
|
238
266
|
selectedIndex,
|
|
239
267
|
};
|
|
240
|
-
|
|
268
|
+
requestHandlers.onCompleted(finalState);
|
|
241
269
|
// Call onFinished callback and handle result
|
|
242
270
|
try {
|
|
243
271
|
if (onFinished) {
|
|
244
272
|
onFinished(newValues);
|
|
245
273
|
}
|
|
246
274
|
// Success - complete with success feedback
|
|
247
|
-
lifecycleHandlers
|
|
275
|
+
lifecycleHandlers.completeActive(createFeedback(FeedbackType.Succeeded, 'Configuration saved successfully.'));
|
|
248
276
|
}
|
|
249
277
|
catch (error) {
|
|
250
278
|
// Failure - complete with error feedback
|
|
251
279
|
const errorMessage = error instanceof Error ? error.message : 'Configuration failed';
|
|
252
|
-
lifecycleHandlers
|
|
280
|
+
lifecycleHandlers.completeActive(createFeedback(FeedbackType.Failed, errorMessage));
|
|
253
281
|
}
|
|
254
282
|
setStep(steps.length);
|
|
255
283
|
}
|
|
256
284
|
else {
|
|
257
|
-
// Save state after each step
|
|
258
|
-
const stateUpdate = {
|
|
259
|
-
values: newValues,
|
|
260
|
-
completedStep: step + 1,
|
|
261
|
-
selectedIndex,
|
|
262
|
-
};
|
|
263
|
-
stateHandlers?.updateState(stateUpdate);
|
|
264
285
|
const nextStep = step + 1;
|
|
265
286
|
setStep(nextStep);
|
|
266
287
|
// Reset selectedIndex for next step
|
|
@@ -270,39 +291,12 @@ export function Config(props) {
|
|
|
270
291
|
}
|
|
271
292
|
}
|
|
272
293
|
};
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
if (isCurrentStep) {
|
|
280
|
-
return (_jsx(TextStep, { value: inputValue, placeholder: stepConfig.value || undefined, validate: stepConfig.validate, onChange: setInputValue, onSubmit: handleSubmit }));
|
|
281
|
-
}
|
|
282
|
-
return (_jsx(Text, { dimColor: true, wrap: "truncate-end", children: displayValue || '' }));
|
|
283
|
-
case StepType.Selection: {
|
|
284
|
-
if (!isCurrentStep) {
|
|
285
|
-
// Find the option that matches the saved/current value
|
|
286
|
-
const option = stepConfig.options.find((opt) => opt.value === displayValue);
|
|
287
|
-
return _jsx(Text, { dimColor: true, children: option?.label || '' });
|
|
288
|
-
}
|
|
289
|
-
return (_jsx(SelectionStep, { options: stepConfig.options, selectedIndex: selectedIndex, isCurrentStep: true }));
|
|
290
|
-
}
|
|
291
|
-
default: {
|
|
292
|
-
const _exhaustiveCheck = stepConfig;
|
|
293
|
-
throw new Error('Unsupported step type');
|
|
294
|
-
}
|
|
295
|
-
}
|
|
294
|
+
// Build current state for View
|
|
295
|
+
// Controller always renders View, passing current state and callbacks
|
|
296
|
+
const state = {
|
|
297
|
+
values,
|
|
298
|
+
completedStep: step,
|
|
299
|
+
selectedIndex,
|
|
296
300
|
};
|
|
297
|
-
return (_jsx(
|
|
298
|
-
const isCurrentStep = index === step && isActive;
|
|
299
|
-
const isCompleted = index < step;
|
|
300
|
-
const wasAborted = index === step && !isActive;
|
|
301
|
-
const shouldShow = isCompleted || isCurrentStep || wasAborted;
|
|
302
|
-
if (!shouldShow) {
|
|
303
|
-
return null;
|
|
304
|
-
}
|
|
305
|
-
const postfix = getPostfix(stepConfig.path, debug);
|
|
306
|
-
return (_jsxs(Box, { flexDirection: "column", marginTop: index === 0 ? 0 : 1, children: [_jsxs(Box, { children: [_jsx(Text, { children: stepConfig.description }), _jsx(Text, { children: ": " }), postfix && _jsx(Text, { color: Colors.Type.Config, children: postfix })] }), _jsxs(Box, { children: [_jsx(Text, { children: " " }), _jsx(Text, { color: Colors.Action.Select, dimColor: !isCurrentStep, children: ">" }), _jsx(Text, { children: " " }), renderStepInput(stepConfig, isCurrentStep)] })] }, stepConfig.path || stepConfig.key));
|
|
307
|
-
}) }));
|
|
301
|
+
return (_jsx(ConfigView, { steps: steps, state: state, status: status, debug: debug, onInputChange: setInputValue, onInputSubmit: handleSubmit }));
|
|
308
302
|
}
|