orquesta-cli 0.2.49 → 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.
@@ -0,0 +1,3 @@
1
+ export declare function getRelevantContext(userMessage: string): string;
2
+ export declare function resetEmbeddingsCache(): void;
3
+ //# sourceMappingURL=embeddings-context.d.ts.map
@@ -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
@@ -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';
@@ -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,3 @@
1
+ import { Message } from '../types/index.js';
2
+ export declare function scheduleSessionSync(sessionId: string, messages: Message[]): void;
3
+ //# sourceMappingURL=session-sync.d.ts.map
@@ -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,3 @@
1
+ import { LLMSimpleTool } from '../../types.js';
2
+ export declare const SpawnSubAgentTool: LLMSimpleTool;
3
+ //# sourceMappingURL=sub-agent-tool.d.ts.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
@@ -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() {
@@ -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
- return null;
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');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orquesta-cli",
3
- "version": "0.2.49",
3
+ "version": "0.2.50",
4
4
  "description": "Orquesta CLI - AI-powered coding assistant with team collaboration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",