@saulwade/swl-ses 1.4.1 → 1.4.2
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/CLAUDE.md +1 -1
- package/README.md +1 -1
- package/agentes/nemesis-auditor-swl.md +161 -161
- package/bin/swl-mcp-server.js +187 -187
- package/comandos/swl/.evolved.json +22 -22
- package/comandos/swl/contribuir.md +233 -233
- package/comandos/swl/nemesis.md +122 -122
- package/gateway/lib/event-channel.js +191 -191
- package/habilidades/backend-production-resilience/SKILL.md +288 -288
- package/habilidades/benchmark-memoria/SKILL.md +186 -186
- package/habilidades/diagrama-arquitectura/assets/template.html +276 -276
- package/habilidades/doubt-driven-review/SKILL.md +171 -171
- package/habilidades/doubt-driven-review/recursos/EXAMPLES.md +130 -130
- package/habilidades/eval-framework/SKILL.md +212 -212
- package/habilidades/feynman-auditor-swl/SKILL.md +123 -123
- package/habilidades/feynman-auditor-swl/recursos/preguntas-language-agnostic.md +108 -108
- package/habilidades/harness-claude-code/SKILL.md +299 -299
- package/habilidades/infra-github-actions/SKILL.md +166 -166
- package/habilidades/legacy-code-rescue/SKILL.md +267 -267
- package/habilidades/manejo-errores/.evolved.json +8 -8
- package/habilidades/meta-skills-estandar/recursos/convencion-examples.md +93 -93
- package/habilidades/meta-skills-estandar/recursos/skills-as-agents.md +163 -163
- package/habilidades/patrones-python/SKILL.md +229 -229
- package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -469
- package/habilidades/planear-fase/SKILL.md +319 -319
- package/habilidades/release-semver/.evolved.json +8 -8
- package/habilidades/state-inconsistency-auditor-swl/SKILL.md +166 -166
- package/habilidades/state-inconsistency-auditor-swl/recursos/coupled-state-patterns.md +147 -147
- package/habilidades/testing-python/SKILL.md +340 -340
- package/habilidades/web-fetcher-routing/SKILL.md +75 -75
- package/hooks/claudemd-bloat-detector.js +161 -161
- package/hooks/lib/agent-routing.js +107 -107
- package/hooks/lib/auto-consolidator.js +335 -335
- package/hooks/lib/error-classifier.js +308 -308
- package/hooks/lib/merkle-audit.js +96 -96
- package/hooks/lib/provenance-tracker.js +191 -191
- package/hooks/lib/rate-limit-tracker.js +253 -253
- package/hooks/lib/resource-quota.js +122 -122
- package/hooks/lib/retry-jitter.js +165 -165
- package/hooks/lib/security-net.js +201 -201
- package/hooks/lib/skill-auditor.js +588 -588
- package/hooks/lib/sync-status.js +228 -228
- package/hooks/lib/taint-tracker.js +107 -107
- package/hooks/lib/text-similarity.js +241 -241
- package/hooks/lib/toon-compressor.js +245 -245
- package/hooks/registro-turnos.js +209 -209
- package/hooks/sugerir-regenerar-inventario.js +170 -170
- package/hooks/validar-formato-post-subagente.js +140 -140
- package/hooks/validar-memoria-hook.js +218 -218
- package/instintos/prompt-appendices.yaml +57 -57
- package/manifiestos/agent-output-schemas.json +57 -57
- package/manifiestos/modulos.json +11 -6
- package/manifiestos/perfiles.json +2 -1
- package/manifiestos/skills-lock.json +1114 -1114
- package/package.json +1 -1
- package/plantillas/auditor-veto-template.md +105 -105
- package/plantillas/github-workflows/README.md +47 -47
- package/plantillas/github-workflows/release-please.yml +44 -44
- package/plantillas/github-workflows/swl-ci.yml +107 -107
- package/plantillas/github-workflows/swl-security.yml +51 -51
- package/plugin.json +9 -1
- package/reglas/analisis-previo-tareas-grandes.md +172 -172
- package/reglas/arreglar-al-detectar.md +147 -147
- package/reglas/fragmentos-compartidos.md +152 -152
- package/reglas/harness-claude-code.md +213 -213
- package/reglas/usar-context7.md +226 -226
- package/schemas/diary-entry.schema.json +80 -80
- package/scripts/audit-tools/audit-history.js +330 -330
- package/scripts/audit-tools/bundle-tracker.js +290 -290
- package/scripts/audit-tools/canary-monitor.js +352 -352
- package/scripts/audit-tools/code-profiler.js +605 -605
- package/scripts/audit-tools/dep-doctor.js +320 -320
- package/scripts/audit-tools/env-validator.js +206 -206
- package/scripts/audit-tools/lib/fs-walk.js +48 -48
- package/scripts/audit-tools/lib/output.js +23 -23
- package/scripts/audit-tools/migration-checker.js +392 -392
- package/scripts/audit-tools/pentest-scanner.js +1436 -1436
- package/scripts/benchmark-memoria.js +167 -167
- package/scripts/configurar-branch-protection.js +418 -418
- package/scripts/detectar-aprendizajes-duplicados.js +151 -151
- package/scripts/field-report.js +199 -199
- package/scripts/generar-checklists-consolidados.js +273 -273
- package/scripts/generar-inventario.js +420 -420
- package/scripts/generar-matriz-lenguajes.js +271 -271
- package/scripts/lib/artefactos-python.js +43 -43
- package/scripts/lib/benchmark-metrics.js +160 -160
- package/scripts/lib/budget-enforcer.js +252 -252
- package/scripts/lib/configurar-ci.js +380 -380
- package/scripts/lib/contadores-inventario.js +217 -217
- package/scripts/lib/detectar-stack-detallado.js +307 -307
- package/scripts/lib/diary-entry.js +234 -234
- package/scripts/lib/eval-metrics-store.js +218 -218
- package/scripts/lib/eval-quality.js +171 -171
- package/scripts/lib/eval-schemas.js +144 -144
- package/scripts/lib/eval-self-correct.js +106 -106
- package/scripts/lib/eval-validator.js +185 -185
- package/scripts/lib/jaccard-similarity.js +98 -98
- package/scripts/lib/longmemeval-runner.js +125 -125
- package/scripts/lib/manifiestos.js +42 -1
- package/scripts/lib/npm-version.js +261 -261
- package/scripts/lib/paquetes-conocidos.js +50 -50
- package/scripts/lib/prompt-builder.js +264 -264
- package/scripts/lib/rrf-fusion.js +175 -175
- package/scripts/lib/scoring-instintos.js +277 -277
- package/scripts/lib/semantic-search.js +252 -252
- package/scripts/limpiar-artefactos-python.js +131 -131
- package/scripts/mcp-server/README.md +128 -128
- package/scripts/mcp-server/handlers.js +206 -206
- package/scripts/migrar-csv-a-array.js +168 -168
- package/scripts/migrar-fase-dominio.js +201 -201
- package/scripts/publicar.js +511 -511
- package/scripts/run-eval.js +141 -141
- package/scripts/validar-manifest.js +231 -195
- package/scripts/validar-userland-vacio.js +110 -110
package/scripts/field-report.js
CHANGED
|
@@ -1,199 +1,199 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* field-report.js — Generador de reportes de validación en campo.
|
|
6
|
-
*
|
|
7
|
-
* Analiza sesiones JSONL de Claude Code para extraer métricas reales de uso
|
|
8
|
-
* de skills y agentes SWL en proyectos de usuario.
|
|
9
|
-
*
|
|
10
|
-
* Uso:
|
|
11
|
-
* node scripts/field-report.js # Todas las sesiones
|
|
12
|
-
* node scripts/field-report.js --days=30 # Últimos 30 días
|
|
13
|
-
* node scripts/field-report.js --project=/path # Proyecto específico
|
|
14
|
-
* node scripts/field-report.js --output=json # Salida JSON
|
|
15
|
-
*
|
|
16
|
-
* Métricas extraídas:
|
|
17
|
-
* - Skills invocados (Skill("nombre"))
|
|
18
|
-
* - Agentes delegados (Agent con description)
|
|
19
|
-
* - Comandos /swl:* ejecutados
|
|
20
|
-
* - Proyectos donde se usó swl-ses
|
|
21
|
-
* - Frecuencia de uso por componente
|
|
22
|
-
* - Skills nunca usados (candidatos a poda)
|
|
23
|
-
*
|
|
24
|
-
* @module scripts/field-report
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
const fs = require('fs');
|
|
28
|
-
const path = require('path');
|
|
29
|
-
const os = require('os');
|
|
30
|
-
|
|
31
|
-
// ── Argumentos ────────────────────────────────────────────────────────
|
|
32
|
-
|
|
33
|
-
const args = process.argv.slice(2).reduce((acc, arg) => {
|
|
34
|
-
const [k, v] = arg.replace(/^--/, '').split('=');
|
|
35
|
-
acc[k] = v || true;
|
|
36
|
-
return acc;
|
|
37
|
-
}, {});
|
|
38
|
-
|
|
39
|
-
const DAYS = parseInt(args.days) || 0;
|
|
40
|
-
const PROJECT = args.project || null;
|
|
41
|
-
const OUTPUT = args.output || 'md';
|
|
42
|
-
const CLAUDE_DIR = path.join(os.homedir(), '.claude', 'projects');
|
|
43
|
-
|
|
44
|
-
// ── Explorar sesiones ─────────────────────────────────────────────────
|
|
45
|
-
|
|
46
|
-
function buscarSesiones() {
|
|
47
|
-
const sesiones = [];
|
|
48
|
-
if (!fs.existsSync(CLAUDE_DIR)) {
|
|
49
|
-
console.error(`No se encontró ${CLAUDE_DIR}`);
|
|
50
|
-
return sesiones;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const proyectos = fs.readdirSync(CLAUDE_DIR).filter(d =>
|
|
54
|
-
fs.statSync(path.join(CLAUDE_DIR, d)).isDirectory()
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
for (const proy of proyectos) {
|
|
58
|
-
if (PROJECT && !proy.includes(PROJECT.replace(/[/\\:]/g, '-'))) continue;
|
|
59
|
-
const dir = path.join(CLAUDE_DIR, proy);
|
|
60
|
-
const archivos = fs.readdirSync(dir).filter(f => f.endsWith('.jsonl'));
|
|
61
|
-
for (const archivo of archivos) {
|
|
62
|
-
const ruta = path.join(dir, archivo);
|
|
63
|
-
const stat = fs.statSync(ruta);
|
|
64
|
-
if (DAYS > 0) {
|
|
65
|
-
const hace = Date.now() - DAYS * 86400000;
|
|
66
|
-
if (stat.mtimeMs < hace) continue;
|
|
67
|
-
}
|
|
68
|
-
sesiones.push({ proyecto: proy, ruta, fecha: stat.mtime });
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return sesiones;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function analizarSesion(ruta) {
|
|
76
|
-
const stats = { skills: {}, agentes: {}, comandos: {}, toolCalls: 0 };
|
|
77
|
-
try {
|
|
78
|
-
const contenido = fs.readFileSync(ruta, 'utf8');
|
|
79
|
-
const lineas = contenido.split('\n').filter(Boolean);
|
|
80
|
-
|
|
81
|
-
for (const linea of lineas) {
|
|
82
|
-
try {
|
|
83
|
-
const entry = JSON.parse(linea);
|
|
84
|
-
const msg = entry.message || entry;
|
|
85
|
-
|
|
86
|
-
// Buscar Skill("nombre") en contenido
|
|
87
|
-
const texto = JSON.stringify(msg);
|
|
88
|
-
const skillMatches = texto.matchAll(/Skill\s*\(\s*["']([^"']+)["']\s*\)/g);
|
|
89
|
-
for (const m of skillMatches) {
|
|
90
|
-
stats.skills[m[1]] = (stats.skills[m[1]] || 0) + 1;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Buscar agentes delegados
|
|
94
|
-
const agentMatches = texto.matchAll(/"description"\s*:\s*"([^"]*swl[^"]*)"/gi);
|
|
95
|
-
for (const m of agentMatches) {
|
|
96
|
-
const nombre = m[1].substring(0, 60);
|
|
97
|
-
stats.agentes[nombre] = (stats.agentes[nombre] || 0) + 1;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Buscar comandos /swl:*
|
|
101
|
-
const cmdMatches = texto.matchAll(/\/swl:([a-z-]+)/g);
|
|
102
|
-
for (const m of cmdMatches) {
|
|
103
|
-
const cmd = `/swl:${m[1]}`;
|
|
104
|
-
stats.comandos[cmd] = (stats.comandos[cmd] || 0) + 1;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Contar tool calls
|
|
108
|
-
if (msg.type === 'tool_use' || msg.tool_name) stats.toolCalls++;
|
|
109
|
-
} catch { /* línea JSONL inválida, ignorar */ }
|
|
110
|
-
}
|
|
111
|
-
} catch { /* archivo ilegible, ignorar */ }
|
|
112
|
-
|
|
113
|
-
return stats;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// ── Generar reporte ───────────────────────────────────────────────────
|
|
117
|
-
|
|
118
|
-
function generarReporte() {
|
|
119
|
-
const sesiones = buscarSesiones();
|
|
120
|
-
if (sesiones.length === 0) {
|
|
121
|
-
console.log('No se encontraron sesiones para analizar.');
|
|
122
|
-
console.log(`Directorio buscado: ${CLAUDE_DIR}`);
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const totales = { skills: {}, agentes: {}, comandos: {}, sesiones: sesiones.length, proyectos: new Set(), toolCalls: 0 };
|
|
127
|
-
|
|
128
|
-
for (const sesion of sesiones) {
|
|
129
|
-
totales.proyectos.add(sesion.proyecto);
|
|
130
|
-
const stats = analizarSesion(sesion.ruta);
|
|
131
|
-
totales.toolCalls += stats.toolCalls;
|
|
132
|
-
|
|
133
|
-
for (const [k, v] of Object.entries(stats.skills)) totales.skills[k] = (totales.skills[k] || 0) + v;
|
|
134
|
-
for (const [k, v] of Object.entries(stats.agentes)) totales.agentes[k] = (totales.agentes[k] || 0) + v;
|
|
135
|
-
for (const [k, v] of Object.entries(stats.comandos)) totales.comandos[k] = (totales.comandos[k] || 0) + v;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Cargar inventario de skills
|
|
139
|
-
const skillsDir = path.join(__dirname, '..', 'habilidades');
|
|
140
|
-
const todosSkills = fs.existsSync(skillsDir)
|
|
141
|
-
? fs.readdirSync(skillsDir).filter(d => fs.statSync(path.join(skillsDir, d)).isDirectory())
|
|
142
|
-
: [];
|
|
143
|
-
|
|
144
|
-
const skillsUsados = new Set(Object.keys(totales.skills));
|
|
145
|
-
const skillsNoUsados = todosSkills.filter(s => !skillsUsados.has(s));
|
|
146
|
-
|
|
147
|
-
if (OUTPUT === 'json') {
|
|
148
|
-
console.log(JSON.stringify({
|
|
149
|
-
fecha: new Date().toISOString(),
|
|
150
|
-
sesiones: totales.sesiones,
|
|
151
|
-
proyectos: [...totales.proyectos],
|
|
152
|
-
toolCalls: totales.toolCalls,
|
|
153
|
-
skills: Object.entries(totales.skills).sort((a, b) => b[1] - a[1]),
|
|
154
|
-
agentes: Object.entries(totales.agentes).sort((a, b) => b[1] - a[1]),
|
|
155
|
-
comandos: Object.entries(totales.comandos).sort((a, b) => b[1] - a[1]),
|
|
156
|
-
skillsNoUsados,
|
|
157
|
-
cobertura: `${skillsUsados.size}/${todosSkills.length}`,
|
|
158
|
-
}, null, 2));
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Markdown
|
|
163
|
-
const sorted = (obj) => Object.entries(obj).sort((a, b) => b[1] - a[1]);
|
|
164
|
-
|
|
165
|
-
console.log('# Reporte de Validación en Campo — swl-ses');
|
|
166
|
-
console.log(`\n**Fecha**: ${new Date().toISOString().slice(0, 10)}`);
|
|
167
|
-
console.log(`**Sesiones analizadas**: ${totales.sesiones}`);
|
|
168
|
-
console.log(`**Proyectos**: ${totales.proyectos.size}`);
|
|
169
|
-
console.log(`**Tool calls totales**: ${totales.toolCalls}`);
|
|
170
|
-
console.log(`**Cobertura de skills**: ${skillsUsados.size}/${todosSkills.length} (${Math.round(skillsUsados.size / todosSkills.length * 100)}%)`);
|
|
171
|
-
|
|
172
|
-
console.log('\n## Skills más usados\n');
|
|
173
|
-
console.log('| Skill | Invocaciones |');
|
|
174
|
-
console.log('|-------|-------------|');
|
|
175
|
-
for (const [s, c] of sorted(totales.skills).slice(0, 30)) {
|
|
176
|
-
console.log(`| ${s} | ${c} |`);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
console.log('\n## Comandos /swl:* más usados\n');
|
|
180
|
-
console.log('| Comando | Invocaciones |');
|
|
181
|
-
console.log('|---------|-------------|');
|
|
182
|
-
for (const [s, c] of sorted(totales.comandos).slice(0, 20)) {
|
|
183
|
-
console.log(`| ${s} | ${c} |`);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
if (skillsNoUsados.length > 0) {
|
|
187
|
-
console.log('\n## Skills nunca usados (candidatos a evaluación)\n');
|
|
188
|
-
console.log('| Skill |');
|
|
189
|
-
console.log('|-------|');
|
|
190
|
-
for (const s of skillsNoUsados.sort()) {
|
|
191
|
-
console.log(`| ${s} |`);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
console.log('\n---');
|
|
196
|
-
console.log(`\n> Generado por \`node scripts/field-report.js\``);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
generarReporte();
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* field-report.js — Generador de reportes de validación en campo.
|
|
6
|
+
*
|
|
7
|
+
* Analiza sesiones JSONL de Claude Code para extraer métricas reales de uso
|
|
8
|
+
* de skills y agentes SWL en proyectos de usuario.
|
|
9
|
+
*
|
|
10
|
+
* Uso:
|
|
11
|
+
* node scripts/field-report.js # Todas las sesiones
|
|
12
|
+
* node scripts/field-report.js --days=30 # Últimos 30 días
|
|
13
|
+
* node scripts/field-report.js --project=/path # Proyecto específico
|
|
14
|
+
* node scripts/field-report.js --output=json # Salida JSON
|
|
15
|
+
*
|
|
16
|
+
* Métricas extraídas:
|
|
17
|
+
* - Skills invocados (Skill("nombre"))
|
|
18
|
+
* - Agentes delegados (Agent con description)
|
|
19
|
+
* - Comandos /swl:* ejecutados
|
|
20
|
+
* - Proyectos donde se usó swl-ses
|
|
21
|
+
* - Frecuencia de uso por componente
|
|
22
|
+
* - Skills nunca usados (candidatos a poda)
|
|
23
|
+
*
|
|
24
|
+
* @module scripts/field-report
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
const fs = require('fs');
|
|
28
|
+
const path = require('path');
|
|
29
|
+
const os = require('os');
|
|
30
|
+
|
|
31
|
+
// ── Argumentos ────────────────────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
const args = process.argv.slice(2).reduce((acc, arg) => {
|
|
34
|
+
const [k, v] = arg.replace(/^--/, '').split('=');
|
|
35
|
+
acc[k] = v || true;
|
|
36
|
+
return acc;
|
|
37
|
+
}, {});
|
|
38
|
+
|
|
39
|
+
const DAYS = parseInt(args.days) || 0;
|
|
40
|
+
const PROJECT = args.project || null;
|
|
41
|
+
const OUTPUT = args.output || 'md';
|
|
42
|
+
const CLAUDE_DIR = path.join(os.homedir(), '.claude', 'projects');
|
|
43
|
+
|
|
44
|
+
// ── Explorar sesiones ─────────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
function buscarSesiones() {
|
|
47
|
+
const sesiones = [];
|
|
48
|
+
if (!fs.existsSync(CLAUDE_DIR)) {
|
|
49
|
+
console.error(`No se encontró ${CLAUDE_DIR}`);
|
|
50
|
+
return sesiones;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const proyectos = fs.readdirSync(CLAUDE_DIR).filter(d =>
|
|
54
|
+
fs.statSync(path.join(CLAUDE_DIR, d)).isDirectory()
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
for (const proy of proyectos) {
|
|
58
|
+
if (PROJECT && !proy.includes(PROJECT.replace(/[/\\:]/g, '-'))) continue;
|
|
59
|
+
const dir = path.join(CLAUDE_DIR, proy);
|
|
60
|
+
const archivos = fs.readdirSync(dir).filter(f => f.endsWith('.jsonl'));
|
|
61
|
+
for (const archivo of archivos) {
|
|
62
|
+
const ruta = path.join(dir, archivo);
|
|
63
|
+
const stat = fs.statSync(ruta);
|
|
64
|
+
if (DAYS > 0) {
|
|
65
|
+
const hace = Date.now() - DAYS * 86400000;
|
|
66
|
+
if (stat.mtimeMs < hace) continue;
|
|
67
|
+
}
|
|
68
|
+
sesiones.push({ proyecto: proy, ruta, fecha: stat.mtime });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return sesiones;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function analizarSesion(ruta) {
|
|
76
|
+
const stats = { skills: {}, agentes: {}, comandos: {}, toolCalls: 0 };
|
|
77
|
+
try {
|
|
78
|
+
const contenido = fs.readFileSync(ruta, 'utf8');
|
|
79
|
+
const lineas = contenido.split('\n').filter(Boolean);
|
|
80
|
+
|
|
81
|
+
for (const linea of lineas) {
|
|
82
|
+
try {
|
|
83
|
+
const entry = JSON.parse(linea);
|
|
84
|
+
const msg = entry.message || entry;
|
|
85
|
+
|
|
86
|
+
// Buscar Skill("nombre") en contenido
|
|
87
|
+
const texto = JSON.stringify(msg);
|
|
88
|
+
const skillMatches = texto.matchAll(/Skill\s*\(\s*["']([^"']+)["']\s*\)/g);
|
|
89
|
+
for (const m of skillMatches) {
|
|
90
|
+
stats.skills[m[1]] = (stats.skills[m[1]] || 0) + 1;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Buscar agentes delegados
|
|
94
|
+
const agentMatches = texto.matchAll(/"description"\s*:\s*"([^"]*swl[^"]*)"/gi);
|
|
95
|
+
for (const m of agentMatches) {
|
|
96
|
+
const nombre = m[1].substring(0, 60);
|
|
97
|
+
stats.agentes[nombre] = (stats.agentes[nombre] || 0) + 1;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Buscar comandos /swl:*
|
|
101
|
+
const cmdMatches = texto.matchAll(/\/swl:([a-z-]+)/g);
|
|
102
|
+
for (const m of cmdMatches) {
|
|
103
|
+
const cmd = `/swl:${m[1]}`;
|
|
104
|
+
stats.comandos[cmd] = (stats.comandos[cmd] || 0) + 1;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Contar tool calls
|
|
108
|
+
if (msg.type === 'tool_use' || msg.tool_name) stats.toolCalls++;
|
|
109
|
+
} catch { /* línea JSONL inválida, ignorar */ }
|
|
110
|
+
}
|
|
111
|
+
} catch { /* archivo ilegible, ignorar */ }
|
|
112
|
+
|
|
113
|
+
return stats;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ── Generar reporte ───────────────────────────────────────────────────
|
|
117
|
+
|
|
118
|
+
function generarReporte() {
|
|
119
|
+
const sesiones = buscarSesiones();
|
|
120
|
+
if (sesiones.length === 0) {
|
|
121
|
+
console.log('No se encontraron sesiones para analizar.');
|
|
122
|
+
console.log(`Directorio buscado: ${CLAUDE_DIR}`);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const totales = { skills: {}, agentes: {}, comandos: {}, sesiones: sesiones.length, proyectos: new Set(), toolCalls: 0 };
|
|
127
|
+
|
|
128
|
+
for (const sesion of sesiones) {
|
|
129
|
+
totales.proyectos.add(sesion.proyecto);
|
|
130
|
+
const stats = analizarSesion(sesion.ruta);
|
|
131
|
+
totales.toolCalls += stats.toolCalls;
|
|
132
|
+
|
|
133
|
+
for (const [k, v] of Object.entries(stats.skills)) totales.skills[k] = (totales.skills[k] || 0) + v;
|
|
134
|
+
for (const [k, v] of Object.entries(stats.agentes)) totales.agentes[k] = (totales.agentes[k] || 0) + v;
|
|
135
|
+
for (const [k, v] of Object.entries(stats.comandos)) totales.comandos[k] = (totales.comandos[k] || 0) + v;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Cargar inventario de skills
|
|
139
|
+
const skillsDir = path.join(__dirname, '..', 'habilidades');
|
|
140
|
+
const todosSkills = fs.existsSync(skillsDir)
|
|
141
|
+
? fs.readdirSync(skillsDir).filter(d => fs.statSync(path.join(skillsDir, d)).isDirectory())
|
|
142
|
+
: [];
|
|
143
|
+
|
|
144
|
+
const skillsUsados = new Set(Object.keys(totales.skills));
|
|
145
|
+
const skillsNoUsados = todosSkills.filter(s => !skillsUsados.has(s));
|
|
146
|
+
|
|
147
|
+
if (OUTPUT === 'json') {
|
|
148
|
+
console.log(JSON.stringify({
|
|
149
|
+
fecha: new Date().toISOString(),
|
|
150
|
+
sesiones: totales.sesiones,
|
|
151
|
+
proyectos: [...totales.proyectos],
|
|
152
|
+
toolCalls: totales.toolCalls,
|
|
153
|
+
skills: Object.entries(totales.skills).sort((a, b) => b[1] - a[1]),
|
|
154
|
+
agentes: Object.entries(totales.agentes).sort((a, b) => b[1] - a[1]),
|
|
155
|
+
comandos: Object.entries(totales.comandos).sort((a, b) => b[1] - a[1]),
|
|
156
|
+
skillsNoUsados,
|
|
157
|
+
cobertura: `${skillsUsados.size}/${todosSkills.length}`,
|
|
158
|
+
}, null, 2));
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Markdown
|
|
163
|
+
const sorted = (obj) => Object.entries(obj).sort((a, b) => b[1] - a[1]);
|
|
164
|
+
|
|
165
|
+
console.log('# Reporte de Validación en Campo — swl-ses');
|
|
166
|
+
console.log(`\n**Fecha**: ${new Date().toISOString().slice(0, 10)}`);
|
|
167
|
+
console.log(`**Sesiones analizadas**: ${totales.sesiones}`);
|
|
168
|
+
console.log(`**Proyectos**: ${totales.proyectos.size}`);
|
|
169
|
+
console.log(`**Tool calls totales**: ${totales.toolCalls}`);
|
|
170
|
+
console.log(`**Cobertura de skills**: ${skillsUsados.size}/${todosSkills.length} (${Math.round(skillsUsados.size / todosSkills.length * 100)}%)`);
|
|
171
|
+
|
|
172
|
+
console.log('\n## Skills más usados\n');
|
|
173
|
+
console.log('| Skill | Invocaciones |');
|
|
174
|
+
console.log('|-------|-------------|');
|
|
175
|
+
for (const [s, c] of sorted(totales.skills).slice(0, 30)) {
|
|
176
|
+
console.log(`| ${s} | ${c} |`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
console.log('\n## Comandos /swl:* más usados\n');
|
|
180
|
+
console.log('| Comando | Invocaciones |');
|
|
181
|
+
console.log('|---------|-------------|');
|
|
182
|
+
for (const [s, c] of sorted(totales.comandos).slice(0, 20)) {
|
|
183
|
+
console.log(`| ${s} | ${c} |`);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (skillsNoUsados.length > 0) {
|
|
187
|
+
console.log('\n## Skills nunca usados (candidatos a evaluación)\n');
|
|
188
|
+
console.log('| Skill |');
|
|
189
|
+
console.log('|-------|');
|
|
190
|
+
for (const s of skillsNoUsados.sort()) {
|
|
191
|
+
console.log(`| ${s} |`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
console.log('\n---');
|
|
196
|
+
console.log(`\n> Generado por \`node scripts/field-report.js\``);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
generarReporte();
|