orquesta-cli 0.2.48 → 0.2.50
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/core/embeddings-context.d.ts +3 -0
- package/dist/core/embeddings-context.js +109 -0
- package/dist/core/llm/llm-client.js +10 -0
- package/dist/core/session/session-manager.js +2 -0
- package/dist/core/sub-agent.d.ts +14 -0
- package/dist/core/sub-agent.js +27 -0
- package/dist/core/usage-tracker.js +2 -2
- package/dist/orchestration/plan-executor.js +7 -4
- package/dist/orchestration/utils.js +1 -1
- package/dist/orquesta/session-sync.d.ts +3 -0
- package/dist/orquesta/session-sync.js +31 -0
- package/dist/tools/llm/simple/sub-agent-tool.d.ts +3 -0
- package/dist/tools/llm/simple/sub-agent-tool.js +37 -0
- package/dist/tools/registry.js +2 -0
- package/dist/ui/components/ActivityIndicator.js +10 -10
- package/dist/ui/components/PlanExecuteApp.js +26 -10
- package/dist/ui/components/StatusBar.js +1 -1
- package/dist/ui/components/ThinkingIndicator.js +7 -7
- package/dist/ui/components/views/ChatView.js +16 -16
- package/package.json +1 -1
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { shouldIgnore } from './ignore-filter.js';
|
|
4
|
+
const SOURCE_EXTENSIONS = new Set([
|
|
5
|
+
'.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs',
|
|
6
|
+
'.py', '.rs', '.go', '.java', '.rb', '.vue', '.svelte',
|
|
7
|
+
]);
|
|
8
|
+
const SYMBOL_RE = /(?:function|class|interface|type|enum|const|let|var|export\s+(?:default\s+)?(?:function|class|const|let|async\s+function))\s+(\w+)/g;
|
|
9
|
+
let indexCache = null;
|
|
10
|
+
function scanDir(dir, entries, depth = 0) {
|
|
11
|
+
if (depth > 6)
|
|
12
|
+
return;
|
|
13
|
+
let items;
|
|
14
|
+
try {
|
|
15
|
+
items = fs.readdirSync(dir);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
for (const item of items) {
|
|
21
|
+
const full = path.join(dir, item);
|
|
22
|
+
const rel = path.relative(process.cwd(), full);
|
|
23
|
+
if (shouldIgnore(rel))
|
|
24
|
+
continue;
|
|
25
|
+
let stat;
|
|
26
|
+
try {
|
|
27
|
+
stat = fs.statSync(full);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (stat.isDirectory()) {
|
|
33
|
+
scanDir(full, entries, depth + 1);
|
|
34
|
+
}
|
|
35
|
+
else if (SOURCE_EXTENSIONS.has(path.extname(item))) {
|
|
36
|
+
try {
|
|
37
|
+
const content = fs.readFileSync(full, 'utf-8');
|
|
38
|
+
const lines = content.split('\n');
|
|
39
|
+
const symbols = [];
|
|
40
|
+
let m;
|
|
41
|
+
SYMBOL_RE.lastIndex = 0;
|
|
42
|
+
while ((m = SYMBOL_RE.exec(content)) !== null) {
|
|
43
|
+
if (m[1])
|
|
44
|
+
symbols.push(m[1]);
|
|
45
|
+
}
|
|
46
|
+
entries.set(rel, {
|
|
47
|
+
path: rel,
|
|
48
|
+
symbols,
|
|
49
|
+
preview: lines.slice(0, 5).join('\n'),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
catch { }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function getIndex() {
|
|
57
|
+
if (!indexCache) {
|
|
58
|
+
indexCache = new Map();
|
|
59
|
+
scanDir(process.cwd(), indexCache);
|
|
60
|
+
}
|
|
61
|
+
return indexCache;
|
|
62
|
+
}
|
|
63
|
+
export function getRelevantContext(userMessage) {
|
|
64
|
+
const index = getIndex();
|
|
65
|
+
if (index.size === 0)
|
|
66
|
+
return '';
|
|
67
|
+
const words = userMessage
|
|
68
|
+
.toLowerCase()
|
|
69
|
+
.split(/[\s\W]+/)
|
|
70
|
+
.filter(w => w.length > 2);
|
|
71
|
+
if (words.length === 0)
|
|
72
|
+
return '';
|
|
73
|
+
const scored = [];
|
|
74
|
+
for (const entry of index.values()) {
|
|
75
|
+
let score = 0;
|
|
76
|
+
const pathLower = entry.path.toLowerCase();
|
|
77
|
+
const symbolsLower = entry.symbols.map(s => s.toLowerCase());
|
|
78
|
+
for (const word of words) {
|
|
79
|
+
if (pathLower.includes(word))
|
|
80
|
+
score += 2;
|
|
81
|
+
for (const sym of symbolsLower) {
|
|
82
|
+
if (sym.includes(word))
|
|
83
|
+
score += 3;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (score > 0)
|
|
87
|
+
scored.push({ entry, score });
|
|
88
|
+
}
|
|
89
|
+
if (scored.length === 0)
|
|
90
|
+
return '';
|
|
91
|
+
scored.sort((a, b) => b.score - a.score);
|
|
92
|
+
const top = scored.slice(0, 5);
|
|
93
|
+
const sections = top.map(({ entry }) => {
|
|
94
|
+
let preview;
|
|
95
|
+
try {
|
|
96
|
+
const content = fs.readFileSync(path.resolve(process.cwd(), entry.path), 'utf-8');
|
|
97
|
+
preview = content.split('\n').slice(0, 20).join('\n');
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
preview = entry.preview;
|
|
101
|
+
}
|
|
102
|
+
return `File: ${entry.path}\nSymbols: ${entry.symbols.join(', ')}\nPreview:\n${preview.split('\n').map(l => ' ' + l).join('\n')}`;
|
|
103
|
+
});
|
|
104
|
+
return `\n<relevant_files>\n${sections.join('\n\n')}\n</relevant_files>`;
|
|
105
|
+
}
|
|
106
|
+
export function resetEmbeddingsCache() {
|
|
107
|
+
indexCache = null;
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=embeddings-context.js.map
|
|
@@ -203,6 +203,16 @@ export class LLMClient {
|
|
|
203
203
|
const processedMessages = options.messages ?
|
|
204
204
|
this.preprocessMessages(options.messages, modelId) : [];
|
|
205
205
|
logger.vars({ name: 'modelId', value: modelId }, { name: 'originalMessages', value: options.messages?.length || 0 }, { name: 'processedMessages', value: processedMessages.length }, { name: 'temperature', value: options.temperature ?? 0 });
|
|
206
|
+
let systemCached = false;
|
|
207
|
+
for (const msg of processedMessages) {
|
|
208
|
+
if (!systemCached && msg.role === 'system') {
|
|
209
|
+
msg.cache_control = { type: 'ephemeral' };
|
|
210
|
+
systemCached = true;
|
|
211
|
+
}
|
|
212
|
+
else if (msg.role === 'user' && typeof msg.content === 'string' && msg.content.length > 2000) {
|
|
213
|
+
msg.cache_control = { type: 'ephemeral' };
|
|
214
|
+
}
|
|
215
|
+
}
|
|
206
216
|
const isClaudeModel = /claude|sonnet|opus|haiku/i.test(modelId);
|
|
207
217
|
const requestBody = {
|
|
208
218
|
model: modelId,
|
|
@@ -4,6 +4,7 @@ import { configManager } from '../config/config-manager.js';
|
|
|
4
4
|
import { PROJECTS_DIR, cwdToProjectSegment } from '../../constants.js';
|
|
5
5
|
import { initializeJsonStreamLogger } from '../../utils/json-stream-logger.js';
|
|
6
6
|
import { logger } from '../../utils/logger.js';
|
|
7
|
+
import { scheduleSessionSync } from '../../orquesta/session-sync.js';
|
|
7
8
|
export class SessionManager {
|
|
8
9
|
currentSessionId = null;
|
|
9
10
|
currentSessionCreatedAt = null;
|
|
@@ -233,6 +234,7 @@ export class SessionManager {
|
|
|
233
234
|
const sessionsDir = this.getSessionsDir();
|
|
234
235
|
const filePath = path.join(sessionsDir, `${this.currentSessionId}.json`);
|
|
235
236
|
await fs.writeFile(filePath, JSON.stringify(sessionData, null, 2), 'utf-8');
|
|
237
|
+
scheduleSessionSync(this.currentSessionId, normalizedMessages);
|
|
236
238
|
}
|
|
237
239
|
finally {
|
|
238
240
|
this.isSaving = false;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface SubAgentResult {
|
|
2
|
+
success: boolean;
|
|
3
|
+
output: string;
|
|
4
|
+
error?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function spawnSubAgent(task: string, options?: {
|
|
7
|
+
cwd?: string;
|
|
8
|
+
timeout?: number;
|
|
9
|
+
}): Promise<SubAgentResult>;
|
|
10
|
+
export declare function spawnSubAgentsParallel(tasks: Array<{
|
|
11
|
+
task: string;
|
|
12
|
+
cwd?: string;
|
|
13
|
+
}>, maxConcurrent?: number): Promise<SubAgentResult[]>;
|
|
14
|
+
//# sourceMappingURL=sub-agent.d.ts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
export async function spawnSubAgent(task, options) {
|
|
3
|
+
const cwd = options?.cwd || process.cwd();
|
|
4
|
+
const timeout = options?.timeout || 120000;
|
|
5
|
+
try {
|
|
6
|
+
const output = execSync(`orquesta -p '${task.replace(/'/g, "'\\''")}' --dangerously-skip-permissions`, { cwd, timeout, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
7
|
+
return { success: true, output: output.trim() };
|
|
8
|
+
}
|
|
9
|
+
catch (err) {
|
|
10
|
+
const error = err;
|
|
11
|
+
return {
|
|
12
|
+
success: false,
|
|
13
|
+
output: error.stdout?.trim() || '',
|
|
14
|
+
error: error.stderr?.trim() || error.message || 'Unknown error',
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export async function spawnSubAgentsParallel(tasks, maxConcurrent = 3) {
|
|
19
|
+
const results = [];
|
|
20
|
+
for (let i = 0; i < tasks.length; i += maxConcurrent) {
|
|
21
|
+
const batch = tasks.slice(i, i + maxConcurrent);
|
|
22
|
+
const batchResults = await Promise.all(batch.map(t => spawnSubAgent(t.task, { cwd: t.cwd })));
|
|
23
|
+
results.push(...batchResults);
|
|
24
|
+
}
|
|
25
|
+
return results;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=sub-agent.js.map
|
|
@@ -198,13 +198,13 @@ class UsageTrackerClass {
|
|
|
198
198
|
tokenStr = `${(tokens / 1000000).toFixed(2)}M`;
|
|
199
199
|
}
|
|
200
200
|
const activity = currentActivity || 'Processing';
|
|
201
|
-
return
|
|
201
|
+
return `🎵 ${activity}… (esc to interrupt · ${timeStr} · ↑ ${tokenStr} tokens)`;
|
|
202
202
|
}
|
|
203
203
|
formatUsageDisplay() {
|
|
204
204
|
logger.enter('UsageTracker.formatUsageDisplay');
|
|
205
205
|
const summary = this.getSummary();
|
|
206
206
|
const lines = [];
|
|
207
|
-
lines.push('
|
|
207
|
+
lines.push('🎼 Usage Statistics');
|
|
208
208
|
lines.push('');
|
|
209
209
|
lines.push('📅 Today');
|
|
210
210
|
if (summary.today) {
|
|
@@ -16,6 +16,7 @@ import { GIT_COMMIT_RULES } from '../prompts/shared/git-rules.js';
|
|
|
16
16
|
import { logger } from '../utils/logger.js';
|
|
17
17
|
import { getStreamLogger } from '../utils/json-stream-logger.js';
|
|
18
18
|
import { detectGitRepo } from '../utils/git-utils.js';
|
|
19
|
+
import { getRelevantContext } from '../core/embeddings-context.js';
|
|
19
20
|
import { formatErrorMessage, buildTodoContext, findActiveTodo, getTodoStats } from './utils.js';
|
|
20
21
|
import { BaseError } from '../errors/base.js';
|
|
21
22
|
import { runParallelGraph, shouldUseParallelOrchestrator } from './parallel-orchestrator.js';
|
|
@@ -146,8 +147,8 @@ export class PlanExecutor {
|
|
|
146
147
|
callbacks.setTodos(currentTodos);
|
|
147
148
|
emitPlanCreated(currentTodos.map(t => t.title));
|
|
148
149
|
const planMessage = planResult.docsSearchNeeded
|
|
149
|
-
?
|
|
150
|
-
:
|
|
150
|
+
? `🎼 Created ${currentTodos.length} tasks (including docs search). Starting execution...`
|
|
151
|
+
: `🎼 Created ${currentTodos.length} tasks. Starting execution...`;
|
|
151
152
|
const lastMsgForPlan = currentMessages[currentMessages.length - 1];
|
|
152
153
|
const needsUserMessageForPlan = !(lastMsgForPlan?.role === 'user' && lastMsgForPlan?.content === userMessage);
|
|
153
154
|
currentMessages = needsUserMessageForPlan
|
|
@@ -169,8 +170,9 @@ export class PlanExecutor {
|
|
|
169
170
|
const tools = toolRegistry.getLLMToolDefinitions();
|
|
170
171
|
const hasSystemMessage = currentMessages.some(m => m.role === 'system');
|
|
171
172
|
if (!hasSystemMessage) {
|
|
173
|
+
const relevantContext = getRelevantContext(userMessage);
|
|
172
174
|
currentMessages = [
|
|
173
|
-
{ role: 'system', content: this.getSystemPrompt() },
|
|
175
|
+
{ role: 'system', content: this.getSystemPrompt() + relevantContext },
|
|
174
176
|
...currentMessages
|
|
175
177
|
];
|
|
176
178
|
}
|
|
@@ -318,8 +320,9 @@ export class PlanExecutor {
|
|
|
318
320
|
const tools = toolRegistry.getLLMToolDefinitions();
|
|
319
321
|
const hasSystemMessage = currentMessages.some(m => m.role === 'system');
|
|
320
322
|
if (!hasSystemMessage) {
|
|
323
|
+
const relevantContext = getRelevantContext(userMessage);
|
|
321
324
|
currentMessages = [
|
|
322
|
-
{ role: 'system', content: this.getSystemPrompt() },
|
|
325
|
+
{ role: 'system', content: this.getSystemPrompt() + relevantContext },
|
|
323
326
|
...currentMessages
|
|
324
327
|
];
|
|
325
328
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { configManager } from '../core/config/config-manager.js';
|
|
2
|
+
const ORQUESTA_API = process.env['ORQUESTA_API_URL'] || 'https://getorquesta.com';
|
|
3
|
+
let syncTimeout = null;
|
|
4
|
+
export function scheduleSessionSync(sessionId, messages) {
|
|
5
|
+
if (syncTimeout)
|
|
6
|
+
clearTimeout(syncTimeout);
|
|
7
|
+
syncTimeout = setTimeout(() => doSync(sessionId, messages), 5000);
|
|
8
|
+
}
|
|
9
|
+
async function doSync(sessionId, messages) {
|
|
10
|
+
const config = configManager.getOrquestaConfig();
|
|
11
|
+
if (!config?.token)
|
|
12
|
+
return;
|
|
13
|
+
try {
|
|
14
|
+
await fetch(`${ORQUESTA_API}/api/sessions/sync`, {
|
|
15
|
+
method: 'POST',
|
|
16
|
+
headers: {
|
|
17
|
+
'Content-Type': 'application/json',
|
|
18
|
+
'Authorization': `Bearer ${config.token}`,
|
|
19
|
+
},
|
|
20
|
+
body: JSON.stringify({
|
|
21
|
+
sessionId,
|
|
22
|
+
messages: messages.map(m => ({ role: m.role, content: m.content?.slice(0, 10000) })),
|
|
23
|
+
cwd: process.cwd(),
|
|
24
|
+
model: configManager.getCurrentModel()?.id,
|
|
25
|
+
updatedAt: new Date().toISOString(),
|
|
26
|
+
}),
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
catch { }
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=session-sync.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { spawnSubAgent } from '../../../core/sub-agent.js';
|
|
2
|
+
const SUB_AGENT_DEFINITION = {
|
|
3
|
+
type: 'function',
|
|
4
|
+
function: {
|
|
5
|
+
name: 'spawn_sub_agent',
|
|
6
|
+
description: 'Spawn a sub-agent to handle a specific task in parallel. Use for independent sub-tasks that can run concurrently.',
|
|
7
|
+
parameters: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
task: {
|
|
11
|
+
type: 'string',
|
|
12
|
+
description: 'The task description to pass to the sub-agent',
|
|
13
|
+
},
|
|
14
|
+
cwd: {
|
|
15
|
+
type: 'string',
|
|
16
|
+
description: 'Working directory for the sub-agent (optional, defaults to current directory)',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
required: ['task'],
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
async function executeSpawnSubAgent(args) {
|
|
24
|
+
const task = args['task'];
|
|
25
|
+
const cwd = args['cwd'];
|
|
26
|
+
const result = await spawnSubAgent(task, { cwd });
|
|
27
|
+
if (result.success) {
|
|
28
|
+
return { success: true, result: result.output };
|
|
29
|
+
}
|
|
30
|
+
return { success: false, result: result.output, error: result.error };
|
|
31
|
+
}
|
|
32
|
+
export const SpawnSubAgentTool = {
|
|
33
|
+
definition: SUB_AGENT_DEFINITION,
|
|
34
|
+
execute: executeSpawnSubAgent,
|
|
35
|
+
categories: ['llm-simple'],
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=sub-agent-tool.js.map
|
package/dist/tools/registry.js
CHANGED
|
@@ -8,6 +8,7 @@ import { TODO_TOOLS } from './llm/simple/todo-tools.js';
|
|
|
8
8
|
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
|
+
import { SpawnSubAgentTool } from './llm/simple/sub-agent-tool.js';
|
|
11
12
|
import { getShellTools } from './llm/simple/index.js';
|
|
12
13
|
let _browserTools = null;
|
|
13
14
|
let _startBrowserServer = null;
|
|
@@ -316,6 +317,7 @@ export function initializeToolRegistry() {
|
|
|
316
317
|
toolRegistry.registerAll(TODO_TOOLS);
|
|
317
318
|
toolRegistry.register(FinalResponseTool);
|
|
318
319
|
toolRegistry.register(RemotePhoneTool);
|
|
320
|
+
toolRegistry.register(SpawnSubAgentTool);
|
|
319
321
|
toolRegistry.registerAll(PLANNING_TOOLS);
|
|
320
322
|
}
|
|
321
323
|
export async function initializeOptionalTools() {
|
|
@@ -3,16 +3,16 @@ import { Box, Text } from 'ink';
|
|
|
3
3
|
import Spinner from 'ink-spinner';
|
|
4
4
|
import { logger } from '../../utils/logger.js';
|
|
5
5
|
const ACTIVITY_INFO = {
|
|
6
|
-
thinking: { icon: '
|
|
7
|
-
planning: { icon: '
|
|
8
|
-
executing: { icon: '
|
|
9
|
-
docs_search: { icon: '
|
|
10
|
-
file_read: { icon: '
|
|
11
|
-
file_write: { icon: '
|
|
12
|
-
file_search: { icon: '
|
|
13
|
-
tool_call: { icon: '
|
|
14
|
-
validating: { icon: '
|
|
15
|
-
waiting: { icon: '
|
|
6
|
+
thinking: { icon: '🎵', label: 'Thinking', color: 'magenta', spinnerType: 'dots' },
|
|
7
|
+
planning: { icon: '🎼', label: 'Composing', color: 'blue', spinnerType: 'dots' },
|
|
8
|
+
executing: { icon: '🥁', label: 'Executing', color: 'green', spinnerType: 'line' },
|
|
9
|
+
docs_search: { icon: '🎷', label: 'Searching docs', color: 'yellow', spinnerType: 'dots' },
|
|
10
|
+
file_read: { icon: '🎸', label: 'Reading file', color: 'cyan', spinnerType: 'pipe' },
|
|
11
|
+
file_write: { icon: '🎹', label: 'Writing file', color: 'green', spinnerType: 'pipe' },
|
|
12
|
+
file_search: { icon: '🎺', label: 'Searching files', color: 'yellow', spinnerType: 'dots' },
|
|
13
|
+
tool_call: { icon: '🪕', label: 'Tool call', color: 'yellow', spinnerType: 'star' },
|
|
14
|
+
validating: { icon: '🎻', label: 'Validating', color: 'cyan', spinnerType: 'dots' },
|
|
15
|
+
waiting: { icon: '🪘', label: 'Waiting', color: 'gray', spinnerType: 'dots' },
|
|
16
16
|
};
|
|
17
17
|
function formatTokens(count) {
|
|
18
18
|
if (count < 1000)
|
|
@@ -1233,21 +1233,21 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo, resumeL
|
|
|
1233
1233
|
return '🌐';
|
|
1234
1234
|
switch (toolName) {
|
|
1235
1235
|
case 'read_file':
|
|
1236
|
-
return '
|
|
1236
|
+
return '🎸';
|
|
1237
1237
|
case 'create_file':
|
|
1238
|
-
return '
|
|
1238
|
+
return '🎹';
|
|
1239
1239
|
case 'edit_file':
|
|
1240
|
-
return '
|
|
1240
|
+
return '🎻';
|
|
1241
1241
|
case 'list_files':
|
|
1242
|
-
return '
|
|
1242
|
+
return '🎼';
|
|
1243
1243
|
case 'find_files':
|
|
1244
|
-
return '
|
|
1244
|
+
return '🎷';
|
|
1245
1245
|
case 'tell_to_user':
|
|
1246
|
-
return '
|
|
1246
|
+
return '🎺';
|
|
1247
1247
|
case 'bash':
|
|
1248
|
-
return '
|
|
1248
|
+
return '🥁';
|
|
1249
1249
|
default:
|
|
1250
|
-
return '
|
|
1250
|
+
return '🪕';
|
|
1251
1251
|
}
|
|
1252
1252
|
};
|
|
1253
1253
|
const getToolParams = (toolName, args) => {
|
|
@@ -1321,8 +1321,24 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo, resumeL
|
|
|
1321
1321
|
React.createElement(Text, { color: "yellow" }, "\uD83D\uDCAD "),
|
|
1322
1322
|
React.createElement(Text, { color: "white", dimColor: true }, truncatedReason)))));
|
|
1323
1323
|
}
|
|
1324
|
-
case 'tool_result':
|
|
1325
|
-
|
|
1324
|
+
case 'tool_result': {
|
|
1325
|
+
if (!entry.diff || entry.diff.length === 0)
|
|
1326
|
+
return null;
|
|
1327
|
+
let filePath = '';
|
|
1328
|
+
try {
|
|
1329
|
+
filePath = JSON.parse(entry.details || '{}').file || '';
|
|
1330
|
+
}
|
|
1331
|
+
catch { }
|
|
1332
|
+
const header = filePath ? `┌─ ${filePath} ─` : '┌─';
|
|
1333
|
+
return (React.createElement(Box, { key: entry.id, flexDirection: "column", marginLeft: 2 },
|
|
1334
|
+
React.createElement(Text, { color: "gray" },
|
|
1335
|
+
header,
|
|
1336
|
+
'─'.repeat(Math.max(0, 32 - header.length))),
|
|
1337
|
+
entry.diff.map((line, i) => (React.createElement(Text, { key: i, color: line.startsWith('+') ? 'green' : line.startsWith('-') ? 'red' : 'gray' },
|
|
1338
|
+
'│ ',
|
|
1339
|
+
line))),
|
|
1340
|
+
React.createElement(Text, { color: "gray" }, '└' + '─'.repeat(32))));
|
|
1341
|
+
}
|
|
1326
1342
|
case 'shell_result': {
|
|
1327
1343
|
const SHELL_MAX_LINES = 8;
|
|
1328
1344
|
const allLines = (entry.content || '').split('\n');
|
|
@@ -30,7 +30,7 @@ const Clock = () => {
|
|
|
30
30
|
};
|
|
31
31
|
const AnimatedStar = () => {
|
|
32
32
|
const [phase, setPhase] = useState(0);
|
|
33
|
-
const stars = ['
|
|
33
|
+
const stars = ['🎵', '🎶', '♪', '♫'];
|
|
34
34
|
const colors = ['magentaBright', 'magenta', 'gray', 'magenta'];
|
|
35
35
|
useEffect(() => {
|
|
36
36
|
const interval = setInterval(() => {
|
|
@@ -3,13 +3,13 @@ import { Box, Text } from 'ink';
|
|
|
3
3
|
import Spinner from 'ink-spinner';
|
|
4
4
|
import { logger } from '../../utils/logger.js';
|
|
5
5
|
const PHASE_INFO = {
|
|
6
|
-
analyzing: { icon: '
|
|
7
|
-
planning: { icon: '
|
|
8
|
-
generating: { icon: '
|
|
9
|
-
executing: { icon: '
|
|
10
|
-
validating: { icon: '
|
|
11
|
-
tool_calling: { icon: '
|
|
12
|
-
waiting: { icon: '
|
|
6
|
+
analyzing: { icon: '🎷', label: 'Analyzing', color: 'yellow' },
|
|
7
|
+
planning: { icon: '🎼', label: 'Composing', color: 'blue' },
|
|
8
|
+
generating: { icon: '🎹', label: 'Generating', color: 'magenta' },
|
|
9
|
+
executing: { icon: '🥁', label: 'Executing', color: 'green' },
|
|
10
|
+
validating: { icon: '🎻', label: 'Validating', color: 'cyan' },
|
|
11
|
+
tool_calling: { icon: '🪕', label: 'Tool Call', color: 'yellow' },
|
|
12
|
+
waiting: { icon: '🪘', label: 'Waiting', color: 'gray' },
|
|
13
13
|
};
|
|
14
14
|
export const ThinkingIndicator = ({ phase, startTime, currentStep, totalSteps, completedSteps, }) => {
|
|
15
15
|
const [elapsedSeconds, setElapsedSeconds] = useState(0);
|
|
@@ -66,22 +66,22 @@ export const ChatView = ({ messages, currentResponse, maxDisplayMessages = 10, s
|
|
|
66
66
|
content.includes('`');
|
|
67
67
|
};
|
|
68
68
|
const TOOL_ICONS = {
|
|
69
|
-
'read_file': '
|
|
70
|
-
'Read': '
|
|
71
|
-
'create_file': '
|
|
72
|
-
'Create': '
|
|
73
|
-
'edit_file': '
|
|
74
|
-
'Edit': '
|
|
75
|
-
'search_files': '
|
|
76
|
-
'Search': '
|
|
77
|
-
'Grep': '
|
|
78
|
-
'grep_search': '
|
|
79
|
-
'list_files': '
|
|
80
|
-
'list_directory': '
|
|
81
|
-
'Glob': '
|
|
82
|
-
'find_files': '
|
|
83
|
-
'execute_command': '
|
|
84
|
-
'Bash': '
|
|
69
|
+
'read_file': '🎸',
|
|
70
|
+
'Read': '🎸',
|
|
71
|
+
'create_file': '🎹',
|
|
72
|
+
'Create': '🎹',
|
|
73
|
+
'edit_file': '🎻',
|
|
74
|
+
'Edit': '🎻',
|
|
75
|
+
'search_files': '🎷',
|
|
76
|
+
'Search': '🎷',
|
|
77
|
+
'Grep': '🎷',
|
|
78
|
+
'grep_search': '🎷',
|
|
79
|
+
'list_files': '🎼',
|
|
80
|
+
'list_directory': '🎼',
|
|
81
|
+
'Glob': '🎼',
|
|
82
|
+
'find_files': '🎷',
|
|
83
|
+
'execute_command': '🥁',
|
|
84
|
+
'Bash': '🥁',
|
|
85
85
|
};
|
|
86
86
|
const normalizeToolName = (name) => {
|
|
87
87
|
const nameMap = {
|