@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,359 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/** Fachada single-use compuesta sobre transacciones privadas del ledger. */
|
|
4
|
+
|
|
5
|
+
const crypto = require('node:crypto');
|
|
6
|
+
|
|
7
|
+
const { assertApprovalCurrent, isVerifiedApproval } = require('./approval-receipts');
|
|
8
|
+
const { assertPrincipalCurrent, isVerifiedPrincipal } = require('./principal-verifier');
|
|
9
|
+
const {
|
|
10
|
+
exactKeys, fail, freezeDeep, getContractValidators, plainObject,
|
|
11
|
+
} = require('./policy-verifier-utils');
|
|
12
|
+
const { canonicalizarIJson, validarTimestampIso } = require('./signed-envelope');
|
|
13
|
+
|
|
14
|
+
const GRANT_OWNERS = new WeakMap();
|
|
15
|
+
const CLAIM_RECEIPT_OWNERS = new WeakMap();
|
|
16
|
+
const SHA256 = /^[a-f0-9]{64}$/u;
|
|
17
|
+
const IDENTIFIER = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
18
|
+
const TASK = /^T-[0-9]{2}$/u;
|
|
19
|
+
|
|
20
|
+
function hitFault(point) {
|
|
21
|
+
if (process.env.SWL_SINGLE_USE_FAULT !== point) return;
|
|
22
|
+
if (process.env.NODE_ENV !== 'test') fail('FAULT_CONFIG_INVALID', 'NODE_ENV inválido');
|
|
23
|
+
if (process.env.SWL_INTEGRITY_TEST_MODE !== '1') {
|
|
24
|
+
fail('FAULT_CONFIG_INVALID', 'modo de integridad inválido');
|
|
25
|
+
}
|
|
26
|
+
fail('FAULT_INJECTED', `falla inyectada: ${point}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function validatePrincipalOperation(principal, operation) {
|
|
30
|
+
if (!isVerifiedPrincipal(principal)) fail('SINGLE_USE_PRINCIPAL_INVALID', 'principal inválido');
|
|
31
|
+
if (!getContractValidators().isVerifiedOperation(operation)) {
|
|
32
|
+
fail('SINGLE_USE_OPERATION_INVALID', 'operación inválida');
|
|
33
|
+
}
|
|
34
|
+
if (principal.operationDigest !== operation.operationDigest) {
|
|
35
|
+
fail('SINGLE_USE_BINDING_INVALID', 'principal ligado a otra operación');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function validateApprovalBinding(approval, principal, operation, authority) {
|
|
40
|
+
if (!isVerifiedApproval(approval)) fail('APPROVAL_INVALID', 'approval no verificada');
|
|
41
|
+
assertPrincipalCurrent(principal, authority);
|
|
42
|
+
assertApprovalCurrent(approval, authority);
|
|
43
|
+
const expected = {
|
|
44
|
+
principalDigest: principal.principalDigest, operationDigest: operation.operationDigest,
|
|
45
|
+
sessionId: principal.sessionId, runtimeId: principal.runtimeId,
|
|
46
|
+
projectId: principal.projectId, worktreeDigest: principal.worktreeDigest,
|
|
47
|
+
capability: operation.capability, effectKind: operation.effectKind,
|
|
48
|
+
scopeDigest: operation.scope.digest, fsmVersion: operation.fsmVersion,
|
|
49
|
+
trustEpoch: authority.trustEpoch, trustDigest: authority.trustDigest,
|
|
50
|
+
};
|
|
51
|
+
if (Object.entries(expected).some(([key, value]) => approval[key] !== value)) {
|
|
52
|
+
fail('APPROVAL_BINDING_INVALID', 'approval prestada o stale');
|
|
53
|
+
}
|
|
54
|
+
validarTimestampIso(approval.expiresAt, 'approval expiresAt');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function grantIdFor(approval) {
|
|
58
|
+
return `grant:${approval.approvalDigest}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function grantClaims(approval, authority) {
|
|
62
|
+
const base = {
|
|
63
|
+
kind: 'authorization-grant', schemaVersion: '1.0.0', grantId: grantIdFor(approval),
|
|
64
|
+
approvalDigest: approval.approvalDigest,
|
|
65
|
+
principalDigest: approval.principalDigest, operationDigest: approval.operationDigest,
|
|
66
|
+
capability: approval.capability, effectKind: approval.effectKind,
|
|
67
|
+
scopeDigest: approval.scopeDigest, planDigest: approval.planDigest,
|
|
68
|
+
fsmVersion: approval.fsmVersion, issuedAt: authority.observedAt,
|
|
69
|
+
expiresAt: approval.expiresAt, trustEpoch: authority.trustEpoch,
|
|
70
|
+
trustDigest: authority.trustDigest, rootKeyDigest: authority.rootKeyDigest,
|
|
71
|
+
status: 'issued',
|
|
72
|
+
};
|
|
73
|
+
let claims = base;
|
|
74
|
+
if (approval.schemaVersion !== '1.0.0') {
|
|
75
|
+
claims = {
|
|
76
|
+
...base, schemaVersion: approval.schemaVersion,
|
|
77
|
+
phaseId: approval.phaseId, taskId: approval.taskId,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
if (approval.schemaVersion === '1.2.0') {
|
|
81
|
+
claims.g1ApprovalDigest = approval.g1ApprovalDigest;
|
|
82
|
+
}
|
|
83
|
+
return { ...claims, grantDigest: crypto.createHash('sha256')
|
|
84
|
+
.update(canonicalizarIJson(claims), 'utf8').digest('hex') };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function policyGrantBase(state, authority, options) {
|
|
88
|
+
const seed = canonicalizarIJson({
|
|
89
|
+
gateAuthorizationDigest: options.gate.gateAuthorizationDigest,
|
|
90
|
+
operationDigest: options.operation.operationDigest,
|
|
91
|
+
sequence: state.sequence + 1,
|
|
92
|
+
});
|
|
93
|
+
const base = {
|
|
94
|
+
kind: 'authorization-grant', schemaVersion: options.gate.g1ApprovalDigest
|
|
95
|
+
? '1.2.0' : '1.1.0',
|
|
96
|
+
grantId: `grant:${crypto.createHash('sha256').update(seed).digest('hex')}`,
|
|
97
|
+
approvalDigest: options.gate.g1ApprovalDigest
|
|
98
|
+
|| options.gate.gateAuthorizationDigest,
|
|
99
|
+
principalDigest: options.principal.principalDigest,
|
|
100
|
+
operationDigest: options.operation.operationDigest,
|
|
101
|
+
capability: options.operation.capability, effectKind: options.operation.effectKind,
|
|
102
|
+
scopeDigest: options.operation.scope.digest, planDigest: options.gate.planDigest,
|
|
103
|
+
fsmVersion: options.operation.fsmVersion, phaseId: options.operation.phaseId,
|
|
104
|
+
taskId: options.operation.taskId,
|
|
105
|
+
issuedAt: authority.observedAt, expiresAt: options.expiresAt,
|
|
106
|
+
trustEpoch: authority.trustEpoch, trustDigest: authority.trustDigest,
|
|
107
|
+
rootKeyDigest: authority.rootKeyDigest, status: 'issued',
|
|
108
|
+
};
|
|
109
|
+
return base;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function policyGrantClaims(state, authority, options) {
|
|
113
|
+
validatePrincipalOperation(options.principal, options.operation);
|
|
114
|
+
assertPrincipalCurrent(options.principal, authority);
|
|
115
|
+
validarTimestampIso(options.expiresAt, 'policy grant expiresAt');
|
|
116
|
+
if (options.expiresAt < authority.observedAt) {
|
|
117
|
+
fail('GRANT_EXPIRED', 'policy grant expirada');
|
|
118
|
+
}
|
|
119
|
+
const base = policyGrantBase(state, authority, options);
|
|
120
|
+
if (options.gate.g1ApprovalDigest) {
|
|
121
|
+
base.g1ApprovalDigest = options.gate.g1ApprovalDigest;
|
|
122
|
+
}
|
|
123
|
+
return { ...base, grantDigest: crypto.createHash('sha256')
|
|
124
|
+
.update(canonicalizarIJson(base), 'utf8').digest('hex') };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function createGrant(claims, owner) {
|
|
128
|
+
const grant = freezeDeep(structuredClone(claims));
|
|
129
|
+
GRANT_OWNERS.set(grant, owner);
|
|
130
|
+
return grant;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function committedEvents(state) {
|
|
134
|
+
return state.events.filter(({ eventType }) => eventType === 'authorization.committed');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function claimedEvents(state) {
|
|
138
|
+
return state.events.filter(({ eventType }) => eventType === 'grant.claimed');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function assertApprovalUnused(state, approval) {
|
|
142
|
+
const used = committedEvents(state).some(({ payload }) => (
|
|
143
|
+
payload.approvalClaim?.approvalDigest === approval.approvalDigest
|
|
144
|
+
|| payload.approvalClaim?.approvalId === approval.approvalId
|
|
145
|
+
|| payload.approvalClaim?.nonce === approval.nonce
|
|
146
|
+
));
|
|
147
|
+
if (used) fail('APPROVAL_ALREADY_USED', 'approval ya reclamada');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function validateRedeemOptions(options) {
|
|
151
|
+
plainObject(options, 'SINGLE_USE_OPTIONS_INVALID', 'opciones redeem');
|
|
152
|
+
exactKeys(options, ['approval', 'principal', 'operation'],
|
|
153
|
+
'SINGLE_USE_OPTIONS_INVALID', 'opciones redeem');
|
|
154
|
+
validatePrincipalOperation(options.principal, options.operation);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function authorizationPayload(approval, grant) {
|
|
158
|
+
return {
|
|
159
|
+
schemaVersion: grant.schemaVersion,
|
|
160
|
+
approvalClaim: {
|
|
161
|
+
approvalId: approval.approvalId, approvalDigest: approval.approvalDigest,
|
|
162
|
+
nonce: approval.nonce, signerPublicKeyDigest: approval.signerPublicKeyDigest,
|
|
163
|
+
},
|
|
164
|
+
decision: { outcome: 'allow', reasonCode: 'APPROVAL_ACCEPTED' },
|
|
165
|
+
grant: structuredClone(grant),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function issueApprovalGrant(state, authority, owner, options) {
|
|
170
|
+
validateRedeemOptions(options);
|
|
171
|
+
validateApprovalBinding(options.approval, options.principal, options.operation, authority);
|
|
172
|
+
assertApprovalUnused(state, options.approval);
|
|
173
|
+
const grant = createGrant(grantClaims(options.approval, authority), owner);
|
|
174
|
+
return Object.freeze({
|
|
175
|
+
approvalClaim: Object.freeze({
|
|
176
|
+
approvalId: options.approval.approvalId,
|
|
177
|
+
approvalDigest: options.approval.approvalDigest,
|
|
178
|
+
nonce: options.approval.nonce,
|
|
179
|
+
signerPublicKeyDigest: options.approval.signerPublicKeyDigest,
|
|
180
|
+
}),
|
|
181
|
+
grant,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function issuePolicyGrant(state, authority, owner, options) {
|
|
186
|
+
plainObject(options, 'SINGLE_USE_OPTIONS_INVALID', 'opciones policy grant');
|
|
187
|
+
exactKeys(options, ['expiresAt', 'gate', 'principal', 'operation'],
|
|
188
|
+
'SINGLE_USE_OPTIONS_INVALID', 'opciones policy grant');
|
|
189
|
+
return createGrant(policyGrantClaims(state, authority, options), owner);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function redeemApproval(writer, owner, options) {
|
|
193
|
+
validateRedeemOptions(options);
|
|
194
|
+
let grant;
|
|
195
|
+
writer.transact((state, authority) => {
|
|
196
|
+
validateApprovalBinding(options.approval, options.principal, options.operation, authority);
|
|
197
|
+
assertApprovalUnused(state, options.approval);
|
|
198
|
+
hitFault('after-approval-claim-before-decision');
|
|
199
|
+
grant = createGrant(grantClaims(options.approval, authority), owner);
|
|
200
|
+
hitFault('after-decision-before-grant');
|
|
201
|
+
return {
|
|
202
|
+
expectedSequence: state.sequence, expectedHeadHash: state.headHash,
|
|
203
|
+
eventType: 'authorization.committed',
|
|
204
|
+
payload: authorizationPayload(options.approval, grant),
|
|
205
|
+
};
|
|
206
|
+
});
|
|
207
|
+
return grant;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function validateStoredGrant(grant) {
|
|
211
|
+
plainObject(grant, 'GRANT_INVALID', 'grant persistida');
|
|
212
|
+
const baseKeys = [
|
|
213
|
+
'kind', 'schemaVersion', 'grantId', 'approvalDigest', 'principalDigest',
|
|
214
|
+
'operationDigest', 'capability', 'effectKind', 'scopeDigest', 'planDigest',
|
|
215
|
+
'fsmVersion', 'issuedAt', 'expiresAt', 'trustEpoch', 'trustDigest', 'status',
|
|
216
|
+
'rootKeyDigest', 'grantDigest',
|
|
217
|
+
];
|
|
218
|
+
const scoped = ['1.1.0', '1.2.0'].includes(grant.schemaVersion);
|
|
219
|
+
const keys = scoped ? [...baseKeys, 'phaseId', 'taskId'] : baseKeys;
|
|
220
|
+
if (grant.schemaVersion === '1.2.0') keys.push('g1ApprovalDigest');
|
|
221
|
+
exactKeys(grant, keys, 'GRANT_INVALID', 'grant persistida');
|
|
222
|
+
const unsigned = { ...grant };
|
|
223
|
+
delete unsigned.grantDigest;
|
|
224
|
+
const digest = crypto.createHash('sha256')
|
|
225
|
+
.update(canonicalizarIJson(unsigned), 'utf8').digest('hex');
|
|
226
|
+
if (grant.kind !== 'authorization-grant'
|
|
227
|
+
|| !['1.0.0', '1.1.0', '1.2.0'].includes(grant.schemaVersion)
|
|
228
|
+
|| grant.status !== 'issued' || !IDENTIFIER.test(grant.grantId)
|
|
229
|
+
|| !SHA256.test(grant.grantDigest) || grant.grantDigest !== digest) {
|
|
230
|
+
fail('GRANT_INVALID', 'grant persistida inválida');
|
|
231
|
+
}
|
|
232
|
+
if (scoped && (!IDENTIFIER.test(grant.phaseId) || !TASK.test(grant.taskId))) {
|
|
233
|
+
fail('GRANT_INVALID', 'alcance de gate persistido inválido');
|
|
234
|
+
}
|
|
235
|
+
if (grant.schemaVersion === '1.2.0' && !SHA256.test(grant.g1ApprovalDigest)) {
|
|
236
|
+
fail('GRANT_INVALID', 'época G1 persistida inválida');
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function validateGrantBinding(grant, principal, operation, authority) {
|
|
241
|
+
assertPrincipalCurrent(principal, authority);
|
|
242
|
+
const expected = {
|
|
243
|
+
principalDigest: principal.principalDigest, operationDigest: operation.operationDigest,
|
|
244
|
+
capability: operation.capability, effectKind: operation.effectKind,
|
|
245
|
+
scopeDigest: operation.scope.digest, trustEpoch: authority.trustEpoch,
|
|
246
|
+
trustDigest: authority.trustDigest, rootKeyDigest: authority.rootKeyDigest,
|
|
247
|
+
};
|
|
248
|
+
if (Object.entries(expected).some(([key, value]) => grant[key] !== value)) {
|
|
249
|
+
fail('GRANT_BINDING_INVALID', 'grant prestada o stale');
|
|
250
|
+
}
|
|
251
|
+
validarTimestampIso(grant.expiresAt, 'grant expiresAt');
|
|
252
|
+
if (grant.expiresAt < authority.observedAt || principal.expiresAt < authority.observedAt) {
|
|
253
|
+
fail('GRANT_EXPIRED', 'grant o principal expirado');
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function findGrant(state, grantId) {
|
|
258
|
+
const matches = committedEvents(state).filter(({ payload }) => (
|
|
259
|
+
payload.grant?.grantId === grantId
|
|
260
|
+
));
|
|
261
|
+
if (matches.length !== 1) fail('GRANT_NOT_FOUND', 'grant ausente o duplicada');
|
|
262
|
+
const grant = matches[0].payload.grant;
|
|
263
|
+
validateStoredGrant(grant);
|
|
264
|
+
return grant;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function validateLoadOptions(options) {
|
|
268
|
+
plainObject(options, 'SINGLE_USE_OPTIONS_INVALID', 'opciones load');
|
|
269
|
+
exactKeys(options, ['grantId', 'principal', 'operation'],
|
|
270
|
+
'SINGLE_USE_OPTIONS_INVALID', 'opciones load');
|
|
271
|
+
if (!IDENTIFIER.test(options.grantId)) fail('GRANT_INVALID', 'grantId inválido');
|
|
272
|
+
validatePrincipalOperation(options.principal, options.operation);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function loadGrant(writer, owner, options) {
|
|
276
|
+
validateLoadOptions(options);
|
|
277
|
+
return writer.inspect((state, authority) => {
|
|
278
|
+
const stored = findGrant(state, options.grantId);
|
|
279
|
+
validateGrantBinding(stored, options.principal, options.operation, authority);
|
|
280
|
+
return createGrant(stored, owner);
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function validateClaimOptions(options, owner) {
|
|
285
|
+
plainObject(options, 'SINGLE_USE_OPTIONS_INVALID', 'opciones claim');
|
|
286
|
+
exactKeys(options, ['grant', 'principal', 'operation'],
|
|
287
|
+
'SINGLE_USE_OPTIONS_INVALID', 'opciones claim');
|
|
288
|
+
if (GRANT_OWNERS.get(options.grant) !== owner) {
|
|
289
|
+
fail('GRANT_INVALID', 'grant no verificada por esta store');
|
|
290
|
+
}
|
|
291
|
+
validatePrincipalOperation(options.principal, options.operation);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function claimGrant(writer, owner, options) {
|
|
295
|
+
validateClaimOptions(options, owner);
|
|
296
|
+
let receipt;
|
|
297
|
+
const appendReceipt = writer.transact((state, authority) => {
|
|
298
|
+
const stored = findGrant(state, options.grant.grantId);
|
|
299
|
+
validateGrantBinding(stored, options.principal, options.operation, authority);
|
|
300
|
+
if (stored.grantDigest !== options.grant.grantDigest) {
|
|
301
|
+
fail('GRANT_BINDING_INVALID', 'grant no coincide con autoridad durable');
|
|
302
|
+
}
|
|
303
|
+
if (claimedEvents(state).some(({ payload }) => payload.grantDigest === stored.grantDigest)) {
|
|
304
|
+
fail('GRANT_ALREADY_CLAIMED', 'grant ya reclamada');
|
|
305
|
+
}
|
|
306
|
+
const payload = {
|
|
307
|
+
schemaVersion: stored.schemaVersion, grantId: stored.grantId,
|
|
308
|
+
grantDigest: stored.grantDigest, principalDigest: stored.principalDigest,
|
|
309
|
+
operationDigest: stored.operationDigest, claimedAt: authority.observedAt,
|
|
310
|
+
};
|
|
311
|
+
if (stored.schemaVersion !== '1.0.0') {
|
|
312
|
+
payload.planDigest = stored.planDigest;
|
|
313
|
+
payload.phaseId = stored.phaseId;
|
|
314
|
+
payload.taskId = stored.taskId;
|
|
315
|
+
}
|
|
316
|
+
if (stored.schemaVersion === '1.2.0') {
|
|
317
|
+
payload.g1ApprovalDigest = stored.g1ApprovalDigest;
|
|
318
|
+
}
|
|
319
|
+
return {
|
|
320
|
+
expectedSequence: state.sequence, expectedHeadHash: state.headHash,
|
|
321
|
+
eventType: 'grant.claimed', payload,
|
|
322
|
+
};
|
|
323
|
+
});
|
|
324
|
+
receipt = freezeDeep({
|
|
325
|
+
kind: 'grant-claim-receipt', grantId: options.grant.grantId,
|
|
326
|
+
grantDigest: options.grant.grantDigest,
|
|
327
|
+
planDigest: options.grant.planDigest,
|
|
328
|
+
g1ApprovalDigest: options.grant.g1ApprovalDigest || null,
|
|
329
|
+
phaseId: options.grant.phaseId || null, taskId: options.grant.taskId || null,
|
|
330
|
+
sequence: appendReceipt.sequence, headHash: appendReceipt.headHash,
|
|
331
|
+
});
|
|
332
|
+
CLAIM_RECEIPT_OWNERS.set(receipt, owner);
|
|
333
|
+
return receipt;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function createSingleUseStore(writer) {
|
|
337
|
+
const owner = Object.freeze({});
|
|
338
|
+
const publicStore = Object.freeze({
|
|
339
|
+
claimGrant: (options) => claimGrant(writer, owner, options),
|
|
340
|
+
isAuthorizationGrant: (value) => GRANT_OWNERS.get(value) === owner,
|
|
341
|
+
isGrantClaimReceipt: (value) => CLAIM_RECEIPT_OWNERS.get(value) === owner,
|
|
342
|
+
loadGrant: (options) => loadGrant(writer, owner, options),
|
|
343
|
+
});
|
|
344
|
+
const authorizationStore = Object.freeze({
|
|
345
|
+
redeemApproval: (options) => redeemApproval(writer, owner, options),
|
|
346
|
+
});
|
|
347
|
+
const authorizationGrantIssuer = Object.freeze({
|
|
348
|
+
isVerifiedApproval,
|
|
349
|
+
issueApprovalGrant: (state, authority, options) => (
|
|
350
|
+
issueApprovalGrant(state, authority, owner, options)
|
|
351
|
+
),
|
|
352
|
+
issuePolicyGrant: (state, authority, options) => (
|
|
353
|
+
issuePolicyGrant(state, authority, owner, options)
|
|
354
|
+
),
|
|
355
|
+
});
|
|
356
|
+
return Object.freeze({ authorizationGrantIssuer, authorizationStore, publicStore });
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
module.exports = { createSingleUseStore };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/** Catálogo cerrado de referencias opt-in que no viven en habilidades/. */
|
|
4
|
+
const SKILLS_EXTERNAS = Object.freeze([
|
|
5
|
+
'pdf',
|
|
6
|
+
'docx',
|
|
7
|
+
'xlsx',
|
|
8
|
+
'pptx',
|
|
9
|
+
'frontend-design',
|
|
10
|
+
'web-artifacts-builder',
|
|
11
|
+
'canvas-design',
|
|
12
|
+
'algorithmic-art',
|
|
13
|
+
'theme-factory',
|
|
14
|
+
'slack-gif-creator',
|
|
15
|
+
'claude-api',
|
|
16
|
+
'mcp-builder',
|
|
17
|
+
'webapp-testing',
|
|
18
|
+
'doc-coauthoring',
|
|
19
|
+
'internal-comms',
|
|
20
|
+
'brand-guidelines',
|
|
21
|
+
'skill-creator',
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
const SKILLS_EXTERNAS_SET = new Set(SKILLS_EXTERNAS);
|
|
25
|
+
const CENTINELA_TODOS = 'todos';
|
|
26
|
+
|
|
27
|
+
module.exports = {
|
|
28
|
+
CENTINELA_TODOS,
|
|
29
|
+
SKILLS_EXTERNAS,
|
|
30
|
+
SKILLS_EXTERNAS_SET,
|
|
31
|
+
};
|
|
@@ -24,6 +24,8 @@ const path = require('path');
|
|
|
24
24
|
const TransformadorBase = require('./base');
|
|
25
25
|
const { upsertMcpServer } = require('../toml-merge');
|
|
26
26
|
|
|
27
|
+
const CONTROLES_TOML = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/u;
|
|
28
|
+
|
|
27
29
|
class TransformadorCodex extends TransformadorBase {
|
|
28
30
|
/**
|
|
29
31
|
* Sub-fase 11 v1.5.0: Codex CLI SÍ acepta agentes individuales en formato
|
|
@@ -34,7 +36,6 @@ class TransformadorCodex extends TransformadorBase {
|
|
|
34
36
|
* - name (required)
|
|
35
37
|
* - description (required)
|
|
36
38
|
* - developer_instructions (required) — multi-line literal del cuerpo Markdown
|
|
37
|
-
* - model (opcional) — heredado del frontmatter SWL si existe
|
|
38
39
|
* - sandbox_mode (opcional) — mapeado desde permisosEscritura=false → "read-only"
|
|
39
40
|
*
|
|
40
41
|
* El cuerpo del .md SWL completo (después del frontmatter) se inserta como
|
|
@@ -64,7 +65,6 @@ class TransformadorCodex extends TransformadorBase {
|
|
|
64
65
|
name,
|
|
65
66
|
description,
|
|
66
67
|
developer_instructions: developerInstructions,
|
|
67
|
-
model: frontmatter.model || null,
|
|
68
68
|
sandbox_mode: (frontmatter.permisosEscritura === false) ? 'read-only' : null,
|
|
69
69
|
});
|
|
70
70
|
|
|
@@ -88,7 +88,6 @@ class TransformadorCodex extends TransformadorBase {
|
|
|
88
88
|
lineas.push('');
|
|
89
89
|
lineas.push(`name = ${this._tomlBasicString(campos.name)}`);
|
|
90
90
|
lineas.push(`description = ${this._tomlBasicString(campos.description)}`);
|
|
91
|
-
if (campos.model) lineas.push(`model = ${this._tomlBasicString(campos.model)}`);
|
|
92
91
|
if (campos.sandbox_mode) lineas.push(`sandbox_mode = ${this._tomlBasicString(campos.sandbox_mode)}`);
|
|
93
92
|
lineas.push('');
|
|
94
93
|
lineas.push(`developer_instructions = ${this._tomlMultiline(campos.developer_instructions)}`);
|
|
@@ -96,7 +95,9 @@ class TransformadorCodex extends TransformadorBase {
|
|
|
96
95
|
}
|
|
97
96
|
|
|
98
97
|
_tomlBasicString(s) {
|
|
99
|
-
const
|
|
98
|
+
const texto = String(s);
|
|
99
|
+
if (CONTROLES_TOML.test(texto)) throw new TypeError('Control TOML inválido');
|
|
100
|
+
const escapado = texto
|
|
100
101
|
.replace(/\\/g, '\\\\')
|
|
101
102
|
.replace(/"/g, '\\"')
|
|
102
103
|
.replace(/\n/g, '\\n')
|
|
@@ -107,6 +108,7 @@ class TransformadorCodex extends TransformadorBase {
|
|
|
107
108
|
|
|
108
109
|
_tomlMultiline(s) {
|
|
109
110
|
const contenido = String(s);
|
|
111
|
+
if (CONTROLES_TOML.test(contenido)) throw new TypeError('Control TOML inválido');
|
|
110
112
|
if (!contenido.includes("'''")) {
|
|
111
113
|
// Literal multi-line: preserva contenido sin escapes.
|
|
112
114
|
return `'''\n${contenido}\n'''`;
|
|
@@ -122,11 +124,13 @@ class TransformadorCodex extends TransformadorBase {
|
|
|
122
124
|
// `.agents/skills/` como ubicaciones válidas — NO `~/.codex/skills/`.
|
|
123
125
|
// El path `~/.codex/skills/` que apareció en Sub-fase 8 era de una sample
|
|
124
126
|
// sub-doc del repo (no canónica). Ahora apunta al path correcto vía dirBase.
|
|
125
|
-
const
|
|
127
|
+
const dirBase = metadatos.esGlobal
|
|
128
|
+
? path.join(require('os').homedir(), '.agents')
|
|
129
|
+
: '.agents';
|
|
126
130
|
return {
|
|
127
131
|
tipo: 'directorio',
|
|
128
132
|
rutaRelativa: `skills/${metadatos.nombreDirectorio}`,
|
|
129
|
-
dirBase
|
|
133
|
+
dirBase,
|
|
130
134
|
};
|
|
131
135
|
}
|
|
132
136
|
|
|
@@ -178,9 +182,12 @@ class TransformadorCodex extends TransformadorBase {
|
|
|
178
182
|
|
|
179
183
|
for (const [hookFile, def] of Object.entries(hooksConfig)) {
|
|
180
184
|
if (hookFilesFiltro && !hookFilesFiltro.has(hookFile)) continue;
|
|
181
|
-
if (!def
|
|
185
|
+
if (!def) continue;
|
|
186
|
+
const events = Array.isArray(def.events)
|
|
187
|
+
? def.events
|
|
188
|
+
: (typeof def.event === 'string' ? [def.event] : []);
|
|
182
189
|
|
|
183
|
-
for (const event of
|
|
190
|
+
for (const event of events) {
|
|
184
191
|
if (!EVENTOS_VALIDOS.has(event)) {
|
|
185
192
|
descartados.push({ hookFile, event, motivo: 'evento no soportado por Codex' });
|
|
186
193
|
continue;
|
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
const TransformadorBase = require('./base');
|
|
4
4
|
|
|
5
|
+
const EVENTOS_GEMINI = Object.freeze({
|
|
6
|
+
PreToolUse: 'BeforeTool',
|
|
7
|
+
PostToolUse: 'AfterTool',
|
|
8
|
+
SessionStart: 'SessionStart',
|
|
9
|
+
});
|
|
10
|
+
const CONTROLES_TOML = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/u;
|
|
11
|
+
|
|
5
12
|
class TransformadorGemini extends TransformadorBase {
|
|
6
13
|
transformarAgente(contenidoMd, metadatos) {
|
|
7
14
|
// Gemini soporta frontmatter similar a Claude
|
|
@@ -12,7 +19,6 @@ class TransformadorGemini extends TransformadorBase {
|
|
|
12
19
|
if (frontmatter.name) fmGemini.name = frontmatter.name;
|
|
13
20
|
if (frontmatter.description) fmGemini.description = frontmatter.description;
|
|
14
21
|
if (frontmatter.tools) fmGemini.tools = frontmatter.tools;
|
|
15
|
-
if (frontmatter.model) fmGemini.model = frontmatter.model;
|
|
16
22
|
|
|
17
23
|
return {
|
|
18
24
|
contenido: this.construirMarkdown(fmGemini, cuerpo),
|
|
@@ -35,12 +41,36 @@ class TransformadorGemini extends TransformadorBase {
|
|
|
35
41
|
}
|
|
36
42
|
|
|
37
43
|
transformarComando(contenidoMd, metadatos) {
|
|
44
|
+
const { frontmatter, cuerpo } = this.parsearFrontmatter(contenidoMd);
|
|
45
|
+
const nombreArchivo = metadatos.nombreArchivo.replace(/\.md$/iu, '.toml');
|
|
46
|
+
const descripcion = this._tomlString(frontmatter.description || '');
|
|
47
|
+
const prompt = this._tomlMultiline(String(cuerpo || '').trim());
|
|
38
48
|
return {
|
|
39
|
-
contenido:
|
|
40
|
-
rutaRelativa: `commands/swl/${
|
|
49
|
+
contenido: `description = ${descripcion}\nprompt = ${prompt}\n`,
|
|
50
|
+
rutaRelativa: `commands/swl/${nombreArchivo}`,
|
|
41
51
|
};
|
|
42
52
|
}
|
|
43
53
|
|
|
54
|
+
_tomlString(valor) {
|
|
55
|
+
const texto = String(valor);
|
|
56
|
+
if (CONTROLES_TOML.test(texto)) throw new TypeError('Control TOML inválido');
|
|
57
|
+
const escapado = texto
|
|
58
|
+
.replace(/\\/gu, '\\\\')
|
|
59
|
+
.replace(/"/gu, '\\"')
|
|
60
|
+
.replace(/\n/gu, '\\n')
|
|
61
|
+
.replace(/\r/gu, '\\r')
|
|
62
|
+
.replace(/\t/gu, '\\t');
|
|
63
|
+
return `"${escapado}"`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
_tomlMultiline(valor) {
|
|
67
|
+
const texto = String(valor);
|
|
68
|
+
if (CONTROLES_TOML.test(texto)) throw new TypeError('Control TOML inválido');
|
|
69
|
+
const escapado = texto.replace(/\r\n/gu, '\n')
|
|
70
|
+
.replace(/\\/gu, '\\\\').replace(/"""/gu, '\\"""');
|
|
71
|
+
return `"""\n${escapado}\n"""`;
|
|
72
|
+
}
|
|
73
|
+
|
|
44
74
|
generarArchivoInstrucciones(contexto) {
|
|
45
75
|
const lineas = [];
|
|
46
76
|
lineas.push('# GEMINI.md — Sistema SWL');
|
|
@@ -86,8 +116,52 @@ class TransformadorGemini extends TransformadorBase {
|
|
|
86
116
|
}
|
|
87
117
|
|
|
88
118
|
transformarHooks(hooksConfig, opciones) {
|
|
89
|
-
|
|
90
|
-
|
|
119
|
+
if (!hooksConfig || typeof hooksConfig !== 'object') return null;
|
|
120
|
+
const opts = opciones || {};
|
|
121
|
+
const filtro = Array.isArray(opts.hookFiles) ? new Set(opts.hookFiles) : null;
|
|
122
|
+
const rutaHooksDir = opts.rutaHooksDir || 'hooks';
|
|
123
|
+
const grupos = {};
|
|
124
|
+
const descartados = [];
|
|
125
|
+
|
|
126
|
+
for (const [hookFile, definicion] of Object.entries(hooksConfig)) {
|
|
127
|
+
const eventos = this._eventosHook(definicion);
|
|
128
|
+
if ((filtro && !filtro.has(hookFile)) || eventos.length === 0) continue;
|
|
129
|
+
for (const evento of eventos) {
|
|
130
|
+
const eventoGemini = EVENTOS_GEMINI[evento];
|
|
131
|
+
if (!eventoGemini) {
|
|
132
|
+
descartados.push({ hookFile, event: evento, motivo: 'evento no soportado por Gemini' });
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (!grupos[eventoGemini]) grupos[eventoGemini] = [];
|
|
136
|
+
const entrada = this._crearHookGemini(hookFile, definicion, rutaHooksDir);
|
|
137
|
+
grupos[eventoGemini].push(entrada);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
formato: 'gemini-hooks-json',
|
|
143
|
+
destino: 'hooks/hooks.json',
|
|
144
|
+
config: { hooks: grupos },
|
|
145
|
+
descartados,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
_eventosHook(definicion) {
|
|
150
|
+
if (Array.isArray(definicion?.events)) return definicion.events;
|
|
151
|
+
return typeof definicion?.event === 'string' ? [definicion.event] : [];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
_crearHookGemini(hookFile, definicion, rutaHooksDir) {
|
|
155
|
+
const hook = {
|
|
156
|
+
name: hookFile.replace(/\.js$/iu, ''),
|
|
157
|
+
type: 'command',
|
|
158
|
+
command: `node ${rutaHooksDir}/${hookFile}`,
|
|
159
|
+
...(definicion.timeout ? { timeout: definicion.timeout } : {}),
|
|
160
|
+
};
|
|
161
|
+
return {
|
|
162
|
+
...(definicion.matcher ? { matcher: definicion.matcher } : {}),
|
|
163
|
+
hooks: [hook],
|
|
164
|
+
};
|
|
91
165
|
}
|
|
92
166
|
}
|
|
93
167
|
|