@saulwade/swl-ses 1.6.0 → 1.6.3

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.
Files changed (82) hide show
  1. package/CLAUDE.md +32 -61
  2. package/README.md +4 -4
  3. package/agentes/_intent-spec.md +73 -0
  4. package/agentes/auto-evolucion-swl.md +24 -0
  5. package/agentes/cloud-infra-swl.md +25 -0
  6. package/agentes/datos-swl.md +24 -1
  7. package/agentes/devops-ci-swl.md +24 -0
  8. package/agentes/frontend-angular-swl.md +7 -7
  9. package/agentes/frontend-css-swl.md +4 -4
  10. package/agentes/frontend-react-swl.md +7 -7
  11. package/agentes/frontend-swl.md +9 -9
  12. package/agentes/frontend-tailwind-swl.md +4 -4
  13. package/agentes/migrador-swl.md +22 -0
  14. package/agentes/pagos-swl.md +25 -0
  15. package/agentes/release-manager-swl.md +24 -0
  16. package/agentes/rendimiento-swl.md +2 -2
  17. package/agentes/sre-swl.md +24 -0
  18. package/comandos/swl/brainstorm.md +1 -0
  19. package/comandos/swl/compactar.md +1 -1
  20. package/comandos/swl/discutir-fase.md +15 -1
  21. package/comandos/swl/mapear-codebase.md +1 -1
  22. package/comandos/swl/nemesis.md +29 -0
  23. package/comandos/swl/planear-fase.md +18 -2
  24. package/comandos/swl/verificar.md +4 -4
  25. package/habilidades/aprender-de-git-diff/SKILL.md +288 -0
  26. package/habilidades/aprendizaje-continuo/SKILL.md +7 -1
  27. package/habilidades/diseno-herramientas-agente/SKILL.md +17 -0
  28. package/habilidades/doc-sync/SKILL.md +441 -1
  29. package/habilidades/doubt-driven-review/SKILL.md +177 -171
  30. package/habilidades/feynman-auditor-swl/SKILL.md +129 -123
  31. package/habilidades/infra-github-actions/SKILL.md +172 -166
  32. package/habilidades/meta-skills-estandar/SKILL.md +6 -0
  33. package/habilidades/meta-skills-estandar/recursos/skill-judge-rubrica.md +281 -0
  34. package/habilidades/meta-skills-estandar/recursos/skills-as-agents.md +163 -163
  35. package/habilidades/nemesis-evaluacion-json/SKILL.md +5 -0
  36. package/habilidades/nemesis-redistribuir/SKILL.md +5 -0
  37. package/habilidades/node-experto/SKILL.md +197 -3
  38. package/habilidades/prevencion-racionalizacion/SKILL.md +1 -0
  39. package/habilidades/privacy-memoria/SKILL.md +1 -0
  40. package/habilidades/proceso-autoverificacion-evidencias/SKILL.md +258 -0
  41. package/habilidades/proceso-confianza-pre-implementacion/SKILL.md +246 -0
  42. package/habilidades/proceso-ddia-fundamentos/SKILL.md +255 -0
  43. package/habilidades/proceso-ddia-streaming/SKILL.md +231 -0
  44. package/habilidades/proceso-intent-engineering/SKILL.md +269 -0
  45. package/habilidades/reducir-entropia/SKILL.md +219 -0
  46. package/habilidades/sre-patrones/SKILL.md +1 -1
  47. package/habilidades/state-inconsistency-auditor-swl/SKILL.md +172 -166
  48. package/habilidades/tdd-workflow/SKILL.md +178 -3
  49. package/habilidades/verificacion-evidencia/SKILL.md +1 -0
  50. package/habilidades/web-fetcher-routing/SKILL.md +81 -75
  51. package/habilidades/workflow-claude-code/SKILL.md +2 -2
  52. package/hooks/lib/task-budget.js +218 -0
  53. package/hooks/validar-intent-spec.js +222 -0
  54. package/manifiestos/hooks-config.json +9 -0
  55. package/manifiestos/modulos.json +12 -2
  56. package/manifiestos/skills-lock.json +1191 -1142
  57. package/package.json +5 -3
  58. package/plugin.json +9 -2
  59. package/reglas/auditorias-documentales-estructurales.md +205 -0
  60. package/reglas/fragmentos-compartidos.md +26 -0
  61. package/reglas/intent-engineering.md +214 -0
  62. package/reglas/registro-componentes-nuevos.md +38 -0
  63. package/schemas/agent-frontmatter.schema.json +294 -167
  64. package/schemas/agent-message.schema.json +73 -53
  65. package/schemas/agent-output-implementacion.schema.json +114 -85
  66. package/schemas/agent-output-planificacion.schema.json +150 -113
  67. package/schemas/agent-output-review.schema.json +98 -78
  68. package/schemas/diary-entry.schema.json +42 -10
  69. package/schemas/hook-profiles.schema.json +54 -39
  70. package/schemas/hooks-config.schema.json +89 -74
  71. package/schemas/instinct.schema.json +152 -115
  72. package/schemas/modulos.schema.json +38 -29
  73. package/schemas/perfiles.schema.json +36 -28
  74. package/schemas/plugin.schema.json +77 -64
  75. package/schemas/skill-evals.schema.json +119 -95
  76. package/schemas/skill-frontmatter.schema.json +245 -170
  77. package/scripts/generar-inventario.js +452 -420
  78. package/scripts/lib/schema-version.js +164 -0
  79. package/scripts/validar-manifest.js +1 -1
  80. package/scripts/validar.js +3 -2
  81. package/scripts/verificar-docs-vs-codigo.js +654 -425
  82. package/scripts/verificar-evolucion.js +19 -3
