@saulwade/swl-ses 1.5.2 → 1.6.1
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 +32 -61
- package/README.md +20 -3
- package/agentes/datos-swl.md +1 -1
- package/agentes/frontend-angular-swl.md +7 -7
- package/agentes/frontend-css-swl.md +4 -4
- package/agentes/frontend-react-swl.md +7 -7
- package/agentes/frontend-swl.md +9 -9
- package/agentes/frontend-tailwind-swl.md +4 -4
- package/agentes/rendimiento-swl.md +2 -2
- package/bin/swl-ses.js +49 -7
- package/comandos/swl/brainstorm.md +1 -0
- package/comandos/swl/compactar.md +1 -1
- package/comandos/swl/discutir-fase.md +15 -1
- package/comandos/swl/mapear-codebase.md +1 -1
- package/comandos/swl/nemesis.md +29 -0
- package/comandos/swl/planear-fase.md +2 -2
- package/comandos/swl/verificar.md +4 -4
- package/habilidades/aprendizaje-continuo/SKILL.md +7 -1
- package/habilidades/diseno-herramientas-agente/SKILL.md +1 -0
- package/habilidades/doc-sync/SKILL.md +441 -1
- package/habilidades/doubt-driven-review/SKILL.md +177 -171
- package/habilidades/feynman-auditor-swl/SKILL.md +129 -123
- package/habilidades/infra-github-actions/SKILL.md +172 -166
- package/habilidades/meta-skills-estandar/recursos/skills-as-agents.md +163 -163
- package/habilidades/nemesis-evaluacion-json/SKILL.md +5 -0
- package/habilidades/nemesis-redistribuir/SKILL.md +5 -0
- package/habilidades/node-experto/SKILL.md +197 -3
- package/habilidades/prevencion-racionalizacion/SKILL.md +1 -0
- package/habilidades/privacy-memoria/SKILL.md +1 -0
- package/habilidades/sre-patrones/SKILL.md +1 -1
- package/habilidades/state-inconsistency-auditor-swl/SKILL.md +172 -166
- package/habilidades/tdd-workflow/SKILL.md +178 -3
- package/habilidades/verificacion-evidencia/SKILL.md +1 -0
- package/habilidades/web-fetcher-routing/SKILL.md +81 -75
- package/habilidades/workflow-claude-code/SKILL.md +2 -2
- package/hooks/extraccion-aprendizajes.js +11 -0
- package/manifiestos/modulos.json +2 -1
- package/manifiestos/skills-lock.json +1142 -1114
- package/package.json +7 -4
- package/plugin.json +4 -2
- package/reglas/auditorias-documentales-estructurales.md +205 -0
- package/schemas/agent-frontmatter.schema.json +1 -1
- package/scripts/desinstalar.js +105 -24
- package/scripts/generar-inventario.js +450 -420
- package/scripts/instalador.js +55 -4
- package/scripts/lib/parsear-opciones.js +3 -0
- package/scripts/lib/ui.js +148 -22
- package/scripts/tui/componentes/selector-multi.js +189 -0
- package/scripts/tui/componentes/selector-unico.js +158 -0
- package/scripts/tui/ejecutores.js +375 -0
- package/scripts/tui/index.js +162 -0
- package/scripts/tui/lib/colores.js +129 -0
- package/scripts/tui/lib/render.js +264 -0
- package/scripts/tui/lib/teclas.js +113 -0
- package/scripts/tui/pantallas/inspect.js +173 -0
- package/scripts/tui/pantallas/install-wizard.js +334 -0
- package/scripts/tui/pantallas/menu-principal.js +52 -0
- package/scripts/tui/pantallas/progreso.js +274 -0
- package/scripts/tui/pantallas/resumen.js +132 -0
- package/scripts/tui/pantallas/uninstall-wizard.js +208 -0
- package/scripts/tui/pantallas/update-wizard.js +232 -0
- package/scripts/tui/pantallas/welcome.js +187 -0
- package/scripts/verificar-docs-vs-codigo.js +654 -0
- package/scripts/verificar-evolucion.js +19 -3
|
@@ -1,163 +1,163 @@
|
|
|
1
|
-
# Skills as Agents — patrón técnico avanzado
|
|
2
|
-
|
|
3
|
-
Documenta el patrón Anthropic nativo de ejecutar un skill SWL en su propio
|
|
4
|
-
sub-agente con `agent: true` + `model:` en el frontmatter. Origen: artículo
|
|
5
|
-
"Claude Code's Limits Are Generous. The Problem Is Your Harness." sección 2.3.
|
|
6
|
-
|
|
7
|
-
## Concepto
|
|
8
|
-
|
|
9
|
-
Un SKILL.md normal es contenido que se carga al contexto del padre cuando
|
|
10
|
-
se invoca con `Skill("nombre")`. El skill aporta reglas, conocimiento o
|
|
11
|
-
guía al modelo del padre, que las usa al razonar.
|
|
12
|
-
|
|
13
|
-
Con el patrón skills as agents, el skill se vuelve **invocable como
|
|
14
|
-
sub-agente independiente**: tiene su propio modelo, su propio contexto,
|
|
15
|
-
y solo devuelve un resultado acotado al padre. Útil cuando el skill
|
|
16
|
-
procesa un input grande y debe entregar un resumen.
|
|
17
|
-
|
|
18
|
-
## Cuándo usar este patrón
|
|
19
|
-
|
|
20
|
-
- El skill procesa un archivo o input grande y debe devolver solo un
|
|
21
|
-
resumen al padre (ejemplo: extraer TLDR de un PDF de 50 páginas, sin
|
|
22
|
-
que el texto completo entre al contexto del padre).
|
|
23
|
-
- El skill ejecuta búsqueda exhaustiva o crawling de un codebase completo
|
|
24
|
-
y el resultado es un set acotado de findings.
|
|
25
|
-
- El skill necesita un modelo distinto al del padre por costo o latencia
|
|
26
|
-
(Haiku para trabajo mecánico cuando el padre es Opus).
|
|
27
|
-
- El trabajo del skill es paralelizable y se va a invocar varias veces en
|
|
28
|
-
la misma sesión.
|
|
29
|
-
|
|
30
|
-
## Cuándo NO usar
|
|
31
|
-
|
|
32
|
-
- El skill solo aporta reglas o conocimiento estático que se aplica al
|
|
33
|
-
razonamiento del padre — esos son skills "passive" normales y NO
|
|
34
|
-
necesitan `agent: true`. Su valor es justamente que cargan reglas al
|
|
35
|
-
contexto del padre.
|
|
36
|
-
- El skill es referenciado dentro de otro skill como contenido de apoyo
|
|
37
|
-
— el contexto del padre lo necesita ver.
|
|
38
|
-
- El skill requiere acceso al contexto activo del padre (decisiones
|
|
39
|
-
tomadas mid-session, archivos ya leídos) — `agent: true` aísla del
|
|
40
|
-
padre, así que el sub-agente no vería esa información.
|
|
41
|
-
- El skill es invocado una sola vez en la sesión y su output es pequeño
|
|
42
|
-
— el overhead de spawn de sub-agente puede dominar.
|
|
43
|
-
|
|
44
|
-
## Ejemplo de skill como agent
|
|
45
|
-
|
|
46
|
-
```yaml
|
|
47
|
-
---
|
|
48
|
-
name: tldr-pdf
|
|
49
|
-
description: >
|
|
50
|
-
Extrae TLDR de 200 palabras de un PDF sin cargar el texto completo en
|
|
51
|
-
el contexto del padre. Cargar cuando se reciba ruta a un PDF que solo
|
|
52
|
-
necesita resumen ejecutivo.
|
|
53
|
-
agent: true
|
|
54
|
-
model: claude-haiku-4-5-20251001
|
|
55
|
-
herramientasPermitidas: [Bash, Read]
|
|
56
|
-
---
|
|
57
|
-
|
|
58
|
-
# tldr-pdf
|
|
59
|
-
|
|
60
|
-
Recibes la ruta de un PDF como input.
|
|
61
|
-
|
|
62
|
-
1. Ejecuta `pdftotext "$1" -` para extraer texto.
|
|
63
|
-
2. Lee la salida.
|
|
64
|
-
3. Devuelve SOLO:
|
|
65
|
-
- 5 bullets de TLDR
|
|
66
|
-
- 3 quotes que valga la pena conservar
|
|
67
|
-
- URLs citadas
|
|
68
|
-
|
|
69
|
-
Nunca devuelvas el texto completo. Nunca expandas más allá de la estructura.
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
Flujo: el padre invoca `Skill("
|
|
73
|
-
sub-agente Haiku ejecuta `pdftotext`, lee la salida (que llena su propio
|
|
74
|
-
contexto, no el del padre), y devuelve ~200 palabras estructuradas. El PDF
|
|
75
|
-
completo nunca toca el contexto del padre.
|
|
76
|
-
|
|
77
|
-
## Reglas del patrón
|
|
78
|
-
|
|
79
|
-
### Frontmatter
|
|
80
|
-
|
|
81
|
-
- `agent: true` activa el modo sub-agente.
|
|
82
|
-
- `model:` debe ser un modelo Anthropic válido. Para skills mecánicos,
|
|
83
|
-
preferir Haiku (5× más barato que Sonnet, 25× más barato que Opus 4.7).
|
|
84
|
-
- El frontmatter sigue siendo válido SWL: incluir `name`, `description`,
|
|
85
|
-
`herramientasPermitidas`, `exclusiones` y demás campos obligatorios.
|
|
86
|
-
|
|
87
|
-
### Cuerpo
|
|
88
|
-
|
|
89
|
-
- Debe ser **autocontenido**: el sub-agente NO tiene acceso al contexto
|
|
90
|
-
del padre (CLAUDE.md del proyecto, instintos cargados, sesión activa,
|
|
91
|
-
archivos previamente leídos). Todo lo que necesite saber tiene que
|
|
92
|
-
estar en el cuerpo del skill o en lo que reciba como input.
|
|
93
|
-
- El output debe ser **acotado**: si el sub-agente devuelve un dump
|
|
94
|
-
masivo, se pierde el beneficio (terminamos pasando lo mismo al padre,
|
|
95
|
-
solo con un round-trip extra). Especificar formato exacto en el
|
|
96
|
-
cuerpo del skill: número de bullets, longitud máxima, qué incluir y
|
|
97
|
-
qué no.
|
|
98
|
-
|
|
99
|
-
### Errores comunes a evitar
|
|
100
|
-
|
|
101
|
-
- **Usar Opus en `model:` para skills mecánicos**: invalida el ahorro de
|
|
102
|
-
costo. La mayoría de skills as agents deberían ser Haiku o Sonnet.
|
|
103
|
-
- **Devolver el texto crudo del input**: si el skill recibe un PDF y
|
|
104
|
-
devuelve el texto completo, no estamos aislando nada. Devuelve siempre
|
|
105
|
-
la transformación, no el material crudo.
|
|
106
|
-
- **Asumir que el skill verá el CLAUDE.md del proyecto**: no lo verá.
|
|
107
|
-
Si el skill necesita seguir convenciones del proyecto, hay que
|
|
108
|
-
explicárselas en el cuerpo.
|
|
109
|
-
|
|
110
|
-
## Skills SWL candidatos al patrón (no migración masiva)
|
|
111
|
-
|
|
112
|
-
Evaluación caso por caso. NO aplicar masivamente — el patrón aporta solo
|
|
113
|
-
cuando el skill consume input grande y devuelve output acotado.
|
|
114
|
-
|
|
115
|
-
| Skill SWL | Por qué encaja | Modelo recomendado |
|
|
116
|
-
|-----------|----------------|--------------------|
|
|
117
|
-
| `extraccion-documentos` | Procesa PDFs/Office grandes | Haiku |
|
|
118
|
-
| `mapear-codebase` | Procesa codebase entero, devuelve reporte | Sonnet |
|
|
119
|
-
| `wiki-conocimiento` (modo query) | Búsqueda en wiki con respuesta acotada | Sonnet |
|
|
120
|
-
|
|
121
|
-
Skills NO candidatos (son passive, su valor es cargar reglas al contexto del padre):
|
|
122
|
-
|
|
123
|
-
- `manejo-errores`, `auth-patrones`, `api-rest-diseno`, `accesibilidad-a11y`, etc.
|
|
124
|
-
- Toda regla de estilo o convención por lenguaje.
|
|
125
|
-
- Toda regla SWL en `reglas/`.
|
|
126
|
-
|
|
127
|
-
## Implicaciones operativas
|
|
128
|
-
|
|
129
|
-
### Costo
|
|
130
|
-
|
|
131
|
-
Spawn de sub-agente tiene overhead (system prompt + carga del skill +
|
|
132
|
-
contexto inicial). Para inputs chicos, el overhead supera el ahorro.
|
|
133
|
-
Regla práctica: usar el patrón si el input que el skill procesa es
|
|
134
|
-
≥3,000 tokens (o ≥10K caracteres).
|
|
135
|
-
|
|
136
|
-
### Latencia
|
|
137
|
-
|
|
138
|
-
El padre espera al sub-agente antes de continuar. Si el skill demora >10s,
|
|
139
|
-
considerar si la operación cabe en el padre directamente (sin spawn).
|
|
140
|
-
|
|
141
|
-
### Trazabilidad
|
|
142
|
-
|
|
143
|
-
Los sub-agentes spawneados aparecen en `claude-usage` (vendor SWL) como
|
|
144
|
-
invocaciones separadas con su propio modelo. Útil para entender qué
|
|
145
|
-
porcentaje del costo viene de sub-agentes vs. trabajo del padre.
|
|
146
|
-
|
|
147
|
-
### Compatibilidad con SWL
|
|
148
|
-
|
|
149
|
-
- El skill sigue registrado normalmente en `manifiestos/modulos.json`.
|
|
150
|
-
- El skill se invoca igual: `Skill("nombre")`. La diferencia es interna
|
|
151
|
-
(el harness Anthropic decide si lo carga al padre o lo ejecuta como
|
|
152
|
-
sub-agente según el frontmatter).
|
|
153
|
-
- Hooks SWL siguen aplicando: `linea-estado.js`, `monitor-contexto.js`,
|
|
154
|
-
`audit-trail.js` registran las invocaciones del sub-agente.
|
|
155
|
-
|
|
156
|
-
## Referencias
|
|
157
|
-
|
|
158
|
-
- Artículo de origen: "Claude Code's Limits Are Generous. The Problem Is
|
|
159
|
-
Your Harness." (sección 2.3 — Skills Can Also Be Invoked as Agents).
|
|
160
|
-
- Documentación oficial Anthropic sobre Claude Skills (cuando esté
|
|
161
|
-
disponible).
|
|
162
|
-
- `Skill("harness-claude-code")` — uso operativo del patrón en sesiones
|
|
163
|
-
largas, junto con sub-agentes via Task tool y delegación explícita.
|
|
1
|
+
# Skills as Agents — patrón técnico avanzado
|
|
2
|
+
|
|
3
|
+
Documenta el patrón Anthropic nativo de ejecutar un skill SWL en su propio
|
|
4
|
+
sub-agente con `agent: true` + `model:` en el frontmatter. Origen: artículo
|
|
5
|
+
"Claude Code's Limits Are Generous. The Problem Is Your Harness." sección 2.3.
|
|
6
|
+
|
|
7
|
+
## Concepto
|
|
8
|
+
|
|
9
|
+
Un SKILL.md normal es contenido que se carga al contexto del padre cuando
|
|
10
|
+
se invoca con `Skill("nombre")`. El skill aporta reglas, conocimiento o
|
|
11
|
+
guía al modelo del padre, que las usa al razonar.
|
|
12
|
+
|
|
13
|
+
Con el patrón skills as agents, el skill se vuelve **invocable como
|
|
14
|
+
sub-agente independiente**: tiene su propio modelo, su propio contexto,
|
|
15
|
+
y solo devuelve un resultado acotado al padre. Útil cuando el skill
|
|
16
|
+
procesa un input grande y debe entregar un resumen.
|
|
17
|
+
|
|
18
|
+
## Cuándo usar este patrón
|
|
19
|
+
|
|
20
|
+
- El skill procesa un archivo o input grande y debe devolver solo un
|
|
21
|
+
resumen al padre (ejemplo: extraer TLDR de un PDF de 50 páginas, sin
|
|
22
|
+
que el texto completo entre al contexto del padre).
|
|
23
|
+
- El skill ejecuta búsqueda exhaustiva o crawling de un codebase completo
|
|
24
|
+
y el resultado es un set acotado de findings.
|
|
25
|
+
- El skill necesita un modelo distinto al del padre por costo o latencia
|
|
26
|
+
(Haiku para trabajo mecánico cuando el padre es Opus).
|
|
27
|
+
- El trabajo del skill es paralelizable y se va a invocar varias veces en
|
|
28
|
+
la misma sesión.
|
|
29
|
+
|
|
30
|
+
## Cuándo NO usar
|
|
31
|
+
|
|
32
|
+
- El skill solo aporta reglas o conocimiento estático que se aplica al
|
|
33
|
+
razonamiento del padre — esos son skills "passive" normales y NO
|
|
34
|
+
necesitan `agent: true`. Su valor es justamente que cargan reglas al
|
|
35
|
+
contexto del padre.
|
|
36
|
+
- El skill es referenciado dentro de otro skill como contenido de apoyo
|
|
37
|
+
— el contexto del padre lo necesita ver.
|
|
38
|
+
- El skill requiere acceso al contexto activo del padre (decisiones
|
|
39
|
+
tomadas mid-session, archivos ya leídos) — `agent: true` aísla del
|
|
40
|
+
padre, así que el sub-agente no vería esa información.
|
|
41
|
+
- El skill es invocado una sola vez en la sesión y su output es pequeño
|
|
42
|
+
— el overhead de spawn de sub-agente puede dominar.
|
|
43
|
+
|
|
44
|
+
## Ejemplo de skill como agent
|
|
45
|
+
|
|
46
|
+
```yaml
|
|
47
|
+
---
|
|
48
|
+
name: tldr-pdf
|
|
49
|
+
description: >
|
|
50
|
+
Extrae TLDR de 200 palabras de un PDF sin cargar el texto completo en
|
|
51
|
+
el contexto del padre. Cargar cuando se reciba ruta a un PDF que solo
|
|
52
|
+
necesita resumen ejecutivo.
|
|
53
|
+
agent: true
|
|
54
|
+
model: claude-haiku-4-5-20251001
|
|
55
|
+
herramientasPermitidas: [Bash, Read]
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
# tldr-pdf
|
|
59
|
+
|
|
60
|
+
Recibes la ruta de un PDF como input.
|
|
61
|
+
|
|
62
|
+
1. Ejecuta `pdftotext "$1" -` para extraer texto.
|
|
63
|
+
2. Lee la salida.
|
|
64
|
+
3. Devuelve SOLO:
|
|
65
|
+
- 5 bullets de TLDR
|
|
66
|
+
- 3 quotes que valga la pena conservar
|
|
67
|
+
- URLs citadas
|
|
68
|
+
|
|
69
|
+
Nunca devuelvas el texto completo. Nunca expandas más allá de la estructura.
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Flujo: el padre invoca `Skill("swl-markitdown")` con la ruta del PDF; el
|
|
73
|
+
sub-agente Haiku ejecuta `pdftotext`, lee la salida (que llena su propio
|
|
74
|
+
contexto, no el del padre), y devuelve ~200 palabras estructuradas. El PDF
|
|
75
|
+
completo nunca toca el contexto del padre.
|
|
76
|
+
|
|
77
|
+
## Reglas del patrón
|
|
78
|
+
|
|
79
|
+
### Frontmatter
|
|
80
|
+
|
|
81
|
+
- `agent: true` activa el modo sub-agente.
|
|
82
|
+
- `model:` debe ser un modelo Anthropic válido. Para skills mecánicos,
|
|
83
|
+
preferir Haiku (5× más barato que Sonnet, 25× más barato que Opus 4.7).
|
|
84
|
+
- El frontmatter sigue siendo válido SWL: incluir `name`, `description`,
|
|
85
|
+
`herramientasPermitidas`, `exclusiones` y demás campos obligatorios.
|
|
86
|
+
|
|
87
|
+
### Cuerpo
|
|
88
|
+
|
|
89
|
+
- Debe ser **autocontenido**: el sub-agente NO tiene acceso al contexto
|
|
90
|
+
del padre (CLAUDE.md del proyecto, instintos cargados, sesión activa,
|
|
91
|
+
archivos previamente leídos). Todo lo que necesite saber tiene que
|
|
92
|
+
estar en el cuerpo del skill o en lo que reciba como input.
|
|
93
|
+
- El output debe ser **acotado**: si el sub-agente devuelve un dump
|
|
94
|
+
masivo, se pierde el beneficio (terminamos pasando lo mismo al padre,
|
|
95
|
+
solo con un round-trip extra). Especificar formato exacto en el
|
|
96
|
+
cuerpo del skill: número de bullets, longitud máxima, qué incluir y
|
|
97
|
+
qué no.
|
|
98
|
+
|
|
99
|
+
### Errores comunes a evitar
|
|
100
|
+
|
|
101
|
+
- **Usar Opus en `model:` para skills mecánicos**: invalida el ahorro de
|
|
102
|
+
costo. La mayoría de skills as agents deberían ser Haiku o Sonnet.
|
|
103
|
+
- **Devolver el texto crudo del input**: si el skill recibe un PDF y
|
|
104
|
+
devuelve el texto completo, no estamos aislando nada. Devuelve siempre
|
|
105
|
+
la transformación, no el material crudo.
|
|
106
|
+
- **Asumir que el skill verá el CLAUDE.md del proyecto**: no lo verá.
|
|
107
|
+
Si el skill necesita seguir convenciones del proyecto, hay que
|
|
108
|
+
explicárselas en el cuerpo.
|
|
109
|
+
|
|
110
|
+
## Skills SWL candidatos al patrón (no migración masiva)
|
|
111
|
+
|
|
112
|
+
Evaluación caso por caso. NO aplicar masivamente — el patrón aporta solo
|
|
113
|
+
cuando el skill consume input grande y devuelve output acotado.
|
|
114
|
+
|
|
115
|
+
| Skill SWL | Por qué encaja | Modelo recomendado |
|
|
116
|
+
|-----------|----------------|--------------------|
|
|
117
|
+
| `extraccion-documentos` | Procesa PDFs/Office grandes | Haiku |
|
|
118
|
+
| `mapear-codebase` | Procesa codebase entero, devuelve reporte | Sonnet |
|
|
119
|
+
| `wiki-conocimiento` (modo query) | Búsqueda en wiki con respuesta acotada | Sonnet |
|
|
120
|
+
|
|
121
|
+
Skills NO candidatos (son passive, su valor es cargar reglas al contexto del padre):
|
|
122
|
+
|
|
123
|
+
- `manejo-errores`, `auth-patrones`, `api-rest-diseno`, `accesibilidad-a11y`, etc.
|
|
124
|
+
- Toda regla de estilo o convención por lenguaje.
|
|
125
|
+
- Toda regla SWL en `reglas/`.
|
|
126
|
+
|
|
127
|
+
## Implicaciones operativas
|
|
128
|
+
|
|
129
|
+
### Costo
|
|
130
|
+
|
|
131
|
+
Spawn de sub-agente tiene overhead (system prompt + carga del skill +
|
|
132
|
+
contexto inicial). Para inputs chicos, el overhead supera el ahorro.
|
|
133
|
+
Regla práctica: usar el patrón si el input que el skill procesa es
|
|
134
|
+
≥3,000 tokens (o ≥10K caracteres).
|
|
135
|
+
|
|
136
|
+
### Latencia
|
|
137
|
+
|
|
138
|
+
El padre espera al sub-agente antes de continuar. Si el skill demora >10s,
|
|
139
|
+
considerar si la operación cabe en el padre directamente (sin spawn).
|
|
140
|
+
|
|
141
|
+
### Trazabilidad
|
|
142
|
+
|
|
143
|
+
Los sub-agentes spawneados aparecen en `claude-usage` (vendor SWL) como
|
|
144
|
+
invocaciones separadas con su propio modelo. Útil para entender qué
|
|
145
|
+
porcentaje del costo viene de sub-agentes vs. trabajo del padre.
|
|
146
|
+
|
|
147
|
+
### Compatibilidad con SWL
|
|
148
|
+
|
|
149
|
+
- El skill sigue registrado normalmente en `manifiestos/modulos.json`.
|
|
150
|
+
- El skill se invoca igual: `Skill("nombre")`. La diferencia es interna
|
|
151
|
+
(el harness Anthropic decide si lo carga al padre o lo ejecuta como
|
|
152
|
+
sub-agente según el frontmatter).
|
|
153
|
+
- Hooks SWL siguen aplicando: `linea-estado.js`, `monitor-contexto.js`,
|
|
154
|
+
`audit-trail.js` registran las invocaciones del sub-agente.
|
|
155
|
+
|
|
156
|
+
## Referencias
|
|
157
|
+
|
|
158
|
+
- Artículo de origen: "Claude Code's Limits Are Generous. The Problem Is
|
|
159
|
+
Your Harness." (sección 2.3 — Skills Can Also Be Invoked as Agents).
|
|
160
|
+
- Documentación oficial Anthropic sobre Claude Skills (cuando esté
|
|
161
|
+
disponible).
|
|
162
|
+
- `Skill("harness-claude-code")` — uso operativo del patrón en sesiones
|
|
163
|
+
largas, junto con sub-agentes via Task tool y delegación explícita.
|
|
@@ -9,6 +9,11 @@ description: >
|
|
|
9
9
|
alimentar el ciclo de remediación de /swl:nemesis --remediar, o cuando
|
|
10
10
|
un componente downstream necesita parsear el veredicto de una auditoría
|
|
11
11
|
nemesis.
|
|
12
|
+
version: "1.0.0"
|
|
13
|
+
exclusiones:
|
|
14
|
+
- "No cargar para schemas de otros tipos de auditoría (revisor-codigo, revisor-seguridad) — cada uno tiene su propio formato."
|
|
15
|
+
- "No cargar fuera del contexto del agente nemesis-auditor-swl o del comando /swl:nemesis — el schema es específico de esta auditoría."
|
|
16
|
+
- "No cargar para definir nuevos formatos de evaluación — este skill describe el formato existente, no especifica formatos nuevos."
|
|
12
17
|
---
|
|
13
18
|
|
|
14
19
|
# Schema del evaluacion.json del Nemesis
|
|
@@ -9,6 +9,11 @@ description: >
|
|
|
9
9
|
/swl:nemesis cuando la fase 0 detecta scope grande, antes de invocar al
|
|
10
10
|
agente nemesis-auditor-swl. NO cargar para scope pequeño — el bucle iterativo
|
|
11
11
|
Feynman+State del agente es más profundo que la redistribución.
|
|
12
|
+
version: "1.0.0"
|
|
13
|
+
exclusiones:
|
|
14
|
+
- "No cargar para scope <1500 LOC ni <5 archivos — el agente Nemesis directo es suficiente."
|
|
15
|
+
- "No cargar fuera del flujo /swl:nemesis — la redistribución es específica de ese comando."
|
|
16
|
+
- "No cargar para revisiones de otros tipos (revisor-codigo, revisor-seguridad) — esos no requieren redistribución por scope."
|
|
12
17
|
---
|
|
13
18
|
|
|
14
19
|
# Redistribución de scope para Nemesis
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: node-experto
|
|
3
3
|
description: Node.js y TypeScript backend moderno. Cubre patterns de Express/Fastify/NestJS, error handling middleware, streams y buffers, worker threads y clustering, Prisma/Drizzle ORM, validación con Zod, graceful shutdown y anti-patrones críticos como callback hell y event loop blocking.
|
|
4
|
-
version: "1.0
|
|
4
|
+
version: "1.1.0"
|
|
5
5
|
evolved: true
|
|
6
|
-
evolved-from: "1.0.
|
|
6
|
+
evolved-from: "1.0.2"
|
|
7
7
|
evolved-at: "2026-05-16"
|
|
8
8
|
evolved-by: "aprender"
|
|
9
|
-
evolved-note: "v1.0.
|
|
9
|
+
evolved-note: "v1.1.0 (MINOR): sección completa nueva 'Patrones de readline + prompts asíncronos' — Promise+close obligatorio, pausa de spinners durante prompts, listener leak prevention. Origen: H1 sesión 2026-05-16 (race condition spinner/prompt en swl-ses update + premature close detectado por Cursor IDE + /swl:nemesis --remediar iter-2 con 2 fixes adicionales). v1.0.2: gotchas `undefined<N` (NaN comparison), first-wins vs deep merge en config (PR #27 #30 v1.5.2), `assert.notMatch` no existe. v1.0.1: gotcha req.destroy() + CRLF + TOML literal."
|
|
10
10
|
herramientasPermitidas: [Read, Write, Glob]
|
|
11
11
|
exclusiones:
|
|
12
12
|
- "No cargar para aplicaciones Next.js con App Router — Next.js tiene patrones de Server Components, SSR y caché que difieren del backend Node puro; cargar `nextjs-experto`."
|
|
@@ -324,6 +324,200 @@ Verificar siempre `process.env.WEBHOOK_URL` antes de llamar — sin config reque
|
|
|
324
324
|
|
|
325
325
|
---
|
|
326
326
|
|
|
327
|
+
## Patrones de readline + prompts asíncronos
|
|
328
|
+
|
|
329
|
+
### NUNCA: usar `rl.question(cb)` envuelto en Promise sin manejar el cierre
|
|
330
|
+
|
|
331
|
+
**Problema**: `readline.Interface.question(prompt, callback)` solo invoca el
|
|
332
|
+
callback cuando el usuario presiona Enter con input válido. Si el stream
|
|
333
|
+
`stdin` se cierra antes (Ctrl+C, EOF, pipe roto, error), el callback nunca
|
|
334
|
+
se ejecuta y el Promise queda colgado para siempre. Cualquier código que
|
|
335
|
+
dependa de ese resolve (spinners pausados, locks, cleanup) queda bloqueado.
|
|
336
|
+
|
|
337
|
+
```javascript
|
|
338
|
+
// MAL — Promise huérfano si stdin se cierra antes del input
|
|
339
|
+
function preguntarSiNo(mensaje) {
|
|
340
|
+
return new Promise((resolve) => {
|
|
341
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
342
|
+
rl.question(`${mensaje} [s/N] `, (respuesta) => {
|
|
343
|
+
rl.close();
|
|
344
|
+
resolve(respuesta.trim().toLowerCase() === 's');
|
|
345
|
+
});
|
|
346
|
+
// Si el usuario presiona Ctrl+C, rl emite 'close' pero el callback
|
|
347
|
+
// de question() nunca corre. Promise queda colgado.
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
```javascript
|
|
353
|
+
// BIEN — 'close' como única ruta de finalización + flag idempotente
|
|
354
|
+
function preguntarSiNo(mensaje, valorDefault = false) {
|
|
355
|
+
return new Promise((resolve) => {
|
|
356
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
357
|
+
|
|
358
|
+
let valorFinal = valorDefault;
|
|
359
|
+
let resuelto = false;
|
|
360
|
+
|
|
361
|
+
function finalizar() {
|
|
362
|
+
if (resuelto) return;
|
|
363
|
+
resuelto = true;
|
|
364
|
+
resolve(valorFinal);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// 'close' se dispara SIEMPRE: input válido (tras rl.close()),
|
|
368
|
+
// Ctrl+C, EOF, error. Garantiza resolución del Promise.
|
|
369
|
+
rl.on('close', finalizar);
|
|
370
|
+
|
|
371
|
+
rl.question(`${mensaje} [s/N] `, (respuesta) => {
|
|
372
|
+
valorFinal = respuesta.trim().toLowerCase() === 's';
|
|
373
|
+
rl.close(); // dispara 'close' → finalizar(); idempotente vía `resuelto`
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
**Regla de oro**: el callback de `rl.question` NUNCA debe llamar `resolve()`
|
|
380
|
+
directamente. Solo debe **setear el valor final** y llamar `rl.close()`. El
|
|
381
|
+
listener `rl.on('close', ...)` es el único punto que llama `resolve()`.
|
|
382
|
+
|
|
383
|
+
### NUNCA: ejecutar `setInterval` que escriba a stdout durante un prompt readline
|
|
384
|
+
|
|
385
|
+
**Problema**: un spinner que escribe `\r ⠏ Procesando... ` cada 80ms sobre
|
|
386
|
+
stdout sobrescribe el prompt renderizado por `readline.createInterface()`.
|
|
387
|
+
El cursor lógico de readline está en el lugar correcto, pero la
|
|
388
|
+
representación visual queda mezclada — el usuario ve algo como
|
|
389
|
+
`⠏ Procesando... ntes? [S/n]` (fragmentos del prompt cortados).
|
|
390
|
+
|
|
391
|
+
```javascript
|
|
392
|
+
// MAL — race entre spinner y prompt
|
|
393
|
+
const sp = spinner('Cargando datos...'); // setInterval cada 80ms
|
|
394
|
+
try {
|
|
395
|
+
const datos = await fetchDatos();
|
|
396
|
+
if (necesitaConfirmacion(datos)) {
|
|
397
|
+
// BUG: el spinner sigue escribiendo \r mientras readline renderiza el prompt
|
|
398
|
+
const confirma = await preguntarSiNo('¿Continuar?');
|
|
399
|
+
}
|
|
400
|
+
} finally {
|
|
401
|
+
sp.detener();
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
```javascript
|
|
406
|
+
// BIEN — registro global de spinners + pausa coordinada
|
|
407
|
+
const _spinnersActivos = new Set();
|
|
408
|
+
|
|
409
|
+
function _pausarSpinnersActivos() {
|
|
410
|
+
for (const sp of _spinnersActivos) sp._pausar(); // clearInterval + limpiarLinea
|
|
411
|
+
}
|
|
412
|
+
function _reanudarSpinnersActivos() {
|
|
413
|
+
for (const sp of _spinnersActivos) sp._reanudar(); // re-setInterval si sigue activo
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function spinner(mensaje) {
|
|
417
|
+
let pausado = false, intervalo = null;
|
|
418
|
+
function _arrancarIntervalo() {
|
|
419
|
+
if (!intervalo) intervalo = setInterval(tick, 80);
|
|
420
|
+
}
|
|
421
|
+
const handle = {
|
|
422
|
+
_pausar() {
|
|
423
|
+
if (pausado) return;
|
|
424
|
+
pausado = true;
|
|
425
|
+
if (intervalo) { clearInterval(intervalo); intervalo = null; }
|
|
426
|
+
process.stdout.write('\r\x1b[2K'); // limpiar línea para el prompt
|
|
427
|
+
},
|
|
428
|
+
_reanudar() {
|
|
429
|
+
if (!pausado) return;
|
|
430
|
+
pausado = false;
|
|
431
|
+
_arrancarIntervalo();
|
|
432
|
+
},
|
|
433
|
+
detener() {
|
|
434
|
+
_spinnersActivos.delete(handle);
|
|
435
|
+
if (intervalo) clearInterval(intervalo);
|
|
436
|
+
},
|
|
437
|
+
};
|
|
438
|
+
_spinnersActivos.add(handle);
|
|
439
|
+
_arrancarIntervalo();
|
|
440
|
+
return handle;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// preguntarSiNo pausa antes de createInterface y reanuda en finalizar
|
|
444
|
+
function preguntarSiNo(mensaje) {
|
|
445
|
+
return new Promise((resolve) => {
|
|
446
|
+
_pausarSpinnersActivos();
|
|
447
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
448
|
+
let resuelto = false;
|
|
449
|
+
rl.on('close', () => {
|
|
450
|
+
if (resuelto) return;
|
|
451
|
+
resuelto = true;
|
|
452
|
+
_reanudarSpinnersActivos();
|
|
453
|
+
resolve(valorFinal);
|
|
454
|
+
});
|
|
455
|
+
// ... rl.question como en el patrón anterior
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### NUNCA: registrar listeners en `process` desde un spinner sin removerlos
|
|
461
|
+
|
|
462
|
+
**Problema**: un spinner registra `process.once('exit', cleanup)` al
|
|
463
|
+
crearse. Si el spinner termina con éxito (no por exit del proceso), el
|
|
464
|
+
listener queda colgado en `process`. Un loop que crea N spinners
|
|
465
|
+
secuenciales (un instalador iterando runtimes) acumula N listeners hasta
|
|
466
|
+
disparar `MaxListenersExceededWarning` con default 10.
|
|
467
|
+
|
|
468
|
+
```javascript
|
|
469
|
+
// BIEN — declarar exitHandler antes del handle y removerlo en terminación
|
|
470
|
+
function spinner(mensaje) {
|
|
471
|
+
// Declarado antes del handle para que las funciones de terminación
|
|
472
|
+
// puedan removerlo. Closure: la arrow function captura la binding de
|
|
473
|
+
// `handle`, no su valor — se resuelve al invocar 'exit'.
|
|
474
|
+
const exitHandler = () => { handle.detener(); };
|
|
475
|
+
|
|
476
|
+
const handle = {
|
|
477
|
+
exito(msg) {
|
|
478
|
+
// ... limpieza interna
|
|
479
|
+
process.removeListener('exit', exitHandler); // ← OBLIGATORIO
|
|
480
|
+
console.log(` ✓ ${msg}`);
|
|
481
|
+
},
|
|
482
|
+
fallo(msg) {
|
|
483
|
+
process.removeListener('exit', exitHandler); // ← OBLIGATORIO
|
|
484
|
+
console.log(` ✗ ${msg}`);
|
|
485
|
+
},
|
|
486
|
+
detener() {
|
|
487
|
+
process.removeListener('exit', exitHandler); // ← OBLIGATORIO
|
|
488
|
+
},
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
process.once('exit', exitHandler);
|
|
492
|
+
return handle;
|
|
493
|
+
}
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
Para verificar que no hay leak en tests:
|
|
497
|
+
|
|
498
|
+
```javascript
|
|
499
|
+
test('spinner: no acumula listeners en process al iterar', () => {
|
|
500
|
+
const antes = process.listenerCount('exit');
|
|
501
|
+
for (let i = 0; i < 12; i++) {
|
|
502
|
+
const sp = spinner(`iter-${i}`);
|
|
503
|
+
if (i % 3 === 0) sp.exito(`ok-${i}`);
|
|
504
|
+
else if (i % 3 === 1) sp.fallo(`err-${i}`);
|
|
505
|
+
else sp.detener();
|
|
506
|
+
}
|
|
507
|
+
const despues = process.listenerCount('exit');
|
|
508
|
+
assert.equal(despues, antes, 'listeners no removidos');
|
|
509
|
+
});
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
**Detección iterativa con `/swl:nemesis --remediar`**: este patrón
|
|
513
|
+
(race spinner/prompt + premature close + listener leak) emergió en swl-ses
|
|
514
|
+
v1.6.0 tras 3 iteraciones de fix. La primera detectó el race obvio; Cursor
|
|
515
|
+
IDE detectó el premature close en revisión estática del fix; nemesis iter-2
|
|
516
|
+
detectó el listener leak (F-1) y `preguntarOpcion` pausando tarde (F-2).
|
|
517
|
+
Lección operativa: para módulos pequeños con alta densidad de control de
|
|
518
|
+
flujo (event handlers, callbacks, setInterval/setTimeout, readline),
|
|
519
|
+
nemesis con `--remediar` es costo-efectivo (~15 turnos para ~300 LOC).
|
|
520
|
+
|
|
327
521
|
## Scripts dual-use: librería + CLI
|
|
328
522
|
|
|
329
523
|
### `require.main === module` para scripts en `scripts/lib/`
|
|
@@ -6,6 +6,7 @@ description: >
|
|
|
6
6
|
red flags de pensamiento y técnicas para construir skills resistentes a la
|
|
7
7
|
racionalización. Cargar al diseñar skills, al detectar agentes saltando procesos,
|
|
8
8
|
o al revisar por qué un agente no siguió un paso obligatorio.
|
|
9
|
+
version: "1.0.0"
|
|
9
10
|
herramientasPermitidas: [Read, Grep]
|
|
10
11
|
exclusiones:
|
|
11
12
|
- "No cargar para prevenir sobre-ingeniería o cambios fuera del scope — ese es `prevencion-sobreingenieria`; este skill trata racionalización de *omisión* de pasos obligatorios, no de *adición* innecesaria de complejidad."
|
|
@@ -5,6 +5,7 @@ description: >
|
|
|
5
5
|
sensible, y mejores prácticas para proteger datos en aprendizajes y sesiones.
|
|
6
6
|
Cargar cuando se trabaje con datos sensibles, se configure el sistema de memoria
|
|
7
7
|
o se implementen componentes que lean/escriban APRENDIZAJES.md o session-store.
|
|
8
|
+
version: "1.0.0"
|
|
8
9
|
herramientasPermitidas: [Read, Write]
|
|
9
10
|
evolvable: false # bloqueado por lista (skill de seguridad/privacidad)
|
|
10
11
|
nist_csf: [GV.OV-01, PR.DS-01, PR.DS-02, DE.CM-09]
|
|
@@ -303,7 +303,7 @@ Ejemplo incorrecto: "Juan olvidó activar el health check."]
|
|
|
303
303
|
| Chaos engineering: steady state, herramientas, safety | [recursos/chaos-engineering.md](recursos/chaos-engineering.md) |
|
|
304
304
|
| On-call: rotación, escalación, fatiga de alertas | [recursos/oncall-design.md](recursos/oncall-design.md) |
|
|
305
305
|
| Alertas Prometheus con runbook completo | `Skill("monitoring-alertas")` |
|
|
306
|
-
| Implementación de métricas en código | `
|
|
306
|
+
| Implementación de métricas en código | Invocar `Agent(subagent_type="observabilidad-swl")` — es agente, no skill |
|
|
307
307
|
|
|
308
308
|
---
|
|
309
309
|
|