@arcadialdev/arcality 2.4.32 → 2.4.33
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/bin/arcality.mjs
CHANGED
|
@@ -14,15 +14,24 @@ const rawArgs = process.argv.slice(2);
|
|
|
14
14
|
let isRun = false;
|
|
15
15
|
let isInit = false;
|
|
16
16
|
let isSetup = false;
|
|
17
|
+
let isLogs = false;
|
|
17
18
|
|
|
18
19
|
// Buscamos los comandos en cualquier posición para evitar filtros de NPM en Windows
|
|
19
20
|
rawArgs.forEach(arg => {
|
|
20
21
|
if (arg === 'run') isRun = true;
|
|
21
22
|
if (arg === 'init') isInit = true;
|
|
22
23
|
if (arg === 'setup') isSetup = true;
|
|
24
|
+
if (arg === '--logs') isLogs = true;
|
|
23
25
|
});
|
|
24
26
|
|
|
25
|
-
if (
|
|
27
|
+
if (isLogs) {
|
|
28
|
+
const logsScript = path.join(PACKAGE_ROOT, 'scripts', 'arcality-logs.mjs');
|
|
29
|
+
spawn('node', [logsScript], {
|
|
30
|
+
stdio: 'inherit',
|
|
31
|
+
cwd: process.cwd(),
|
|
32
|
+
env: { ...process.env, ARCALITY_ROOT: PACKAGE_ROOT }
|
|
33
|
+
}).on('exit', code => process.exit(code || 0));
|
|
34
|
+
} else if (isInit) {
|
|
26
35
|
const initScript = path.join(PACKAGE_ROOT, 'scripts', 'init.mjs');
|
|
27
36
|
spawn('node', [initScript, ...rawArgs.filter(a => a !== 'init')], {
|
|
28
37
|
stdio: 'inherit',
|
package/package.json
CHANGED
package/playwright.config.js
CHANGED
|
@@ -7,6 +7,7 @@ module.exports = defineConfig({
|
|
|
7
7
|
testDir: path.join(__dirname, 'tests', '_helpers'),
|
|
8
8
|
testMatch: ['agentic-runner.bundle.spec.js'],
|
|
9
9
|
reporter: [
|
|
10
|
+
['line'],
|
|
10
11
|
[path.join(__dirname, 'tests', '_helpers', 'ArcalityReporter.js'),
|
|
11
12
|
{ outputDir: process.env.REPORTS_DIR || path.join(__dirname, 'tests-report') }]
|
|
12
13
|
],
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
const PROJECT_ROOT = process.env.ARCALITY_ROOT || path.join(__dirname, "..");
|
|
10
|
+
|
|
11
|
+
const contextDir = path.join(process.cwd(), "out");
|
|
12
|
+
const centralLog = path.join(contextDir, "arcality-history.log");
|
|
13
|
+
|
|
14
|
+
console.log(chalk.cyan(`\n🔍 Arcality Logs Viewer`));
|
|
15
|
+
console.log(chalk.gray(`=========================\n`));
|
|
16
|
+
|
|
17
|
+
if (!fs.existsSync(centralLog)) {
|
|
18
|
+
console.log(chalk.yellow(`No se encontró un historial de logs en este proyecto.`));
|
|
19
|
+
console.log(chalk.gray(`(Directorio actual: ${contextDir})\n`));
|
|
20
|
+
console.log(`Ejecuta algunas misiones de prueba primero usando el comando arcality.\n`);
|
|
21
|
+
process.exit(0);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const rawData = fs.readFileSync(centralLog, 'utf8');
|
|
26
|
+
const lines = rawData.split('\n').filter(l => l.trim().length > 0);
|
|
27
|
+
|
|
28
|
+
if (lines.length === 0) {
|
|
29
|
+
console.log(chalk.yellow(`El archivo de historial de misiones está vacío.\n`));
|
|
30
|
+
process.exit(0);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
console.log(chalk.bold(`Registros encontrados: `) + chalk.cyan(lines.length));
|
|
34
|
+
console.log(chalk.gray(`Mostrando los últimos 20 resultados:`));
|
|
35
|
+
console.log();
|
|
36
|
+
|
|
37
|
+
const recentLines = lines.slice(-20);
|
|
38
|
+
let totalInput = 0;
|
|
39
|
+
let totalOutput = 0;
|
|
40
|
+
|
|
41
|
+
recentLines.forEach((line, index) => {
|
|
42
|
+
try {
|
|
43
|
+
const data = JSON.parse(line);
|
|
44
|
+
|
|
45
|
+
// Icono según resultado
|
|
46
|
+
const statusIcon = data.success ? chalk.green('✅') : chalk.red('❌');
|
|
47
|
+
|
|
48
|
+
// Fecha bonita
|
|
49
|
+
const date = new Date(data.ts).toLocaleString();
|
|
50
|
+
|
|
51
|
+
// Contabilizar tokens
|
|
52
|
+
const inT = data.usage?.input_tokens || 0;
|
|
53
|
+
const caT = data.usage?.cache_creation_input_tokens || 0;
|
|
54
|
+
const crT = data.usage?.cache_read_input_tokens || 0;
|
|
55
|
+
const totalIn = inT + caT + crT;
|
|
56
|
+
const totalOut = data.usage?.output_tokens || 0;
|
|
57
|
+
|
|
58
|
+
totalInput += totalIn;
|
|
59
|
+
totalOutput += totalOut;
|
|
60
|
+
|
|
61
|
+
const costStr = chalk.yellow(`[IN: ${totalIn} | OUT: ${totalOut}]`);
|
|
62
|
+
const targetStr = data.target ? chalk.gray(` @ ${data.target}`) : '';
|
|
63
|
+
|
|
64
|
+
console.log(`${statusIcon} ${chalk.blue(date)} ${costStr}`);
|
|
65
|
+
console.log(` ${chalk.white(data.mission)}${targetStr}`);
|
|
66
|
+
console.log(` ${chalk.gray(`Pasos dados: ${data.steps}`)}\n`);
|
|
67
|
+
} catch (e) {
|
|
68
|
+
console.log(chalk.red(`⚠️ Error parseando línea: ${line.substring(0, 50)}...\n`));
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
console.log(chalk.gray(`--------------------------------------------------\n`));
|
|
73
|
+
|
|
74
|
+
} catch (err) {
|
|
75
|
+
console.error(chalk.red(`❌ Ocurrió un error al leer los logs:`), err.message);
|
|
76
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
// Mapeo curado de tecnologías clave a sus skills de pruebas/qa/patrones de autoskills.sh
|
|
5
|
+
// Son recursos muy ligeros en formato Markdown
|
|
6
|
+
const SKILLS_MAP: Record<string, string> = {
|
|
7
|
+
'next': 'https://skills.sh/vercel-labs/next-skills/next-best-practices/raw',
|
|
8
|
+
'vue': 'https://skills.sh/antfu/skills/vue-best-practices/raw',
|
|
9
|
+
'tailwindcss': 'https://skills.sh/giuseppe-trisciuoglio/developer-kit/tailwind-css-patterns/raw',
|
|
10
|
+
'react': 'https://skills.sh/vercel-labs/agent-skills/vercel-react-best-practices/raw',
|
|
11
|
+
'angular': 'https://skills.sh/angular/skills/angular-developer/raw',
|
|
12
|
+
'nuxt': 'https://skills.sh/antfu/skills/nuxt/raw',
|
|
13
|
+
'svelte': 'https://skills.sh/ejirocodes/agent-skills/svelte5-best-practices/raw',
|
|
14
|
+
'astro': 'https://skills.sh/astrolicious/agent-skills/astro/raw',
|
|
15
|
+
'prisma': 'https://skills.sh/prisma/skills/prisma-postgres/raw'
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export async function detectAndFetchEphemeralSkills(projectRoot: string): Promise<string> {
|
|
19
|
+
const pkgPath = path.join(projectRoot, 'package.json');
|
|
20
|
+
if (!fs.existsSync(pkgPath)) return '';
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
24
|
+
const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
|
|
25
|
+
|
|
26
|
+
const matchedUrls: string[] = [];
|
|
27
|
+
const matchedTechs: string[] = [];
|
|
28
|
+
|
|
29
|
+
for (const [lib, url] of Object.entries(SKILLS_MAP)) {
|
|
30
|
+
// Buscamos coincidencia parcial (ej. si usan @angular/core, next, react-dom)
|
|
31
|
+
const hasLib = Object.keys(deps).some(dep => dep === lib || dep.includes(`/${lib}`));
|
|
32
|
+
if (hasLib) {
|
|
33
|
+
matchedUrls.push(url);
|
|
34
|
+
matchedTechs.push(lib);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (matchedUrls.length === 0) return '';
|
|
39
|
+
|
|
40
|
+
console.log(`>>ARCALITY_STATUS>> ⚡ Stack detectado (${matchedTechs.join(', ')}). QA Skills inyectadas en memoria.`);
|
|
41
|
+
|
|
42
|
+
// Fetch strings in parallel with simple timeout
|
|
43
|
+
const skillsPromises = matchedUrls.map(url => {
|
|
44
|
+
const controller = new AbortController();
|
|
45
|
+
const id = setTimeout(() => controller.abort(), 2000); // Max 2s per request
|
|
46
|
+
return fetch(url, { signal: controller.signal })
|
|
47
|
+
.then(r => r.ok ? r.text() : '')
|
|
48
|
+
.catch(() => '')
|
|
49
|
+
.finally(() => clearTimeout(id));
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const skillsTexts = await Promise.all(skillsPromises);
|
|
53
|
+
|
|
54
|
+
const validTexts = skillsTexts.filter(Boolean);
|
|
55
|
+
if (validTexts.length === 0) return '';
|
|
56
|
+
|
|
57
|
+
return `\n<TECH_STACK_CONTEXT>\nEl proyecto objetivo utiliza las siguientes tecnologías. Usa estas directrices técnicas para entender el DOM, rutas y posibles bugs visuales:\n\n${validTexts.join('\n\n---\n\n')}\n</TECH_STACK_CONTEXT>\n`;
|
|
58
|
+
} catch {
|
|
59
|
+
return ''; // Fails silently
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -1375,6 +1375,14 @@ ${relevantFailures.map((m) => `- ${m.prompt}`).join("\n")}
|
|
|
1375
1375
|
} catch (e) {
|
|
1376
1376
|
console.log(`>>ARCALITY_STATUS>> \u26A0\uFE0F Error en hook de contexto: ${e.message}`);
|
|
1377
1377
|
}
|
|
1378
|
+
let autoskillsContext = "";
|
|
1379
|
+
try {
|
|
1380
|
+
const path3 = require("path");
|
|
1381
|
+
const toolsRoot = process.env.ARCALITY_ROOT || path3.join(__dirname, "..", "..");
|
|
1382
|
+
const { detectAndFetchEphemeralSkills } = require(path3.join(toolsRoot, "src", "services", "autoskillsService"));
|
|
1383
|
+
autoskillsContext = await detectAndFetchEphemeralSkills(process.cwd());
|
|
1384
|
+
} catch (e) {
|
|
1385
|
+
}
|
|
1378
1386
|
const systemPromptBlocks = [
|
|
1379
1387
|
{
|
|
1380
1388
|
type: "text",
|
|
@@ -1386,6 +1394,7 @@ ${prompt}
|
|
|
1386
1394
|
|
|
1387
1395
|
${projectInfo}
|
|
1388
1396
|
${skillsContext}
|
|
1397
|
+
${autoskillsContext}
|
|
1389
1398
|
${memoryContext}
|
|
1390
1399
|
${credentialsContext}
|
|
1391
1400
|
${memoryBackendContext}`
|
|
@@ -2476,23 +2485,28 @@ function captureValidationRule(errorMessage, context) {
|
|
|
2476
2485
|
}
|
|
2477
2486
|
}
|
|
2478
2487
|
}
|
|
2488
|
+
const effectiveLocalSteps = stepsData.length > 0 ? stepsData : stepsDataBackup;
|
|
2479
2489
|
const missionData = {
|
|
2480
2490
|
prompt,
|
|
2481
2491
|
steps: history,
|
|
2482
|
-
steps_data:
|
|
2492
|
+
steps_data: effectiveLocalSteps,
|
|
2483
2493
|
success: finalSuccess,
|
|
2484
2494
|
error: hasCriticalError,
|
|
2485
2495
|
timestamp: Date.now()
|
|
2486
2496
|
};
|
|
2487
2497
|
memories.push(missionData);
|
|
2488
2498
|
fs2.writeFileSync(memoryFile, JSON.stringify(memories, null, 2));
|
|
2489
|
-
console.log(`>>ARCALITY_STATUS>> \u{1F9E0} Memoria guardada: ${
|
|
2499
|
+
console.log(`>>ARCALITY_STATUS>> \u{1F9E0} Memoria guardada: ${effectiveLocalSteps.length} pasos nuevos`);
|
|
2490
2500
|
} catch (err) {
|
|
2491
2501
|
console.error("\u274C Fall\xF3 al guardar memoria:", err);
|
|
2492
2502
|
}
|
|
2493
2503
|
try {
|
|
2494
2504
|
const logPath = path2.join(contextDir, `agent-log-${Date.now()}.json`);
|
|
2495
|
-
fs2.writeFileSync(logPath, JSON.stringify({ prompt, history, steps: stepCount, success: finalSuccess, error: hasCriticalError, usage: totalMissionUsage }, null, 2));
|
|
2505
|
+
fs2.writeFileSync(logPath, JSON.stringify({ prompt, history, steps: stepCount, success: finalSuccess, error: hasCriticalError, usage: totalMissionUsage, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, null, 2));
|
|
2506
|
+
try {
|
|
2507
|
+
fs2.appendFileSync(path2.join(contextDir, "arcality-history.log"), JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), mission: prompt.substring(0, 150), success: finalSuccess, steps: stepCount, usage: totalMissionUsage }) + "\n");
|
|
2508
|
+
} catch (e) {
|
|
2509
|
+
}
|
|
2496
2510
|
} catch (err) {
|
|
2497
2511
|
}
|
|
2498
2512
|
};
|
|
@@ -2513,7 +2527,12 @@ function captureValidationRule(errorMessage, context) {
|
|
|
2513
2527
|
console.warn(`[CollectiveMemory] \u26A0\uFE0F stepsData vac\xEDo en path de \xE9xito \u2014 intentando usar backup...`);
|
|
2514
2528
|
}
|
|
2515
2529
|
const effectiveSteps = stepsData.length > 0 ? stepsData : stepsDataBackup;
|
|
2516
|
-
console.log(
|
|
2530
|
+
console.log(`
|
|
2531
|
+
======================================================`);
|
|
2532
|
+
console.log(`\u{1F9E0} [MEMORIA COLECTIVA - INGESTA DE APRENDIZAJE]`);
|
|
2533
|
+
console.log(`>>arcality>> Persistiendo conocimiento (project: ${pid}, steps: ${effectiveSteps.length}, isFailed: ${isFailed})...`);
|
|
2534
|
+
console.log(`======================================================
|
|
2535
|
+
`);
|
|
2517
2536
|
try {
|
|
2518
2537
|
const stepsNarrative = effectiveSteps.map((s) => `${s.action_data?.action || "action"} "${s.componentName}" en ${s.url}`).join(" \u2192 ").substring(0, 500);
|
|
2519
2538
|
if (!isFailed) {
|
|
@@ -2703,7 +2722,12 @@ function captureValidationRule(errorMessage, context) {
|
|
|
2703
2722
|
response = null;
|
|
2704
2723
|
} else {
|
|
2705
2724
|
guideStepCount++;
|
|
2706
|
-
console.log(
|
|
2725
|
+
console.log(`
|
|
2726
|
+
======================================================`);
|
|
2727
|
+
console.log(`\u{1F680} [USANDO GU\xCDA DE \xC9XITO - MEMORIA COLECTIVA AHORRANDO TOKENS]`);
|
|
2728
|
+
console.log(`>>arcality>> Paso validado (Gu\xEDa #${guideStepCount}, NO consume turno IA)`);
|
|
2729
|
+
console.log(`======================================================
|
|
2730
|
+
`);
|
|
2707
2731
|
}
|
|
2708
2732
|
}
|
|
2709
2733
|
if (!response) {
|
|
@@ -2733,7 +2757,12 @@ function captureValidationRule(errorMessage, context) {
|
|
|
2733
2757
|
}
|
|
2734
2758
|
}
|
|
2735
2759
|
}
|
|
2736
|
-
console.log(
|
|
2760
|
+
console.log(`
|
|
2761
|
+
======================================================`);
|
|
2762
|
+
console.log(`\u{1F916} [RAZONAMIENTO DEL AGENTE]`);
|
|
2763
|
+
console.log(`${response.thought}`);
|
|
2764
|
+
console.log(`======================================================
|
|
2765
|
+
`);
|
|
2737
2766
|
if (!response.finish && history.length >= 6) {
|
|
2738
2767
|
const lastSixActions = history.slice(-6);
|
|
2739
2768
|
const clickActions = lastSixActions.filter((h) => h.includes("click en"));
|