@@ -1,75 +1,81 @@
1
- ---
2
- name: web-fetcher-routing
3
- description: >
4
- Routing inteligente para fetching de URLs según dominio y formato:
5
- GitHub raw, PDF, sitios JS-heavy y default. Reduce tokens consumidos
6
- evitando WebFetch cuando hay alternativa más eficiente. Cargar antes
7
- de hacer WebFetch a URLs externas, especialmente repositorios GitHub,
8
- PDFs públicos, o sitios con paywall/Cloudflare/JS heavy.
9
- ---
10
-
11
- # web-fetcher-routing
12
-
13
- Selecciona la herramienta correcta para cada URL antes de intentar el fetch.
14
- Un WebFetch al HTML de GitHub UI consume 10-30× más tokens que leer el raw.
15
- Un PDF vía Read devuelve basura binaria. La elección equivocada cuesta tokens
16
- reales; este skill la automatiza.
17
-
18
- ## Cuándo cargar
19
-
20
- - Antes de hacer `WebFetch` a cualquier URL externa no trivial.
21
- - Cuando la URL apunta a GitHub (repositorio, archivo o blob).
22
- - Cuando la URL termina en `.pdf` o el contexto indica documento PDF.
23
- - Cuando el fetch previo falló con 402, página de login o contenido vacío
24
- (señal de JS-heavy o Cloudflare).
25
-
26
- ## Cuándo NO cargar
27
-
28
- - URLs internas del proyecto o localhost usar `Read` directamente.
29
- - Contenido ya cargado en el contexto de esta sesión no re-fetchar.
30
- - Cuando el usuario dictó explícitamente la herramienta a usar
31
- ("usa WebFetch", "lee con curl").
32
-
33
- ## Tabla de routing
34
-
35
- | Patrón de URL | Herramienta | Razón |
36
- |---|---|---|
37
- | `github.com/[user]/[repo]/blob/[branch]/[path]` | Reescribir a `raw.githubusercontent.com/[user]/[repo]/[branch]/[path]` y hacer `WebFetch` con la URL reescrita | Evita el HTML de la UI de GitHub; 10-30× menos tokens |
38
- | `raw.githubusercontent.com/...` | `WebFetch` directo | Ya es raw content |
39
- | URL termina en `.pdf` | Invocar `Skill("swl-markitdown")` para conversión con `pdftotext` o `markitdown` | `Read` no soporta PDF; `WebFetch` devuelve binario o HTML de visor |
40
- | `x.com`, `twitter.com`, cualquier host con CAPTCHA o Cloudflare detectado | Invocar `Skill("agent-browser")` con headless Chrome | `WebFetch` devuelve 402 o página de login; `agent-browser` usa accessibility tree (~82% menos tokens que screenshots) |
41
- | `mp.weixin.qq.com`, `feishu.cn`, `larksuite.com` | Invocar `Skill("agent-browser")` | Plataformas chinas con autenticación o JS requerido |
42
- | Todo lo demás | `WebFetch` directo | El caso común; probar primero antes de escalar |
43
-
44
- ## Algoritmo de decisión
45
-
46
- 1. Parsear la URL: extraer esquema, dominio y extensión del path.
47
- 2. Comparar con los patrones de la tabla, en orden de arriba hacia abajo.
48
- 3. Si el dominio es `github.com` con segmento `/blob/` en el path:
49
- reescribir la URL antes de hacer el fetch.
50
- 4. Invocar la herramienta asignada al patrón que coincide.
51
- 5. Si el resultado tiene señales de paywall o error (primeras 10 líneas
52
- contienen "Subscribe", "Sign in", "403", página vacía): escalar al
53
- patrón JS-heavy con `Skill("agent-browser")`.
54
- 6. Reportar qué método se usó y por qué, en una línea antes del contenido.
55
-
56
- ## Ejemplo de reescritura GitHub
57
-
58
- ```
59
- # URL original
60
- https://github.com/tw93/Waza/blob/main/skills/read/SKILL.md
61
-
62
- # URL reescrita para raw
63
- https://raw.githubusercontent.com/tw93/Waza/main/skills/read/SKILL.md
64
- ```
65
-
66
- ## Señales de fallo que activan escalado
67
-
68
- | Señal en la respuesta | Acción |
69
- |---|---|
70
- | HTTP 402 | Escalar a `agent-browser` |
71
- | Primeras líneas con "Sign in" o "Subscribe" | Detener, avisar al usuario |
72
- | Contenido HTML con menos de 200 caracteres | Reintentar con método alternativo |
73
- | Binario o caracteres ilegibles | Verificar si es PDF; si sí, usar `swl-markitdown` |
74
-
75
- <!-- Adaptado de Waza/skills/read bajo MIT License (tw93/Waza) -->
1
+ ---
2
+ name: web-fetcher-routing
3
+ description: >
4
+ Routing inteligente para fetching de URLs según dominio y formato:
5
+ GitHub raw, PDF, sitios JS-heavy y default. Reduce tokens consumidos
6
+ evitando WebFetch cuando hay alternativa más eficiente. Cargar antes
7
+ de hacer WebFetch a URLs externas, especialmente repositorios GitHub,
8
+ PDFs públicos, o sitios con paywall/Cloudflare/JS heavy.
9
+ version: "1.0.0"
10
+ exclusiones:
11
+ - "No cargar para descarga de archivos binarios grandes (>10 MB) — el routing está optimizado para texto y HTML."
12
+ - "No cargar cuando la URL ya es directa al recurso (raw GitHub, PDF, JSON API) y no hay duda de cómo procesarla."
13
+ - "No cargar para web scraping con interacción (login, formularios) — eso es agent-browser."
14
+ - "No cargar para fetching local de filesystem (file://) usar Read tool."
15
+ ---
16
+
17
+ # web-fetcher-routing
18
+
19
+ Selecciona la herramienta correcta para cada URL antes de intentar el fetch.
20
+ Un WebFetch al HTML de GitHub UI consume 10-30× más tokens que leer el raw.
21
+ Un PDF vía Read devuelve basura binaria. La elección equivocada cuesta tokens
22
+ reales; este skill la automatiza.
23
+
24
+ ## Cuándo cargar
25
+
26
+ - Antes de hacer `WebFetch` a cualquier URL externa no trivial.
27
+ - Cuando la URL apunta a GitHub (repositorio, archivo o blob).
28
+ - Cuando la URL termina en `.pdf` o el contexto indica documento PDF.
29
+ - Cuando el fetch previo falló con 402, página de login o contenido vacío
30
+ (señal de JS-heavy o Cloudflare).
31
+
32
+ ## Cuándo NO cargar
33
+
34
+ - URLs internas del proyecto o localhost — usar `Read` directamente.
35
+ - Contenido ya cargado en el contexto de esta sesión no re-fetchar.
36
+ - Cuando el usuario dictó explícitamente la herramienta a usar
37
+ ("usa WebFetch", "lee con curl").
38
+
39
+ ## Tabla de routing
40
+
41
+ | Patrón de URL | Herramienta | Razón |
42
+ |---|---|---|
43
+ | `github.com/[user]/[repo]/blob/[branch]/[path]` | Reescribir a `raw.githubusercontent.com/[user]/[repo]/[branch]/[path]` y hacer `WebFetch` con la URL reescrita | Evita el HTML de la UI de GitHub; 10-30× menos tokens |
44
+ | `raw.githubusercontent.com/...` | `WebFetch` directo | Ya es raw content |
45
+ | URL termina en `.pdf` | Invocar `Skill("swl-markitdown")` para conversión con `pdftotext` o `markitdown` | `Read` no soporta PDF; `WebFetch` devuelve binario o HTML de visor |
46
+ | `x.com`, `twitter.com`, cualquier host con CAPTCHA o Cloudflare detectado | Invocar `Skill("agent-browser")` con headless Chrome | `WebFetch` devuelve 402 o página de login; `agent-browser` usa accessibility tree (~82% menos tokens que screenshots) |
47
+ | `mp.weixin.qq.com`, `feishu.cn`, `larksuite.com` | Invocar `Skill("agent-browser")` | Plataformas chinas con autenticación o JS requerido |
48
+ | Todo lo demás | `WebFetch` directo | El caso común; probar primero antes de escalar |
49
+
50
+ ## Algoritmo de decisión
51
+
52
+ 1. Parsear la URL: extraer esquema, dominio y extensión del path.
53
+ 2. Comparar con los patrones de la tabla, en orden de arriba hacia abajo.
54
+ 3. Si el dominio es `github.com` con segmento `/blob/` en el path:
55
+ reescribir la URL antes de hacer el fetch.
56
+ 4. Invocar la herramienta asignada al patrón que coincide.
57
+ 5. Si el resultado tiene señales de paywall o error (primeras 10 líneas
58
+ contienen "Subscribe", "Sign in", "403", página vacía): escalar al
59
+ patrón JS-heavy con `Skill("agent-browser")`.
60
+ 6. Reportar qué método se usó y por qué, en una línea antes del contenido.
61
+
62
+ ## Ejemplo de reescritura GitHub
63
+
64
+ ```
65
+ # URL original
66
+ https://github.com/tw93/Waza/blob/main/skills/read/SKILL.md
67
+
68
+ # URL reescrita para raw
69
+ https://raw.githubusercontent.com/tw93/Waza/main/skills/read/SKILL.md
70
+ ```
71
+
72
+ ## Señales de fallo que activan escalado
73
+
74
+ | Señal en la respuesta | Acción |
75
+ |---|---|
76
+ | HTTP 402 | Escalar a `agent-browser` |
77
+ | Primeras líneas con "Sign in" o "Subscribe" | Detener, avisar al usuario |
78
+ | Contenido HTML con menos de 200 caracteres | Reintentar con método alternativo |
79
+ | Binario o caracteres ilegibles | Verificar si es PDF; si sí, usar `swl-markitdown` |
80
+
81
+ <!-- Adaptado de Waza/skills/read bajo MIT License (tw93/Waza) -->
@@ -14,7 +14,7 @@ exclusiones:
14
14
  - "No cargar para configurar la estructura de directorios de un proyecto Claude-ready; usar `estructura-proyecto-claude`."
