agent-rev 0.3.0 → 0.3.1
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.js +12 -3
- package/dist/core/engine.d.ts +3 -0
- package/dist/core/engine.js +63 -5
- package/dist/utils/qwen-auth.d.ts +2 -0
- package/dist/utils/qwen-auth.js +37 -7
- package/package.json +1 -23
package/dist/commands/repl.js
CHANGED
|
@@ -7,7 +7,7 @@ import chalk from 'chalk';
|
|
|
7
7
|
import { execSync } from 'child_process';
|
|
8
8
|
import { CLI_REGISTRY } from '../types.js';
|
|
9
9
|
import { writeJson, ensureDir, readJson, listDir, fileExists } from '../utils/fs.js';
|
|
10
|
-
import { loadAuth, saveAuth, loadCliConfig, saveCliConfig, loadProjectConfig } from '../utils/config.js';
|
|
10
|
+
import { loadAuth, saveAuth, loadCliConfig, saveCliConfig, loadProjectConfig, PKG_NAME } from '../utils/config.js';
|
|
11
11
|
import { log } from '../utils/logger.js';
|
|
12
12
|
import { AgentEngine, ExitError } from '../core/engine.js';
|
|
13
13
|
import { qwenAuthStatus, QWEN_AGENT_HOME, fetchQwenModels } from '../utils/qwen-auth.js';
|
|
@@ -1227,8 +1227,17 @@ export async function runRole(role, arg, model) {
|
|
|
1227
1227
|
}
|
|
1228
1228
|
case 'explorer':
|
|
1229
1229
|
case 'exp': {
|
|
1230
|
-
const
|
|
1231
|
-
|
|
1230
|
+
const explorerCli = config.roles.explorer?.cli;
|
|
1231
|
+
if (explorerCli && explorerCli === PKG_NAME) {
|
|
1232
|
+
// This binary IS the configured explorer CLI — avoid recursion.
|
|
1233
|
+
// Call Qwen API directly using own credentials.
|
|
1234
|
+
const result = await engine.runExplorerDirect(arg || undefined);
|
|
1235
|
+
console.log(result);
|
|
1236
|
+
}
|
|
1237
|
+
else {
|
|
1238
|
+
const result = await engine.runExplorer(arg || undefined);
|
|
1239
|
+
console.log(result);
|
|
1240
|
+
}
|
|
1232
1241
|
break;
|
|
1233
1242
|
}
|
|
1234
1243
|
case 'coordinator':
|
package/dist/core/engine.d.ts
CHANGED
|
@@ -39,5 +39,8 @@ export declare class AgentEngine {
|
|
|
39
39
|
verdict: string;
|
|
40
40
|
}>;
|
|
41
41
|
runExplorer(task?: string): Promise<string>;
|
|
42
|
+
/** Called when the current binary IS the configured explorer CLI (prevents recursion).
|
|
43
|
+
* Builds the full exploration prompt and calls Qwen API using own credentials. */
|
|
44
|
+
runExplorerDirect(task?: string): Promise<string>;
|
|
42
45
|
runFullCycle(task: string): Promise<void>;
|
|
43
46
|
}
|
package/dist/core/engine.js
CHANGED
|
@@ -6,8 +6,7 @@ import { CLI_REGISTRY } from '../types.js';
|
|
|
6
6
|
import { writeJson, readJson, fileExists, writeFile, readFile } from '../utils/fs.js';
|
|
7
7
|
import { log } from '../utils/logger.js';
|
|
8
8
|
import chalk from 'chalk';
|
|
9
|
-
import { QWEN_AGENT_HOME } from '../utils/qwen-auth.js';
|
|
10
|
-
import { callQwenAPI } from '../utils/qwen-auth.js';
|
|
9
|
+
import { QWEN_AGENT_HOME, callQwenAPI } from '../utils/qwen-auth.js';
|
|
11
10
|
import * as fs from 'fs/promises';
|
|
12
11
|
/** Thrown when a slash command inside a conversation requests exit */
|
|
13
12
|
export class ExitError extends Error {
|
|
@@ -448,12 +447,13 @@ INSTRUCCIONES:
|
|
|
448
447
|
}
|
|
449
448
|
};
|
|
450
449
|
// Role binaries (agent-orch, agent-impl, etc.) are wrappers, not AI CLIs.
|
|
451
|
-
//
|
|
450
|
+
// runExplorer handles them separately by spawning with the short task.
|
|
451
|
+
// For orch/impl/rev the full cycle never spawns role binaries via runWithFallback.
|
|
452
452
|
const ROLE_BINARIES = new Set(['agent-orch', 'agent-impl', 'agent-rev', 'agent-explorer']);
|
|
453
453
|
// Try primary
|
|
454
454
|
log.info(`Launching ${roleName}: ${role.cli} (${role.model})`);
|
|
455
455
|
if (ROLE_BINARIES.has(role.cli)) {
|
|
456
|
-
log.warn(`${role.cli} is a role binary — skipping to fallback
|
|
456
|
+
log.warn(`${role.cli} is a role binary — skipping to fallback`);
|
|
457
457
|
}
|
|
458
458
|
else {
|
|
459
459
|
const primaryResult = await tryWithAutoRepair(role.cli, role.model, role.cmd);
|
|
@@ -783,7 +783,26 @@ REGLAS:
|
|
|
783
783
|
- NO modifiques archivos de aplicacion (solo .agent/context/)
|
|
784
784
|
- NO ejecutes comandos que cambien estado (npm install, migraciones, etc.)
|
|
785
785
|
- Si un directorio esta en node_modules, dist, .git: ignoralo`;
|
|
786
|
-
const
|
|
786
|
+
const ROLE_BINS = new Set(['agent-orch', 'agent-impl', 'agent-rev', 'agent-explorer']);
|
|
787
|
+
let res;
|
|
788
|
+
if (ROLE_BINS.has(this.config.roles.explorer.cli)) {
|
|
789
|
+
// Spawn the role binary as a separate process with its own credentials.
|
|
790
|
+
// Pass only the short task — the binary builds its own full prompt internally.
|
|
791
|
+
const explorerRole = this.config.roles.explorer;
|
|
792
|
+
const spawnCmd = `${explorerRole.cli} --model ${explorerRole.model}`;
|
|
793
|
+
log.info(`Spawning ${explorerRole.cli} (${explorerRole.model}) as separate process`);
|
|
794
|
+
const result = await runCli(spawnCmd, effectiveTask);
|
|
795
|
+
if (result.exitCode !== 0) {
|
|
796
|
+
log.warn(`${explorerRole.cli} exited with code ${result.exitCode} — trying fallback`);
|
|
797
|
+
res = await this.runWithFallback('explorer', prompt, 'Exploracion');
|
|
798
|
+
}
|
|
799
|
+
else {
|
|
800
|
+
res = result.output;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
else {
|
|
804
|
+
res = await this.runWithFallback('explorer', prompt, 'Exploracion');
|
|
805
|
+
}
|
|
787
806
|
const text = extractCliText(res);
|
|
788
807
|
// Always save a timestamped explorer report
|
|
789
808
|
try {
|
|
@@ -794,6 +813,45 @@ REGLAS:
|
|
|
794
813
|
catch { /* don't fail if save fails */ }
|
|
795
814
|
return text;
|
|
796
815
|
}
|
|
816
|
+
/** Called when the current binary IS the configured explorer CLI (prevents recursion).
|
|
817
|
+
* Builds the full exploration prompt and calls Qwen API using own credentials. */
|
|
818
|
+
async runExplorerDirect(task) {
|
|
819
|
+
const role = this.config.roles.explorer;
|
|
820
|
+
if (!role)
|
|
821
|
+
throw new Error('Explorer role not configured.');
|
|
822
|
+
const agentDir = path.join(this.projectDir, '.agent');
|
|
823
|
+
const contextDir = path.join(agentDir, 'context');
|
|
824
|
+
await fs.mkdir(contextDir, { recursive: true });
|
|
825
|
+
const archPath = path.join(contextDir, 'architecture.md');
|
|
826
|
+
let existingArch = '';
|
|
827
|
+
try {
|
|
828
|
+
existingArch = await readFile(archPath);
|
|
829
|
+
}
|
|
830
|
+
catch { /* new */ }
|
|
831
|
+
const effectiveTask = task || 'Explorar y documentar todas las aplicaciones y servicios del proyecto';
|
|
832
|
+
const context = await this.buildOrchestratorContext();
|
|
833
|
+
const prompt = this.buildRolePrompt('explorer', `TAREA DE EXPLORACION: ${effectiveTask}
|
|
834
|
+
DIRECTORIO_TRABAJO: ${this.projectDir}
|
|
835
|
+
PROYECTO: ${this.config.project}
|
|
836
|
+
|
|
837
|
+
${existingArch ? `DOCUMENTACION EXISTENTE:\n${existingArch.slice(0, 3000)}\n` : 'Sin documentacion previa.\n'}
|
|
838
|
+
CONTEXTO: ${context.slice(0, 2000)}
|
|
839
|
+
|
|
840
|
+
INSTRUCCIONES:
|
|
841
|
+
1. Lista el directorio raiz e identifica todos los servicios/apps.
|
|
842
|
+
2. Para cada uno: lee package.json/requirements.txt, explora src/, identifica entry point, rutas/endpoints, puerto.
|
|
843
|
+
3. Identifica dependencias entre servicios.
|
|
844
|
+
4. Crea/actualiza ${archPath} con tabla resumen y detalle por servicio.
|
|
845
|
+
5. Crea/actualiza ${contextDir}/<servicio>/architecture.md para cada servicio.`);
|
|
846
|
+
const result = await callQwenAPI(prompt, role.model);
|
|
847
|
+
try {
|
|
848
|
+
const ts = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
849
|
+
await writeFile(path.join(contextDir, `explorer-${ts}.md`), `# Explorer Report\n\nTask: ${effectiveTask}\n\n${result}\n`);
|
|
850
|
+
log.ok(`Saved to .agent/context/explorer-${ts}.md`);
|
|
851
|
+
}
|
|
852
|
+
catch { /* ignore */ }
|
|
853
|
+
return result;
|
|
854
|
+
}
|
|
797
855
|
async runFullCycle(task) {
|
|
798
856
|
// Header is now shown by the REPL before the first user message
|
|
799
857
|
// ══════════════════════════════════════════════════
|
|
@@ -11,3 +11,5 @@ export declare function qwenAuthStatus(): Promise<{
|
|
|
11
11
|
export declare function fetchQwenModels(): Promise<string[]>;
|
|
12
12
|
export declare function getQwenAccessToken(): Promise<string | null>;
|
|
13
13
|
export declare function callQwenAPI(prompt: string, model?: string): Promise<string>;
|
|
14
|
+
/** Call Qwen API using credentials from a specific file path (for role binaries) */
|
|
15
|
+
export declare function callQwenAPIFromCreds(prompt: string, model: string, credsPath: string): Promise<string>;
|
package/dist/utils/qwen-auth.js
CHANGED
|
@@ -237,12 +237,7 @@ export async function getQwenAccessToken() {
|
|
|
237
237
|
const token = await loadToken();
|
|
238
238
|
return token?.accessToken || null;
|
|
239
239
|
}
|
|
240
|
-
|
|
241
|
-
const token = await loadToken();
|
|
242
|
-
if (!token) {
|
|
243
|
-
throw new Error('No hay token de Qwen. Ejecutá /login primero.');
|
|
244
|
-
}
|
|
245
|
-
// Qwen OAuth tokens work with chat.qwen.ai API
|
|
240
|
+
async function callQwenAPIWithToken(token, prompt, model) {
|
|
246
241
|
const baseUrl = 'https://chat.qwen.ai/api/v1';
|
|
247
242
|
const response = await fetch(`${baseUrl}/chat/completions`, {
|
|
248
243
|
method: 'POST',
|
|
@@ -253,7 +248,6 @@ export async function callQwenAPI(prompt, model = 'coder-model') {
|
|
|
253
248
|
body: JSON.stringify({
|
|
254
249
|
model: model || 'coder-model',
|
|
255
250
|
messages: [
|
|
256
|
-
{ role: 'system', content: 'Sos el COORDINADOR de un equipo multi-agente de desarrollo. Tu trabajo es ENTENDER lo que el programador necesita haciendo PREGUNTAS si es necesario. Habla de forma NATURAL, como un compañero de equipo. Sé breve y directo.' },
|
|
257
251
|
{ role: 'user', content: prompt },
|
|
258
252
|
],
|
|
259
253
|
}),
|
|
@@ -265,3 +259,39 @@ export async function callQwenAPI(prompt, model = 'coder-model') {
|
|
|
265
259
|
const data = await response.json();
|
|
266
260
|
return data.choices?.[0]?.message?.content || '';
|
|
267
261
|
}
|
|
262
|
+
export async function callQwenAPI(prompt, model = 'coder-model') {
|
|
263
|
+
const token = await loadToken();
|
|
264
|
+
if (!token) {
|
|
265
|
+
throw new Error('No hay token de Qwen. Ejecutá /login primero.');
|
|
266
|
+
}
|
|
267
|
+
return callQwenAPIWithToken(token, prompt, model);
|
|
268
|
+
}
|
|
269
|
+
/** Call Qwen API using credentials from a specific file path (for role binaries) */
|
|
270
|
+
export async function callQwenAPIFromCreds(prompt, model, credsPath) {
|
|
271
|
+
let raw;
|
|
272
|
+
try {
|
|
273
|
+
raw = JSON.parse(await fs.readFile(credsPath, 'utf-8'));
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
throw new Error(`No credentials found at ${credsPath}. Run the role binary with --login first.`);
|
|
277
|
+
}
|
|
278
|
+
let token = {
|
|
279
|
+
accessToken: raw.accessToken || raw.access_token || '',
|
|
280
|
+
refreshToken: raw.refreshToken || raw.refresh_token || '',
|
|
281
|
+
expiresAt: raw.expiresAt || raw.expiry_date || 0,
|
|
282
|
+
idToken: raw.idToken || raw.id_token,
|
|
283
|
+
resourceUrl: raw.resourceUrl || raw.resource_url,
|
|
284
|
+
};
|
|
285
|
+
if (!token.accessToken) {
|
|
286
|
+
throw new Error(`Invalid credentials at ${credsPath}. Run the role binary with --login first.`);
|
|
287
|
+
}
|
|
288
|
+
// Refresh if expired
|
|
289
|
+
if (token.expiresAt <= Date.now() && token.refreshToken) {
|
|
290
|
+
const refreshed = await refreshAccessToken(token.refreshToken);
|
|
291
|
+
if (refreshed) {
|
|
292
|
+
token = refreshed;
|
|
293
|
+
await fs.writeFile(credsPath, JSON.stringify(token, null, 2), 'utf-8');
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return callQwenAPIWithToken(token, prompt, model);
|
|
297
|
+
}
|
package/package.json
CHANGED
|
@@ -1,23 +1 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "agent-rev",
|
|
3
|
-
"version": "0.3.0",
|
|
4
|
-
"description": "agent-rev agent",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "./dist/index.js",
|
|
7
|
-
"files": ["dist/"],
|
|
8
|
-
"bin": { "agent-rev": "dist/index.js" },
|
|
9
|
-
"scripts": {
|
|
10
|
-
"build": "tsc && echo '#!/usr/bin/env node' | cat - dist/index.js > dist/index.tmp && mv dist/index.tmp dist/index.js && chmod +x dist/index.js"
|
|
11
|
-
},
|
|
12
|
-
"keywords": ["ai", "agent", "cli"],
|
|
13
|
-
"license": "MIT",
|
|
14
|
-
"dependencies": {
|
|
15
|
-
"@anthropic-ai/sdk": "^0.39.0",
|
|
16
|
-
"@google/generative-ai": "^0.24.0",
|
|
17
|
-
"chalk": "^5.4.1",
|
|
18
|
-
"commander": "^13.1.0",
|
|
19
|
-
"open": "^11.0.0",
|
|
20
|
-
"openai": "^4.91.0"
|
|
21
|
-
},
|
|
22
|
-
"engines": { "node": ">=18.0.0" }
|
|
23
|
-
}
|
|
1
|
+
{"name":"agent-rev","version":"0.3.1","description":"agent-rev agent","type":"module","main":"./dist/index.js","files":["dist/"],"bin":{"agent-rev":"dist/index.js"},"scripts":{"build":"tsc"},"keywords":["ai","agent","cli"],"license":"MIT","dependencies":{"@anthropic-ai/sdk":"^0.39.0","@google/generative-ai":"^0.24.0","chalk":"^5.4.1","commander":"^13.1.0","open":"^11.0.0","openai":"^4.91.0"},"engines":{"node":">=18.0.0"}}
|