prompt-language-shell 0.8.2 → 0.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/configuration/io.js +85 -0
- package/dist/configuration/messages.js +30 -0
- package/dist/configuration/schema.js +167 -0
- package/dist/configuration/transformation.js +55 -0
- package/dist/configuration/types.js +30 -0
- package/dist/configuration/validation.js +52 -0
- package/dist/execution/handlers.js +135 -0
- package/dist/execution/processing.js +35 -0
- package/dist/execution/reducer.js +148 -0
- package/dist/execution/types.js +12 -0
- package/dist/execution/validation.js +12 -0
- package/dist/index.js +1 -1
- package/dist/services/anthropic.js +43 -56
- package/dist/services/colors.js +2 -1
- package/dist/services/components.js +40 -24
- package/dist/services/config-labels.js +15 -15
- package/dist/services/filesystem.js +114 -0
- package/dist/services/loader.js +8 -5
- package/dist/services/logger.js +26 -1
- package/dist/services/messages.js +32 -1
- package/dist/services/parser.js +3 -1
- package/dist/services/refinement.js +10 -10
- package/dist/services/router.js +43 -27
- 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 +38 -16
- package/dist/ui/Command.js +48 -22
- package/dist/ui/Component.js +147 -33
- package/dist/ui/Config.js +69 -78
- package/dist/ui/Confirm.js +34 -21
- package/dist/ui/Execute.js +151 -178
- package/dist/ui/Feedback.js +1 -0
- package/dist/ui/Introspect.js +54 -25
- package/dist/ui/Label.js +1 -1
- package/dist/ui/Main.js +10 -6
- package/dist/ui/Refinement.js +8 -1
- package/dist/ui/Schedule.js +76 -53
- package/dist/ui/Validate.js +77 -77
- package/dist/ui/Workflow.js +60 -61
- package/package.json +3 -2
- package/dist/services/configuration.js +0 -409
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { ExecutionStatus } from '../services/shell.js';
|
|
2
|
+
import { formatDuration } from '../services/utils.js';
|
|
3
|
+
import { ExecuteActionType, } from './types.js';
|
|
4
|
+
export const initialState = {
|
|
5
|
+
error: null,
|
|
6
|
+
taskInfos: [],
|
|
7
|
+
message: '',
|
|
8
|
+
completed: 0,
|
|
9
|
+
hasProcessed: false,
|
|
10
|
+
taskExecutionTimes: [],
|
|
11
|
+
completionMessage: null,
|
|
12
|
+
summary: '',
|
|
13
|
+
};
|
|
14
|
+
export function executeReducer(state, action) {
|
|
15
|
+
switch (action.type) {
|
|
16
|
+
case ExecuteActionType.ProcessingComplete:
|
|
17
|
+
return {
|
|
18
|
+
...state,
|
|
19
|
+
message: action.payload.message,
|
|
20
|
+
hasProcessed: true,
|
|
21
|
+
};
|
|
22
|
+
case ExecuteActionType.CommandsReady:
|
|
23
|
+
return {
|
|
24
|
+
...state,
|
|
25
|
+
message: action.payload.message,
|
|
26
|
+
summary: action.payload.summary,
|
|
27
|
+
taskInfos: action.payload.taskInfos,
|
|
28
|
+
completed: 0,
|
|
29
|
+
};
|
|
30
|
+
case ExecuteActionType.ProcessingError:
|
|
31
|
+
return {
|
|
32
|
+
...state,
|
|
33
|
+
error: action.payload.error,
|
|
34
|
+
hasProcessed: true,
|
|
35
|
+
};
|
|
36
|
+
case ExecuteActionType.TaskComplete: {
|
|
37
|
+
const updatedTimes = [
|
|
38
|
+
...state.taskExecutionTimes,
|
|
39
|
+
action.payload.elapsed,
|
|
40
|
+
];
|
|
41
|
+
const updatedTaskInfos = state.taskInfos.map((task, i) => i === action.payload.index
|
|
42
|
+
? {
|
|
43
|
+
...task,
|
|
44
|
+
status: ExecutionStatus.Success,
|
|
45
|
+
elapsed: action.payload.elapsed,
|
|
46
|
+
}
|
|
47
|
+
: task);
|
|
48
|
+
return {
|
|
49
|
+
...state,
|
|
50
|
+
taskInfos: updatedTaskInfos,
|
|
51
|
+
taskExecutionTimes: updatedTimes,
|
|
52
|
+
completed: action.payload.index + 1,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
case ExecuteActionType.AllTasksComplete: {
|
|
56
|
+
const updatedTimes = [
|
|
57
|
+
...state.taskExecutionTimes,
|
|
58
|
+
action.payload.elapsed,
|
|
59
|
+
];
|
|
60
|
+
const updatedTaskInfos = state.taskInfos.map((task, i) => i === action.payload.index
|
|
61
|
+
? {
|
|
62
|
+
...task,
|
|
63
|
+
status: ExecutionStatus.Success,
|
|
64
|
+
elapsed: action.payload.elapsed,
|
|
65
|
+
}
|
|
66
|
+
: task);
|
|
67
|
+
const totalElapsed = updatedTimes.reduce((sum, time) => sum + time, 0);
|
|
68
|
+
const completion = `${action.payload.summaryText} in ${formatDuration(totalElapsed)}.`;
|
|
69
|
+
return {
|
|
70
|
+
...state,
|
|
71
|
+
taskInfos: updatedTaskInfos,
|
|
72
|
+
taskExecutionTimes: updatedTimes,
|
|
73
|
+
completed: action.payload.index + 1,
|
|
74
|
+
completionMessage: completion,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
case ExecuteActionType.TaskErrorCritical: {
|
|
78
|
+
const updatedTaskInfos = state.taskInfos.map((task, i) => i === action.payload.index
|
|
79
|
+
? { ...task, status: ExecutionStatus.Failed, elapsed: 0 }
|
|
80
|
+
: task);
|
|
81
|
+
return {
|
|
82
|
+
...state,
|
|
83
|
+
taskInfos: updatedTaskInfos,
|
|
84
|
+
error: action.payload.error,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
case ExecuteActionType.TaskErrorContinue: {
|
|
88
|
+
const updatedTimes = [
|
|
89
|
+
...state.taskExecutionTimes,
|
|
90
|
+
action.payload.elapsed,
|
|
91
|
+
];
|
|
92
|
+
const updatedTaskInfos = state.taskInfos.map((task, i) => i === action.payload.index
|
|
93
|
+
? {
|
|
94
|
+
...task,
|
|
95
|
+
status: ExecutionStatus.Failed,
|
|
96
|
+
elapsed: action.payload.elapsed,
|
|
97
|
+
}
|
|
98
|
+
: task);
|
|
99
|
+
return {
|
|
100
|
+
...state,
|
|
101
|
+
taskInfos: updatedTaskInfos,
|
|
102
|
+
taskExecutionTimes: updatedTimes,
|
|
103
|
+
completed: action.payload.index + 1,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
case ExecuteActionType.LastTaskError: {
|
|
107
|
+
const updatedTimes = [
|
|
108
|
+
...state.taskExecutionTimes,
|
|
109
|
+
action.payload.elapsed,
|
|
110
|
+
];
|
|
111
|
+
const updatedTaskInfos = state.taskInfos.map((task, i) => i === action.payload.index
|
|
112
|
+
? {
|
|
113
|
+
...task,
|
|
114
|
+
status: ExecutionStatus.Failed,
|
|
115
|
+
elapsed: action.payload.elapsed,
|
|
116
|
+
}
|
|
117
|
+
: task);
|
|
118
|
+
const totalElapsed = updatedTimes.reduce((sum, time) => sum + time, 0);
|
|
119
|
+
const completion = `${action.payload.summaryText} in ${formatDuration(totalElapsed)}.`;
|
|
120
|
+
return {
|
|
121
|
+
...state,
|
|
122
|
+
taskInfos: updatedTaskInfos,
|
|
123
|
+
taskExecutionTimes: updatedTimes,
|
|
124
|
+
completed: action.payload.index + 1,
|
|
125
|
+
completionMessage: completion,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
case ExecuteActionType.CancelExecution: {
|
|
129
|
+
const updatedTaskInfos = state.taskInfos.map((task, taskIndex) => {
|
|
130
|
+
if (taskIndex < action.payload.completed) {
|
|
131
|
+
return { ...task, status: ExecutionStatus.Success };
|
|
132
|
+
}
|
|
133
|
+
else if (taskIndex === action.payload.completed) {
|
|
134
|
+
return { ...task, status: ExecutionStatus.Aborted };
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
return { ...task, status: ExecutionStatus.Cancelled };
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
return {
|
|
141
|
+
...state,
|
|
142
|
+
taskInfos: updatedTaskInfos,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
default:
|
|
146
|
+
return state;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export var ExecuteActionType;
|
|
2
|
+
(function (ExecuteActionType) {
|
|
3
|
+
ExecuteActionType["ProcessingComplete"] = "PROCESSING_COMPLETE";
|
|
4
|
+
ExecuteActionType["CommandsReady"] = "COMMANDS_READY";
|
|
5
|
+
ExecuteActionType["ProcessingError"] = "PROCESSING_ERROR";
|
|
6
|
+
ExecuteActionType["TaskComplete"] = "TASK_COMPLETE";
|
|
7
|
+
ExecuteActionType["AllTasksComplete"] = "ALL_TASKS_COMPLETE";
|
|
8
|
+
ExecuteActionType["TaskErrorCritical"] = "TASK_ERROR_CRITICAL";
|
|
9
|
+
ExecuteActionType["TaskErrorContinue"] = "TASK_ERROR_CONTINUE";
|
|
10
|
+
ExecuteActionType["LastTaskError"] = "LAST_TASK_ERROR";
|
|
11
|
+
ExecuteActionType["CancelExecution"] = "CANCEL_EXECUTION";
|
|
12
|
+
})(ExecuteActionType || (ExecuteActionType = {}));
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { getUnresolvedPlaceholdersMessage } from '../services/messages.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validates that all placeholders in a command have been resolved.
|
|
4
|
+
* Throws an error if unresolved placeholders are found.
|
|
5
|
+
*/
|
|
6
|
+
export function validatePlaceholderResolution(command) {
|
|
7
|
+
const unresolvedPattern = /\{[^}]+\}/g;
|
|
8
|
+
const matches = command.match(unresolvedPattern);
|
|
9
|
+
if (matches && matches.length > 0) {
|
|
10
|
+
throw new Error(getUnresolvedPlaceholdersMessage(matches.length));
|
|
11
|
+
}
|
|
12
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { existsSync, readFileSync } from 'fs';
|
|
|
4
4
|
import { dirname, join } from 'path';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import { render } from 'ink';
|
|
7
|
-
import { DebugLevel } from './
|
|
7
|
+
import { DebugLevel } from './configuration/types.js';
|
|
8
8
|
import { Main } from './ui/Main.js';
|
|
9
9
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
10
|
const __dirname = dirname(__filename);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import Anthropic from '@anthropic-ai/sdk';
|
|
2
|
-
import { getAvailableConfigStructure, getConfiguredKeys, } from '
|
|
2
|
+
import { getAvailableConfigStructure, getConfiguredKeys, } from '../configuration/schema.js';
|
|
3
3
|
import { logPrompt, logResponse } from './logger.js';
|
|
4
4
|
import { formatSkillsForPrompt, loadSkillsWithValidation } from './skills.js';
|
|
5
5
|
import { toolRegistry } from './registry.js';
|
|
6
|
+
import { CommandResultSchema, IntrospectResultSchema, } from '../types/schemas.js';
|
|
6
7
|
/**
|
|
7
8
|
* Wraps text to ensure no line exceeds the specified width.
|
|
8
9
|
* Breaks at word boundaries to maintain readability.
|
|
@@ -45,6 +46,19 @@ export function cleanAnswerText(text) {
|
|
|
45
46
|
cleaned = wrapText(cleaned, 80);
|
|
46
47
|
return cleaned;
|
|
47
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Formats Zod validation errors into readable error messages.
|
|
51
|
+
* Provides detailed information about what failed validation.
|
|
52
|
+
*/
|
|
53
|
+
function formatValidationError(error) {
|
|
54
|
+
const issues = error.issues
|
|
55
|
+
.map((issue) => {
|
|
56
|
+
const path = issue.path.length > 0 ? issue.path.join('.') : 'root';
|
|
57
|
+
return ` - ${path}: ${issue.message}`;
|
|
58
|
+
})
|
|
59
|
+
.join('\n');
|
|
60
|
+
return `LLM response validation failed:\n${issues}`;
|
|
61
|
+
}
|
|
48
62
|
export class AnthropicService {
|
|
49
63
|
client;
|
|
50
64
|
model;
|
|
@@ -148,88 +162,61 @@ export class AnthropicService {
|
|
|
148
162
|
const input = content.input;
|
|
149
163
|
// Handle execute tool response
|
|
150
164
|
if (toolName === 'execute') {
|
|
151
|
-
|
|
152
|
-
throw new Error('Invalid tool response: missing or invalid message field');
|
|
153
|
-
}
|
|
154
|
-
if (!input.commands || !Array.isArray(input.commands)) {
|
|
155
|
-
throw new Error('Invalid tool response: missing or invalid commands array');
|
|
156
|
-
}
|
|
157
|
-
// Validate each command has required fields
|
|
158
|
-
input.commands.forEach((cmd, i) => {
|
|
159
|
-
if (!cmd.description || typeof cmd.description !== 'string') {
|
|
160
|
-
throw new Error(`Invalid command at index ${String(i)}: missing or invalid 'description' field`);
|
|
161
|
-
}
|
|
162
|
-
if (!cmd.command || typeof cmd.command !== 'string') {
|
|
163
|
-
throw new Error(`Invalid command at index ${String(i)}: missing or invalid 'command' field`);
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
return {
|
|
165
|
+
const validation = CommandResultSchema.safeParse({
|
|
167
166
|
message: input.message,
|
|
168
167
|
summary: input.summary,
|
|
169
168
|
tasks: [],
|
|
170
169
|
commands: input.commands,
|
|
171
170
|
debug,
|
|
172
|
-
};
|
|
171
|
+
});
|
|
172
|
+
if (!validation.success) {
|
|
173
|
+
throw new Error(`I received an unexpected response while preparing to execute commands:\n${formatValidationError(validation.error)}`);
|
|
174
|
+
}
|
|
175
|
+
return validation.data;
|
|
173
176
|
}
|
|
174
177
|
// Handle answer tool response
|
|
175
178
|
if (toolName === 'answer') {
|
|
179
|
+
// Validate question and answer fields exist
|
|
176
180
|
if (!input.question || typeof input.question !== 'string') {
|
|
177
|
-
throw new Error('
|
|
181
|
+
throw new Error('I received an unexpected response while answering your question:\nLLM response validation failed:\n - question: missing or invalid');
|
|
178
182
|
}
|
|
179
183
|
if (!input.answer || typeof input.answer !== 'string') {
|
|
180
|
-
throw new Error('
|
|
184
|
+
throw new Error('I received an unexpected response while answering your question:\nLLM response validation failed:\n - answer: missing or invalid');
|
|
181
185
|
}
|
|
182
|
-
|
|
186
|
+
// Validate the result structure with Zod
|
|
187
|
+
const validation = CommandResultSchema.safeParse({
|
|
183
188
|
message: '',
|
|
184
189
|
tasks: [],
|
|
185
190
|
answer: cleanAnswerText(input.answer),
|
|
186
191
|
debug,
|
|
187
|
-
};
|
|
192
|
+
});
|
|
193
|
+
if (!validation.success) {
|
|
194
|
+
throw new Error(`I received an unexpected response while answering your question:\n${formatValidationError(validation.error)}`);
|
|
195
|
+
}
|
|
196
|
+
return validation.data;
|
|
188
197
|
}
|
|
189
198
|
// Handle introspect tool response
|
|
190
199
|
if (toolName === 'introspect') {
|
|
191
|
-
|
|
192
|
-
throw new Error('Invalid tool response: missing or invalid message field');
|
|
193
|
-
}
|
|
194
|
-
if (!input.capabilities || !Array.isArray(input.capabilities)) {
|
|
195
|
-
throw new Error('Invalid tool response: missing or invalid capabilities array');
|
|
196
|
-
}
|
|
197
|
-
// Validate each capability has required fields
|
|
198
|
-
input.capabilities.forEach((cap, i) => {
|
|
199
|
-
if (!cap.name || typeof cap.name !== 'string') {
|
|
200
|
-
throw new Error(`Invalid capability at index ${String(i)}: missing or invalid 'name' field`);
|
|
201
|
-
}
|
|
202
|
-
if (!cap.description || typeof cap.description !== 'string') {
|
|
203
|
-
throw new Error(`Invalid capability at index ${String(i)}: missing or invalid 'description' field`);
|
|
204
|
-
}
|
|
205
|
-
if (typeof cap.origin !== 'string') {
|
|
206
|
-
throw new Error(`Invalid capability at index ${String(i)}: invalid 'origin' field`);
|
|
207
|
-
}
|
|
208
|
-
});
|
|
209
|
-
return {
|
|
200
|
+
const validation = IntrospectResultSchema.safeParse({
|
|
210
201
|
message: input.message,
|
|
211
202
|
capabilities: input.capabilities,
|
|
212
203
|
debug,
|
|
213
|
-
};
|
|
204
|
+
});
|
|
205
|
+
if (!validation.success) {
|
|
206
|
+
throw new Error(`I received an unexpected response while listing capabilities:\n${formatValidationError(validation.error)}`);
|
|
207
|
+
}
|
|
208
|
+
return validation.data;
|
|
214
209
|
}
|
|
215
210
|
// Handle schedule tool responses
|
|
216
|
-
|
|
217
|
-
throw new Error('Invalid tool response: missing or invalid message field');
|
|
218
|
-
}
|
|
219
|
-
if (!input.tasks || !Array.isArray(input.tasks)) {
|
|
220
|
-
throw new Error('Invalid tool response: missing or invalid tasks array');
|
|
221
|
-
}
|
|
222
|
-
// Validate each task has required action field
|
|
223
|
-
input.tasks.forEach((task, i) => {
|
|
224
|
-
if (!task.action || typeof task.action !== 'string') {
|
|
225
|
-
throw new Error(`Invalid task at index ${String(i)}: missing or invalid 'action' field`);
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
return {
|
|
211
|
+
const validation = CommandResultSchema.safeParse({
|
|
229
212
|
message: input.message,
|
|
230
213
|
tasks: input.tasks,
|
|
231
214
|
debug,
|
|
232
|
-
};
|
|
215
|
+
});
|
|
216
|
+
if (!validation.success) {
|
|
217
|
+
throw new Error(`I received an unexpected response while planning tasks:\n${formatValidationError(validation.error)}`);
|
|
218
|
+
}
|
|
219
|
+
return validation.data;
|
|
233
220
|
}
|
|
234
221
|
}
|
|
235
222
|
export function createAnthropicService(config) {
|
package/dist/services/colors.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { DebugLevel } from '../configuration/types.js';
|
|
1
2
|
import { FeedbackType, Origin, TaskType } from '../types/types.js';
|
|
2
|
-
import { DebugLevel } from './configuration.js';
|
|
3
3
|
import { ExecutionStatus } from './shell.js';
|
|
4
4
|
/**
|
|
5
5
|
* Base color palette - raw color values with descriptive names.
|
|
@@ -128,6 +128,7 @@ const taskColors = {
|
|
|
128
128
|
*/
|
|
129
129
|
const feedbackColors = {
|
|
130
130
|
[FeedbackType.Info]: Colors.Status.Info,
|
|
131
|
+
[FeedbackType.Warning]: Palette.Yellow,
|
|
131
132
|
[FeedbackType.Succeeded]: Colors.Status.Success,
|
|
132
133
|
[FeedbackType.Aborted]: Palette.MediumOrange,
|
|
133
134
|
[FeedbackType.Failed]: Colors.Status.Error,
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
3
2
|
import { parse as parseYaml } from 'yaml';
|
|
3
|
+
import { ConfigDefinitionType, } from '../configuration/types.js';
|
|
4
4
|
import { ComponentStatus, } from '../types/components.js';
|
|
5
5
|
import { ComponentName } from '../types/types.js';
|
|
6
|
-
import {
|
|
6
|
+
import { getConfigPath, loadConfig } from '../configuration/io.js';
|
|
7
|
+
import { getConfigSchema } from '../configuration/schema.js';
|
|
8
|
+
import { getConfigLabel } from './config-labels.js';
|
|
9
|
+
import { defaultFileSystem } from './filesystem.js';
|
|
7
10
|
import { getConfirmationMessage } from './messages.js';
|
|
8
11
|
import { StepType } from '../ui/Config.js';
|
|
9
12
|
export function createWelcomeDefinition(app) {
|
|
@@ -11,6 +14,7 @@ export function createWelcomeDefinition(app) {
|
|
|
11
14
|
id: randomUUID(),
|
|
12
15
|
name: ComponentName.Welcome,
|
|
13
16
|
props: { app },
|
|
17
|
+
status: ComponentStatus.Awaiting,
|
|
14
18
|
};
|
|
15
19
|
}
|
|
16
20
|
export function createConfigSteps() {
|
|
@@ -55,13 +59,13 @@ function getValidator(definition) {
|
|
|
55
59
|
/**
|
|
56
60
|
* Create config steps from schema for specified keys
|
|
57
61
|
*/
|
|
58
|
-
export function createConfigStepsFromSchema(keys) {
|
|
62
|
+
export function createConfigStepsFromSchema(keys, fs = defaultFileSystem) {
|
|
59
63
|
const schema = getConfigSchema();
|
|
60
64
|
let currentConfig = null;
|
|
61
65
|
let rawConfig = null;
|
|
62
66
|
// Load validated config (may fail if config has validation errors)
|
|
63
67
|
try {
|
|
64
|
-
currentConfig = loadConfig();
|
|
68
|
+
currentConfig = loadConfig(fs);
|
|
65
69
|
}
|
|
66
70
|
catch {
|
|
67
71
|
// Config doesn't exist or has validation errors, use defaults
|
|
@@ -69,8 +73,8 @@ export function createConfigStepsFromSchema(keys) {
|
|
|
69
73
|
// Load raw config separately (for discovered keys not in schema)
|
|
70
74
|
try {
|
|
71
75
|
const configFile = getConfigPath();
|
|
72
|
-
if (
|
|
73
|
-
const content =
|
|
76
|
+
if (fs.exists(configFile)) {
|
|
77
|
+
const content = fs.readFile(configFile, 'utf-8');
|
|
74
78
|
rawConfig = parseYaml(content);
|
|
75
79
|
}
|
|
76
80
|
}
|
|
@@ -81,7 +85,7 @@ export function createConfigStepsFromSchema(keys) {
|
|
|
81
85
|
// Check if key is in schema (system config)
|
|
82
86
|
if (!(key in schema)) {
|
|
83
87
|
// Key is not in schema - it's from a skill or discovered config
|
|
84
|
-
// Create a simple text step with
|
|
88
|
+
// Create a simple text step with cached label or full path as description
|
|
85
89
|
const keyParts = key.split('.');
|
|
86
90
|
const shortKey = keyParts[keyParts.length - 1];
|
|
87
91
|
// Load current value if it exists (use rawConfig since discovered keys aren't in validated config)
|
|
@@ -89,8 +93,10 @@ export function createConfigStepsFromSchema(keys) {
|
|
|
89
93
|
const value = currentValue !== undefined && typeof currentValue === 'string'
|
|
90
94
|
? currentValue
|
|
91
95
|
: null;
|
|
96
|
+
// Use cached label if available, fallback to key path
|
|
97
|
+
const cachedLabel = getConfigLabel(key, fs);
|
|
92
98
|
return {
|
|
93
|
-
description: key,
|
|
99
|
+
description: cachedLabel ?? key,
|
|
94
100
|
key: shortKey,
|
|
95
101
|
path: key,
|
|
96
102
|
type: StepType.Text,
|
|
@@ -217,7 +223,11 @@ export function createCommandDefinition(command, service) {
|
|
|
217
223
|
id: randomUUID(),
|
|
218
224
|
name: ComponentName.Command,
|
|
219
225
|
status: ComponentStatus.Awaiting,
|
|
220
|
-
state: {
|
|
226
|
+
state: {
|
|
227
|
+
error: null,
|
|
228
|
+
message: null,
|
|
229
|
+
tasks: [],
|
|
230
|
+
},
|
|
221
231
|
props: {
|
|
222
232
|
command,
|
|
223
233
|
service,
|
|
@@ -249,6 +259,7 @@ export function createFeedback(type, ...messages) {
|
|
|
249
259
|
type,
|
|
250
260
|
message: messages.join('\n\n'),
|
|
251
261
|
},
|
|
262
|
+
status: ComponentStatus.Awaiting,
|
|
252
263
|
};
|
|
253
264
|
}
|
|
254
265
|
export function createMessage(text) {
|
|
@@ -258,6 +269,7 @@ export function createMessage(text) {
|
|
|
258
269
|
props: {
|
|
259
270
|
text,
|
|
260
271
|
},
|
|
272
|
+
status: ComponentStatus.Awaiting,
|
|
261
273
|
};
|
|
262
274
|
}
|
|
263
275
|
export function createDebugDefinition(title, content, color) {
|
|
@@ -269,6 +281,7 @@ export function createDebugDefinition(title, content, color) {
|
|
|
269
281
|
content,
|
|
270
282
|
color,
|
|
271
283
|
},
|
|
284
|
+
status: ComponentStatus.Awaiting,
|
|
272
285
|
};
|
|
273
286
|
}
|
|
274
287
|
export function createRefinement(text, onAborted) {
|
|
@@ -288,7 +301,10 @@ export function createConfirmDefinition(onConfirmed, onCancelled) {
|
|
|
288
301
|
id: randomUUID(),
|
|
289
302
|
name: ComponentName.Confirm,
|
|
290
303
|
status: ComponentStatus.Awaiting,
|
|
291
|
-
state: {
|
|
304
|
+
state: {
|
|
305
|
+
confirmed: false,
|
|
306
|
+
selectedIndex: 0,
|
|
307
|
+
},
|
|
292
308
|
props: {
|
|
293
309
|
message: getConfirmationMessage(),
|
|
294
310
|
onConfirmed,
|
|
@@ -301,7 +317,11 @@ export function createIntrospectDefinition(tasks, service) {
|
|
|
301
317
|
id: randomUUID(),
|
|
302
318
|
name: ComponentName.Introspect,
|
|
303
319
|
status: ComponentStatus.Awaiting,
|
|
304
|
-
state: {
|
|
320
|
+
state: {
|
|
321
|
+
error: null,
|
|
322
|
+
capabilities: [],
|
|
323
|
+
message: null,
|
|
324
|
+
},
|
|
305
325
|
props: {
|
|
306
326
|
tasks,
|
|
307
327
|
service,
|
|
@@ -316,6 +336,7 @@ export function createReportDefinition(message, capabilities) {
|
|
|
316
336
|
message,
|
|
317
337
|
capabilities,
|
|
318
338
|
},
|
|
339
|
+
status: ComponentStatus.Awaiting,
|
|
319
340
|
};
|
|
320
341
|
}
|
|
321
342
|
export function createAnswerDefinition(question, service) {
|
|
@@ -323,14 +344,17 @@ export function createAnswerDefinition(question, service) {
|
|
|
323
344
|
id: randomUUID(),
|
|
324
345
|
name: ComponentName.Answer,
|
|
325
346
|
status: ComponentStatus.Awaiting,
|
|
326
|
-
state: {
|
|
347
|
+
state: {
|
|
348
|
+
error: null,
|
|
349
|
+
answer: null,
|
|
350
|
+
},
|
|
327
351
|
props: {
|
|
328
352
|
question,
|
|
329
353
|
service,
|
|
330
354
|
},
|
|
331
355
|
};
|
|
332
356
|
}
|
|
333
|
-
export function
|
|
357
|
+
export function isSimple(component) {
|
|
334
358
|
return !('state' in component);
|
|
335
359
|
}
|
|
336
360
|
/**
|
|
@@ -361,7 +385,7 @@ export function createExecuteDefinition(tasks, service) {
|
|
|
361
385
|
},
|
|
362
386
|
};
|
|
363
387
|
}
|
|
364
|
-
export function createValidateDefinition(missingConfig, userRequest, service, onError,
|
|
388
|
+
export function createValidateDefinition(missingConfig, userRequest, service, onError, onValidationComplete, onAborted) {
|
|
365
389
|
return {
|
|
366
390
|
id: randomUUID(),
|
|
367
391
|
name: ComponentName.Validate,
|
|
@@ -369,7 +393,7 @@ export function createValidateDefinition(missingConfig, userRequest, service, on
|
|
|
369
393
|
state: {
|
|
370
394
|
error: null,
|
|
371
395
|
completionMessage: null,
|
|
372
|
-
configRequirements:
|
|
396
|
+
configRequirements: [],
|
|
373
397
|
validated: false,
|
|
374
398
|
},
|
|
375
399
|
props: {
|
|
@@ -377,16 +401,8 @@ export function createValidateDefinition(missingConfig, userRequest, service, on
|
|
|
377
401
|
userRequest,
|
|
378
402
|
service,
|
|
379
403
|
onError,
|
|
380
|
-
|
|
404
|
+
onValidationComplete,
|
|
381
405
|
onAborted,
|
|
382
406
|
},
|
|
383
407
|
};
|
|
384
408
|
}
|
|
385
|
-
/**
|
|
386
|
-
* Add debug components to timeline if present in result
|
|
387
|
-
*/
|
|
388
|
-
export function addDebugToTimeline(debugComponents, handlers) {
|
|
389
|
-
if (debugComponents && debugComponents.length > 0 && handlers) {
|
|
390
|
-
handlers.addToTimeline(...debugComponents);
|
|
391
|
-
}
|
|
392
|
-
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
2
1
|
import { homedir } from 'os';
|
|
3
2
|
import { join } from 'path';
|
|
3
|
+
import { defaultFileSystem } from './filesystem.js';
|
|
4
4
|
/**
|
|
5
5
|
* Get the path to the config labels cache file
|
|
6
6
|
*/
|
|
@@ -16,23 +16,23 @@ function getCacheDirectoryPath() {
|
|
|
16
16
|
/**
|
|
17
17
|
* Ensure the cache directory exists
|
|
18
18
|
*/
|
|
19
|
-
function ensureCacheDirectoryExists() {
|
|
19
|
+
function ensureCacheDirectoryExists(fs = defaultFileSystem) {
|
|
20
20
|
const cacheDir = getCacheDirectoryPath();
|
|
21
|
-
if (!
|
|
22
|
-
|
|
21
|
+
if (!fs.exists(cacheDir)) {
|
|
22
|
+
fs.createDirectory(cacheDir, { recursive: true });
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
26
|
* Load config labels from cache file
|
|
27
27
|
* Returns empty object if file doesn't exist or is corrupted
|
|
28
28
|
*/
|
|
29
|
-
export function loadConfigLabels() {
|
|
29
|
+
export function loadConfigLabels(fs = defaultFileSystem) {
|
|
30
30
|
try {
|
|
31
31
|
const cachePath = getConfigLabelsCachePath();
|
|
32
|
-
if (!
|
|
32
|
+
if (!fs.exists(cachePath)) {
|
|
33
33
|
return {};
|
|
34
34
|
}
|
|
35
|
-
const content =
|
|
35
|
+
const content = fs.readFile(cachePath, 'utf-8');
|
|
36
36
|
const parsed = JSON.parse(content);
|
|
37
37
|
// Validate that parsed content is an object
|
|
38
38
|
if (typeof parsed !== 'object' ||
|
|
@@ -50,26 +50,26 @@ export function loadConfigLabels() {
|
|
|
50
50
|
/**
|
|
51
51
|
* Save multiple config labels to cache
|
|
52
52
|
*/
|
|
53
|
-
export function saveConfigLabels(labels) {
|
|
54
|
-
ensureCacheDirectoryExists();
|
|
53
|
+
export function saveConfigLabels(labels, fs = defaultFileSystem) {
|
|
54
|
+
ensureCacheDirectoryExists(fs);
|
|
55
55
|
// Load existing labels and merge with new ones
|
|
56
|
-
const existing = loadConfigLabels();
|
|
56
|
+
const existing = loadConfigLabels(fs);
|
|
57
57
|
const merged = { ...existing, ...labels };
|
|
58
58
|
const cachePath = getConfigLabelsCachePath();
|
|
59
59
|
const content = JSON.stringify(merged, null, 2);
|
|
60
|
-
|
|
60
|
+
fs.writeFile(cachePath, content);
|
|
61
61
|
}
|
|
62
62
|
/**
|
|
63
63
|
* Save a single config label to cache
|
|
64
64
|
*/
|
|
65
|
-
export function saveConfigLabel(key, label) {
|
|
66
|
-
saveConfigLabels({ [key]: label });
|
|
65
|
+
export function saveConfigLabel(key, label, fs = defaultFileSystem) {
|
|
66
|
+
saveConfigLabels({ [key]: label }, fs);
|
|
67
67
|
}
|
|
68
68
|
/**
|
|
69
69
|
* Get a config label from cache
|
|
70
70
|
* Returns undefined if label doesn't exist
|
|
71
71
|
*/
|
|
72
|
-
export function getConfigLabel(key) {
|
|
73
|
-
const labels = loadConfigLabels();
|
|
72
|
+
export function getConfigLabel(key, fs = defaultFileSystem) {
|
|
73
|
+
const labels = loadConfigLabels(fs);
|
|
74
74
|
return labels[key];
|
|
75
75
|
}
|