15
15
  - "No cargar para resolver problemas de degradación de contexto en mitad de una sesión larga; en ese caso ejecutar `/swl:compactar` directamente."
16
16
  - "No cargar para consultar documentación de librerías o frameworks externos; usar Context7 MCP."
17
- - "No cargar para ajustar permisos de herramientas en settings.json; usar `Skill('update-config')`."
17
+ - "No cargar para ajustar permisos de herramientas en settings.json; usar editar manualmente `.claude/settings.json` o `~/.claude/settings.json`."
18
18
  evolvable: true # default para skill estandar
19
19
  ---
20
20
  # Workflow con Claude Code
@@ -37,7 +37,7 @@ Para ESTRUCTURA de proyecto, usar `estructura-proyecto-claude`.
37
37
  - Se necesita estructura de archivos para un proyecto Claude-ready (CLAUDE.md, .claude/agents/, etc.); usar `estructura-proyecto-claude`.
38
38
  - El contexto ya está al 70%+ y el usuario quiere compactar ahora mismo; ejecutar `/swl:compactar` sin cargar skills adicionales.
39
39
  - El usuario pregunta por una librería específica (ej: "¿cómo uso FastAPI?"); la respuesta está en Context7 MCP, no en este skill de workflow.
40
- - Se busca configurar hooks, permisos o settings.json; usar `Skill("update-config")`.
40
+ - Se busca configurar hooks, permisos o settings.json; editar manualmente `.claude/settings.json` o `~/.claude/settings.json`.
41
41
 
