prompt-language-shell 0.4.8 → 0.5.0
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/config/EXECUTE.md +279 -0
- package/dist/config/INTROSPECT.md +9 -6
- package/dist/config/PLAN.md +57 -6
- package/dist/config/VALIDATE.md +139 -0
- package/dist/handlers/answer.js +13 -20
- package/dist/handlers/command.js +26 -30
- package/dist/handlers/config.js +32 -24
- package/dist/handlers/execute.js +46 -0
- package/dist/handlers/execution.js +133 -81
- package/dist/handlers/introspect.js +13 -20
- package/dist/handlers/plan.js +31 -34
- package/dist/services/anthropic.js +28 -2
- package/dist/services/colors.js +3 -3
- package/dist/services/components.js +50 -1
- package/dist/services/config-loader.js +67 -0
- package/dist/services/execution-validator.js +110 -0
- package/dist/services/messages.js +1 -0
- package/dist/services/placeholder-resolver.js +120 -0
- package/dist/services/shell.js +118 -0
- package/dist/services/skill-expander.js +91 -0
- package/dist/services/skill-parser.js +169 -0
- package/dist/services/skills.js +26 -0
- package/dist/services/timing.js +38 -0
- package/dist/services/tool-registry.js +10 -0
- package/dist/services/utils.js +21 -0
- package/dist/tools/execute.tool.js +44 -0
- package/dist/tools/validate.tool.js +43 -0
- package/dist/types/handlers.js +1 -0
- package/dist/types/skills.js +4 -0
- package/dist/types/types.js +2 -0
- package/dist/ui/Answer.js +3 -9
- package/dist/ui/Command.js +3 -6
- package/dist/ui/Component.js +13 -1
- package/dist/ui/Config.js +2 -2
- package/dist/ui/Confirm.js +2 -2
- package/dist/ui/Execute.js +262 -0
- package/dist/ui/Introspect.js +5 -7
- package/dist/ui/Main.js +30 -69
- package/dist/ui/Spinner.js +10 -5
- package/dist/ui/Validate.js +120 -0
- package/package.json +7 -7
package/dist/handlers/config.js
CHANGED
|
@@ -1,66 +1,74 @@
|
|
|
1
1
|
import { ComponentName, FeedbackType } from '../types/types.js';
|
|
2
2
|
import { createAnthropicService, } from '../services/anthropic.js';
|
|
3
|
-
import { createCommandDefinition, createFeedback, markAsDone, } from '../services/components.js';
|
|
3
|
+
import { createCommandDefinition, createExecuteDefinition, createFeedback, markAsDone, } from '../services/components.js';
|
|
4
4
|
import { saveAnthropicConfig, saveConfig } from '../services/configuration.js';
|
|
5
5
|
import { FeedbackMessages } from '../services/messages.js';
|
|
6
6
|
import { exitApp } from '../services/process.js';
|
|
7
7
|
import { withQueueHandler } from '../services/queue.js';
|
|
8
8
|
/**
|
|
9
|
-
* Creates config
|
|
9
|
+
* Creates all config handlers
|
|
10
10
|
*/
|
|
11
|
-
export function
|
|
12
|
-
|
|
11
|
+
export function createConfigHandlers(ops, handleAborted, command, commandHandlers, setService) {
|
|
12
|
+
const onFinished = (config) => {
|
|
13
13
|
const anthropicConfig = config;
|
|
14
14
|
saveAnthropicConfig(anthropicConfig);
|
|
15
15
|
const newService = createAnthropicService(anthropicConfig);
|
|
16
16
|
setService(newService);
|
|
17
|
-
|
|
18
|
-
addToTimeline(markAsDone(first), createFeedback(FeedbackType.Succeeded, FeedbackMessages.ConfigurationComplete));
|
|
19
|
-
// Add command to queue if we have one
|
|
17
|
+
ops.setQueue(withQueueHandler(ComponentName.Config, (first, rest) => {
|
|
18
|
+
ops.addToTimeline(markAsDone(first), createFeedback(FeedbackType.Succeeded, FeedbackMessages.ConfigurationComplete));
|
|
20
19
|
if (command) {
|
|
21
20
|
return [
|
|
22
21
|
...rest,
|
|
23
|
-
createCommandDefinition(command, newService,
|
|
22
|
+
createCommandDefinition(command, newService, commandHandlers.onError, commandHandlers.onComplete, commandHandlers.onAborted),
|
|
24
23
|
];
|
|
25
24
|
}
|
|
26
|
-
// No command - exit after showing completion message
|
|
27
25
|
exitApp(0);
|
|
28
26
|
return rest;
|
|
29
|
-
}, false, 0);
|
|
27
|
+
}, false, 0));
|
|
30
28
|
};
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Creates config aborted handler
|
|
34
|
-
*/
|
|
35
|
-
export function createConfigAbortedHandler(handleAborted) {
|
|
36
|
-
return () => {
|
|
29
|
+
const onAborted = () => {
|
|
37
30
|
handleAborted('Configuration');
|
|
38
31
|
};
|
|
32
|
+
return { onFinished, onAborted };
|
|
39
33
|
}
|
|
40
34
|
/**
|
|
41
35
|
* Creates config execution finished handler for CONFIG skill
|
|
42
|
-
* Saves arbitrary config keys and
|
|
36
|
+
* Saves arbitrary config keys and optionally continues with execution
|
|
43
37
|
*/
|
|
44
|
-
export function createConfigExecutionFinishedHandler(addToTimeline, keys) {
|
|
38
|
+
export function createConfigExecutionFinishedHandler(addToTimeline, keys, tasks, service, executeHandlers) {
|
|
45
39
|
return (config) => {
|
|
46
|
-
//
|
|
47
|
-
// Group by section (e.g., "anthropic", "settings")
|
|
40
|
+
// Group by top-level section
|
|
48
41
|
const sections = {};
|
|
49
42
|
for (const fullKey of keys) {
|
|
50
43
|
const parts = fullKey.split('.');
|
|
51
44
|
const shortKey = parts[parts.length - 1];
|
|
52
|
-
const
|
|
53
|
-
|
|
45
|
+
const topSection = parts[0];
|
|
46
|
+
// Initialize section if needed
|
|
47
|
+
sections[topSection] = sections[topSection] ?? {};
|
|
54
48
|
if (shortKey in config) {
|
|
55
|
-
|
|
49
|
+
const value = config[shortKey];
|
|
50
|
+
// Build nested structure recursively
|
|
51
|
+
let current = sections[topSection];
|
|
52
|
+
for (let i = 1; i < parts.length - 1; i++) {
|
|
53
|
+
current[parts[i]] = current[parts[i]] ?? {};
|
|
54
|
+
current = current[parts[i]];
|
|
55
|
+
}
|
|
56
|
+
current[parts[parts.length - 1]] = value;
|
|
56
57
|
}
|
|
57
58
|
}
|
|
58
|
-
// Save each section
|
|
59
59
|
for (const [section, sectionConfig] of Object.entries(sections)) {
|
|
60
60
|
saveConfig(section, sectionConfig);
|
|
61
61
|
}
|
|
62
62
|
return withQueueHandler(ComponentName.Config, (first, rest) => {
|
|
63
63
|
addToTimeline(markAsDone(first), createFeedback(FeedbackType.Succeeded, FeedbackMessages.ConfigurationComplete));
|
|
64
|
+
// If tasks are provided, continue with execution
|
|
65
|
+
if (tasks && service && executeHandlers) {
|
|
66
|
+
return [
|
|
67
|
+
...rest,
|
|
68
|
+
createExecuteDefinition(tasks, service, executeHandlers.onError, executeHandlers.onComplete, executeHandlers.onAborted),
|
|
69
|
+
];
|
|
70
|
+
}
|
|
71
|
+
// Otherwise, exit (legacy behavior for initial setup)
|
|
64
72
|
exitApp(0);
|
|
65
73
|
return rest;
|
|
66
74
|
}, false, 0);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ComponentName, FeedbackType } from '../types/types.js';
|
|
2
|
+
import { createFeedback, createMessage, markAsDone, } from '../services/components.js';
|
|
3
|
+
import { formatDuration } from '../services/messages.js';
|
|
4
|
+
import { exitApp } from '../services/process.js';
|
|
5
|
+
import { ExecutionResult } from '../services/shell.js';
|
|
6
|
+
import { withQueueHandler } from '../services/queue.js';
|
|
7
|
+
/**
|
|
8
|
+
* Creates all execute handlers
|
|
9
|
+
*/
|
|
10
|
+
export function createExecuteHandlers(ops, handleAborted) {
|
|
11
|
+
void handleAborted;
|
|
12
|
+
const onError = (error) => {
|
|
13
|
+
ops.setQueue(withQueueHandler(ComponentName.Execute, (first) => {
|
|
14
|
+
ops.addToTimeline(markAsDone(first), createFeedback(FeedbackType.Failed, error));
|
|
15
|
+
exitApp(1);
|
|
16
|
+
return [];
|
|
17
|
+
}));
|
|
18
|
+
};
|
|
19
|
+
const onComplete = (outputs, totalElapsed) => {
|
|
20
|
+
ops.setQueue(withQueueHandler(ComponentName.Execute, (first) => {
|
|
21
|
+
const failed = outputs.find((out) => out.result !== ExecutionResult.Success);
|
|
22
|
+
if (failed) {
|
|
23
|
+
const errorMessage = failed.error
|
|
24
|
+
? `${failed.description}: ${failed.error}`
|
|
25
|
+
: `${failed.description} failed`;
|
|
26
|
+
ops.addToTimeline(markAsDone(first), createFeedback(FeedbackType.Failed, errorMessage));
|
|
27
|
+
exitApp(1);
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
ops.addToTimeline(markAsDone(first), createMessage(`Execution completed in ${formatDuration(totalElapsed)}.`));
|
|
31
|
+
exitApp(0);
|
|
32
|
+
return [];
|
|
33
|
+
}));
|
|
34
|
+
};
|
|
35
|
+
const onAborted = (elapsedTime) => {
|
|
36
|
+
ops.setQueue(withQueueHandler(ComponentName.Execute, (first) => {
|
|
37
|
+
const message = elapsedTime > 0
|
|
38
|
+
? `The execution was cancelled after ${formatDuration(elapsedTime)}.`
|
|
39
|
+
: 'The execution was cancelled.';
|
|
40
|
+
ops.addToTimeline(markAsDone(first), createFeedback(FeedbackType.Aborted, message));
|
|
41
|
+
exitApp(0);
|
|
42
|
+
return [];
|
|
43
|
+
}));
|
|
44
|
+
};
|
|
45
|
+
return { onError, onComplete, onAborted };
|
|
46
|
+
}
|
|
@@ -1,88 +1,140 @@
|
|
|
1
1
|
import { ComponentName, FeedbackType, TaskType } from '../types/types.js';
|
|
2
|
-
import { createAnswerDefinition, createConfigDefinitionWithKeys, createFeedback, createIntrospectDefinition, markAsDone, } from '../services/components.js';
|
|
3
|
-
import {
|
|
2
|
+
import { createAnswerDefinition, createConfigDefinitionWithKeys, createExecuteDefinition, createFeedback, createIntrospectDefinition, createValidateDefinition, markAsDone, } from '../services/components.js';
|
|
3
|
+
import { StepType } from '../ui/Config.js';
|
|
4
4
|
import { getCancellationMessage } from '../services/messages.js';
|
|
5
5
|
import { exitApp } from '../services/process.js';
|
|
6
6
|
import { withQueueHandler } from '../services/queue.js';
|
|
7
|
+
import { createConfigExecutionAbortedHandler, createConfigExecutionFinishedHandler, } from './config.js';
|
|
8
|
+
import { validateExecuteTasks } from '../services/execution-validator.js';
|
|
7
9
|
/**
|
|
8
|
-
* Creates execution
|
|
9
|
-
*/
|
|
10
|
-
export function createExecutionConfirmedHandler(timelineRef, addToTimeline, service, handleIntrospectError, handleIntrospectComplete, handleIntrospectAborted, handleAnswerError, handleAnswerComplete, handleAnswerAborted, setQueue) {
|
|
11
|
-
return () => withQueueHandler(ComponentName.Confirm, (first) => {
|
|
12
|
-
// Find the most recent Plan in timeline to get tasks
|
|
13
|
-
const currentTimeline = timelineRef.current;
|
|
14
|
-
const lastPlanIndex = [...currentTimeline]
|
|
15
|
-
.reverse()
|
|
16
|
-
.findIndex((item) => item.name === ComponentName.Plan);
|
|
17
|
-
const lastPlan = lastPlanIndex >= 0
|
|
18
|
-
? currentTimeline[currentTimeline.length - 1 - lastPlanIndex]
|
|
19
|
-
: null;
|
|
20
|
-
const tasks = lastPlan?.name === ComponentName.Plan &&
|
|
21
|
-
Array.isArray(lastPlan.props.tasks)
|
|
22
|
-
? lastPlan.props.tasks
|
|
23
|
-
: [];
|
|
24
|
-
const allIntrospect = tasks.every((task) => task.type === TaskType.Introspect);
|
|
25
|
-
const allAnswer = tasks.every((task) => task.type === TaskType.Answer);
|
|
26
|
-
const allConfig = tasks.every((task) => task.type === TaskType.Config);
|
|
27
|
-
if (allIntrospect && tasks.length > 0) {
|
|
28
|
-
// Execute introspection
|
|
29
|
-
addToTimeline(markAsDone(first));
|
|
30
|
-
return [
|
|
31
|
-
createIntrospectDefinition(tasks, service, handleIntrospectError, handleIntrospectComplete, handleIntrospectAborted),
|
|
32
|
-
];
|
|
33
|
-
}
|
|
34
|
-
else if (allAnswer && tasks.length > 0) {
|
|
35
|
-
// Execute answer - extract question from first task
|
|
36
|
-
const question = tasks[0].action;
|
|
37
|
-
addToTimeline(markAsDone(first));
|
|
38
|
-
return [
|
|
39
|
-
createAnswerDefinition(question, service, handleAnswerError, handleAnswerComplete, handleAnswerAborted),
|
|
40
|
-
];
|
|
41
|
-
}
|
|
42
|
-
else if (allConfig && tasks.length > 0) {
|
|
43
|
-
// Execute config - extract keys from task params
|
|
44
|
-
const keys = tasks
|
|
45
|
-
.map((task) => task.params?.key)
|
|
46
|
-
.filter((key) => typeof key === 'string');
|
|
47
|
-
addToTimeline(markAsDone(first));
|
|
48
|
-
// Create handlers with keys for proper saving
|
|
49
|
-
// Wrap in setQueue to properly update queue when Config finishes
|
|
50
|
-
const handleConfigFinished = (config) => {
|
|
51
|
-
setQueue(createConfigExecutionFinishedHandler(addToTimeline, keys)(config));
|
|
52
|
-
};
|
|
53
|
-
const handleConfigAborted = () => {
|
|
54
|
-
setQueue(createConfigExecutionAbortedHandler(addToTimeline)());
|
|
55
|
-
};
|
|
56
|
-
return [
|
|
57
|
-
createConfigDefinitionWithKeys(keys, handleConfigFinished, handleConfigAborted),
|
|
58
|
-
];
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
// Regular execution - just exit for now
|
|
62
|
-
addToTimeline(markAsDone(first));
|
|
63
|
-
exitApp(0);
|
|
64
|
-
return [];
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Creates execution cancelled handler
|
|
10
|
+
* Creates all execution handlers
|
|
70
11
|
*/
|
|
71
|
-
export function
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
.
|
|
77
|
-
.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
12
|
+
export function createExecutionHandlers(ops, taskHandlers) {
|
|
13
|
+
const onConfirmed = (tasks) => {
|
|
14
|
+
ops.setQueue(withQueueHandler(ComponentName.Confirm, (first) => {
|
|
15
|
+
const allIntrospect = tasks.every((task) => task.type === TaskType.Introspect);
|
|
16
|
+
const allAnswer = tasks.every((task) => task.type === TaskType.Answer);
|
|
17
|
+
const allConfig = tasks.every((task) => task.type === TaskType.Config);
|
|
18
|
+
const allExecute = tasks.every((task) => task.type === TaskType.Execute);
|
|
19
|
+
const service = ops.service;
|
|
20
|
+
if (!service) {
|
|
21
|
+
ops.addToTimeline(markAsDone(first), createFeedback(FeedbackType.Failed, 'Service not available'));
|
|
22
|
+
exitApp(1);
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
if (allIntrospect && tasks.length > 0) {
|
|
26
|
+
ops.addToTimeline(markAsDone(first));
|
|
27
|
+
return [
|
|
28
|
+
createIntrospectDefinition(tasks, service, taskHandlers.introspect.onError, taskHandlers.introspect.onComplete, taskHandlers.introspect.onAborted),
|
|
29
|
+
];
|
|
30
|
+
}
|
|
31
|
+
else if (allAnswer && tasks.length > 0) {
|
|
32
|
+
const question = tasks[0].action;
|
|
33
|
+
ops.addToTimeline(markAsDone(first));
|
|
34
|
+
return [
|
|
35
|
+
createAnswerDefinition(question, service, taskHandlers.answer.onError, taskHandlers.answer.onComplete, taskHandlers.answer.onAborted),
|
|
36
|
+
];
|
|
37
|
+
}
|
|
38
|
+
else if (allConfig && tasks.length > 0) {
|
|
39
|
+
const keys = tasks
|
|
40
|
+
.map((task) => task.params?.key)
|
|
41
|
+
.filter((key) => typeof key === 'string');
|
|
42
|
+
ops.addToTimeline(markAsDone(first));
|
|
43
|
+
const handleConfigFinished = (config) => {
|
|
44
|
+
ops.setQueue(createConfigExecutionFinishedHandler(ops.addToTimeline, keys)(config));
|
|
45
|
+
};
|
|
46
|
+
const handleConfigAborted = () => {
|
|
47
|
+
ops.setQueue(createConfigExecutionAbortedHandler(ops.addToTimeline)());
|
|
48
|
+
};
|
|
49
|
+
return [
|
|
50
|
+
createConfigDefinitionWithKeys(keys, handleConfigFinished, handleConfigAborted),
|
|
51
|
+
];
|
|
52
|
+
}
|
|
53
|
+
else if (allExecute && tasks.length > 0) {
|
|
54
|
+
// Validate config requirements before execution
|
|
55
|
+
const missingConfig = validateExecuteTasks(tasks);
|
|
56
|
+
if (missingConfig.length > 0) {
|
|
57
|
+
// Config is missing - call VALIDATE tool to get contextual descriptions
|
|
58
|
+
const keys = missingConfig.map((req) => req.path);
|
|
59
|
+
const userRequest = tasks.map((t) => t.action).join(', ');
|
|
60
|
+
ops.addToTimeline(markAsDone(first));
|
|
61
|
+
// Create handlers for Validate completion
|
|
62
|
+
const handleValidateComplete = (configWithDescriptions) => {
|
|
63
|
+
ops.setQueue(withQueueHandler(ComponentName.Validate, (first) => {
|
|
64
|
+
// Create CONFIG component with descriptions from VALIDATE
|
|
65
|
+
const handleConfigFinished = (config) => {
|
|
66
|
+
ops.setQueue(createConfigExecutionFinishedHandler(ops.addToTimeline, keys, tasks, service, taskHandlers.execute)(config));
|
|
67
|
+
};
|
|
68
|
+
const handleConfigAborted = () => {
|
|
69
|
+
ops.setQueue(createConfigExecutionAbortedHandler(ops.addToTimeline)());
|
|
70
|
+
};
|
|
71
|
+
// Create config steps from validated descriptions
|
|
72
|
+
const steps = configWithDescriptions.map((req) => {
|
|
73
|
+
const keyParts = req.path.split('.');
|
|
74
|
+
const shortKey = keyParts[keyParts.length - 1];
|
|
75
|
+
// Extract description without the {path} suffix
|
|
76
|
+
// Format from VALIDATE: "Description {path}"
|
|
77
|
+
let description = req.description || req.path;
|
|
78
|
+
const pathPattern = /\s*\{[^}]+\}\s*$/;
|
|
79
|
+
description = description.replace(pathPattern, '').trim();
|
|
80
|
+
const step = {
|
|
81
|
+
description,
|
|
82
|
+
key: shortKey,
|
|
83
|
+
path: req.path,
|
|
84
|
+
type: StepType.Text,
|
|
85
|
+
value: null,
|
|
86
|
+
validate: () => true,
|
|
87
|
+
};
|
|
88
|
+
return step;
|
|
89
|
+
});
|
|
90
|
+
// Mark Validate as done and move to timeline
|
|
91
|
+
ops.addToTimeline(markAsDone(first));
|
|
92
|
+
return [
|
|
93
|
+
{
|
|
94
|
+
id: crypto.randomUUID(),
|
|
95
|
+
name: ComponentName.Config,
|
|
96
|
+
state: { done: false },
|
|
97
|
+
props: {
|
|
98
|
+
steps,
|
|
99
|
+
onFinished: handleConfigFinished,
|
|
100
|
+
onAborted: handleConfigAborted,
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
];
|
|
104
|
+
}));
|
|
105
|
+
};
|
|
106
|
+
const handleValidateError = (error) => {
|
|
107
|
+
ops.addToTimeline(createFeedback(FeedbackType.Failed, error));
|
|
108
|
+
exitApp(1);
|
|
109
|
+
};
|
|
110
|
+
const handleValidateAborted = () => {
|
|
111
|
+
ops.addToTimeline(createFeedback(FeedbackType.Aborted, 'Configuration validation cancelled'));
|
|
112
|
+
exitApp(0);
|
|
113
|
+
};
|
|
114
|
+
return [
|
|
115
|
+
createValidateDefinition(missingConfig, userRequest, service, handleValidateError, handleValidateComplete, handleValidateAborted),
|
|
116
|
+
];
|
|
117
|
+
}
|
|
118
|
+
// No missing config - execute directly
|
|
119
|
+
ops.addToTimeline(markAsDone(first));
|
|
120
|
+
return [
|
|
121
|
+
createExecuteDefinition(tasks, service, taskHandlers.execute.onError, taskHandlers.execute.onComplete, taskHandlers.execute.onAborted),
|
|
122
|
+
];
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
ops.addToTimeline(markAsDone(first), createFeedback(FeedbackType.Failed, 'I can only process one type of task at a time for now.'));
|
|
126
|
+
exitApp(0);
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
}));
|
|
130
|
+
};
|
|
131
|
+
const onCancelled = (tasks) => {
|
|
132
|
+
ops.setQueue(withQueueHandler(ComponentName.Confirm, (first) => {
|
|
133
|
+
const allIntrospect = tasks.every((task) => task.type === TaskType.Introspect);
|
|
134
|
+
const operation = allIntrospect ? 'introspection' : 'execution';
|
|
135
|
+
ops.addToTimeline(markAsDone(first), createFeedback(FeedbackType.Aborted, getCancellationMessage(operation)));
|
|
136
|
+
return undefined;
|
|
137
|
+
}, true, 0));
|
|
138
|
+
};
|
|
139
|
+
return { onConfirmed, onCancelled };
|
|
88
140
|
}
|
|
@@ -2,27 +2,20 @@ import { ComponentName } from '../types/types.js';
|
|
|
2
2
|
import { createReportDefinition } from '../services/components.js';
|
|
3
3
|
import { createErrorHandler, withQueueHandler } from '../services/queue.js';
|
|
4
4
|
/**
|
|
5
|
-
* Creates introspect
|
|
5
|
+
* Creates all introspect handlers
|
|
6
6
|
*/
|
|
7
|
-
export function
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
return undefined;
|
|
19
|
-
}, true, 0);
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Creates introspect aborted handler
|
|
23
|
-
*/
|
|
24
|
-
export function createIntrospectAbortedHandler(handleAborted) {
|
|
25
|
-
return () => {
|
|
7
|
+
export function createIntrospectHandlers(ops, handleAborted) {
|
|
8
|
+
const onError = (error) => {
|
|
9
|
+
ops.setQueue(createErrorHandler(ComponentName.Introspect, ops.addToTimeline)(error));
|
|
10
|
+
};
|
|
11
|
+
const onComplete = (message, capabilities) => {
|
|
12
|
+
ops.setQueue(withQueueHandler(ComponentName.Introspect, () => {
|
|
13
|
+
ops.addToTimeline(createReportDefinition(message, capabilities));
|
|
14
|
+
return undefined;
|
|
15
|
+
}, true, 0));
|
|
16
|
+
};
|
|
17
|
+
const onAborted = () => {
|
|
26
18
|
handleAborted('Introspection');
|
|
27
19
|
};
|
|
20
|
+
return { onError, onComplete, onAborted };
|
|
28
21
|
}
|
package/dist/handlers/plan.js
CHANGED
|
@@ -3,46 +3,41 @@ import { createConfirmDefinition, createFeedback, createPlanDefinition, markAsDo
|
|
|
3
3
|
import { FeedbackMessages, formatErrorMessage, getRefiningMessage, } from '../services/messages.js';
|
|
4
4
|
import { exitApp } from '../services/process.js';
|
|
5
5
|
/**
|
|
6
|
-
* Creates plan
|
|
6
|
+
* Creates all plan handlers
|
|
7
7
|
*/
|
|
8
|
-
export function
|
|
9
|
-
|
|
8
|
+
export function createPlanHandlers(ops, handleAborted, executionHandlers) {
|
|
9
|
+
const onAborted = () => {
|
|
10
10
|
handleAborted('Task selection');
|
|
11
11
|
};
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Creates plan abort handler factory
|
|
15
|
-
*/
|
|
16
|
-
export function createPlanAbortHandlerFactory(handleAborted, handlePlanAborted) {
|
|
17
|
-
return (tasks) => {
|
|
12
|
+
const createAbortHandler = (tasks) => {
|
|
18
13
|
const allIntrospect = tasks.every((task) => task.type === TaskType.Introspect);
|
|
19
14
|
if (allIntrospect) {
|
|
20
15
|
return () => {
|
|
21
16
|
handleAborted('Introspection');
|
|
22
17
|
};
|
|
23
18
|
}
|
|
24
|
-
return
|
|
19
|
+
return onAborted;
|
|
25
20
|
};
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return async (selectedTasks) => {
|
|
32
|
-
// Mark current plan as done and add refinement to queue
|
|
33
|
-
const refinementDef = createRefinement(getRefiningMessage(), handleRefinementAborted);
|
|
34
|
-
setQueue((currentQueue) => {
|
|
21
|
+
const onSelectionConfirmed = async (selectedTasks) => {
|
|
22
|
+
const refinementDef = createRefinement(getRefiningMessage(), () => {
|
|
23
|
+
handleAborted('Plan refinement');
|
|
24
|
+
});
|
|
25
|
+
ops.setQueue((currentQueue) => {
|
|
35
26
|
if (currentQueue.length === 0)
|
|
36
27
|
return currentQueue;
|
|
37
28
|
const [first] = currentQueue;
|
|
38
29
|
if (first.name === ComponentName.Plan) {
|
|
39
|
-
addToTimeline(markAsDone(first));
|
|
30
|
+
ops.addToTimeline(markAsDone(first));
|
|
40
31
|
}
|
|
41
|
-
// Add refinement to queue so it becomes the active component
|
|
42
32
|
return [refinementDef];
|
|
43
33
|
});
|
|
44
|
-
// Process refined command in background
|
|
45
34
|
try {
|
|
35
|
+
const service = ops.service;
|
|
36
|
+
if (!service) {
|
|
37
|
+
ops.addToTimeline(createFeedback(FeedbackType.Failed, FeedbackMessages.UnexpectedError, 'Service not available'));
|
|
38
|
+
exitApp(1);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
46
41
|
const refinedCommand = selectedTasks
|
|
47
42
|
.map((task) => {
|
|
48
43
|
const action = task.action.toLowerCase().replace(/,/g, ' -');
|
|
@@ -51,32 +46,34 @@ export function createPlanSelectionConfirmedHandler(addToTimeline, service, hand
|
|
|
51
46
|
})
|
|
52
47
|
.join(', ');
|
|
53
48
|
const result = await service.processWithTool(refinedCommand, 'plan');
|
|
54
|
-
|
|
55
|
-
setQueue((currentQueue) => {
|
|
49
|
+
ops.setQueue((currentQueue) => {
|
|
56
50
|
if (currentQueue.length > 0 &&
|
|
57
51
|
currentQueue[0].id === refinementDef.id) {
|
|
58
|
-
addToTimeline(markAsDone(currentQueue[0]));
|
|
52
|
+
ops.addToTimeline(markAsDone(currentQueue[0]));
|
|
59
53
|
}
|
|
60
54
|
return [];
|
|
61
55
|
});
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
56
|
+
const planDefinition = createPlanDefinition(result.message, result.tasks, createAbortHandler(result.tasks), undefined);
|
|
57
|
+
const confirmDefinition = createConfirmDefinition(() => {
|
|
58
|
+
executionHandlers.onConfirmed(result.tasks);
|
|
59
|
+
}, () => {
|
|
60
|
+
executionHandlers.onCancelled(result.tasks);
|
|
61
|
+
});
|
|
62
|
+
ops.addToTimeline(planDefinition);
|
|
63
|
+
ops.setQueue([confirmDefinition]);
|
|
67
64
|
}
|
|
68
65
|
catch (error) {
|
|
69
66
|
const errorMessage = formatErrorMessage(error);
|
|
70
|
-
|
|
71
|
-
setQueue((currentQueue) => {
|
|
67
|
+
ops.setQueue((currentQueue) => {
|
|
72
68
|
if (currentQueue.length > 0 &&
|
|
73
69
|
currentQueue[0].id === refinementDef.id) {
|
|
74
|
-
addToTimeline(markAsDone(currentQueue[0]));
|
|
70
|
+
ops.addToTimeline(markAsDone(currentQueue[0]));
|
|
75
71
|
}
|
|
76
72
|
return [];
|
|
77
73
|
});
|
|
78
|
-
addToTimeline(createFeedback(FeedbackType.Failed, FeedbackMessages.UnexpectedError, errorMessage));
|
|
74
|
+
ops.addToTimeline(createFeedback(FeedbackType.Failed, FeedbackMessages.UnexpectedError, errorMessage));
|
|
79
75
|
exitApp(1);
|
|
80
76
|
}
|
|
81
77
|
};
|
|
78
|
+
return { onAborted, createAbortHandler, onSelectionConfirmed };
|
|
82
79
|
}
|
|
@@ -16,7 +16,10 @@ export class AnthropicService {
|
|
|
16
16
|
// Build system prompt with additional context based on tool
|
|
17
17
|
let systemPrompt = instructions;
|
|
18
18
|
// Add skills section for applicable tools
|
|
19
|
-
if (toolName === 'plan' ||
|
|
19
|
+
if (toolName === 'plan' ||
|
|
20
|
+
toolName === 'introspect' ||
|
|
21
|
+
toolName === 'execute' ||
|
|
22
|
+
toolName === 'validate') {
|
|
20
23
|
const skills = loadSkills();
|
|
21
24
|
const skillsSection = formatSkillsForPrompt(skills);
|
|
22
25
|
systemPrompt += skillsSection;
|
|
@@ -74,6 +77,29 @@ export class AnthropicService {
|
|
|
74
77
|
const content = toolUseContent;
|
|
75
78
|
// Extract and validate response based on tool type
|
|
76
79
|
const input = content.input;
|
|
80
|
+
// Handle execute tool response
|
|
81
|
+
if (toolName === 'execute') {
|
|
82
|
+
if (!input.message || typeof input.message !== 'string') {
|
|
83
|
+
throw new Error('Invalid tool response: missing or invalid message field');
|
|
84
|
+
}
|
|
85
|
+
if (!input.commands || !Array.isArray(input.commands)) {
|
|
86
|
+
throw new Error('Invalid tool response: missing or invalid commands array');
|
|
87
|
+
}
|
|
88
|
+
// Validate each command has required fields
|
|
89
|
+
input.commands.forEach((cmd, i) => {
|
|
90
|
+
if (!cmd.description || typeof cmd.description !== 'string') {
|
|
91
|
+
throw new Error(`Invalid command at index ${String(i)}: missing or invalid 'description' field`);
|
|
92
|
+
}
|
|
93
|
+
if (!cmd.command || typeof cmd.command !== 'string') {
|
|
94
|
+
throw new Error(`Invalid command at index ${String(i)}: missing or invalid 'command' field`);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
return {
|
|
98
|
+
message: input.message,
|
|
99
|
+
tasks: [],
|
|
100
|
+
commands: input.commands,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
77
103
|
// Handle answer tool response
|
|
78
104
|
if (toolName === 'answer') {
|
|
79
105
|
if (!input.question || typeof input.question !== 'string') {
|
|
@@ -89,7 +115,7 @@ export class AnthropicService {
|
|
|
89
115
|
};
|
|
90
116
|
}
|
|
91
117
|
// Handle plan and introspect tool responses
|
|
92
|
-
if (
|
|
118
|
+
if (input.message === undefined || typeof input.message !== 'string') {
|
|
93
119
|
throw new Error('Invalid tool response: missing or invalid message field');
|
|
94
120
|
}
|
|
95
121
|
if (!input.tasks || !Array.isArray(input.tasks)) {
|
package/dist/services/colors.js
CHANGED
|
@@ -12,11 +12,11 @@ export const Palette = {
|
|
|
12
12
|
CharcoalGray: '#282828',
|
|
13
13
|
Green: '#5aaa8a',
|
|
14
14
|
LightGreen: '#65b595',
|
|
15
|
-
BrightGreen: '#
|
|
15
|
+
BrightGreen: '#3e9a3e',
|
|
16
16
|
Yellow: '#cccc5c',
|
|
17
17
|
LightYellow: '#d4d47a',
|
|
18
|
-
Orange: '#
|
|
19
|
-
DarkOrange: '#
|
|
18
|
+
Orange: '#f48c80',
|
|
19
|
+
DarkOrange: '#ab5e40',
|
|
20
20
|
BurntOrange: '#cc7a5c',
|
|
21
21
|
Red: '#cc5c5c',
|
|
22
22
|
Cyan: '#5c9ccc',
|
|
@@ -65,8 +65,22 @@ export function createConfigStepsFromSchema(keys) {
|
|
|
65
65
|
// Config doesn't exist yet, use defaults
|
|
66
66
|
}
|
|
67
67
|
return keys.map((key) => {
|
|
68
|
+
// Check if key is in schema (built-in config)
|
|
68
69
|
if (!(key in schema)) {
|
|
69
|
-
|
|
70
|
+
// Key is not in schema - it's from a skill
|
|
71
|
+
// Create a simple text step with placeholder description
|
|
72
|
+
const keyParts = key.split('.');
|
|
73
|
+
const shortKey = keyParts[keyParts.length - 1];
|
|
74
|
+
const description = keyParts
|
|
75
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
76
|
+
.join(' ');
|
|
77
|
+
return {
|
|
78
|
+
description: `${description} {${key}}`,
|
|
79
|
+
key: shortKey,
|
|
80
|
+
type: StepType.Text,
|
|
81
|
+
value: null,
|
|
82
|
+
validate: () => true, // Accept any string for now
|
|
83
|
+
};
|
|
70
84
|
}
|
|
71
85
|
const definition = schema[key];
|
|
72
86
|
const currentValue = getConfigValue(currentConfig, key);
|
|
@@ -301,3 +315,38 @@ export function createAnswerDisplayDefinition(answer) {
|
|
|
301
315
|
export function isStateless(component) {
|
|
302
316
|
return !('state' in component);
|
|
303
317
|
}
|
|
318
|
+
export function createExecuteDefinition(tasks, service, onError, onComplete, onAborted) {
|
|
319
|
+
return {
|
|
320
|
+
id: randomUUID(),
|
|
321
|
+
name: ComponentName.Execute,
|
|
322
|
+
state: {
|
|
323
|
+
done: false,
|
|
324
|
+
isLoading: true,
|
|
325
|
+
},
|
|
326
|
+
props: {
|
|
327
|
+
tasks,
|
|
328
|
+
service,
|
|
329
|
+
onError,
|
|
330
|
+
onComplete,
|
|
331
|
+
onAborted,
|
|
332
|
+
},
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
export function createValidateDefinition(missingConfig, userRequest, service, onError, onComplete, onAborted) {
|
|
336
|
+
return {
|
|
337
|
+
id: randomUUID(),
|
|
338
|
+
name: ComponentName.Validate,
|
|
339
|
+
state: {
|
|
340
|
+
done: false,
|
|
341
|
+
isLoading: true,
|
|
342
|
+
},
|
|
343
|
+
props: {
|
|
344
|
+
missingConfig,
|
|
345
|
+
userRequest,
|
|
346
|
+
service,
|
|
347
|
+
onError,
|
|
348
|
+
onComplete,
|
|
349
|
+
onAborted,
|
|
350
|
+
},
|
|
351
|
+
};
|
|
352
|
+
}
|