@saulwade/swl-ses 2.6.1 → 2.8.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 +14 -2
- package/README.md +65 -18
- package/agentes/_intent-spec.md +73 -73
- package/agentes/_propose-step.md +90 -90
- package/bin/swl-ses.js +10 -0
- package/comandos/swl/brainstorm.md +1 -0
- package/comandos/swl/briefing.md +119 -119
- package/comandos/swl/contribuir.md +233 -233
- package/comandos/swl/deuda-codigo.md +97 -97
- package/comandos/swl/mcp-status.md +1 -0
- package/gateway/lib/event-channel.js +191 -191
- package/habilidades/agent-deep-links/SKILL.md +148 -148
- package/habilidades/backend-async-postgres-testing/SKILL.md +216 -216
- package/habilidades/backend-error-design/SKILL.md +221 -221
- package/habilidades/backend-production-resilience/SKILL.md +288 -288
- package/habilidades/calidad-anti-patrones-universales/SKILL.md +105 -1
- package/habilidades/calidad-contract-testing/SKILL.md +165 -165
- package/habilidades/calidad-mutation-testing/SKILL.md +25 -1
- package/habilidades/checklist-seguridad/recursos/stride-cobertura.md +60 -60
- package/habilidades/ci-cd-pipelines/SKILL.md +5 -1
- package/habilidades/css-moderno/SKILL.md +7 -1
- package/habilidades/diagrama-arquitectura/assets/template.html +276 -276
- package/habilidades/doubt-driven-review/recursos/EXAMPLES.md +130 -130
- package/habilidades/estructura-proyecto-claude/recursos/mcp-json-template.json +57 -57
- package/habilidades/extractor-de-aprendizajes/SKILL.md +5 -1
- package/habilidades/feynman-auditor-swl/recursos/preguntas-language-agnostic.md +108 -108
- package/habilidades/harness-claude-code/SKILL.md +3 -2
- package/habilidades/meta-skills-estandar/recursos/convencion-examples.md +93 -93
- package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -469
- package/habilidades/perfil-usuario/SKILL.md +200 -200
- package/habilidades/prevencion-sobreingenieria/recursos/EXAMPLES.md +580 -580
- package/habilidades/prevencion-sobreingenieria/recursos/soluciones-nativas.md +166 -166
- package/habilidades/prevencion-sobreingenieria/recursos/variables-residuales-post-refactor.md +85 -85
- package/habilidades/proceso-ddia-streaming/SKILL.md +231 -231
- package/habilidades/proceso-discovery-machote/SKILL.md +157 -157
- package/habilidades/proceso-dynamic-workflows/SKILL.md +60 -0
- package/habilidades/proceso-dynamic-workflows/recursos/template-adversarial-verify.js +65 -65
- package/habilidades/proceso-dynamic-workflows/recursos/template-triage.js +65 -65
- package/habilidades/proceso-intent-engineering/SKILL.md +269 -269
- package/habilidades/proceso-modular-split/SKILL.md +256 -256
- package/habilidades/state-inconsistency-auditor-swl/recursos/coupled-state-patterns.md +147 -147
- package/habilidades/swl-claudemd/recursos/contrato-aprender.md +83 -83
- package/habilidades/swl-claudemd/recursos/duplicacion-reglas-globales.md +85 -85
- package/habilidades/swl-claudemd/recursos/plantillas-init.md +94 -94
- package/habilidades/tdd-workflow/recursos/gherkin-bdd.md +111 -111
- package/hooks/calidad-pre-commit.js +159 -10
- package/hooks/ciclo-evolucion-subagente.js +26 -26
- package/hooks/ciclo-evolucion.js +26 -26
- package/hooks/contexto-subagente.js +68 -68
- package/hooks/lib/auto-consolidator.js +335 -335
- package/hooks/lib/ciclo-evolucion.js +47 -47
- package/hooks/lib/deep-links.js +185 -185
- package/hooks/lib/error-classifier.js +308 -308
- package/hooks/lib/notificacion-formato.js +45 -11
- package/hooks/lib/provenance-tracker.js +191 -191
- package/hooks/lib/raiz-proyecto.js +35 -4
- 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/notificacion-telegram.js +5 -11
- package/hooks/session-briefing.js +12 -4
- package/instintos/autonomia.yaml +27 -27
- package/instintos/prompt-appendices.yaml +57 -57
- package/llms.txt +1 -1
- package/manifiestos/agent-output-schemas.json +57 -57
- package/manifiestos/canonical-hashes.json +662 -0
- package/manifiestos/harness-ir.json +47536 -0
- package/manifiestos/hooks-config.json +469 -469
- package/manifiestos/invariantes-criticos.json +30 -30
- package/manifiestos/policy-bundle.json +2065 -0
- package/manifiestos/policy-corpus-w2.json +3926 -0
- package/manifiestos/runtime-adapters-core3.json +208 -0
- package/manifiestos/runtime-conformance.json +139 -0
- package/manifiestos/skills-lock.json +43 -43
- package/package.json +2 -2
- package/plantillas/auditor-veto-template.md +105 -105
- 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 +2 -2
- package/reglas/accesibilidad.md +10 -10
- package/reglas/auditorias-documentales-estructurales.md +7 -7
- package/reglas/cloud-infra.md +8 -8
- package/reglas/consultar-vault-primero.md +195 -195
- package/reglas/git-workflow.md +1 -0
- package/reglas/hooks.md +6 -6
- package/reglas/intent-engineering.md +218 -218
- package/reglas/markitdown.md +8 -8
- package/reglas/monitor-ci.md +12 -0
- package/reglas/patrones.md +6 -6
- package/reglas/testing.md +7 -7
- package/reglas/tests-cleanup.md +224 -224
- package/schemas/agent-message.schema.json +73 -73
- package/schemas/agent-output-implementacion.schema.json +114 -114
- package/schemas/agent-output-planificacion.schema.json +150 -150
- package/schemas/agent-output-review.schema.json +98 -98
- package/schemas/diary-entry.schema.json +112 -112
- package/schemas/gate-state.schema.json +76 -0
- package/schemas/harness-ir.schema.json +369 -0
- package/schemas/hook-profiles.schema.json +54 -54
- package/schemas/hooks-config.schema.json +89 -89
- package/schemas/legacy-gates.schema.json +45 -0
- package/schemas/modulos.schema.json +38 -38
- package/schemas/perfiles.schema.json +36 -36
- package/schemas/plugin.schema.json +77 -77
- package/schemas/policy-bundle.schema.json +140 -0
- package/schemas/policy-enforcement.schema.json +117 -0
- package/schemas/policy-operation.schema.json +261 -0
- package/schemas/runtime-adapter.schema.json +176 -0
- package/schemas/runtime-build-attestation.schema.json +100 -0
- package/schemas/runtime-conformance.schema.json +239 -0
- package/schemas/runtime-diagnostic.schema.json +395 -0
- package/schemas/skill-evals.schema.json +119 -119
- package/schemas/skill-frontmatter.schema.json +245 -245
- package/schemas/w4-certification-request.schema.json +72 -0
- package/schemas/w4-certification-verdict.schema.json +224 -0
- package/schemas/w4-corpus.schema.json +172 -0
- package/schemas/w4-mutation-report.schema.json +116 -0
- package/schemas/w4-replay-result.schema.json +164 -0
- package/schemas/w4-scoring-report.schema.json +89 -0
- 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/bootstrap-instintos.js +3 -0
- package/scripts/cli/aprobar-plan.js +73 -73
- package/scripts/cli/briefing.js +23 -23
- package/scripts/cli/ciclo-evolucion.js +26 -26
- package/scripts/cli/derivar-feature-list.js +25 -25
- package/scripts/cli/detectar-host.js +27 -27
- package/scripts/cli/diary-entry.js +69 -69
- package/scripts/cli/execution-state.js +18 -18
- package/scripts/cli/gateway-notify.js +41 -41
- package/scripts/cli/liberar-fase.js +42 -42
- package/scripts/cli/mark-evolved.js +56 -56
- package/scripts/cli/metricas-dora.js +26 -26
- package/scripts/cli/near-duplicate.js +55 -55
- package/scripts/cli/notificaciones.js +123 -123
- package/scripts/cli/propose-step.js +29 -29
- package/scripts/cli/schedule-parse.js +19 -19
- package/scripts/cli/sugerir-modelo.js +20 -20
- package/scripts/cli/verificar-plan.js +36 -36
- package/scripts/cli/verificar-trazabilidad.js +35 -35
- package/scripts/comandos/install-asistido.js +8 -7
- package/scripts/configurar-branch-protection.js +418 -418
- package/scripts/detectar-aprendizajes-duplicados.js +151 -151
- package/scripts/doctor.js +61 -36
- package/scripts/generar-checklists-consolidados.js +273 -273
- package/scripts/generar-claims-runtime.js +1342 -0
- package/scripts/generar-harness-ir.js +257 -0
- package/scripts/generar-inventario.js +52 -54
- package/scripts/generar-policy-bundle.js +202 -0
- package/scripts/instalador.js +26 -7
- package/scripts/lib/approval-receipts.js +190 -0
- 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/certificacion-loop-state.js +421 -0
- package/scripts/lib/ci-reader.js +193 -193
- package/scripts/lib/ciclo-autonomo/yaml-instintos.js +56 -0
- package/scripts/lib/clasificar-directorio.js +92 -0
- package/scripts/lib/contadores-inventario.js +217 -217
- package/scripts/lib/detectar-host-swl.js +175 -175
- package/scripts/lib/detectar-runtime.js +29 -20
- package/scripts/lib/detectar-stack-detallado.js +307 -307
- package/scripts/lib/detector-autoduplicacion-intra-archivo.js +234 -234
- package/scripts/lib/detector-reglas-duplicadas.js +220 -220
- 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/evidence-verifier.js +192 -0
- package/scripts/lib/evidencia-release.js +322 -322
- package/scripts/lib/frontmatter-canonico.js +509 -0
- package/scripts/lib/gate-engine.js +871 -0
- package/scripts/lib/gate-hooks-requires.js +249 -249
- package/scripts/lib/gate-licencias.js +212 -212
- package/scripts/lib/git-config-preflight.js +48 -0
- package/scripts/lib/git-metricas.js +257 -257
- package/scripts/lib/harness-ir.js +778 -0
- package/scripts/lib/harness-source-snapshot.js +309 -0
- package/scripts/lib/integrity-ledger.js +1147 -0
- package/scripts/lib/jaccard-similarity.js +98 -98
- package/scripts/lib/legacy-gate-migration.js +324 -0
- package/scripts/lib/limpiar-basura-global.js +45 -2
- package/scripts/lib/longmemeval-runner.js +125 -125
- package/scripts/lib/metricas-dora.js +204 -204
- package/scripts/lib/notificaciones-telegram.js +1 -0
- package/scripts/lib/npm-version.js +1 -0
- package/scripts/lib/paquetes-conocidos.js +50 -50
- package/scripts/lib/plan-lock.js +61 -13
- package/scripts/lib/policy-broker.js +338 -0
- package/scripts/lib/policy-bundle.js +342 -0
- package/scripts/lib/policy-context-provider.js +310 -0
- package/scripts/lib/policy-contract.js +479 -0
- package/scripts/lib/policy-verifier-utils.js +65 -0
- package/scripts/lib/pr-analyzer.js +399 -399
- package/scripts/lib/principal-verifier.js +178 -0
- package/scripts/lib/prompt-builder.js +264 -264
- package/scripts/lib/resolver-plan-fase.js +37 -37
- package/scripts/lib/rrf-fusion.js +175 -175
- package/scripts/lib/runtime-adapter-contract.js +267 -0
- package/scripts/lib/runtime-artifact-verifier.js +426 -0
- package/scripts/lib/runtime-build-attestation.js +127 -0
- package/scripts/lib/runtime-bundle-installer.js +586 -0
- package/scripts/lib/runtime-compiler.js +327 -0
- package/scripts/lib/runtime-conformance.js +202 -0
- package/scripts/lib/runtime-doctor-core3.js +567 -0
- package/scripts/lib/runtime-doctor-input.js +59 -0
- package/scripts/lib/runtime-operation-adapter.js +267 -0
- package/scripts/lib/schema-version.js +164 -164
- package/scripts/lib/semantic-search.js +252 -252
- package/scripts/lib/signed-envelope.js +545 -0
- package/scripts/lib/single-use-store.js +359 -0
- package/scripts/lib/skills-externas.js +31 -0
- package/scripts/lib/transformadores/codex.js +15 -8
- package/scripts/lib/transformadores/gemini.js +79 -5
- package/scripts/lib/w4-attestation-adapter.js +158 -0
- package/scripts/lib/w4-canario.js +337 -0
- package/scripts/lib/w4-claims.js +182 -0
- package/scripts/lib/w4-corpus-generador.js +542 -0
- package/scripts/lib/w4-gate-c5.js +115 -0
- package/scripts/lib/w4-harness-bajo-prueba.js +155 -0
- package/scripts/lib/w4-matriz-combos.js +55 -0
- package/scripts/lib/w4-motor-mutacion.js +1348 -0
- package/scripts/lib/w4-motor-replay.js +735 -0
- package/scripts/lib/w4-pin-origen.js +54 -0
- package/scripts/lib/w4-publicar-request.js +132 -0
- package/scripts/lib/w4-revocacion.js +62 -0
- package/scripts/lib/w4-runtimes-core3.js +38 -0
- package/scripts/lib/w4-scorer-certificacion.js +692 -0
- package/scripts/lib/w4-superficie-candidato.js +49 -0
- package/scripts/lib/w4-veredicto.js +452 -0
- package/scripts/lib/w4-verificar-veredicto.js +302 -0
- package/scripts/limpiar-artefactos-python.js +131 -131
- package/scripts/migrar-csv-a-array.js +168 -168
- package/scripts/migrar-fase-dominio.js +200 -200
- package/scripts/migrar-gates-legacy.js +108 -0
- package/scripts/publicar-certification-request.js +115 -0
- package/scripts/runtime-doctor.js +107 -0
- package/scripts/tui/componentes/selector-multi.js +189 -189
- package/scripts/tui/componentes/selector-unico.js +158 -158
- package/scripts/tui/ejecutores.js +375 -375
- package/scripts/tui/lib/colores.js +129 -129
- package/scripts/tui/lib/render.js +264 -264
- package/scripts/tui/lib/teclas.js +113 -113
- package/scripts/tui/pantallas/install-wizard.js +12 -7
- package/scripts/tui/pantallas/menu-principal.js +52 -52
- package/scripts/tui/pantallas/progreso.js +274 -274
- package/scripts/tui/pantallas/resumen.js +132 -132
- package/scripts/validar-userland-vacio.js +110 -110
- package/scripts/verificar-aislamiento-swl-eval.js +87 -0
- package/scripts/verificar-empaquetado-downstream.js +375 -0
- package/scripts/verificar-loop-constructor.js +215 -0
- package/scripts/verificar-trazabilidad.js +13 -6
- package/scripts/verificar-veredicto-real.js +84 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Captura inmutable y confinada de las fuentes tracked del harness.
|
|
5
|
+
* Es la única frontera de lectura compartida por Harness IR y PolicyBundle.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const childProcess = require('node:child_process');
|
|
9
|
+
const crypto = require('node:crypto');
|
|
10
|
+
const fs = require('node:fs');
|
|
11
|
+
const path = require('node:path');
|
|
12
|
+
|
|
13
|
+
const { normalizarLF, parsearFrontmatter, parsearJSONSeguro } = require('./frontmatter-canonico');
|
|
14
|
+
|
|
15
|
+
const HOOKS_CONFIG = 'manifiestos/hooks-config.json';
|
|
16
|
+
const MAX_BUFFER_GIT = 16 * 1024 * 1024;
|
|
17
|
+
|
|
18
|
+
function crearError(codigo, mensaje, extras = {}) {
|
|
19
|
+
const excepcion = new Error(mensaje);
|
|
20
|
+
Object.assign(excepcion, { codigo, ...extras });
|
|
21
|
+
return excepcion;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function sha256(valor) {
|
|
25
|
+
return crypto.createHash('sha256').update(valor).digest('hex');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function congelarProfundo(valor) {
|
|
29
|
+
if (!valor || typeof valor !== 'object' || Object.isFrozen(valor)) return valor;
|
|
30
|
+
Object.values(valor).forEach(congelarProfundo);
|
|
31
|
+
return Object.freeze(valor);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function ejecutarGit(args, cwd = process.cwd()) {
|
|
35
|
+
try {
|
|
36
|
+
return childProcess.execFileSync('git', args, {
|
|
37
|
+
cwd,
|
|
38
|
+
encoding: 'utf8',
|
|
39
|
+
maxBuffer: MAX_BUFFER_GIT,
|
|
40
|
+
});
|
|
41
|
+
} catch (cause) {
|
|
42
|
+
throw crearError('GIT_DISCOVERY_FAILED', 'No fue posible consultar Git', { cause });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function resolverRaizGit() {
|
|
47
|
+
const declarada = ejecutarGit(['rev-parse', '--show-toplevel']).trim();
|
|
48
|
+
if (/^[\\/]{2}/u.test(declarada)) {
|
|
49
|
+
throw crearError('SOURCE_ROOT_UNC_FORBIDDEN', 'La raíz Git no puede ser UNC');
|
|
50
|
+
}
|
|
51
|
+
const absoluta = path.resolve(declarada);
|
|
52
|
+
const stat = fs.lstatSync(absoluta);
|
|
53
|
+
const real = fs.realpathSync(absoluta);
|
|
54
|
+
const esAlias = path.relative(absoluta, real) !== '';
|
|
55
|
+
if (stat.isSymbolicLink() || !stat.isDirectory() || esAlias) {
|
|
56
|
+
throw crearError('SOURCE_ROOT_LINK_FORBIDDEN', 'La raíz Git debe ser real no enlazada');
|
|
57
|
+
}
|
|
58
|
+
return { absoluta, real };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function valorTemporalNs(stat, campoNs, campoMs) {
|
|
62
|
+
return String(stat[campoNs] === undefined ? stat[campoMs] : stat[campoNs]);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function fingerprint(stat) {
|
|
66
|
+
return {
|
|
67
|
+
dev: String(stat.dev),
|
|
68
|
+
ino: String(stat.ino),
|
|
69
|
+
size: String(stat.size),
|
|
70
|
+
mode: String(stat.mode),
|
|
71
|
+
mtimeNs: valorTemporalNs(stat, 'mtimeNs', 'mtimeMs'),
|
|
72
|
+
ctimeNs: valorTemporalNs(stat, 'ctimeNs', 'ctimeMs'),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function fingerprintPath(ruta) {
|
|
77
|
+
const stat = fs.lstatSync(ruta, { bigint: true });
|
|
78
|
+
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
79
|
+
throw crearError('SOURCE_INDEX_INVALID', 'El path no es un archivo regular');
|
|
80
|
+
}
|
|
81
|
+
return fingerprint(stat);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function resolverIndiceGit(root) {
|
|
85
|
+
const declarada = ejecutarGit(
|
|
86
|
+
['rev-parse', '--path-format=absolute', '--git-path', 'index'],
|
|
87
|
+
root.absoluta
|
|
88
|
+
).trim();
|
|
89
|
+
if (!declarada) throw crearError('SOURCE_INDEX_INVALID', 'Ruta de índice vacía');
|
|
90
|
+
const absoluta = path.resolve(root.absoluta, declarada);
|
|
91
|
+
const real = fs.realpathSync(absoluta);
|
|
92
|
+
if (path.relative(absoluta, real) !== '') {
|
|
93
|
+
throw crearError('SOURCE_INDEX_INVALID', 'El índice Git está enlazado');
|
|
94
|
+
}
|
|
95
|
+
return { absoluta, fingerprint: fingerprintPath(absoluta) };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function revalidarFingerprintIndice(indiceGit) {
|
|
99
|
+
const actual = fingerprintPath(indiceGit.absoluta);
|
|
100
|
+
if (JSON.stringify(actual) !== JSON.stringify(indiceGit.fingerprint)) {
|
|
101
|
+
throw crearError('SOURCE_INDEX_CHANGED', 'La identidad del índice Git cambió');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function obtenerIndice(root) {
|
|
106
|
+
const snapshot = ejecutarGit(['ls-files', '--stage', '-z'], root.absoluta);
|
|
107
|
+
const entradas = snapshot.split('\0').filter(Boolean);
|
|
108
|
+
const tracked = [];
|
|
109
|
+
const modes = new Map();
|
|
110
|
+
for (const entrada of entradas) {
|
|
111
|
+
const match = entrada.match(/^(\d+) [0-9a-f]+ (\d+)\t([\s\S]+)$/u);
|
|
112
|
+
if (!match || match[2] !== '0' || modes.has(match[3])) {
|
|
113
|
+
throw crearError('SOURCE_INDEX_INVALID', 'Entrada de índice Git inválida');
|
|
114
|
+
}
|
|
115
|
+
tracked.push(match[3]);
|
|
116
|
+
modes.set(match[3], match[1]);
|
|
117
|
+
}
|
|
118
|
+
return { tracked, modes, snapshot };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function revalidarIndice(root, indice, indiceGit) {
|
|
122
|
+
const actual = ejecutarGit(['ls-files', '--stage', '-z'], root.absoluta);
|
|
123
|
+
if (actual !== indice.snapshot) {
|
|
124
|
+
throw crearError('SOURCE_INDEX_CHANGED', 'El índice Git cambió durante la captura');
|
|
125
|
+
}
|
|
126
|
+
revalidarFingerprintIndice(indiceGit);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function validarPathFuente(relativo) {
|
|
130
|
+
const segmentos = relativo.split('/');
|
|
131
|
+
const esInvalido = relativo.includes('\\')
|
|
132
|
+
|| path.posix.isAbsolute(relativo)
|
|
133
|
+
|| /^[A-Za-z]:/u.test(relativo)
|
|
134
|
+
|| segmentos.some((segmento) => !segmento || segmento === '.' || segmento === '..');
|
|
135
|
+
if (esInvalido) throw crearError('SOURCE_PATH_INVALID', `Path fuente inválido: ${relativo}`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function tipoFuente(relativo) {
|
|
139
|
+
if (/^agentes\/[^/]+\.md$/u.test(relativo) && !path.posix.basename(relativo).startsWith('_')) {
|
|
140
|
+
return 'agent';
|
|
141
|
+
}
|
|
142
|
+
if (/^comandos\/swl\/[^/]+\.md$/u.test(relativo)) return 'command';
|
|
143
|
+
if (/^habilidades\/[^/]+\/SKILL\.md$/u.test(relativo)) return 'skill';
|
|
144
|
+
if (/^reglas\/(?:[^/]+\/)*[^/]+\.md$/u.test(relativo)) return 'rule';
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function seleccionarFuentes(indice) {
|
|
149
|
+
const fuentes = indice.tracked.flatMap((relativo) => {
|
|
150
|
+
validarPathFuente(relativo);
|
|
151
|
+
const kind = tipoFuente(relativo);
|
|
152
|
+
return kind ? [{ path: relativo, kind }] : [];
|
|
153
|
+
});
|
|
154
|
+
if (!indice.tracked.includes(HOOKS_CONFIG)) {
|
|
155
|
+
throw crearError('HOOKS_CONFIG_NOT_TRACKED', `${HOOKS_CONFIG} debe estar tracked`);
|
|
156
|
+
}
|
|
157
|
+
return fuentes;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function validarModos(indice, fuentes) {
|
|
161
|
+
const paths = [...fuentes.map(({ path: relativo }) => relativo), HOOKS_CONFIG];
|
|
162
|
+
for (const relativo of paths) {
|
|
163
|
+
if (indice.modes.get(relativo) === '120000') {
|
|
164
|
+
throw crearError('SOURCE_INDEX_MODE_FORBIDDEN', `Symlink tracked prohibido: ${relativo}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function estaDentro(base, destino) {
|
|
170
|
+
const relativo = path.relative(base, destino);
|
|
171
|
+
return relativo === '' || (!relativo.startsWith(`..${path.sep}`) && !path.isAbsolute(relativo));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function validarSegmentos(root, relativo) {
|
|
175
|
+
let actual = root.absoluta;
|
|
176
|
+
for (const segmento of relativo.split('/').slice(0, -1)) {
|
|
177
|
+
actual = path.join(actual, segmento);
|
|
178
|
+
const stat = fs.lstatSync(actual);
|
|
179
|
+
if (stat.isSymbolicLink()) {
|
|
180
|
+
throw crearError('SOURCE_LINK_FORBIDDEN', `Segmento enlazado prohibido: ${relativo}`);
|
|
181
|
+
}
|
|
182
|
+
if (!estaDentro(root.real, fs.realpathSync(actual))) {
|
|
183
|
+
throw crearError('SOURCE_PATH_ESCAPE', `Segmento fuera de raíz: ${relativo}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function mismaIdentidad(antes, despues) {
|
|
189
|
+
return JSON.stringify(fingerprint(antes)) === JSON.stringify(fingerprint(despues));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function abrirDescriptorFuente(absoluta, relativo) {
|
|
193
|
+
try {
|
|
194
|
+
return fs.openSync(absoluta, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW || 0));
|
|
195
|
+
} catch (cause) {
|
|
196
|
+
if (cause.code === 'ELOOP') {
|
|
197
|
+
throw crearError('SOURCE_LINK_FORBIDDEN', `Fuente enlazada prohibida: ${relativo}`, { cause });
|
|
198
|
+
}
|
|
199
|
+
throw crearError('SOURCE_OPEN_FAILED', `No fue posible abrir: ${relativo}`, { cause });
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function leerBytesFuente(descriptor, antes, relativo) {
|
|
204
|
+
const abierta = fs.fstatSync(descriptor);
|
|
205
|
+
if (!mismaIdentidad(antes, abierta)) {
|
|
206
|
+
throw crearError('SOURCE_CHANGED_DURING_READ', `Fuente cambió antes: ${relativo}`);
|
|
207
|
+
}
|
|
208
|
+
const bytes = fs.readFileSync(descriptor);
|
|
209
|
+
if (!mismaIdentidad(abierta, fs.fstatSync(descriptor))) {
|
|
210
|
+
throw crearError('SOURCE_CHANGED_DURING_READ', `Fuente cambió durante: ${relativo}`);
|
|
211
|
+
}
|
|
212
|
+
return bytes;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function leerFuente(root, relativo) {
|
|
216
|
+
validarPathFuente(relativo);
|
|
217
|
+
validarSegmentos(root, relativo);
|
|
218
|
+
const absoluta = path.join(root.absoluta, ...relativo.split('/'));
|
|
219
|
+
const antes = fs.lstatSync(absoluta);
|
|
220
|
+
const real = fs.realpathSync(absoluta);
|
|
221
|
+
if (antes.isSymbolicLink()) {
|
|
222
|
+
throw crearError('SOURCE_LINK_FORBIDDEN', `Fuente enlazada prohibida: ${relativo}`);
|
|
223
|
+
}
|
|
224
|
+
if (!estaDentro(root.real, real)) {
|
|
225
|
+
throw crearError('SOURCE_PATH_ESCAPE', `Fuente fuera de raíz: ${relativo}`);
|
|
226
|
+
}
|
|
227
|
+
const descriptor = abrirDescriptorFuente(absoluta, relativo);
|
|
228
|
+
let bytes;
|
|
229
|
+
try {
|
|
230
|
+
bytes = leerBytesFuente(descriptor, antes, relativo);
|
|
231
|
+
} finally {
|
|
232
|
+
fs.closeSync(descriptor);
|
|
233
|
+
}
|
|
234
|
+
const texto = normalizarLF(bytes.toString('utf8'));
|
|
235
|
+
return {
|
|
236
|
+
source: { path: relativo, texto, sha256: sha256(Buffer.from(texto, 'utf8')) },
|
|
237
|
+
evidencia: { absoluta, real, stat: antes },
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function parsearArchivo(source, descriptor) {
|
|
242
|
+
if (descriptor.kind === 'rule' && !source.texto.startsWith('---\n')) {
|
|
243
|
+
return { valor: {}, campos: {}, cuerpo: source.texto };
|
|
244
|
+
}
|
|
245
|
+
try {
|
|
246
|
+
const parsed = parsearFrontmatter(source.texto, { path: descriptor.path });
|
|
247
|
+
if (descriptor.kind !== 'rule' && parsed.valor === null) {
|
|
248
|
+
throw crearError('SOURCE_FRONTMATTER_REQUIRED', `Frontmatter requerido: ${descriptor.path}`);
|
|
249
|
+
}
|
|
250
|
+
return parsed;
|
|
251
|
+
} catch (cause) {
|
|
252
|
+
if (cause.codigo === 'SOURCE_FRONTMATTER_REQUIRED') throw cause;
|
|
253
|
+
throw crearError('SOURCE_FRONTMATTER_INVALID', 'Frontmatter inválido', {
|
|
254
|
+
cause,
|
|
255
|
+
path: descriptor.path,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function parsearHooks(source) {
|
|
261
|
+
try {
|
|
262
|
+
const resultado = parsearJSONSeguro(source.texto, { path: source.path });
|
|
263
|
+
return { valor: resultado.valor, propiedades: resultado.propiedades };
|
|
264
|
+
} catch (cause) {
|
|
265
|
+
throw crearError('HOOKS_CONFIG_INVALID', 'JSON de hooks inválido', { cause });
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function revalidarFuentes(capturas) {
|
|
270
|
+
for (const { evidencia, source } of capturas) {
|
|
271
|
+
const despues = fs.lstatSync(evidencia.absoluta);
|
|
272
|
+
const realDespues = fs.realpathSync(evidencia.absoluta);
|
|
273
|
+
if (!mismaIdentidad(evidencia.stat, despues) || evidencia.real !== realDespues) {
|
|
274
|
+
throw crearError('SOURCE_CHANGED_DURING_READ', `Fuente cambió: ${source.path}`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function digestSnapshot(entries, hooks) {
|
|
280
|
+
const fuentes = entries.map(({ kind, path: relativo, source }) => ({
|
|
281
|
+
kind,
|
|
282
|
+
path: relativo,
|
|
283
|
+
sha256: source.sha256,
|
|
284
|
+
}));
|
|
285
|
+
fuentes.push({ kind: 'hooks-config', path: hooks.source.path, sha256: hooks.source.sha256 });
|
|
286
|
+
return sha256(JSON.stringify(fuentes));
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function capturarHarnessSourceSnapshot() {
|
|
290
|
+
const root = resolverRaizGit();
|
|
291
|
+
const indiceGit = resolverIndiceGit(root);
|
|
292
|
+
const indice = obtenerIndice(root);
|
|
293
|
+
revalidarFingerprintIndice(indiceGit);
|
|
294
|
+
const descriptores = seleccionarFuentes(indice);
|
|
295
|
+
validarModos(indice, descriptores);
|
|
296
|
+
const capturas = descriptores.map((descriptor) => ({ ...leerFuente(root, descriptor.path), descriptor }));
|
|
297
|
+
const hooksCaptura = leerFuente(root, HOOKS_CONFIG);
|
|
298
|
+
const entries = capturas.map(({ descriptor, source }) => ({
|
|
299
|
+
...descriptor,
|
|
300
|
+
source,
|
|
301
|
+
parsed: parsearArchivo(source, descriptor),
|
|
302
|
+
}));
|
|
303
|
+
const hooks = { source: hooksCaptura.source, parsed: parsearHooks(hooksCaptura.source) };
|
|
304
|
+
revalidarFuentes([...capturas, hooksCaptura]);
|
|
305
|
+
revalidarIndice(root, indice, indiceGit);
|
|
306
|
+
return congelarProfundo({ entries, hooks, snapshotDigest: digestSnapshot(entries, hooks) });
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
module.exports = { capturarHarnessSourceSnapshot };
|