prompt-language-shell 0.8.0 → 0.8.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/services/anthropic.js +50 -37
- package/dist/services/colors.js +21 -1
- package/dist/services/components.js +6 -14
- package/dist/services/config-labels.js +75 -0
- package/dist/services/config-utils.js +20 -0
- package/dist/services/configuration.js +38 -59
- package/dist/services/filesystem.js +114 -0
- package/dist/services/loader.js +8 -5
- package/dist/services/logger.js +24 -0
- package/dist/services/parser.js +3 -1
- package/dist/services/refinement.js +9 -9
- package/dist/services/registry.js +1 -1
- package/dist/services/router.js +29 -28
- package/dist/services/skills.js +15 -14
- package/dist/services/validator.js +4 -3
- package/dist/skills/introspect.md +52 -43
- package/dist/skills/schedule.md +8 -3
- package/dist/tools/introspect.tool.js +18 -9
- package/dist/types/guards.js +23 -0
- package/dist/types/handlers.js +1 -0
- package/dist/types/schemas.js +103 -0
- package/dist/types/types.js +7 -0
- package/dist/ui/Answer.js +11 -15
- package/dist/ui/Command.js +24 -20
- package/dist/ui/Config.js +44 -32
- package/dist/ui/Confirm.js +9 -9
- package/dist/ui/Execute.js +265 -75
- package/dist/ui/Feedback.js +1 -0
- package/dist/ui/Introspect.js +16 -61
- package/dist/ui/Main.js +6 -6
- package/dist/ui/Report.js +4 -8
- package/dist/ui/Schedule.js +12 -12
- package/dist/ui/Spinner.js +3 -1
- package/dist/ui/Subtask.js +1 -1
- package/dist/ui/Task.js +7 -6
- package/dist/ui/Validate.js +28 -21
- package/dist/ui/Workflow.js +102 -30
- package/package.json +3 -2
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { TaskType } from './types.js';
|
|
2
|
+
import { TaskSchema } from './schemas.js';
|
|
3
|
+
/**
|
|
4
|
+
* Type guard to check if a task is a ScheduledTask
|
|
5
|
+
* ScheduledTask has optional subtasks property or is a Group type
|
|
6
|
+
*/
|
|
7
|
+
export function isScheduledTask(task) {
|
|
8
|
+
return 'subtasks' in task || task.type === TaskType.Group;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Type-safe conversion of Task array to ScheduledTask array
|
|
12
|
+
* This is safe because Tasks can be treated as ScheduledTask when checking for Groups
|
|
13
|
+
*/
|
|
14
|
+
export function asScheduledTasks(tasks) {
|
|
15
|
+
return tasks;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Type guard to check if a value is a valid Task.
|
|
19
|
+
* Uses Zod schema for comprehensive runtime validation.
|
|
20
|
+
*/
|
|
21
|
+
export function isTask(value) {
|
|
22
|
+
return TaskSchema.safeParse(value).success;
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { Origin, TaskType } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Zod schema for TaskType enum values.
|
|
5
|
+
* Validates that task types match the expected enum values.
|
|
6
|
+
*/
|
|
7
|
+
export const TaskTypeSchema = z.enum([
|
|
8
|
+
TaskType.Config,
|
|
9
|
+
TaskType.Schedule,
|
|
10
|
+
TaskType.Execute,
|
|
11
|
+
TaskType.Answer,
|
|
12
|
+
TaskType.Introspect,
|
|
13
|
+
TaskType.Report,
|
|
14
|
+
TaskType.Define,
|
|
15
|
+
TaskType.Ignore,
|
|
16
|
+
TaskType.Select,
|
|
17
|
+
TaskType.Discard,
|
|
18
|
+
TaskType.Group,
|
|
19
|
+
]);
|
|
20
|
+
/**
|
|
21
|
+
* Zod schema for Origin enum values.
|
|
22
|
+
* Validates capability origin types.
|
|
23
|
+
*/
|
|
24
|
+
export const OriginSchema = z.enum([
|
|
25
|
+
Origin.BuiltIn,
|
|
26
|
+
Origin.UserProvided,
|
|
27
|
+
Origin.Indirect,
|
|
28
|
+
]);
|
|
29
|
+
/**
|
|
30
|
+
* Zod schema for base Task type.
|
|
31
|
+
* Validates task structure with required action and type fields.
|
|
32
|
+
*/
|
|
33
|
+
export const TaskSchema = z.object({
|
|
34
|
+
action: z.string().min(1),
|
|
35
|
+
type: TaskTypeSchema,
|
|
36
|
+
params: z.record(z.string(), z.unknown()).optional(),
|
|
37
|
+
config: z.array(z.string()).optional(),
|
|
38
|
+
});
|
|
39
|
+
/**
|
|
40
|
+
* Zod schema for recursive ScheduledTask type.
|
|
41
|
+
* Uses z.lazy for self-referential subtasks validation.
|
|
42
|
+
*/
|
|
43
|
+
export const ScheduledTaskSchema = z.object({
|
|
44
|
+
action: z.string().min(1),
|
|
45
|
+
type: TaskTypeSchema,
|
|
46
|
+
params: z.record(z.string(), z.unknown()).optional(),
|
|
47
|
+
config: z.array(z.string()).optional(),
|
|
48
|
+
subtasks: z.lazy(() => ScheduledTaskSchema.array()).optional(),
|
|
49
|
+
});
|
|
50
|
+
/**
|
|
51
|
+
* Zod schema for ExecuteCommand type.
|
|
52
|
+
* Validates shell command execution parameters.
|
|
53
|
+
*/
|
|
54
|
+
export const ExecuteCommandSchema = z.object({
|
|
55
|
+
description: z.string().min(1),
|
|
56
|
+
command: z.string().min(1),
|
|
57
|
+
workdir: z.string().optional(),
|
|
58
|
+
timeout: z.number().int().positive().optional(),
|
|
59
|
+
critical: z.boolean().optional(),
|
|
60
|
+
});
|
|
61
|
+
/**
|
|
62
|
+
* Zod schema for Capability type.
|
|
63
|
+
* Validates skill and capability definitions.
|
|
64
|
+
*/
|
|
65
|
+
export const CapabilitySchema = z.object({
|
|
66
|
+
name: z.string().min(1),
|
|
67
|
+
description: z.string().min(1),
|
|
68
|
+
origin: OriginSchema,
|
|
69
|
+
isIncomplete: z.boolean().optional(),
|
|
70
|
+
});
|
|
71
|
+
/**
|
|
72
|
+
* Zod schema for ComponentDefinition type.
|
|
73
|
+
* Flexible schema for debug component validation.
|
|
74
|
+
* Accepts both stateless and stateful component structures.
|
|
75
|
+
*/
|
|
76
|
+
export const ComponentDefinitionSchema = z.object({
|
|
77
|
+
id: z.string(),
|
|
78
|
+
name: z.string(),
|
|
79
|
+
props: z.record(z.string(), z.unknown()),
|
|
80
|
+
state: z.record(z.string(), z.unknown()).optional(),
|
|
81
|
+
status: z.string().optional(),
|
|
82
|
+
});
|
|
83
|
+
/**
|
|
84
|
+
* Zod schema for CommandResult type.
|
|
85
|
+
* Validates LLM responses from execute, answer, and schedule tools.
|
|
86
|
+
*/
|
|
87
|
+
export const CommandResultSchema = z.object({
|
|
88
|
+
message: z.string(),
|
|
89
|
+
summary: z.string().optional(),
|
|
90
|
+
tasks: z.array(ScheduledTaskSchema),
|
|
91
|
+
answer: z.string().optional(),
|
|
92
|
+
commands: z.array(ExecuteCommandSchema).optional(),
|
|
93
|
+
debug: z.array(ComponentDefinitionSchema).optional(),
|
|
94
|
+
});
|
|
95
|
+
/**
|
|
96
|
+
* Zod schema for IntrospectResult type.
|
|
97
|
+
* Validates LLM responses from introspect tool.
|
|
98
|
+
*/
|
|
99
|
+
export const IntrospectResultSchema = z.object({
|
|
100
|
+
message: z.string(),
|
|
101
|
+
capabilities: z.array(CapabilitySchema),
|
|
102
|
+
debug: z.array(ComponentDefinitionSchema).optional(),
|
|
103
|
+
});
|
package/dist/types/types.js
CHANGED
|
@@ -29,9 +29,16 @@ export var TaskType;
|
|
|
29
29
|
TaskType["Discard"] = "discard";
|
|
30
30
|
TaskType["Group"] = "group";
|
|
31
31
|
})(TaskType || (TaskType = {}));
|
|
32
|
+
export var Origin;
|
|
33
|
+
(function (Origin) {
|
|
34
|
+
Origin["BuiltIn"] = "system";
|
|
35
|
+
Origin["UserProvided"] = "user";
|
|
36
|
+
Origin["Indirect"] = "meta";
|
|
37
|
+
})(Origin || (Origin = {}));
|
|
32
38
|
export var FeedbackType;
|
|
33
39
|
(function (FeedbackType) {
|
|
34
40
|
FeedbackType["Info"] = "info";
|
|
41
|
+
FeedbackType["Warning"] = "warning";
|
|
35
42
|
FeedbackType["Succeeded"] = "succeeded";
|
|
36
43
|
FeedbackType["Aborted"] = "aborted";
|
|
37
44
|
FeedbackType["Failed"] = "failed";
|
package/dist/ui/Answer.js
CHANGED
|
@@ -3,19 +3,18 @@ import { useEffect, useState } from 'react';
|
|
|
3
3
|
import { Box, Text } from 'ink';
|
|
4
4
|
import { ComponentStatus } from '../types/components.js';
|
|
5
5
|
import { Colors, getTextColor } from '../services/colors.js';
|
|
6
|
-
import { addDebugToTimeline } from '../services/components.js';
|
|
7
6
|
import { useInput } from '../services/keyboard.js';
|
|
8
7
|
import { formatErrorMessage } from '../services/messages.js';
|
|
9
8
|
import { withMinimumTime } from '../services/timing.js';
|
|
10
9
|
import { Spinner } from './Spinner.js';
|
|
11
10
|
const MINIMUM_PROCESSING_TIME = 400;
|
|
12
|
-
export function Answer({ question, state, status, service,
|
|
11
|
+
export function Answer({ question, state, status, service, stateHandlers, lifecycleHandlers, errorHandlers, workflowHandlers, }) {
|
|
13
12
|
const isActive = status === ComponentStatus.Active;
|
|
14
13
|
const [error, setError] = useState(null);
|
|
15
14
|
const [answer, setAnswer] = useState(state?.answer ?? null);
|
|
16
15
|
useInput((input, key) => {
|
|
17
16
|
if (key.escape && isActive) {
|
|
18
|
-
|
|
17
|
+
errorHandlers?.onAborted('answer');
|
|
19
18
|
}
|
|
20
19
|
}, { isActive });
|
|
21
20
|
useEffect(() => {
|
|
@@ -23,11 +22,6 @@ export function Answer({ question, state, status, service, handlers, }) {
|
|
|
23
22
|
if (!isActive) {
|
|
24
23
|
return;
|
|
25
24
|
}
|
|
26
|
-
// Skip processing if no service available
|
|
27
|
-
if (!service) {
|
|
28
|
-
setError('No service available');
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
25
|
let mounted = true;
|
|
32
26
|
async function process(svc) {
|
|
33
27
|
try {
|
|
@@ -35,34 +29,36 @@ export function Answer({ question, state, status, service, handlers, }) {
|
|
|
35
29
|
const result = await withMinimumTime(() => svc.processWithTool(question, 'answer'), MINIMUM_PROCESSING_TIME);
|
|
36
30
|
if (mounted) {
|
|
37
31
|
// Add debug components to timeline if present
|
|
38
|
-
|
|
32
|
+
if (result.debug?.length) {
|
|
33
|
+
workflowHandlers?.addToTimeline(...result.debug);
|
|
34
|
+
}
|
|
39
35
|
// Extract answer from result
|
|
40
36
|
const answerText = result.answer || '';
|
|
41
37
|
setAnswer(answerText);
|
|
42
38
|
// Update component state so answer persists in timeline
|
|
43
|
-
|
|
39
|
+
stateHandlers?.updateState({
|
|
44
40
|
answer: answerText,
|
|
45
41
|
});
|
|
46
42
|
// Signal completion
|
|
47
|
-
|
|
43
|
+
lifecycleHandlers?.completeActive();
|
|
48
44
|
}
|
|
49
45
|
}
|
|
50
46
|
catch (err) {
|
|
51
47
|
if (mounted) {
|
|
52
48
|
const errorMessage = formatErrorMessage(err);
|
|
53
49
|
setError(errorMessage);
|
|
54
|
-
|
|
50
|
+
stateHandlers?.updateState({
|
|
55
51
|
error: errorMessage,
|
|
56
52
|
});
|
|
57
|
-
|
|
53
|
+
errorHandlers?.onError(errorMessage);
|
|
58
54
|
}
|
|
59
55
|
}
|
|
60
56
|
}
|
|
61
|
-
process(service);
|
|
57
|
+
void process(service);
|
|
62
58
|
return () => {
|
|
63
59
|
mounted = false;
|
|
64
60
|
};
|
|
65
|
-
}, [question, isActive, service
|
|
61
|
+
}, [question, isActive, service]);
|
|
66
62
|
const lines = answer ? answer.split('\n') : [];
|
|
67
63
|
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] }) }))] }));
|
|
68
64
|
}
|
package/dist/ui/Command.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
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
|
|
4
|
+
import { ComponentStatus } from '../types/components.js';
|
|
5
5
|
import { TaskType } from '../types/types.js';
|
|
6
6
|
import { Colors } from '../services/colors.js';
|
|
7
|
-
import {
|
|
7
|
+
import { createScheduleDefinition } from '../services/components.js';
|
|
8
8
|
import { useInput } from '../services/keyboard.js';
|
|
9
9
|
import { formatErrorMessage } from '../services/messages.js';
|
|
10
10
|
import { handleRefinement } from '../services/refinement.js';
|
|
@@ -13,12 +13,12 @@ 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 function Command({ command, state, status, service,
|
|
16
|
+
export function Command({ command, state, status, service, stateHandlers, lifecycleHandlers, queueHandlers, errorHandlers, workflowHandlers, onAborted, }) {
|
|
17
17
|
const isActive = status === ComponentStatus.Active;
|
|
18
18
|
const [error, setError] = useState(state?.error ?? null);
|
|
19
19
|
useInput((_, key) => {
|
|
20
20
|
if (key.escape && isActive) {
|
|
21
|
-
|
|
21
|
+
errorHandlers?.onAborted('request');
|
|
22
22
|
onAborted?.('request');
|
|
23
23
|
}
|
|
24
24
|
}, { isActive });
|
|
@@ -27,11 +27,6 @@ export function Command({ command, state, status, service, handlers, onAborted,
|
|
|
27
27
|
if (!isActive) {
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
30
|
-
// Skip processing if no service available
|
|
31
|
-
if (!service) {
|
|
32
|
-
setError('No service available');
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
30
|
let mounted = true;
|
|
36
31
|
async function process(svc) {
|
|
37
32
|
const startTime = Date.now();
|
|
@@ -56,30 +51,39 @@ export function Command({ command, state, status, service, handlers, onAborted,
|
|
|
56
51
|
const debugComponents = allConfig
|
|
57
52
|
? [...scheduleDebug, ...(result.debug || [])]
|
|
58
53
|
: scheduleDebug;
|
|
59
|
-
|
|
54
|
+
if (debugComponents.length > 0) {
|
|
55
|
+
workflowHandlers?.addToTimeline(...debugComponents);
|
|
56
|
+
}
|
|
60
57
|
// Save result to state for timeline display
|
|
61
|
-
|
|
58
|
+
stateHandlers?.updateState({
|
|
62
59
|
message: result.message,
|
|
63
60
|
tasks: result.tasks,
|
|
64
61
|
});
|
|
65
62
|
// Check if tasks contain DEFINE type (variant selection needed)
|
|
66
63
|
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
|
+
}
|
|
67
71
|
// Create Schedule definition
|
|
68
72
|
const scheduleDefinition = createScheduleDefinition(result.message, result.tasks, hasDefineTask
|
|
69
73
|
? async (selectedTasks) => {
|
|
70
74
|
// Refinement flow for DEFINE tasks
|
|
71
|
-
await handleRefinement(selectedTasks, svc, command,
|
|
75
|
+
await handleRefinement(selectedTasks, svc, command, queueHandlers, lifecycleHandlers, workflowHandlers, errorHandlers);
|
|
72
76
|
}
|
|
73
77
|
: undefined);
|
|
74
78
|
if (hasDefineTask) {
|
|
75
79
|
// DEFINE tasks: Move Command to timeline, add Schedule to queue
|
|
76
|
-
|
|
77
|
-
|
|
80
|
+
lifecycleHandlers.completeActive();
|
|
81
|
+
queueHandlers.addToQueue(scheduleDefinition);
|
|
78
82
|
}
|
|
79
83
|
else {
|
|
80
84
|
// No DEFINE tasks: Complete Command, then route to Confirm flow
|
|
81
|
-
|
|
82
|
-
routeTasksWithConfirm(result.tasks, result.message, svc, command,
|
|
85
|
+
lifecycleHandlers.completeActive();
|
|
86
|
+
routeTasksWithConfirm(result.tasks, result.message, svc, command, queueHandlers, workflowHandlers, errorHandlers, false);
|
|
83
87
|
}
|
|
84
88
|
}
|
|
85
89
|
}
|
|
@@ -88,17 +92,17 @@ export function Command({ command, state, status, service, handlers, onAborted,
|
|
|
88
92
|
if (mounted) {
|
|
89
93
|
const errorMessage = formatErrorMessage(err);
|
|
90
94
|
setError(errorMessage);
|
|
91
|
-
|
|
95
|
+
stateHandlers?.updateState({
|
|
92
96
|
error: errorMessage,
|
|
93
97
|
});
|
|
94
|
-
|
|
98
|
+
errorHandlers?.onError(errorMessage);
|
|
95
99
|
}
|
|
96
100
|
}
|
|
97
101
|
}
|
|
98
|
-
process(service);
|
|
102
|
+
void process(service);
|
|
99
103
|
return () => {
|
|
100
104
|
mounted = false;
|
|
101
105
|
};
|
|
102
|
-
}, [command, isActive, service
|
|
106
|
+
}, [command, isActive, service]);
|
|
103
107
|
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] }) }))] }));
|
|
104
108
|
}
|
package/dist/ui/Config.js
CHANGED
|
@@ -8,6 +8,19 @@ import { Colors } from '../services/colors.js';
|
|
|
8
8
|
import { createFeedback } from '../services/components.js';
|
|
9
9
|
import { DebugLevel } from '../services/configuration.js';
|
|
10
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
|
+
}
|
|
11
24
|
export var StepType;
|
|
12
25
|
(function (StepType) {
|
|
13
26
|
StepType["Text"] = "text";
|
|
@@ -65,7 +78,8 @@ function SelectionStep({ options, selectedIndex, isCurrentStep, }) {
|
|
|
65
78
|
return (_jsx(Box, { marginRight: 2, children: _jsx(Text, { dimColor: !isSelected || !isCurrentStep, bold: isSelected, children: option.label }) }, option.value));
|
|
66
79
|
}) }));
|
|
67
80
|
}
|
|
68
|
-
export function Config(
|
|
81
|
+
export function Config(props) {
|
|
82
|
+
const { steps, state, status, debug = DebugLevel.None, stateHandlers, lifecycleHandlers, onFinished, onAborted, } = props;
|
|
69
83
|
const isActive = status === ComponentStatus.Active;
|
|
70
84
|
const [step, setStep] = useState(!isActive ? (state?.completedStep ?? steps.length) : 0);
|
|
71
85
|
const [values, setValues] = useState(() => {
|
|
@@ -89,8 +103,8 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
|
|
|
89
103
|
stepConfig.options[stepConfig.defaultIndex].value;
|
|
90
104
|
break;
|
|
91
105
|
default: {
|
|
92
|
-
const
|
|
93
|
-
throw new Error(
|
|
106
|
+
const _exhaustiveCheck = stepConfig;
|
|
107
|
+
throw new Error('Unsupported step type');
|
|
94
108
|
}
|
|
95
109
|
}
|
|
96
110
|
});
|
|
@@ -132,7 +146,6 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
|
|
|
132
146
|
const value = values[configKey] || '';
|
|
133
147
|
setInputValue(value);
|
|
134
148
|
}
|
|
135
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
136
149
|
}, [step, isActive, steps]);
|
|
137
150
|
useInput((_, key) => {
|
|
138
151
|
if (!isActive || step >= steps.length)
|
|
@@ -140,27 +153,25 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
|
|
|
140
153
|
const currentStepConfig = steps[step];
|
|
141
154
|
if (key.escape) {
|
|
142
155
|
// Save current value before aborting
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
throw new Error(`Unsupported step type: ${exhaustiveCheck}`);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
if (currentValue) {
|
|
159
|
-
setValues({ ...values, [configKey]: currentValue });
|
|
156
|
+
const configKey = currentStepConfig.path || currentStepConfig.key;
|
|
157
|
+
let currentValue = '';
|
|
158
|
+
switch (currentStepConfig.type) {
|
|
159
|
+
case StepType.Text:
|
|
160
|
+
currentValue = inputValue || values[configKey] || '';
|
|
161
|
+
break;
|
|
162
|
+
case StepType.Selection:
|
|
163
|
+
currentValue = values[configKey] || '';
|
|
164
|
+
break;
|
|
165
|
+
default: {
|
|
166
|
+
const _exhaustiveCheck = currentStepConfig;
|
|
167
|
+
throw new Error('Unsupported step type');
|
|
160
168
|
}
|
|
161
169
|
}
|
|
170
|
+
if (currentValue) {
|
|
171
|
+
setValues({ ...values, [configKey]: currentValue });
|
|
172
|
+
}
|
|
162
173
|
// Save state before aborting
|
|
163
|
-
|
|
174
|
+
stateHandlers?.updateState({
|
|
164
175
|
values,
|
|
165
176
|
completedStep: step,
|
|
166
177
|
selectedIndex,
|
|
@@ -169,7 +180,7 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
|
|
|
169
180
|
onAborted('configuration');
|
|
170
181
|
}
|
|
171
182
|
// Complete with abort feedback
|
|
172
|
-
|
|
183
|
+
lifecycleHandlers?.completeActive(createFeedback(FeedbackType.Aborted, 'Configuration cancelled.'));
|
|
173
184
|
return;
|
|
174
185
|
}
|
|
175
186
|
// Handle selection step navigation
|
|
@@ -204,8 +215,8 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
|
|
|
204
215
|
break;
|
|
205
216
|
}
|
|
206
217
|
default: {
|
|
207
|
-
const
|
|
208
|
-
throw new Error(
|
|
218
|
+
const _exhaustiveCheck = currentStepConfig;
|
|
219
|
+
throw new Error('Unsupported step type');
|
|
209
220
|
}
|
|
210
221
|
}
|
|
211
222
|
// Don't allow empty or invalid value
|
|
@@ -226,19 +237,19 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
|
|
|
226
237
|
completedStep: steps.length,
|
|
227
238
|
selectedIndex,
|
|
228
239
|
};
|
|
229
|
-
|
|
240
|
+
stateHandlers?.updateState(stateUpdate);
|
|
230
241
|
// Call onFinished callback and handle result
|
|
231
242
|
try {
|
|
232
243
|
if (onFinished) {
|
|
233
244
|
onFinished(newValues);
|
|
234
245
|
}
|
|
235
246
|
// Success - complete with success feedback
|
|
236
|
-
|
|
247
|
+
lifecycleHandlers?.completeActive(createFeedback(FeedbackType.Succeeded, 'Configuration saved successfully.'));
|
|
237
248
|
}
|
|
238
249
|
catch (error) {
|
|
239
250
|
// Failure - complete with error feedback
|
|
240
251
|
const errorMessage = error instanceof Error ? error.message : 'Configuration failed';
|
|
241
|
-
|
|
252
|
+
lifecycleHandlers?.completeActive(createFeedback(FeedbackType.Failed, errorMessage));
|
|
242
253
|
}
|
|
243
254
|
setStep(steps.length);
|
|
244
255
|
}
|
|
@@ -249,7 +260,7 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
|
|
|
249
260
|
completedStep: step + 1,
|
|
250
261
|
selectedIndex,
|
|
251
262
|
};
|
|
252
|
-
|
|
263
|
+
stateHandlers?.updateState(stateUpdate);
|
|
253
264
|
const nextStep = step + 1;
|
|
254
265
|
setStep(nextStep);
|
|
255
266
|
// Reset selectedIndex for next step
|
|
@@ -278,8 +289,8 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
|
|
|
278
289
|
return (_jsx(SelectionStep, { options: stepConfig.options, selectedIndex: selectedIndex, isCurrentStep: true }));
|
|
279
290
|
}
|
|
280
291
|
default: {
|
|
281
|
-
const
|
|
282
|
-
throw new Error(
|
|
292
|
+
const _exhaustiveCheck = stepConfig;
|
|
293
|
+
throw new Error('Unsupported step type');
|
|
283
294
|
}
|
|
284
295
|
}
|
|
285
296
|
};
|
|
@@ -291,6 +302,7 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
|
|
|
291
302
|
if (!shouldShow) {
|
|
292
303
|
return null;
|
|
293
304
|
}
|
|
294
|
-
|
|
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));
|
|
295
307
|
}) }));
|
|
296
308
|
}
|
package/dist/ui/Confirm.js
CHANGED
|
@@ -2,10 +2,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
import { Box, Text } from 'ink';
|
|
4
4
|
import { ComponentStatus } from '../types/components.js';
|
|
5
|
-
import { Colors, Palette } from '../services/colors.js';
|
|
5
|
+
import { Colors, getTextColor, Palette } from '../services/colors.js';
|
|
6
6
|
import { useInput } from '../services/keyboard.js';
|
|
7
7
|
import { UserQuery } from './UserQuery.js';
|
|
8
|
-
export function Confirm({ message, state, status,
|
|
8
|
+
export function Confirm({ message, state, status, stateHandlers, onConfirmed, onCancelled, }) {
|
|
9
9
|
const isActive = status === ComponentStatus.Active;
|
|
10
10
|
const [selectedIndex, setSelectedIndex] = useState(state?.selectedIndex ?? 0); // 0 = Yes, 1 = No
|
|
11
11
|
useInput((input, key) => {
|
|
@@ -14,23 +14,23 @@ export function Confirm({ message, state, status, handlers, onConfirmed, onCance
|
|
|
14
14
|
if (key.escape) {
|
|
15
15
|
// Escape: highlight "No" and cancel
|
|
16
16
|
setSelectedIndex(1);
|
|
17
|
-
|
|
18
|
-
onCancelled
|
|
17
|
+
stateHandlers?.updateState({ selectedIndex: 1 });
|
|
18
|
+
onCancelled();
|
|
19
19
|
}
|
|
20
20
|
else if (key.tab) {
|
|
21
21
|
// Toggle between Yes (0) and No (1)
|
|
22
22
|
const newIndex = selectedIndex === 0 ? 1 : 0;
|
|
23
23
|
setSelectedIndex(newIndex);
|
|
24
|
-
|
|
24
|
+
stateHandlers?.updateState({ selectedIndex: newIndex });
|
|
25
25
|
}
|
|
26
26
|
else if (key.return) {
|
|
27
27
|
// Confirm selection
|
|
28
|
-
|
|
28
|
+
stateHandlers?.updateState({ selectedIndex, confirmed: true });
|
|
29
29
|
if (selectedIndex === 0) {
|
|
30
|
-
onConfirmed
|
|
30
|
+
onConfirmed();
|
|
31
31
|
}
|
|
32
32
|
else {
|
|
33
|
-
onCancelled
|
|
33
|
+
onCancelled();
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
}, { isActive });
|
|
@@ -42,7 +42,7 @@ export function Confirm({ message, state, status, handlers, onConfirmed, onCance
|
|
|
42
42
|
// When done, show both the message and user's choice in timeline
|
|
43
43
|
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, marginLeft: 1, children: _jsx(Text, { color: undefined, children: message }) }), _jsxs(UserQuery, { children: ["> ", options[selectedIndex].label] })] }));
|
|
44
44
|
}
|
|
45
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, marginLeft: 1, children: _jsx(Text, { color: isActive
|
|
45
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, marginLeft: 1, children: _jsx(Text, { color: getTextColor(isActive), children: message }) }), _jsxs(Box, { marginLeft: 1, children: [_jsx(Text, { color: Colors.Action.Select, children: ">" }), _jsx(Text, { children: " " }), _jsx(Box, { children: options.map((option, index) => {
|
|
46
46
|
const isSelected = index === selectedIndex;
|
|
47
47
|
return (_jsx(Box, { marginRight: 2, children: _jsx(Text, { color: isSelected ? option.color : undefined, dimColor: !isSelected, children: option.label }) }, option.value));
|
|
48
48
|
}) })] })] }));
|