prompt-language-shell 0.9.0 → 0.9.4
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/{ui/Main.js → Main.js} +24 -17
- package/dist/{ui → components}/Component.js +31 -26
- package/dist/{ui → components}/Workflow.js +23 -7
- package/dist/{ui → components/controllers}/Answer.js +18 -17
- package/dist/{ui → components/controllers}/Command.js +21 -24
- package/dist/{ui → components/controllers}/Config.js +17 -119
- package/dist/components/controllers/Confirm.js +42 -0
- package/dist/components/controllers/Execute.js +288 -0
- package/dist/{ui → components/controllers}/Introspect.js +22 -39
- package/dist/components/controllers/Refinement.js +18 -0
- package/dist/{ui → components/controllers}/Schedule.js +8 -124
- package/dist/{ui → components/controllers}/Validate.js +37 -50
- package/dist/components/views/Answer.js +28 -0
- package/dist/components/views/Command.js +11 -0
- package/dist/components/views/Config.js +115 -0
- package/dist/components/views/Confirm.js +24 -0
- package/dist/components/views/Execute.js +60 -0
- package/dist/{ui → components/views}/Feedback.js +3 -3
- package/dist/components/views/Introspect.js +17 -0
- package/dist/{ui → components/views}/Label.js +3 -3
- package/dist/{ui → components/views}/List.js +3 -3
- package/dist/{ui → components/views}/Output.js +2 -2
- package/dist/components/views/Refinement.js +9 -0
- package/dist/{ui → components/views}/Report.js +1 -1
- package/dist/components/views/Schedule.js +120 -0
- package/dist/{ui → components/views}/Separator.js +1 -1
- package/dist/{ui → components/views}/Spinner.js +1 -1
- package/dist/{ui → components/views}/Subtask.js +10 -7
- package/dist/components/views/Task.js +18 -0
- package/dist/components/views/Upcoming.js +30 -0
- package/dist/{ui → components/views}/UserQuery.js +1 -1
- package/dist/components/views/Validate.js +17 -0
- package/dist/{ui → components/views}/Welcome.js +1 -1
- package/dist/{services/config-labels.js → configuration/labels.js} +1 -1
- package/dist/configuration/schema.js +2 -2
- package/dist/configuration/steps.js +171 -0
- package/dist/configuration/transformation.js +17 -0
- package/dist/execution/handlers.js +20 -60
- package/dist/execution/processing.js +3 -1
- package/dist/execution/reducer.js +34 -44
- package/dist/execution/runner.js +99 -0
- package/dist/execution/types.js +4 -4
- package/dist/execution/utils.js +23 -1
- package/dist/index.js +1 -1
- package/dist/services/components.js +109 -394
- package/dist/services/logger.js +3 -3
- package/dist/services/messages.js +19 -0
- package/dist/services/refinement.js +5 -2
- package/dist/services/router.js +136 -55
- package/dist/services/shell.js +26 -6
- package/dist/services/timing.js +1 -0
- package/dist/skills/execute.md +40 -14
- package/dist/tools/execute.tool.js +0 -4
- package/dist/types/schemas.js +0 -1
- package/package.json +1 -1
- package/dist/parser.js +0 -13
- package/dist/services/config-utils.js +0 -20
- package/dist/ui/Confirm.js +0 -62
- package/dist/ui/Execute.js +0 -294
- package/dist/ui/Refinement.js +0 -23
- package/dist/ui/Task.js +0 -175
- /package/dist/{ui → components/views}/Debug.js +0 -0
- /package/dist/{ui → components/views}/Message.js +0 -0
- /package/dist/{ui → components/views}/Panel.js +0 -0
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useState } from 'react';
|
|
3
|
-
import { DebugLevel } from '
|
|
4
|
-
import { FeedbackType } from '
|
|
5
|
-
import { loadConfig, loadDebugSetting, saveConfig, saveDebugSetting, } from '
|
|
6
|
-
import { getConfigurationRequiredMessage } from '
|
|
7
|
-
import { getMissingConfigKeys } from '
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
3
|
+
import { DebugLevel } from './configuration/types.js';
|
|
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 { createConfigStepsFromSchema } from './configuration/steps.js';
|
|
9
|
+
import { unflattenConfig } from './configuration/transformation.js';
|
|
10
|
+
import { createAnthropicService } from './services/anthropic.js';
|
|
11
|
+
import { createCommand, createConfig, createFeedback, createMessage, createWelcome, } from './services/components.js';
|
|
12
|
+
import { registerGlobalShortcut } from './services/keyboard.js';
|
|
13
|
+
import { initializeLogger, setDebugLevel } from './services/logger.js';
|
|
14
|
+
import { Workflow } from './components/Workflow.js';
|
|
14
15
|
export const Main = ({ app, command, serviceFactory = createAnthropicService, }) => {
|
|
15
16
|
const [service, setService] = useState(null);
|
|
16
17
|
const [initialQueue, setInitialQueue] = useState(null);
|
|
@@ -56,7 +57,9 @@ export const Main = ({ app, command, serviceFactory = createAnthropicService, })
|
|
|
56
57
|
const errorMessage = error instanceof Error
|
|
57
58
|
? error.message
|
|
58
59
|
: 'Failed to initialize service';
|
|
59
|
-
setInitialQueue([
|
|
60
|
+
setInitialQueue([
|
|
61
|
+
createFeedback({ type: FeedbackType.Failed, message: errorMessage }),
|
|
62
|
+
]);
|
|
60
63
|
}
|
|
61
64
|
}
|
|
62
65
|
// If config is missing, service will be created after config completes
|
|
@@ -94,18 +97,22 @@ export const Main = ({ app, command, serviceFactory = createAnthropicService, })
|
|
|
94
97
|
// Config was cancelled
|
|
95
98
|
};
|
|
96
99
|
setInitialQueue([
|
|
97
|
-
|
|
98
|
-
createMessage(getConfigurationRequiredMessage()),
|
|
99
|
-
|
|
100
|
+
createWelcome({ app }),
|
|
101
|
+
createMessage({ text: getConfigurationRequiredMessage() }),
|
|
102
|
+
createConfig({
|
|
103
|
+
steps: createConfigStepsFromSchema(missingKeys),
|
|
104
|
+
onFinished: handleConfigFinished,
|
|
105
|
+
onAborted: handleConfigAborted,
|
|
106
|
+
}),
|
|
100
107
|
]);
|
|
101
108
|
}
|
|
102
109
|
else if (service && command) {
|
|
103
110
|
// Valid service exists and command provided - execute command
|
|
104
|
-
setInitialQueue([
|
|
111
|
+
setInitialQueue([createCommand({ command, service })]);
|
|
105
112
|
}
|
|
106
113
|
else if (service && !command) {
|
|
107
114
|
// Valid service exists, no command - show welcome
|
|
108
|
-
setInitialQueue([
|
|
115
|
+
setInitialQueue([createWelcome({ app })]);
|
|
109
116
|
}
|
|
110
117
|
// Wait for service to be initialized before setting queue
|
|
111
118
|
}, [app, command, service, initialQueue]);
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { memo } from 'react';
|
|
3
|
+
import { ComponentStatus, } from '../types/components.js';
|
|
3
4
|
import { ComponentName } from '../types/types.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
|
-
import { Debug } from './Debug.js';
|
|
9
|
-
import { Execute, ExecuteView } from './Execute.js';
|
|
10
|
-
import { Feedback } from './Feedback.js';
|
|
11
|
-
import { Introspect, IntrospectView } from './Introspect.js';
|
|
12
|
-
import { Message } from './Message.js';
|
|
13
|
-
import { Refinement, RefinementView } from './Refinement.js';
|
|
14
|
-
import { Report } from './Report.js';
|
|
15
|
-
import { Schedule, ScheduleView } from './Schedule.js';
|
|
16
|
-
import { Validate, ValidateView } from './Validate.js';
|
|
17
|
-
import { Welcome } from './Welcome.js';
|
|
5
|
+
import { Answer, AnswerView } from './controllers/Answer.js';
|
|
6
|
+
import { Command, CommandView } from './controllers/Command.js';
|
|
7
|
+
import { Config, ConfigView } from './controllers/Config.js';
|
|
8
|
+
import { Confirm, ConfirmView } from './controllers/Confirm.js';
|
|
9
|
+
import { Debug } from './views/Debug.js';
|
|
10
|
+
import { Execute, ExecuteView, mapStateToViewProps, } from './controllers/Execute.js';
|
|
11
|
+
import { Feedback } from './views/Feedback.js';
|
|
12
|
+
import { Introspect, IntrospectView } from './controllers/Introspect.js';
|
|
13
|
+
import { Message } from './views/Message.js';
|
|
14
|
+
import { Refinement, RefinementView } from './controllers/Refinement.js';
|
|
15
|
+
import { Report } from './views/Report.js';
|
|
16
|
+
import { Schedule, ScheduleView } from './controllers/Schedule.js';
|
|
17
|
+
import { Validate, ValidateView } from './controllers/Validate.js';
|
|
18
|
+
import { Welcome } from './views/Welcome.js';
|
|
18
19
|
/**
|
|
19
20
|
* Render a simple component (no lifecycle management)
|
|
20
21
|
*/
|
|
@@ -74,16 +75,16 @@ export const ControllerComponent = memo(function ControllerComponent({ def, debu
|
|
|
74
75
|
return (_jsx(Introspect, { tasks: tasks, service: service, children: children, requestHandlers: requestHandlers, lifecycleHandlers: lifecycleHandlers, workflowHandlers: workflowHandlers, status: status, debug: debug }));
|
|
75
76
|
}
|
|
76
77
|
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 }));
|
|
78
|
+
const { props: { question, service, upcoming }, status, } = def;
|
|
79
|
+
return (_jsx(Answer, { question: question, service: service, upcoming: upcoming, requestHandlers: requestHandlers, lifecycleHandlers: lifecycleHandlers, workflowHandlers: workflowHandlers, status: status }));
|
|
79
80
|
}
|
|
80
81
|
case ComponentName.Validate: {
|
|
81
82
|
const { props: { missingConfig, userRequest, service, onError, onValidationComplete, onAborted, }, status, } = def;
|
|
82
83
|
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
|
}
|
|
84
85
|
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 }));
|
|
86
|
+
const { props: { tasks, service, upcoming, label }, status, } = def;
|
|
87
|
+
return (_jsx(Execute, { tasks: tasks, service: service, upcoming: upcoming, label: label, requestHandlers: requestHandlers, lifecycleHandlers: lifecycleHandlers, workflowHandlers: workflowHandlers, status: status }));
|
|
87
88
|
}
|
|
88
89
|
default:
|
|
89
90
|
throw new Error(`Unknown managed component: ${def.name}`);
|
|
@@ -96,7 +97,7 @@ export const ViewComponent = memo(function ViewComponent({ def, }) {
|
|
|
96
97
|
switch (def.name) {
|
|
97
98
|
case ComponentName.Confirm: {
|
|
98
99
|
const { props: { message }, state, status, } = def;
|
|
99
|
-
return _jsx(ConfirmView, {
|
|
100
|
+
return (_jsx(ConfirmView, { status: status, message: message, selectedIndex: state.selectedIndex }));
|
|
100
101
|
}
|
|
101
102
|
case ComponentName.Config: {
|
|
102
103
|
const { props: { steps }, state, status, } = def;
|
|
@@ -104,15 +105,18 @@ export const ViewComponent = memo(function ViewComponent({ def, }) {
|
|
|
104
105
|
}
|
|
105
106
|
case ComponentName.Schedule: {
|
|
106
107
|
const { props: { message, tasks }, state, status, } = def;
|
|
107
|
-
return (_jsx(ScheduleView, { message: message, tasks: tasks, state: state,
|
|
108
|
+
return (_jsx(ScheduleView, { status: status, message: message, tasks: tasks, highlightedIndex: state.highlightedIndex, currentDefineGroupIndex: state.currentDefineGroupIndex, completedSelections: state.completedSelections }));
|
|
108
109
|
}
|
|
109
110
|
case ComponentName.Execute: {
|
|
110
|
-
const { state, status } = def;
|
|
111
|
-
|
|
111
|
+
const { props: { upcoming, label }, state, status, } = def;
|
|
112
|
+
const isActive = status === ComponentStatus.Active;
|
|
113
|
+
const viewProps = mapStateToViewProps(state, isActive, upcoming, label);
|
|
114
|
+
return _jsx(ExecuteView, { ...viewProps });
|
|
112
115
|
}
|
|
113
116
|
case ComponentName.Answer: {
|
|
114
|
-
const { props: { question }, state, status, } = def;
|
|
115
|
-
|
|
117
|
+
const { props: { question, upcoming }, state, status, } = def;
|
|
118
|
+
const lines = state.answer ? state.answer.split('\n') : null;
|
|
119
|
+
return (_jsx(AnswerView, { status: status, question: question, lines: lines, error: state.error, upcoming: upcoming, cancelled: state.cancelled }));
|
|
116
120
|
}
|
|
117
121
|
case ComponentName.Command: {
|
|
118
122
|
const { props: { command }, state, status, } = def;
|
|
@@ -120,11 +124,12 @@ export const ViewComponent = memo(function ViewComponent({ def, }) {
|
|
|
120
124
|
}
|
|
121
125
|
case ComponentName.Introspect: {
|
|
122
126
|
const { props: { children }, state, status, } = def;
|
|
123
|
-
|
|
127
|
+
const hasCapabilities = state.capabilities.length > 0;
|
|
128
|
+
return (_jsx(IntrospectView, { status: status, hasCapabilities: hasCapabilities, error: state.error, children: children }));
|
|
124
129
|
}
|
|
125
130
|
case ComponentName.Validate: {
|
|
126
131
|
const { state, status } = def;
|
|
127
|
-
return _jsx(ValidateView, {
|
|
132
|
+
return (_jsx(ValidateView, { status: status, completionMessage: state.completionMessage, error: state.error }));
|
|
128
133
|
}
|
|
129
134
|
case ComponentName.Refinement: {
|
|
130
135
|
const { props: { text }, status, } = def;
|
|
@@ -3,11 +3,19 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
|
3
3
|
import { Box, Static } from 'ink';
|
|
4
4
|
import { ComponentStatus, } from '../types/components.js';
|
|
5
5
|
import { ComponentName, FeedbackType } from '../types/types.js';
|
|
6
|
-
import { createFeedback
|
|
6
|
+
import { createFeedback } from '../services/components.js';
|
|
7
7
|
import { getWarnings } from '../services/logger.js';
|
|
8
8
|
import { getCancellationMessage } from '../services/messages.js';
|
|
9
9
|
import { exitApp } from '../services/process.js';
|
|
10
10
|
import { SimpleComponent, ControllerComponent, TimelineComponent, } from './Component.js';
|
|
11
|
+
/**
|
|
12
|
+
* Mark a component as done. Returns the component to be added to timeline.
|
|
13
|
+
* Components use handlers.updateState to save their state before completion,
|
|
14
|
+
* so this function sets the status to Done and returns the updated component.
|
|
15
|
+
*/
|
|
16
|
+
function markAsDone(component) {
|
|
17
|
+
return { ...component, status: ComponentStatus.Done };
|
|
18
|
+
}
|
|
11
19
|
export const Workflow = ({ initialQueue, debug }) => {
|
|
12
20
|
const [timeline, setTimeline] = useState([]);
|
|
13
21
|
const [current, setCurrent] = useState({ active: null, pending: null });
|
|
@@ -39,17 +47,16 @@ export const Workflow = ({ initialQueue, debug }) => {
|
|
|
39
47
|
const requestHandlers = useMemo(() => ({
|
|
40
48
|
onError: (error) => {
|
|
41
49
|
moveActiveToTimeline();
|
|
42
|
-
//
|
|
43
|
-
setQueue(
|
|
44
|
-
|
|
45
|
-
createFeedback(FeedbackType.Failed, error),
|
|
50
|
+
// Clear queue and add only feedback to prevent subsequent components from executing
|
|
51
|
+
setQueue([
|
|
52
|
+
createFeedback({ type: FeedbackType.Failed, message: error }),
|
|
46
53
|
]);
|
|
47
54
|
},
|
|
48
55
|
onAborted: (operation) => {
|
|
49
56
|
moveActiveToTimeline();
|
|
50
57
|
// Clear queue and add only feedback to prevent subsequent components from executing
|
|
51
58
|
const message = getCancellationMessage(operation);
|
|
52
|
-
setQueue([createFeedback(FeedbackType.Aborted, message)]);
|
|
59
|
+
setQueue([createFeedback({ type: FeedbackType.Aborted, message })]);
|
|
53
60
|
},
|
|
54
61
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
|
|
55
62
|
onCompleted: (finalState) => {
|
|
@@ -144,7 +151,7 @@ export const Workflow = ({ initialQueue, debug }) => {
|
|
|
144
151
|
useEffect(() => {
|
|
145
152
|
const warningMessages = getWarnings();
|
|
146
153
|
if (warningMessages.length > 0) {
|
|
147
|
-
const warningComponents = warningMessages.map((msg) =>
|
|
154
|
+
const warningComponents = warningMessages.map((msg) => createFeedback({ type: FeedbackType.Warning, message: msg }, ComponentStatus.Done));
|
|
148
155
|
setTimeline((prev) => [...prev, ...warningComponents]);
|
|
149
156
|
}
|
|
150
157
|
}, [timeline, current]);
|
|
@@ -187,3 +194,12 @@ export const Workflow = ({ initialQueue, debug }) => {
|
|
|
187
194
|
const pendingComponent = useMemo(() => renderComponent(current.pending, ComponentStatus.Pending), [current.pending, renderComponent]);
|
|
188
195
|
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Static, { items: timeline, children: (item) => (_jsx(Box, { marginTop: 1, children: _jsx(TimelineComponent, { def: item }) }, item.id)) }, "timeline"), pendingComponent && _jsx(Box, { marginTop: 1, children: pendingComponent }), activeComponent && _jsx(Box, { marginTop: 1, children: activeComponent })] }));
|
|
189
196
|
};
|
|
197
|
+
/**
|
|
198
|
+
* Check if a component is stateless (simple).
|
|
199
|
+
* Stateless components are display-only and complete immediately without
|
|
200
|
+
* tracking internal state. Stateful components manage user interaction
|
|
201
|
+
* and maintain state across their lifecycle.
|
|
202
|
+
*/
|
|
203
|
+
export function isSimple(component) {
|
|
204
|
+
return !('state' in component);
|
|
205
|
+
}
|
|
@@ -1,28 +1,29 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useState } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
import { Spinner } from './Spinner.js';
|
|
3
|
+
import { ComponentStatus, } from '../../types/components.js';
|
|
4
|
+
import { useInput } from '../../services/keyboard.js';
|
|
5
|
+
import { formatErrorMessage } from '../../services/messages.js';
|
|
6
|
+
import { withMinimumTime } from '../../services/timing.js';
|
|
7
|
+
import { AnswerView } from '../views/Answer.js';
|
|
8
|
+
export { AnswerView } from '../views/Answer.js';
|
|
10
9
|
const MINIMUM_PROCESSING_TIME = 400;
|
|
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
10
|
/**
|
|
18
11
|
* Answer controller: Fetches answer from LLM
|
|
19
12
|
*/
|
|
20
|
-
export function Answer({ question, status, service, requestHandlers, lifecycleHandlers, workflowHandlers, }) {
|
|
13
|
+
export function Answer({ question, status, service, upcoming, requestHandlers, lifecycleHandlers, workflowHandlers, }) {
|
|
21
14
|
const isActive = status === ComponentStatus.Active;
|
|
22
15
|
const [error, setError] = useState(null);
|
|
23
16
|
const [answer, setAnswer] = useState(null);
|
|
17
|
+
const [cancelled, setCancelled] = useState(false);
|
|
24
18
|
useInput((input, key) => {
|
|
25
19
|
if (key.escape && isActive) {
|
|
20
|
+
setCancelled(true);
|
|
21
|
+
const finalState = {
|
|
22
|
+
answer: null,
|
|
23
|
+
error: null,
|
|
24
|
+
cancelled: true,
|
|
25
|
+
};
|
|
26
|
+
requestHandlers.onCompleted(finalState);
|
|
26
27
|
requestHandlers.onAborted('answer');
|
|
27
28
|
}
|
|
28
29
|
}, { isActive });
|
|
@@ -80,6 +81,6 @@ export function Answer({ question, status, service, requestHandlers, lifecycleHa
|
|
|
80
81
|
lifecycleHandlers,
|
|
81
82
|
workflowHandlers,
|
|
82
83
|
]);
|
|
83
|
-
const
|
|
84
|
-
return _jsx(AnswerView, { question: question,
|
|
84
|
+
const lines = answer ? answer.split('\n') : null;
|
|
85
|
+
return (_jsx(AnswerView, { status: status, question: question, lines: lines, error: error, upcoming: upcoming, cancelled: cancelled }));
|
|
85
86
|
}
|
|
@@ -1,23 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useState } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
import { Spinner } from './Spinner.js';
|
|
14
|
-
import { UserQuery } from './UserQuery.js';
|
|
3
|
+
import { ComponentStatus, } from '../../types/components.js';
|
|
4
|
+
import { TaskType } from '../../types/types.js';
|
|
5
|
+
import { createSchedule } from '../../services/components.js';
|
|
6
|
+
import { useInput } from '../../services/keyboard.js';
|
|
7
|
+
import { formatErrorMessage } from '../../services/messages.js';
|
|
8
|
+
import { handleRefinement } from '../../services/refinement.js';
|
|
9
|
+
import { routeTasksWithConfirm } from '../../services/router.js';
|
|
10
|
+
import { ensureMinimumTime } from '../../services/timing.js';
|
|
11
|
+
import { CommandView } from '../views/Command.js';
|
|
12
|
+
export { CommandView } from '../views/Command.js';
|
|
15
13
|
const MIN_PROCESSING_TIME = 400; // purely for visual effect
|
|
16
|
-
export const CommandView = ({ command, state, status }) => {
|
|
17
|
-
const isActive = status === ComponentStatus.Active;
|
|
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
14
|
/**
|
|
22
15
|
* Command controller: Processes and routes command
|
|
23
16
|
*/
|
|
@@ -77,12 +70,16 @@ export function Command({ command, status, service, requestHandlers, lifecycleHa
|
|
|
77
70
|
// Check if tasks contain DEFINE type (variant selection needed)
|
|
78
71
|
const hasDefineTask = result.tasks.some((task) => task.type === TaskType.Define);
|
|
79
72
|
// Create Schedule definition
|
|
80
|
-
const scheduleDefinition =
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
73
|
+
const scheduleDefinition = createSchedule({
|
|
74
|
+
message: result.message,
|
|
75
|
+
tasks: result.tasks,
|
|
76
|
+
onSelectionConfirmed: hasDefineTask
|
|
77
|
+
? async (selectedTasks) => {
|
|
78
|
+
// Refinement flow for DEFINE tasks
|
|
79
|
+
await handleRefinement(selectedTasks, svc, command, lifecycleHandlers, workflowHandlers, requestHandlers);
|
|
80
|
+
}
|
|
81
|
+
: undefined,
|
|
82
|
+
});
|
|
86
83
|
if (hasDefineTask) {
|
|
87
84
|
// DEFINE tasks: Move Command to timeline, add Schedule to queue
|
|
88
85
|
lifecycleHandlers.completeActive();
|
|
@@ -1,120 +1,12 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useState } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
import { useInput } from '../services/keyboard.js';
|
|
11
|
-
/**
|
|
12
|
-
* Get postfix with debug brackets if debug is enabled
|
|
13
|
-
* Info: {key} | Verbose: {key} entry
|
|
14
|
-
*/
|
|
15
|
-
function getPostfix(text, debugLevel) {
|
|
16
|
-
if (debugLevel === DebugLevel.None || !text) {
|
|
17
|
-
return '';
|
|
18
|
-
}
|
|
19
|
-
if (debugLevel === DebugLevel.Info) {
|
|
20
|
-
return `{${text}}`;
|
|
21
|
-
}
|
|
22
|
-
return `{${text}} entry`;
|
|
23
|
-
}
|
|
24
|
-
export var StepType;
|
|
25
|
-
(function (StepType) {
|
|
26
|
-
StepType["Text"] = "text";
|
|
27
|
-
StepType["Selection"] = "selection";
|
|
28
|
-
})(StepType || (StepType = {}));
|
|
29
|
-
function TextStep({ value, placeholder, validate, onChange, onSubmit, }) {
|
|
30
|
-
const [inputValue, setInputValue] = useState(value);
|
|
31
|
-
const [validationFailed, setValidationFailed] = useState(false);
|
|
32
|
-
const { isFocused } = useFocus({ autoFocus: true });
|
|
33
|
-
// Sync internal state with prop changes
|
|
34
|
-
useEffect(() => {
|
|
35
|
-
setInputValue(value);
|
|
36
|
-
}, [value]);
|
|
37
|
-
const handleChange = (newValue) => {
|
|
38
|
-
setInputValue(newValue);
|
|
39
|
-
onChange(newValue);
|
|
40
|
-
if (validationFailed) {
|
|
41
|
-
setValidationFailed(false);
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
const handleSubmit = (value) => {
|
|
45
|
-
// Use placeholder if input is empty
|
|
46
|
-
const finalValue = value || placeholder || '';
|
|
47
|
-
if (!validate(finalValue)) {
|
|
48
|
-
setValidationFailed(true);
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
onSubmit(finalValue);
|
|
52
|
-
};
|
|
53
|
-
// Handle input manually when validation fails
|
|
54
|
-
useInput((input, key) => {
|
|
55
|
-
if (!validationFailed)
|
|
56
|
-
return;
|
|
57
|
-
if (key.return) {
|
|
58
|
-
handleSubmit(inputValue);
|
|
59
|
-
}
|
|
60
|
-
else if (key.backspace || key.delete) {
|
|
61
|
-
const newValue = inputValue.slice(0, -1);
|
|
62
|
-
handleChange(newValue);
|
|
63
|
-
}
|
|
64
|
-
else if (!key.ctrl && !key.meta && input) {
|
|
65
|
-
const newValue = inputValue + input;
|
|
66
|
-
handleChange(newValue);
|
|
67
|
-
}
|
|
68
|
-
}, { isActive: validationFailed });
|
|
69
|
-
// When validation fails, show colored text
|
|
70
|
-
if (validationFailed) {
|
|
71
|
-
return (_jsxs(Text, { color: Colors.Status.Error, children: [inputValue || placeholder, isFocused && _jsx(Text, { inverse: true, children: " " })] }));
|
|
72
|
-
}
|
|
73
|
-
return (_jsx(TextInput, { value: inputValue, onChange: handleChange, onSubmit: handleSubmit, placeholder: placeholder }));
|
|
74
|
-
}
|
|
75
|
-
function SelectionStep({ options, selectedIndex, isCurrentStep, }) {
|
|
76
|
-
return (_jsx(Box, { children: options.map((option, optIndex) => {
|
|
77
|
-
const isSelected = optIndex === selectedIndex;
|
|
78
|
-
return (_jsx(Box, { marginRight: 2, children: _jsx(Text, { dimColor: !isSelected || !isCurrentStep, bold: isSelected, children: option.label }) }, option.value));
|
|
79
|
-
}) }));
|
|
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
|
-
};
|
|
3
|
+
import { ComponentStatus } from '../../types/components.js';
|
|
4
|
+
import { FeedbackType } from '../../types/types.js';
|
|
5
|
+
import { createFeedback } from '../../services/components.js';
|
|
6
|
+
import { DebugLevel } from '../../configuration/types.js';
|
|
7
|
+
import { useInput } from '../../services/keyboard.js';
|
|
8
|
+
import { ConfigView, StepType } from '../views/Config.js';
|
|
9
|
+
export { ConfigView, StepType, } from '../views/Config.js';
|
|
118
10
|
/**
|
|
119
11
|
* Config controller: Multi-step wizard logic
|
|
120
12
|
*/
|
|
@@ -208,7 +100,10 @@ export function Config(props) {
|
|
|
208
100
|
}
|
|
209
101
|
else {
|
|
210
102
|
// Fallback: complete with abort feedback directly
|
|
211
|
-
lifecycleHandlers.completeActive(createFeedback(
|
|
103
|
+
lifecycleHandlers.completeActive(createFeedback({
|
|
104
|
+
type: FeedbackType.Aborted,
|
|
105
|
+
message: 'Configuration cancelled.',
|
|
106
|
+
}));
|
|
212
107
|
}
|
|
213
108
|
return;
|
|
214
109
|
}
|
|
@@ -272,12 +167,15 @@ export function Config(props) {
|
|
|
272
167
|
onFinished(newValues);
|
|
273
168
|
}
|
|
274
169
|
// Success - complete with success feedback
|
|
275
|
-
lifecycleHandlers.completeActive(createFeedback(
|
|
170
|
+
lifecycleHandlers.completeActive(createFeedback({
|
|
171
|
+
type: FeedbackType.Succeeded,
|
|
172
|
+
message: 'Configuration saved successfully.',
|
|
173
|
+
}));
|
|
276
174
|
}
|
|
277
175
|
catch (error) {
|
|
278
176
|
// Failure - complete with error feedback
|
|
279
177
|
const errorMessage = error instanceof Error ? error.message : 'Configuration failed';
|
|
280
|
-
lifecycleHandlers.completeActive(createFeedback(FeedbackType.Failed, errorMessage));
|
|
178
|
+
lifecycleHandlers.completeActive(createFeedback({ type: FeedbackType.Failed, message: errorMessage }));
|
|
281
179
|
}
|
|
282
180
|
setStep(steps.length);
|
|
283
181
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { ComponentStatus, } from '../../types/components.js';
|
|
4
|
+
import { useInput } from '../../services/keyboard.js';
|
|
5
|
+
import { ConfirmView } from '../views/Confirm.js';
|
|
6
|
+
export { ConfirmView } from '../views/Confirm.js';
|
|
7
|
+
/**
|
|
8
|
+
* Confirm controller: Manages yes/no selection
|
|
9
|
+
*/
|
|
10
|
+
export function Confirm({ message, status, requestHandlers, onConfirmed, onCancelled, }) {
|
|
11
|
+
const isActive = status === ComponentStatus.Active;
|
|
12
|
+
const [selectedIndex, setSelectedIndex] = useState(0); // 0 = Yes, 1 = No
|
|
13
|
+
useInput((input, key) => {
|
|
14
|
+
if (!isActive)
|
|
15
|
+
return;
|
|
16
|
+
if (key.escape) {
|
|
17
|
+
// Escape: highlight "No" and cancel
|
|
18
|
+
const finalState = { selectedIndex: 1, confirmed: false };
|
|
19
|
+
requestHandlers.onCompleted(finalState);
|
|
20
|
+
onCancelled();
|
|
21
|
+
}
|
|
22
|
+
else if (key.tab) {
|
|
23
|
+
// Toggle between Yes (0) and No (1)
|
|
24
|
+
setSelectedIndex((prev) => (prev === 0 ? 1 : 0));
|
|
25
|
+
}
|
|
26
|
+
else if (key.return) {
|
|
27
|
+
// Confirm selection
|
|
28
|
+
const finalState = {
|
|
29
|
+
selectedIndex,
|
|
30
|
+
confirmed: true,
|
|
31
|
+
};
|
|
32
|
+
requestHandlers.onCompleted(finalState);
|
|
33
|
+
if (selectedIndex === 0) {
|
|
34
|
+
onConfirmed();
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
onCancelled();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}, { isActive });
|
|
41
|
+
return (_jsx(ConfirmView, { status: status, message: message, selectedIndex: selectedIndex }));
|
|
42
|
+
}
|