orquesta-cli 0.2.45 → 0.2.47
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/agents/planner/index.js +2 -1
- package/dist/cli.js +17 -16
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/core/commands/clear.d.ts +3 -0
- package/dist/core/commands/clear.js +22 -0
- package/dist/core/commands/compact.d.ts +3 -0
- package/dist/core/commands/compact.js +45 -0
- package/dist/core/commands/help.d.ts +3 -0
- package/dist/core/commands/help.js +50 -0
- package/dist/core/commands/index.d.ts +3 -0
- package/dist/core/commands/index.js +11 -0
- package/dist/core/commands/memory.d.ts +3 -0
- package/dist/core/commands/memory.js +40 -0
- package/dist/core/commands/registry.d.ts +11 -0
- package/dist/core/commands/registry.js +25 -0
- package/dist/core/commands/types.d.ts +10 -0
- package/dist/core/commands/types.js +2 -0
- package/dist/core/event-bus.d.ts +20 -0
- package/dist/core/event-bus.js +35 -0
- package/dist/core/git-context.d.ts +11 -0
- package/dist/core/git-context.js +62 -0
- package/dist/core/ignore-filter.d.ts +4 -0
- package/dist/core/ignore-filter.js +50 -0
- package/dist/core/llm/llm-client.d.ts +1 -0
- package/dist/core/llm/llm-client.js +118 -40
- package/dist/core/onboarding.d.ts +3 -0
- package/dist/core/onboarding.js +48 -0
- package/dist/core/slash-command-handler.js +8 -135
- package/dist/eval/eval-runner.d.ts +1 -0
- package/dist/eval/eval-runner.js +22 -0
- package/dist/orchestration/plan-executor.js +77 -71
- package/dist/prompts/shared/tool-usage.js +0 -1
- package/dist/prompts/system/plan-execute.js +50 -57
- package/dist/tools/llm/simple/file-tools.js +12 -1
- package/dist/tools/llm/simple/final-response-tool.js +7 -11
- package/dist/tools/registry.js +63 -10
- package/dist/ui/components/PlanExecuteApp.d.ts +1 -0
- package/dist/ui/components/PlanExecuteApp.js +59 -22
- package/package.json +8 -4
|
@@ -4,12 +4,14 @@ import { CompactManager, contextTracker, buildCompactedMessages, } from '../core
|
|
|
4
4
|
import { configManager } from '../core/config/config-manager.js';
|
|
5
5
|
import { setTodoWriteCallback, clearTodoCallbacks, } from '../tools/llm/simple/todo-tools.js';
|
|
6
6
|
import { setGetTodosCallback, setFinalResponseCallback, setMarkTodosCompletedCallback, clearFinalResponseCallbacks, } from '../tools/llm/simple/final-response-tool.js';
|
|
7
|
+
import { eventBus, Events } from '../core/event-bus.js';
|
|
7
8
|
import { setDocsSearchLLMClientGetter, clearDocsSearchLLMClientGetter, } from '../tools/llm/simple/docs-search-agent-tool.js';
|
|
8
9
|
import { emitPlanCreated, emitTodoStart, emitTodoComplete, emitTodoFail, emitCompact, emitAssistantResponse, } from '../tools/llm/simple/file-tools.js';
|
|
9
10
|
import { toolRegistry } from '../tools/registry.js';
|
|
10
11
|
import { PLAN_EXECUTE_SYSTEM_PROMPT as PLAN_PROMPT } from '../prompts/system/plan-execute.js';
|
|
11
12
|
import { getProjectContext } from '../core/project-context.js';
|
|
12
13
|
import { getMemoryPrompt } from '../core/memory.js';
|
|
14
|
+
import { getGitContextPrompt } from '../core/git-context.js';
|
|
13
15
|
import { GIT_COMMIT_RULES } from '../prompts/shared/git-rules.js';
|
|
14
16
|
import { logger } from '../utils/logger.js';
|
|
15
17
|
import { getStreamLogger } from '../utils/json-stream-logger.js';
|
|
@@ -34,7 +36,7 @@ function buildSystemPrompt() {
|
|
|
34
36
|
const projectContext = getProjectContext();
|
|
35
37
|
const base = isGitRepo ? `${PLAN_PROMPT}\n\n${GIT_COMMIT_RULES}` : PLAN_PROMPT;
|
|
36
38
|
const appended = appendedSystemPrompt ? `\n\n${appendedSystemPrompt}` : '';
|
|
37
|
-
return base + buildEnvironmentContext() + projectContext + getMemoryPrompt() + appended;
|
|
39
|
+
return base + buildEnvironmentContext() + projectContext + getMemoryPrompt() + getGitContextPrompt() + appended;
|
|
38
40
|
}
|
|
39
41
|
export class PlanExecutor {
|
|
40
42
|
currentLLMClient = null;
|
|
@@ -80,74 +82,82 @@ export class PlanExecutor {
|
|
|
80
82
|
throw new Error('INTERRUPTED');
|
|
81
83
|
}
|
|
82
84
|
let currentMessages = messages;
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
planningLLM.setAskUserCallback(callbacks.askUser);
|
|
85
|
+
const isSimpleTask = userMessage.length < 500 &&
|
|
86
|
+
!/\b(and then|after that|first.*then|step \d|multiple|several|refactor.*entire|migrate|rewrite.*all)\b/i.test(userMessage);
|
|
87
|
+
if (isSimpleTask) {
|
|
88
|
+
logger.flow('Simple task detected — skipping planner, executor will handle directly');
|
|
89
|
+
streamLogger?.logPlanningEnd(0, [], false, 0);
|
|
89
90
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
91
|
+
else {
|
|
92
|
+
callbacks.setCurrentActivity('Thinking');
|
|
93
|
+
const plannerModel = configManager.getRoleModel('planner');
|
|
94
|
+
const planningLLM = new PlanningLLM(llmClient, plannerModel ?? undefined);
|
|
95
|
+
const plannerStartedAt = Date.now();
|
|
96
|
+
if (callbacks.askUser) {
|
|
97
|
+
planningLLM.setAskUserCallback(callbacks.askUser);
|
|
98
|
+
}
|
|
99
|
+
const planResult = await planningLLM.generateTODOListWithDocsDecision(userMessage, currentMessages);
|
|
100
|
+
auditLog.emit(auditSid, 'planner.complete', {
|
|
101
|
+
runId,
|
|
102
|
+
model: plannerModel,
|
|
103
|
+
durationMs: Date.now() - plannerStartedAt,
|
|
104
|
+
todoCount: planResult.todos.length,
|
|
105
|
+
directResponse: !!planResult.directResponse,
|
|
103
106
|
});
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
107
|
+
if (planResult.clarificationMessages?.length) {
|
|
108
|
+
currentMessages = [...currentMessages, ...planResult.clarificationMessages];
|
|
109
|
+
callbacks.setMessages([...currentMessages]);
|
|
110
|
+
logger.flow('Added planning clarification messages to history', {
|
|
111
|
+
count: planResult.clarificationMessages.length,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
if (planResult.directResponse) {
|
|
115
|
+
logger.flow('Direct response - no execution needed');
|
|
116
|
+
streamLogger?.logPlanningEnd(0, [], true, Date.now() - planningStartTime);
|
|
117
|
+
const lastMsg = currentMessages[currentMessages.length - 1];
|
|
118
|
+
const needsUserMessage = !(lastMsg?.role === 'user' && lastMsg?.content === userMessage);
|
|
119
|
+
const updatedMessages = needsUserMessage
|
|
120
|
+
? [
|
|
121
|
+
...currentMessages,
|
|
122
|
+
{ role: 'user', content: userMessage },
|
|
123
|
+
{ role: 'assistant', content: planResult.directResponse }
|
|
124
|
+
]
|
|
125
|
+
: [
|
|
126
|
+
...currentMessages,
|
|
127
|
+
{ role: 'assistant', content: planResult.directResponse }
|
|
128
|
+
];
|
|
129
|
+
emitAssistantResponse(planResult.directResponse);
|
|
130
|
+
callbacks.setMessages([...updatedMessages]);
|
|
131
|
+
sessionManager.autoSaveCurrentSession(updatedMessages);
|
|
132
|
+
callbacks.setExecutionPhase('idle');
|
|
133
|
+
logger.exit('PlanExecutor.executePlanMode', { directResponse: true });
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
currentTodos = planResult.todos;
|
|
137
|
+
streamLogger?.logPlanningEnd(currentTodos.length, currentTodos.map(t => ({ id: t.id, title: t.title, status: t.status })), false, Date.now() - planningStartTime);
|
|
138
|
+
logger.vars({ name: 'todoCount', value: currentTodos.length }, { name: 'docsSearchNeeded', value: planResult.docsSearchNeeded });
|
|
139
|
+
callbacks.setTodos(currentTodos);
|
|
140
|
+
emitPlanCreated(currentTodos.map(t => t.title));
|
|
141
|
+
const planMessage = planResult.docsSearchNeeded
|
|
142
|
+
? `📋 Created ${currentTodos.length} tasks (including docs search). Starting execution...`
|
|
143
|
+
: `📋 Created ${currentTodos.length} tasks. Starting execution...`;
|
|
144
|
+
const lastMsgForPlan = currentMessages[currentMessages.length - 1];
|
|
145
|
+
const needsUserMessageForPlan = !(lastMsgForPlan?.role === 'user' && lastMsgForPlan?.content === userMessage);
|
|
146
|
+
currentMessages = needsUserMessageForPlan
|
|
111
147
|
? [
|
|
112
148
|
...currentMessages,
|
|
113
149
|
{ role: 'user', content: userMessage },
|
|
114
|
-
{ role: 'assistant', content:
|
|
150
|
+
{ role: 'assistant', content: planMessage }
|
|
115
151
|
]
|
|
116
152
|
: [
|
|
117
153
|
...currentMessages,
|
|
118
|
-
{ role: 'assistant', content:
|
|
154
|
+
{ role: 'assistant', content: planMessage }
|
|
119
155
|
];
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
logger.exit('PlanExecutor.executePlanMode', { directResponse: true });
|
|
125
|
-
return;
|
|
156
|
+
callbacks.setMessages(currentMessages);
|
|
157
|
+
this.setupTodoCallbacks(currentTodos, callbacks, (updated) => {
|
|
158
|
+
currentTodos = updated;
|
|
159
|
+
});
|
|
126
160
|
}
|
|
127
|
-
currentTodos = planResult.todos;
|
|
128
|
-
streamLogger?.logPlanningEnd(currentTodos.length, currentTodos.map(t => ({ id: t.id, title: t.title, status: t.status })), false, Date.now() - planningStartTime);
|
|
129
|
-
logger.vars({ name: 'todoCount', value: currentTodos.length }, { name: 'docsSearchNeeded', value: planResult.docsSearchNeeded });
|
|
130
|
-
callbacks.setTodos(currentTodos);
|
|
131
|
-
emitPlanCreated(currentTodos.map(t => t.title));
|
|
132
|
-
const planMessage = planResult.docsSearchNeeded
|
|
133
|
-
? `📋 Created ${currentTodos.length} tasks (including docs search). Starting execution...`
|
|
134
|
-
: `📋 Created ${currentTodos.length} tasks. Starting execution...`;
|
|
135
|
-
const lastMsgForPlan = currentMessages[currentMessages.length - 1];
|
|
136
|
-
const needsUserMessageForPlan = !(lastMsgForPlan?.role === 'user' && lastMsgForPlan?.content === userMessage);
|
|
137
|
-
currentMessages = needsUserMessageForPlan
|
|
138
|
-
? [
|
|
139
|
-
...currentMessages,
|
|
140
|
-
{ role: 'user', content: userMessage },
|
|
141
|
-
{ role: 'assistant', content: planMessage }
|
|
142
|
-
]
|
|
143
|
-
: [
|
|
144
|
-
...currentMessages,
|
|
145
|
-
{ role: 'assistant', content: planMessage }
|
|
146
|
-
];
|
|
147
|
-
callbacks.setMessages(currentMessages);
|
|
148
|
-
this.setupTodoCallbacks(currentTodos, callbacks, (updated) => {
|
|
149
|
-
currentTodos = updated;
|
|
150
|
-
});
|
|
151
161
|
callbacks.setExecutionPhase('executing');
|
|
152
162
|
const tools = toolRegistry.getLLMToolDefinitions();
|
|
153
163
|
const hasSystemMessage = currentMessages.some(m => m.role === 'system');
|
|
@@ -198,12 +208,9 @@ export class PlanExecutor {
|
|
|
198
208
|
}
|
|
199
209
|
else {
|
|
200
210
|
const todoContext = buildTodoContext(currentTodos);
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
? { ...m, content: m.content + todoContext }
|
|
205
|
-
: m)
|
|
206
|
-
: [...currentMessages, { role: 'user', content: `Execute the TODO list.${todoContext}` }];
|
|
211
|
+
const messagesForLLM = todoContext
|
|
212
|
+
? [...currentMessages, { role: 'user', content: `[Current task status]${todoContext}` }]
|
|
213
|
+
: currentMessages;
|
|
207
214
|
const executorModel = configManager.getRoleModel('executor');
|
|
208
215
|
const result = await llmClient.chatCompletionWithTools(messagesForLLM, tools, {
|
|
209
216
|
getPendingMessage: callbacks.getPendingMessage,
|
|
@@ -314,12 +321,9 @@ export class PlanExecutor {
|
|
|
314
321
|
const activeTodo = findActiveTodo(currentTodos);
|
|
315
322
|
callbacks.setCurrentActivity(activeTodo?.title || 'Working on tasks');
|
|
316
323
|
const todoContext = buildTodoContext(currentTodos);
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
? { ...m, content: m.content + todoContext }
|
|
321
|
-
: m)
|
|
322
|
-
: [...currentMessages, { role: 'user', content: `Resume the TODO list.${todoContext}` }];
|
|
324
|
+
const messagesForLLM = todoContext
|
|
325
|
+
? [...currentMessages, { role: 'user', content: `[Current task status]${todoContext}` }]
|
|
326
|
+
: currentMessages;
|
|
323
327
|
const executorModel = configManager.getRoleModel('executor');
|
|
324
328
|
const result = await llmClient.chatCompletionWithTools(messagesForLLM, tools, {
|
|
325
329
|
getPendingMessage: callbacks.getPendingMessage,
|
|
@@ -460,6 +464,8 @@ export class PlanExecutor {
|
|
|
460
464
|
setFinalResponseCallback((message) => {
|
|
461
465
|
emitAssistantResponse(message);
|
|
462
466
|
});
|
|
467
|
+
void eventBus.on(Events.FINAL_RESPONSE, (_message) => {
|
|
468
|
+
});
|
|
463
469
|
setMarkTodosCompletedCallback(() => {
|
|
464
470
|
const completed = todosRef.map(t => t.status === 'completed' || t.status === 'failed'
|
|
465
471
|
? t
|
|
@@ -22,7 +22,6 @@ export const AVAILABLE_TOOLS_WITH_TODO = `
|
|
|
22
22
|
- **tell_to_user**: Send status updates to the user
|
|
23
23
|
- **ask_to_user**: Ask user a question with multiple choice options
|
|
24
24
|
- **write_todos**: Update entire TODO list (replaces current list)
|
|
25
|
-
- **call_docs_search_agent**: Search local documentation (~/.local-cli/docs)
|
|
26
25
|
`.trim();
|
|
27
26
|
export const TOOL_REASON_GUIDE = `
|
|
28
27
|
## CRITICAL - Tool "reason" Parameter
|
|
@@ -1,83 +1,76 @@
|
|
|
1
1
|
import { LANGUAGE_PRIORITY_RULE } from '../shared/language-rules.js';
|
|
2
|
-
import { AVAILABLE_TOOLS_WITH_TODO, TOOL_REASON_GUIDE } from '../shared/tool-usage.js';
|
|
3
2
|
import { CODEBASE_FIRST_RULE } from '../shared/codebase-rules.js';
|
|
4
|
-
export const PLAN_EXECUTE_SYSTEM_PROMPT = `You are an AI assistant
|
|
3
|
+
export const PLAN_EXECUTE_SYSTEM_PROMPT = `You are Orquesta, an expert AI coding assistant working in the user's terminal. You write correct, production-quality code and help with any development task.
|
|
5
4
|
|
|
6
5
|
${LANGUAGE_PRIORITY_RULE}
|
|
7
6
|
|
|
8
|
-
##
|
|
7
|
+
## How You Work
|
|
9
8
|
|
|
10
|
-
1.
|
|
11
|
-
2.
|
|
12
|
-
3. **
|
|
9
|
+
1. **Understand first** — Read relevant code before modifying it. Never guess file contents.
|
|
10
|
+
2. **Act, don't describe** — Use tools to do the work. Don't say "I would do X", just do X.
|
|
11
|
+
3. **Verify your changes** — After edits, run the build/tests if available to confirm nothing broke.
|
|
12
|
+
4. **Be concise** — Short answers for simple questions. Thorough work for complex tasks.
|
|
13
|
+
5. **Match the user's intent** — Do what was asked, no more. Don't add unrequested features or refactors.
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
- When starting a task → mark it "in_progress" IMMEDIATELY
|
|
16
|
-
- When finishing a task → mark it "completed" IMMEDIATELY
|
|
17
|
-
- The user sees the TODO list in real-time - mismatched status is confusing
|
|
18
|
-
- Call \`write_todos\` FREQUENTLY, not just at the end
|
|
15
|
+
## Decision Framework
|
|
19
16
|
|
|
20
|
-
|
|
17
|
+
- **Simple questions** (what is X, explain Y): Respond directly with knowledge.
|
|
18
|
+
- **Code tasks** (fix, add, edit, refactor): Read → Edit → Verify. Use tools.
|
|
19
|
+
- **Investigation** (why is this failing, what does X do): Read code, search, then explain.
|
|
20
|
+
- **Ambiguous requests**: Infer the most useful action and proceed. Only ask if truly blocked.
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
## Tool Usage
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
Use tools for all file operations, commands, and code changes. Your available tools:
|
|
25
|
+
- **read_file**: Always read before editing
|
|
26
|
+
- **edit_file**: Modify existing files (match exact content for old_string)
|
|
27
|
+
- **create_file**: Create new files
|
|
28
|
+
- **list_files** / **find_files** / **search_content**: Navigate the codebase
|
|
29
|
+
- **bash**: Run commands (build, test, git, etc.)
|
|
30
|
+
- **tell_to_user**: Show progress updates
|
|
31
|
+
- **write_todos**: Track task progress (for multi-step work)
|
|
25
32
|
|
|
26
|
-
|
|
27
|
-
2. **Use tools** - Perform actual work, don't just describe
|
|
28
|
-
3. **Retry on error** - Up to 3 attempts before marking "failed"
|
|
29
|
-
4. **Stay focused** - Only work on TODOs, no unrelated features
|
|
33
|
+
Every tool has a "reason" parameter shown to the user. Write it naturally in the user's language.
|
|
30
34
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
## CRITICAL: Tool Error Handling
|
|
34
|
-
|
|
35
|
-
**If a tool returns an error, you MUST retry the same tool with corrected parameters.**
|
|
35
|
+
## Code Quality Rules
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
- Write minimal, correct code that solves the problem
|
|
38
|
+
- Follow existing project conventions (style, naming, patterns)
|
|
39
|
+
- Use secure coding practices by default
|
|
40
|
+
- Don't introduce new dependencies unless necessary
|
|
41
|
+
- Include error handling where appropriate
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
- Skip the failed tool and move to next task
|
|
44
|
-
- Say "I'll try a different approach" without actually retrying
|
|
45
|
-
- Mark TODO as complete if the tool failed
|
|
43
|
+
## Error Handling
|
|
46
44
|
|
|
47
|
-
|
|
48
|
-
1.
|
|
49
|
-
2.
|
|
50
|
-
3.
|
|
51
|
-
4. Only
|
|
45
|
+
If a tool fails:
|
|
46
|
+
1. Read the error carefully
|
|
47
|
+
2. Investigate (read_file to check actual content)
|
|
48
|
+
3. Retry with corrected parameters
|
|
49
|
+
4. Only give up after 3 failed attempts
|
|
52
50
|
|
|
53
|
-
|
|
51
|
+
Common edit_file failures: wrong old_string → re-read file, copy exact text, retry.
|
|
54
52
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
- Responding early = execution ends prematurely
|
|
58
|
-
- Use \`tell_to_user\` to communicate progress during execution
|
|
59
|
-
- \`write_todos\` only updates internal state
|
|
60
|
-
|
|
61
|
-
**Before final response, verify:**
|
|
62
|
-
- All TODOs completed?
|
|
63
|
-
- All tool calls successful?
|
|
64
|
-
- User's request fulfilled?
|
|
53
|
+
${CODEBASE_FIRST_RULE}
|
|
65
54
|
|
|
66
|
-
##
|
|
55
|
+
## Response Style
|
|
67
56
|
|
|
68
|
-
|
|
69
|
-
-
|
|
70
|
-
-
|
|
57
|
+
- Direct and concise. No filler phrases.
|
|
58
|
+
- Code in markdown blocks with language tags.
|
|
59
|
+
- When summarizing completed work: state what was done in 1-3 sentences.
|
|
60
|
+
- Match the user's language (if they write in Spanish, respond in Spanish).
|
|
71
61
|
|
|
72
|
-
|
|
62
|
+
## TODO Workflow (for multi-step tasks)
|
|
73
63
|
|
|
74
|
-
|
|
75
|
-
-
|
|
76
|
-
-
|
|
64
|
+
When working on a plan with TODOs:
|
|
65
|
+
- Update status via write_todos as you progress
|
|
66
|
+
- Mark "in_progress" when starting, "completed" when done
|
|
67
|
+
- Stay focused on the current task
|
|
77
68
|
|
|
78
|
-
##
|
|
69
|
+
## IMPORTANT
|
|
79
70
|
|
|
80
|
-
|
|
71
|
+
- You can respond directly without using any tool — do so for simple questions or when you're done working.
|
|
72
|
+
- After completing all requested work, give a brief summary of what was done.
|
|
73
|
+
- Never fabricate file contents, paths, or command outputs. If unsure, investigate first.
|
|
81
74
|
`;
|
|
82
75
|
export default PLAN_EXECUTE_SYSTEM_PROMPT;
|
|
83
76
|
//# sourceMappingURL=plan-execute.js.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as fs from 'fs/promises';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { logger } from '../../../utils/logger.js';
|
|
4
|
+
import { shouldIgnore } from '../../../core/ignore-filter.js';
|
|
4
5
|
const EXCLUDED_DIRS = new Set([
|
|
5
6
|
'node_modules',
|
|
6
7
|
'.git',
|
|
@@ -451,6 +452,9 @@ async function getFilesRecursively(dirPath, baseDir = dirPath, depth = 0, fileCo
|
|
|
451
452
|
}
|
|
452
453
|
const fullPath = path.join(dirPath, entry.name);
|
|
453
454
|
const relativePath = path.relative(baseDir, fullPath);
|
|
455
|
+
if (shouldIgnore(fullPath)) {
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
454
458
|
if (entry.isDirectory()) {
|
|
455
459
|
if (EXCLUDED_DIRS.has(entry.name)) {
|
|
456
460
|
continue;
|
|
@@ -491,7 +495,9 @@ async function _executeListFilesInternal(args) {
|
|
|
491
495
|
}
|
|
492
496
|
else {
|
|
493
497
|
const entries = await fs.readdir(resolvedPath, { withFileTypes: true });
|
|
494
|
-
const files = entries
|
|
498
|
+
const files = entries
|
|
499
|
+
.filter(entry => !shouldIgnore(path.join(resolvedPath, entry.name)))
|
|
500
|
+
.map((entry) => ({
|
|
495
501
|
name: entry.name,
|
|
496
502
|
type: entry.isDirectory() ? 'directory' : 'file',
|
|
497
503
|
path: path.join(directoryPath, entry.name),
|
|
@@ -580,6 +586,9 @@ async function findFilesRecursively(dirPath, regex, baseDir, depth = 0, fileCoun
|
|
|
580
586
|
continue;
|
|
581
587
|
}
|
|
582
588
|
const fullPath = path.join(dirPath, entry.name);
|
|
589
|
+
if (shouldIgnore(fullPath)) {
|
|
590
|
+
continue;
|
|
591
|
+
}
|
|
583
592
|
if (entry.isDirectory()) {
|
|
584
593
|
if (EXCLUDED_DIRS.has(entry.name)) {
|
|
585
594
|
continue;
|
|
@@ -700,6 +709,8 @@ async function searchContentRecursively(dirPath, searchRegex, baseDir, fileRegex
|
|
|
700
709
|
if (entry.name.startsWith('.'))
|
|
701
710
|
continue;
|
|
702
711
|
const fullPath = path.join(dirPath, entry.name);
|
|
712
|
+
if (shouldIgnore(fullPath))
|
|
713
|
+
continue;
|
|
703
714
|
if (entry.isDirectory()) {
|
|
704
715
|
if (EXCLUDED_DIRS.has(entry.name))
|
|
705
716
|
continue;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { logger } from '../../../utils/logger.js';
|
|
2
|
+
import { eventBus, Events } from '../../../core/event-bus.js';
|
|
2
3
|
let getTodosCallback = null;
|
|
3
4
|
let finalResponseCallback = null;
|
|
4
5
|
let markTodosCompletedCallback = null;
|
|
@@ -28,23 +29,16 @@ const FINAL_RESPONSE_DEFINITION = {
|
|
|
28
29
|
type: 'function',
|
|
29
30
|
function: {
|
|
30
31
|
name: 'final_response',
|
|
31
|
-
description: `
|
|
32
|
+
description: `Deliver a final summary to the user after completing work. Optional — you can also respond directly without this tool.
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
- If any TODO is not completed, this tool will return an error
|
|
36
|
-
- After all tasks are done, use this tool to summarize what was accomplished
|
|
37
|
-
|
|
38
|
-
Example:
|
|
39
|
-
{
|
|
40
|
-
"message": "I've completed all the requested tasks:\\n\\n1. Fixed the bug in the login form\\n2. Added input validation\\n3. Updated the tests\\n\\nAll changes have been committed."
|
|
41
|
-
}`,
|
|
34
|
+
Use this when you want to explicitly signal task completion with a summary.
|
|
35
|
+
Any incomplete TODOs will be auto-marked as done.`,
|
|
42
36
|
parameters: {
|
|
43
37
|
type: 'object',
|
|
44
38
|
properties: {
|
|
45
39
|
message: {
|
|
46
40
|
type: 'string',
|
|
47
|
-
description: 'Your final response message to the user.
|
|
41
|
+
description: 'Your final response message to the user.',
|
|
48
42
|
},
|
|
49
43
|
},
|
|
50
44
|
required: ['message'],
|
|
@@ -138,6 +132,7 @@ async function executeFinalResponse(args) {
|
|
|
138
132
|
if (finalResponseCallback) {
|
|
139
133
|
finalResponseCallback(message);
|
|
140
134
|
}
|
|
135
|
+
eventBus.emit(Events.FINAL_RESPONSE, message);
|
|
141
136
|
return {
|
|
142
137
|
success: true,
|
|
143
138
|
result: message,
|
|
@@ -163,6 +158,7 @@ async function executeFinalResponse(args) {
|
|
|
163
158
|
if (finalResponseCallback) {
|
|
164
159
|
finalResponseCallback(message);
|
|
165
160
|
}
|
|
161
|
+
eventBus.emit(Events.FINAL_RESPONSE, message);
|
|
166
162
|
return {
|
|
167
163
|
success: true,
|
|
168
164
|
result: message,
|
package/dist/tools/registry.js
CHANGED
|
@@ -9,10 +9,33 @@ import { PLANNING_TOOLS } from './llm/simple/planning-tools.js';
|
|
|
9
9
|
import { FinalResponseTool } from './llm/simple/final-response-tool.js';
|
|
10
10
|
import { RemotePhoneTool } from './llm/simple/remote-phone-tool.js';
|
|
11
11
|
import { getShellTools } from './llm/simple/index.js';
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
let _browserTools = null;
|
|
13
|
+
let _startBrowserServer = null;
|
|
14
|
+
let _shutdownBrowserServer = null;
|
|
15
|
+
async function getBrowserModule() {
|
|
16
|
+
if (!_browserTools) {
|
|
17
|
+
const mod = await import('./browser/index.js');
|
|
18
|
+
_browserTools = mod.BROWSER_TOOLS;
|
|
19
|
+
_startBrowserServer = mod.startBrowserServer;
|
|
20
|
+
_shutdownBrowserServer = mod.shutdownBrowserServer;
|
|
21
|
+
}
|
|
22
|
+
return { BROWSER_TOOLS: _browserTools, startBrowserServer: _startBrowserServer, shutdownBrowserServer: _shutdownBrowserServer };
|
|
23
|
+
}
|
|
24
|
+
let _wordTools = null;
|
|
25
|
+
let _excelTools = null;
|
|
26
|
+
let _powerpointTools = null;
|
|
27
|
+
async function getOfficeModule() {
|
|
28
|
+
if (!_wordTools) {
|
|
29
|
+
const mod = await import('./office/index.js');
|
|
30
|
+
_wordTools = mod.WORD_TOOLS;
|
|
31
|
+
_excelTools = mod.EXCEL_TOOLS;
|
|
32
|
+
_powerpointTools = mod.POWERPOINT_TOOLS;
|
|
33
|
+
}
|
|
34
|
+
return { WORD_TOOLS: _wordTools, EXCEL_TOOLS: _excelTools, POWERPOINT_TOOLS: _powerpointTools };
|
|
35
|
+
}
|
|
14
36
|
async function validateBrowserTools() {
|
|
15
37
|
try {
|
|
38
|
+
const { startBrowserServer } = await getBrowserModule();
|
|
16
39
|
await startBrowserServer();
|
|
17
40
|
return { success: true };
|
|
18
41
|
}
|
|
@@ -29,10 +52,19 @@ function getOptionalToolGroupsConfig() {
|
|
|
29
52
|
id: 'browser',
|
|
30
53
|
name: 'Browser Automation',
|
|
31
54
|
description: 'Control Chrome/Edge browser for web testing (navigate, click, screenshot, etc.)',
|
|
32
|
-
tools:
|
|
55
|
+
tools: [],
|
|
33
56
|
enabled: false,
|
|
34
|
-
onEnable:
|
|
35
|
-
|
|
57
|
+
onEnable: async () => {
|
|
58
|
+
const { BROWSER_TOOLS } = await getBrowserModule();
|
|
59
|
+
const group = OPTIONAL_TOOL_GROUPS.find(g => g.id === 'browser');
|
|
60
|
+
if (group)
|
|
61
|
+
group.tools = BROWSER_TOOLS;
|
|
62
|
+
return validateBrowserTools();
|
|
63
|
+
},
|
|
64
|
+
onDisable: async () => {
|
|
65
|
+
const { shutdownBrowserServer } = await getBrowserModule();
|
|
66
|
+
await shutdownBrowserServer();
|
|
67
|
+
},
|
|
36
68
|
},
|
|
37
69
|
];
|
|
38
70
|
if (hasWindowsAccess()) {
|
|
@@ -40,20 +72,41 @@ function getOptionalToolGroupsConfig() {
|
|
|
40
72
|
id: 'word',
|
|
41
73
|
name: 'Microsoft Word',
|
|
42
74
|
description: 'Control Word for document editing (write, read, save, export PDF, header/footer)',
|
|
43
|
-
tools:
|
|
75
|
+
tools: [],
|
|
44
76
|
enabled: false,
|
|
77
|
+
onEnable: async () => {
|
|
78
|
+
const { WORD_TOOLS } = await getOfficeModule();
|
|
79
|
+
const group = OPTIONAL_TOOL_GROUPS.find(g => g.id === 'word');
|
|
80
|
+
if (group)
|
|
81
|
+
group.tools = WORD_TOOLS;
|
|
82
|
+
return { success: true };
|
|
83
|
+
},
|
|
45
84
|
}, {
|
|
46
85
|
id: 'excel',
|
|
47
86
|
name: 'Microsoft Excel',
|
|
48
87
|
description: 'Control Excel for spreadsheet editing (cells, ranges, formulas, charts)',
|
|
49
|
-
tools:
|
|
88
|
+
tools: [],
|
|
50
89
|
enabled: false,
|
|
90
|
+
onEnable: async () => {
|
|
91
|
+
const { EXCEL_TOOLS } = await getOfficeModule();
|
|
92
|
+
const group = OPTIONAL_TOOL_GROUPS.find(g => g.id === 'excel');
|
|
93
|
+
if (group)
|
|
94
|
+
group.tools = EXCEL_TOOLS;
|
|
95
|
+
return { success: true };
|
|
96
|
+
},
|
|
51
97
|
}, {
|
|
52
98
|
id: 'powerpoint',
|
|
53
99
|
name: 'Microsoft PowerPoint',
|
|
54
100
|
description: 'Control PowerPoint for presentations (slides, shapes, transitions, PDF export)',
|
|
55
|
-
tools:
|
|
101
|
+
tools: [],
|
|
56
102
|
enabled: false,
|
|
103
|
+
onEnable: async () => {
|
|
104
|
+
const { POWERPOINT_TOOLS } = await getOfficeModule();
|
|
105
|
+
const group = OPTIONAL_TOOL_GROUPS.find(g => g.id === 'powerpoint');
|
|
106
|
+
if (group)
|
|
107
|
+
group.tools = POWERPOINT_TOOLS;
|
|
108
|
+
return { success: true };
|
|
109
|
+
},
|
|
57
110
|
});
|
|
58
111
|
}
|
|
59
112
|
return groups;
|
|
@@ -138,9 +191,9 @@ class ToolRegistry {
|
|
|
138
191
|
logger.error('Tool group not found', { groupId });
|
|
139
192
|
return { success: false, error: `Tool group '${groupId}' not found` };
|
|
140
193
|
}
|
|
141
|
-
if (
|
|
194
|
+
if (group.onEnable) {
|
|
142
195
|
const result = await group.onEnable();
|
|
143
|
-
if (!result.success) {
|
|
196
|
+
if (!skipValidation && !result.success) {
|
|
144
197
|
return result;
|
|
145
198
|
}
|
|
146
199
|
}
|