prompt-language-shell 0.8.2 → 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 +42 -55
- package/dist/services/colors.js +1 -0
- package/dist/services/components.js +5 -13
- package/dist/services/configuration.js +23 -23
- 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/router.js +25 -25
- package/dist/services/skills.js +12 -11
- package/dist/services/validator.js +4 -3
- package/dist/types/guards.js +4 -6
- package/dist/types/handlers.js +1 -0
- package/dist/types/schemas.js +103 -0
- package/dist/types/types.js +1 -0
- package/dist/ui/Answer.js +10 -9
- package/dist/ui/Command.js +20 -14
- package/dist/ui/Config.js +8 -7
- package/dist/ui/Confirm.js +4 -4
- package/dist/ui/Execute.js +263 -69
- package/dist/ui/Feedback.js +1 -0
- package/dist/ui/Introspect.js +12 -10
- package/dist/ui/Main.js +5 -5
- package/dist/ui/Schedule.js +8 -8
- package/dist/ui/Validate.js +10 -8
- package/dist/ui/Workflow.js +102 -30
- package/package.json +3 -2
package/dist/services/router.js
CHANGED
|
@@ -20,7 +20,7 @@ export function getOperationName(tasks) {
|
|
|
20
20
|
* Route tasks to appropriate components with Confirm flow
|
|
21
21
|
* Handles the complete flow: Plan → Confirm → Execute/Answer/Introspect
|
|
22
22
|
*/
|
|
23
|
-
export function routeTasksWithConfirm(tasks, message, service, userRequest,
|
|
23
|
+
export function routeTasksWithConfirm(tasks, message, service, userRequest, queueHandlers, workflowHandlers, errorHandlers, hasDefineTask = false) {
|
|
24
24
|
if (tasks.length === 0)
|
|
25
25
|
return;
|
|
26
26
|
// Filter out ignore and discard tasks early
|
|
@@ -28,7 +28,7 @@ export function routeTasksWithConfirm(tasks, message, service, userRequest, hand
|
|
|
28
28
|
// Check if no valid tasks remain after filtering
|
|
29
29
|
if (validTasks.length === 0) {
|
|
30
30
|
const message = createMessage(getUnknownRequestMessage());
|
|
31
|
-
|
|
31
|
+
queueHandlers.addToQueue(message);
|
|
32
32
|
return;
|
|
33
33
|
}
|
|
34
34
|
const operation = getOperationName(validTasks);
|
|
@@ -36,7 +36,7 @@ export function routeTasksWithConfirm(tasks, message, service, userRequest, hand
|
|
|
36
36
|
// Has DEFINE tasks - add Schedule to queue for user selection
|
|
37
37
|
// Refinement flow will call this function again with refined tasks
|
|
38
38
|
const scheduleDefinition = createScheduleDefinition(message, validTasks);
|
|
39
|
-
|
|
39
|
+
queueHandlers.addToQueue(scheduleDefinition);
|
|
40
40
|
}
|
|
41
41
|
else {
|
|
42
42
|
// No DEFINE tasks - Schedule auto-completes and adds Confirm to queue
|
|
@@ -47,17 +47,17 @@ export function routeTasksWithConfirm(tasks, message, service, userRequest, hand
|
|
|
47
47
|
// Schedule completed - add Confirm to queue
|
|
48
48
|
const confirmDefinition = createConfirmDefinition(() => {
|
|
49
49
|
// User confirmed - complete both Confirm and Schedule, then route to appropriate component
|
|
50
|
-
|
|
51
|
-
executeTasksAfterConfirm(validTasks, service, userRequest,
|
|
50
|
+
workflowHandlers.completeActiveAndPending();
|
|
51
|
+
executeTasksAfterConfirm(validTasks, service, userRequest, queueHandlers, errorHandlers);
|
|
52
52
|
}, () => {
|
|
53
53
|
// User cancelled - complete both Confirm and Schedule, then show cancellation
|
|
54
|
-
|
|
54
|
+
workflowHandlers.completeActiveAndPending();
|
|
55
55
|
const message = getCancellationMessage(operation);
|
|
56
|
-
|
|
56
|
+
queueHandlers.addToQueue(createFeedback(FeedbackType.Aborted, message));
|
|
57
57
|
});
|
|
58
|
-
|
|
58
|
+
queueHandlers.addToQueue(confirmDefinition);
|
|
59
59
|
});
|
|
60
|
-
|
|
60
|
+
queueHandlers.addToQueue(scheduleDefinition);
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
/**
|
|
@@ -88,13 +88,13 @@ function validateTaskTypes(tasks) {
|
|
|
88
88
|
* Validates task types and routes each type appropriately
|
|
89
89
|
* Supports mixed types at top level with Groups
|
|
90
90
|
*/
|
|
91
|
-
function executeTasksAfterConfirm(tasks, service, userRequest,
|
|
91
|
+
function executeTasksAfterConfirm(tasks, service, userRequest, queueHandlers, errorHandlers) {
|
|
92
92
|
// Validate task types (Groups must have uniform subtasks)
|
|
93
93
|
try {
|
|
94
94
|
validateTaskTypes(tasks);
|
|
95
95
|
}
|
|
96
96
|
catch (error) {
|
|
97
|
-
|
|
97
|
+
errorHandlers.onError(error instanceof Error ? error.message : String(error));
|
|
98
98
|
return;
|
|
99
99
|
}
|
|
100
100
|
const scheduledTasks = asScheduledTasks(tasks);
|
|
@@ -117,7 +117,7 @@ function executeTasksAfterConfirm(tasks, service, userRequest, handlers) {
|
|
|
117
117
|
const taskType = type;
|
|
118
118
|
if (typeTasks.length === 0)
|
|
119
119
|
continue;
|
|
120
|
-
routeTasksByType(taskType, typeTasks, service, userRequest,
|
|
120
|
+
routeTasksByType(taskType, typeTasks, service, userRequest, queueHandlers, errorHandlers);
|
|
121
121
|
}
|
|
122
122
|
consecutiveStandaloneTasks = [];
|
|
123
123
|
};
|
|
@@ -130,7 +130,7 @@ function executeTasksAfterConfirm(tasks, service, userRequest, handlers) {
|
|
|
130
130
|
if (task.subtasks.length > 0) {
|
|
131
131
|
const subtasks = task.subtasks;
|
|
132
132
|
const taskType = subtasks[0].type;
|
|
133
|
-
routeTasksByType(taskType, subtasks, service, userRequest,
|
|
133
|
+
routeTasksByType(taskType, subtasks, service, userRequest, queueHandlers, errorHandlers);
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
else {
|
|
@@ -145,22 +145,22 @@ function executeTasksAfterConfirm(tasks, service, userRequest, handlers) {
|
|
|
145
145
|
* Route tasks by type to appropriate components
|
|
146
146
|
* Extracted to allow reuse for both Groups and standalone tasks
|
|
147
147
|
*/
|
|
148
|
-
function routeTasksByType(taskType, typeTasks, service, userRequest,
|
|
148
|
+
function routeTasksByType(taskType, typeTasks, service, userRequest, queueHandlers, errorHandlers) {
|
|
149
149
|
if (taskType === TaskType.Answer) {
|
|
150
150
|
// Create separate Answer component for each question
|
|
151
151
|
for (const task of typeTasks) {
|
|
152
|
-
|
|
152
|
+
queueHandlers.addToQueue(createAnswerDefinition(task.action, service));
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
else if (taskType === TaskType.Introspect) {
|
|
156
|
-
|
|
156
|
+
queueHandlers.addToQueue(createIntrospectDefinition(typeTasks, service));
|
|
157
157
|
}
|
|
158
158
|
else if (taskType === TaskType.Config) {
|
|
159
159
|
// Route to Config flow - extract keys from task params
|
|
160
160
|
const configKeys = typeTasks
|
|
161
161
|
.map((task) => task.params?.key)
|
|
162
162
|
.filter((key) => key !== undefined);
|
|
163
|
-
|
|
163
|
+
queueHandlers.addToQueue(createConfigDefinitionWithKeys(configKeys, (config) => {
|
|
164
164
|
// Save config - Config component will handle completion and feedback
|
|
165
165
|
try {
|
|
166
166
|
// Convert flat dotted keys to nested structure grouped by section
|
|
@@ -177,7 +177,7 @@ function routeTasksByType(taskType, typeTasks, service, userRequest, handlers) {
|
|
|
177
177
|
throw new Error(errorMessage);
|
|
178
178
|
}
|
|
179
179
|
}, (operation) => {
|
|
180
|
-
|
|
180
|
+
errorHandlers.onAborted(operation);
|
|
181
181
|
}));
|
|
182
182
|
}
|
|
183
183
|
else if (taskType === TaskType.Execute) {
|
|
@@ -192,26 +192,26 @@ function routeTasksByType(taskType, typeTasks, service, userRequest, handlers) {
|
|
|
192
192
|
.join('\n');
|
|
193
193
|
return `Invalid skill definition "${error.skill}":\n\n${issuesList}`;
|
|
194
194
|
});
|
|
195
|
-
|
|
195
|
+
queueHandlers.addToQueue(createFeedback(FeedbackType.Failed, errorMessages.join('\n\n')));
|
|
196
196
|
}
|
|
197
197
|
else if (validation.missingConfig.length > 0) {
|
|
198
|
-
|
|
199
|
-
|
|
198
|
+
queueHandlers.addToQueue(createValidateDefinition(validation.missingConfig, userRequest, service, (error) => {
|
|
199
|
+
errorHandlers.onError(error);
|
|
200
200
|
}, () => {
|
|
201
|
-
|
|
201
|
+
queueHandlers.addToQueue(createExecuteDefinition(typeTasks, service));
|
|
202
202
|
}, (operation) => {
|
|
203
|
-
|
|
203
|
+
errorHandlers.onAborted(operation);
|
|
204
204
|
}));
|
|
205
205
|
}
|
|
206
206
|
else {
|
|
207
|
-
|
|
207
|
+
queueHandlers.addToQueue(createExecuteDefinition(typeTasks, service));
|
|
208
208
|
}
|
|
209
209
|
}
|
|
210
210
|
catch (error) {
|
|
211
211
|
// Handle skill reference errors (e.g., unknown skills)
|
|
212
212
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
213
213
|
const message = createMessage(errorMessage);
|
|
214
|
-
|
|
214
|
+
queueHandlers.addToQueue(message);
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
217
|
}
|
package/dist/services/skills.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { existsSync, readdirSync, readFileSync } from 'fs';
|
|
2
1
|
import { homedir } from 'os';
|
|
3
2
|
import { join } from 'path';
|
|
3
|
+
import { defaultFileSystem } from './filesystem.js';
|
|
4
|
+
import { displayWarning } from './logger.js';
|
|
4
5
|
import { getUnknownSkillMessage } from './messages.js';
|
|
5
6
|
import { parseSkillMarkdown, displayNameToKey } from './parser.js';
|
|
6
7
|
/**
|
|
@@ -48,14 +49,14 @@ export function getSkillsDirectory() {
|
|
|
48
49
|
* Returns an array of objects with filename (key) and content
|
|
49
50
|
* Filters out invalid filenames and conflicts with system skills
|
|
50
51
|
*/
|
|
51
|
-
export function loadSkills() {
|
|
52
|
+
export function loadSkills(fs = defaultFileSystem) {
|
|
52
53
|
const skillsDir = getSkillsDirectory();
|
|
53
54
|
// Return empty array if directory doesn't exist
|
|
54
|
-
if (!
|
|
55
|
+
if (!fs.exists(skillsDir)) {
|
|
55
56
|
return [];
|
|
56
57
|
}
|
|
57
58
|
try {
|
|
58
|
-
const files =
|
|
59
|
+
const files = fs.readDirectory(skillsDir);
|
|
59
60
|
// Filter and map valid skill files
|
|
60
61
|
return files
|
|
61
62
|
.filter((file) => {
|
|
@@ -75,12 +76,12 @@ export function loadSkills() {
|
|
|
75
76
|
// Extract key (filename without extension, handles both .md and .MD)
|
|
76
77
|
const key = file.slice(0, -3);
|
|
77
78
|
const filePath = join(skillsDir, file);
|
|
78
|
-
const content =
|
|
79
|
+
const content = fs.readFile(filePath, 'utf-8');
|
|
79
80
|
return { key, content };
|
|
80
81
|
});
|
|
81
82
|
}
|
|
82
|
-
catch {
|
|
83
|
-
|
|
83
|
+
catch (error) {
|
|
84
|
+
displayWarning('Failed to load skills directory', error);
|
|
84
85
|
return [];
|
|
85
86
|
}
|
|
86
87
|
}
|
|
@@ -88,16 +89,16 @@ export function loadSkills() {
|
|
|
88
89
|
* Load and parse all skill definitions
|
|
89
90
|
* Returns structured skill definitions (including invalid skills)
|
|
90
91
|
*/
|
|
91
|
-
export function loadSkillDefinitions() {
|
|
92
|
-
const skills = loadSkills();
|
|
92
|
+
export function loadSkillDefinitions(fs = defaultFileSystem) {
|
|
93
|
+
const skills = loadSkills(fs);
|
|
93
94
|
return skills.map(({ key, content }) => parseSkillMarkdown(key, content));
|
|
94
95
|
}
|
|
95
96
|
/**
|
|
96
97
|
* Load skills and mark incomplete ones in their markdown
|
|
97
98
|
* Returns array of skill markdown with status markers
|
|
98
99
|
*/
|
|
99
|
-
export function loadSkillsWithValidation() {
|
|
100
|
-
const skills = loadSkills();
|
|
100
|
+
export function loadSkillsWithValidation(fs = defaultFileSystem) {
|
|
101
|
+
const skills = loadSkills(fs);
|
|
101
102
|
return skills.map(({ key, content }) => {
|
|
102
103
|
const parsed = parseSkillMarkdown(key, content);
|
|
103
104
|
// If skill is incomplete (either validation failed or needs more documentation), append (INCOMPLETE) to the name
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
+
import { defaultFileSystem } from './filesystem.js';
|
|
1
2
|
import { loadUserConfig, hasConfigPath } from './loader.js';
|
|
2
3
|
import { loadSkillDefinitions, createSkillLookup } from './skills.js';
|
|
3
4
|
/**
|
|
4
5
|
* Validate config requirements for execute tasks
|
|
5
6
|
* Returns validation result with missing config and validation errors
|
|
6
7
|
*/
|
|
7
|
-
export function validateExecuteTasks(tasks) {
|
|
8
|
-
const userConfig = loadUserConfig();
|
|
8
|
+
export function validateExecuteTasks(tasks, fs = defaultFileSystem) {
|
|
9
|
+
const userConfig = loadUserConfig(fs);
|
|
9
10
|
const missing = [];
|
|
10
11
|
const seenPaths = new Set();
|
|
11
12
|
const validationErrors = [];
|
|
12
13
|
const seenSkills = new Set();
|
|
13
14
|
// Load all skills (including invalid ones for validation)
|
|
14
|
-
const parsedSkills = loadSkillDefinitions();
|
|
15
|
+
const parsedSkills = loadSkillDefinitions(fs);
|
|
15
16
|
const skillLookup = createSkillLookup(parsedSkills);
|
|
16
17
|
// Check for invalid skills being used in tasks
|
|
17
18
|
for (const task of tasks) {
|
package/dist/types/guards.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { TaskType } from './types.js';
|
|
2
|
+
import { TaskSchema } from './schemas.js';
|
|
2
3
|
/**
|
|
3
4
|
* Type guard to check if a task is a ScheduledTask
|
|
4
5
|
* ScheduledTask has optional subtasks property or is a Group type
|
|
@@ -14,12 +15,9 @@ export function asScheduledTasks(tasks) {
|
|
|
14
15
|
return tasks;
|
|
15
16
|
}
|
|
16
17
|
/**
|
|
17
|
-
* Type guard to check if a value is a valid Task
|
|
18
|
+
* Type guard to check if a value is a valid Task.
|
|
19
|
+
* Uses Zod schema for comprehensive runtime validation.
|
|
18
20
|
*/
|
|
19
21
|
export function isTask(value) {
|
|
20
|
-
return (
|
|
21
|
-
value !== null &&
|
|
22
|
-
'action' in value &&
|
|
23
|
-
typeof value.action === 'string' &&
|
|
24
|
-
'type' in value);
|
|
22
|
+
return TaskSchema.safeParse(value).success;
|
|
25
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
|
@@ -38,6 +38,7 @@ export var Origin;
|
|
|
38
38
|
export var FeedbackType;
|
|
39
39
|
(function (FeedbackType) {
|
|
40
40
|
FeedbackType["Info"] = "info";
|
|
41
|
+
FeedbackType["Warning"] = "warning";
|
|
41
42
|
FeedbackType["Succeeded"] = "succeeded";
|
|
42
43
|
FeedbackType["Aborted"] = "aborted";
|
|
43
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(() => {
|
|
@@ -30,26 +29,28 @@ export function Answer({ question, state, status, service, handlers, }) {
|
|
|
30
29
|
const result = await withMinimumTime(() => svc.processWithTool(question, 'answer'), MINIMUM_PROCESSING_TIME);
|
|
31
30
|
if (mounted) {
|
|
32
31
|
// Add debug components to timeline if present
|
|
33
|
-
|
|
32
|
+
if (result.debug?.length) {
|
|
33
|
+
workflowHandlers?.addToTimeline(...result.debug);
|
|
34
|
+
}
|
|
34
35
|
// Extract answer from result
|
|
35
36
|
const answerText = result.answer || '';
|
|
36
37
|
setAnswer(answerText);
|
|
37
38
|
// Update component state so answer persists in timeline
|
|
38
|
-
|
|
39
|
+
stateHandlers?.updateState({
|
|
39
40
|
answer: answerText,
|
|
40
41
|
});
|
|
41
42
|
// Signal completion
|
|
42
|
-
|
|
43
|
+
lifecycleHandlers?.completeActive();
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
46
|
catch (err) {
|
|
46
47
|
if (mounted) {
|
|
47
48
|
const errorMessage = formatErrorMessage(err);
|
|
48
49
|
setError(errorMessage);
|
|
49
|
-
|
|
50
|
+
stateHandlers?.updateState({
|
|
50
51
|
error: errorMessage,
|
|
51
52
|
});
|
|
52
|
-
|
|
53
|
+
errorHandlers?.onError(errorMessage);
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
56
|
}
|
|
@@ -57,7 +58,7 @@ export function Answer({ question, state, status, service, handlers, }) {
|
|
|
57
58
|
return () => {
|
|
58
59
|
mounted = false;
|
|
59
60
|
};
|
|
60
|
-
}, [question, isActive, service
|
|
61
|
+
}, [question, isActive, service]);
|
|
61
62
|
const lines = answer ? answer.split('\n') : [];
|
|
62
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] }) }))] }));
|
|
63
64
|
}
|
package/dist/ui/Command.js
CHANGED
|
@@ -4,7 +4,7 @@ import { Box, Text } from 'ink';
|
|
|
4
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 });
|
|
@@ -51,33 +51,39 @@ export function Command({ command, state, status, service, handlers, onAborted,
|
|
|
51
51
|
const debugComponents = allConfig
|
|
52
52
|
? [...scheduleDebug, ...(result.debug || [])]
|
|
53
53
|
: scheduleDebug;
|
|
54
|
-
|
|
54
|
+
if (debugComponents.length > 0) {
|
|
55
|
+
workflowHandlers?.addToTimeline(...debugComponents);
|
|
56
|
+
}
|
|
55
57
|
// Save result to state for timeline display
|
|
56
|
-
|
|
58
|
+
stateHandlers?.updateState({
|
|
57
59
|
message: result.message,
|
|
58
60
|
tasks: result.tasks,
|
|
59
61
|
});
|
|
60
62
|
// Check if tasks contain DEFINE type (variant selection needed)
|
|
61
63
|
const hasDefineTask = result.tasks.some((task) => task.type === TaskType.Define);
|
|
62
|
-
|
|
64
|
+
// Guard: ensure all required handlers are present
|
|
65
|
+
if (!queueHandlers ||
|
|
66
|
+
!lifecycleHandlers ||
|
|
67
|
+
!workflowHandlers ||
|
|
68
|
+
!errorHandlers) {
|
|
63
69
|
return;
|
|
64
70
|
}
|
|
65
71
|
// Create Schedule definition
|
|
66
72
|
const scheduleDefinition = createScheduleDefinition(result.message, result.tasks, hasDefineTask
|
|
67
73
|
? async (selectedTasks) => {
|
|
68
74
|
// Refinement flow for DEFINE tasks
|
|
69
|
-
await handleRefinement(selectedTasks, svc, command,
|
|
75
|
+
await handleRefinement(selectedTasks, svc, command, queueHandlers, lifecycleHandlers, workflowHandlers, errorHandlers);
|
|
70
76
|
}
|
|
71
77
|
: undefined);
|
|
72
78
|
if (hasDefineTask) {
|
|
73
79
|
// DEFINE tasks: Move Command to timeline, add Schedule to queue
|
|
74
|
-
|
|
75
|
-
|
|
80
|
+
lifecycleHandlers.completeActive();
|
|
81
|
+
queueHandlers.addToQueue(scheduleDefinition);
|
|
76
82
|
}
|
|
77
83
|
else {
|
|
78
84
|
// No DEFINE tasks: Complete Command, then route to Confirm flow
|
|
79
|
-
|
|
80
|
-
routeTasksWithConfirm(result.tasks, result.message, svc, command,
|
|
85
|
+
lifecycleHandlers.completeActive();
|
|
86
|
+
routeTasksWithConfirm(result.tasks, result.message, svc, command, queueHandlers, workflowHandlers, errorHandlers, false);
|
|
81
87
|
}
|
|
82
88
|
}
|
|
83
89
|
}
|
|
@@ -86,10 +92,10 @@ export function Command({ command, state, status, service, handlers, onAborted,
|
|
|
86
92
|
if (mounted) {
|
|
87
93
|
const errorMessage = formatErrorMessage(err);
|
|
88
94
|
setError(errorMessage);
|
|
89
|
-
|
|
95
|
+
stateHandlers?.updateState({
|
|
90
96
|
error: errorMessage,
|
|
91
97
|
});
|
|
92
|
-
|
|
98
|
+
errorHandlers?.onError(errorMessage);
|
|
93
99
|
}
|
|
94
100
|
}
|
|
95
101
|
}
|
|
@@ -97,6 +103,6 @@ export function Command({ command, state, status, service, handlers, onAborted,
|
|
|
97
103
|
return () => {
|
|
98
104
|
mounted = false;
|
|
99
105
|
};
|
|
100
|
-
}, [command, isActive, service
|
|
106
|
+
}, [command, isActive, service]);
|
|
101
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] }) }))] }));
|
|
102
108
|
}
|
package/dist/ui/Config.js
CHANGED
|
@@ -78,7 +78,8 @@ 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 function Config(
|
|
81
|
+
export function Config(props) {
|
|
82
|
+
const { steps, state, status, debug = DebugLevel.None, stateHandlers, lifecycleHandlers, onFinished, onAborted, } = props;
|
|
82
83
|
const isActive = status === ComponentStatus.Active;
|
|
83
84
|
const [step, setStep] = useState(!isActive ? (state?.completedStep ?? steps.length) : 0);
|
|
84
85
|
const [values, setValues] = useState(() => {
|
|
@@ -170,7 +171,7 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
|
|
|
170
171
|
setValues({ ...values, [configKey]: currentValue });
|
|
171
172
|
}
|
|
172
173
|
// Save state before aborting
|
|
173
|
-
|
|
174
|
+
stateHandlers?.updateState({
|
|
174
175
|
values,
|
|
175
176
|
completedStep: step,
|
|
176
177
|
selectedIndex,
|
|
@@ -179,7 +180,7 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
|
|
|
179
180
|
onAborted('configuration');
|
|
180
181
|
}
|
|
181
182
|
// Complete with abort feedback
|
|
182
|
-
|
|
183
|
+
lifecycleHandlers?.completeActive(createFeedback(FeedbackType.Aborted, 'Configuration cancelled.'));
|
|
183
184
|
return;
|
|
184
185
|
}
|
|
185
186
|
// Handle selection step navigation
|
|
@@ -236,19 +237,19 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
|
|
|
236
237
|
completedStep: steps.length,
|
|
237
238
|
selectedIndex,
|
|
238
239
|
};
|
|
239
|
-
|
|
240
|
+
stateHandlers?.updateState(stateUpdate);
|
|
240
241
|
// Call onFinished callback and handle result
|
|
241
242
|
try {
|
|
242
243
|
if (onFinished) {
|
|
243
244
|
onFinished(newValues);
|
|
244
245
|
}
|
|
245
246
|
// Success - complete with success feedback
|
|
246
|
-
|
|
247
|
+
lifecycleHandlers?.completeActive(createFeedback(FeedbackType.Succeeded, 'Configuration saved successfully.'));
|
|
247
248
|
}
|
|
248
249
|
catch (error) {
|
|
249
250
|
// Failure - complete with error feedback
|
|
250
251
|
const errorMessage = error instanceof Error ? error.message : 'Configuration failed';
|
|
251
|
-
|
|
252
|
+
lifecycleHandlers?.completeActive(createFeedback(FeedbackType.Failed, errorMessage));
|
|
252
253
|
}
|
|
253
254
|
setStep(steps.length);
|
|
254
255
|
}
|
|
@@ -259,7 +260,7 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
|
|
|
259
260
|
completedStep: step + 1,
|
|
260
261
|
selectedIndex,
|
|
261
262
|
};
|
|
262
|
-
|
|
263
|
+
stateHandlers?.updateState(stateUpdate);
|
|
263
264
|
const nextStep = step + 1;
|
|
264
265
|
setStep(nextStep);
|
|
265
266
|
// Reset selectedIndex for next step
|
package/dist/ui/Confirm.js
CHANGED
|
@@ -5,7 +5,7 @@ import { ComponentStatus } from '../types/components.js';
|
|
|
5
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,18 +14,18 @@ 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
|
-
|
|
17
|
+
stateHandlers?.updateState({ selectedIndex: 1 });
|
|
18
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
30
|
onConfirmed();
|
|
31
31
|
}
|