agent-rev 0.5.14 → 0.5.24
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 +0 -2
- package/dist/commands/repl.js +196 -233
- package/dist/commands/setup.js +64 -6
- package/dist/core/engine.js +265 -172
- package/dist/index.js +26 -19
- package/dist/ui/input.d.ts +23 -25
- package/dist/ui/input.js +310 -248
- package/dist/utils/qwen-auth.d.ts +14 -4
- package/dist/utils/qwen-auth.js +81 -8
- package/package.json +44 -1
package/dist/commands/repl.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import * as readline from 'readline';
|
|
2
1
|
import { CliInfo } from '../types.js';
|
|
3
2
|
import { Session } from '../utils/sessions.js';
|
|
4
3
|
/**
|
|
@@ -17,7 +16,6 @@ export declare function initCoordinator(): Promise<{
|
|
|
17
16
|
info: CliInfo;
|
|
18
17
|
path: string;
|
|
19
18
|
}>;
|
|
20
|
-
rl: readline.Interface;
|
|
21
19
|
} | null>;
|
|
22
20
|
/** REPL mode — interactive loop */
|
|
23
21
|
export declare function runRepl(resumeSession?: Session): Promise<void>;
|
package/dist/commands/repl.js
CHANGED
|
@@ -10,7 +10,7 @@ import { writeJson, ensureDir, readJson, listDir, fileExists } from '../utils/fs
|
|
|
10
10
|
import { loadAuth, saveAuth, loadCliConfig, saveCliConfig, loadProjectConfig, resolveProjectDir } from '../utils/config.js';
|
|
11
11
|
import { log } from '../utils/logger.js';
|
|
12
12
|
import { AgentEngine, ExitError } from '../core/engine.js';
|
|
13
|
-
import {
|
|
13
|
+
import { qwenAuthStatus, QWEN_AGENT_HOME, fetchQwenModels, loadApiKeyConfig, saveApiKeyConfig, fetchApiKeyModels, DEFAULT_API_BASE_URL } 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';
|
|
@@ -256,55 +256,52 @@ function buildCmd(cliName, model) {
|
|
|
256
256
|
const promptFlag = info.promptFlag ? ` ${info.promptFlag}` : '';
|
|
257
257
|
return `${info.command} ${info.modelFlag} ${model}${extra}${promptFlag}`.trim();
|
|
258
258
|
}
|
|
259
|
-
async function
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
if (auth.activeProvider === 'qwen') {
|
|
264
|
-
console.log(chalk.dim(` Credentials will be saved to: ${QWEN_AGENT_HOME}/\n`));
|
|
265
|
-
try {
|
|
266
|
-
const success = await qwenLogin();
|
|
267
|
-
if (!success)
|
|
268
|
-
return false;
|
|
269
|
-
const emailInput = await ask(rl, ' Your email (optional, for display): ');
|
|
270
|
-
auth.entries = auth.entries.filter((e) => e.provider !== 'qwen');
|
|
271
|
-
auth.entries.push({ provider: 'qwen', method: 'oauth', ...(emailInput.trim() ? { email: emailInput.trim() } : {}) });
|
|
272
|
-
await saveAuth(auth);
|
|
273
|
-
return true;
|
|
274
|
-
}
|
|
275
|
-
catch (err) {
|
|
276
|
-
console.log(chalk.red(' Login failed: ' + err.message));
|
|
277
|
-
return false;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
// For other providers, use their CLI
|
|
281
|
-
console.log(chalk.dim(' Available providers:'));
|
|
282
|
-
for (const [id, cmd] of Object.entries(CLI_AUTH_COMMANDS)) {
|
|
283
|
-
const installed = isCliInstalled(id) ? chalk.green('✓') : chalk.red('✗');
|
|
284
|
-
console.log(chalk.dim(` ${installed} ${id} - uses: ${cmd}`));
|
|
259
|
+
async function promptApiKeySetup(rl, askFn) {
|
|
260
|
+
const existing = await loadApiKeyConfig();
|
|
261
|
+
if (existing) {
|
|
262
|
+
console.log(chalk.dim(` Current: ${existing.provider} / ${existing.model}`));
|
|
285
263
|
}
|
|
286
|
-
const
|
|
287
|
-
const
|
|
288
|
-
if (!
|
|
289
|
-
console.log(chalk.red(
|
|
264
|
+
const apiKey = await askFn(` API Key${existing ? ' [Enter to keep]' : ''}: `);
|
|
265
|
+
const resolvedKey = apiKey.trim() || existing?.api_key || '';
|
|
266
|
+
if (!resolvedKey) {
|
|
267
|
+
console.log(chalk.red(' API key is required.'));
|
|
290
268
|
return false;
|
|
291
269
|
}
|
|
292
|
-
console.log(chalk.
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
270
|
+
console.log(chalk.dim('\n Fetching available models...'));
|
|
271
|
+
const tempCfg = { provider: 'openai-compatible', api_key: resolvedKey, base_url: DEFAULT_API_BASE_URL, model: '' };
|
|
272
|
+
const models = await fetchApiKeyModels(tempCfg);
|
|
273
|
+
let chosenModel = existing?.model ?? 'qwen-plus';
|
|
274
|
+
if (models.length > 0) {
|
|
275
|
+
console.log(chalk.bold('\n Available models:'));
|
|
276
|
+
models.forEach((m, i) => console.log(chalk.dim(` ${i + 1}. ${m}`)));
|
|
277
|
+
const pick = await askFn(`\n Model [${chosenModel}]: `);
|
|
278
|
+
const num = parseInt(pick.trim());
|
|
279
|
+
if (!isNaN(num) && num >= 1 && num <= models.length) {
|
|
280
|
+
chosenModel = models[num - 1];
|
|
281
|
+
}
|
|
282
|
+
else if (pick.trim()) {
|
|
283
|
+
chosenModel = pick.trim();
|
|
284
|
+
}
|
|
303
285
|
}
|
|
304
|
-
|
|
305
|
-
console.log(chalk.
|
|
306
|
-
|
|
286
|
+
else {
|
|
287
|
+
console.log(chalk.yellow(' Could not fetch models — enter model name manually.'));
|
|
288
|
+
const pick = await askFn(` Model [${chosenModel}]: `);
|
|
289
|
+
if (pick.trim())
|
|
290
|
+
chosenModel = pick.trim();
|
|
307
291
|
}
|
|
292
|
+
const cfg = {
|
|
293
|
+
provider: existing?.provider ?? 'openai-compatible',
|
|
294
|
+
api_key: resolvedKey,
|
|
295
|
+
base_url: DEFAULT_API_BASE_URL,
|
|
296
|
+
model: chosenModel,
|
|
297
|
+
};
|
|
298
|
+
await saveApiKeyConfig(cfg);
|
|
299
|
+
console.log(chalk.green(`\n ✓ API key saved — ${cfg.model}\n`));
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
async function cmdLogin(rl) {
|
|
303
|
+
console.log(chalk.bold.cyan('\n Configure API Key\n'));
|
|
304
|
+
return promptApiKeySetup(rl, (q) => ask(rl, q));
|
|
308
305
|
}
|
|
309
306
|
async function cmdSetup(rl) {
|
|
310
307
|
console.log(chalk.bold.cyan('\n Setup Wizard\n'));
|
|
@@ -405,12 +402,15 @@ async function cmdSetup(rl) {
|
|
|
405
402
|
await writeJson(path.join(projectDir, '.agent', 'config.json'), agentConfig);
|
|
406
403
|
console.log(chalk.green(' .agent/config.json generated'));
|
|
407
404
|
const templates = {
|
|
408
|
-
'.agent/INDEX.md': `# ${projectName}
|
|
409
|
-
'.agent/rules/workflow.md':
|
|
410
|
-
'.agent/rules/
|
|
411
|
-
'.agent/rules/
|
|
412
|
-
'.agent/
|
|
413
|
-
'.agent/
|
|
405
|
+
'.agent/INDEX.md': `# ${projectName} — Agent Entry Point\n\n> Leé este archivo antes de cualquier accion.\n\n## Proyecto\n${projectName}\n\n## Navegacion por rol\n| Si sos... | Lee primero... | Luego... |\n|-----------|----------------|----------|\n| ORCHESTRATOR | .agent/rules/workflow.md | .agent/rules/orchestrator.md |\n| IMPLEMENTOR | .agent/rules/implementor.md | .agent/tasks/{ID}/plan.json |\n| REVIEWER | .agent/rules/reviewer.md | .agent/rules/workflow.md |\n\n## Contexto del proyecto\n- Arquitectura: .agent/context/architecture.md\n- Stack: ver .agent/config.json\n`,
|
|
406
|
+
'.agent/rules/workflow.md': `# Workflow Contract — ${projectName}\n\n## Flujo general\n\`\`\`\n[ORCHESTRATOR] planifica → genera plan.json\n ↓\n[IMPLEMENTOR] ejecuta el plan → modifica archivos\n ↓\n[REVIEWER] valida → genera result.md\n\`\`\`\n\n## REGLA #1 — Separacion de roles\nCada CLI opera SOLO en su rol. Nunca ejecuta acciones de otro rol.\n\n## REGLA #2 — Orden estricto\nOrchestrator siempre primero. Implementor no actua sin plan. Reviewer no actua sin implementacion.\n\n## REGLA #3 — Sin inventar\nCada rol trabaja con los archivos reales del proyecto. No asume ni infiere sin leer.\n`,
|
|
407
|
+
'.agent/rules/orchestrator.md': `# Rol: ORCHESTRATOR — ${projectName}\n\n## Responsabilidad\nAnalizar el requerimiento, leer el contexto del proyecto y generar un plan de tareas claro y ejecutable.\n\n## Antes de planificar\n1. Leer .agent/context/architecture.md para entender los componentes\n2. Leer .agent/rules/structure.md para saber qué dirs son validos\n3. Leer .agent/rules/patterns.md para respetar convenciones del proyecto\n\n## Al generar el plan\n- Dividir el trabajo en pasos atomicos (1 paso = 1 cambio verificable)\n- Especificar rutas de archivos REALES (no genericas)\n- Indicar dependencias entre pasos si las hay\n\n## Prohibido\n- Modificar archivos de codigo directamente\n- Ejecutar comandos de build o test\n`,
|
|
408
|
+
'.agent/rules/implementor.md': `# Rol: IMPLEMENTOR — ${projectName}\n\n## Responsabilidad\nEjecutar el plan generado por el Orchestrator. Modificar archivos de codigo siguiendo las instrucciones exactas del plan.\n\n## Antes de implementar\n1. Leer .agent/tasks/{ID}/plan.json completo\n2. Leer .agent/rules/structure.md — no crear archivos en dirs prohibidos\n3. Leer .agent/rules/patterns.md — respetar convenciones\n\n## Al implementar\n- Seguir el plan paso a paso, en orden\n- No agregar cambios fuera del scope del plan\n- Marcar cada paso como completado al terminarlo\n\n## Prohibido\n- Modificar el plan\n- Hacer refactors no solicitados\n`,
|
|
409
|
+
'.agent/rules/reviewer.md': `# Rol: REVIEWER — ${projectName}\n\n## Responsabilidad\nValidar que la implementacion cumple el plan y respeta las reglas del proyecto.\n\n## Checklist de revision\n- [ ] Cada paso del plan fue implementado\n- [ ] No se modificaron archivos fuera del scope\n- [ ] Se respetan las convenciones de .agent/rules/patterns.md\n- [ ] La estructura cumple .agent/rules/structure.md\n- [ ] No hay codigo muerto ni imports sin usar\n\n## Al generar result.md\n- Indicar cada item como OK / FAIL / SKIP con razon\n- Si hay FAILs: describir exactamente que falta y en que archivo\n\n## Prohibido\n- Modificar archivos de codigo directamente\n- Aprobar implementaciones incompletas\n`,
|
|
410
|
+
'.agent/rules/structure.md': `# Estructura del proyecto — ${projectName}\n\n## Directorios aprobados\n- . (raiz del proyecto)\n\n## Directorios prohibidos\n- node_modules/\n- dist/\n- .git/\n- build/\n- target/\n- __pycache__/\n`,
|
|
411
|
+
'.agent/rules/patterns.md': `# Patrones y convenciones — ${projectName}\n\n## Nombrado\n- Archivos: kebab-case\n- Clases: PascalCase\n- Variables y funciones: camelCase\n- Constantes: UPPER_SNAKE_CASE\n\n## Reglas generales\n- No dejar console.log de debug en codigo productivo\n- Un archivo = una responsabilidad principal\n`,
|
|
412
|
+
'.agent/context/architecture.md': '# Arquitectura — NIVEL 0\n\n> Ejecuta `agent-mp explorer` para generar documentacion automatica.\n\n| Componente | Stack | Puerto | Proposito |\n|------------|-------|--------|-----------|\n| - | - | - | - |\n',
|
|
413
|
+
'.agent/AGENT.md': `# Instrucciones para Sub-Agentes — ${projectName}\n\n## Paso 1: Identificar tu rol\nLee .agent/INDEX.md para saber que archivo leer segun tu rol.\n\n## Paso 2: Leer el contexto\nAntes de cualquier accion, lee .agent/context/architecture.md.\n\n## Paso 3: Actua SOLO dentro de tu rol\nVer .agent/rules/workflow.md para las reglas de separacion de roles.\n`,
|
|
414
414
|
};
|
|
415
415
|
for (const [fp, content] of Object.entries(templates)) {
|
|
416
416
|
await fs.mkdir(path.join(projectDir, path.dirname(fp)), { recursive: true });
|
|
@@ -751,7 +751,7 @@ function cmdHelp(fi) {
|
|
|
751
751
|
{ key: '/explorer [task]', value: 'Run explorer (shortcut)' },
|
|
752
752
|
{ key: '/models', value: 'List models for all installed CLIs' },
|
|
753
753
|
{ key: '/models <cli>', value: 'List models for a specific CLI' },
|
|
754
|
-
{ key: '/login', value: '
|
|
754
|
+
{ key: '/login', value: 'Configure API key' },
|
|
755
755
|
{ key: '/logout', value: 'Logout and clear credentials' },
|
|
756
756
|
{ key: '/auth-status', value: 'Show authentication status' },
|
|
757
757
|
{ key: '/usage', value: 'Check quota status for all role CLIs' },
|
|
@@ -795,112 +795,59 @@ export async function initCoordinator() {
|
|
|
795
795
|
input: process.stdin,
|
|
796
796
|
output: process.stdout,
|
|
797
797
|
});
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
return null;
|
|
804
|
-
}
|
|
805
|
-
// Step 2: Pick coordinator
|
|
806
|
-
const auth = await loadAuth();
|
|
807
|
-
const currentAuth = auth.activeProvider;
|
|
808
|
-
let activeCli = installed.find((c) => c.name === currentAuth);
|
|
809
|
-
// Check if corporate credentials exist for qwen
|
|
810
|
-
const hasCorporateCreds = (() => {
|
|
811
|
-
if (currentAuth === 'qwen') {
|
|
812
|
-
const corporateCreds = path.join(QWEN_AGENT_HOME, 'oauth_creds.json');
|
|
813
|
-
try {
|
|
814
|
-
const stats = fsSync.statSync(corporateCreds);
|
|
815
|
-
return stats.isFile();
|
|
816
|
-
}
|
|
817
|
-
catch {
|
|
818
|
-
return false;
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
return true;
|
|
822
|
-
})();
|
|
823
|
-
// If no active provider OR no corporate credentials, show selector
|
|
824
|
-
if (!activeCli || !hasCorporateCreds) {
|
|
825
|
-
// No coordinator configured - show selector
|
|
826
|
-
const qwenInstalled = installed.find((c) => c.name === 'qwen');
|
|
827
|
-
if (!qwenInstalled) {
|
|
828
|
-
console.log(chalk.red(' Qwen CLI not found. Install with: npm install -g @qwen-code/qwen-code'));
|
|
829
|
-
rl.close();
|
|
798
|
+
try {
|
|
799
|
+
// Step 1: Detect installed CLIs
|
|
800
|
+
const installed = detectInstalledClis();
|
|
801
|
+
if (installed.length === 0) {
|
|
802
|
+
console.log(chalk.red(' No CLIs detected. Install at least one: qwen, claude, gemini, codex'));
|
|
830
803
|
return null;
|
|
831
804
|
}
|
|
832
|
-
//
|
|
833
|
-
const
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
try {
|
|
850
|
-
// For qwen, do corporate login flow
|
|
851
|
-
if (activeCli.name === 'qwen') {
|
|
852
|
-
const personalCreds = path.join(os.homedir(), '.qwen', 'oauth_creds.json');
|
|
853
|
-
const corporateCreds = path.join(QWEN_AGENT_HOME, 'oauth_creds.json');
|
|
854
|
-
let personalBackup = null;
|
|
855
|
-
if (await fileExists(personalCreds)) {
|
|
856
|
-
personalBackup = await fs.readFile(personalCreds, 'utf-8');
|
|
857
|
-
await fs.unlink(personalCreds);
|
|
858
|
-
}
|
|
859
|
-
await fs.unlink(corporateCreds).catch(() => { });
|
|
860
|
-
execSync('qwen auth qwen-oauth', { stdio: 'inherit' });
|
|
861
|
-
if (await fileExists(personalCreds)) {
|
|
862
|
-
const newCreds = await fs.readFile(personalCreds, 'utf-8');
|
|
863
|
-
await fs.writeFile(corporateCreds, newCreds);
|
|
864
|
-
}
|
|
865
|
-
if (personalBackup) {
|
|
866
|
-
await fs.writeFile(personalCreds, personalBackup);
|
|
867
|
-
}
|
|
868
|
-
authResult = await checkCliAuth(activeCli.name);
|
|
869
|
-
}
|
|
870
|
-
else {
|
|
871
|
-
execSync(authCmd, { stdio: 'inherit' });
|
|
872
|
-
authResult = await checkCliAuth(activeCli.name);
|
|
873
|
-
}
|
|
874
|
-
if (authResult.ok) {
|
|
875
|
-
console.log(chalk.green(` ${activeCli.name} authenticated successfully\n`));
|
|
876
|
-
}
|
|
877
|
-
else {
|
|
878
|
-
console.log(chalk.red(` ${activeCli.name} auth failed. Try /login.\n`));
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
catch {
|
|
882
|
-
console.log(chalk.yellow(` Login interrupted. Type /login to retry.\n`));
|
|
883
|
-
}
|
|
805
|
+
// Step 2: Pick coordinator
|
|
806
|
+
const auth = await loadAuth();
|
|
807
|
+
const currentAuth = auth.activeProvider;
|
|
808
|
+
let activeCli = installed.find((c) => c.name === currentAuth);
|
|
809
|
+
// Fast-path: API key configured
|
|
810
|
+
const apiKeyCfg = await loadApiKeyConfig();
|
|
811
|
+
if (apiKeyCfg?.api_key) {
|
|
812
|
+
const cliCfg = await loadCliConfig();
|
|
813
|
+
const model = cliCfg.coordinatorModel || apiKeyCfg.model;
|
|
814
|
+
gCoordinatorCmd = `qwen-direct -m ${model}`;
|
|
815
|
+
console.log(chalk.green(` ✓ Auth: ${apiKeyCfg.provider} / ${model}\n`));
|
|
816
|
+
const syntheticCli = {
|
|
817
|
+
name: apiKeyCfg.provider,
|
|
818
|
+
info: { command: 'qwen-direct', modelFlag: '-m', promptFlag: '-p', description: 'API key' },
|
|
819
|
+
path: 'qwen-direct',
|
|
820
|
+
};
|
|
821
|
+
return { activeCli: syntheticCli, installed, coordinatorCmd: gCoordinatorCmd };
|
|
884
822
|
}
|
|
823
|
+
// No API key — prompt to configure one
|
|
824
|
+
console.log(chalk.yellow('\n No auth configured.'));
|
|
825
|
+
console.log(chalk.dim(' Configure an API key to continue.\n'));
|
|
826
|
+
const doSetup = await new Promise((resolve) => {
|
|
827
|
+
rl.question(' Set up API key now? (y/N): ', (a) => resolve(a.trim().toLowerCase() === 'y'));
|
|
828
|
+
});
|
|
829
|
+
if (doSetup) {
|
|
830
|
+
const askFn = (q) => new Promise((resolve) => rl.question(q, resolve));
|
|
831
|
+
const ok = await promptApiKeySetup(rl, askFn);
|
|
832
|
+
if (!ok)
|
|
833
|
+
return null;
|
|
834
|
+
const saved = await loadApiKeyConfig();
|
|
835
|
+
if (!saved)
|
|
836
|
+
return null;
|
|
837
|
+
gCoordinatorCmd = `qwen-direct -m ${saved.model}`;
|
|
838
|
+
const syntheticCli = {
|
|
839
|
+
name: saved.provider,
|
|
840
|
+
info: { command: 'qwen-direct', modelFlag: '-m', promptFlag: '-p', description: 'API key' },
|
|
841
|
+
path: 'qwen-direct',
|
|
842
|
+
};
|
|
843
|
+
return { coordinatorCmd: gCoordinatorCmd, activeCli: syntheticCli, installed };
|
|
844
|
+
}
|
|
845
|
+
console.log(chalk.dim(' Run: agent-mp setup api-key'));
|
|
846
|
+
return null;
|
|
885
847
|
}
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
console.log(chalk.green(` ${activeCli.name} session active${emailStr}\n`));
|
|
889
|
-
}
|
|
890
|
-
// Save email to auth store if we have it
|
|
891
|
-
if (authResult.email) {
|
|
892
|
-
const auth2 = await loadAuth();
|
|
893
|
-
const entry = auth2.entries.find((e) => e.provider === activeCli.name);
|
|
894
|
-
if (entry)
|
|
895
|
-
entry.email = authResult.email;
|
|
896
|
-
await saveAuth(auth2);
|
|
848
|
+
finally {
|
|
849
|
+
rl.close();
|
|
897
850
|
}
|
|
898
|
-
// Build coordinator command using the ACTIVE CLI (not orchestrator)
|
|
899
|
-
// The coordinator converses naturally using whatever CLI is active (qwen, claude, etc.)
|
|
900
|
-
const cliCfg = await loadCliConfig();
|
|
901
|
-
const activeModel = cliCfg.coordinatorModel || detectModels(activeCli.name)[0] || 'default';
|
|
902
|
-
gCoordinatorCmd = buildCmd(activeCli.name, activeModel);
|
|
903
|
-
return { coordinatorCmd: gCoordinatorCmd, activeCli, installed, rl };
|
|
904
851
|
}
|
|
905
852
|
/** REPL mode — interactive loop */
|
|
906
853
|
export async function runRepl(resumeSession) {
|
|
@@ -908,7 +855,6 @@ export async function runRepl(resumeSession) {
|
|
|
908
855
|
if (!init)
|
|
909
856
|
return;
|
|
910
857
|
const { activeCli } = init;
|
|
911
|
-
const rl = init.rl;
|
|
912
858
|
const session = resumeSession ?? newSession(process.cwd());
|
|
913
859
|
// Resolve actual project root (may be deeper than cwd)
|
|
914
860
|
const projectDir = await resolveProjectDir(process.cwd());
|
|
@@ -931,10 +877,48 @@ export async function runRepl(resumeSession) {
|
|
|
931
877
|
}
|
|
932
878
|
}
|
|
933
879
|
catch { }
|
|
880
|
+
// Fixed-bottom input — owns the last terminal rows
|
|
881
|
+
const fi = new FixedInput();
|
|
882
|
+
fi.setup();
|
|
883
|
+
// Helper: run a wizard command that needs readline (suspends fixed input)
|
|
884
|
+
const withRl = async (fn) => {
|
|
885
|
+
const resume = fi.suspend();
|
|
886
|
+
const tempRl = readline.createInterface({
|
|
887
|
+
input: process.stdin,
|
|
888
|
+
output: process.stdout,
|
|
889
|
+
});
|
|
890
|
+
try {
|
|
891
|
+
await fn(tempRl);
|
|
892
|
+
}
|
|
893
|
+
finally {
|
|
894
|
+
tempRl.close();
|
|
895
|
+
resume();
|
|
896
|
+
}
|
|
897
|
+
};
|
|
898
|
+
// Helper to create engine with readline (suspends fixed input)
|
|
899
|
+
const withEngine = async (fn) => {
|
|
900
|
+
const resume = fi.suspend();
|
|
901
|
+
const tempRl = readline.createInterface({
|
|
902
|
+
input: process.stdin,
|
|
903
|
+
output: process.stdout,
|
|
904
|
+
});
|
|
905
|
+
try {
|
|
906
|
+
const dir = await resolveProjectDir(process.cwd());
|
|
907
|
+
const config = await loadProjectConfig(dir);
|
|
908
|
+
const engine = new AgentEngine(config, dir, gCoordinatorCmd, tempRl, fi, handleCmd);
|
|
909
|
+
return await fn(engine);
|
|
910
|
+
}
|
|
911
|
+
finally {
|
|
912
|
+
tempRl.close();
|
|
913
|
+
resume();
|
|
914
|
+
}
|
|
915
|
+
};
|
|
934
916
|
if (!hasRoles) {
|
|
935
917
|
console.log(chalk.yellow('\n Todos los roles deben estar configurados antes de trabajar.'));
|
|
936
918
|
console.log(chalk.yellow(' Configurando roles ahora...\n'));
|
|
937
|
-
await
|
|
919
|
+
await withRl(async (tempRl) => {
|
|
920
|
+
await cmdConfigMulti(tempRl);
|
|
921
|
+
});
|
|
938
922
|
// Re-check
|
|
939
923
|
try {
|
|
940
924
|
const config = await loadProjectConfig(process.cwd());
|
|
@@ -952,7 +936,6 @@ export async function runRepl(resumeSession) {
|
|
|
952
936
|
catch { }
|
|
953
937
|
if (!hasRoles) {
|
|
954
938
|
console.log(chalk.red('\n Error: roles no configurados. Saliendo.'));
|
|
955
|
-
rl.close();
|
|
956
939
|
return;
|
|
957
940
|
}
|
|
958
941
|
}
|
|
@@ -984,9 +967,6 @@ export async function runRepl(resumeSession) {
|
|
|
984
967
|
}
|
|
985
968
|
}
|
|
986
969
|
catch { }
|
|
987
|
-
// Fixed-bottom input — owns the last 3 terminal rows permanently
|
|
988
|
-
const fi = new FixedInput();
|
|
989
|
-
fi.setup();
|
|
990
970
|
// If resuming, print past conversation history
|
|
991
971
|
if (resumeSession && resumeSession.messages.length > 0) {
|
|
992
972
|
const date = new Date(resumeSession.createdAt).toLocaleString();
|
|
@@ -1006,16 +986,6 @@ export async function runRepl(resumeSession) {
|
|
|
1006
986
|
fi.println(chalk.dim(' ─── Continue below ───'));
|
|
1007
987
|
fi.println('');
|
|
1008
988
|
}
|
|
1009
|
-
// Helper: run a wizard command that needs readline (suspends fixed input)
|
|
1010
|
-
const withRl = async (fn) => {
|
|
1011
|
-
const resume = fi.suspend();
|
|
1012
|
-
try {
|
|
1013
|
-
await fn(rl);
|
|
1014
|
-
}
|
|
1015
|
-
finally {
|
|
1016
|
-
resume();
|
|
1017
|
-
}
|
|
1018
|
-
};
|
|
1019
989
|
// Command handler — used by the main loop AND by the engine during mid-conversation
|
|
1020
990
|
const handleCmd = async (trimmed) => {
|
|
1021
991
|
const parts = trimmed.slice(1).split(/\s+/);
|
|
@@ -1043,58 +1013,53 @@ export async function runRepl(resumeSession) {
|
|
|
1043
1013
|
await cmdStatus(fi);
|
|
1044
1014
|
break;
|
|
1045
1015
|
case 'run': {
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1016
|
+
await withEngine(async (engine) => {
|
|
1017
|
+
const dir = await resolveProjectDir(process.cwd());
|
|
1018
|
+
const config = await loadProjectConfig(dir);
|
|
1019
|
+
if (args[0] === 'orch' || args[0] === 'orchestrator') {
|
|
1020
|
+
// Ensure documentation exists before orchestrator runs
|
|
1021
|
+
const contextDir = path.join(dir, '.agent', 'context');
|
|
1022
|
+
const archPath = path.join(contextDir, 'architecture.md');
|
|
1023
|
+
if (!(await fileExists(archPath))) {
|
|
1024
|
+
fi.println(chalk.yellow('\n ── Generando documentación del proyecto ──'));
|
|
1025
|
+
fi.println(chalk.dim(' El orquestador necesita conocer la estructura del proyecto primero.\n'));
|
|
1026
|
+
await engine.runExplorer('Document the complete project structure, services, dependencies, and entry points. Create/update .agent/context/architecture.md');
|
|
1027
|
+
fi.println(chalk.green(' ✓ Documentación del proyecto generada.\n'));
|
|
1028
|
+
}
|
|
1029
|
+
const task = args.slice(1).join(' ');
|
|
1030
|
+
const result = await engine.runOrchestrator(task);
|
|
1031
|
+
fi.println(chalk.green(` Task ID: ${result.taskId}`));
|
|
1058
1032
|
}
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
const engine = new AgentEngine(config, dir, gCoordinatorCmd, rl, fi, handleCmd);
|
|
1082
|
-
await engine.runExplorer(task);
|
|
1083
|
-
}
|
|
1084
|
-
else {
|
|
1085
|
-
const task = args.join(' ');
|
|
1086
|
-
const engine = new AgentEngine(config, dir, gCoordinatorCmd, rl, fi, handleCmd);
|
|
1087
|
-
await engine.runFullCycle(task);
|
|
1088
|
-
}
|
|
1033
|
+
else if (args[0] === 'impl' || args[0] === 'implementor') {
|
|
1034
|
+
const taskId = args[1];
|
|
1035
|
+
const taskDir = path.join(dir, '.agent', 'tasks', taskId);
|
|
1036
|
+
const plan = await readJson(path.join(taskDir, 'plan.json'));
|
|
1037
|
+
await engine.runImplementor(taskId, plan);
|
|
1038
|
+
}
|
|
1039
|
+
else if (args[0] === 'rev' || args[0] === 'reviewer') {
|
|
1040
|
+
const taskId = args[1];
|
|
1041
|
+
const taskDir = path.join(dir, '.agent', 'tasks', taskId);
|
|
1042
|
+
const plan = await readJson(path.join(taskDir, 'plan.json'));
|
|
1043
|
+
const progress = await readJson(path.join(taskDir, 'progress.json'));
|
|
1044
|
+
await engine.runReviewer(taskId, plan, progress);
|
|
1045
|
+
}
|
|
1046
|
+
else if (args[0] === 'exp' || args[0] === 'explorer') {
|
|
1047
|
+
const task = args.slice(1).join(' ');
|
|
1048
|
+
await engine.runExplorer(task);
|
|
1049
|
+
}
|
|
1050
|
+
else {
|
|
1051
|
+
const task = args.join(' ');
|
|
1052
|
+
await engine.runFullCycle(task);
|
|
1053
|
+
}
|
|
1054
|
+
});
|
|
1089
1055
|
break;
|
|
1090
1056
|
}
|
|
1091
1057
|
case 'explorer':
|
|
1092
1058
|
case 'exp': {
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
await engine.runExplorer(task);
|
|
1059
|
+
await withEngine(async (engine) => {
|
|
1060
|
+
const task = args.join(' ');
|
|
1061
|
+
await engine.runExplorer(task);
|
|
1062
|
+
});
|
|
1098
1063
|
break;
|
|
1099
1064
|
}
|
|
1100
1065
|
case 'models':
|
|
@@ -1105,15 +1070,15 @@ export async function runRepl(resumeSession) {
|
|
|
1105
1070
|
await withRl(async (rl) => { await cmdLogin(rl); });
|
|
1106
1071
|
break;
|
|
1107
1072
|
case 'logout': {
|
|
1108
|
-
const
|
|
1109
|
-
await fs.unlink(
|
|
1073
|
+
const { getApiKeyConfigPath } = await import('../utils/qwen-auth.js');
|
|
1074
|
+
await fs.unlink(path.join(QWEN_AGENT_HOME, 'oauth_creds.json')).catch(() => { });
|
|
1075
|
+
await fs.unlink(await getApiKeyConfigPath()).catch(() => { });
|
|
1110
1076
|
const authStore = await loadAuth();
|
|
1111
1077
|
authStore.entries = [];
|
|
1112
1078
|
delete authStore.activeProvider;
|
|
1113
1079
|
await saveAuth(authStore);
|
|
1114
1080
|
fi.println(chalk.dim(' Logged out. Credentials cleared.'));
|
|
1115
1081
|
fi.teardown();
|
|
1116
|
-
rl.close();
|
|
1117
1082
|
throw new ExitError();
|
|
1118
1083
|
}
|
|
1119
1084
|
case 'auth-status':
|
|
@@ -1137,7 +1102,6 @@ export async function runRepl(resumeSession) {
|
|
|
1137
1102
|
case 'quit':
|
|
1138
1103
|
fi.println(chalk.dim(' Bye!'));
|
|
1139
1104
|
fi.teardown();
|
|
1140
|
-
rl.close();
|
|
1141
1105
|
throw new ExitError();
|
|
1142
1106
|
default:
|
|
1143
1107
|
fi.println(chalk.red(` Unknown command: /${cmd}`));
|
|
@@ -1172,17 +1136,17 @@ export async function runRepl(resumeSession) {
|
|
|
1172
1136
|
}
|
|
1173
1137
|
else {
|
|
1174
1138
|
// Before running the orchestrator, ensure project documentation exists
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1139
|
+
await withEngine(async (engine) => {
|
|
1140
|
+
const contextDir = path.join(dir, '.agent', 'context');
|
|
1141
|
+
const archPath = path.join(contextDir, 'architecture.md');
|
|
1142
|
+
if (!(await fileExists(archPath))) {
|
|
1143
|
+
fi.println(chalk.yellow('\n ── Generando documentación del proyecto ──'));
|
|
1144
|
+
fi.println(chalk.dim(' El orquestador necesita conocer la estructura del proyecto primero.\n'));
|
|
1145
|
+
await engine.runExplorer('Document the complete project structure, services, dependencies, and entry points. Create/update .agent/context/architecture.md');
|
|
1146
|
+
fi.println(chalk.green(' ✓ Documentación del proyecto generada.\n'));
|
|
1147
|
+
}
|
|
1148
|
+
await engine.runFullCycle(trimmed);
|
|
1149
|
+
});
|
|
1186
1150
|
session.messages.push({ role: 'agent', content: `[task cycle completed for: ${trimmed}]`, ts: new Date().toISOString() });
|
|
1187
1151
|
await saveSession(session);
|
|
1188
1152
|
}
|
|
@@ -1298,7 +1262,7 @@ export async function runRole(role, arg, model) {
|
|
|
1298
1262
|
if (!init)
|
|
1299
1263
|
process.exit(1);
|
|
1300
1264
|
const { coordinatorCmd } = init;
|
|
1301
|
-
const rl =
|
|
1265
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1302
1266
|
const engine = new AgentEngine(config, dir, coordinatorCmd, rl);
|
|
1303
1267
|
try {
|
|
1304
1268
|
switch (role.toLowerCase()) {
|
|
@@ -1344,5 +1308,4 @@ export async function runRole(role, arg, model) {
|
|
|
1344
1308
|
console.log(chalk.red(` Error: ${err.message}`));
|
|
1345
1309
|
process.exit(1);
|
|
1346
1310
|
}
|
|
1347
|
-
rl.close();
|
|
1348
1311
|
}
|