agent-rev 0.1.1 → 0.2.3
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/commands/repl.d.ts +1 -1
- package/dist/commands/repl.js +207 -149
- package/dist/commands/setup.js +18 -0
- package/dist/core/engine.d.ts +7 -1
- package/dist/core/engine.js +55 -75
- package/dist/index.js +62 -7
- package/dist/types.js +32 -0
- package/dist/utils/config.d.ts +1 -0
- package/dist/utils/config.js +12 -12
- package/dist/utils/qwen-auth.d.ts +1 -0
- package/dist/utils/qwen-auth.js +17 -0
- package/package.json +3 -10
package/dist/commands/repl.d.ts
CHANGED
|
@@ -26,4 +26,4 @@ export declare function runRepl(resumeSession?: Session): Promise<void>;
|
|
|
26
26
|
* NO spawnea CLIs externos. Usa la conexión OAuth de qwen directamente.
|
|
27
27
|
* Prompt estricto: cada rol solo hace lo que debe, sin extras.
|
|
28
28
|
*/
|
|
29
|
-
export declare function runRole(role: string, arg: string): Promise<void>;
|
|
29
|
+
export declare function runRole(role: string, arg: string, model?: string): Promise<void>;
|
package/dist/commands/repl.js
CHANGED
|
@@ -9,8 +9,8 @@ import { CLI_REGISTRY } from '../types.js';
|
|
|
9
9
|
import { writeJson, ensureDir, readJson, listDir, fileExists } from '../utils/fs.js';
|
|
10
10
|
import { loadAuth, saveAuth, loadCliConfig, saveCliConfig, loadProjectConfig } from '../utils/config.js';
|
|
11
11
|
import { log } from '../utils/logger.js';
|
|
12
|
-
import { AgentEngine } from '../core/engine.js';
|
|
13
|
-
import { qwenAuthStatus, QWEN_AGENT_HOME } from '../utils/qwen-auth.js';
|
|
12
|
+
import { AgentEngine, ExitError } from '../core/engine.js';
|
|
13
|
+
import { qwenAuthStatus, QWEN_AGENT_HOME, fetchQwenModels } from '../utils/qwen-auth.js';
|
|
14
14
|
import { renderWelcomePanel, renderHelpHint, renderSectionBox, renderMultiSectionBox } from '../ui/theme.js';
|
|
15
15
|
import { FixedInput } from '../ui/input.js';
|
|
16
16
|
import { newSession, saveSession } from '../utils/sessions.js';
|
|
@@ -219,6 +219,25 @@ function detectModels(cliName) {
|
|
|
219
219
|
return out.trim().split('\n').filter(Boolean);
|
|
220
220
|
}
|
|
221
221
|
case 'aider': return ['default'];
|
|
222
|
+
case 'agent-orch':
|
|
223
|
+
case 'agent-impl':
|
|
224
|
+
case 'agent-rev':
|
|
225
|
+
case 'agent-explorer': {
|
|
226
|
+
try {
|
|
227
|
+
const out = execSync(`${cliName} --list-models 2>/dev/null`, { encoding: 'utf-8' });
|
|
228
|
+
const models = out.trim().split('\n').filter(Boolean);
|
|
229
|
+
if (models.length)
|
|
230
|
+
return models;
|
|
231
|
+
}
|
|
232
|
+
catch { }
|
|
233
|
+
// fallback: list from opencode since that's typically the underlying CLI
|
|
234
|
+
try {
|
|
235
|
+
const out = execSync('opencode models 2>/dev/null', { encoding: 'utf-8' });
|
|
236
|
+
return out.trim().split('\n').filter(Boolean);
|
|
237
|
+
}
|
|
238
|
+
catch { }
|
|
239
|
+
return ['configured'];
|
|
240
|
+
}
|
|
222
241
|
default: return ['default'];
|
|
223
242
|
}
|
|
224
243
|
}
|
|
@@ -634,29 +653,51 @@ async function cmdStatus(fi) {
|
|
|
634
653
|
fi.println(renderMultiSectionBox(sections));
|
|
635
654
|
fi.println('');
|
|
636
655
|
}
|
|
637
|
-
async function cmdModels(
|
|
638
|
-
const
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
656
|
+
async function cmdModels(roleArg, fi, rl) {
|
|
657
|
+
const auth = await loadAuth();
|
|
658
|
+
const authedProviders = auth.entries.map(e => e.provider);
|
|
659
|
+
if (authedProviders.length === 0) {
|
|
660
|
+
fi.println(chalk.yellow(' No authenticated providers. Run /login first.'));
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
// Fetch models from the OAuth provider (coordinator's native connection)
|
|
664
|
+
fi.println(chalk.dim(' Fetching models from your OAuth account...'));
|
|
665
|
+
const prov = authedProviders[0];
|
|
666
|
+
let models;
|
|
667
|
+
if (prov === 'qwen') {
|
|
668
|
+
models = await fetchQwenModels();
|
|
649
669
|
}
|
|
650
670
|
else {
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
671
|
+
models = detectModels(prov);
|
|
672
|
+
}
|
|
673
|
+
if (!models.length) {
|
|
674
|
+
fi.println(chalk.red(' No models available.'));
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
// Show numbered list
|
|
678
|
+
const resume = fi.suspend();
|
|
679
|
+
const cliConfig = await loadCliConfig();
|
|
680
|
+
const currentModel = cliConfig.coordinatorModel ?? '(auto)';
|
|
681
|
+
console.log(chalk.bold.cyan(`\n Coordinator model [current: ${currentModel}]\n`));
|
|
682
|
+
console.log(chalk.dim(' These are your OAuth account models. CLIs are configured separately with /setup.\n'));
|
|
683
|
+
models.forEach((m, i) => console.log(chalk.dim(` ${String(i + 1).padStart(2)}. ${m}`)));
|
|
684
|
+
const modelInput = await ask(rl, chalk.bold('\n Model # or name (Enter to keep current): '));
|
|
685
|
+
resume();
|
|
686
|
+
if (!modelInput.trim()) {
|
|
687
|
+
fi.println(chalk.dim(' → no change'));
|
|
688
|
+
return;
|
|
659
689
|
}
|
|
690
|
+
const num = parseInt(modelInput.trim());
|
|
691
|
+
const selectedModel = (!isNaN(num) && num >= 1 && num <= models.length)
|
|
692
|
+
? models[num - 1]
|
|
693
|
+
: modelInput.trim();
|
|
694
|
+
// Save as coordinator model only
|
|
695
|
+
cliConfig.coordinatorModel = selectedModel;
|
|
696
|
+
await saveCliConfig(cliConfig);
|
|
697
|
+
// Update gCoordinatorCmd in memory
|
|
698
|
+
gCoordinatorCmd = buildCmd(prov, selectedModel);
|
|
699
|
+
fi.println(chalk.green(`\n ✓ Coordinator model → ${selectedModel}`));
|
|
700
|
+
fi.println(chalk.dim(` CMD: ${gCoordinatorCmd}`));
|
|
660
701
|
fi.println('');
|
|
661
702
|
}
|
|
662
703
|
async function cmdAuthStatus(fi) {
|
|
@@ -829,7 +870,8 @@ export async function initCoordinator() {
|
|
|
829
870
|
}
|
|
830
871
|
// Build coordinator command using the ACTIVE CLI (not orchestrator)
|
|
831
872
|
// The coordinator converses naturally using whatever CLI is active (qwen, claude, etc.)
|
|
832
|
-
const
|
|
873
|
+
const cliCfg = await loadCliConfig();
|
|
874
|
+
const activeModel = cliCfg.coordinatorModel || detectModels(activeCli.name)[0] || 'default';
|
|
833
875
|
gCoordinatorCmd = buildCmd(activeCli.name, activeModel);
|
|
834
876
|
return { coordinatorCmd: gCoordinatorCmd, activeCli, installed, rl };
|
|
835
877
|
}
|
|
@@ -928,31 +970,133 @@ export async function runRepl(resumeSession) {
|
|
|
928
970
|
resume();
|
|
929
971
|
}
|
|
930
972
|
};
|
|
973
|
+
// Show coordinator header before the REPL loop (once, at startup)
|
|
974
|
+
try {
|
|
975
|
+
const dir = process.cwd();
|
|
976
|
+
const config = await loadProjectConfig(dir);
|
|
977
|
+
if (config.roles?.orchestrator?.cli) {
|
|
978
|
+
fi.println('');
|
|
979
|
+
fi.println(chalk.bold.cyan('═'.repeat(46)));
|
|
980
|
+
fi.println(chalk.bold.cyan(` COORDINADOR — ${config.project}`));
|
|
981
|
+
fi.println(chalk.bold.cyan('═'.repeat(46)));
|
|
982
|
+
fi.println(chalk.blue(` → Rol: COORDINADOR (solo orquesta, nunca ejecuta)`));
|
|
983
|
+
fi.println(chalk.dim(' ' + '─'.repeat(42)));
|
|
984
|
+
fi.println(chalk.bold(` ── FASE 0 — Clarificacion ──`));
|
|
985
|
+
fi.println(chalk.dim(` → El coordinador va a conversar con vos para entender la tarea.`));
|
|
986
|
+
fi.println(chalk.dim(` → Cuando el coordinador tenga claro el objetivo, te va a pedir confirmación para lanzar el plan.`));
|
|
987
|
+
fi.println(chalk.dim(' ' + '─'.repeat(42)));
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
catch { }
|
|
991
|
+
// Command handler — used by the main loop AND by the engine during mid-conversation
|
|
992
|
+
const handleCmd = async (trimmed) => {
|
|
993
|
+
const parts = trimmed.slice(1).split(/\s+/);
|
|
994
|
+
const cmd = parts[0].toLowerCase();
|
|
995
|
+
const args = parts.slice(1);
|
|
996
|
+
switch (cmd) {
|
|
997
|
+
case 'setup': {
|
|
998
|
+
const sub = args[0]?.toLowerCase();
|
|
999
|
+
const roleMap = {
|
|
1000
|
+
orch: 'orchestrator', impl: 'implementor', rev: 'reviewer',
|
|
1001
|
+
explorer: 'explorer', proposer: 'proposer', critic: 'critic',
|
|
1002
|
+
};
|
|
1003
|
+
if (sub && roleMap[sub]) {
|
|
1004
|
+
await withRl((rl) => cmdConfigOneRole(rl, roleMap[sub]));
|
|
1005
|
+
}
|
|
1006
|
+
else {
|
|
1007
|
+
await withRl(cmdSetup);
|
|
1008
|
+
}
|
|
1009
|
+
break;
|
|
1010
|
+
}
|
|
1011
|
+
case 'config-multi':
|
|
1012
|
+
await withRl(cmdConfigMulti);
|
|
1013
|
+
break;
|
|
1014
|
+
case 'status':
|
|
1015
|
+
await cmdStatus(fi);
|
|
1016
|
+
break;
|
|
1017
|
+
case 'run': {
|
|
1018
|
+
const dir = process.cwd();
|
|
1019
|
+
const config = await loadProjectConfig(dir);
|
|
1020
|
+
if (args[0] === 'orch' || args[0] === 'orchestrator') {
|
|
1021
|
+
const task = args.slice(1).join(' ');
|
|
1022
|
+
const engine = new AgentEngine(config, dir, gCoordinatorCmd, rl, fi, handleCmd);
|
|
1023
|
+
const result = await engine.runOrchestrator(task);
|
|
1024
|
+
fi.println(chalk.green(` Task ID: ${result.taskId}`));
|
|
1025
|
+
}
|
|
1026
|
+
else if (args[0] === 'impl' || args[0] === 'implementor') {
|
|
1027
|
+
const taskId = args[1];
|
|
1028
|
+
const engine = new AgentEngine(config, dir, gCoordinatorCmd, rl, fi, handleCmd);
|
|
1029
|
+
const taskDir = path.join(dir, '.agent', 'tasks', taskId);
|
|
1030
|
+
const plan = await readJson(path.join(taskDir, 'plan.json'));
|
|
1031
|
+
await engine.runImplementor(taskId, plan);
|
|
1032
|
+
}
|
|
1033
|
+
else if (args[0] === 'rev' || args[0] === 'reviewer') {
|
|
1034
|
+
const taskId = args[1];
|
|
1035
|
+
const engine = new AgentEngine(config, dir, gCoordinatorCmd, rl, fi, handleCmd);
|
|
1036
|
+
const taskDir = path.join(dir, '.agent', 'tasks', taskId);
|
|
1037
|
+
const plan = await readJson(path.join(taskDir, 'plan.json'));
|
|
1038
|
+
const progress = await readJson(path.join(taskDir, 'progress.json'));
|
|
1039
|
+
await engine.runReviewer(taskId, plan, progress);
|
|
1040
|
+
}
|
|
1041
|
+
else {
|
|
1042
|
+
const task = args.join(' ');
|
|
1043
|
+
const engine = new AgentEngine(config, dir, gCoordinatorCmd, rl, fi, handleCmd);
|
|
1044
|
+
await engine.runFullCycle(task);
|
|
1045
|
+
}
|
|
1046
|
+
break;
|
|
1047
|
+
}
|
|
1048
|
+
case 'models':
|
|
1049
|
+
case 'model':
|
|
1050
|
+
await withRl((rl) => cmdModels(args[0], fi, rl));
|
|
1051
|
+
break;
|
|
1052
|
+
case 'login':
|
|
1053
|
+
await withRl(async (rl) => { await cmdLogin(rl); });
|
|
1054
|
+
break;
|
|
1055
|
+
case 'logout': {
|
|
1056
|
+
const credsPath = path.join(QWEN_AGENT_HOME, 'oauth_creds.json');
|
|
1057
|
+
await fs.unlink(credsPath).catch(() => { });
|
|
1058
|
+
const authStore = await loadAuth();
|
|
1059
|
+
authStore.entries = [];
|
|
1060
|
+
delete authStore.activeProvider;
|
|
1061
|
+
await saveAuth(authStore);
|
|
1062
|
+
fi.println(chalk.dim(' Logged out. Credentials cleared.'));
|
|
1063
|
+
fi.teardown();
|
|
1064
|
+
rl.close();
|
|
1065
|
+
throw new ExitError();
|
|
1066
|
+
}
|
|
1067
|
+
case 'auth-status':
|
|
1068
|
+
await cmdAuthStatus(fi);
|
|
1069
|
+
break;
|
|
1070
|
+
case 'tasks':
|
|
1071
|
+
await cmdTasks(fi);
|
|
1072
|
+
break;
|
|
1073
|
+
case 'clear':
|
|
1074
|
+
fi.teardown();
|
|
1075
|
+
console.clear();
|
|
1076
|
+
fi.setup();
|
|
1077
|
+
break;
|
|
1078
|
+
case 'help':
|
|
1079
|
+
cmdHelp(fi);
|
|
1080
|
+
break;
|
|
1081
|
+
case 'exit':
|
|
1082
|
+
case 'quit':
|
|
1083
|
+
fi.println(chalk.dim(' Bye!'));
|
|
1084
|
+
fi.teardown();
|
|
1085
|
+
rl.close();
|
|
1086
|
+
throw new ExitError();
|
|
1087
|
+
default:
|
|
1088
|
+
fi.println(chalk.red(` Unknown command: /${cmd}`));
|
|
1089
|
+
fi.println(chalk.dim(' Type /help for available commands'));
|
|
1090
|
+
}
|
|
1091
|
+
fi.redrawBox();
|
|
1092
|
+
};
|
|
931
1093
|
// Main REPL loop
|
|
932
|
-
let firstMessage = true;
|
|
933
1094
|
// eslint-disable-next-line no-constant-condition
|
|
934
1095
|
while (true) {
|
|
935
1096
|
const line = await fi.readLine();
|
|
936
1097
|
const trimmed = line.trim();
|
|
937
1098
|
if (!trimmed)
|
|
938
1099
|
continue;
|
|
939
|
-
// Show coordinator header before the first user message
|
|
940
|
-
if (firstMessage && !trimmed.startsWith('/')) {
|
|
941
|
-
firstMessage = false;
|
|
942
|
-
try {
|
|
943
|
-
const dir = process.cwd();
|
|
944
|
-
const config = await loadProjectConfig(dir);
|
|
945
|
-
if (config.roles?.orchestrator?.cli) {
|
|
946
|
-
fi.println('');
|
|
947
|
-
fi.println(chalk.bold.cyan('═'.repeat(46)));
|
|
948
|
-
fi.println(chalk.bold.cyan(` COORDINADOR — ${config.project}`));
|
|
949
|
-
fi.println(chalk.bold.cyan('═'.repeat(46)));
|
|
950
|
-
fi.println(chalk.blue(` → Rol: COORDINADOR (solo orquesta, nunca ejecuta)`));
|
|
951
|
-
fi.println(chalk.dim(' ' + '─'.repeat(42)));
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
catch { }
|
|
955
|
-
}
|
|
956
1100
|
// Show user message in the scroll area (chat style, right-aligned per line)
|
|
957
1101
|
const userLines = trimmed.split('\n').filter(l => l.trim());
|
|
958
1102
|
for (let i = 0; i < userLines.length; i++) {
|
|
@@ -972,128 +1116,29 @@ export async function runRepl(resumeSession) {
|
|
|
972
1116
|
fi.println(chalk.red(' No roles configured. Run /config-multi first.'));
|
|
973
1117
|
}
|
|
974
1118
|
else {
|
|
975
|
-
const engine = new AgentEngine(config, dir, gCoordinatorCmd, rl, fi);
|
|
1119
|
+
const engine = new AgentEngine(config, dir, gCoordinatorCmd, rl, fi, handleCmd);
|
|
976
1120
|
await engine.runFullCycle(trimmed);
|
|
977
1121
|
session.messages.push({ role: 'agent', content: `[task cycle completed for: ${trimmed}]`, ts: new Date().toISOString() });
|
|
978
1122
|
await saveSession(session);
|
|
979
1123
|
}
|
|
980
1124
|
}
|
|
981
1125
|
catch (err) {
|
|
1126
|
+
if (err instanceof ExitError)
|
|
1127
|
+
return;
|
|
982
1128
|
fi.println(chalk.red(` Error: ${err.message}`));
|
|
983
1129
|
}
|
|
984
1130
|
fi.redrawBox();
|
|
985
1131
|
continue;
|
|
986
1132
|
}
|
|
987
|
-
const parts = trimmed.slice(1).split(/\s+/);
|
|
988
|
-
const cmd = parts[0].toLowerCase();
|
|
989
|
-
const args = parts.slice(1);
|
|
990
1133
|
try {
|
|
991
|
-
|
|
992
|
-
case 'setup': {
|
|
993
|
-
const sub = args[0]?.toLowerCase();
|
|
994
|
-
const roleMap = {
|
|
995
|
-
orch: 'orchestrator', impl: 'implementor', rev: 'reviewer',
|
|
996
|
-
explorer: 'explorer', proposer: 'proposer', critic: 'critic',
|
|
997
|
-
};
|
|
998
|
-
if (sub && roleMap[sub]) {
|
|
999
|
-
await withRl((rl) => cmdConfigOneRole(rl, roleMap[sub]));
|
|
1000
|
-
}
|
|
1001
|
-
else {
|
|
1002
|
-
await withRl(cmdSetup);
|
|
1003
|
-
}
|
|
1004
|
-
break;
|
|
1005
|
-
}
|
|
1006
|
-
case 'config-multi':
|
|
1007
|
-
await withRl(cmdConfigMulti);
|
|
1008
|
-
break;
|
|
1009
|
-
case 'status':
|
|
1010
|
-
await cmdStatus(fi);
|
|
1011
|
-
break;
|
|
1012
|
-
case 'run':
|
|
1013
|
-
if (args[0] === 'orch' || args[0] === 'orchestrator') {
|
|
1014
|
-
const task = args.slice(1).join(' ');
|
|
1015
|
-
const dir = process.cwd();
|
|
1016
|
-
const config = await loadProjectConfig(dir);
|
|
1017
|
-
const engine = new AgentEngine(config, dir, gCoordinatorCmd, rl, fi);
|
|
1018
|
-
const result = await engine.runOrchestrator(task);
|
|
1019
|
-
fi.println(chalk.green(` Task ID: ${result.taskId}`));
|
|
1020
|
-
}
|
|
1021
|
-
else if (args[0] === 'impl' || args[0] === 'implementor') {
|
|
1022
|
-
const taskId = args[1];
|
|
1023
|
-
const dir = process.cwd();
|
|
1024
|
-
const config = await loadProjectConfig(dir);
|
|
1025
|
-
const engine = new AgentEngine(config, dir, gCoordinatorCmd, rl, fi);
|
|
1026
|
-
const taskDir = path.join(dir, '.agent', 'tasks', taskId);
|
|
1027
|
-
const plan = await readJson(path.join(taskDir, 'plan.json'));
|
|
1028
|
-
await engine.runImplementor(taskId, plan);
|
|
1029
|
-
}
|
|
1030
|
-
else if (args[0] === 'rev' || args[0] === 'reviewer') {
|
|
1031
|
-
const taskId = args[1];
|
|
1032
|
-
const dir = process.cwd();
|
|
1033
|
-
const config = await loadProjectConfig(dir);
|
|
1034
|
-
const engine = new AgentEngine(config, dir, gCoordinatorCmd, rl, fi);
|
|
1035
|
-
const taskDir = path.join(dir, '.agent', 'tasks', taskId);
|
|
1036
|
-
const plan = await readJson(path.join(taskDir, 'plan.json'));
|
|
1037
|
-
const progress = await readJson(path.join(taskDir, 'progress.json'));
|
|
1038
|
-
await engine.runReviewer(taskId, plan, progress);
|
|
1039
|
-
}
|
|
1040
|
-
else {
|
|
1041
|
-
const task = args.join(' ');
|
|
1042
|
-
const dir = process.cwd();
|
|
1043
|
-
const config = await loadProjectConfig(dir);
|
|
1044
|
-
const engine = new AgentEngine(config, dir, gCoordinatorCmd, rl, fi);
|
|
1045
|
-
await engine.runFullCycle(task);
|
|
1046
|
-
}
|
|
1047
|
-
break;
|
|
1048
|
-
case 'models':
|
|
1049
|
-
await cmdModels(args[0], fi);
|
|
1050
|
-
break;
|
|
1051
|
-
case 'login':
|
|
1052
|
-
await withRl(async (rl) => { await cmdLogin(rl); });
|
|
1053
|
-
break;
|
|
1054
|
-
case 'logout': {
|
|
1055
|
-
// Clear OAuth credentials and auth store
|
|
1056
|
-
const credsPath = path.join(QWEN_AGENT_HOME, 'oauth_creds.json');
|
|
1057
|
-
await fs.unlink(credsPath).catch(() => { });
|
|
1058
|
-
const authStore = await loadAuth();
|
|
1059
|
-
authStore.entries = [];
|
|
1060
|
-
delete authStore.activeProvider;
|
|
1061
|
-
await saveAuth(authStore);
|
|
1062
|
-
fi.println(chalk.dim(' Logged out. Credentials cleared.'));
|
|
1063
|
-
fi.teardown();
|
|
1064
|
-
rl.close();
|
|
1065
|
-
return;
|
|
1066
|
-
}
|
|
1067
|
-
case 'auth-status':
|
|
1068
|
-
await cmdAuthStatus(fi);
|
|
1069
|
-
break;
|
|
1070
|
-
case 'tasks':
|
|
1071
|
-
await cmdTasks(fi);
|
|
1072
|
-
break;
|
|
1073
|
-
case 'clear':
|
|
1074
|
-
fi.teardown();
|
|
1075
|
-
console.clear();
|
|
1076
|
-
fi.setup();
|
|
1077
|
-
break;
|
|
1078
|
-
case 'help':
|
|
1079
|
-
cmdHelp(fi);
|
|
1080
|
-
break;
|
|
1081
|
-
case 'exit':
|
|
1082
|
-
case 'quit':
|
|
1083
|
-
fi.println(chalk.dim(' Bye!'));
|
|
1084
|
-
fi.teardown();
|
|
1085
|
-
rl.close();
|
|
1086
|
-
return;
|
|
1087
|
-
default:
|
|
1088
|
-
fi.println(chalk.red(` Unknown command: /${cmd}`));
|
|
1089
|
-
fi.println(chalk.dim(' Type /help for available commands'));
|
|
1090
|
-
}
|
|
1134
|
+
await handleCmd(trimmed);
|
|
1091
1135
|
}
|
|
1092
1136
|
catch (err) {
|
|
1137
|
+
if (err instanceof ExitError)
|
|
1138
|
+
return;
|
|
1093
1139
|
fi.println(chalk.red(` Error: ${err.message}`));
|
|
1140
|
+
fi.redrawBox();
|
|
1094
1141
|
}
|
|
1095
|
-
// Always redraw box after each command
|
|
1096
|
-
fi.redrawBox();
|
|
1097
1142
|
}
|
|
1098
1143
|
}
|
|
1099
1144
|
/**
|
|
@@ -1101,7 +1146,7 @@ export async function runRepl(resumeSession) {
|
|
|
1101
1146
|
* NO spawnea CLIs externos. Usa la conexión OAuth de qwen directamente.
|
|
1102
1147
|
* Prompt estricto: cada rol solo hace lo que debe, sin extras.
|
|
1103
1148
|
*/
|
|
1104
|
-
export async function runRole(role, arg) {
|
|
1149
|
+
export async function runRole(role, arg, model) {
|
|
1105
1150
|
const init = await initCoordinator();
|
|
1106
1151
|
if (!init)
|
|
1107
1152
|
process.exit(1);
|
|
@@ -1123,7 +1168,20 @@ export async function runRole(role, arg) {
|
|
|
1123
1168
|
rl.close();
|
|
1124
1169
|
process.exit(1);
|
|
1125
1170
|
}
|
|
1126
|
-
|
|
1171
|
+
// Override model if passed via --model flag
|
|
1172
|
+
if (model) {
|
|
1173
|
+
const roleKey = role.toLowerCase().startsWith('orch') ? 'orchestrator'
|
|
1174
|
+
: role.toLowerCase().startsWith('impl') ? 'implementor'
|
|
1175
|
+
: role.toLowerCase().startsWith('rev') ? 'reviewer'
|
|
1176
|
+
: role.toLowerCase().startsWith('exp') ? 'explorer'
|
|
1177
|
+
: undefined;
|
|
1178
|
+
if (roleKey && config.roles[roleKey]) {
|
|
1179
|
+
const r = config.roles[roleKey];
|
|
1180
|
+
r.model = model;
|
|
1181
|
+
r.cmd = r.cmd.replace(/(-m|--model)\s+\S+/, `$1 ${model}`);
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
console.log(chalk.bold.cyan(`\n Agent-mp — Rol: ${role.toUpperCase()}${model ? ` (${model})` : ''}\n`));
|
|
1127
1185
|
const engine = new AgentEngine(config, dir, coordinatorCmd, rl);
|
|
1128
1186
|
try {
|
|
1129
1187
|
switch (role.toLowerCase()) {
|
package/dist/commands/setup.js
CHANGED
|
@@ -57,6 +57,24 @@ function detectModels(cliName) {
|
|
|
57
57
|
}
|
|
58
58
|
case 'aider':
|
|
59
59
|
return ['default'];
|
|
60
|
+
case 'agent-orch':
|
|
61
|
+
case 'agent-impl':
|
|
62
|
+
case 'agent-rev':
|
|
63
|
+
case 'agent-explorer': {
|
|
64
|
+
try {
|
|
65
|
+
const out = execSync(`${cliName} --list-models 2>/dev/null`, { encoding: 'utf-8' });
|
|
66
|
+
const models = out.trim().split('\n').filter(Boolean);
|
|
67
|
+
if (models.length)
|
|
68
|
+
return models;
|
|
69
|
+
}
|
|
70
|
+
catch { }
|
|
71
|
+
try {
|
|
72
|
+
const out = execSync('opencode models 2>/dev/null', { encoding: 'utf-8' });
|
|
73
|
+
return out.trim().split('\n').filter(Boolean);
|
|
74
|
+
}
|
|
75
|
+
catch { }
|
|
76
|
+
return ['configured'];
|
|
77
|
+
}
|
|
60
78
|
default:
|
|
61
79
|
return ['default'];
|
|
62
80
|
}
|
package/dist/core/engine.d.ts
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import * as readline from 'readline';
|
|
2
2
|
import { AgentConfig, TaskPlan, TaskProgress } from '../types.js';
|
|
3
3
|
import type { FixedInput } from '../ui/input.js';
|
|
4
|
+
/** Thrown when a slash command inside a conversation requests exit */
|
|
5
|
+
export declare class ExitError extends Error {
|
|
6
|
+
constructor();
|
|
7
|
+
}
|
|
8
|
+
export type SlashHandler = (input: string) => Promise<void>;
|
|
4
9
|
export declare class AgentEngine {
|
|
5
10
|
private config;
|
|
6
11
|
private projectDir;
|
|
7
12
|
private coordinatorCmd;
|
|
8
13
|
private rl?;
|
|
9
14
|
private fi?;
|
|
15
|
+
private slashHandler?;
|
|
10
16
|
private totalTokens;
|
|
11
17
|
private phaseTokens;
|
|
12
|
-
constructor(config: AgentConfig, projectDir: string, coordinatorCmd?: string, rl?: readline.Interface, fi?: FixedInput);
|
|
18
|
+
constructor(config: AgentConfig, projectDir: string, coordinatorCmd?: string, rl?: readline.Interface, fi?: FixedInput, slashHandler?: SlashHandler);
|
|
13
19
|
/**
|
|
14
20
|
* FASE 0 — Clarificacion con el programador.
|
|
15
21
|
* El coordinador (CLI activo, ej: Qwen) conversa con el usuario
|
package/dist/core/engine.js
CHANGED
|
@@ -9,6 +9,10 @@ import chalk from 'chalk';
|
|
|
9
9
|
import { QWEN_AGENT_HOME } from '../utils/qwen-auth.js';
|
|
10
10
|
import { callQwenAPI } from '../utils/qwen-auth.js';
|
|
11
11
|
import * as fs from 'fs/promises';
|
|
12
|
+
/** Thrown when a slash command inside a conversation requests exit */
|
|
13
|
+
export class ExitError extends Error {
|
|
14
|
+
constructor() { super('exit'); }
|
|
15
|
+
}
|
|
12
16
|
/** Parse --help output to detect current flags */
|
|
13
17
|
function detectCliFlags(cliName) {
|
|
14
18
|
try {
|
|
@@ -205,14 +209,16 @@ export class AgentEngine {
|
|
|
205
209
|
coordinatorCmd;
|
|
206
210
|
rl;
|
|
207
211
|
fi;
|
|
212
|
+
slashHandler;
|
|
208
213
|
totalTokens = { input_tokens: 0, output_tokens: 0, cache_read_input_tokens: 0 };
|
|
209
214
|
phaseTokens = [];
|
|
210
|
-
constructor(config, projectDir, coordinatorCmd, rl, fi) {
|
|
215
|
+
constructor(config, projectDir, coordinatorCmd, rl, fi, slashHandler) {
|
|
211
216
|
this.config = config;
|
|
212
217
|
this.projectDir = projectDir;
|
|
213
218
|
this.coordinatorCmd = coordinatorCmd || '';
|
|
214
219
|
this.rl = rl;
|
|
215
220
|
this.fi = fi;
|
|
221
|
+
this.slashHandler = slashHandler;
|
|
216
222
|
}
|
|
217
223
|
/**
|
|
218
224
|
* FASE 0 — Clarificacion con el programador.
|
|
@@ -223,14 +229,10 @@ export class AgentEngine {
|
|
|
223
229
|
if (!this.coordinatorCmd) {
|
|
224
230
|
return initialTask;
|
|
225
231
|
}
|
|
226
|
-
log.section('FASE 0 — Clarificacion');
|
|
227
|
-
log.info('El coordinador va a conversar con vos para entender la tarea.');
|
|
228
|
-
log.info('Cuando el coordinador tenga claro el objetivo, te va a pedir confirmación para lanzar el plan.');
|
|
229
|
-
log.divider();
|
|
230
232
|
const context = await this.buildCoordinatorContext();
|
|
231
233
|
let conversationHistory = `TAREA INICIAL: ${initialTask}`;
|
|
232
|
-
//
|
|
233
|
-
|
|
234
|
+
// Helper: call coordinator CLI and return response text
|
|
235
|
+
const callCoordinator = async () => {
|
|
234
236
|
const prompt = `Sos el COORDINADOR de un equipo multi-agente de desarrollo.
|
|
235
237
|
Tu trabajo es ENTENDER lo que el programador necesita haciendo PREGUNTAS si es necesario.
|
|
236
238
|
|
|
@@ -247,12 +249,11 @@ INSTRUCCIONES:
|
|
|
247
249
|
- NO uses JSON, habla normalmente.
|
|
248
250
|
- Sé breve y directo.`;
|
|
249
251
|
log.info('Coordinador analizando...');
|
|
250
|
-
// Usar credenciales corporativas si es qwen
|
|
251
252
|
const envOverride = {};
|
|
252
|
-
|
|
253
|
-
const personalCreds = path.join(os.homedir(), '.qwen', 'oauth_creds.json');
|
|
253
|
+
let res;
|
|
254
254
|
if (this.coordinatorCmd.startsWith('qwen')) {
|
|
255
|
-
|
|
255
|
+
const corporateCreds = path.join(QWEN_AGENT_HOME, 'oauth_creds.json');
|
|
256
|
+
const personalCreds = path.join(os.homedir(), '.qwen', 'oauth_creds.json');
|
|
256
257
|
let corporateCredsContent = null;
|
|
257
258
|
try {
|
|
258
259
|
corporateCredsContent = await fs.readFile(corporateCreds, 'utf-8');
|
|
@@ -260,93 +261,63 @@ INSTRUCCIONES:
|
|
|
260
261
|
catch {
|
|
261
262
|
console.log(chalk.red('\n ✗ No hay credenciales corporativas de Qwen.'));
|
|
262
263
|
console.log(chalk.yellow(' Ejecutá /login para autenticarte.\n'));
|
|
263
|
-
return
|
|
264
|
+
return '';
|
|
264
265
|
}
|
|
265
|
-
// Backup personal creds
|
|
266
266
|
let personalBackup = null;
|
|
267
267
|
try {
|
|
268
268
|
personalBackup = await fs.readFile(personalCreds, 'utf-8');
|
|
269
269
|
}
|
|
270
270
|
catch { }
|
|
271
|
-
// Copy corporate creds to personal location
|
|
272
271
|
await fs.writeFile(personalCreds, corporateCredsContent);
|
|
273
|
-
|
|
274
|
-
// Restore personal creds
|
|
272
|
+
res = await runCli(this.coordinatorCmd, prompt, 600000, envOverride);
|
|
275
273
|
if (personalBackup) {
|
|
276
274
|
await fs.writeFile(personalCreds, personalBackup);
|
|
277
275
|
}
|
|
278
276
|
else {
|
|
279
|
-
// No personal backup, remove the file
|
|
280
277
|
await fs.unlink(personalCreds).catch(() => { });
|
|
281
278
|
}
|
|
282
|
-
// Extraer texto legible del output (Qwen CLI devuelve JSON con eventos)
|
|
283
|
-
let responseText = res.output.trim();
|
|
284
|
-
try {
|
|
285
|
-
const json = JSON.parse(res.output);
|
|
286
|
-
if (Array.isArray(json)) {
|
|
287
|
-
// Buscar el resultado final
|
|
288
|
-
for (const item of json) {
|
|
289
|
-
if (item.type === 'result' && typeof item.result === 'string') {
|
|
290
|
-
responseText = item.result;
|
|
291
|
-
break;
|
|
292
|
-
}
|
|
293
|
-
if (item.type === 'assistant' && item.message?.content?.length > 0) {
|
|
294
|
-
const textContent = item.message.content.find((c) => c.type === 'text');
|
|
295
|
-
if (textContent?.text) {
|
|
296
|
-
responseText = textContent.text;
|
|
297
|
-
break;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
catch {
|
|
304
|
-
// No es JSON, usar el output directo
|
|
305
|
-
}
|
|
306
|
-
console.log('');
|
|
307
|
-
console.log(chalk.cyan(' Coordinador:'));
|
|
308
|
-
console.log(chalk.white(` ${responseText}`));
|
|
309
|
-
console.log('');
|
|
310
|
-
// Verificar si el coordinador quiere lanzar el orchestrator EXPLICITAMENTE
|
|
311
|
-
// Solo si pregunta "confirmás" o similar
|
|
312
|
-
const lower = responseText.toLowerCase();
|
|
313
|
-
if ((lower.includes('confirm') || lower.includes('procedo') || lower.includes('lanzo el plan')) &&
|
|
314
|
-
(lower.includes('orchestrator') || lower.includes('plan'))) {
|
|
315
|
-
const confirm = await ask(' ¿Confirmás lanzar el orchestrator? (y/n): ', this.rl, this.fi);
|
|
316
|
-
if (confirm.toLowerCase() === 'y' || confirm.toLowerCase() === 's') {
|
|
317
|
-
return conversationHistory;
|
|
318
|
-
}
|
|
319
|
-
const correction = await ask(' ¿Qué querés cambiar o agregar?: ', this.rl, this.fi);
|
|
320
|
-
conversationHistory += `\nPROGRAMADOR: ${correction}`;
|
|
321
|
-
continue;
|
|
322
|
-
}
|
|
323
|
-
// Si no, seguir conversando
|
|
324
|
-
const answer = await ask(' Tu respuesta: ', this.rl, this.fi);
|
|
325
|
-
conversationHistory += `\nPROGRAMADOR: ${answer}`;
|
|
326
279
|
}
|
|
327
280
|
else {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
281
|
+
res = await runCli(this.coordinatorCmd, prompt, 600000, envOverride);
|
|
282
|
+
}
|
|
283
|
+
// Extract readable text (Qwen CLI returns JSON events)
|
|
284
|
+
let responseText = res.output.trim();
|
|
285
|
+
try {
|
|
286
|
+
const json = JSON.parse(res.output);
|
|
287
|
+
if (Array.isArray(json)) {
|
|
288
|
+
for (const item of json) {
|
|
289
|
+
if (item.type === 'result' && typeof item.result === 'string') {
|
|
290
|
+
responseText = item.result;
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
if (item.type === 'assistant' && item.message?.content?.length > 0) {
|
|
294
|
+
const t = item.message.content.find((c) => c.type === 'text');
|
|
295
|
+
if (t?.text) {
|
|
296
|
+
responseText = t.text;
|
|
338
297
|
break;
|
|
339
298
|
}
|
|
340
299
|
}
|
|
341
300
|
}
|
|
342
301
|
}
|
|
343
|
-
|
|
302
|
+
}
|
|
303
|
+
catch { }
|
|
304
|
+
return responseText;
|
|
305
|
+
};
|
|
306
|
+
// Clarification loop — coordinator is only called when there is new user input
|
|
307
|
+
let needsCoordinatorCall = true;
|
|
308
|
+
while (true) {
|
|
309
|
+
if (needsCoordinatorCall) {
|
|
310
|
+
needsCoordinatorCall = false;
|
|
311
|
+
const responseText = await callCoordinator();
|
|
312
|
+
if (!responseText)
|
|
313
|
+
return initialTask; // auth error
|
|
344
314
|
console.log('');
|
|
345
315
|
console.log(chalk.cyan(' Coordinador:'));
|
|
346
316
|
console.log(chalk.white(` ${responseText}`));
|
|
347
317
|
console.log('');
|
|
318
|
+
// If coordinator is asking for confirmation to launch the plan
|
|
348
319
|
const lower = responseText.toLowerCase();
|
|
349
|
-
if ((lower.includes('confirm') || lower.includes('procedo')) &&
|
|
320
|
+
if ((lower.includes('confirm') || lower.includes('procedo') || lower.includes('lanzo el plan')) &&
|
|
350
321
|
(lower.includes('orchestrator') || lower.includes('plan'))) {
|
|
351
322
|
const confirm = await ask(' ¿Confirmás lanzar el orchestrator? (y/n): ', this.rl, this.fi);
|
|
352
323
|
if (confirm.toLowerCase() === 'y' || confirm.toLowerCase() === 's') {
|
|
@@ -354,11 +325,20 @@ INSTRUCCIONES:
|
|
|
354
325
|
}
|
|
355
326
|
const correction = await ask(' ¿Qué querés cambiar o agregar?: ', this.rl, this.fi);
|
|
356
327
|
conversationHistory += `\nPROGRAMADOR: ${correction}`;
|
|
328
|
+
needsCoordinatorCall = true;
|
|
357
329
|
continue;
|
|
358
330
|
}
|
|
359
|
-
const answer = await ask(' Tu respuesta: ', this.rl, this.fi);
|
|
360
|
-
conversationHistory += `\nPROGRAMADOR: ${answer}`;
|
|
361
331
|
}
|
|
332
|
+
// Ask user — slash commands are handled here without calling coordinator again
|
|
333
|
+
const answer = await ask(' Tu respuesta: ', this.rl, this.fi);
|
|
334
|
+
const trimmedAnswer = answer.trim();
|
|
335
|
+
if (trimmedAnswer.startsWith('/') && this.slashHandler) {
|
|
336
|
+
await this.slashHandler(trimmedAnswer);
|
|
337
|
+
// needsCoordinatorCall is still false — re-prompt without coordinator call
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
conversationHistory += `\nPROGRAMADOR: ${answer}`;
|
|
341
|
+
needsCoordinatorCall = true; // got real input, coordinator should respond
|
|
362
342
|
}
|
|
363
343
|
}
|
|
364
344
|
async runWithFallback(roleName, prompt, phaseName) {
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import * as os from 'os';
|
|
|
5
5
|
import * as fs from 'fs/promises';
|
|
6
6
|
import * as readline from 'readline';
|
|
7
7
|
import chalk from 'chalk';
|
|
8
|
-
import { AGENT_HOME } from './utils/config.js';
|
|
8
|
+
import { AGENT_HOME, PKG_NAME } from './utils/config.js';
|
|
9
9
|
import { runRepl, runRole } from './commands/repl.js';
|
|
10
10
|
import { setupCommand } from './commands/setup.js';
|
|
11
11
|
import { getLastSessionForDir, listSessions, loadSession } from './utils/sessions.js';
|
|
@@ -18,6 +18,8 @@ program
|
|
|
18
18
|
.option('--continue', 'Resume the last session in the current directory')
|
|
19
19
|
.option('--resume [id]', 'Resume any saved session by ID (omit ID to pick from list)')
|
|
20
20
|
.option('--rol <role>', 'Run a specific role directly: orchestrator | implementor | reviewer | coordinator')
|
|
21
|
+
.option('--models [cli]', 'List available models (optionally filter by CLI name)')
|
|
22
|
+
.option('--model [cli]', 'Alias for --models')
|
|
21
23
|
.option('--reset-coordinator', 'Clear coordinator selection (re-pick on next run)')
|
|
22
24
|
.option('--reset-auth', 'Wipe all auth credentials and start fresh')
|
|
23
25
|
.addHelpText('after', `
|
|
@@ -64,16 +66,21 @@ const ROLE_BINS = {
|
|
|
64
66
|
'agent-rev': 'reviewer',
|
|
65
67
|
'agent-explorer': 'explorer',
|
|
66
68
|
};
|
|
67
|
-
const
|
|
68
|
-
const nativeRole = ROLE_BINS[binName];
|
|
69
|
+
const nativeRole = ROLE_BINS[PKG_NAME];
|
|
69
70
|
if (nativeRole) {
|
|
70
|
-
//
|
|
71
|
-
const
|
|
71
|
+
// Parse --model flag if provided
|
|
72
|
+
const args = process.argv.slice(2);
|
|
73
|
+
const modelIdx = args.findIndex(a => a === '--model' || a === '-m');
|
|
74
|
+
let model;
|
|
75
|
+
if (modelIdx !== -1 && args[modelIdx + 1]) {
|
|
76
|
+
model = args[modelIdx + 1];
|
|
77
|
+
}
|
|
78
|
+
const taskArg = args.filter((a, i) => !a.startsWith('-') && i !== modelIdx + 1).join(' ').trim();
|
|
72
79
|
if (!taskArg) {
|
|
73
|
-
console.error(chalk.red(` Usage: ${
|
|
80
|
+
console.error(chalk.red(` Usage: ${PKG_NAME} [--model <model>] "<task description or task-id>"`));
|
|
74
81
|
process.exit(1);
|
|
75
82
|
}
|
|
76
|
-
await runRole(nativeRole, taskArg);
|
|
83
|
+
await runRole(nativeRole, taskArg, model);
|
|
77
84
|
process.exit(0);
|
|
78
85
|
}
|
|
79
86
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -99,6 +106,54 @@ program.action(async (task, options) => {
|
|
|
99
106
|
}
|
|
100
107
|
process.exit(0);
|
|
101
108
|
}
|
|
109
|
+
// --models / --model: list models for authenticated providers only
|
|
110
|
+
if (options.models !== undefined || options.model !== undefined) {
|
|
111
|
+
const { execSync } = await import('child_process');
|
|
112
|
+
const { loadAuth } = await import('./utils/config.js');
|
|
113
|
+
const filter = typeof options.models === 'string' ? options.models
|
|
114
|
+
: typeof options.model === 'string' ? options.model
|
|
115
|
+
: undefined;
|
|
116
|
+
const auth = await loadAuth();
|
|
117
|
+
const authedProviders = auth.entries.map(e => e.provider);
|
|
118
|
+
if (authedProviders.length === 0) {
|
|
119
|
+
console.log(chalk.yellow(' No authenticated providers. Run /login first.'));
|
|
120
|
+
process.exit(0);
|
|
121
|
+
}
|
|
122
|
+
const targets = filter ? [filter] : authedProviders;
|
|
123
|
+
const { fetchQwenModels } = await import('./utils/qwen-auth.js');
|
|
124
|
+
for (const provider of targets) {
|
|
125
|
+
try {
|
|
126
|
+
let models = [];
|
|
127
|
+
if (provider === 'qwen') {
|
|
128
|
+
models = await fetchQwenModels();
|
|
129
|
+
}
|
|
130
|
+
else if (provider === 'opencode') {
|
|
131
|
+
models = execSync('opencode models 2>/dev/null', { encoding: 'utf-8' }).trim().split('\n').filter(Boolean);
|
|
132
|
+
}
|
|
133
|
+
else if (provider === 'claude') {
|
|
134
|
+
const help = execSync('claude --help 2>/dev/null', { encoding: 'utf-8' });
|
|
135
|
+
models = help.match(/'([^']+)'/g)?.map(m => m.replace(/'/g, '')).filter(Boolean) ?? ['opus', 'sonnet', 'haiku'];
|
|
136
|
+
}
|
|
137
|
+
else if (provider === 'gemini') {
|
|
138
|
+
models = execSync('gemini models 2>/dev/null', { encoding: 'utf-8' }).trim().split('\n').filter(Boolean);
|
|
139
|
+
if (!models.length)
|
|
140
|
+
models = ['gemini-2.5-pro', 'gemini-2.5-flash'];
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
models = ['default'];
|
|
144
|
+
}
|
|
145
|
+
const email = auth.entries.find(e => e.provider === provider)?.email;
|
|
146
|
+
const label = email ? `${provider} (${email})` : provider;
|
|
147
|
+
console.log(chalk.bold.cyan(`\n ${label}:`));
|
|
148
|
+
models.forEach(m => console.log(chalk.dim(` ${m}`)));
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
console.log(chalk.dim(` ${provider}: (unavailable)`));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
console.log('');
|
|
155
|
+
process.exit(0);
|
|
156
|
+
}
|
|
102
157
|
// --continue: resume last session for current dir
|
|
103
158
|
if (options.continue) {
|
|
104
159
|
const session = await getLastSessionForDir(process.cwd());
|
package/dist/types.js
CHANGED
|
@@ -55,4 +55,36 @@ export const CLI_REGISTRY = {
|
|
|
55
55
|
promptFlag: '-p',
|
|
56
56
|
promptPosition: 'flag',
|
|
57
57
|
},
|
|
58
|
+
'agent-orch': {
|
|
59
|
+
name: 'Agent Orch',
|
|
60
|
+
command: 'agent-orch',
|
|
61
|
+
modelFlag: '--model',
|
|
62
|
+
extraFlags: '',
|
|
63
|
+
promptFlag: '',
|
|
64
|
+
promptPosition: 'arg',
|
|
65
|
+
},
|
|
66
|
+
'agent-impl': {
|
|
67
|
+
name: 'Agent Impl',
|
|
68
|
+
command: 'agent-impl',
|
|
69
|
+
modelFlag: '--model',
|
|
70
|
+
extraFlags: '',
|
|
71
|
+
promptFlag: '',
|
|
72
|
+
promptPosition: 'arg',
|
|
73
|
+
},
|
|
74
|
+
'agent-rev': {
|
|
75
|
+
name: 'Agent Rev',
|
|
76
|
+
command: 'agent-rev',
|
|
77
|
+
modelFlag: '--model',
|
|
78
|
+
extraFlags: '',
|
|
79
|
+
promptFlag: '',
|
|
80
|
+
promptPosition: 'arg',
|
|
81
|
+
},
|
|
82
|
+
'agent-explorer': {
|
|
83
|
+
name: 'Agent Explorer',
|
|
84
|
+
command: 'agent-explorer',
|
|
85
|
+
modelFlag: '--model',
|
|
86
|
+
extraFlags: '',
|
|
87
|
+
promptFlag: '',
|
|
88
|
+
promptPosition: 'arg',
|
|
89
|
+
},
|
|
58
90
|
};
|
package/dist/utils/config.d.ts
CHANGED
package/dist/utils/config.js
CHANGED
|
@@ -2,13 +2,9 @@ import { promises as fs } from 'fs';
|
|
|
2
2
|
import * as fsSync from 'fs';
|
|
3
3
|
import * as path from 'path';
|
|
4
4
|
import * as os from 'os';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
if (known.includes(binName)) {
|
|
9
|
-
return path.join(os.homedir(), `.${binName}`);
|
|
10
|
-
}
|
|
11
|
-
// dev mode (tsx src/index.ts) — read name from package.json
|
|
5
|
+
const KNOWN_PKGS = ['agent-mp', 'agent-orch', 'agent-impl', 'agent-rev', 'agent-explorer'];
|
|
6
|
+
function resolvePackageName() {
|
|
7
|
+
// 1. Read package.json (reliable for both global installs and dev mode)
|
|
12
8
|
const __filename = new URL(import.meta.url).pathname;
|
|
13
9
|
const __dirname = path.dirname(__filename);
|
|
14
10
|
for (const p of [
|
|
@@ -17,15 +13,19 @@ function getAgentHome() {
|
|
|
17
13
|
]) {
|
|
18
14
|
try {
|
|
19
15
|
const pkg = JSON.parse(fsSync.readFileSync(p, 'utf-8'));
|
|
20
|
-
if (pkg.name &&
|
|
21
|
-
return
|
|
22
|
-
}
|
|
16
|
+
if (pkg.name && KNOWN_PKGS.includes(pkg.name))
|
|
17
|
+
return pkg.name;
|
|
23
18
|
}
|
|
24
19
|
catch { }
|
|
25
20
|
}
|
|
26
|
-
|
|
21
|
+
// 2. Fallback: binary name (e.g. running from a symlink in PATH)
|
|
22
|
+
const bin = path.basename(process.argv[1]).replace(/\.(js|ts|mjs)$/, '');
|
|
23
|
+
if (KNOWN_PKGS.includes(bin))
|
|
24
|
+
return bin;
|
|
25
|
+
return 'agent-mp';
|
|
27
26
|
}
|
|
28
|
-
export const
|
|
27
|
+
export const PKG_NAME = resolvePackageName();
|
|
28
|
+
export const AGENT_HOME = path.join(os.homedir(), `.${PKG_NAME}`);
|
|
29
29
|
export const AUTH_FILE = path.join(AGENT_HOME, 'auth.json');
|
|
30
30
|
export const CONFIG_FILE = path.join(AGENT_HOME, 'config.json');
|
|
31
31
|
export async function loadAuth() {
|
|
@@ -8,5 +8,6 @@ export declare function qwenAuthStatus(): Promise<{
|
|
|
8
8
|
authenticated: boolean;
|
|
9
9
|
email?: string;
|
|
10
10
|
}>;
|
|
11
|
+
export declare function fetchQwenModels(): Promise<string[]>;
|
|
11
12
|
export declare function getQwenAccessToken(): Promise<string | null>;
|
|
12
13
|
export declare function callQwenAPI(prompt: string, model?: string): Promise<string>;
|
package/dist/utils/qwen-auth.js
CHANGED
|
@@ -216,6 +216,23 @@ export async function qwenAuthStatus() {
|
|
|
216
216
|
return { authenticated: false };
|
|
217
217
|
return { authenticated: true };
|
|
218
218
|
}
|
|
219
|
+
export async function fetchQwenModels() {
|
|
220
|
+
const token = await loadToken();
|
|
221
|
+
if (!token)
|
|
222
|
+
return [];
|
|
223
|
+
try {
|
|
224
|
+
const res = await fetch('https://chat.qwen.ai/api/models', {
|
|
225
|
+
headers: { Authorization: `Bearer ${token.accessToken}` },
|
|
226
|
+
});
|
|
227
|
+
if (!res.ok)
|
|
228
|
+
return [];
|
|
229
|
+
const data = await res.json();
|
|
230
|
+
return (data.data ?? []).map((m) => m.id).filter(Boolean);
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
return [];
|
|
234
|
+
}
|
|
235
|
+
}
|
|
219
236
|
export async function getQwenAccessToken() {
|
|
220
237
|
const token = await loadToken();
|
|
221
238
|
return token?.accessToken || null;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-rev",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Reviewer agent — validates and reviews
|
|
3
|
+
"version": "0.2.3",
|
|
4
|
+
"description": "Reviewer agent — validates and reviews code written by the implementor",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"files": [
|
|
@@ -15,14 +15,7 @@
|
|
|
15
15
|
"dev": "tsx src/index.ts",
|
|
16
16
|
"prepublishOnly": "npm run build"
|
|
17
17
|
},
|
|
18
|
-
"keywords": [
|
|
19
|
-
"ai",
|
|
20
|
-
"agent",
|
|
21
|
-
"orchestrator",
|
|
22
|
-
"multi-agent",
|
|
23
|
-
"cli",
|
|
24
|
-
"coding"
|
|
25
|
-
],
|
|
18
|
+
"keywords": ["ai", "agent", "reviewer", "multi-agent", "cli", "coding"],
|
|
26
19
|
"license": "MIT",
|
|
27
20
|
"dependencies": {
|
|
28
21
|
"@anthropic-ai/sdk": "^0.39.0",
|