prompt-language-shell 0.7.0 → 0.7.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/README.md +0 -1
- package/dist/services/anthropic.js +31 -23
- package/dist/services/colors.js +39 -3
- package/dist/services/components.js +2 -2
- package/dist/services/refinement.js +1 -1
- package/dist/services/registry.js +4 -4
- package/dist/services/router.js +118 -83
- package/dist/services/skills.js +5 -2
- package/dist/services/utils.js +16 -0
- package/dist/services/validator.js +10 -78
- package/dist/skills/{config.md → configure.md} +3 -3
- package/dist/skills/introspect.md +11 -11
- package/dist/skills/schedule.md +357 -0
- package/dist/skills/validate.md +9 -8
- package/dist/tools/{config.tool.js → configure.tool.js} +7 -7
- package/dist/tools/schedule.tool.js +55 -0
- package/dist/tools/validate.tool.js +1 -1
- package/dist/types/types.js +4 -3
- package/dist/ui/Command.js +18 -11
- package/dist/ui/Component.js +3 -3
- package/dist/ui/Config.js +21 -3
- package/dist/ui/Debug.js +2 -3
- package/dist/ui/Introspect.js +3 -3
- package/dist/ui/Label.js +4 -3
- package/dist/ui/List.js +3 -2
- package/dist/ui/Main.js +10 -5
- package/dist/ui/{Plan.js → Schedule.js} +30 -9
- package/dist/ui/Validate.js +1 -1
- package/package.json +1 -1
- package/dist/skills/plan.md +0 -870
- package/dist/tools/plan.tool.js +0 -36
package/README.md
CHANGED
|
@@ -159,7 +159,6 @@ $ pls build test
|
|
|
159
159
|
|
|
160
160
|
## Roadmap
|
|
161
161
|
|
|
162
|
-
- **0.7** - Comprehend skill, simplified prompts, better debugging
|
|
163
162
|
- **0.8** - Sequential and interlaced skill execution
|
|
164
163
|
- **0.9** - Learn skill, codebase refinement, complex dependency handling
|
|
165
164
|
- **1.0** - Production release
|
|
@@ -52,31 +52,39 @@ export class AnthropicService {
|
|
|
52
52
|
this.client = new Anthropic({ apiKey: key });
|
|
53
53
|
this.model = model;
|
|
54
54
|
}
|
|
55
|
-
async processWithTool(command, toolName) {
|
|
55
|
+
async processWithTool(command, toolName, customInstructions) {
|
|
56
56
|
// Load tool from registry
|
|
57
57
|
const tool = toolRegistry.getSchema(toolName);
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
toolName === 'introspect' ||
|
|
64
|
-
toolName === 'execute' ||
|
|
65
|
-
toolName === 'validate') {
|
|
66
|
-
const skills = loadSkillsWithValidation();
|
|
67
|
-
const skillsSection = formatSkillsForPrompt(skills);
|
|
68
|
-
systemPrompt += skillsSection;
|
|
58
|
+
// Use custom instructions if provided, otherwise load from registry
|
|
59
|
+
let systemPrompt;
|
|
60
|
+
if (customInstructions) {
|
|
61
|
+
// Custom instructions provided (typically for testing)
|
|
62
|
+
systemPrompt = customInstructions;
|
|
69
63
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
64
|
+
else {
|
|
65
|
+
// Load and build system prompt automatically (production)
|
|
66
|
+
const instructions = toolRegistry.getInstructions(toolName);
|
|
67
|
+
systemPrompt = instructions;
|
|
68
|
+
// Add skills section for applicable tools
|
|
69
|
+
if (toolName === 'schedule' ||
|
|
70
|
+
toolName === 'introspect' ||
|
|
71
|
+
toolName === 'execute' ||
|
|
72
|
+
toolName === 'validate') {
|
|
73
|
+
const skills = loadSkillsWithValidation();
|
|
74
|
+
const skillsSection = formatSkillsForPrompt(skills);
|
|
75
|
+
systemPrompt += skillsSection;
|
|
76
|
+
}
|
|
77
|
+
// Add config structure for config tool only
|
|
78
|
+
if (toolName === 'config') {
|
|
79
|
+
const configStructure = getAvailableConfigStructure();
|
|
80
|
+
const configuredKeys = getConfiguredKeys();
|
|
81
|
+
const configSection = '\n## Available Configuration\n\n' +
|
|
82
|
+
'Config structure (key: description):\n' +
|
|
83
|
+
JSON.stringify(configStructure, null, 2) +
|
|
84
|
+
'\n\nConfigured keys (keys that exist in config file):\n' +
|
|
85
|
+
JSON.stringify(configuredKeys, null, 2);
|
|
86
|
+
systemPrompt += configSection;
|
|
87
|
+
}
|
|
80
88
|
}
|
|
81
89
|
// Build tools array - add web search for answer tool
|
|
82
90
|
const tools = [tool];
|
|
@@ -177,7 +185,7 @@ export class AnthropicService {
|
|
|
177
185
|
debug,
|
|
178
186
|
};
|
|
179
187
|
}
|
|
180
|
-
// Handle
|
|
188
|
+
// Handle schedule and introspect tool responses
|
|
181
189
|
if (input.message === undefined || typeof input.message !== 'string') {
|
|
182
190
|
throw new Error('Invalid tool response: missing or invalid message field');
|
|
183
191
|
}
|
package/dist/services/colors.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { FeedbackType, TaskType } from '../types/types.js';
|
|
2
|
+
import { DebugLevel } from './configuration.js';
|
|
2
3
|
/**
|
|
3
4
|
* Base color palette - raw color values with descriptive names.
|
|
4
5
|
* All colors used in the interface are defined here.
|
|
@@ -56,7 +57,7 @@ export const Colors = {
|
|
|
56
57
|
},
|
|
57
58
|
Type: {
|
|
58
59
|
Config: Palette.Cyan,
|
|
59
|
-
|
|
60
|
+
Schedule: Palette.LightCyan,
|
|
60
61
|
Execute: Palette.Green,
|
|
61
62
|
Answer: Palette.Purple,
|
|
62
63
|
Introspect: Palette.Purple,
|
|
@@ -65,6 +66,7 @@ export const Colors = {
|
|
|
65
66
|
Ignore: Palette.BurntOrange,
|
|
66
67
|
Select: Palette.SteelBlue,
|
|
67
68
|
Discard: Palette.DarkOrange,
|
|
69
|
+
Group: Palette.Yellow,
|
|
68
70
|
},
|
|
69
71
|
Origin: {
|
|
70
72
|
BuiltIn: Palette.Cyan,
|
|
@@ -80,9 +82,9 @@ const taskColors = {
|
|
|
80
82
|
description: Colors.Label.Default,
|
|
81
83
|
type: Colors.Type.Config,
|
|
82
84
|
},
|
|
83
|
-
[TaskType.
|
|
85
|
+
[TaskType.Schedule]: {
|
|
84
86
|
description: Colors.Label.Default,
|
|
85
|
-
type: Colors.Type.
|
|
87
|
+
type: Colors.Type.Schedule,
|
|
86
88
|
},
|
|
87
89
|
[TaskType.Execute]: {
|
|
88
90
|
description: Colors.Label.Default,
|
|
@@ -116,6 +118,10 @@ const taskColors = {
|
|
|
116
118
|
description: Colors.Label.Discarded,
|
|
117
119
|
type: Colors.Type.Discard,
|
|
118
120
|
},
|
|
121
|
+
[TaskType.Group]: {
|
|
122
|
+
description: Colors.Label.Default,
|
|
123
|
+
type: Colors.Type.Group,
|
|
124
|
+
},
|
|
119
125
|
};
|
|
120
126
|
/**
|
|
121
127
|
* Feedback-specific color mappings (internal)
|
|
@@ -174,3 +180,33 @@ export function getFeedbackColor(type, isCurrent) {
|
|
|
174
180
|
export function getTextColor(isCurrent) {
|
|
175
181
|
return isCurrent ? Colors.Text.Active : Colors.Text.Inactive;
|
|
176
182
|
}
|
|
183
|
+
/**
|
|
184
|
+
* Verbose task type labels - two-word descriptions that start with the same
|
|
185
|
+
* keyword as the short version
|
|
186
|
+
*/
|
|
187
|
+
const verboseTaskTypeLabels = {
|
|
188
|
+
[TaskType.Config]: 'configure option',
|
|
189
|
+
[TaskType.Schedule]: 'schedule tasks',
|
|
190
|
+
[TaskType.Execute]: 'execute command',
|
|
191
|
+
[TaskType.Answer]: 'answer question',
|
|
192
|
+
[TaskType.Introspect]: 'introspect capabilities',
|
|
193
|
+
[TaskType.Report]: 'report results',
|
|
194
|
+
[TaskType.Define]: 'define options',
|
|
195
|
+
[TaskType.Ignore]: 'ignore request',
|
|
196
|
+
[TaskType.Select]: 'select option',
|
|
197
|
+
[TaskType.Discard]: 'discard option',
|
|
198
|
+
[TaskType.Group]: 'group tasks',
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* Get task type label based on debug level.
|
|
202
|
+
*
|
|
203
|
+
* Returns:
|
|
204
|
+
* - Verbose label (2 words) in verbose mode
|
|
205
|
+
* - Short label (1 word) in info mode or when debug is off
|
|
206
|
+
*/
|
|
207
|
+
export function getTaskTypeLabel(type, debug) {
|
|
208
|
+
if (debug === DebugLevel.Verbose) {
|
|
209
|
+
return verboseTaskTypeLabels[type];
|
|
210
|
+
}
|
|
211
|
+
return type;
|
|
212
|
+
}
|
|
@@ -216,10 +216,10 @@ export function createCommandDefinition(command, service) {
|
|
|
216
216
|
},
|
|
217
217
|
};
|
|
218
218
|
}
|
|
219
|
-
export function
|
|
219
|
+
export function createScheduleDefinition(message, tasks, onSelectionConfirmed) {
|
|
220
220
|
return {
|
|
221
221
|
id: randomUUID(),
|
|
222
|
-
name: ComponentName.
|
|
222
|
+
name: ComponentName.Schedule,
|
|
223
223
|
status: ComponentStatus.Awaiting,
|
|
224
224
|
state: {
|
|
225
225
|
highlightedIndex: null,
|
|
@@ -21,7 +21,7 @@ export async function handleRefinement(selectedTasks, service, originalCommand,
|
|
|
21
21
|
})
|
|
22
22
|
.join(', ');
|
|
23
23
|
// Call LLM to refine plan with selected tasks
|
|
24
|
-
const refinedResult = await service.processWithTool(refinedCommand, '
|
|
24
|
+
const refinedResult = await service.processWithTool(refinedCommand, 'schedule');
|
|
25
25
|
// Complete the Refinement component
|
|
26
26
|
handlers.completeActive();
|
|
27
27
|
// Add debug components to timeline if present
|
|
@@ -34,17 +34,17 @@ class ToolRegistry {
|
|
|
34
34
|
export const toolRegistry = new ToolRegistry();
|
|
35
35
|
// Register built-in tools
|
|
36
36
|
import { answerTool } from '../tools/answer.tool.js';
|
|
37
|
-
import {
|
|
37
|
+
import { configureTool } from '../tools/configure.tool.js';
|
|
38
38
|
import { executeTool } from '../tools/execute.tool.js';
|
|
39
39
|
import { introspectTool } from '../tools/introspect.tool.js';
|
|
40
|
-
import {
|
|
40
|
+
import { scheduleTool } from '../tools/schedule.tool.js';
|
|
41
41
|
import { validateTool } from '../tools/validate.tool.js';
|
|
42
42
|
const tools = {
|
|
43
43
|
answer: answerTool,
|
|
44
|
-
|
|
44
|
+
configure: configureTool,
|
|
45
45
|
execute: executeTool,
|
|
46
46
|
introspect: introspectTool,
|
|
47
|
-
|
|
47
|
+
schedule: scheduleTool,
|
|
48
48
|
validate: validateTool,
|
|
49
49
|
};
|
|
50
50
|
for (const [name, schema] of Object.entries(tools)) {
|
package/dist/services/router.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TaskType } from '../types/types.js';
|
|
2
|
-
import { createAnswerDefinition, createConfigDefinitionWithKeys, createConfirmDefinition, createExecuteDefinition, createFeedback, createIntrospectDefinition, createMessage,
|
|
2
|
+
import { createAnswerDefinition, createConfigDefinitionWithKeys, createConfirmDefinition, createExecuteDefinition, createFeedback, createIntrospectDefinition, createMessage, createScheduleDefinition, createValidateDefinition, } from './components.js';
|
|
3
3
|
import { saveConfig, unflattenConfig } from './configuration.js';
|
|
4
4
|
import { FeedbackType } from '../types/types.js';
|
|
5
5
|
import { validateExecuteTasks } from './validator.js';
|
|
@@ -33,52 +33,63 @@ export function routeTasksWithConfirm(tasks, message, service, userRequest, hand
|
|
|
33
33
|
}
|
|
34
34
|
const operation = getOperationName(validTasks);
|
|
35
35
|
if (hasDefineTask) {
|
|
36
|
-
// Has DEFINE tasks - add
|
|
36
|
+
// Has DEFINE tasks - add Schedule to queue for user selection
|
|
37
37
|
// Refinement flow will call this function again with refined tasks
|
|
38
|
-
const
|
|
39
|
-
handlers.addToQueue(
|
|
38
|
+
const scheduleDefinition = createScheduleDefinition(message, validTasks);
|
|
39
|
+
handlers.addToQueue(scheduleDefinition);
|
|
40
40
|
}
|
|
41
41
|
else {
|
|
42
|
-
// No DEFINE tasks -
|
|
43
|
-
// When
|
|
44
|
-
// When
|
|
45
|
-
// When Confirm activates,
|
|
46
|
-
const
|
|
47
|
-
//
|
|
42
|
+
// No DEFINE tasks - Schedule auto-completes and adds Confirm to queue
|
|
43
|
+
// When Schedule activates, Command moves to timeline
|
|
44
|
+
// When Schedule completes, it moves to pending
|
|
45
|
+
// When Confirm activates, Schedule stays pending (visible for context)
|
|
46
|
+
const scheduleDefinition = createScheduleDefinition(message, validTasks, () => {
|
|
47
|
+
// Schedule completed - add Confirm to queue
|
|
48
48
|
const confirmDefinition = createConfirmDefinition(() => {
|
|
49
|
-
// User confirmed - complete both Confirm and
|
|
49
|
+
// User confirmed - complete both Confirm and Schedule, then route to appropriate component
|
|
50
50
|
handlers.completeActiveAndPending();
|
|
51
51
|
executeTasksAfterConfirm(validTasks, service, userRequest, handlers);
|
|
52
52
|
}, () => {
|
|
53
|
-
// User cancelled - complete both Confirm and
|
|
53
|
+
// User cancelled - complete both Confirm and Schedule, then show cancellation
|
|
54
54
|
handlers.completeActiveAndPending();
|
|
55
55
|
const message = getCancellationMessage(operation);
|
|
56
56
|
handlers.addToQueue(createFeedback(FeedbackType.Aborted, message));
|
|
57
57
|
});
|
|
58
58
|
handlers.addToQueue(confirmDefinition);
|
|
59
59
|
});
|
|
60
|
-
handlers.addToQueue(
|
|
60
|
+
handlers.addToQueue(scheduleDefinition);
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
/**
|
|
64
|
-
* Validate
|
|
65
|
-
*
|
|
64
|
+
* Validate task types - allows mixed types at top level with Groups,
|
|
65
|
+
* but each Group must have uniform subtask types
|
|
66
66
|
*/
|
|
67
67
|
function validateTaskTypes(tasks) {
|
|
68
68
|
if (tasks.length === 0)
|
|
69
69
|
return;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
// Cast to ScheduledTask to access subtasks property
|
|
71
|
+
const scheduledTasks = tasks;
|
|
72
|
+
// Check each Group task's subtasks for uniform types
|
|
73
|
+
for (const task of scheduledTasks) {
|
|
74
|
+
if (task.type === TaskType.Group &&
|
|
75
|
+
task.subtasks &&
|
|
76
|
+
task.subtasks.length > 0) {
|
|
77
|
+
const subtaskTypes = new Set(task.subtasks.map((t) => t.type));
|
|
78
|
+
if (subtaskTypes.size > 1) {
|
|
79
|
+
throw new Error(getMixedTaskTypesError(Array.from(subtaskTypes)));
|
|
80
|
+
}
|
|
81
|
+
// Recursively validate nested groups
|
|
82
|
+
validateTaskTypes(task.subtasks);
|
|
83
|
+
}
|
|
73
84
|
}
|
|
74
85
|
}
|
|
75
86
|
/**
|
|
76
87
|
* Execute tasks after confirmation (internal helper)
|
|
77
|
-
* Validates task types
|
|
88
|
+
* Validates task types and routes each type appropriately
|
|
89
|
+
* Supports mixed types at top level with Groups
|
|
78
90
|
*/
|
|
79
91
|
function executeTasksAfterConfirm(tasks, service, userRequest, handlers) {
|
|
80
|
-
// Validate
|
|
81
|
-
// Per FLOWS.md: "Confirm component completes → Execution handler analyzes task types"
|
|
92
|
+
// Validate task types (Groups must have uniform subtasks)
|
|
82
93
|
try {
|
|
83
94
|
validateTaskTypes(tasks);
|
|
84
95
|
}
|
|
@@ -86,75 +97,99 @@ function executeTasksAfterConfirm(tasks, service, userRequest, handlers) {
|
|
|
86
97
|
handlers.onError(error instanceof Error ? error.message : String(error));
|
|
87
98
|
return;
|
|
88
99
|
}
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
100
|
+
// Flatten Group tasks to get actual executable subtasks
|
|
101
|
+
const flattenedTasks = [];
|
|
102
|
+
const scheduledTasks = tasks;
|
|
103
|
+
for (const task of scheduledTasks) {
|
|
104
|
+
if (task.type === TaskType.Group && task.subtasks) {
|
|
105
|
+
// Add all subtasks from the group
|
|
106
|
+
flattenedTasks.push(...task.subtasks);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
// Add non-group tasks as-is
|
|
110
|
+
flattenedTasks.push(task);
|
|
111
|
+
}
|
|
95
112
|
}
|
|
96
|
-
|
|
97
|
-
|
|
113
|
+
// Group flattened tasks by type - initialize all TaskType keys with empty arrays
|
|
114
|
+
const tasksByType = {};
|
|
115
|
+
for (const type of Object.values(TaskType)) {
|
|
116
|
+
tasksByType[type] = [];
|
|
98
117
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
118
|
+
for (const task of flattenedTasks) {
|
|
119
|
+
tasksByType[task.type].push(task);
|
|
120
|
+
}
|
|
121
|
+
// Route each type group appropriately
|
|
122
|
+
for (const [type, typeTasks] of Object.entries(tasksByType)) {
|
|
123
|
+
const taskType = type;
|
|
124
|
+
// Skip empty task groups (pre-initialized but unused)
|
|
125
|
+
if (typeTasks.length === 0) {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (taskType === TaskType.Answer) {
|
|
129
|
+
const question = typeTasks[0].action;
|
|
130
|
+
handlers.addToQueue(createAnswerDefinition(question, service));
|
|
131
|
+
}
|
|
132
|
+
else if (taskType === TaskType.Introspect) {
|
|
133
|
+
handlers.addToQueue(createIntrospectDefinition(typeTasks, service));
|
|
134
|
+
}
|
|
135
|
+
else if (taskType === TaskType.Config) {
|
|
136
|
+
// Route to Config flow - extract keys from task params
|
|
137
|
+
const configKeys = typeTasks
|
|
138
|
+
.map((task) => task.params?.key)
|
|
139
|
+
.filter((key) => key !== undefined);
|
|
140
|
+
handlers.addToQueue(createConfigDefinitionWithKeys(configKeys, (config) => {
|
|
141
|
+
// Save config - Config component will handle completion and feedback
|
|
142
|
+
try {
|
|
143
|
+
// Convert flat dotted keys to nested structure grouped by section
|
|
144
|
+
const configBySection = unflattenConfig(config);
|
|
145
|
+
// Save each section
|
|
146
|
+
for (const [section, sectionConfig] of Object.entries(configBySection)) {
|
|
147
|
+
saveConfig(section, sectionConfig);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
const errorMessage = error instanceof Error
|
|
152
|
+
? error.message
|
|
153
|
+
: 'Failed to save configuration';
|
|
154
|
+
throw new Error(errorMessage);
|
|
155
|
+
}
|
|
156
|
+
}, (operation) => {
|
|
157
|
+
handlers.onAborted(operation);
|
|
158
|
+
}));
|
|
159
|
+
}
|
|
160
|
+
else if (taskType === TaskType.Execute) {
|
|
161
|
+
// Execute tasks with validation
|
|
106
162
|
try {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
163
|
+
const validation = validateExecuteTasks(typeTasks);
|
|
164
|
+
if (validation.validationErrors.length > 0) {
|
|
165
|
+
// Show error feedback for invalid skills
|
|
166
|
+
const errorMessages = validation.validationErrors.map((error) => {
|
|
167
|
+
const issuesList = error.issues
|
|
168
|
+
.map((issue) => ` - ${issue}`)
|
|
169
|
+
.join('\n');
|
|
170
|
+
return `Invalid skill definition "${error.skill}":\n\n${issuesList}`;
|
|
171
|
+
});
|
|
172
|
+
handlers.addToQueue(createFeedback(FeedbackType.Failed, errorMessages.join('\n\n')));
|
|
173
|
+
}
|
|
174
|
+
else if (validation.missingConfig.length > 0) {
|
|
175
|
+
handlers.addToQueue(createValidateDefinition(validation.missingConfig, userRequest, service, (error) => {
|
|
176
|
+
handlers.onError(error);
|
|
177
|
+
}, () => {
|
|
178
|
+
handlers.addToQueue(createExecuteDefinition(typeTasks, service));
|
|
179
|
+
}, (operation) => {
|
|
180
|
+
handlers.onAborted(operation);
|
|
181
|
+
}));
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
handlers.addToQueue(createExecuteDefinition(typeTasks, service));
|
|
112
185
|
}
|
|
113
|
-
handlers.completeActive();
|
|
114
|
-
handlers.addToQueue(createFeedback(FeedbackType.Succeeded, 'Configuration updated successfully.'));
|
|
115
186
|
}
|
|
116
187
|
catch (error) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
handlers.
|
|
121
|
-
}
|
|
122
|
-
}, (operation) => {
|
|
123
|
-
handlers.onAborted(operation);
|
|
124
|
-
}));
|
|
125
|
-
}
|
|
126
|
-
else {
|
|
127
|
-
// Execute tasks with validation
|
|
128
|
-
try {
|
|
129
|
-
const validation = validateExecuteTasks(tasks);
|
|
130
|
-
if (validation.validationErrors.length > 0) {
|
|
131
|
-
// Show error feedback for invalid skills
|
|
132
|
-
const errorMessages = validation.validationErrors.map((error) => {
|
|
133
|
-
const issuesList = error.issues
|
|
134
|
-
.map((issue) => ` - ${issue}`)
|
|
135
|
-
.join('\n');
|
|
136
|
-
return `Invalid skill definition "${error.skill}":\n\n${issuesList}`;
|
|
137
|
-
});
|
|
138
|
-
handlers.addToQueue(createFeedback(FeedbackType.Failed, errorMessages.join('\n\n')));
|
|
139
|
-
}
|
|
140
|
-
else if (validation.missingConfig.length > 0) {
|
|
141
|
-
handlers.addToQueue(createValidateDefinition(validation.missingConfig, userRequest, service, (error) => {
|
|
142
|
-
handlers.onError(error);
|
|
143
|
-
}, () => {
|
|
144
|
-
handlers.addToQueue(createExecuteDefinition(tasks, service));
|
|
145
|
-
}, (operation) => {
|
|
146
|
-
handlers.onAborted(operation);
|
|
147
|
-
}));
|
|
188
|
+
// Handle skill reference errors (e.g., unknown skills)
|
|
189
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
190
|
+
const message = createMessage(errorMessage);
|
|
191
|
+
handlers.addToQueue(message);
|
|
148
192
|
}
|
|
149
|
-
else {
|
|
150
|
-
handlers.addToQueue(createExecuteDefinition(tasks, service));
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
catch (error) {
|
|
154
|
-
// Handle skill reference errors (e.g., unknown skills)
|
|
155
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
156
|
-
const message = createMessage(errorMessage);
|
|
157
|
-
handlers.addToQueue(message);
|
|
158
193
|
}
|
|
159
194
|
}
|
|
160
195
|
}
|
package/dist/services/skills.js
CHANGED
|
@@ -146,8 +146,11 @@ brackets for additional information. Use commas instead. For example:
|
|
|
146
146
|
- WRONG: "Build project Alpha (the legacy version)"
|
|
147
147
|
|
|
148
148
|
`;
|
|
149
|
-
const
|
|
150
|
-
|
|
149
|
+
const separator = '-'.repeat(64);
|
|
150
|
+
const skillsContent = skills
|
|
151
|
+
.map((s) => s.trim())
|
|
152
|
+
.join('\n\n' + separator + '\n\n');
|
|
153
|
+
return header + separator + '\n\n' + skillsContent;
|
|
151
154
|
}
|
|
152
155
|
/**
|
|
153
156
|
* Parse skill reference from execution line
|
package/dist/services/utils.js
CHANGED
|
@@ -19,3 +19,19 @@ export function formatDuration(ms) {
|
|
|
19
19
|
}
|
|
20
20
|
return parts.join(' ');
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Recursively extracts all leaf tasks from a hierarchical task structure.
|
|
24
|
+
* Leaf tasks are tasks without subtasks.
|
|
25
|
+
*/
|
|
26
|
+
export function getAllLeafTasks(tasks) {
|
|
27
|
+
const leafTasks = [];
|
|
28
|
+
for (const task of tasks) {
|
|
29
|
+
if (!task.subtasks || task.subtasks.length === 0) {
|
|
30
|
+
leafTasks.push(task);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
leafTasks.push(...getAllLeafTasks(task.subtasks));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return leafTasks;
|
|
37
|
+
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import { extractPlaceholders, pathToString, resolveVariant, } from './resolver.js';
|
|
2
1
|
import { loadUserConfig, hasConfigPath } from './loader.js';
|
|
3
|
-
import { loadSkillDefinitions, createSkillLookup
|
|
4
|
-
import { getConfigType } from './parser.js';
|
|
2
|
+
import { loadSkillDefinitions, createSkillLookup } from './skills.js';
|
|
5
3
|
/**
|
|
6
4
|
* Validate config requirements for execute tasks
|
|
7
5
|
* Returns validation result with missing config and validation errors
|
|
@@ -38,88 +36,22 @@ export function validateExecuteTasks(tasks) {
|
|
|
38
36
|
};
|
|
39
37
|
}
|
|
40
38
|
for (const task of tasks) {
|
|
41
|
-
// Check
|
|
42
|
-
|
|
43
|
-
if (
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (!skill) {
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
// Get variant from task params (if any)
|
|
50
|
-
// Try params.variant first, then look for other param keys that might be the variant
|
|
51
|
-
let variant = null;
|
|
52
|
-
if (typeof task.params?.variant === 'string') {
|
|
53
|
-
variant = task.params.variant.toLowerCase();
|
|
54
|
-
}
|
|
55
|
-
else if (task.params && typeof task.params === 'object') {
|
|
56
|
-
// Look for other params that could be the variant (e.g., product, target, option, etc.)
|
|
57
|
-
// Exclude known non-variant params
|
|
58
|
-
const excludeKeys = new Set(['skill', 'type']);
|
|
59
|
-
for (const [key, value] of Object.entries(task.params)) {
|
|
60
|
-
if (!excludeKeys.has(key) && typeof value === 'string') {
|
|
61
|
-
variant = value.toLowerCase();
|
|
62
|
-
break;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
// Expand skill references to get actual commands
|
|
67
|
-
const expanded = expandSkillReferences(skill.execution, skillLookup);
|
|
68
|
-
// Extract placeholders from actual commands
|
|
69
|
-
for (const line of expanded) {
|
|
70
|
-
const placeholders = extractPlaceholders(line);
|
|
71
|
-
for (const placeholder of placeholders) {
|
|
72
|
-
let resolvedPath;
|
|
73
|
-
if (placeholder.hasVariant) {
|
|
74
|
-
// Variant placeholder - resolve with variant from params
|
|
75
|
-
if (!variant) {
|
|
76
|
-
// No variant provided - skip this placeholder
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
const resolvedPathArray = resolveVariant(placeholder.path, variant);
|
|
80
|
-
resolvedPath = pathToString(resolvedPathArray);
|
|
81
|
-
}
|
|
82
|
-
else {
|
|
83
|
-
// Strict placeholder - use as-is
|
|
84
|
-
resolvedPath = pathToString(placeholder.path);
|
|
85
|
-
}
|
|
86
|
-
// Skip if already processed
|
|
87
|
-
if (seenPaths.has(resolvedPath)) {
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
seenPaths.add(resolvedPath);
|
|
91
|
-
// Check if config exists
|
|
92
|
-
if (!hasConfigPath(userConfig, resolvedPath)) {
|
|
93
|
-
// Get type from skill config
|
|
94
|
-
const type = skill.config
|
|
95
|
-
? getConfigType(skill.config, resolvedPath)
|
|
96
|
-
: undefined;
|
|
97
|
-
missing.push({
|
|
98
|
-
path: resolvedPath,
|
|
99
|
-
type: type || 'string',
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
106
|
-
// Task doesn't come from a skill - check task action for placeholders
|
|
107
|
-
const placeholders = extractPlaceholders(task.action);
|
|
108
|
-
for (const placeholder of placeholders) {
|
|
109
|
-
// Skip variant placeholders - they should have been resolved during planning
|
|
110
|
-
if (placeholder.hasVariant) {
|
|
39
|
+
// Check task's config array from SCHEDULE tool
|
|
40
|
+
// This is the authoritative source for required configuration
|
|
41
|
+
if (task.config && task.config.length > 0) {
|
|
42
|
+
for (const configPath of task.config) {
|
|
43
|
+
if (typeof configPath !== 'string') {
|
|
111
44
|
continue;
|
|
112
45
|
}
|
|
113
|
-
const path = placeholder.path.join('.');
|
|
114
46
|
// Skip if already processed
|
|
115
|
-
if (seenPaths.has(
|
|
47
|
+
if (seenPaths.has(configPath)) {
|
|
116
48
|
continue;
|
|
117
49
|
}
|
|
118
|
-
seenPaths.add(
|
|
50
|
+
seenPaths.add(configPath);
|
|
119
51
|
// Check if config exists
|
|
120
|
-
if (!hasConfigPath(userConfig,
|
|
52
|
+
if (!hasConfigPath(userConfig, configPath)) {
|
|
121
53
|
missing.push({
|
|
122
|
-
path,
|
|
54
|
+
path: configPath,
|
|
123
55
|
type: 'string', // Default to string for now
|
|
124
56
|
});
|
|
125
57
|
}
|
|
@@ -49,14 +49,14 @@ as tasks.
|
|
|
49
49
|
"tasks": [
|
|
50
50
|
{
|
|
51
51
|
"action": "Anthropic API key",
|
|
52
|
-
"type": "
|
|
52
|
+
"type": "configure",
|
|
53
53
|
"params": {
|
|
54
54
|
"key": "anthropic.key"
|
|
55
55
|
}
|
|
56
56
|
},
|
|
57
57
|
{
|
|
58
58
|
"action": "Model",
|
|
59
|
-
"type": "
|
|
59
|
+
"type": "configure",
|
|
60
60
|
"params": {
|
|
61
61
|
"key": "anthropic.model"
|
|
62
62
|
}
|
|
@@ -69,7 +69,7 @@ as tasks.
|
|
|
69
69
|
|
|
70
70
|
- Use the exact config keys from `configStructure`
|
|
71
71
|
- Use the descriptions from `configStructure` as the action text
|
|
72
|
-
- Always use type "
|
|
72
|
+
- Always use type "configure"
|
|
73
73
|
- Always include the key in params
|
|
74
74
|
- Keep message concise (≤64 characters)
|
|
75
75
|
- Return at least one task (required keys if unsure)
|