@saulwade/swl-ses 2.5.2 → 2.6.0
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 +194 -192
- package/README.md +600 -600
- package/agentes/auto-evolucion-swl.md +27 -3
- package/bin/swl-ses.js +32 -6
- package/comandos/swl/actualizar.md +174 -174
- package/comandos/swl/adoptar-proyecto.md +265 -265
- package/comandos/swl/aprender.md +836 -823
- package/comandos/swl/aprobar-plan.md +146 -146
- package/comandos/swl/auditar-deps.md +134 -134
- package/comandos/swl/autoresearch.md +264 -264
- package/comandos/swl/ayuda.md +224 -224
- package/comandos/swl/brainstorm.md +51 -51
- package/comandos/swl/briefing.md +119 -119
- package/comandos/swl/checkpoint.md +325 -325
- package/comandos/swl/claudemd.md +234 -234
- package/comandos/swl/compactar.md +310 -310
- package/comandos/swl/configurar-ci.md +235 -235
- package/comandos/swl/contexto.md +110 -110
- package/comandos/swl/contribuir.md +233 -233
- package/comandos/swl/crear-skill.md +292 -292
- package/comandos/swl/cron.md +194 -194
- package/comandos/swl/deuda-codigo.md +97 -97
- package/comandos/swl/discutir-fase.md +169 -169
- package/comandos/swl/ejecutar-fase.md +233 -233
- package/comandos/swl/evaluar-skill.md +520 -505
- package/comandos/swl/evolucion-continua.md +73 -0
- package/comandos/swl/evolucionar.md +267 -254
- package/comandos/swl/exportar-vault.md +583 -583
- package/comandos/swl/fix.md +118 -118
- package/comandos/swl/gateway.md +158 -158
- package/comandos/swl/inbox.md +116 -116
- package/comandos/swl/instalar.md +220 -220
- package/comandos/swl/instintos.md +86 -86
- package/comandos/swl/mapear-codebase.md +312 -312
- package/comandos/swl/mcp-status.md +175 -175
- package/comandos/swl/modelo.md +100 -100
- package/comandos/swl/nemesis.md +433 -433
- package/comandos/swl/notificaciones.md +299 -299
- package/comandos/swl/nuevo-proyecto.md +251 -251
- package/comandos/swl/planear-fase.md +263 -263
- package/comandos/swl/plugins.md +256 -256
- package/comandos/swl/predecir.md +169 -169
- package/comandos/swl/reflect-skills.md +125 -125
- package/comandos/swl/release.md +450 -450
- package/comandos/swl/revisar-impacto.md +201 -201
- package/comandos/swl/revisar.md +330 -330
- package/comandos/swl/seguridad.md +189 -189
- package/comandos/swl/sesiones.md +200 -200
- package/comandos/swl/skill-search.md +113 -113
- package/comandos/swl/status.md +343 -343
- package/comandos/swl/verificar.md +817 -817
- package/comandos/swl/wiki.md +620 -620
- package/gateway/cron/jobs.example.json +12 -0
- package/habilidades/auto-evolucion-protocolo/SKILL.md +294 -276
- package/habilidades/autoresearch/SKILL.md +3 -2
- package/habilidades/benchmark-memoria/SKILL.md +7 -7
- package/habilidades/changelog-generator/SKILL.md +174 -174
- package/habilidades/changelog-generator/scripts/parse-commits.js +2 -1
- package/habilidades/checkpoints-verificacion/SKILL.md +6 -0
- package/habilidades/context-builder/SKILL.md +4 -0
- package/habilidades/doubt-driven-review/SKILL.md +207 -191
- package/habilidades/drift-detection/SKILL.md +6 -1
- package/habilidades/ejecutar-fase/SKILL.md +6 -6
- package/habilidades/eval-framework/SKILL.md +8 -3
- package/habilidades/harness-claude-code/SKILL.md +314 -308
- package/habilidades/infra-github-actions/SKILL.md +4 -3
- package/habilidades/instalar-sistema/SKILL.md +227 -223
- package/habilidades/memoria-busqueda/SKILL.md +31 -39
- package/habilidades/planear-fase/SKILL.md +358 -350
- package/habilidades/proceso-ddia-fundamentos/SKILL.md +3 -2
- package/habilidades/release-semver/SKILL.md +4 -2
- package/habilidades/swl-claudemd/SKILL.md +6 -7
- package/habilidades/swl-dashboard/SKILL.md +11 -43
- package/habilidades/tdd-workflow/SKILL.md +749 -744
- package/habilidades/validacion-ci-sistema/SKILL.md +1 -1
- package/hooks/agente-lifecycle.js +2 -1
- package/hooks/aiisms-detector.js +13 -4
- package/hooks/audit-trail.js +2 -1
- package/hooks/auto-consolidacion.js +2 -1
- package/hooks/captura-acciones-post.js +2 -1
- package/hooks/captura-acciones-session.js +2 -1
- package/hooks/captura-feedback-usuario.js +3 -2
- package/hooks/claudemd-bloat-detector.js +12 -3
- package/hooks/claudemd-duplicacion-detector.js +13 -3
- package/hooks/contexto-iteracion.js +2 -1
- package/hooks/degradacion-instintos.js +2 -1
- package/hooks/extraccion-aprendizajes.js +109 -15
- package/hooks/grafo-contexto.js +2 -1
- package/hooks/guardrail-modelo.js +2 -1
- package/hooks/inbox-aviso.js +2 -1
- package/hooks/inyeccion-contexto.js +2 -1
- package/hooks/lib/agent-matcher.js +2 -1
- package/hooks/lib/agent-routing.js +2 -1
- package/hooks/lib/autonomia.js +5 -3
- package/hooks/lib/captura-acciones.js +2 -1
- package/hooks/lib/consolidation-lock.js +21 -10
- package/hooks/lib/etapa-auto-evolucion.js +10 -4
- package/hooks/lib/etapa-metricas.js +2 -1
- package/hooks/lib/etapa-perfil-usuario.js +20 -4
- package/hooks/lib/evolution-tracker.js +2 -1
- package/hooks/lib/gateway-notify.js +193 -179
- package/hooks/lib/loop-telemetry.js +5 -4
- package/hooks/lib/mcp-health.js +2 -1
- package/hooks/lib/memory-search.js +4 -0
- package/hooks/lib/merkle-audit.js +58 -6
- package/hooks/lib/nudge-tracker.js +2 -1
- package/hooks/lib/otlp-exporter.js +2 -1
- package/hooks/lib/propose-step.js +3 -2
- package/hooks/lib/raiz-proyecto.js +102 -0
- package/hooks/lib/run-log.js +2 -1
- package/hooks/lib/singleton-guard.js +218 -27
- package/hooks/lib/telegram-cliente.js +17 -8
- package/hooks/preservar-estado-pre-compact.js +2 -1
- package/hooks/proteccion-rutas.js +59 -3
- package/hooks/registro-turnos.js +2 -1
- package/hooks/resumen-sesion.js +2 -1
- package/hooks/risk-scoring.js +2 -1
- package/hooks/rotar-audit-auto.js +46 -20
- package/hooks/session-briefing.js +127 -1
- package/hooks/spec-gate.js +2 -1
- package/hooks/sugerir-contribuir.js +6 -3
- package/hooks/sugerir-regenerar-inventario.js +3 -2
- package/hooks/tdd-gate.js +2 -1
- package/hooks/telemetria-agentes.js +2 -1
- package/hooks/telemetria-skill-routing.js +2 -1
- package/hooks/tracking-costos.js +4 -3
- package/hooks/validar-formato-post-subagente.js +2 -1
- package/hooks/validar-intent-spec.js +2 -1
- package/hooks/validar-memoria-hook.js +13 -3
- package/hooks/validar-planning-paths.js +2 -1
- package/instintos/.backups/perfil-usuario.yaml.2026-07-10-165128.bak +53 -0
- package/instintos/.backups/proyecto.yaml.2026-07-10-165128.bak +372 -0
- package/instintos/perfil-usuario.yaml +506 -3
- package/instintos/proyecto.yaml +78 -0
- package/llms.txt +2 -2
- package/manifiestos/canonical-hashes.json +664 -2
- package/manifiestos/modulos.json +19 -14
- package/manifiestos/planning-paths.json +1 -0
- package/manifiestos/skills-lock.json +53 -53
- package/package.json +2 -3
- package/plugin.json +2 -2
- package/scripts/actualizar.js +3 -0
- package/scripts/auditar-clases-conocidas.js +32 -4
- package/scripts/benchmark-memoria.js +1 -0
- package/scripts/cli/autonomia.js +23 -0
- package/scripts/cli/benchmark-memoria.js +37 -0
- package/scripts/cli/ciclo-autonomo.js +73 -0
- package/scripts/cli/ciclo-fase-b.js +102 -0
- package/scripts/cli/guardrail-metrics.js +39 -0
- package/scripts/cli/loop-telemetry.js +4 -2
- package/scripts/cli/memoria-search.js +69 -0
- package/scripts/cli/nudge-accionar.js +39 -0
- package/scripts/cli/run-eval.js +38 -0
- package/scripts/cli/run-skill-evals.js +13 -2
- package/scripts/derivar-feature-list.js +15 -14
- package/scripts/desinstalar.js +11 -0
- package/scripts/doctor.js +24 -10
- package/scripts/instalador.js +106 -7
- package/scripts/lib/activar-hooks-proyecto.js +116 -0
- package/scripts/lib/auditar-invocaciones-comandos.js +96 -6
- package/scripts/lib/ciclo-autonomo/candidatos.js +174 -0
- package/scripts/lib/ciclo-autonomo/config.js +165 -0
- package/scripts/lib/ciclo-autonomo/drenador-feedback.js +174 -0
- package/scripts/lib/ciclo-autonomo/fallback.js +77 -0
- package/scripts/lib/ciclo-autonomo/guard-convivencia.js +139 -0
- package/scripts/lib/ciclo-autonomo/higiene-nudges.js +112 -0
- package/scripts/lib/ciclo-autonomo/index.js +301 -0
- package/scripts/lib/ciclo-autonomo/lock.js +124 -0
- package/scripts/lib/ciclo-autonomo/presupuesto.js +122 -0
- package/scripts/lib/ciclo-autonomo/puente-degradacion.js +240 -0
- package/scripts/lib/ciclo-autonomo/runner-fase-b.js +248 -0
- package/scripts/lib/ciclo-autonomo/writer-instintos.js +190 -0
- package/scripts/lib/ciclo-autonomo/yaml-instintos.js +535 -0
- package/scripts/lib/estado.js +9 -0
- package/scripts/lib/evidencia-valor.js +1 -1
- package/scripts/lib/gitignore-manifest.js +8 -1
- package/scripts/lib/hooks-settings.js +45 -0
- package/scripts/rotar-audit-logs.js +48 -2
- package/scripts/run-eval.js +1 -0
- package/scripts/run-skill-evals.js +287 -8
- package/scripts/smoke-test.js +16 -8
- package/scripts/tui/pantallas/install-wizard.js +403 -347
- package/scripts/validar.js +40 -1
|
@@ -30,7 +30,7 @@ validar y un score de componentes sería engañoso. El gate determinista vive en
|
|
|
30
30
|
`/swl:status salud` (Paso 0):
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
|
|
33
|
+
swl-ses detectar-host --json
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
Si `esHost: false`, NO ejecutar este skill: reportar que `salud` no aplica y
|
|
@@ -21,9 +21,10 @@
|
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
23
|
const { desdePayloadHook, emitirCompletado } = require('./lib/agent-backend');
|
|
24
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
24
25
|
const es = require('./lib/execution-state');
|
|
25
26
|
|
|
26
|
-
const CWD = process.cwd();
|
|
27
|
+
const CWD = raizProyecto(process.cwd());
|
|
27
28
|
|
|
28
29
|
// ---------------------------------------------------------------------------
|
|
29
30
|
// Entrypoint
|
package/hooks/aiisms-detector.js
CHANGED
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
*/
|
|
35
35
|
|
|
36
36
|
const fs = require('fs');
|
|
37
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
37
38
|
const path = require('path');
|
|
38
39
|
const { spawnSync, randomUUID } = require('child_process');
|
|
39
40
|
const crypto = require('crypto');
|
|
@@ -92,10 +93,18 @@ if (!fs.existsSync(filePath)) {
|
|
|
92
93
|
process.exit(0);
|
|
93
94
|
}
|
|
94
95
|
|
|
95
|
-
//
|
|
96
|
-
const CWD = process.cwd();
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
// Ancla de DATOS (nudges, rutas relativas): raíz del proyecto.
|
|
97
|
+
const CWD = raizProyecto(process.cwd());
|
|
98
|
+
// El detector es EJECUTABLE de la distribución SWL: se resuelve SOLO desde
|
|
99
|
+
// la ubicación instalada del hook (__dirname), NUNCA desde el árbol del
|
|
100
|
+
// proyecto — un repo no confiable podría plantar el script (hallazgo
|
|
101
|
+
// revisor-seguridad 2026-07-10, clase spawn/require-desde-raíz).
|
|
102
|
+
const detector = [
|
|
103
|
+
path.join(__dirname, '..', 'habilidades', 'estilo-sin-ai-isms', 'scripts', 'detectar_aiisms.py'), // repo madre
|
|
104
|
+
path.join(__dirname, '..', 'skills', 'estilo-sin-ai-isms', 'scripts', 'detectar_aiisms.py'), // destino global (~/.claude/hooks ~ skills hermanos)
|
|
105
|
+
path.join(__dirname, '..', '.claude', 'skills', 'estilo-sin-ai-isms', 'scripts', 'detectar_aiisms.py'), // destino local (<proyecto>/hooks + <proyecto>/.claude/skills)
|
|
106
|
+
].find(p => { try { return fs.existsSync(p); } catch (_) { return false; } });
|
|
107
|
+
if (!detector) {
|
|
99
108
|
process.exit(0);
|
|
100
109
|
}
|
|
101
110
|
|
package/hooks/audit-trail.js
CHANGED
|
@@ -19,13 +19,14 @@
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
const fs = require('fs');
|
|
22
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
22
23
|
const path = require('path');
|
|
23
24
|
const http = require('http');
|
|
24
25
|
const https = require('https');
|
|
25
26
|
|
|
26
27
|
const { appendAudit: merkleAppend } = require(path.join(__dirname, 'lib', 'merkle-audit'));
|
|
27
28
|
|
|
28
|
-
const CWD = process.cwd();
|
|
29
|
+
const CWD = raizProyecto(process.cwd());
|
|
29
30
|
const AUDIT_FILE = path.join(CWD, '.planning', 'audit.jsonl');
|
|
30
31
|
const MERKLE_FILE = path.join(CWD, '.planning', 'audit-merkle.jsonl');
|
|
31
32
|
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
31
|
const path = require('path');
|
|
32
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
32
33
|
const fs = require('fs');
|
|
33
34
|
const os = require('os');
|
|
34
35
|
|
|
@@ -97,7 +98,7 @@ process.stdin.on('end', () => {
|
|
|
97
98
|
// Anti-spam: solo ejecutar 1 vez por sesión
|
|
98
99
|
if (alreadyRan()) return;
|
|
99
100
|
|
|
100
|
-
const baseDir = process.cwd();
|
|
101
|
+
const baseDir = raizProyecto(process.cwd());
|
|
101
102
|
|
|
102
103
|
// Verificar las 3 gates
|
|
103
104
|
const gates = checkAllGates(baseDir, {
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
const fs = require('fs');
|
|
22
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
22
23
|
const path = require('path');
|
|
23
24
|
const { execFileSync } = require('child_process');
|
|
24
25
|
|
|
@@ -142,7 +143,7 @@ if (!require.main || require.main === module) {
|
|
|
142
143
|
process.stdin.on('end', () => {
|
|
143
144
|
try {
|
|
144
145
|
const data = JSON.parse(inputRaw);
|
|
145
|
-
procesarTurno(data, process.cwd());
|
|
146
|
+
procesarTurno(data, raizProyecto(process.cwd()));
|
|
146
147
|
} catch (_) {
|
|
147
148
|
/* silencioso */
|
|
148
149
|
}
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
const fs = require('fs');
|
|
23
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
23
24
|
const path = require('path');
|
|
24
25
|
const { execFileSync } = require('child_process');
|
|
25
26
|
|
|
@@ -145,7 +146,7 @@ if (!require.main || require.main === module) {
|
|
|
145
146
|
process.stdin.on('data', (c) => { inputRaw += c; });
|
|
146
147
|
process.stdin.on('end', () => {
|
|
147
148
|
try {
|
|
148
|
-
inspeccionar(process.cwd());
|
|
149
|
+
inspeccionar(raizProyecto(process.cwd()));
|
|
149
150
|
} catch (_) {
|
|
150
151
|
/* silencioso: el hook nunca bloquea la sesión */
|
|
151
152
|
}
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
27
|
const fs = require('fs');
|
|
28
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
28
29
|
const path = require('path');
|
|
29
30
|
|
|
30
31
|
// ---------------------------------------------------------------------------
|
|
@@ -104,7 +105,7 @@ function detectarFeedback(prompt) {
|
|
|
104
105
|
*/
|
|
105
106
|
function encolar(hallazgo, meta) {
|
|
106
107
|
try {
|
|
107
|
-
const dir = path.join(process.cwd(), '.planning', 'evolution');
|
|
108
|
+
const dir = path.join(raizProyecto(process.cwd()), '.planning', 'evolution');
|
|
108
109
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
109
110
|
|
|
110
111
|
const entrada = {
|
|
@@ -140,7 +141,7 @@ process.stdin.on('end', () => {
|
|
|
140
141
|
if (hallazgos.length === 0) return;
|
|
141
142
|
|
|
142
143
|
for (const h of hallazgos) {
|
|
143
|
-
encolar(h, { sessionId, cwd: process.cwd() });
|
|
144
|
+
encolar(h, { sessionId, cwd: raizProyecto(process.cwd()) });
|
|
144
145
|
}
|
|
145
146
|
} catch (_) { /* silencioso */ }
|
|
146
147
|
});
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
*/
|
|
37
37
|
|
|
38
38
|
const fs = require('fs');
|
|
39
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
39
40
|
const path = require('path');
|
|
40
41
|
const crypto = require('crypto');
|
|
41
42
|
|
|
@@ -93,9 +94,17 @@ if (!fs.existsSync(filePath)) {
|
|
|
93
94
|
}
|
|
94
95
|
|
|
95
96
|
// ─── Ejecutar auditor (módulo, no subproceso) ─────────────────────────────
|
|
96
|
-
|
|
97
|
-
const
|
|
98
|
-
|
|
97
|
+
// Ancla de DATOS (nudges, rutas relativas): raíz del proyecto.
|
|
98
|
+
const CWD = raizProyecto(process.cwd());
|
|
99
|
+
// El auditor es MÓDULO de la distribución SWL: se resuelve SOLO desde la
|
|
100
|
+
// ubicación instalada del hook (__dirname), NUNCA desde el árbol del
|
|
101
|
+
// proyecto — un require() de un path plantado por un repo no confiable es
|
|
102
|
+
// ejecución de código (hallazgo revisor-seguridad 2026-07-10).
|
|
103
|
+
const auditorPath = [
|
|
104
|
+
path.join(__dirname, '..', 'scripts', 'auditar-claudemd.js'), // repo madre
|
|
105
|
+
path.join(__dirname, 'auditar-claudemd.js'), // destino aplanado
|
|
106
|
+
].find(p => { try { return fs.existsSync(p); } catch (_) { return false; } });
|
|
107
|
+
if (!auditorPath) {
|
|
99
108
|
// No hay auditor instalado en este destino; salir silenciosamente
|
|
100
109
|
process.exit(0);
|
|
101
110
|
}
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
*/
|
|
40
40
|
|
|
41
41
|
const fs = require('fs');
|
|
42
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
42
43
|
const path = require('path');
|
|
43
44
|
const os = require('os');
|
|
44
45
|
const crypto = require('crypto');
|
|
@@ -102,9 +103,18 @@ if (!fs.existsSync(filePath)) {
|
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
// ─── Cargar detector ──────────────────────────────────────────────────────
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
|
|
106
|
+
// Ancla de DATOS (nudges, rutas relativas): raíz del proyecto.
|
|
107
|
+
const CWD = raizProyecto(process.cwd());
|
|
108
|
+
// El detector es MÓDULO de la distribución SWL: se resuelve SOLO desde la
|
|
109
|
+
// ubicación instalada del hook (__dirname), NUNCA desde el árbol del
|
|
110
|
+
// proyecto — un require() de un path plantado por un repo no confiable es
|
|
111
|
+
// ejecución de código (hallazgo revisor-seguridad 2026-07-10).
|
|
112
|
+
const detectorPath = [
|
|
113
|
+
path.join(__dirname, '..', 'scripts', 'lib', 'detector-reglas-duplicadas.js'), // repo madre
|
|
114
|
+
path.join(__dirname, 'lib', 'detector-reglas-duplicadas.js'), // destino (scripts/lib → hooks/lib)
|
|
115
|
+
path.join(__dirname, 'detector-reglas-duplicadas.js'), // destino legado aplanado
|
|
116
|
+
].find(p => { try { return fs.existsSync(p); } catch (_) { return false; } });
|
|
117
|
+
if (!detectorPath) {
|
|
108
118
|
// No hay detector instalado en este destino; salir silenciosamente
|
|
109
119
|
process.exit(0);
|
|
110
120
|
}
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
30
|
const fs = require('fs');
|
|
31
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
31
32
|
const path = require('path');
|
|
32
33
|
|
|
33
34
|
const { atomicWriteJSON } = require('./lib/atomic-write');
|
|
@@ -112,7 +113,7 @@ function main() {
|
|
|
112
113
|
try {
|
|
113
114
|
if (process.env.SWL_CONTEXTO_ITERACION === '0') return;
|
|
114
115
|
|
|
115
|
-
const activa = corridaActiva(process.cwd(), { maxEdadMin: 30 });
|
|
116
|
+
const activa = corridaActiva(raizProyecto(process.cwd()), { maxEdadMin: 30 });
|
|
116
117
|
if (!activa) return;
|
|
117
118
|
if (!debeInyectar(activa.dir)) return;
|
|
118
119
|
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
const fs = require('fs');
|
|
23
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
23
24
|
const path = require('path');
|
|
24
25
|
|
|
25
26
|
const { atomicWriteSync } = require('./lib/atomic-write');
|
|
@@ -28,7 +29,7 @@ const { atomicWriteSync } = require('./lib/atomic-write');
|
|
|
28
29
|
// Constantes
|
|
29
30
|
// ---------------------------------------------------------------------------
|
|
30
31
|
|
|
31
|
-
const RUTA_APRENDIZAJES = path.join(process.cwd(), '.planning', 'APRENDIZAJES.md');
|
|
32
|
+
const RUTA_APRENDIZAJES = path.join(raizProyecto(process.cwd()), '.planning', 'APRENDIZAJES.md');
|
|
32
33
|
const MAX_CONTRADICCIONES = 3;
|
|
33
34
|
|
|
34
35
|
// Palabras clave que indican exito en el output del agente
|
|
@@ -31,6 +31,8 @@ const path = require('path');
|
|
|
31
31
|
const os = require('os');
|
|
32
32
|
|
|
33
33
|
const { atomicWriteSync } = require('./lib/atomic-write');
|
|
34
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
35
|
+
const { conLockRetry } = require('./lib/singleton-guard');
|
|
34
36
|
|
|
35
37
|
// ---------------------------------------------------------------------------
|
|
36
38
|
// Constantes de configuración
|
|
@@ -38,9 +40,13 @@ const { atomicWriteSync } = require('./lib/atomic-write');
|
|
|
38
40
|
|
|
39
41
|
/**
|
|
40
42
|
* Ruta del archivo donde se acumulan los aprendizajes.
|
|
41
|
-
* Se resuelve
|
|
43
|
+
* Se re-resuelve en el entrypoint con la RAÍZ DEL PROYECTO (raizProyecto:
|
|
44
|
+
* primer .planning/ real ascendiendo, o raíz del repo git, o fallback cwd) —
|
|
45
|
+
* NUNCA con el CWD crudo de la sesión: en monorepos con sesión abierta en un
|
|
46
|
+
* subdirectorio eso fragmentaba la memoria en árboles .planning/ huérfanos
|
|
47
|
+
* (DT-HOOKS-RAIZ-GIT, reportes de campo sigm + oic 2026-07-10).
|
|
42
48
|
*/
|
|
43
|
-
|
|
49
|
+
let RUTA_APRENDIZAJES = path.join(raizProyecto(process.cwd()), '.planning', 'APRENDIZAJES.md');
|
|
44
50
|
|
|
45
51
|
/**
|
|
46
52
|
* Número de líneas de contexto a extraer alrededor del keyword detectado.
|
|
@@ -364,9 +370,15 @@ function extraerTextoYAgente(data) {
|
|
|
364
370
|
* 4. Clasificar por el tipo de keyword que disparó la detección.
|
|
365
371
|
*
|
|
366
372
|
* @param {string} texto
|
|
373
|
+
* @param {object} [opts]
|
|
374
|
+
* @param {boolean} [opts.descartarFragmentos=false] - Descarta candidatos cuya
|
|
375
|
+
* descripción quedó TRUNCADA a media frase (termina en '…' sin oración
|
|
376
|
+
* completa). Obligatorio para fuentes Write/Edit: los comentarios de código
|
|
377
|
+
* de dominio largos producían entradas cortadas a medias que no son
|
|
378
|
+
* aprendizajes (reporte de campo sigm 2026-07-10).
|
|
367
379
|
* @returns {Aprendizaje|null}
|
|
368
380
|
*/
|
|
369
|
-
function detectarAprendizaje(texto) {
|
|
381
|
+
function detectarAprendizaje(texto, opts = {}) {
|
|
370
382
|
if (!texto || texto.trim().length < MIN_CHARS_DESCRIPCION) return null;
|
|
371
383
|
|
|
372
384
|
const lineas = texto.split('\n');
|
|
@@ -395,12 +407,28 @@ function detectarAprendizaje(texto) {
|
|
|
395
407
|
// Descripción: la línea del keyword, limpiada de símbolos de código.
|
|
396
408
|
// Preferir cortar al final de la primera oración si cabe en 120 chars;
|
|
397
409
|
// si no, al último espacio antes del límite.
|
|
398
|
-
const
|
|
410
|
+
const limpiarLinea = l => l
|
|
399
411
|
.trim()
|
|
400
412
|
.replace(/^[#*\/\-–•]+\s*/, '') // Quitar prefijos de comentario/lista
|
|
401
413
|
.replace(/\*\*/g, '') // Quitar marcadores de negrita Markdown
|
|
402
414
|
.replace(/`+/g, ''); // Quitar backticks
|
|
403
415
|
|
|
416
|
+
let lineaLimpia = limpiarLinea(lineas[i]);
|
|
417
|
+
|
|
418
|
+
// Wrap del editor ≠ fin del pensamiento: si la línea del keyword no
|
|
419
|
+
// cierra oración, unir hasta 2 líneas siguientes antes de buscar el
|
|
420
|
+
// corte — los comentarios largos envuelven la frase y cortar en el
|
|
421
|
+
// wrap producía títulos a media frase (ticket de campo sigm
|
|
422
|
+
// 2026-07-10: "...pueden cerrar la 'corrección' apuntando a").
|
|
423
|
+
if (!/[.!?]/.test(lineaLimpia)) {
|
|
424
|
+
for (let j = i + 1; j < Math.min(i + 3, lineas.length); j++) {
|
|
425
|
+
const continuacion = limpiarLinea(lineas[j]);
|
|
426
|
+
if (!continuacion) break;
|
|
427
|
+
lineaLimpia += ' ' + continuacion;
|
|
428
|
+
if (/[.!?]/.test(continuacion)) break;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
404
432
|
// Si la línea tiene un punto temprano, usarlo como fin natural
|
|
405
433
|
const matchOracion = lineaLimpia.match(/^[^.!?]*[.!?]/);
|
|
406
434
|
const descripcionBase = matchOracion
|
|
@@ -411,6 +439,12 @@ function detectarAprendizaje(texto) {
|
|
|
411
439
|
|
|
412
440
|
if (descripcion.length < 10) continue;
|
|
413
441
|
|
|
442
|
+
// Fragmento a media frase: ni la línea ni sus continuaciones cierran
|
|
443
|
+
// oración Y hubo que truncar — para fuentes de escritura de código
|
|
444
|
+
// eso es ruido. Una oración completa truncada elegante (corte en
|
|
445
|
+
// frontera de cláusula con '…') SÍ es título legítimo.
|
|
446
|
+
if (opts.descartarFragmentos && !matchOracion && descripcion.endsWith('…')) continue;
|
|
447
|
+
|
|
414
448
|
return { tipo, contexto: contextoFinal, descripcion };
|
|
415
449
|
}
|
|
416
450
|
}
|
|
@@ -540,6 +574,27 @@ function formatearEntrada(fecha, tipo, descripcion, agente, contexto) {
|
|
|
540
574
|
* @param {string} agente
|
|
541
575
|
*/
|
|
542
576
|
function escribirAprendizaje(aprendizaje, agente) {
|
|
577
|
+
// Read-modify-write sobre APRENDIZAJES.md (leer todo → dedup → reescribir
|
|
578
|
+
// todo). Dos hooks concurrentes leían la misma base y el segundo write
|
|
579
|
+
// pisaba la entrada del primero → aprendizaje perdido (nemesis S2-2).
|
|
580
|
+
// El lock O_EXCL serializa el CASO COMÚN (secciones críticas de <<1s): dos
|
|
581
|
+
// hooks casi-simultáneos se ordenan, sin pérdida. La lectura ocurre DENTRO de
|
|
582
|
+
// la sección crítica para ver base fresca.
|
|
583
|
+
//
|
|
584
|
+
// Límite honesto (re-auditoría nemesis iter-2): siNoAdquiere:'ejecutar'
|
|
585
|
+
// significa que si tras maxWaitMs NO se adquiere el lock —solo posible con un
|
|
586
|
+
// holder VIVO ocupando el lock >1s, patológico para un archivo de KB (un
|
|
587
|
+
// holder muerto se reclama al instante vía pidVivo)— se corre igual SIN lock.
|
|
588
|
+
// En ese caso extremo el rewrite-whole-file PUEDE pisar la entrada de otro
|
|
589
|
+
// proceso. Se prefiere ese riesgo casi-inalcanzable a OMITIR y descartar el
|
|
590
|
+
// aprendizaje actual con certeza.
|
|
591
|
+
conLockRetry('extraccion-aprendizajes-md', () => _escribirAprendizajeInterno(aprendizaje, agente), {
|
|
592
|
+
maxWaitMs: 1000,
|
|
593
|
+
siNoAdquiere: 'ejecutar',
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
function _escribirAprendizajeInterno(aprendizaje, agente) {
|
|
543
598
|
asegurarDirectorio();
|
|
544
599
|
|
|
545
600
|
const { contenidoExistente, descripciones } = leerAprendizajesExistentes();
|
|
@@ -676,6 +731,11 @@ const PATRONES_ARCHIVO_SWL_EXCLUIDO = [
|
|
|
676
731
|
// "rules" (inglés). Detectado 2026-06-11: fragmentos de una edición a
|
|
677
732
|
// verificar-citas-normativas.md (global) se promovieron como "patrón".
|
|
678
733
|
/(?:^|[\\/])\.claude[\\/]rules[\\/]/,
|
|
734
|
+
// Workflows y config de CI (.github/): sus comentarios YAML describen el
|
|
735
|
+
// proceso con keywords narrativas ("decisión", "aprobación") que el hook
|
|
736
|
+
// confunde con descubrimientos. Detectado 2026-07-10: el comentario del
|
|
737
|
+
// environment de publish-npm.yml se promovió como entrada "decisión".
|
|
738
|
+
/(?:^|[\\/])\.github[\\/]/,
|
|
679
739
|
];
|
|
680
740
|
|
|
681
741
|
/**
|
|
@@ -715,6 +775,16 @@ function pareceContenidoEstructuralSwl(contexto) {
|
|
|
715
775
|
return true;
|
|
716
776
|
}
|
|
717
777
|
|
|
778
|
+
// Bold-labels de docs sin cuerpo: líneas que son SOLO una etiqueta en
|
|
779
|
+
// negritas ("**Uso:**", "**¿Cuándo usarlo?**"), con o sin valor corto de
|
|
780
|
+
// código a continuación. Junto con los headings (abajo) cubren fragmentos
|
|
781
|
+
// estructurales de manuales/skills que el hook confundía con aprendizajes
|
|
782
|
+
// (detectado 2026-07-10: headings y labels de MANUAL_USO promovidos como
|
|
783
|
+
// "gotcha"/"patrón").
|
|
784
|
+
const esLineaEstructural = l =>
|
|
785
|
+
/^(>\s*)?#{1,6}\s+\S/.test(l) ||
|
|
786
|
+
/^(>\s*)?\*\*[^*]{1,60}\*\*:?\s*(`[^`]*`\s*)?$/.test(l);
|
|
787
|
+
|
|
718
788
|
// Enumeración de encabezados sin cuerpo sustantivo
|
|
719
789
|
// (3+ líneas tipo "## [fecha] tipo — descripción" juntas)
|
|
720
790
|
const encabezadosFecha = lineas.filter(l => /^##\s+\[\d{4}-\d{2}-\d{2}\]/.test(l));
|
|
@@ -722,15 +792,15 @@ function pareceContenidoEstructuralSwl(contexto) {
|
|
|
722
792
|
return true;
|
|
723
793
|
}
|
|
724
794
|
|
|
725
|
-
// Contexto compuesto mayoritariamente de headings sin cuerpo.
|
|
726
|
-
// placeholders auto-generados al hacer Write de archivos meta
|
|
727
|
-
// RESUMEN, PLAN)
|
|
728
|
-
//
|
|
729
|
-
// como
|
|
730
|
-
// el contexto con `> ` antes de evaluar
|
|
795
|
+
// Contexto compuesto mayoritariamente de headings/bold-labels sin cuerpo.
|
|
796
|
+
// Detecta placeholders auto-generados al hacer Write de archivos meta
|
|
797
|
+
// (RELEASE-NOTES, RESUMEN, PLAN) y fragmentos de docs de comandos cuya
|
|
798
|
+
// estructura es solo `## Sección`, `**Uso:**`... sin párrafos sustantivos.
|
|
799
|
+
// Acepta tanto líneas desnudas como indentadas como blockquote (`> ## X`)
|
|
800
|
+
// porque el hook envuelve el contexto con `> ` antes de evaluar.
|
|
731
801
|
if (noVacias.length >= 3) {
|
|
732
|
-
const
|
|
733
|
-
if (
|
|
802
|
+
const estructurales = noVacias.filter(esLineaEstructural);
|
|
803
|
+
if (estructurales.length / noVacias.length > 0.6) {
|
|
734
804
|
return true;
|
|
735
805
|
}
|
|
736
806
|
}
|
|
@@ -781,15 +851,36 @@ process.stdin.on('end', () => {
|
|
|
781
851
|
return;
|
|
782
852
|
}
|
|
783
853
|
|
|
854
|
+
// Anclar la memoria a la RAÍZ DEL PROYECTO, no al CWD de la sesión
|
|
855
|
+
// (DT-HOOKS-RAIZ-GIT): una sesión abierta en backend/ de un monorepo
|
|
856
|
+
// escribía backend/.planning/APRENDIZAJES.md huérfano.
|
|
857
|
+
const baseCwd = String(data.cwd || process.cwd());
|
|
858
|
+
const raiz = raizProyecto(baseCwd);
|
|
859
|
+
RUTA_APRENDIZAJES = path.join(raiz, '.planning', 'APRENDIZAJES.md');
|
|
860
|
+
|
|
861
|
+
const esFuenteEscritura =
|
|
862
|
+
toolName === 'Write' || toolName === 'Edit' || toolName === 'MultiEdit';
|
|
863
|
+
|
|
784
864
|
// Filtrar Write/Edit sobre archivos del sistema SWL (auto-referencia).
|
|
785
865
|
// Editar habilidades/, agentes/, reglas/, CHANGELOG, CLAUDE.md, etc. es
|
|
786
866
|
// mantenimiento del sistema, no descubrimiento en tiempo de ejecución.
|
|
787
|
-
if (
|
|
867
|
+
if (esFuenteEscritura) {
|
|
788
868
|
const toolInput = data.tool_input || data.tool?.input || {};
|
|
789
869
|
const filePath = String(toolInput.file_path || toolInput.path || '');
|
|
790
870
|
if (esArchivoSwlExcluido(filePath)) {
|
|
791
871
|
return;
|
|
792
872
|
}
|
|
873
|
+
// Escrituras FUERA de la raíz del proyecto (scratchpad del harness,
|
|
874
|
+
// temp, otros repos) no son descubrimiento de ESTE proyecto — su
|
|
875
|
+
// contenido meta (borradores, docs en construcción) generaba entradas
|
|
876
|
+
// espurias (detectado 2026-07-10 con archivos de trabajo en scratchpad).
|
|
877
|
+
if (filePath) {
|
|
878
|
+
const abs = path.resolve(baseCwd, filePath);
|
|
879
|
+
const rel = path.relative(raiz, abs);
|
|
880
|
+
if (rel.startsWith('..') || path.isAbsolute(rel)) {
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
793
884
|
}
|
|
794
885
|
|
|
795
886
|
// Extraer texto y nombre del agente del payload
|
|
@@ -800,8 +891,11 @@ process.stdin.on('end', () => {
|
|
|
800
891
|
return;
|
|
801
892
|
}
|
|
802
893
|
|
|
803
|
-
// Detectar aprendizaje en el texto
|
|
804
|
-
|
|
894
|
+
// Detectar aprendizaje en el texto. Las fuentes de escritura de código
|
|
895
|
+
// exigen descripción con oración completa (sin fragmentos truncados).
|
|
896
|
+
const aprendizaje = detectarAprendizaje(texto, {
|
|
897
|
+
descartarFragmentos: esFuenteEscritura,
|
|
898
|
+
});
|
|
805
899
|
|
|
806
900
|
if (!aprendizaje) {
|
|
807
901
|
// Sin aprendizaje detectado — salir silenciosamente
|
package/hooks/grafo-contexto.js
CHANGED
|
@@ -19,9 +19,10 @@
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
const fs = require('fs');
|
|
22
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
22
23
|
const path = require('path');
|
|
23
24
|
|
|
24
|
-
const CWD = process.env.PWD || process.cwd();
|
|
25
|
+
const CWD = raizProyecto(process.env.PWD || process.cwd());
|
|
25
26
|
const REPORT_FILE = path.join(CWD, '.planning', 'REPORTE-GRAFO.md');
|
|
26
27
|
const GRAPH_FILE = path.join(CWD, '.planning', 'graph.json');
|
|
27
28
|
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
const fs = require('fs');
|
|
26
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
26
27
|
const path = require('path');
|
|
27
28
|
|
|
28
29
|
// ---------------------------------------------------------------------------
|
|
@@ -42,7 +43,7 @@ if (ES_ENTRYPOINT && !process.env.SWL_GUARDRAIL_MODELO) {
|
|
|
42
43
|
// Rutas
|
|
43
44
|
// ---------------------------------------------------------------------------
|
|
44
45
|
|
|
45
|
-
const CWD = process.cwd();
|
|
46
|
+
const CWD = raizProyecto(process.cwd());
|
|
46
47
|
const DIR_EVOL = path.join(CWD, '.planning', 'evolution');
|
|
47
48
|
const GUARDRAIL_LOG = path.join(DIR_EVOL, 'guardrail-observaciones.jsonl');
|
|
48
49
|
|
package/hooks/inbox-aviso.js
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
const fs = require('fs');
|
|
19
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
19
20
|
const path = require('path');
|
|
20
21
|
|
|
21
22
|
const INBOX_DIR = '.planning/inbox';
|
|
@@ -51,7 +52,7 @@ process.stdin.on('end', () => {
|
|
|
51
52
|
try {
|
|
52
53
|
if (process.env.SWL_INBOX_AVISO === '0') return;
|
|
53
54
|
|
|
54
|
-
const { total, preview } = contarPendientes(process.cwd());
|
|
55
|
+
const { total, preview } = contarPendientes(raizProyecto(process.cwd()));
|
|
55
56
|
if (total === 0) return;
|
|
56
57
|
|
|
57
58
|
const lineas = [];
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
const fs = require('fs');
|
|
26
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
26
27
|
const path = require('path');
|
|
27
28
|
const os = require('os');
|
|
28
29
|
|
|
@@ -205,7 +206,7 @@ process.stdin.on('end', () => {
|
|
|
205
206
|
return;
|
|
206
207
|
}
|
|
207
208
|
|
|
208
|
-
const cwd = process.cwd();
|
|
209
|
+
const cwd = raizProyecto(process.cwd());
|
|
209
210
|
const sesion = leerSesionAnterior(cwd);
|
|
210
211
|
|
|
211
212
|
// Solo inyectar si la sesion anterior tiene contenido util
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
const fs = require('fs');
|
|
19
|
+
const { raizProyecto } = require('./raiz-proyecto');
|
|
19
20
|
const path = require('path');
|
|
20
21
|
|
|
21
22
|
// ---------------------------------------------------------------------------
|
|
@@ -123,7 +124,7 @@ function loadAgents(agentesDir) {
|
|
|
123
124
|
* @returns {Array<{name: string, score: number, skills: string[]}>} Ordenado por score descendente.
|
|
124
125
|
*/
|
|
125
126
|
function matchAgents(taskText, agentesDir) {
|
|
126
|
-
const dir = agentesDir || path.join(process.cwd(), 'agentes');
|
|
127
|
+
const dir = agentesDir || path.join(raizProyecto(process.cwd()), 'agentes');
|
|
127
128
|
const agents = loadAgents(dir);
|
|
128
129
|
const taskKeywords = extractKeywords(taskText);
|
|
129
130
|
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
27
|
const fs = require('fs');
|
|
28
|
+
const { raizProyecto } = require('./raiz-proyecto');
|
|
28
29
|
const path = require('path');
|
|
29
30
|
|
|
30
31
|
const _cache = Object.create(null);
|
|
@@ -38,7 +39,7 @@ const _cache = Object.create(null);
|
|
|
38
39
|
* @returns {string|null} - Ruta absoluta o null si no existe
|
|
39
40
|
*/
|
|
40
41
|
function resolveAgentPath(agentName, cwd) {
|
|
41
|
-
const baseDir = cwd || process.cwd();
|
|
42
|
+
const baseDir = cwd || raizProyecto(process.cwd());
|
|
42
43
|
const candidates = [
|
|
43
44
|
path.join(baseDir, 'agentes', `${agentName}.md`),
|
|
44
45
|
path.join(baseDir, '.claude', 'agents', `${agentName}.md`),
|
package/hooks/lib/autonomia.js
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
const fs = require('fs');
|
|
25
|
+
const { raizProyecto } = require('./raiz-proyecto');
|
|
25
26
|
const path = require('path');
|
|
26
27
|
const { execFileSync } = require('child_process');
|
|
27
28
|
|
|
@@ -89,7 +90,7 @@ function _normalizarNivel(valor) {
|
|
|
89
90
|
* @returns {{version?:string, defaults?:string, clases:Object}}
|
|
90
91
|
*/
|
|
91
92
|
function cargarDial(baseDir) {
|
|
92
|
-
const ruta = path.join(baseDir || process.cwd(), ...YAML_PATH);
|
|
93
|
+
const ruta = path.join(baseDir || raizProyecto(process.cwd()), ...YAML_PATH);
|
|
93
94
|
let parsed = {};
|
|
94
95
|
try {
|
|
95
96
|
parsed = _parsearYamlPlano(fs.readFileSync(ruta, 'utf8'));
|
|
@@ -153,7 +154,7 @@ function _archivosModificados(baseDir) {
|
|
|
153
154
|
const MAX_ACCION_LEN = 512; // tope para que el JSONL no crezca sin límite
|
|
154
155
|
|
|
155
156
|
function autoCheckpoint(baseDir, accion, tsISO) {
|
|
156
|
-
const base = baseDir || process.cwd();
|
|
157
|
+
const base = baseDir || raizProyecto(process.cwd());
|
|
157
158
|
const registro = {
|
|
158
159
|
ts: tsISO || new Date().toISOString(),
|
|
159
160
|
accion: String(accion || '').slice(0, MAX_ACCION_LEN),
|
|
@@ -186,7 +187,7 @@ function _parseAccion(args) {
|
|
|
186
187
|
|
|
187
188
|
function main(argv) {
|
|
188
189
|
const accion = _parseAccion(argv.slice(2));
|
|
189
|
-
const baseDir = process.cwd();
|
|
190
|
+
const baseDir = raizProyecto(process.cwd());
|
|
190
191
|
const dial = cargarDial(baseDir);
|
|
191
192
|
if (requiereAutoCheckpoint(dial, 'cambio_reversible')) {
|
|
192
193
|
const reg = autoCheckpoint(baseDir, accion || '(sin descripción)');
|
|
@@ -204,5 +205,6 @@ module.exports = {
|
|
|
204
205
|
cargarDial,
|
|
205
206
|
requiereAutoCheckpoint,
|
|
206
207
|
autoCheckpoint,
|
|
208
|
+
main,
|
|
207
209
|
_parsearYamlPlano,
|
|
208
210
|
};
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
29
|
const fs = require('fs');
|
|
30
|
+
const { raizProyecto } = require('./raiz-proyecto');
|
|
30
31
|
const path = require('path');
|
|
31
32
|
const crypto = require('crypto');
|
|
32
33
|
|
|
@@ -384,7 +385,7 @@ module.exports = {
|
|
|
384
385
|
if (require.main === module) {
|
|
385
386
|
try {
|
|
386
387
|
if (process.argv.includes('--conteo')) {
|
|
387
|
-
process.stdout.write(JSON.stringify(contarPorSubtipo(process.cwd()), null, 2) + '\n');
|
|
388
|
+
process.stdout.write(JSON.stringify(contarPorSubtipo(raizProyecto(process.cwd())), null, 2) + '\n');
|
|
388
389
|
}
|
|
389
390
|
} catch (_) {
|
|
390
391
|
/* exit 0 siempre */
|