@saulwade/swl-ses 2.5.3 → 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 +192 -192
- package/README.md +600 -600
- package/agentes/auto-evolucion-swl.md +27 -3
- package/bin/swl-ses.js +32 -7
- 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 +312 -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/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 +335 -3
- package/manifiestos/modulos.json +19 -14
- package/manifiestos/planning-paths.json +1 -0
- package/manifiestos/skills-lock.json +50 -50
- 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 +85 -7
- package/scripts/lib/activar-hooks-proyecto.js +12 -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/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
|
@@ -228,8 +228,62 @@ const RUTAS_PERMITIDAS_EXTERNAS = [
|
|
|
228
228
|
path.join(os.homedir(), '.claude', 'commands'),
|
|
229
229
|
// Reglas globales del usuario (modificables durante consolidación cross-proyecto)
|
|
230
230
|
path.join(os.homedir(), '.claude', 'rules'),
|
|
231
|
+
// Scratchpad de sesión del harness de Claude Code (os.tmpdir()/claude/<proyecto>/<sesión>/)
|
|
232
|
+
// — el propio harness instruye usarlo para todo temporal (caso real 2026-07-10:
|
|
233
|
+
// el hook bloqueaba al agente escribiendo en su scratchpad oficial).
|
|
234
|
+
path.join(os.tmpdir(), 'claude'),
|
|
231
235
|
];
|
|
232
236
|
|
|
237
|
+
/**
|
|
238
|
+
* Asciende desde `desde` buscando la raíz del repositorio git que lo contiene
|
|
239
|
+
* (directorio con `.git` — directorio normal o archivo, para worktrees).
|
|
240
|
+
*
|
|
241
|
+
* La frontera de seguridad correcta es el PROYECTO, no el subdirectorio donde
|
|
242
|
+
* se abrió la sesión: con CWD=<repo>/backend, escribir `<repo>/.planning/...`
|
|
243
|
+
* es legítimo (todo el sistema SWL escribe `.planning/` en la raíz). Caso real
|
|
244
|
+
* SIGM 2026-07-10: el loop de auditoría se auto-bloqueaba en un monorepo.
|
|
245
|
+
*
|
|
246
|
+
* @param {string} desde - Directorio inicial (típicamente el CWD).
|
|
247
|
+
* @returns {string|null} Raíz del repo, o null si `desde` no está en un repo git.
|
|
248
|
+
*/
|
|
249
|
+
function raizRepoGit(desde) {
|
|
250
|
+
const fs = require('fs');
|
|
251
|
+
let actual = path.resolve(desde);
|
|
252
|
+
for (let i = 0; i < 40; i++) {
|
|
253
|
+
try {
|
|
254
|
+
if (fs.existsSync(path.join(actual, '.git'))) return actual;
|
|
255
|
+
} catch (_) {
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
const padre = path.dirname(actual);
|
|
259
|
+
if (padre === actual) return null; // raíz del filesystem
|
|
260
|
+
actual = padre;
|
|
261
|
+
}
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Verifica si la ruta resuelta cae dentro de la raíz del repositorio git que
|
|
267
|
+
* contiene al CWD. Boundary check por componente completo (prefijo + '/'),
|
|
268
|
+
* case-insensitive en Windows — un sibling `<repo>-evil` NO pasa.
|
|
269
|
+
*
|
|
270
|
+
* @param {string} rutaResuelta
|
|
271
|
+
* @param {string} cwdActual
|
|
272
|
+
* @returns {boolean}
|
|
273
|
+
*/
|
|
274
|
+
function estaDentroDeRaizGit(rutaResuelta, cwdActual) {
|
|
275
|
+
const raiz = raizRepoGit(cwdActual);
|
|
276
|
+
if (!raiz) return false;
|
|
277
|
+
|
|
278
|
+
const raizNorm = normalizarParaComparacion(raiz);
|
|
279
|
+
const rutaNorm = normalizarParaComparacion(rutaResuelta);
|
|
280
|
+
const [raizCmp, rutaCmp] = process.platform === 'win32'
|
|
281
|
+
? [raizNorm.toLowerCase(), rutaNorm.toLowerCase()]
|
|
282
|
+
: [raizNorm, rutaNorm];
|
|
283
|
+
|
|
284
|
+
return rutaCmp === raizCmp || rutaCmp.startsWith(raizCmp + '/');
|
|
285
|
+
}
|
|
286
|
+
|
|
233
287
|
/**
|
|
234
288
|
* Parsea la variable de entorno SWL_PROTECCION_RUTAS_PERMITIR (CSV de rutas
|
|
235
289
|
* absolutas) y devuelve un array de paths normalizados.
|
|
@@ -361,7 +415,8 @@ function construirMensajeBloqueo(tipoViolacion, rutaRaw, rutaResuelta, cwdActual
|
|
|
361
415
|
`Directorio CWD: ${cwdActual}`,
|
|
362
416
|
``,
|
|
363
417
|
`Reglas de seguridad:`,
|
|
364
|
-
` - La ruta de destino debe estar dentro del directorio de trabajo actual
|
|
418
|
+
` - La ruta de destino debe estar dentro del directorio de trabajo actual`,
|
|
419
|
+
` o de la raíz del repositorio git que lo contiene (monorepos).`,
|
|
365
420
|
` - No se permiten secuencias de path traversal (../, ~/, %2e%2e, etc.).`,
|
|
366
421
|
` - No se permiten rutas absolutas fuera del proyecto.`,
|
|
367
422
|
];
|
|
@@ -440,9 +495,10 @@ process.stdin.on('end', () => {
|
|
|
440
495
|
process.exit(2);
|
|
441
496
|
}
|
|
442
497
|
|
|
443
|
-
// --- Verificación 2: destino dentro del CWD
|
|
498
|
+
// --- Verificación 2: destino dentro del CWD, de la raíz del repo git
|
|
499
|
+
// del CWD, o en rutas externas permitidas ---
|
|
444
500
|
const { dentro, rutaResuelta } = verificarDentroDeCwd(rutaRaw, cwdActual);
|
|
445
|
-
if (!dentro && !esRutaExternaPermitida(rutaResuelta)) {
|
|
501
|
+
if (!dentro && !estaDentroDeRaizGit(rutaResuelta, cwdActual) && !esRutaExternaPermitida(rutaResuelta)) {
|
|
446
502
|
const razon = construirMensajeBloqueo(
|
|
447
503
|
`Destino fuera del directorio de trabajo actual`,
|
|
448
504
|
rutaRaw,
|
package/hooks/registro-turnos.js
CHANGED
|
@@ -54,12 +54,13 @@
|
|
|
54
54
|
*/
|
|
55
55
|
|
|
56
56
|
const fs = require('fs');
|
|
57
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
57
58
|
const path = require('path');
|
|
58
59
|
|
|
59
60
|
const LIMITE_TURNOS = parseInt(process.env.SWL_REGISTRO_TURNOS_LIMITE || '1000', 10);
|
|
60
61
|
const MAX_INPUT_CHARS = 200;
|
|
61
62
|
const MAX_OUTPUT_CHARS = 500;
|
|
62
|
-
const DIR_BASE = path.join(process.cwd(), '.planning', 'sessions');
|
|
63
|
+
const DIR_BASE = path.join(raizProyecto(process.cwd()), '.planning', 'sessions');
|
|
63
64
|
|
|
64
65
|
// Patrones de sanitización conservadores: tokens largos, llaves API, JWT,
|
|
65
66
|
// password en URLs, paths con .env. Si aparece un match, se reemplaza con
|
package/hooks/resumen-sesion.js
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
const fs = require('fs');
|
|
21
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
21
22
|
const path = require('path');
|
|
22
23
|
const os = require('os');
|
|
23
24
|
const { execFile } = require('child_process');
|
|
@@ -83,7 +84,7 @@ function hooksDisparados(sessionId) {
|
|
|
83
84
|
|
|
84
85
|
// Fallback: heurística por archivos JSONL de hooks conocidos en .planning/
|
|
85
86
|
try {
|
|
86
|
-
const planningDir = path.join(process.cwd(), '.planning');
|
|
87
|
+
const planningDir = path.join(raizProyecto(process.cwd()), '.planning');
|
|
87
88
|
const jsonlCandidatos = [
|
|
88
89
|
['evolution/nudges.jsonl', 'nudge-tracker'],
|
|
89
90
|
['auto-evolution/agentes.jsonl', 'auto-evolucion'],
|
package/hooks/risk-scoring.js
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
28
|
const fs = require('fs');
|
|
29
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
29
30
|
const path = require('path');
|
|
30
31
|
const os = require('os');
|
|
31
32
|
|
|
@@ -104,7 +105,7 @@ function trackHighOverride(score, factors, toolName) {
|
|
|
104
105
|
});
|
|
105
106
|
|
|
106
107
|
if (state.highOverrides >= MAX_HIGH_OVERRIDES && !state.auditWritten) {
|
|
107
|
-
const auditPath = path.join(process.cwd(), '.planning', 'AUDITORIA.md');
|
|
108
|
+
const auditPath = path.join(raizProyecto(process.cwd()), '.planning', 'AUDITORIA.md');
|
|
108
109
|
try {
|
|
109
110
|
const auditEntry = [
|
|
110
111
|
'',
|
|
@@ -29,16 +29,30 @@
|
|
|
29
29
|
const fs = require('fs');
|
|
30
30
|
const path = require('path');
|
|
31
31
|
const { spawnSync } = require('child_process');
|
|
32
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
33
|
+
const guard = require('./lib/singleton-guard');
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
// Ancla: raíz del PROYECTO (no __dirname/.. — en instalaciones destino el hook
|
|
36
|
+
// vive en ~/.claude/hooks/ y esa ancla apuntaba a ~/.claude/, nunca al
|
|
37
|
+
// proyecto; misma clase que DT-HOOKS-RAIZ-GIT).
|
|
38
|
+
const RAIZ_PROYECTO = raizProyecto(process.cwd());
|
|
34
39
|
const DIR_PLANNING = path.join(RAIZ_PROYECTO, '.planning');
|
|
35
40
|
const ARCHIVOS_AUDIT = [
|
|
36
41
|
path.join(DIR_PLANNING, 'audit.jsonl'),
|
|
37
42
|
path.join(DIR_PLANNING, 'audit-merkle.jsonl'),
|
|
38
43
|
];
|
|
39
|
-
const DIR_ARCHIVO = path.join(DIR_PLANNING, '
|
|
44
|
+
const DIR_ARCHIVO = path.join(DIR_PLANNING, 'archive', 'audit');
|
|
40
45
|
const MARCA_ULTIMA_ROTACION = path.join(DIR_ARCHIVO, '.ultima-rotacion');
|
|
41
|
-
|
|
46
|
+
// El script de rotación es EJECUTABLE de la distribución SWL: se resuelve
|
|
47
|
+
// SOLO desde la ubicación instalada del hook (__dirname) — repo madre en
|
|
48
|
+
// scripts/, destino aplanado junto al hook. NUNCA desde la raíz del
|
|
49
|
+
// proyecto: un repo no confiable podría plantar el script y ejecutarse en
|
|
50
|
+
// el primer Stop (hallazgo MAYOR revisor-seguridad 2026-07-10). Los DATOS
|
|
51
|
+
// (DIR_PLANNING) sí anclan a raizProyecto.
|
|
52
|
+
const SCRIPT_ROTAR = [
|
|
53
|
+
path.join(__dirname, '..', 'scripts', 'rotar-audit-logs.js'),
|
|
54
|
+
path.join(__dirname, 'rotar-audit-logs.js'),
|
|
55
|
+
].find(p => { try { return fs.existsSync(p); } catch (_) { return false; } }) || '';
|
|
42
56
|
|
|
43
57
|
const COOLDOWN_MS = 6 * 60 * 60 * 1000; // 6 horas
|
|
44
58
|
|
|
@@ -93,24 +107,36 @@ function main() {
|
|
|
93
107
|
if (!check.excedido) return;
|
|
94
108
|
if (enCooldown()) return;
|
|
95
109
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
process.stdout.write(
|
|
112
|
-
`[rotacion-audit] ${nombreArchivo}=${sizeMb}MB > ${limiteMb}MB → archivadas ${archivadas} entradas (>${dias}d)\n`
|
|
110
|
+
// Lock cross-proceso (nemesis iter-1 S2-1/S3-4): dos sesiones con Stop
|
|
111
|
+
// simultáneo podían rotar a la vez → re-archivar las mismas entradas
|
|
112
|
+
// (duplicados en el .gz). Si otra rotación está activa, salir — esa corrida
|
|
113
|
+
// ya archiva. Ancla del lock = raíz del proyecto (misma que RAIZ_PROYECTO).
|
|
114
|
+
if (!guard.adquirir('rotar-audit', { staleMs: 300_000 })) return;
|
|
115
|
+
try {
|
|
116
|
+
const dias = diasRetencion();
|
|
117
|
+
const nodeBin = process.execPath;
|
|
118
|
+
const result = spawnSync(
|
|
119
|
+
nodeBin,
|
|
120
|
+
[SCRIPT_ROTAR, `--dias=${dias}`, `--raiz=${DIR_PLANNING}`],
|
|
121
|
+
// Archivos de auditoría de decenas de MB pueden tardar; 30s los mataba a
|
|
122
|
+
// mitad de rotación (nemesis iter-1 S3-4). 5 min da margen; el escribirActivo
|
|
123
|
+
// atómico protege contra la interrupción residual.
|
|
124
|
+
{ encoding: 'utf8', timeout: 300_000, cwd: RAIZ_PROYECTO }
|
|
113
125
|
);
|
|
126
|
+
|
|
127
|
+
if (result.status === 0) {
|
|
128
|
+
marcarRotacion();
|
|
129
|
+
const sizeMb = (check.size / 1024 / 1024).toFixed(1);
|
|
130
|
+
const limiteMb = (check.limite / 1024 / 1024).toFixed(1);
|
|
131
|
+
const nombreArchivo = path.basename(check.ruta);
|
|
132
|
+
const match = (result.stdout || '').match(/(\d+) archivadas/g);
|
|
133
|
+
const archivadas = match ? match.map((m) => m.replace(' archivadas', '')).join('+') : '?';
|
|
134
|
+
process.stdout.write(
|
|
135
|
+
`[rotacion-audit] ${nombreArchivo}=${sizeMb}MB > ${limiteMb}MB → archivadas ${archivadas} entradas (>${dias}d)\n`
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
} finally {
|
|
139
|
+
guard.liberar('rotar-audit');
|
|
114
140
|
}
|
|
115
141
|
}
|
|
116
142
|
|
|
@@ -19,7 +19,9 @@
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
const fs = require('fs');
|
|
22
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
22
23
|
const path = require('path');
|
|
24
|
+
const { spawn } = require('child_process');
|
|
23
25
|
|
|
24
26
|
// Require con fallback dual: el instalador aplana `hooks/lib/X.js → <hooks>/X.js`
|
|
25
27
|
// en el destino, pero en el repo madre vive en `lib/`. Cubre ambos layouts (D-17).
|
|
@@ -32,6 +34,116 @@ function requireDual(nombre) {
|
|
|
32
34
|
}
|
|
33
35
|
}
|
|
34
36
|
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Fallback de disparo del ciclo autónomo (Fase 21, T-11, ADR-0041/0042,
|
|
39
|
+
// REQ-21-11). El scheduler cron (`gateway/cron/jobs.example.json`) es el
|
|
40
|
+
// disparador primario de la Fase A; este fallback cubre proyectos sin
|
|
41
|
+
// scheduler configurado, disparando desde el propio SessionStart cuando toca.
|
|
42
|
+
//
|
|
43
|
+
// Fuera de alcance intencional en instalaciones destino (Fase 26): las libs
|
|
44
|
+
// `scripts/lib/ciclo-autonomo/*.js` viajan en `hooks-core` (resuelven cross-dir
|
|
45
|
+
// igual que `git-metricas`/`npm-version`), pero el wrapper `scripts/cli/
|
|
46
|
+
// ciclo-autonomo.js` se distribuye SOLO por `package.json#files` — en destino
|
|
47
|
+
// puede no resolver. Cuando no resuelve: silencio, nunca crashea el hook.
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Resuelve la lib pura de decisión del fallback (`scripts/lib/ciclo-autonomo/
|
|
52
|
+
* fallback.js`). Dual: repo madre / destino copy-mode (`../scripts/lib/...`)
|
|
53
|
+
* vs destino aplanado (`./lib/ciclo-autonomo/fallback`, mismo criterio que
|
|
54
|
+
* `hooks/check-update.js` con `scripts/lib/npm-version.js`). Ausente → null.
|
|
55
|
+
*/
|
|
56
|
+
function resolverFallbackCiclo() {
|
|
57
|
+
try {
|
|
58
|
+
return require('../scripts/lib/ciclo-autonomo/fallback');
|
|
59
|
+
} catch (e1) {
|
|
60
|
+
if (e1 && e1.code !== 'MODULE_NOT_FOUND') throw e1;
|
|
61
|
+
try {
|
|
62
|
+
return require('./lib/ciclo-autonomo/fallback');
|
|
63
|
+
} catch (e2) {
|
|
64
|
+
if (e2 && e2.code !== 'MODULE_NOT_FOUND') throw e2;
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Resuelve la ruta absoluta del wrapper CLI del ciclo
|
|
72
|
+
* (`scripts/cli/ciclo-autonomo.js`) SIN requerirlo — `require.resolve` nunca
|
|
73
|
+
* ejecuta el módulo, evita cargar en este proceso el árbol completo del ciclo
|
|
74
|
+
* (config/lock/writer/drenador/puente) solo para calcular un path.
|
|
75
|
+
*/
|
|
76
|
+
function resolverRutaCliCiclo() {
|
|
77
|
+
try {
|
|
78
|
+
return require.resolve('../scripts/cli/ciclo-autonomo');
|
|
79
|
+
} catch (e1) {
|
|
80
|
+
if (e1 && e1.code !== 'MODULE_NOT_FOUND') throw e1;
|
|
81
|
+
try {
|
|
82
|
+
return require.resolve('./lib/ciclo-autonomo/ciclo-autonomo');
|
|
83
|
+
} catch (e2) {
|
|
84
|
+
if (e2 && e2.code !== 'MODULE_NOT_FOUND') throw e2;
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Dispara la Fase A del ciclo autónomo en un proceso separado
|
|
92
|
+
* (fire-and-forget, `detached` + `unref`) cuando el motor está habilitado y
|
|
93
|
+
* pasaron >=24h desde la última corrida (o nunca corrió). Guard barato
|
|
94
|
+
* PRIMERO: si no existe `.planning/evolution/ciclo-config.json`, retorna sin
|
|
95
|
+
* requerir ninguna lib del ciclo — la inmensa mayoría de proyectos nunca lo
|
|
96
|
+
* habilitó y el presupuesto de este hook (<200ms) no debe pagar ese costo.
|
|
97
|
+
* El hook NO espera el resultado; el lock propio del ciclo (T-02) garantiza
|
|
98
|
+
* que no haya doble corrida con el scheduler cron.
|
|
99
|
+
*/
|
|
100
|
+
function dispararCicloAutonomoSiToca(cwd, hoy) {
|
|
101
|
+
if (!fs.existsSync(path.join(cwd, '.planning', 'evolution', 'ciclo-config.json'))) return;
|
|
102
|
+
|
|
103
|
+
const fallback = resolverFallbackCiclo();
|
|
104
|
+
if (!fallback) return;
|
|
105
|
+
|
|
106
|
+
const { disparar } = fallback.debeDisparar(cwd, { ahora: hoy });
|
|
107
|
+
if (!disparar) return;
|
|
108
|
+
|
|
109
|
+
const rutaCli = resolverRutaCliCiclo();
|
|
110
|
+
if (!rutaCli) return; // no resuelve en esta instalación: silencio, nunca crashea
|
|
111
|
+
|
|
112
|
+
spawn(
|
|
113
|
+
process.execPath,
|
|
114
|
+
['-e', `require(${JSON.stringify(rutaCli)})({})`],
|
|
115
|
+
{ detached: true, stdio: 'ignore', cwd }
|
|
116
|
+
).unref();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Texto de un archivo o `null` si no existe / no se puede leer. */
|
|
120
|
+
function leerTextoSeguro(p) {
|
|
121
|
+
try {
|
|
122
|
+
return fs.readFileSync(p, 'utf8');
|
|
123
|
+
} catch (_) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Línea de resumen de la última corrida del ciclo autónomo para el digest,
|
|
130
|
+
* cuando `ciclo-config.json` existe y tiene `ultima_corrida` registrada.
|
|
131
|
+
*/
|
|
132
|
+
function lineaResumenCiclo(cwd) {
|
|
133
|
+
const txt = leerTextoSeguro(path.join(cwd, '.planning', 'evolution', 'ciclo-config.json'));
|
|
134
|
+
if (!txt) return null;
|
|
135
|
+
let cfg;
|
|
136
|
+
try {
|
|
137
|
+
cfg = JSON.parse(txt);
|
|
138
|
+
} catch (_) {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
const u = cfg && cfg.ultima_corrida;
|
|
142
|
+
if (!u) return null;
|
|
143
|
+
return `Ciclo autónomo: última corrida ${cfg.last_run || 'desconocida'} — ` +
|
|
144
|
+
`promovió ${u.promovidos || 0}, degradó ${u.degradados || 0}, drenó ${u.drenados || 0}`;
|
|
145
|
+
}
|
|
146
|
+
|
|
35
147
|
let briefing;
|
|
36
148
|
let atomicWriteJSON;
|
|
37
149
|
try {
|
|
@@ -53,7 +165,7 @@ process.stdin.on('end', () => {
|
|
|
53
165
|
if (process.env.SWL_BRIEFING === '0') return; // opt-out
|
|
54
166
|
if (!briefing) return; // lib ausente: degradar a silencio
|
|
55
167
|
|
|
56
|
-
const cwd = process.cwd();
|
|
168
|
+
const cwd = raizProyecto(process.cwd());
|
|
57
169
|
if (!fs.existsSync(path.join(cwd, '.planning'))) return; // zero-config
|
|
58
170
|
|
|
59
171
|
const hoy = new Date();
|
|
@@ -78,8 +190,22 @@ process.stdin.on('end', () => {
|
|
|
78
190
|
const digest = briefing.armarDigest(items, estadoPrevio, dia, {
|
|
79
191
|
categoriasSilenciadas: silenciadas,
|
|
80
192
|
});
|
|
193
|
+
|
|
194
|
+
// Fallback de disparo del ciclo autónomo (Fase 21, T-11, REQ-21-11): aislado
|
|
195
|
+
// en su propio try/catch y evaluado ANTES del early-return de silencio total
|
|
196
|
+
// — es una acción de fondo independiente de si hay señales que mostrar hoy.
|
|
197
|
+
try {
|
|
198
|
+
dispararCicloAutonomoSiToca(cwd, hoy);
|
|
199
|
+
} catch (_) { /* fire-and-forget best-effort: nunca bloquear la sesión */ }
|
|
200
|
+
|
|
81
201
|
if (!digest) return; // silencio total: sin señales nuevas
|
|
82
202
|
|
|
203
|
+
// Línea de resumen del ciclo autónomo (si hubo corrida reciente registrada).
|
|
204
|
+
try {
|
|
205
|
+
const lineaCiclo = lineaResumenCiclo(cwd);
|
|
206
|
+
if (lineaCiclo) digest.texto = `${digest.texto}\n${lineaCiclo}`;
|
|
207
|
+
} catch (_) { /* best-effort: no romper el digest por esto */ }
|
|
208
|
+
|
|
83
209
|
// Persistir el estado de dedupe (best-effort).
|
|
84
210
|
try {
|
|
85
211
|
const dir = path.join(cwd, '.planning', 'user-profile');
|
package/hooks/spec-gate.js
CHANGED
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
*/
|
|
33
33
|
|
|
34
34
|
const fs = require('fs');
|
|
35
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
35
36
|
const path = require('path');
|
|
36
37
|
const crypto = require('crypto');
|
|
37
38
|
|
|
@@ -180,7 +181,7 @@ function main() {
|
|
|
180
181
|
process.exit(0);
|
|
181
182
|
}
|
|
182
183
|
|
|
183
|
-
const cwd = process.cwd();
|
|
184
|
+
const cwd = raizProyecto(process.cwd());
|
|
184
185
|
const toolName = evento?.tool_name;
|
|
185
186
|
const filePath = evento?.tool_input?.file_path;
|
|
186
187
|
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
*/
|
|
34
34
|
|
|
35
35
|
const fs = require('fs');
|
|
36
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
36
37
|
const path = require('path');
|
|
37
38
|
|
|
38
39
|
let isPackageRoot, readEvolutionMeta;
|
|
@@ -52,7 +53,7 @@ const CARPETAS_EVOLUCIONABLES = [
|
|
|
52
53
|
];
|
|
53
54
|
|
|
54
55
|
const STATE_FILE = path.join(
|
|
55
|
-
process.cwd(),
|
|
56
|
+
raizProyecto(process.cwd()),
|
|
56
57
|
'.planning',
|
|
57
58
|
'comms',
|
|
58
59
|
'sugerir-contribuir-cooldown.json'
|
|
@@ -68,7 +69,7 @@ function rutaRelativaAlProyecto(rutaRaw) {
|
|
|
68
69
|
let normalizada = normalizarSeparadores(rutaRaw);
|
|
69
70
|
|
|
70
71
|
if (path.isAbsolute(normalizada)) {
|
|
71
|
-
const cwd = normalizarSeparadores(process.cwd());
|
|
72
|
+
const cwd = normalizarSeparadores(raizProyecto(process.cwd()));
|
|
72
73
|
if (normalizada.toLowerCase().startsWith(cwd.toLowerCase())) {
|
|
73
74
|
normalizada = normalizada.slice(cwd.length).replace(/^\/+/, '');
|
|
74
75
|
} else {
|
|
@@ -174,7 +175,9 @@ function evaluar(toolName, toolInput) {
|
|
|
174
175
|
if (!ruta) return null;
|
|
175
176
|
if (!esEvolucionable(ruta)) return null;
|
|
176
177
|
|
|
177
|
-
|
|
178
|
+
// `ruta` ya es relativa a la raíz del proyecto (rutaRelativaAlProyecto la
|
|
179
|
+
// despoja contra esa misma ancla) — reconstruir contra la raíz, no el cwd.
|
|
180
|
+
const rutaAbsoluta = path.isAbsolute(rutaRaw) ? rutaRaw : path.join(raizProyecto(process.cwd()), ruta);
|
|
178
181
|
if (!fs.existsSync(rutaAbsoluta)) return null;
|
|
179
182
|
|
|
180
183
|
const evo = readEvolutionMeta(rutaAbsoluta);
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
29
|
const fs = require('fs');
|
|
30
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
30
31
|
const path = require('path');
|
|
31
32
|
|
|
32
33
|
// Carpetas cuyo cambio invalida los contadores de INVENTARIO.md
|
|
@@ -38,7 +39,7 @@ const CARPETAS_INVENTARIADAS = [
|
|
|
38
39
|
{ ruta: 'reglas/', etiqueta: 'reglas' },
|
|
39
40
|
];
|
|
40
41
|
|
|
41
|
-
const STATE_FILE = path.join(process.cwd(), '.planning', 'inventario-aviso-state.json');
|
|
42
|
+
const STATE_FILE = path.join(raizProyecto(process.cwd()), '.planning', 'inventario-aviso-state.json');
|
|
42
43
|
const COOLDOWN_MS = 30 * 60 * 1000; // 30 min
|
|
43
44
|
|
|
44
45
|
function normalizarSeparadores(p) {
|
|
@@ -52,7 +53,7 @@ function detectarCarpeta(rutaRaw) {
|
|
|
52
53
|
// Solo nos interesan rutas relativas al proyecto. Si es absoluta, intentamos
|
|
53
54
|
// hacerla relativa al CWD actual.
|
|
54
55
|
if (path.isAbsolute(normalizada)) {
|
|
55
|
-
const cwd = normalizarSeparadores(process.cwd());
|
|
56
|
+
const cwd = normalizarSeparadores(raizProyecto(process.cwd()));
|
|
56
57
|
if (normalizada.toLowerCase().startsWith(cwd.toLowerCase())) {
|
|
57
58
|
normalizada = normalizada.slice(cwd.length).replace(/^\/+/, '');
|
|
58
59
|
} else {
|
package/hooks/tdd-gate.js
CHANGED
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
32
|
const fs = require('fs');
|
|
33
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
33
34
|
const path = require('path');
|
|
34
35
|
const { execFileSync } = require('child_process');
|
|
35
36
|
|
|
@@ -209,7 +210,7 @@ function main() {
|
|
|
209
210
|
|
|
210
211
|
if (evento?.tool_name !== 'Bash') process.exit(0);
|
|
211
212
|
const comando = evento?.tool_input?.command;
|
|
212
|
-
const cwd = process.cwd();
|
|
213
|
+
const cwd = raizProyecto(process.cwd());
|
|
213
214
|
|
|
214
215
|
// Zero-config: sin .planning/ no hay ciclo GSD que vigilar.
|
|
215
216
|
if (!fs.existsSync(path.join(cwd, '.planning'))) process.exit(0);
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
const { registrar } = require('./lib/otlp-exporter');
|
|
20
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
20
21
|
|
|
21
22
|
// Importar esquema de spans con guard de robustez — el hook no debe fallar
|
|
22
23
|
// si el módulo aún no existe en instalaciones anteriores del sistema.
|
|
@@ -27,7 +28,7 @@ try {
|
|
|
27
28
|
// Módulo no disponible — continuar sin tipado de span
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
const CWD = process.cwd();
|
|
31
|
+
const CWD = raizProyecto(process.cwd());
|
|
31
32
|
|
|
32
33
|
// ---------------------------------------------------------------------------
|
|
33
34
|
// Mapping de eventos a tipos de span
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Hook: telemetria-skill-routing.js
|
|
@@ -91,7 +92,7 @@ if (!require.main || require.main === module) {
|
|
|
91
92
|
process.stdin.on('data', (chunk) => { inputRaw += chunk; });
|
|
92
93
|
process.stdin.on('end', () => {
|
|
93
94
|
try {
|
|
94
|
-
procesar(JSON.parse(inputRaw), process.cwd());
|
|
95
|
+
procesar(JSON.parse(inputRaw), raizProyecto(process.cwd()));
|
|
95
96
|
} catch (_) {
|
|
96
97
|
// Silencioso — nunca interrumpir la sesión por un fallo de telemetría.
|
|
97
98
|
}
|
package/hooks/tracking-costos.js
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
23
|
const fs = require('fs');
|
|
24
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
24
25
|
const path = require('path');
|
|
25
26
|
const os = require('os');
|
|
26
27
|
|
|
@@ -75,10 +76,10 @@ const PRESUPUESTO_DEFAULT = {
|
|
|
75
76
|
};
|
|
76
77
|
|
|
77
78
|
/** Ruta del manifesto de configuración de hooks. */
|
|
78
|
-
const RUTA_HOOKS_CONFIG = path.join(process.cwd(), 'manifiestos', 'hooks-config.json');
|
|
79
|
+
const RUTA_HOOKS_CONFIG = path.join(raizProyecto(process.cwd()), 'manifiestos', 'hooks-config.json');
|
|
79
80
|
|
|
80
81
|
/** Ruta del archivo de métricas de sesión. */
|
|
81
|
-
const RUTA_METRICS = path.join(process.cwd(), '.planning', 'METRICAS.md');
|
|
82
|
+
const RUTA_METRICS = path.join(raizProyecto(process.cwd()), '.planning', 'METRICAS.md');
|
|
82
83
|
|
|
83
84
|
// ---------------------------------------------------------------------------
|
|
84
85
|
// Utilidades de configuración
|
|
@@ -673,7 +674,7 @@ process.stdin.on('end', () => {
|
|
|
673
674
|
if (estado.toolCalls % INTERVALO_ACTUALIZACION === 0) {
|
|
674
675
|
try {
|
|
675
676
|
const { actualizarSesion } = require('./lib/session-store');
|
|
676
|
-
const cwd = process.cwd();
|
|
677
|
+
const cwd = raizProyecto(process.cwd());
|
|
677
678
|
actualizarSesion(cwd, {
|
|
678
679
|
toolCall: toolName,
|
|
679
680
|
tokensEstimated: estado.tokensTotal,
|
|
@@ -23,9 +23,10 @@
|
|
|
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
|
-
const CWD = process.cwd();
|
|
29
|
+
const CWD = raizProyecto(process.cwd());
|
|
29
30
|
const SCHEMAS_PATH = path.join(CWD, 'manifiestos', 'agent-output-schemas.json');
|
|
30
31
|
const LOG_PATH = path.join(CWD, '.planning', 'evolution', 'formato-violaciones.jsonl');
|
|
31
32
|
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
*/
|
|
45
45
|
|
|
46
46
|
const fs = require('fs');
|
|
47
|
+
const { raizProyecto } = require('./lib/raiz-proyecto');
|
|
47
48
|
const path = require('path');
|
|
48
49
|
const crypto = require('crypto');
|
|
49
50
|
|
|
@@ -188,7 +189,7 @@ if (faltan.length === 0) {
|
|
|
188
189
|
}
|
|
189
190
|
|
|
190
191
|
// ─── Emitir nudge ─────────────────────────────────────────────────────────
|
|
191
|
-
const CWD = process.cwd();
|
|
192
|
+
const CWD = raizProyecto(process.cwd());
|
|
192
193
|
const rutaRelativa = path.relative(CWD, filePath).replace(/\\/g, '/');
|
|
193
194
|
const nombreAgente = path.basename(filePath, '.md');
|
|
194
195
|
|
|
@@ -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 } = require('child_process');
|
|
39
40
|
|
|
@@ -49,8 +50,17 @@ const ARCHIVOS_MEMORIA = [
|
|
|
49
50
|
'instintos/global.yaml',
|
|
50
51
|
];
|
|
51
52
|
|
|
52
|
-
/**
|
|
53
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Path del validador — EJECUTABLE de la distribución SWL: se resuelve SOLO
|
|
55
|
+
* desde la ubicación instalada del hook (__dirname), NUNCA desde el árbol
|
|
56
|
+
* del proyecto (un repo no confiable podría plantar el script; hallazgo
|
|
57
|
+
* revisor-seguridad 2026-07-10). Los DATOS que valida sí anclan a la raíz
|
|
58
|
+
* del proyecto (cwd del spawn).
|
|
59
|
+
*/
|
|
60
|
+
const VALIDADOR = [
|
|
61
|
+
path.join(__dirname, '..', 'scripts', 'validar-memoria.js'), // repo madre
|
|
62
|
+
path.join(__dirname, 'validar-memoria.js'), // destino aplanado
|
|
63
|
+
].find(p => { try { return fs.existsSync(p); } catch (_) { return false; } }) || '';
|
|
54
64
|
|
|
55
65
|
/** Timeout para el validador (ms). Si tarda más, se aborta sin bloquear. */
|
|
56
66
|
const TIMEOUT_MS = 5000;
|
|
@@ -106,7 +116,7 @@ function ejecutarValidador() {
|
|
|
106
116
|
if (!fs.existsSync(VALIDADOR)) return null;
|
|
107
117
|
try {
|
|
108
118
|
const r = spawnSync(process.execPath, [VALIDADOR, '--json'], {
|
|
109
|
-
cwd: process.cwd(),
|
|
119
|
+
cwd: raizProyecto(process.cwd()),
|
|
110
120
|
encoding: 'utf8',
|
|
111
121
|
timeout: TIMEOUT_MS,
|
|
112
122
|
windowsHide: true,
|
|
@@ -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 crypto = require('crypto');
|
|
24
25
|
|
|
@@ -76,7 +77,7 @@ const DIRS_CANONICOS = [
|
|
|
76
77
|
];
|
|
77
78
|
|
|
78
79
|
// ─── Cargar allowlist (manifest si existe, embebida si no) ──────────────────
|
|
79
|
-
const CWD = process.cwd();
|
|
80
|
+
const CWD = raizProyecto(process.cwd());
|
|
80
81
|
const allowlistPath = path.join(CWD, 'manifiestos', 'planning-paths.json');
|
|
81
82
|
let permitidos;
|
|
82
83
|
try {
|