orquesta-cli 0.2.45 → 0.2.46
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/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
|
@@ -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
|
}
|
|
@@ -38,6 +38,7 @@ import { readHookConfig } from '../../orquesta/hook-init.js';
|
|
|
38
38
|
import { remotePhone } from '../../orquesta/remote-phone.js';
|
|
39
39
|
import { logger } from '../../utils/logger.js';
|
|
40
40
|
import { usageTracker } from '../../core/usage-tracker.js';
|
|
41
|
+
import { estimateCost, formatCostUsd } from '../../core/pricing.js';
|
|
41
42
|
import { UpdateNotification } from '../UpdateNotification.js';
|
|
42
43
|
import { checkForCliUpdate, runCliUpdate, setSkippedVersion } from '../../utils/update-checker.js';
|
|
43
44
|
import { setToolExecutionCallback, setTellToUserCallback, setToolResponseCallback, setPlanCreatedCallback, setTodoStartCallback, setTodoCompleteCallback, setTodoFailCallback, setCompactCallback, setAssistantResponseCallback, setToolApprovalCallback, setReasoningCallback, } from '../../tools/llm/simple/file-tools.js';
|
|
@@ -88,7 +89,7 @@ function getStatusText({ phase, todos, currentToolName }) {
|
|
|
88
89
|
}
|
|
89
90
|
return `${progressPrefix}Processing`;
|
|
90
91
|
}
|
|
91
|
-
export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo }) => {
|
|
92
|
+
export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo, resumeLastSession }) => {
|
|
92
93
|
const { exit } = useApp();
|
|
93
94
|
const hookBinding = useMemo(() => {
|
|
94
95
|
const cfg = readHookConfig(process.cwd());
|
|
@@ -103,6 +104,7 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo }) => {
|
|
|
103
104
|
const [messages, setMessages] = useState([]);
|
|
104
105
|
const { input, setInput, handleHistoryPrev, handleHistoryNext, addToHistory } = useInputHistory();
|
|
105
106
|
const [isProcessing, setIsProcessing] = useState(false);
|
|
107
|
+
const [streamingText, setStreamingText] = useState('');
|
|
106
108
|
const [updatePhase, setUpdatePhase] = useState('hidden');
|
|
107
109
|
const [updateLatest, setUpdateLatest] = useState('');
|
|
108
110
|
const [updateProgress, setUpdateProgress] = useState('');
|
|
@@ -168,6 +170,21 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo }) => {
|
|
|
168
170
|
useEffect(() => {
|
|
169
171
|
sessionManager.setLogEntries(logEntries);
|
|
170
172
|
}, [logEntries]);
|
|
173
|
+
useEffect(() => {
|
|
174
|
+
if (!resumeLastSession)
|
|
175
|
+
return;
|
|
176
|
+
(async () => {
|
|
177
|
+
const sessions = await sessionManager.listSessions();
|
|
178
|
+
if (sessions.length === 0)
|
|
179
|
+
return;
|
|
180
|
+
const last = sessions[0];
|
|
181
|
+
const data = await sessionManager.loadSession(last.id);
|
|
182
|
+
if (data && data.messages.length > 0) {
|
|
183
|
+
setMessages(data.messages);
|
|
184
|
+
addLog({ type: 'session_restored', content: `↩ Resumed session (${data.messages.filter(m => m.role === 'user').length} messages)` });
|
|
185
|
+
}
|
|
186
|
+
})();
|
|
187
|
+
}, [resumeLastSession]);
|
|
171
188
|
const fileBrowserState = useFileBrowserState(input, isProcessing);
|
|
172
189
|
const commandBrowserState = useCommandBrowserState(input, isProcessing);
|
|
173
190
|
const planExecutionState = usePlanExecution(pendingMessageCallbacks);
|
|
@@ -261,6 +278,7 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo }) => {
|
|
|
261
278
|
}, [addLog]);
|
|
262
279
|
useEffect(() => {
|
|
263
280
|
setAssistantResponseCallback((content) => {
|
|
281
|
+
setStreamingText('');
|
|
264
282
|
addLog({
|
|
265
283
|
type: 'assistant_message',
|
|
266
284
|
content,
|
|
@@ -271,6 +289,17 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo }) => {
|
|
|
271
289
|
setAssistantResponseCallback(null);
|
|
272
290
|
};
|
|
273
291
|
}, [addLog]);
|
|
292
|
+
useEffect(() => {
|
|
293
|
+
if (llmClient) {
|
|
294
|
+
llmClient.onStreamingContent = (token) => {
|
|
295
|
+
setStreamingText(prev => prev + token);
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
return () => {
|
|
299
|
+
if (llmClient)
|
|
300
|
+
llmClient.onStreamingContent = null;
|
|
301
|
+
};
|
|
302
|
+
}, [llmClient]);
|
|
274
303
|
useEffect(() => {
|
|
275
304
|
setReasoningCallback((content, _isStreaming) => {
|
|
276
305
|
addLog({
|
|
@@ -432,29 +461,30 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo }) => {
|
|
|
432
461
|
logger.startTimer('app-init');
|
|
433
462
|
try {
|
|
434
463
|
setInitStep('health');
|
|
435
|
-
logger.flow('Running health check');
|
|
464
|
+
logger.flow('Running health check + docs init in parallel');
|
|
436
465
|
setHealthStatus('checking');
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
466
|
+
const healthPromise = (async () => {
|
|
467
|
+
if (configManager.hasEndpoints()) {
|
|
468
|
+
const healthResults = await LLMClient.healthCheckAll();
|
|
469
|
+
let hasHealthy = false;
|
|
470
|
+
for (const [endpointId, modelResults] of healthResults) {
|
|
471
|
+
logger.vars({ name: 'endpointId', value: endpointId }, { name: 'healthyModels', value: modelResults.filter(r => r.healthy).length });
|
|
472
|
+
if (modelResults.some((r) => r.healthy)) {
|
|
473
|
+
hasHealthy = true;
|
|
474
|
+
}
|
|
444
475
|
}
|
|
476
|
+
logger.state('Health status', 'checking', hasHealthy ? 'healthy' : 'unhealthy');
|
|
477
|
+
setHealthStatus(hasHealthy ? 'healthy' : 'unhealthy');
|
|
478
|
+
await configManager.updateAllHealthStatus(healthResults);
|
|
445
479
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
setHealthStatus('unknown');
|
|
452
|
-
}
|
|
453
|
-
setInitStep('docs');
|
|
454
|
-
logger.flow('Initializing docs directory');
|
|
455
|
-
await initializeDocsDirectory().catch((err) => {
|
|
480
|
+
else {
|
|
481
|
+
setHealthStatus('unknown');
|
|
482
|
+
}
|
|
483
|
+
})();
|
|
484
|
+
const docsPromise = initializeDocsDirectory().catch((err) => {
|
|
456
485
|
logger.warn('Docs directory initialization warning', { error: err });
|
|
457
486
|
});
|
|
487
|
+
await Promise.all([healthPromise, docsPromise]);
|
|
458
488
|
setInitStep('config');
|
|
459
489
|
logger.flow('Checking configuration');
|
|
460
490
|
if (!configManager.hasEndpoints()) {
|
|
@@ -938,6 +968,7 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo }) => {
|
|
|
938
968
|
let updatedMessages = [...messages, userMsg];
|
|
939
969
|
setMessages(updatedMessages);
|
|
940
970
|
setIsProcessing(true);
|
|
971
|
+
setStreamingText('');
|
|
941
972
|
setActivityStartTime(Date.now());
|
|
942
973
|
setSubActivities([]);
|
|
943
974
|
if (llmClient) {
|
|
@@ -1389,8 +1420,10 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo }) => {
|
|
|
1389
1420
|
React.createElement(Static, { items: logEntries }, (entry) => renderLogEntry(entry)),
|
|
1390
1421
|
pendingToolApproval && (React.createElement(Box, { marginY: 1 },
|
|
1391
1422
|
React.createElement(ApprovalDialog, { toolName: pendingToolApproval.toolName, args: pendingToolApproval.args, reason: pendingToolApproval.reason, onResponse: handleApprovalResponse }))),
|
|
1392
|
-
isProcessing && (planExecutionState.executionPhase === 'planning' || planExecutionState.todos.length === 0) && !pendingToolApproval && !isDocsSearching && (React.createElement(Box, { marginY: 1 },
|
|
1393
|
-
React.createElement(ActivityIndicator, { activity: getCurrentActivityType(), startTime: activityStartTime, detail: activityDetail, subActivities: subActivities, modelName: currentModelInfo.model })
|
|
1423
|
+
isProcessing && (planExecutionState.executionPhase === 'planning' || planExecutionState.todos.length === 0) && !pendingToolApproval && !isDocsSearching && (React.createElement(Box, { marginY: 1, flexDirection: "column" },
|
|
1424
|
+
React.createElement(ActivityIndicator, { activity: getCurrentActivityType(), startTime: activityStartTime, detail: activityDetail, subActivities: subActivities, modelName: currentModelInfo.model }),
|
|
1425
|
+
streamingText && (React.createElement(Box, { marginTop: 1, paddingX: 1 },
|
|
1426
|
+
React.createElement(Text, { wrap: "wrap" }, streamingText))))),
|
|
1394
1427
|
isDocsSearching && (React.createElement(DocsSearchProgress, { logs: docsSearchLogs, isSearching: isDocsSearching })),
|
|
1395
1428
|
planExecutionState.todos.length > 0 && (React.createElement(Box, { marginTop: 2, marginBottom: 1 },
|
|
1396
1429
|
React.createElement(TodoPanel, { todos: planExecutionState.todos, currentTodoId: planExecutionState.currentTodoId, isProcessing: isProcessing }))),
|
|
@@ -1458,7 +1491,11 @@ export const PlanExecuteApp = ({ llmClient: initialLlmClient, modelInfo }) => {
|
|
|
1458
1491
|
' ',
|
|
1459
1492
|
"(esc to interrupt \u00B7 ",
|
|
1460
1493
|
formatElapsedTime(sessionElapsed),
|
|
1461
|
-
sessionTokens > 0 &&
|
|
1494
|
+
sessionTokens > 0 && (() => {
|
|
1495
|
+
const usage = usageTracker.getSessionUsage();
|
|
1496
|
+
const cost = estimateCost(currentModelInfo.model, usage.inputTokens, usage.outputTokens);
|
|
1497
|
+
return ` · ↑ ${formatTokensCompact(sessionTokens)} tokens${cost ? ` · ${formatCostUsd(cost)}` : ''}`;
|
|
1498
|
+
})(),
|
|
1462
1499
|
")"))),
|
|
1463
1500
|
React.createElement(Box, null,
|
|
1464
1501
|
(() => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orquesta-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.46",
|
|
4
4
|
"description": "Orquesta CLI - AI-powered coding assistant with team collaboration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -11,14 +11,16 @@
|
|
|
11
11
|
"scripts": {
|
|
12
12
|
"preinstall": "node scripts/check-node.cjs",
|
|
13
13
|
"build": "tsc",
|
|
14
|
+
"test": "vitest run",
|
|
15
|
+
"test:watch": "vitest",
|
|
14
16
|
"postbuild": "chmod +x dist/cli.js",
|
|
15
|
-
"dev": "
|
|
17
|
+
"dev": "tsx watch src/cli.ts",
|
|
16
18
|
"start": "node dist/cli.js",
|
|
17
19
|
"watch": "tsc --watch",
|
|
18
20
|
"lint": "eslint src/**/*.ts",
|
|
19
21
|
"lint:fix": "eslint src/**/*.ts --fix",
|
|
20
22
|
"format": "prettier --write \"src/**/*.ts\"",
|
|
21
|
-
"test": "cd tests && python -m pytest test_eval.py -v",
|
|
23
|
+
"test:eval": "cd tests && python -m pytest test_eval.py -v",
|
|
22
24
|
"test:quick": "cd tests && python -m pytest test_eval.py -v -m 'not slow'",
|
|
23
25
|
"prepr": "npm run lint && npm run build",
|
|
24
26
|
"bun:build": "node scripts/inject-version.js && npm run build && bun build dist/cli.js --compile --outfile bin/lcli && cp node_modules/yoga-wasm-web/dist/yoga.wasm bin/",
|
|
@@ -126,7 +128,9 @@
|
|
|
126
128
|
"react-devtools-core": "^4.28.5",
|
|
127
129
|
"react-dom": "^18.3.1",
|
|
128
130
|
"ts-node": "^10.9.2",
|
|
131
|
+
"tsx": "^4.19.0",
|
|
129
132
|
"typescript": "^5.3.3",
|
|
130
|
-
"vite": "^7.3.1"
|
|
133
|
+
"vite": "^7.3.1",
|
|
134
|
+
"vitest": "^3.2.1"
|
|
131
135
|
}
|
|
132
136
|
}
|