prompt-language-shell 0.4.0 → 0.4.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/config/ANSWER.md +119 -0
- package/dist/config/INTROSPECT.md +18 -13
- package/dist/config/PLAN.md +27 -6
- package/dist/handlers/answer.js +28 -0
- package/dist/handlers/command.js +38 -0
- package/dist/handlers/config.js +39 -0
- package/dist/handlers/execution.js +68 -0
- package/dist/handlers/introspect.js +28 -0
- package/dist/handlers/plan.js +82 -0
- package/dist/services/anthropic.js +18 -2
- package/dist/{types → services}/colors.js +58 -6
- package/dist/services/components.js +54 -1
- package/dist/services/queue.js +52 -0
- package/dist/services/tool-registry.js +5 -0
- package/dist/tools/answer.tool.js +18 -0
- package/dist/types/types.js +4 -0
- package/dist/ui/Answer.js +71 -0
- package/dist/ui/AnswerDisplay.js +8 -0
- package/dist/ui/Command.js +3 -1
- package/dist/ui/Component.js +23 -2
- package/dist/ui/Config.js +1 -1
- package/dist/ui/Confirm.js +4 -3
- package/dist/ui/Feedback.js +2 -2
- package/dist/ui/Introspect.js +100 -0
- package/dist/ui/Label.js +4 -2
- package/dist/ui/List.js +3 -3
- package/dist/ui/Main.js +41 -174
- package/dist/ui/Plan.js +13 -10
- package/dist/ui/Report.js +13 -0
- package/dist/ui/Separator.js +1 -1
- package/package.json +1 -1
- /package/dist/services/{config.js → configuration.js} +0 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
## Overview
|
|
2
|
+
|
|
3
|
+
You are the answer execution component of "pls" (please), a professional
|
|
4
|
+
command-line concierge. Your role is to **answer questions** and provide
|
|
5
|
+
up-to-date information when a task with type "answer" has been planned and
|
|
6
|
+
confirmed.
|
|
7
|
+
|
|
8
|
+
**IMPORTANT**: Use web search to find current, accurate information. This tool
|
|
9
|
+
is designed for quick answers from the terminal without needing to open a web
|
|
10
|
+
browser. Always search for the latest data rather than relying solely on
|
|
11
|
+
training data.
|
|
12
|
+
|
|
13
|
+
## Execution Flow
|
|
14
|
+
|
|
15
|
+
This tool is invoked AFTER:
|
|
16
|
+
1. PLAN detected an information request and created a task with type "answer"
|
|
17
|
+
2. User reviewed and confirmed the plan
|
|
18
|
+
3. The answer task is now being executed
|
|
19
|
+
|
|
20
|
+
Your task is to provide a clear, concise answer to the user's question.
|
|
21
|
+
|
|
22
|
+
## Input
|
|
23
|
+
|
|
24
|
+
You will receive:
|
|
25
|
+
- A question or information request from the user
|
|
26
|
+
- Context from the conversation if relevant
|
|
27
|
+
|
|
28
|
+
## Response Format
|
|
29
|
+
|
|
30
|
+
Provide a direct, helpful answer following these strict formatting rules:
|
|
31
|
+
|
|
32
|
+
**Critical constraints:**
|
|
33
|
+
- Maximum 4 lines of text
|
|
34
|
+
- Each line maximum 80 characters
|
|
35
|
+
- Use natural line breaks for readability
|
|
36
|
+
- Be concise but complete
|
|
37
|
+
- Focus on the most important information
|
|
38
|
+
|
|
39
|
+
**Formatting guidelines:**
|
|
40
|
+
- Start directly with the answer (no preamble like "Here's the answer:")
|
|
41
|
+
- Use plain language, avoid jargon unless necessary
|
|
42
|
+
- Break long sentences naturally at phrase boundaries
|
|
43
|
+
- If the answer requires more than 4 lines, prioritize the most essential
|
|
44
|
+
information
|
|
45
|
+
|
|
46
|
+
## Examples
|
|
47
|
+
|
|
48
|
+
### Example 1: Simple factual question
|
|
49
|
+
|
|
50
|
+
Question: "What is TypeScript?"
|
|
51
|
+
|
|
52
|
+
Good answer:
|
|
53
|
+
```
|
|
54
|
+
TypeScript is a programming language that adds static typing to JavaScript.
|
|
55
|
+
It helps catch errors during development and improves code maintainability.
|
|
56
|
+
TypeScript code compiles to JavaScript and runs anywhere JavaScript runs.
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Bad answer (too verbose):
|
|
60
|
+
```
|
|
61
|
+
TypeScript is a strongly typed programming language that builds on JavaScript,
|
|
62
|
+
giving you better tooling at any scale. TypeScript adds additional syntax to
|
|
63
|
+
JavaScript to support a tighter integration with your editor. It catches errors
|
|
64
|
+
early in development by checking types. TypeScript code converts to JavaScript
|
|
65
|
+
which runs anywhere.
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Example 2: Technical explanation
|
|
69
|
+
|
|
70
|
+
Question: "How does async/await work?"
|
|
71
|
+
|
|
72
|
+
Good answer:
|
|
73
|
+
```
|
|
74
|
+
Async/await makes asynchronous code look synchronous. The 'async' keyword
|
|
75
|
+
marks a function that returns a Promise. The 'await' keyword pauses
|
|
76
|
+
execution until the Promise resolves, then returns the result.
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Example 3: Question requiring context
|
|
80
|
+
|
|
81
|
+
Question: "What's the capital of France?"
|
|
82
|
+
|
|
83
|
+
Good answer:
|
|
84
|
+
```
|
|
85
|
+
Paris is the capital of France.
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Example 4: Complex question requiring prioritization
|
|
89
|
+
|
|
90
|
+
Question: "Explain how React hooks work"
|
|
91
|
+
|
|
92
|
+
Good answer:
|
|
93
|
+
```
|
|
94
|
+
React Hooks let you use state and other React features without classes.
|
|
95
|
+
useState manages component state, useEffect handles side effects.
|
|
96
|
+
Hooks must be called at the top level and only in function components.
|
|
97
|
+
They enable cleaner, more reusable component logic.
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Guidelines
|
|
101
|
+
|
|
102
|
+
1. **Be direct**: Answer the question immediately, don't introduce your answer
|
|
103
|
+
2. **Be accurate**: Provide correct, factual information
|
|
104
|
+
3. **Be concise**: Respect the 4-line, 80-character constraints strictly
|
|
105
|
+
4. **Be helpful**: Focus on what the user needs to know
|
|
106
|
+
5. **Be clear**: Use simple language when possible
|
|
107
|
+
|
|
108
|
+
## Common Mistakes to Avoid
|
|
109
|
+
|
|
110
|
+
❌ Starting with "Here's the answer:" or "Let me explain:"
|
|
111
|
+
❌ Exceeding 4 lines or 80 characters per line
|
|
112
|
+
❌ Including unnecessary details
|
|
113
|
+
❌ Using overly technical jargon without explanation
|
|
114
|
+
❌ Repeating the question in the answer
|
|
115
|
+
|
|
116
|
+
✅ Direct, concise answers
|
|
117
|
+
✅ Proper line breaks at natural phrase boundaries
|
|
118
|
+
✅ Essential information only
|
|
119
|
+
✅ Clear, accessible language
|
|
@@ -59,13 +59,13 @@ Present capabilities in two categories:
|
|
|
59
59
|
|
|
60
60
|
These are the core operations available to all users:
|
|
61
61
|
|
|
62
|
-
- **
|
|
63
|
-
- **
|
|
62
|
+
- **Config**: Configuration changes, settings updates
|
|
63
|
+
- **Plan**: Plan and structure tasks from natural language requests, breaking
|
|
64
64
|
them down into clear, actionable steps
|
|
65
|
-
- **
|
|
66
|
-
- **
|
|
67
|
-
- **
|
|
68
|
-
- **
|
|
65
|
+
- **Introspect**: List and describe available capabilities and skills
|
|
66
|
+
- **Answer**: Answer questions, explain concepts, provide information
|
|
67
|
+
- **Execute**: Run shell commands, execute programs, process operations
|
|
68
|
+
- **Report**: Generate summaries, create reports, display results
|
|
69
69
|
|
|
70
70
|
### 2. User-Defined Skills
|
|
71
71
|
|
|
@@ -82,15 +82,20 @@ in the response. For each skill:
|
|
|
82
82
|
Create tasks with type "introspect" for each capability. Each task should:
|
|
83
83
|
|
|
84
84
|
- **Action**: The capability name and a concise description
|
|
85
|
-
- Format: "
|
|
85
|
+
- Format: "Capability Name: description" (note: display format will use " - " separator)
|
|
86
|
+
- **IMPORTANT**: Use title case for capability names (e.g., "Plan", "Execute"), NOT all uppercase (NOT "PLAN", "EXECUTE")
|
|
86
87
|
- Examples:
|
|
87
|
-
- "
|
|
88
|
-
- "
|
|
89
|
-
- "Deploy Application:
|
|
88
|
+
- "Plan: break down requests into actionable steps"
|
|
89
|
+
- "Execute: run shell commands and process operations"
|
|
90
|
+
- "Deploy Application: build and deploy to staging or production"
|
|
90
91
|
- **Type**: Always use "introspect"
|
|
91
92
|
- **Params**: Omit params field
|
|
92
93
|
|
|
93
|
-
**Keep action descriptions concise
|
|
94
|
+
**Keep action descriptions concise:**
|
|
95
|
+
- Maximum 60 characters for the description portion (after the colon)
|
|
96
|
+
- Focus on clarity and brevity
|
|
97
|
+
- Describe the core purpose in one short phrase
|
|
98
|
+
- Start descriptions with a lowercase letter (they follow a colon)
|
|
94
99
|
|
|
95
100
|
## Filtering
|
|
96
101
|
|
|
@@ -111,8 +116,8 @@ Examples:
|
|
|
111
116
|
### Example 1: List All Capabilities
|
|
112
117
|
|
|
113
118
|
When user asks "list your skills", create an introductory message like "here
|
|
114
|
-
are my capabilities:" followed by a task for each built-in capability:
|
|
115
|
-
|
|
119
|
+
are my capabilities:" followed by a task for each built-in capability: Plan,
|
|
120
|
+
Introspect, Answer, Execute, Report, and Config. Each task uses type
|
|
116
121
|
"introspect" with an action describing the capability.
|
|
117
122
|
|
|
118
123
|
### Example 2: Filtered Skills
|
package/dist/config/PLAN.md
CHANGED
|
@@ -237,7 +237,7 @@ Examples that should be aborted as offensive:
|
|
|
237
237
|
capabilities or skills:
|
|
238
238
|
- Verbs: "list skills", "show skills", "what can you do", "list
|
|
239
239
|
capabilities", "show capabilities", "what skills", "describe skills",
|
|
240
|
-
"flex", "show off"
|
|
240
|
+
"introspect", "flex", "show off"
|
|
241
241
|
- **Filtering**: If the request specifies a category, domain, or context
|
|
242
242
|
(e.g., "for deployment", "related to files", "about testing"), add a
|
|
243
243
|
params object with a filter field containing the specified context
|
|
@@ -248,11 +248,32 @@ Examples that should be aborted as offensive:
|
|
|
248
248
|
2. **Information requests** - Use "answer" type when request asks for
|
|
249
249
|
information:
|
|
250
250
|
- Verbs: "explain", "answer", "describe", "tell me", "say", "what
|
|
251
|
-
is", "how does"
|
|
252
|
-
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
251
|
+
is", "how does", "find", "search", "lookup"
|
|
252
|
+
- **CRITICAL**: The action field MUST contain a COMPLETE, SPECIFIC question
|
|
253
|
+
that can be answered definitively with web search
|
|
254
|
+
- **Be extremely clear and specific** - phrase the question so there is NO
|
|
255
|
+
ambiguity about what information is being requested
|
|
256
|
+
- **Include all context** - product names, versions, locations, timeframes
|
|
257
|
+
- **If ambiguous, use "define" type instead** - let user choose the specific
|
|
258
|
+
interpretation before creating the answer task
|
|
259
|
+
- Examples of CLEAR answer tasks:
|
|
260
|
+
- "what is typescript" → action: "What is TypeScript?"
|
|
261
|
+
- "find price of samsung the frame 55 inch" → action: "What is the current
|
|
262
|
+
retail price of the Samsung The Frame 55 inch TV?"
|
|
263
|
+
- "show apple stock price" → action: "What is the current stock price of
|
|
264
|
+
Apple Inc. (AAPL)?"
|
|
265
|
+
- "tell me about docker" → action: "What is Docker and what is it used
|
|
266
|
+
for?"
|
|
267
|
+
- Examples of AMBIGUOUS requests that need "define" type:
|
|
268
|
+
- "explain x" (unclear what x means) → Create "define" with options:
|
|
269
|
+
["Explain the letter X", "Explain X.com platform", "Explain X in
|
|
270
|
+
mathematics"]
|
|
271
|
+
- "find price of frame" (which frame?) → Create "define" with options:
|
|
272
|
+
["Find price of Samsung The Frame TV", "Find price of picture frames",
|
|
273
|
+
"Find price of Frame.io subscription"]
|
|
274
|
+
- "show python version" (which python?) → Create "define" with options:
|
|
275
|
+
["Show Python programming language latest version", "Show installed
|
|
276
|
+
Python version on this system"]
|
|
256
277
|
- **Exception**: Questions about capabilities/skills should use
|
|
257
278
|
"introspect" instead
|
|
258
279
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ComponentName } from '../types/types.js';
|
|
2
|
+
import { createAnswerDisplayDefinition } from '../services/components.js';
|
|
3
|
+
import { createErrorHandler, withQueueHandler } from '../services/queue.js';
|
|
4
|
+
/**
|
|
5
|
+
* Creates answer error handler
|
|
6
|
+
*/
|
|
7
|
+
export function createAnswerErrorHandler(addToTimeline) {
|
|
8
|
+
return (error) => createErrorHandler(ComponentName.Answer, addToTimeline)(error);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Creates answer completion handler
|
|
12
|
+
*/
|
|
13
|
+
export function createAnswerCompleteHandler(addToTimeline) {
|
|
14
|
+
return (answer) => withQueueHandler(ComponentName.Answer, () => {
|
|
15
|
+
// Don't add the Answer component to timeline (it renders null)
|
|
16
|
+
// Only add the AnswerDisplay component
|
|
17
|
+
addToTimeline(createAnswerDisplayDefinition(answer));
|
|
18
|
+
return undefined;
|
|
19
|
+
}, true, 0);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Creates answer aborted handler
|
|
23
|
+
*/
|
|
24
|
+
export function createAnswerAbortedHandler(handleAborted) {
|
|
25
|
+
return () => {
|
|
26
|
+
handleAborted('Answer');
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ComponentName, TaskType } from '../types/types.js';
|
|
2
|
+
import { createConfirmDefinition, createPlanDefinition, markAsDone, } from '../services/components.js';
|
|
3
|
+
import { createErrorHandler, withQueueHandler } from '../services/queue.js';
|
|
4
|
+
/**
|
|
5
|
+
* Creates command error handler
|
|
6
|
+
*/
|
|
7
|
+
export function createCommandErrorHandler(addToTimeline) {
|
|
8
|
+
return (error) => createErrorHandler(ComponentName.Command, addToTimeline)(error);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Creates command completion handler
|
|
12
|
+
*/
|
|
13
|
+
export function createCommandCompleteHandler(addToTimeline, createPlanAbortHandler, handlePlanSelectionConfirmed, handleExecutionConfirmed, handleExecutionCancelled) {
|
|
14
|
+
return (message, tasks) => withQueueHandler(ComponentName.Command, (first) => {
|
|
15
|
+
// Check if tasks contain a Define task that requires user interaction
|
|
16
|
+
const hasDefineTask = tasks.some((task) => task.type === TaskType.Define);
|
|
17
|
+
const planDefinition = createPlanDefinition(message, tasks, createPlanAbortHandler(tasks), hasDefineTask ? handlePlanSelectionConfirmed : undefined);
|
|
18
|
+
if (hasDefineTask) {
|
|
19
|
+
// Don't exit - keep the plan in the queue for interaction
|
|
20
|
+
addToTimeline(markAsDone(first));
|
|
21
|
+
return [planDefinition];
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
// No define task - show plan and confirmation
|
|
25
|
+
const confirmDefinition = createConfirmDefinition(handleExecutionConfirmed, handleExecutionCancelled);
|
|
26
|
+
addToTimeline(markAsDone(first), planDefinition);
|
|
27
|
+
return [confirmDefinition];
|
|
28
|
+
}
|
|
29
|
+
}, false, 0);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Creates command aborted handler
|
|
33
|
+
*/
|
|
34
|
+
export function createCommandAbortedHandler(handleAborted) {
|
|
35
|
+
return () => {
|
|
36
|
+
handleAborted('Request');
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ComponentName, FeedbackType } from '../types/types.js';
|
|
2
|
+
import { createAnthropicService, } from '../services/anthropic.js';
|
|
3
|
+
import { createCommandDefinition, createFeedback, markAsDone, } from '../services/components.js';
|
|
4
|
+
import { saveAnthropicConfig } from '../services/configuration.js';
|
|
5
|
+
import { FeedbackMessages } from '../services/messages.js';
|
|
6
|
+
import { exitApp } from '../services/process.js';
|
|
7
|
+
import { withQueueHandler } from '../services/queue.js';
|
|
8
|
+
/**
|
|
9
|
+
* Creates config finished handler
|
|
10
|
+
*/
|
|
11
|
+
export function createConfigFinishedHandler(addToTimeline, command, handleCommandError, handleCommandComplete, handleCommandAborted, setService) {
|
|
12
|
+
return (config) => {
|
|
13
|
+
const anthropicConfig = config;
|
|
14
|
+
saveAnthropicConfig(anthropicConfig);
|
|
15
|
+
const newService = createAnthropicService(anthropicConfig);
|
|
16
|
+
setService(newService);
|
|
17
|
+
return withQueueHandler(ComponentName.Config, (first, rest) => {
|
|
18
|
+
addToTimeline(markAsDone(first), createFeedback(FeedbackType.Succeeded, FeedbackMessages.ConfigurationComplete));
|
|
19
|
+
// Add command to queue if we have one
|
|
20
|
+
if (command) {
|
|
21
|
+
return [
|
|
22
|
+
...rest,
|
|
23
|
+
createCommandDefinition(command, newService, handleCommandError, handleCommandComplete, handleCommandAborted),
|
|
24
|
+
];
|
|
25
|
+
}
|
|
26
|
+
// No command - exit after showing completion message
|
|
27
|
+
exitApp(0);
|
|
28
|
+
return rest;
|
|
29
|
+
}, false, 0);
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Creates config aborted handler
|
|
34
|
+
*/
|
|
35
|
+
export function createConfigAbortedHandler(handleAborted) {
|
|
36
|
+
return () => {
|
|
37
|
+
handleAborted('Configuration');
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { ComponentName, FeedbackType, TaskType } from '../types/types.js';
|
|
2
|
+
import { createAnswerDefinition, createFeedback, createIntrospectDefinition, markAsDone, } from '../services/components.js';
|
|
3
|
+
import { getCancellationMessage } from '../services/messages.js';
|
|
4
|
+
import { exitApp } from '../services/process.js';
|
|
5
|
+
import { withQueueHandler } from '../services/queue.js';
|
|
6
|
+
/**
|
|
7
|
+
* Creates execution confirmed handler
|
|
8
|
+
*/
|
|
9
|
+
export function createExecutionConfirmedHandler(timelineRef, addToTimeline, service, handleIntrospectError, handleIntrospectComplete, handleIntrospectAborted, handleAnswerError, handleAnswerComplete, handleAnswerAborted) {
|
|
10
|
+
return () => withQueueHandler(ComponentName.Confirm, (first) => {
|
|
11
|
+
// Find the most recent Plan in timeline to get tasks
|
|
12
|
+
const currentTimeline = timelineRef.current;
|
|
13
|
+
const lastPlanIndex = [...currentTimeline]
|
|
14
|
+
.reverse()
|
|
15
|
+
.findIndex((item) => item.name === ComponentName.Plan);
|
|
16
|
+
const lastPlan = lastPlanIndex >= 0
|
|
17
|
+
? currentTimeline[currentTimeline.length - 1 - lastPlanIndex]
|
|
18
|
+
: null;
|
|
19
|
+
const tasks = lastPlan?.name === ComponentName.Plan &&
|
|
20
|
+
Array.isArray(lastPlan.props.tasks)
|
|
21
|
+
? lastPlan.props.tasks
|
|
22
|
+
: [];
|
|
23
|
+
const allIntrospect = tasks.every((task) => task.type === TaskType.Introspect);
|
|
24
|
+
const allAnswer = tasks.every((task) => task.type === TaskType.Answer);
|
|
25
|
+
if (allIntrospect && tasks.length > 0) {
|
|
26
|
+
// Execute introspection
|
|
27
|
+
addToTimeline(markAsDone(first));
|
|
28
|
+
return [
|
|
29
|
+
createIntrospectDefinition(tasks, service, handleIntrospectError, handleIntrospectComplete, handleIntrospectAborted),
|
|
30
|
+
];
|
|
31
|
+
}
|
|
32
|
+
else if (allAnswer && tasks.length > 0) {
|
|
33
|
+
// Execute answer - extract question from first task
|
|
34
|
+
const question = tasks[0].action;
|
|
35
|
+
addToTimeline(markAsDone(first));
|
|
36
|
+
return [
|
|
37
|
+
createAnswerDefinition(question, service, handleAnswerError, handleAnswerComplete, handleAnswerAborted),
|
|
38
|
+
];
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
// Regular execution - just exit for now
|
|
42
|
+
addToTimeline(markAsDone(first));
|
|
43
|
+
exitApp(0);
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Creates execution cancelled handler
|
|
50
|
+
*/
|
|
51
|
+
export function createExecutionCancelledHandler(timelineRef, addToTimeline) {
|
|
52
|
+
return () => withQueueHandler(ComponentName.Confirm, (first) => {
|
|
53
|
+
// Find the most recent Plan in timeline to check task types
|
|
54
|
+
const currentTimeline = timelineRef.current;
|
|
55
|
+
const lastPlanIndex = [...currentTimeline]
|
|
56
|
+
.reverse()
|
|
57
|
+
.findIndex((item) => item.name === ComponentName.Plan);
|
|
58
|
+
const lastPlan = lastPlanIndex >= 0
|
|
59
|
+
? currentTimeline[currentTimeline.length - 1 - lastPlanIndex]
|
|
60
|
+
: null;
|
|
61
|
+
const allIntrospect = lastPlan?.name === ComponentName.Plan &&
|
|
62
|
+
Array.isArray(lastPlan.props.tasks) &&
|
|
63
|
+
lastPlan.props.tasks.every((task) => task.type === TaskType.Introspect);
|
|
64
|
+
const operation = allIntrospect ? 'introspection' : 'execution';
|
|
65
|
+
addToTimeline(markAsDone(first), createFeedback(FeedbackType.Aborted, getCancellationMessage(operation)));
|
|
66
|
+
return undefined;
|
|
67
|
+
}, true, 0);
|
|
68
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ComponentName } from '../types/types.js';
|
|
2
|
+
import { createReportDefinition } from '../services/components.js';
|
|
3
|
+
import { createErrorHandler, withQueueHandler } from '../services/queue.js';
|
|
4
|
+
/**
|
|
5
|
+
* Creates introspect error handler
|
|
6
|
+
*/
|
|
7
|
+
export function createIntrospectErrorHandler(addToTimeline) {
|
|
8
|
+
return (error) => createErrorHandler(ComponentName.Introspect, addToTimeline)(error);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Creates introspect completion handler
|
|
12
|
+
*/
|
|
13
|
+
export function createIntrospectCompleteHandler(addToTimeline) {
|
|
14
|
+
return (message, capabilities) => withQueueHandler(ComponentName.Introspect, () => {
|
|
15
|
+
// Don't add the Introspect component to timeline (it renders null)
|
|
16
|
+
// Only add the Report component
|
|
17
|
+
addToTimeline(createReportDefinition(message, capabilities));
|
|
18
|
+
return undefined;
|
|
19
|
+
}, true, 0);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Creates introspect aborted handler
|
|
23
|
+
*/
|
|
24
|
+
export function createIntrospectAbortedHandler(handleAborted) {
|
|
25
|
+
return () => {
|
|
26
|
+
handleAborted('Introspection');
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { ComponentName, FeedbackType, TaskType } from '../types/types.js';
|
|
2
|
+
import { createConfirmDefinition, createFeedback, createPlanDefinition, markAsDone, createRefinement, } from '../services/components.js';
|
|
3
|
+
import { FeedbackMessages, getRefiningMessage } from '../services/messages.js';
|
|
4
|
+
import { exitApp } from '../services/process.js';
|
|
5
|
+
/**
|
|
6
|
+
* Creates plan aborted handler
|
|
7
|
+
*/
|
|
8
|
+
export function createPlanAbortedHandler(handleAborted) {
|
|
9
|
+
return () => {
|
|
10
|
+
handleAborted('Task selection');
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Creates plan abort handler factory
|
|
15
|
+
*/
|
|
16
|
+
export function createPlanAbortHandlerFactory(handleAborted, handlePlanAborted) {
|
|
17
|
+
return (tasks) => {
|
|
18
|
+
const allIntrospect = tasks.every((task) => task.type === TaskType.Introspect);
|
|
19
|
+
if (allIntrospect) {
|
|
20
|
+
return () => {
|
|
21
|
+
handleAborted('Introspection');
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
return handlePlanAborted;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Creates plan selection confirmed handler
|
|
29
|
+
*/
|
|
30
|
+
export function createPlanSelectionConfirmedHandler(addToTimeline, service, handleRefinementAborted, createPlanAbortHandler, handleExecutionConfirmed, handleExecutionCancelled, setQueue) {
|
|
31
|
+
return async (selectedTasks) => {
|
|
32
|
+
// Mark current plan as done and add refinement to queue
|
|
33
|
+
const refinementDef = createRefinement(getRefiningMessage(), handleRefinementAborted);
|
|
34
|
+
setQueue((currentQueue) => {
|
|
35
|
+
if (currentQueue.length === 0)
|
|
36
|
+
return currentQueue;
|
|
37
|
+
const [first] = currentQueue;
|
|
38
|
+
if (first.name === ComponentName.Plan) {
|
|
39
|
+
addToTimeline(markAsDone(first));
|
|
40
|
+
}
|
|
41
|
+
// Add refinement to queue so it becomes the active component
|
|
42
|
+
return [refinementDef];
|
|
43
|
+
});
|
|
44
|
+
// Process refined command in background
|
|
45
|
+
try {
|
|
46
|
+
const refinedCommand = selectedTasks
|
|
47
|
+
.map((task) => {
|
|
48
|
+
const action = task.action.toLowerCase().replace(/,/g, ' -');
|
|
49
|
+
const type = task.type;
|
|
50
|
+
return `${action} (type: ${type})`;
|
|
51
|
+
})
|
|
52
|
+
.join(', ');
|
|
53
|
+
const result = await service.processWithTool(refinedCommand, 'plan');
|
|
54
|
+
// Mark refinement as done and move to timeline
|
|
55
|
+
setQueue((currentQueue) => {
|
|
56
|
+
if (currentQueue.length > 0 &&
|
|
57
|
+
currentQueue[0].id === refinementDef.id) {
|
|
58
|
+
addToTimeline(markAsDone(currentQueue[0]));
|
|
59
|
+
}
|
|
60
|
+
return [];
|
|
61
|
+
});
|
|
62
|
+
// Show final execution plan with confirmation
|
|
63
|
+
const planDefinition = createPlanDefinition(result.message, result.tasks, createPlanAbortHandler(result.tasks), undefined);
|
|
64
|
+
const confirmDefinition = createConfirmDefinition(handleExecutionConfirmed, handleExecutionCancelled);
|
|
65
|
+
addToTimeline(planDefinition);
|
|
66
|
+
setQueue([confirmDefinition]);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
70
|
+
// Mark refinement as done and move to timeline before showing error
|
|
71
|
+
setQueue((currentQueue) => {
|
|
72
|
+
if (currentQueue.length > 0 &&
|
|
73
|
+
currentQueue[0].id === refinementDef.id) {
|
|
74
|
+
addToTimeline(markAsDone(currentQueue[0]));
|
|
75
|
+
}
|
|
76
|
+
return [];
|
|
77
|
+
});
|
|
78
|
+
addToTimeline(createFeedback(FeedbackType.Failed, FeedbackMessages.UnexpectedError, errorMessage));
|
|
79
|
+
exitApp(1);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
@@ -40,8 +40,25 @@ export class AnthropicService {
|
|
|
40
40
|
throw new Error('Expected tool_use response from Claude API');
|
|
41
41
|
}
|
|
42
42
|
const content = response.content[0];
|
|
43
|
-
// Extract and validate
|
|
43
|
+
// Extract and validate response based on tool type
|
|
44
44
|
const input = content.input;
|
|
45
|
+
const isDebug = process.env.DEBUG === 'true';
|
|
46
|
+
// Handle answer tool response
|
|
47
|
+
if (toolName === 'answer') {
|
|
48
|
+
if (!input.question || typeof input.question !== 'string') {
|
|
49
|
+
throw new Error('Invalid tool response: missing or invalid question field');
|
|
50
|
+
}
|
|
51
|
+
if (!input.answer || typeof input.answer !== 'string') {
|
|
52
|
+
throw new Error('Invalid tool response: missing or invalid answer field');
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
message: '',
|
|
56
|
+
tasks: [],
|
|
57
|
+
answer: input.answer,
|
|
58
|
+
systemPrompt: isDebug ? systemPrompt : undefined,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
// Handle plan and introspect tool responses
|
|
45
62
|
if (!input.message || typeof input.message !== 'string') {
|
|
46
63
|
throw new Error('Invalid tool response: missing or invalid message field');
|
|
47
64
|
}
|
|
@@ -54,7 +71,6 @@ export class AnthropicService {
|
|
|
54
71
|
throw new Error(`Invalid task at index ${String(i)}: missing or invalid 'action' field`);
|
|
55
72
|
}
|
|
56
73
|
});
|
|
57
|
-
const isDebug = process.env.DEBUG === 'true';
|
|
58
74
|
return {
|
|
59
75
|
message: input.message,
|
|
60
76
|
tasks: input.tasks,
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
import { FeedbackType, TaskType } from '
|
|
1
|
+
import { FeedbackType, TaskType } from '../types/types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Semantic color palette - colors organized by their purpose/meaning.
|
|
4
4
|
* Prefer adding semantic names here rather than to DescriptiveColors.
|
|
5
5
|
*/
|
|
6
6
|
export const Colors = {
|
|
7
|
+
Text: {
|
|
8
|
+
Active: '#ffffff', // white
|
|
9
|
+
Inactive: '#d0d0d0', // ash gray
|
|
10
|
+
},
|
|
7
11
|
Action: {
|
|
8
12
|
Execute: '#5aaa8a', // green
|
|
9
13
|
Discard: '#a85c3f', // dark orange
|
|
@@ -16,7 +20,7 @@ export const Colors = {
|
|
|
16
20
|
Info: '#5c9ccc', // cyan
|
|
17
21
|
},
|
|
18
22
|
Label: {
|
|
19
|
-
Default:
|
|
23
|
+
Default: null, // replaced with active or inactive
|
|
20
24
|
Inactive: '#888888', // gray
|
|
21
25
|
Discarded: '#666666', // dark gray
|
|
22
26
|
Skipped: '#cccc5c', // yellow
|
|
@@ -35,9 +39,9 @@ export const Colors = {
|
|
|
35
39
|
},
|
|
36
40
|
};
|
|
37
41
|
/**
|
|
38
|
-
* Task-specific color mappings
|
|
42
|
+
* Task-specific color mappings (internal)
|
|
39
43
|
*/
|
|
40
|
-
|
|
44
|
+
const TaskColors = {
|
|
41
45
|
[TaskType.Config]: {
|
|
42
46
|
description: Colors.Label.Default,
|
|
43
47
|
type: Colors.Type.Config,
|
|
@@ -80,11 +84,59 @@ export const TaskColors = {
|
|
|
80
84
|
},
|
|
81
85
|
};
|
|
82
86
|
/**
|
|
83
|
-
* Feedback-specific color mappings
|
|
87
|
+
* Feedback-specific color mappings (internal)
|
|
84
88
|
*/
|
|
85
|
-
|
|
89
|
+
const FeedbackColors = {
|
|
86
90
|
[FeedbackType.Info]: Colors.Status.Info,
|
|
87
91
|
[FeedbackType.Succeeded]: Colors.Status.Success,
|
|
88
92
|
[FeedbackType.Aborted]: Colors.Status.Warning,
|
|
89
93
|
[FeedbackType.Failed]: Colors.Status.Error,
|
|
90
94
|
};
|
|
95
|
+
/**
|
|
96
|
+
* Process null color values based on current/historical state.
|
|
97
|
+
*
|
|
98
|
+
* Replaces null with:
|
|
99
|
+
* - Colors.Text.Active for current items
|
|
100
|
+
* - Colors.Text.Inactive (undefined) for historical items
|
|
101
|
+
*/
|
|
102
|
+
function processColor(color, isCurrent) {
|
|
103
|
+
return color === null
|
|
104
|
+
? isCurrent
|
|
105
|
+
? Colors.Text.Active
|
|
106
|
+
: Colors.Text.Inactive
|
|
107
|
+
: color;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Get task colors with current/historical state handling.
|
|
111
|
+
*
|
|
112
|
+
* Processes null color values (terminal default) and replaces them with:
|
|
113
|
+
* - Colors.Text.Inactive (undefined) for historical items
|
|
114
|
+
* - Colors.Text.Active for current items
|
|
115
|
+
*/
|
|
116
|
+
export function getTaskColors(type, isCurrent) {
|
|
117
|
+
const colors = TaskColors[type];
|
|
118
|
+
return {
|
|
119
|
+
description: processColor(colors.description, isCurrent),
|
|
120
|
+
type: processColor(colors.type, isCurrent),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Get feedback color with current/historical state handling.
|
|
125
|
+
*
|
|
126
|
+
* Processes null color values (terminal default) and replaces them with:
|
|
127
|
+
* - Colors.Text.Inactive (undefined) for historical items
|
|
128
|
+
* - Colors.Text.Active for current items
|
|
129
|
+
*/
|
|
130
|
+
export function getFeedbackColor(type, isCurrent) {
|
|
131
|
+
return processColor(FeedbackColors[type], isCurrent);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Get text color based on current/historical state.
|
|
135
|
+
*
|
|
136
|
+
* Returns:
|
|
137
|
+
* - Colors.Text.Active for current items
|
|
138
|
+
* - Colors.Text.Inactive for historical items
|
|
139
|
+
*/
|
|
140
|
+
export function getTextColor(isCurrent) {
|
|
141
|
+
return isCurrent ? Colors.Text.Active : Colors.Text.Inactive;
|
|
142
|
+
}
|