orquesta-cli 0.2.95 → 0.2.97
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/cli.js +8 -0
- package/dist/core/config/config-manager.d.ts +2 -0
- package/dist/core/config/config-manager.js +11 -1
- package/dist/core/slash-command-handler.js +8 -1
- package/dist/orchestration/plan-executor.js +23 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/ui/components/TodoListView.js +2 -2
- package/dist/utils/file-system.d.ts +1 -1
- package/dist/utils/file-system.js +8 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -377,6 +377,14 @@ program
|
|
|
377
377
|
await Promise.all([toolsPromise, syncPromise]);
|
|
378
378
|
spinner.stop();
|
|
379
379
|
process.stdout.write('\x1B[2J\x1B[0f');
|
|
380
|
+
if (!process.stdin.isTTY) {
|
|
381
|
+
spinner.stop();
|
|
382
|
+
console.log(chalk.yellow('No interactive terminal available (stdin is not a TTY).'));
|
|
383
|
+
console.log(chalk.dim('For non-interactive use run: orquesta -p "your prompt" (reads the prompt from stdin when value is omitted).'));
|
|
384
|
+
if (cleanup)
|
|
385
|
+
await cleanup();
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
380
388
|
if (options.verbose || options.debug) {
|
|
381
389
|
console.log(chalk.cyan('Starting Orquesta CLI...\n'));
|
|
382
390
|
}
|
|
@@ -60,6 +60,8 @@ export declare class ConfigManager {
|
|
|
60
60
|
setMaxParallelWorkers(n: number): Promise<void>;
|
|
61
61
|
isRefinerEnabled(): boolean;
|
|
62
62
|
setRefinerEnabled(enabled: boolean): Promise<void>;
|
|
63
|
+
isSingleAgentMode(): boolean;
|
|
64
|
+
setSingleAgentMode(enabled: boolean): Promise<void>;
|
|
63
65
|
isWorktreeIsolationEnabled(): boolean;
|
|
64
66
|
setWorktreeIsolationEnabled(enabled: boolean): Promise<void>;
|
|
65
67
|
}
|
|
@@ -48,7 +48,7 @@ export class ConfigManager {
|
|
|
48
48
|
throw error;
|
|
49
49
|
}
|
|
50
50
|
logger.flow('Saving configuration');
|
|
51
|
-
await writeJsonFile(CONFIG_FILE_PATH, this.config);
|
|
51
|
+
await writeJsonFile(CONFIG_FILE_PATH, this.config, 0o600);
|
|
52
52
|
}
|
|
53
53
|
getConfig() {
|
|
54
54
|
if (!this.config) {
|
|
@@ -415,6 +415,16 @@ export class ConfigManager {
|
|
|
415
415
|
config.orchestration.refinerEnabled = enabled;
|
|
416
416
|
await this.saveConfig();
|
|
417
417
|
}
|
|
418
|
+
isSingleAgentMode() {
|
|
419
|
+
return this.config?.orchestration?.singleAgentMode ?? false;
|
|
420
|
+
}
|
|
421
|
+
async setSingleAgentMode(enabled) {
|
|
422
|
+
const config = this.getConfig();
|
|
423
|
+
if (!config.orchestration)
|
|
424
|
+
config.orchestration = {};
|
|
425
|
+
config.orchestration.singleAgentMode = enabled;
|
|
426
|
+
await this.saveConfig();
|
|
427
|
+
}
|
|
418
428
|
isWorktreeIsolationEnabled() {
|
|
419
429
|
return this.config?.orchestration?.worktreeIsolation ?? false;
|
|
420
430
|
}
|
|
@@ -141,7 +141,8 @@ export async function executeSlashCommand(command, context) {
|
|
|
141
141
|
const parallel = configManager.getMaxParallelWorkers();
|
|
142
142
|
const refiner = configManager.isRefinerEnabled() ? 'on' : 'off';
|
|
143
143
|
const worktree = configManager.isWorktreeIsolationEnabled() ? 'on' : 'off';
|
|
144
|
-
|
|
144
|
+
const single = configManager.isSingleAgentMode() ? 'on' : 'off';
|
|
145
|
+
return `Multi-role orchestration\n\n${lines.join('\n')}\n\n max parallel workers: ${parallel}\n refiner pass: ${refiner}\n worktree isolation: ${worktree}\n single-agent mode: ${single}\n\nUsage:\n /role planner <model-id>\n /role executor <model-id>\n /role refiner <model-id> | off\n /role parallel <N>\n /role worktree on|off\n /role single on|off (skip planner; one accumulative agent — best for debugging)\n /role clear`;
|
|
145
146
|
};
|
|
146
147
|
let body;
|
|
147
148
|
try {
|
|
@@ -166,6 +167,12 @@ export async function executeSlashCommand(command, context) {
|
|
|
166
167
|
await configManager.setWorktreeIsolationEnabled(args[1] === 'on');
|
|
167
168
|
body = `Worktree isolation: ${args[1]}.`;
|
|
168
169
|
}
|
|
170
|
+
else if (args[0] === 'single' && (args[1] === 'on' || args[1] === 'off')) {
|
|
171
|
+
await configManager.setSingleAgentMode(args[1] === 'on');
|
|
172
|
+
body = args[1] === 'on'
|
|
173
|
+
? 'Single-agent mode ON — planner/orchestrator bypassed; one accumulative agent (best for debugging/investigation).'
|
|
174
|
+
: 'Single-agent mode OFF — planner + orchestrator restored.';
|
|
175
|
+
}
|
|
169
176
|
else if ((args[0] === 'planner' || args[0] === 'executor' || args[0] === 'refiner') && args[1]) {
|
|
170
177
|
const role = args[0];
|
|
171
178
|
if (role === 'refiner' && args[1] === 'off') {
|
|
@@ -106,6 +106,29 @@ export class PlanExecutor {
|
|
|
106
106
|
throw new Error('INTERRUPTED');
|
|
107
107
|
}
|
|
108
108
|
let currentMessages = messages;
|
|
109
|
+
if (configManager.isSingleAgentMode()) {
|
|
110
|
+
logger.flow('Single-agent mode — bypassing planner/orchestrator');
|
|
111
|
+
callbacks.setExecutionPhase('executing');
|
|
112
|
+
callbacks.setCurrentActivity('Working (single-agent)');
|
|
113
|
+
const saTools = toolRegistry.getLLMToolDefinitions();
|
|
114
|
+
const lastUserIdx = currentMessages.map(m => m.role).lastIndexOf('user');
|
|
115
|
+
const saMessages = (lastUserIdx >= 0 && currentMessages[lastUserIdx]?.content === userMessage)
|
|
116
|
+
? currentMessages
|
|
117
|
+
: [...currentMessages, { role: 'user', content: userMessage }];
|
|
118
|
+
const saExecutorModel = configManager.getRoleModel('executor');
|
|
119
|
+
const saResult = await llmClient.chatCompletionWithTools(saMessages, saTools, {
|
|
120
|
+
getPendingMessage: callbacks.getPendingMessage,
|
|
121
|
+
clearPendingMessage: callbacks.clearPendingMessage,
|
|
122
|
+
...(saExecutorModel ? { model: saExecutorModel } : {}),
|
|
123
|
+
});
|
|
124
|
+
currentMessages = [...saMessages, ...saResult.allMessages.slice(saMessages.length)];
|
|
125
|
+
callbacks.setMessages([...currentMessages]);
|
|
126
|
+
sessionManager.autoSaveCurrentSession(currentMessages);
|
|
127
|
+
callbacks.setExecutionPhase('idle');
|
|
128
|
+
auditLog.emit(auditSid, 'run.complete', { runId, mode: 'single-agent', success: true });
|
|
129
|
+
logger.exit('PlanExecutor.executePlanMode', { singleAgent: true });
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
109
132
|
callbacks.setCurrentActivity('Thinking');
|
|
110
133
|
const plannerModel = configManager.getRoleModel('planner');
|
|
111
134
|
const planningLLM = new PlanningLLM(llmClient, plannerModel ?? undefined);
|
package/dist/types/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { Box, Text } from 'ink';
|
|
|
3
3
|
import Spinner from 'ink-spinner';
|
|
4
4
|
import { logger } from '../../utils/logger.js';
|
|
5
5
|
const STATUS_CONFIG = {
|
|
6
|
-
completed: { icon: '☑', color: '
|
|
6
|
+
completed: { icon: '☑', color: 'green' },
|
|
7
7
|
in_progress: { icon: '☐', color: 'white' },
|
|
8
8
|
failed: { icon: '☒', color: 'red' },
|
|
9
9
|
pending: { icon: '☐', color: 'gray' },
|
|
@@ -57,7 +57,7 @@ export const TodoListView = ({ todos, showProgressBar = true, }) => {
|
|
|
57
57
|
React.createElement(Box, null,
|
|
58
58
|
React.createElement(Box, { width: 2 }, isInProgress ? (React.createElement(Text, { color: "blueBright" },
|
|
59
59
|
React.createElement(Spinner, { type: "dots2" }))) : (React.createElement(Text, { color: config.color }, config.icon))),
|
|
60
|
-
React.createElement(Text, { color: isCompleted ? '
|
|
60
|
+
React.createElement(Text, { color: isCompleted ? 'green' : isInProgress ? 'white' : 'gray', dimColor: false, bold: isInProgress }, todo.title),
|
|
61
61
|
isInProgress && React.createElement(Text, { color: "blueBright" }, " \u2190"),
|
|
62
62
|
todo.dependsOn && todo.dependsOn.length > 0 && (React.createElement(Text, { color: "gray", dimColor: true },
|
|
63
63
|
" (after ",
|
|
@@ -2,7 +2,7 @@ export declare function directoryExists(dirPath: string): Promise<boolean>;
|
|
|
2
2
|
export declare function fileExists(filePath: string): Promise<boolean>;
|
|
3
3
|
export declare function ensureDirectory(dirPath: string): Promise<void>;
|
|
4
4
|
export declare function readJsonFile<T>(filePath: string): Promise<T | null>;
|
|
5
|
-
export declare function writeJsonFile<T>(filePath: string, data: T): Promise<void>;
|
|
5
|
+
export declare function writeJsonFile<T>(filePath: string, data: T, mode?: number): Promise<void>;
|
|
6
6
|
export declare function readTextFile(filePath: string): Promise<string>;
|
|
7
7
|
export declare function writeTextFile(filePath: string, content: string): Promise<void>;
|
|
8
8
|
export declare function getFileSize(filePath: string): Promise<number>;
|
|
@@ -49,14 +49,20 @@ export async function readJsonFile(filePath) {
|
|
|
49
49
|
throw error;
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
-
export async function writeJsonFile(filePath, data) {
|
|
52
|
+
export async function writeJsonFile(filePath, data, mode) {
|
|
53
53
|
try {
|
|
54
54
|
const dirPath = path.dirname(filePath);
|
|
55
55
|
await ensureDirectory(dirPath);
|
|
56
56
|
const content = JSON.stringify(data, null, 2);
|
|
57
57
|
const tmpPath = `${filePath}.tmp.${process.pid}.${tmpCounter++}`;
|
|
58
58
|
try {
|
|
59
|
-
await writeFile(tmpPath, content, 'utf-8');
|
|
59
|
+
await writeFile(tmpPath, content, mode !== undefined ? { encoding: 'utf-8', mode } : 'utf-8');
|
|
60
|
+
if (mode !== undefined) {
|
|
61
|
+
try {
|
|
62
|
+
await new Promise((res, rej) => fs.chmod(tmpPath, mode, e => e ? rej(e) : res()));
|
|
63
|
+
}
|
|
64
|
+
catch { }
|
|
65
|
+
}
|
|
60
66
|
await rename(tmpPath, filePath);
|
|
61
67
|
}
|
|
62
68
|
catch (err) {
|