@saulwade/swl-ses 1.5.0 → 1.5.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 +19 -2
- package/README.md +561 -561
- package/agentes/arquitecto-swl.md +33 -1
- package/agentes/nemesis-auditor-swl.md +59 -19
- package/bin/swl-mcp-server.js +214 -214
- package/comandos/swl/.evolved.json +22 -22
- package/comandos/swl/contribuir.md +233 -233
- package/comandos/swl/nemesis.md +230 -56
- 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/ejecutar-task-iterativo/SKILL.md +278 -278
- 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/SKILL.md +225 -1
- 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/nemesis-evaluacion-json/SKILL.md +266 -0
- package/habilidades/nemesis-redistribuir/SKILL.md +341 -0
- package/habilidades/node-experto/SKILL.md +105 -4
- 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/protocolo-revision-swl/SKILL.md +350 -276
- 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/tdd-workflow/SKILL.md +150 -4
- package/habilidades/testing-python/SKILL.md +340 -340
- package/habilidades/verificar-trabajo/SKILL.md +8 -3
- package/habilidades/web-fetcher-routing/SKILL.md +75 -75
- package/hooks/check-update.js +31 -3
- 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 +1324 -1321
- package/manifiestos/skills-lock.json +1114 -1114
- package/package.json +2 -2
- 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 +353 -351
- 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/registro-componentes-nuevos.md +192 -0
- package/reglas/usar-context7.md +226 -226
- package/schemas/diary-entry.schema.json +80 -80
- package/scripts/actualizar.js +110 -1
- 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/derivar-feature-list.js +489 -489
- package/scripts/detectar-aprendizajes-duplicados.js +151 -151
- package/scripts/doctor.js +58 -4
- 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/expandir-targets.js +71 -71
- package/scripts/lib/jaccard-similarity.js +98 -98
- package/scripts/lib/longmemeval-runner.js +125 -125
- package/scripts/lib/mcp_config.py +127 -0
- 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/lib/toml-merge.js +204 -204
- package/scripts/lib/transformadores/codex.js +375 -375
- package/scripts/lib/transformadores/cursor.js +359 -359
- package/scripts/limpiar-artefactos-python.js +131 -131
- package/scripts/mcp-orchestrator.py +8 -18
- package/scripts/mcp-pool-manager.py +12 -23
- package/scripts/mcp-server/README.md +170 -170
- package/scripts/mcp-server/auth.js +105 -105
- package/scripts/mcp-server/cache.js +106 -106
- package/scripts/mcp-server/telemetry.js +78 -78
- 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-userland-vacio.js +110 -110
|
@@ -1,151 +1,151 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* detectar-aprendizajes-duplicados.js
|
|
6
|
-
*
|
|
7
|
-
* Detecta pares de entradas en `.planning/APRENDIZAJES.md` con alta similitud
|
|
8
|
-
* de tokens (Jaccard > umbral). Útil para identificar candidatos a fusionar
|
|
9
|
-
* cuando el hook de auto-extracción genera entradas redundantes.
|
|
10
|
-
*
|
|
11
|
-
* Patrón adoptado de `temp/agentmemory-main/src/functions/auto-forget.ts`
|
|
12
|
-
* (contradiction detection con Jaccard >= 0.9). Aquí se usa con threshold
|
|
13
|
-
* configurable más bajo (0.6 default) porque queremos sugerir, no auto-borrar.
|
|
14
|
-
*
|
|
15
|
-
* NO modifica APRENDIZAJES.md. Solo reporta. La acción de fusión queda en
|
|
16
|
-
* manos del usuario o de un comando separado (`/swl:aprender consolidar`).
|
|
17
|
-
*
|
|
18
|
-
* Uso:
|
|
19
|
-
* node scripts/detectar-aprendizajes-duplicados.js [threshold]
|
|
20
|
-
*
|
|
21
|
-
* Argumentos:
|
|
22
|
-
* threshold - Similitud mínima para reportar (default: 0.6, rango [0, 1]).
|
|
23
|
-
*
|
|
24
|
-
* Exit codes:
|
|
25
|
-
* 0 - Ejecución OK (haya o no duplicados)
|
|
26
|
-
* 1 - Error de I/O o parseo
|
|
27
|
-
*
|
|
28
|
-
* Output: tabla legible en stdout. Si se detectan ≥ 1 duplicados, también
|
|
29
|
-
* imprime sugerencia para revisar/consolidar.
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
const fs = require('fs');
|
|
33
|
-
const path = require('path');
|
|
34
|
-
|
|
35
|
-
const { tokenize, jaccard } = require('./lib/jaccard-similarity');
|
|
36
|
-
|
|
37
|
-
const RUTA_APRENDIZAJES = path.join(process.cwd(), '.planning', 'APRENDIZAJES.md');
|
|
38
|
-
const DEFAULT_THRESHOLD = 0.6;
|
|
39
|
-
const MAX_PARES_REPORTADOS = 30;
|
|
40
|
-
|
|
41
|
-
function parsearEntradas(contenido) {
|
|
42
|
-
const lineas = contenido.split('\n');
|
|
43
|
-
const entradas = [];
|
|
44
|
-
let actual = null;
|
|
45
|
-
|
|
46
|
-
for (let i = 0; i < lineas.length; i++) {
|
|
47
|
-
const linea = lineas[i];
|
|
48
|
-
if (linea.startsWith('## ')) {
|
|
49
|
-
if (actual) entradas.push(actual);
|
|
50
|
-
actual = {
|
|
51
|
-
lineaInicio: i + 1,
|
|
52
|
-
titulo: linea.slice(3).trim(),
|
|
53
|
-
contenido: '',
|
|
54
|
-
};
|
|
55
|
-
} else if (actual) {
|
|
56
|
-
actual.contenido += linea + '\n';
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
if (actual) entradas.push(actual);
|
|
60
|
-
|
|
61
|
-
// Filtrar entradas vacías o triviales (< 50 chars de contenido real)
|
|
62
|
-
return entradas.filter(e => e.contenido.replace(/\s/g, '').length >= 50);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function detectarDuplicados(entradas, threshold) {
|
|
66
|
-
const tokensCache = entradas.map(e => tokenize(e.titulo + ' ' + e.contenido));
|
|
67
|
-
const pares = [];
|
|
68
|
-
|
|
69
|
-
for (let i = 0; i < entradas.length; i++) {
|
|
70
|
-
for (let j = i + 1; j < entradas.length; j++) {
|
|
71
|
-
const sim = jaccard(tokensCache[i], tokensCache[j]);
|
|
72
|
-
if (sim >= threshold) {
|
|
73
|
-
pares.push({
|
|
74
|
-
entradaA: entradas[i],
|
|
75
|
-
entradaB: entradas[j],
|
|
76
|
-
similitud: sim,
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
pares.sort((a, b) => b.similitud - a.similitud);
|
|
83
|
-
return pares;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function reportarTexto(pares) {
|
|
87
|
-
if (pares.length === 0) {
|
|
88
|
-
console.log('Sin duplicados detectados sobre el umbral.');
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
console.log(`Pares con similitud Jaccard ≥ umbral: ${pares.length}`);
|
|
93
|
-
console.log('');
|
|
94
|
-
|
|
95
|
-
const limite = Math.min(pares.length, MAX_PARES_REPORTADOS);
|
|
96
|
-
for (let i = 0; i < limite; i++) {
|
|
97
|
-
const p = pares[i];
|
|
98
|
-
console.log(` [${(p.similitud * 100).toFixed(1)}%] ` +
|
|
99
|
-
`L${p.entradaA.lineaInicio} ↔ L${p.entradaB.lineaInicio}`);
|
|
100
|
-
console.log(' A: ' + p.entradaA.titulo.slice(0, 80));
|
|
101
|
-
console.log(' B: ' + p.entradaB.titulo.slice(0, 80));
|
|
102
|
-
console.log('');
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (pares.length > limite) {
|
|
106
|
-
console.log(` ... ${pares.length - limite} pares adicionales no mostrados`);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
console.log('Sugerencia: revisa los pares con mayor similitud y considera ' +
|
|
110
|
-
'fusionarlos en una sola entrada con `/swl:aprender consolidar` o manualmente.');
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function main() {
|
|
114
|
-
const threshold = parseFloat(process.argv[2]) || DEFAULT_THRESHOLD;
|
|
115
|
-
|
|
116
|
-
if (!Number.isFinite(threshold) || threshold < 0 || threshold > 1) {
|
|
117
|
-
console.error(`Threshold inválido: ${process.argv[2]}. Usar valor en [0, 1].`);
|
|
118
|
-
process.exit(1);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (!fs.existsSync(RUTA_APRENDIZAJES)) {
|
|
122
|
-
console.error(`No existe ${RUTA_APRENDIZAJES}.`);
|
|
123
|
-
process.exit(1);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
let contenido;
|
|
127
|
-
try {
|
|
128
|
-
contenido = fs.readFileSync(RUTA_APRENDIZAJES, 'utf8');
|
|
129
|
-
} catch (err) {
|
|
130
|
-
console.error(`Error leyendo ${RUTA_APRENDIZAJES}: ${err.message}`);
|
|
131
|
-
process.exit(1);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const entradas = parsearEntradas(contenido);
|
|
135
|
-
console.log(`Entradas encontradas: ${entradas.length}`);
|
|
136
|
-
console.log(`Threshold de similitud: ${threshold}`);
|
|
137
|
-
console.log('');
|
|
138
|
-
|
|
139
|
-
const pares = detectarDuplicados(entradas, threshold);
|
|
140
|
-
reportarTexto(pares);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (require.main === module) {
|
|
144
|
-
main();
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
module.exports = {
|
|
148
|
-
parsearEntradas,
|
|
149
|
-
detectarDuplicados,
|
|
150
|
-
reportarTexto,
|
|
151
|
-
};
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* detectar-aprendizajes-duplicados.js
|
|
6
|
+
*
|
|
7
|
+
* Detecta pares de entradas en `.planning/APRENDIZAJES.md` con alta similitud
|
|
8
|
+
* de tokens (Jaccard > umbral). Útil para identificar candidatos a fusionar
|
|
9
|
+
* cuando el hook de auto-extracción genera entradas redundantes.
|
|
10
|
+
*
|
|
11
|
+
* Patrón adoptado de `temp/agentmemory-main/src/functions/auto-forget.ts`
|
|
12
|
+
* (contradiction detection con Jaccard >= 0.9). Aquí se usa con threshold
|
|
13
|
+
* configurable más bajo (0.6 default) porque queremos sugerir, no auto-borrar.
|
|
14
|
+
*
|
|
15
|
+
* NO modifica APRENDIZAJES.md. Solo reporta. La acción de fusión queda en
|
|
16
|
+
* manos del usuario o de un comando separado (`/swl:aprender consolidar`).
|
|
17
|
+
*
|
|
18
|
+
* Uso:
|
|
19
|
+
* node scripts/detectar-aprendizajes-duplicados.js [threshold]
|
|
20
|
+
*
|
|
21
|
+
* Argumentos:
|
|
22
|
+
* threshold - Similitud mínima para reportar (default: 0.6, rango [0, 1]).
|
|
23
|
+
*
|
|
24
|
+
* Exit codes:
|
|
25
|
+
* 0 - Ejecución OK (haya o no duplicados)
|
|
26
|
+
* 1 - Error de I/O o parseo
|
|
27
|
+
*
|
|
28
|
+
* Output: tabla legible en stdout. Si se detectan ≥ 1 duplicados, también
|
|
29
|
+
* imprime sugerencia para revisar/consolidar.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
const fs = require('fs');
|
|
33
|
+
const path = require('path');
|
|
34
|
+
|
|
35
|
+
const { tokenize, jaccard } = require('./lib/jaccard-similarity');
|
|
36
|
+
|
|
37
|
+
const RUTA_APRENDIZAJES = path.join(process.cwd(), '.planning', 'APRENDIZAJES.md');
|
|
38
|
+
const DEFAULT_THRESHOLD = 0.6;
|
|
39
|
+
const MAX_PARES_REPORTADOS = 30;
|
|
40
|
+
|
|
41
|
+
function parsearEntradas(contenido) {
|
|
42
|
+
const lineas = contenido.split('\n');
|
|
43
|
+
const entradas = [];
|
|
44
|
+
let actual = null;
|
|
45
|
+
|
|
46
|
+
for (let i = 0; i < lineas.length; i++) {
|
|
47
|
+
const linea = lineas[i];
|
|
48
|
+
if (linea.startsWith('## ')) {
|
|
49
|
+
if (actual) entradas.push(actual);
|
|
50
|
+
actual = {
|
|
51
|
+
lineaInicio: i + 1,
|
|
52
|
+
titulo: linea.slice(3).trim(),
|
|
53
|
+
contenido: '',
|
|
54
|
+
};
|
|
55
|
+
} else if (actual) {
|
|
56
|
+
actual.contenido += linea + '\n';
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (actual) entradas.push(actual);
|
|
60
|
+
|
|
61
|
+
// Filtrar entradas vacías o triviales (< 50 chars de contenido real)
|
|
62
|
+
return entradas.filter(e => e.contenido.replace(/\s/g, '').length >= 50);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function detectarDuplicados(entradas, threshold) {
|
|
66
|
+
const tokensCache = entradas.map(e => tokenize(e.titulo + ' ' + e.contenido));
|
|
67
|
+
const pares = [];
|
|
68
|
+
|
|
69
|
+
for (let i = 0; i < entradas.length; i++) {
|
|
70
|
+
for (let j = i + 1; j < entradas.length; j++) {
|
|
71
|
+
const sim = jaccard(tokensCache[i], tokensCache[j]);
|
|
72
|
+
if (sim >= threshold) {
|
|
73
|
+
pares.push({
|
|
74
|
+
entradaA: entradas[i],
|
|
75
|
+
entradaB: entradas[j],
|
|
76
|
+
similitud: sim,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
pares.sort((a, b) => b.similitud - a.similitud);
|
|
83
|
+
return pares;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function reportarTexto(pares) {
|
|
87
|
+
if (pares.length === 0) {
|
|
88
|
+
console.log('Sin duplicados detectados sobre el umbral.');
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
console.log(`Pares con similitud Jaccard ≥ umbral: ${pares.length}`);
|
|
93
|
+
console.log('');
|
|
94
|
+
|
|
95
|
+
const limite = Math.min(pares.length, MAX_PARES_REPORTADOS);
|
|
96
|
+
for (let i = 0; i < limite; i++) {
|
|
97
|
+
const p = pares[i];
|
|
98
|
+
console.log(` [${(p.similitud * 100).toFixed(1)}%] ` +
|
|
99
|
+
`L${p.entradaA.lineaInicio} ↔ L${p.entradaB.lineaInicio}`);
|
|
100
|
+
console.log(' A: ' + p.entradaA.titulo.slice(0, 80));
|
|
101
|
+
console.log(' B: ' + p.entradaB.titulo.slice(0, 80));
|
|
102
|
+
console.log('');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (pares.length > limite) {
|
|
106
|
+
console.log(` ... ${pares.length - limite} pares adicionales no mostrados`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
console.log('Sugerencia: revisa los pares con mayor similitud y considera ' +
|
|
110
|
+
'fusionarlos en una sola entrada con `/swl:aprender consolidar` o manualmente.');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function main() {
|
|
114
|
+
const threshold = parseFloat(process.argv[2]) || DEFAULT_THRESHOLD;
|
|
115
|
+
|
|
116
|
+
if (!Number.isFinite(threshold) || threshold < 0 || threshold > 1) {
|
|
117
|
+
console.error(`Threshold inválido: ${process.argv[2]}. Usar valor en [0, 1].`);
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!fs.existsSync(RUTA_APRENDIZAJES)) {
|
|
122
|
+
console.error(`No existe ${RUTA_APRENDIZAJES}.`);
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let contenido;
|
|
127
|
+
try {
|
|
128
|
+
contenido = fs.readFileSync(RUTA_APRENDIZAJES, 'utf8');
|
|
129
|
+
} catch (err) {
|
|
130
|
+
console.error(`Error leyendo ${RUTA_APRENDIZAJES}: ${err.message}`);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const entradas = parsearEntradas(contenido);
|
|
135
|
+
console.log(`Entradas encontradas: ${entradas.length}`);
|
|
136
|
+
console.log(`Threshold de similitud: ${threshold}`);
|
|
137
|
+
console.log('');
|
|
138
|
+
|
|
139
|
+
const pares = detectarDuplicados(entradas, threshold);
|
|
140
|
+
reportarTexto(pares);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (require.main === module) {
|
|
144
|
+
main();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
module.exports = {
|
|
148
|
+
parsearEntradas,
|
|
149
|
+
detectarDuplicados,
|
|
150
|
+
reportarTexto,
|
|
151
|
+
};
|
package/scripts/doctor.js
CHANGED
|
@@ -50,6 +50,33 @@ async function doctor(opciones = {}) {
|
|
|
50
50
|
advertencias++;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
// 0a. Estado del hook automático check-update.js (ADR-0021 opt-in visibility).
|
|
54
|
+
// Permite al usuario diagnosticar si el hook está ejecutándose correctamente
|
|
55
|
+
// sin tener que esperar a que aparezca una nueva versión real.
|
|
56
|
+
try {
|
|
57
|
+
const os = require('os');
|
|
58
|
+
// SWL_UPDATE_FLAG_PATH permite a los tests aislar el flag por test (evita
|
|
59
|
+
// race entre tests paralelos sobre el flag único de producción).
|
|
60
|
+
const flagPath = process.env.SWL_UPDATE_FLAG_PATH || path.join(os.tmpdir(), 'swl-ses-update-check.json');
|
|
61
|
+
if (fs.existsSync(flagPath)) {
|
|
62
|
+
const flag = JSON.parse(fs.readFileSync(flagPath, 'utf8'));
|
|
63
|
+
const horas = ((Date.now() - flag.timestamp) / 3600000).toFixed(1);
|
|
64
|
+
const detalle = `local=${flag.local}, remota=${flag.remota || '?'}, hace ${horas}h`;
|
|
65
|
+
if (flag.hayNueva) {
|
|
66
|
+
console.log(formatearAdvertencia('Hook check-update', `${detalle} — hay versión nueva pendiente`));
|
|
67
|
+
advertencias++;
|
|
68
|
+
} else {
|
|
69
|
+
console.log(formatearPaso('Hook check-update', detalle));
|
|
70
|
+
ok++;
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
console.log(formatearAdvertencia('Hook check-update', 'Sin flag de throttle — el hook nunca se ejecutó en este equipo'));
|
|
74
|
+
console.log(` ${color.dim('Diagnóstico: SWL_FORCE_UPDATE_NOTIFICATION=1 + abre Claude Code para forzar una ejecución.')}`);
|
|
75
|
+
advertencias++;
|
|
76
|
+
}
|
|
77
|
+
} catch { /* nunca bloquear el doctor por este check */ }
|
|
78
|
+
console.log('');
|
|
79
|
+
|
|
53
80
|
// 0b. Verificar drift entre versionSistema instalado y versión del binario CLI
|
|
54
81
|
// ejecutándose (bug D v1.3.5).
|
|
55
82
|
//
|
|
@@ -324,8 +351,14 @@ async function doctor(opciones = {}) {
|
|
|
324
351
|
// 5b-2. Verificar conteo de componentes contra perfil esperado.
|
|
325
352
|
// Bug H v1.3.5: antes solo verificaba runtime.local; instalaciones globales
|
|
326
353
|
// (~/.claude/) quedaban sin check de conteo. Ahora itera ambos scopes.
|
|
354
|
+
// Sub-fase 12 v1.5.0: deduplicar paths cuando cwd === homedir (runtime.local
|
|
355
|
+
// y runtime.global colapsan al mismo directorio) — evita reporte doble.
|
|
327
356
|
for (const runtime of runtimes) {
|
|
328
|
-
|
|
357
|
+
const dirsUnicos = Array.from(new Set([
|
|
358
|
+
path.resolve(runtime.global),
|
|
359
|
+
path.resolve(runtime.local),
|
|
360
|
+
]));
|
|
361
|
+
for (const dir of dirsUnicos) {
|
|
329
362
|
if (!fs.existsSync(dir)) continue;
|
|
330
363
|
const estado = cargarEstado(dir);
|
|
331
364
|
if (!estado || !estado.perfil) continue;
|
|
@@ -367,8 +400,21 @@ async function doctor(opciones = {}) {
|
|
|
367
400
|
resolucion.archivos = filtrado.archivos;
|
|
368
401
|
}
|
|
369
402
|
|
|
370
|
-
//
|
|
371
|
-
|
|
403
|
+
// Sub-fase 12 v1.5.0: filtrar tipos principales por runtime.tiposSoportados
|
|
404
|
+
// Y por la presencia de `dir<Tipo>` no-null. Codex declara `reglas` en
|
|
405
|
+
// tiposSoportados pero las consolida en AGENTS.md (dirReglas: null) — los
|
|
406
|
+
// archivos individuales no existen aunque el resolver los liste como esperados.
|
|
407
|
+
// Reportar "faltan reglas: 0/N" en ese caso es falso positivo que dispara
|
|
408
|
+
// loop infinito de auto-reparación.
|
|
409
|
+
const TIPO_A_DIR = {
|
|
410
|
+
agentes: 'dirAgentes',
|
|
411
|
+
habilidades: 'dirHabilidades',
|
|
412
|
+
comandos: 'dirComandos',
|
|
413
|
+
reglas: 'dirReglas',
|
|
414
|
+
};
|
|
415
|
+
const tiposPrincipales = ['agentes', 'habilidades', 'comandos', 'reglas']
|
|
416
|
+
.filter(t => !runtime.tiposSoportados || runtime.tiposSoportados.includes(t))
|
|
417
|
+
.filter(t => runtime[TIPO_A_DIR[t]]);
|
|
372
418
|
const esperadosPorTipo = {};
|
|
373
419
|
for (const archivo of resolucion.archivos) {
|
|
374
420
|
const tipo = archivo.tipo || 'otros';
|
|
@@ -451,8 +497,16 @@ async function doctor(opciones = {}) {
|
|
|
451
497
|
}
|
|
452
498
|
}
|
|
453
499
|
|
|
454
|
-
// 5c. Verificar optimizaciones de rendimiento en settings.json
|
|
500
|
+
// 5c. Verificar optimizaciones de rendimiento en settings.json.
|
|
501
|
+
// Sub-fase 12 v1.5.0: las optimizaciones `ENABLE_TOOL_SEARCH` y
|
|
502
|
+
// `showThinkingSummaries` son específicas de Claude Code `settings.json`.
|
|
503
|
+
// Codex usa `config.toml` con schema TOML; Cursor usa schema propio.
|
|
504
|
+
// Aplicar estas claves a Codex/Cursor poluciona su config con keys que
|
|
505
|
+
// no tienen efecto. Restringir el check al runtime claude/openclaude.
|
|
506
|
+
const RUNTIMES_CON_OPTIMIZACIONES = new Set(['claude', 'openclaude']);
|
|
455
507
|
for (const runtime of runtimes) {
|
|
508
|
+
if (!RUNTIMES_CON_OPTIMIZACIONES.has(runtime.id)) continue;
|
|
509
|
+
|
|
456
510
|
const dir = path.resolve(runtime.local);
|
|
457
511
|
if (!fs.existsSync(dir)) continue;
|
|
458
512
|
const estado = cargarEstado(dir);
|