42
42
  ---
43
43
 
@@ -0,0 +1,218 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * task-budget.js — presupuesto SEMÁNTICO de tokens por tarea.
5
+ *
6
+ * Adaptación de SuperClaude_Framework `pm_agent/token_budget.py` (MIT) a Node
7
+ * zero-deps. Provee un contrato de presupuesto por COMPLEJIDAD de tarea que
8
+ * agentes y orquestador pueden consultar para gating, NO un contador exacto
9
+ * de tokens consumidos (el modelo no expone tokens en runtime).
10
+ *
11
+ * IMPORTANTE: no confundir con `hooks/lib/token-budget.js`, que distribuye
12
+ * presupuesto entre BLOQUES DE CONTEXTO (memoria/observaciones/reglas) con
13
+ * scoring de recencia+importancia. Aquel es para inyección de contexto;
14
+ * éste es para clasificación de TAREAS.
15
+ *
16
+ * Niveles de complejidad y presupuesto recomendado:
17
+ * - simple: 200 tokens — typo fix, rename, comentario, formato.
18
+ * - medium: 1000 tokens — bugfix acotado, feature pequeña.
19
+ * - complex: 2500 tokens — feature grande, refactor cross-módulo.
20
+ *
21
+ * Uso típico:
22
+ * const { TaskBudget } = require('./task-budget');
23
+ * const budget = new TaskBudget('medium');
24
+ * if (budget.tryUse(150)) {
25
+ * // proceder con la sub-tarea
26
+ * } else {
27
+ * // presupuesto agotado — pausar, reportar, escalar
28
+ * }
29
+ *
30
+ * Semántica de "rebasado": si un agente declara tarea "simple" y consume
31
+ * 800 tokens, hay señal de que la clasificación fue incorrecta o el scope
32
+ * creció. El orquestador puede actuar (re-clasificar, pausar, alertar).
33
+ *
34
+ * @module hooks/lib/task-budget
35
+ */
36
+
37
+ /** @typedef {'simple' | 'medium' | 'complex'} ComplexityLevel */
38
+
39
+ /**
40
+ * Límites por defecto. Defaults recomendados por el material fuente
41
+ * (SuperClaude pm_agent). Modificables vía constructor para escenarios
42
+ * especiales.
43
+ * @type {Readonly<Record<ComplexityLevel, number>>}
44
+ */
45
+ const DEFAULT_LIMITS = Object.freeze({
46
+ simple: 200,
47
+ medium: 1000,
48
+ complex: 2500,
49
+ });
50
+
51
+ /**
52
+ * Gestor de presupuesto semántico por tarea.
53
+ *
54
+ * Inmutable en `limit` y `complexity` tras la construcción; mutable solo
55
+ * en `used` vía `tryUse()` / `forceUse()` / `reset()`.
56
+ */
57
+ class TaskBudget {
58
+ /**
59
+ * @param {ComplexityLevel} [complexity='medium'] - Complejidad declarada.
60
+ * @param {Partial<Record<ComplexityLevel, number>>} [overrides] - Límites custom.
61
+ */
62
+ constructor(complexity = 'medium', overrides = undefined) {
63
+ const limits = overrides
64
+ ? Object.freeze({ ...DEFAULT_LIMITS, ...overrides })
65
+ : DEFAULT_LIMITS;
66
+
67
+ // Default defensivo a 'medium' si llega un nivel desconocido — el
68
+ // material fuente hace lo mismo. Evita excepciones por typo.
69
+ const normalized = Object.prototype.hasOwnProperty.call(limits, complexity)
70
+ ? complexity
71
+ : 'medium';
72
+
73
+ /** @type {ComplexityLevel} */
74
+ this.complexity = /** @type {ComplexityLevel} */ (normalized);
75
+ /** @type {number} */
76
+ this.limit = limits[normalized];
77
+ /** @type {number} */
78
+ this.used = 0;
79
+ }
80
+
81
+ /**
82
+ * Intenta consumir `amount` tokens. Si excede el presupuesto, NO consume
83
+ * y retorna false. Útil cuando el caller debe decidir entre proceder o
84
+ * escalar.
85
+ *
86
+ * @param {number} amount - Cantidad a consumir. Debe ser número finito >= 0.
87
+ * @returns {boolean} true si se consumió, false si excedería el presupuesto.
88
+ * @throws {TypeError} si amount no es número finito >= 0.
89
+ */
90
+ tryUse(amount) {
91
+ if (!Number.isFinite(amount) || amount < 0) {
92
+ throw new TypeError(`TaskBudget.tryUse: amount debe ser número finito >= 0, recibido: ${amount}`);
93
+ }
94
+ if (this.used + amount > this.limit) {
95
+ return false;
96
+ }
97
+ this.used += amount;
98
+ return true;
99
+ }
100
+
101
+ /**
102
+ * Alias semántico de `tryUse` para callers que prefieren `allocate`.
103
+ * Mantiene paridad con el API original (pm_agent.token_budget.allocate).
104
+ *
105
+ * @param {number} amount
106
+ * @returns {boolean}
107
+ */
108
+ allocate(amount) {
109
+ return this.tryUse(amount);
110
+ }
111
+
112
+ /**
113
+ * Consume `amount` tokens FORZOSAMENTE, incluso si excede el presupuesto.
114
+ * Útil cuando el consumo ya ocurrió y se quiere registrar para auditoría.
115
+ * Retorna true si quedó dentro del presupuesto, false si lo rebasó.
116
+ *
117
+ * @param {number} amount
118
+ * @returns {boolean} true si dentro del presupuesto, false si lo rebasó.
119
+ * @throws {TypeError} si amount no es número finito >= 0.
120
+ */
121
+ forceUse(amount) {
122
+ if (!Number.isFinite(amount) || amount < 0) {
123
+ throw new TypeError(`TaskBudget.forceUse: amount debe ser número finito >= 0, recibido: ${amount}`);
124
+ }
125
+ this.used += amount;
126
+ return this.used <= this.limit;
127
+ }
128
+
129
+ /**
130
+ * Tokens disponibles restantes. Puede ser negativo si se usó `forceUse`
131
+ * con consumo que rebasó el límite.
132
+ * @returns {number}
133
+ */
134
+ get remaining() {
135
+ return this.limit - this.used;
136
+ }
137
+
138
+ /**
139
+ * Fracción del presupuesto consumida en [0, ∞). 1.0 = exactamente lleno;
140
+ * >1.0 = rebasado. Útil para alertas "65% consumido".
141
+ * @returns {number}
142
+ */
143
+ get usedFraction() {
144
+ return this.limit === 0 ? 0 : this.used / this.limit;
145
+ }
146
+
147
+ /**
148
+ * true si el presupuesto fue rebasado (used > limit). Señal operacional
149
+ * de mala clasificación de complejidad o scope creep.
150
+ * @returns {boolean}
151
+ */
152
+ get isOver() {
153
+ return this.used > this.limit;
154
+ }
155
+
156
+ /**
157
+ * Resetea el contador de usados. NO cambia el límite ni la complejidad.
158
+ */
159
+ reset() {
160
+ this.used = 0;
161
+ }
162
+
163
+ /**
164
+ * Representación para logs / debugging.
165
+ * @returns {string}
166
+ */
167
+ toString() {
168
+ return `TaskBudget(complexity=${this.complexity}, limit=${this.limit}, used=${this.used})`;
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Sugiere nivel de complejidad a partir de heurística simple sobre el
174
+ * descriptor de la tarea. NO es clasificación inteligente — es atajo para
175
+ * callers que no quieren razonar el nivel manualmente.
176
+ *
177
+ * Heurística:
178
+ * - menciona "typo", "rename", "format", "comment" → simple
179
+ * - menciona "feature", "refactor", "migration", "arquitectura" → complex
180
+ * - resto → medium
181
+ *
182
+ * @param {string} description - Descripción libre de la tarea.
183
+ * @returns {ComplexityLevel}
184
+ */
185
+ function suggestComplexity(description) {
186
+ if (typeof description !== 'string' || description.length === 0) {
187
+ return 'medium';
188
+ }
189
+ const lower = description.toLowerCase();
190
+
191
+ const simpleHints = ['typo', 'rename', 'format', 'comment', 'whitespace', 'comentario', 'formato'];
192
+ if (simpleHints.some((hint) => lower.includes(hint))) {
193
+ return 'simple';
194
+ }
195
+
196
+ const complexHints = [
197
+ 'refactor',
198
+ 'migration',
199
+ 'migración',
200
+ 'feature nueva',
201
+ 'cross-module',
202
+ 'cross-modulo',
203
+ 'arquitectura',
204
+ 'rewrite',
205
+ 'reescritura',
206
+ ];
207
+ if (complexHints.some((hint) => lower.includes(hint))) {
208
+ return 'complex';
209
+ }
210
+
211
+ return 'medium';
212
+ }
213
+
214
+ module.exports = {
215
+ TaskBudget,
216
+ DEFAULT_LIMITS,
217
+ suggestComplexity,
218
+ };
@@ -0,0 +1,222 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Hook: validar-intent-spec.js
6
+ * Tipo: PostToolUse (aplica a: Write, Edit, MultiEdit sobre agentes/*.md)
7
+ *
8
+ * Verifica que los agentes con `nivelRiesgo: ALTO` declaren las 6 partes
9
+ * del framework Intent Engineering exigidas por `reglas/intent-engineering.md`:
10
+ * - strategy (Parte 1)
11
+ * - healthMetrics (Parte 4)
12
+ * - steering (Parte 6 — prompt-level)
13
+ * - hardGuardrails (Parte 6 — orchestration-level)
14
+ * - fragmentos: [_intent-spec]
15
+ * - maxTurnos (Parte 8 — Stop Rules)
16
+ *
17
+ * Comportamiento:
18
+ * - NUNCA bloquea (exit code 0 siempre — blocking:false en hooks-config)
19
+ * - Solo emite nudge cuando faltan campos en agente ALTO — ruido mínimo
20
+ * - Solo se dispara con archivos `agentes/*.md` (no fragmentos `_*.md`)
21
+ * - Respeta exclusiones: temp/, node_modules/, respositorios-git/, _userland/
22
+ *
23
+ * Opt-out: SWL_INTENT_SPEC=0 desactiva completamente el hook.
24
+ *
25
+ * Formato del nudge:
26
+ * {
27
+ * id: string,
28
+ * kind: "intent-spec-incomplete",
29
+ * target: "auto-evolucion-swl",
30
+ * source: "hooks/validar-intent-spec.js",
31
+ * message: "...",
32
+ * data: { archivo, agente, faltan: [...] },
33
+ * ts: ISO,
34
+ * accionado: false
35
+ * }
36
+ *
37
+ * Origen: ADR-0027 (F3 Opción B integración Lead Agents + DDIA).
38
+ * Plan de promoción a `blocking:true`: tras 2 semanas sin falsos positivos
39
+ * documentados, con nudges accionados, considerar bloqueo activo.
40
+ */
41
+
42
+ const fs = require('fs');
43
+ const path = require('path');
44
+ const crypto = require('crypto');
45
+
46
+ // ─── Opt-out global ───────────────────────────────────────────────────────
47
+ if (process.env.SWL_INTENT_SPEC === '0') {
48
+ process.exit(0);
49
+ }
50
+
51
+ let hookInput = '';
52
+ try {
53
+ hookInput = fs.readFileSync(0, 'utf-8');
54
+ } catch (_) {
55
+ process.exit(0);
56
+ }
57
+
58
+ let evento;
59
+ try {
60
+ evento = JSON.parse(hookInput);
61
+ } catch (_) {
62
+ process.exit(0);
63
+ }
64
+
65
+ const toolName = evento?.tool_name;
66
+ const toolInput = evento?.tool_input;
67
+
68
+ if (!toolName || !['Write', 'Edit', 'MultiEdit'].includes(toolName)) {
69
+ process.exit(0);
70
+ }
71
+
72
+ const filePath = toolInput?.file_path;
73
+ if (!filePath) {
74
+ process.exit(0);
75
+ }
76
+
77
+ const pathNormalized = filePath.replace(/\\/g, '/');
78
+
79
+ // Solo agentes/*.md (NO fragmentos _*.md)
80
+ if (!/\/agentes\/[^_][^/]*\.md$/.test(pathNormalized)) {
81
+ process.exit(0);
82
+ }
83
+
84
+ const RUTAS_EXCLUIDAS = [
85
+ '/temp/',
86
+ '/node_modules/',
87
+ '/respositorios-git/',
88
+ '/_userland/',
89
+ '/.planning/',
90
+ ];
91
+ if (RUTAS_EXCLUIDAS.some((excluida) => pathNormalized.includes(excluida))) {
92
+ process.exit(0);
93
+ }
94
+
95
+ if (!fs.existsSync(filePath)) {
96
+ process.exit(0);
97
+ }
98
+
99
+ // ─── Parsear frontmatter YAML ─────────────────────────────────────────────
100
+ let contenido;
101
+ try {
102
+ contenido = fs.readFileSync(filePath, 'utf-8');
103
+ } catch (_) {
104
+ process.exit(0);
105
+ }
106
+
107
+ // Normalizar line endings (CRLF Windows → LF) antes del regex.
108
+ // Sin esto el hook nunca detectaba frontmatter en archivos guardados desde
109
+ // Git for Windows con autocrlf=true (bug reportado 2026-05-18).
110
+ contenido = contenido.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
111
+
112
+ const matchFM = contenido.match(/^---\n([\s\S]+?)\n---/);
113
+ if (!matchFM) {
114
+ // Sin frontmatter — no es un agente conforme. Otros validadores lo detectan.
115
+ process.exit(0);
116
+ }
117
+
118
+ const yamlBlob = matchFM[1];
119
+
120
+ // Parser mínimo de frontmatter (sin dependencias). Solo lee:
121
+ // - nivelRiesgo: VALOR
122
+ // - presencia de claves de primer nivel
123
+ const lineas = yamlBlob.split('\n');
124
+ const claves = new Set();
125
+ let nivelRiesgo = null;
126
+ let fragmentosTieneIntentSpec = false;
127
+ let dentroDeFragmentos = false;
128
+
129
+ // Strip inline YAML comments (' # ...' o '\t# ...') y comillas wrapping.
130
+ // Sin esto, `nivelRiesgo: ALTO # ejemplo` se parsearía como "ALTO # ejemplo"
131
+ // y la comparación con 'ALTO' fallaría silenciosamente (bug latente 2026-05-18).
132
+ function limpiarValorYaml(crudo) {
133
+ let v = crudo;
134
+ // Comentario inline: '#' precedido de whitespace (no '#' dentro de strings comillados,
135
+ // que en este parser mínimo no soportamos pero sí intentamos respetar).
136
+ const m = v.match(/^(.*?)(?:\s+#.*)?$/);
137
+ if (m) v = m[1];
138
+ v = v.trim();
139
+ // Strip comillas wrapping si las hay
140
+ if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) {
141
+ v = v.slice(1, -1);
142
+ }
143
+ return v;
144
+ }
145
+
146
+ for (const linea of lineas) {
147
+ // Detectar clave de primer nivel (sin indent)
148
+ const m = linea.match(/^([a-zA-Z_][a-zA-Z0-9_]*):/);
149
+ if (m) {
150
+ claves.add(m[1]);
151
+ if (m[1] === 'nivelRiesgo') {
152
+ const crudo = linea.split(':').slice(1).join(':').trim();
153
+ nivelRiesgo = limpiarValorYaml(crudo);
154
+ dentroDeFragmentos = false;
155
+ } else if (m[1] === 'fragmentos') {
156
+ dentroDeFragmentos = true;
157
+ // Caso fragmentos: [_intent-spec] inline
158
+ if (linea.includes('_intent-spec')) {
159
+ fragmentosTieneIntentSpec = true;
160
+ }
161
+ } else {
162
+ dentroDeFragmentos = false;
163
+ }
164
+ } else if (dentroDeFragmentos && linea.includes('_intent-spec')) {
165
+ fragmentosTieneIntentSpec = true;
166
+ }
167
+ }
168
+
169
+ // Solo evaluar agentes ALTO
170
+ if (nivelRiesgo !== 'ALTO') {
171
+ process.exit(0);
172
+ }
173
+
174
+ // Verificar las 6 partes obligatorias
175
+ const CAMPOS_OBLIGATORIOS = ['strategy', 'healthMetrics', 'steering', 'hardGuardrails', 'maxTurnos'];
176
+ const faltan = CAMPOS_OBLIGATORIOS.filter((c) => !claves.has(c));
177
+
178
+ if (!fragmentosTieneIntentSpec) {
179
+ faltan.push('fragmentos:[_intent-spec]');
180
+ }
181
+
182
+ if (faltan.length === 0) {
183
+ process.exit(0);
184
+ }
185
+
186
+ // ─── Emitir nudge ─────────────────────────────────────────────────────────
187
+ const CWD = process.cwd();
188
+ const rutaRelativa = path.relative(CWD, filePath).replace(/\\/g, '/');
189
+ const nombreAgente = path.basename(filePath, '.md');
190
+
191
+ const nudge = {
192
+ id: crypto.randomBytes(8).toString('hex'),
193
+ kind: 'intent-spec-incomplete',
194
+ target: 'auto-evolucion-swl',
195
+ source: 'hooks/validar-intent-spec.js',
196
+ message:
197
+ `Agente ALTO riesgo ${nombreAgente} no declara intent spec completo. ` +
198
+ `Faltan: ${faltan.join(', ')}. Cargar Skill("proceso-intent-engineering") + ` +
199
+ `regla intent-engineering.md y completar frontmatter.`,
200
+ data: {
201
+ archivo: rutaRelativa,
202
+ agente: nombreAgente,
203
+ nivelRiesgo: 'ALTO',
204
+ faltan,
205
+ },
206
+ ts: new Date().toISOString(),
207
+ accionado: false,
208
+ };
209
+
210
+ const nudgesDir = path.join(CWD, '.planning', 'evolucion');
211
+ const nudgesFile = path.join(nudgesDir, 'nudges.jsonl');
212
+
213
+ try {
214
+ if (!fs.existsSync(nudgesDir)) {
215
+ fs.mkdirSync(nudgesDir, { recursive: true });
216
+ }
217
+ fs.appendFileSync(nudgesFile, JSON.stringify(nudge) + '\n');
218
+ } catch (_) {
219
+ // Si falla persistir, no romper el flujo del usuario
220
+ }
221
+
222
+ process.exit(0);
@@ -356,6 +356,15 @@
356
356
  "maxConsecutiveFailures": 5,
357
357
  "degradeOnFailure": "skip"
358
358
  },
359
+ "validar-intent-spec.js": {
360
+ "event": "PostToolUse",
361
+ "matcher": "Write|Edit|MultiEdit",
362
+ "description": "Verifica que agentes nivelRiesgo:ALTO declaren las 6 partes del framework Intent Engineering (strategy, healthMetrics, steering, hardGuardrails, maxTurnos, fragmentos:[_intent-spec]) segun reglas/intent-engineering.md. Solo aplica a agentes/*.md no fragmentos. Emite nudge a .planning/evolucion/nudges.jsonl si faltan campos. No bloquea (modo warn-only inicial; promocion a blocking:true tras 2 semanas estables). Excluye temp/, node_modules/, respositorios-git/, _userland/, .planning/. Opt-out: SWL_INTENT_SPEC=0. ADR-0027.",
363
+ "blocking": false,
364
+ "async": true,
365
+ "maxConsecutiveFailures": 5,
366
+ "degradeOnFailure": "skip"
367
+ },
359
368
  "notificacion-telegram.js": {
360
369
  "event": "Stop",
361
370
  "matcher": "",
@@ -234,7 +234,14 @@
234
234
  "habilidades/tracing-processor",
235
235
  "habilidades/guardrail-semantico",
236
236
  "habilidades/legacy-code-rescue",
237
- "habilidades/harness-claude-code"
237
+ "habilidades/harness-claude-code",
238
+ "habilidades/proceso-confianza-pre-implementacion",
239
+ "habilidades/proceso-autoverificacion-evidencias",
240
+ "habilidades/aprender-de-git-diff",
241
+ "habilidades/reducir-entropia",
242
+ "habilidades/proceso-intent-engineering",
243
+ "habilidades/proceso-ddia-fundamentos",
244
+ "habilidades/proceso-ddia-streaming"
238
245
  ],
239
246
  "targets": [
240
247
  "claude",
@@ -886,7 +893,9 @@
886
893
  "reglas/usar-sistema-swl.md",
887
894
  "reglas/arreglar-al-detectar.md",
888
895
  "reglas/analisis-previo-tareas-grandes.md",
889
- "reglas/registro-componentes-nuevos.md"
896
+ "reglas/registro-componentes-nuevos.md",
897
+ "reglas/auditorias-documentales-estructurales.md",
898
+ "reglas/intent-engineering.md"
890
899
  ],
891
900
  "targets": [
892
901
  "claude",
@@ -1005,6 +1014,7 @@
1005
1014
  "hooks/inbox-aviso.js",
1006
1015
  "hooks/aiisms-detector.js",
1007
1016
  "hooks/claudemd-bloat-detector.js",
1017
+ "hooks/validar-intent-spec.js",
1008
1018
  "hooks/sugerir-regenerar-inventario.js",
1009
1019
  "hooks/sugerir-contribuir.js",
1010
1020
  "hooks/_run-hook.sh",