@damiangilgonzalez/harness 1.3.13 → 1.3.15
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/README.md +15 -9
- package/manifest.json +60 -4
- package/package.json +1 -1
- package/scripts/doctor.js +13 -4
- package/scripts/install-apply.js +73 -9
- package/scripts/lib/backups.js +13 -0
- package/scripts/lib/default-skill-floor.js +14 -0
- package/scripts/lib/domains.js +1 -1
- package/scripts/lib/onboarding-apply.js +73 -12
- package/scripts/lib/onboarding.js +8 -0
- package/scripts/lib/repair.js +8 -4
- package/scripts/lib/review-findings.js +175 -0
- package/scripts/rsc.js +51 -3
- package/skills/code-review/SKILL.md +17 -1
- package/skills/domain-modeling/{CONTEXT-FORMAT.md → GLOSSARY-FORMAT.md} +9 -9
- package/skills/domain-modeling/SKILL.md +14 -12
- package/skills/harness/SKILL.md +6 -0
- package/skills/harness/references/wiki-protocol.md +5 -0
- package/skills/implement-spec/SKILL.md +43 -0
- package/skills/implement-spec/evals/cases.yaml +33 -0
- package/skills/init/references/accompaniment-and-profile.md +1 -1
- package/skills/orient/SKILL.md +9 -1
- package/skills/orient/references/orientation-contract.md +1 -1
- package/skills/pr/CREDITS.md +3 -0
- package/skills/pr/SKILL.md +173 -0
- package/skills/pr/evals/cases.yaml +33 -0
- package/skills/retro/SKILL.md +47 -0
- package/skills/retro/evals/cases.yaml +32 -0
- package/skills/suggest/SKILL.md +1 -1
- package/skills/wait-what/SKILL.md +1 -1
- package/targets/_md-block.js +1 -1
- package/targets/agents.js +37 -1
- package/targets/claude.js +25 -16
- package/targets/hook-once.mjs +6 -3
- package/targets/memory.js +1 -1
- package/targets/sello.mjs +2 -2
- package/targets/session-memory-core.mjs +1 -1
- package/targets/session-start.mjs +2 -2
- package/targets/ship-guard.mjs +1 -1
- package/targets/worktree-reaper.mjs +1 -1
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pr
|
|
3
|
+
description: "Use when writing a PR body: a visual summary, before/after evidence, and the merge danger (one-way or two-way door, blast radius)."
|
|
4
|
+
tags: ["pull-request", "review", "evidence", "risk"]
|
|
5
|
+
profiles: [core, ui, full]
|
|
6
|
+
origin: damiangil
|
|
7
|
+
metadata:
|
|
8
|
+
credits:
|
|
9
|
+
skill: show-me
|
|
10
|
+
author: Dex Horthy
|
|
11
|
+
organisation: Humanlayer
|
|
12
|
+
url: "https://github.com/humanlayer/skills/blob/main/plugins/show-me/skills/show-me/SKILL.md"
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
Usa esta plantilla para escribir el cuerpo de la PR:
|
|
16
|
+
|
|
17
|
+
```markdown
|
|
18
|
+
## Resumen
|
|
19
|
+
|
|
20
|
+
<diagrama, esbozo de diff o árbol>
|
|
21
|
+
|
|
22
|
+
## Evidencia
|
|
23
|
+
|
|
24
|
+
- **Antes:** <captura/salida/ejecución de test que falla>
|
|
25
|
+
**Después:** <captura/salida/ejecución de test que pasa>
|
|
26
|
+
|
|
27
|
+
## Peligro de fusión
|
|
28
|
+
|
|
29
|
+
**Puerta:** <de un sentido o de dos sentidos>
|
|
30
|
+
|
|
31
|
+
<opcional: descripción>
|
|
32
|
+
|
|
33
|
+
**Radio de impacto:** <descripción en una palabra>
|
|
34
|
+
|
|
35
|
+
<opcional: posibles consecuencias de fusionar>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Secciones
|
|
39
|
+
|
|
40
|
+
Sáltate los preámbulos y deja la prosa breve. Usa el lenguaje del dominio del usuario que está en `GLOSSARY.md`.
|
|
41
|
+
|
|
42
|
+
### Resumen
|
|
43
|
+
|
|
44
|
+
Elige la vista más pequeña que deje clara la idea clave.
|
|
45
|
+
|
|
46
|
+
- Muestra una lógica o un algoritmo como pseudocódigo:
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
on(save)
|
|
50
|
+
if content is unchanged
|
|
51
|
+
return cached result
|
|
52
|
+
write new content
|
|
53
|
+
return fresh result
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
- Muestra el flujo de control en ejecución como un árbol de llamadas:
|
|
57
|
+
|
|
58
|
+
```text
|
|
59
|
+
submitForm
|
|
60
|
+
createSession
|
|
61
|
+
persistPrompt
|
|
62
|
+
launchAgent
|
|
63
|
+
navigateToSession
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
- Muestra la estructura de la UI como un árbol de componentes, con el estado y los límites de módulo que importan:
|
|
67
|
+
|
|
68
|
+
```text
|
|
69
|
+
<SessionPage> (apps/example/src/routes/session.tsx)
|
|
70
|
+
useSessionEvents()
|
|
71
|
+
<SessionToolbar>
|
|
72
|
+
<RunSkillButton> (packages/ui)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
- Muestra de qué se encarga cada fichero, o un refactor amplio, como un árbol de ficheros poco profundo:
|
|
76
|
+
|
|
77
|
+
```text
|
|
78
|
+
src/
|
|
79
|
+
├── commands/ # interpreta las acciones del usuario
|
|
80
|
+
├── sessions/ # guarda el estado de la sesión
|
|
81
|
+
└── transport/ # envía las peticiones a la API
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
- Muestra la interacción entre componentes, el flujo de control o el flujo de datos con Mermaid:
|
|
85
|
+
|
|
86
|
+
```mermaid
|
|
87
|
+
sequenceDiagram
|
|
88
|
+
participant User
|
|
89
|
+
participant UI
|
|
90
|
+
participant Daemon
|
|
91
|
+
User->>UI: choose command
|
|
92
|
+
UI->>Daemon: send expanded prompt
|
|
93
|
+
Daemon-->>UI: stream result
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
- Usa `diff` cuando lo importante es qué cambia y la forma de alrededor ya existe. Ajusta la forma del diff al tema.
|
|
97
|
+
|
|
98
|
+
Para un cambio de componente:
|
|
99
|
+
|
|
100
|
+
```diff
|
|
101
|
+
<SessionPage>
|
|
102
|
+
useSessionEvents()
|
|
103
|
+
<SessionToolbar>
|
|
104
|
+
+ <RunSkillButton />
|
|
105
|
+
<SessionTimeline>
|
|
106
|
+
+ <SkillResultCard />
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Para un cambio en la disposición de ficheros:
|
|
110
|
+
|
|
111
|
+
```diff
|
|
112
|
+
src/
|
|
113
|
+
├── commands/
|
|
114
|
+
+│ └── show-me.ts # expande el slash command
|
|
115
|
+
├── sessions/
|
|
116
|
+
-└── transport.ts
|
|
117
|
+
+└── transport/
|
|
118
|
+
+ ├── client.ts
|
|
119
|
+
+ └── stream.ts
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Para un cambio en un árbol o pila de llamadas:
|
|
123
|
+
|
|
124
|
+
```diff
|
|
125
|
+
submitForm
|
|
126
|
+
createSession
|
|
127
|
+
persistPrompt
|
|
128
|
+
+ expandSkillMention
|
|
129
|
+
launchAgent
|
|
130
|
+
- navigateToSession
|
|
131
|
+
+ navigateToSession
|
|
132
|
+
+ subscribeToEvents
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Para un cambio de estado o de flujo de control:
|
|
136
|
+
|
|
137
|
+
```diff
|
|
138
|
+
on(save)
|
|
139
|
+
- write content
|
|
140
|
+
+ if content is unchanged
|
|
141
|
+
+ return cached result
|
|
142
|
+
+ write new content
|
|
143
|
+
+ invalidate cache
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
- Muestra el bloque entero cuando casi todo es nuevo, cuando omitir contexto escondería de quién es algo o en qué orden va, o cuando el usuario necesita una forma objetivo que pueda copiar:
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
function expandSkill(command: string): string {
|
|
150
|
+
const skillName = command.slice(1);
|
|
151
|
+
return `use the ${skillName} skill`;
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
#### Guía
|
|
156
|
+
|
|
157
|
+
Pon cada visual junto al texto corto al que apoya. Quédate solo con las llamadas, ficheros, props, estados y límites que hacen falta para responder a la pregunta actual del usuario, o a las opciones para resolver el punto que se está discutiendo.
|
|
158
|
+
|
|
159
|
+
Puedes usar uno de estos, puedes usar varios; es poco probable que uses todos. Usa tu criterio y no abrumes al usuario.
|
|
160
|
+
|
|
161
|
+
### Evidencia
|
|
162
|
+
|
|
163
|
+
Pruebas concretas de que el cambio funciona. Muestra un antes y un después.
|
|
164
|
+
|
|
165
|
+
Las capturas de pantalla son lo mejor, cuando el entorno lo permite y el cambio es visual.
|
|
166
|
+
|
|
167
|
+
La evidencia basada en ejecución va justo detrás: resultados de tests, salida de consola. Muestra el test exacto que antes fallaba y ahora pasa, en pseudocódigo.
|
|
168
|
+
|
|
169
|
+
### Peligro de fusión
|
|
170
|
+
|
|
171
|
+
Di si es una puerta de un sentido o de dos sentidos. Por una puerta de dos sentidos se puede volver atrás; por una de un sentido, no. Una PR que es barata de revertir tiene menos riesgo. Los cambios que implican acciones destructivas o decisiones difíciles de revertir son puertas de un sentido.
|
|
172
|
+
|
|
173
|
+
El radio de impacto es el alcance posible de los efectos que introduce la PR. Considera todas las posibilidades. Ejemplos: saltos de layout, roturas para quien consume el código, el comportamiento en móvil, etc.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
skill: pr
|
|
2
|
+
|
|
3
|
+
should_trigger:
|
|
4
|
+
- prompt: "Escribe el cuerpo de la PR de esta rama."
|
|
5
|
+
why: "Disparador directo de pr."
|
|
6
|
+
- prompt: "Abre la PR y explica el cambio con un diagrama y el antes y después."
|
|
7
|
+
why: "Disparador directo de pr: resumen visual y evidencia."
|
|
8
|
+
- prompt: "Write the PR description for these changes."
|
|
9
|
+
why: "Disparador directo de pr."
|
|
10
|
+
- prompt: "Prepara la descripción del pull request con el riesgo de fusionarlo."
|
|
11
|
+
why: "Disparador directo de pr: peligro de fusión y radio de impacto."
|
|
12
|
+
- prompt: "¿Qué pongo en la PR para que se revise rápido?"
|
|
13
|
+
why: "Disparador directo de pr."
|
|
14
|
+
|
|
15
|
+
should_not_trigger:
|
|
16
|
+
- prompt: "Revisa el código de esta PR."
|
|
17
|
+
route_to: "code-review"
|
|
18
|
+
why: "Revisar el código es el trabajo de code-review, no de pr."
|
|
19
|
+
- prompt: "Haz el commit de estos cambios con un buen mensaje."
|
|
20
|
+
route_to: "git-workflow"
|
|
21
|
+
why: "El mensaje de commit es el trabajo de git-workflow, no de pr."
|
|
22
|
+
- prompt: "Implementa la spec entera."
|
|
23
|
+
route_to: "implement-spec"
|
|
24
|
+
why: "Implementar la spec es el trabajo de implement-spec, no de pr."
|
|
25
|
+
- prompt: "Escribe la spec de esta funcionalidad antes de tocar código."
|
|
26
|
+
route_to: "to-spec"
|
|
27
|
+
why: "Redactar la spec es el trabajo de to-spec, no de pr."
|
|
28
|
+
|
|
29
|
+
capability:
|
|
30
|
+
- scenario: "El usuario quiere el cuerpo de una PR que se revise de un vistazo."
|
|
31
|
+
must_include:
|
|
32
|
+
- "Peligro de fusión"
|
|
33
|
+
- "Radio de impacto"
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: retro
|
|
3
|
+
description: "Conduct a retrospective on a coding session: find what in the agent's environment (navigation, checks, standards, steering files, tooling) should change so future runs go better."
|
|
4
|
+
tags: ["retrospective", "session-review", "agent-environment"]
|
|
5
|
+
disable-model-invocation: true
|
|
6
|
+
profiles: [core, ui, full]
|
|
7
|
+
origin: damiangil
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
El usuario ha pedido una **retrospectiva**. Vas a proponer mejoras en el **entorno** del agente de código para que las próximas ejecuciones salgan mejor.
|
|
11
|
+
|
|
12
|
+
## Pasos
|
|
13
|
+
|
|
14
|
+
1. Llama a la herramienta Skill con `writing-for-agents` para tener la guía de estilo.
|
|
15
|
+
|
|
16
|
+
2. Lee las fuentes primarias de la sesión que indique el usuario. Puede que tengas que buscar en los registros de sesión de esta máquina (en Claude Code, `~/.claude/projects/`). Si el usuario no indica ninguna, usa la sesión actual.
|
|
17
|
+
|
|
18
|
+
3. Busca candidatos a mejora en estas categorías:
|
|
19
|
+
|
|
20
|
+
- **Navegación**: ¿le costó al agente encontrar los ficheros correctos? ¿Hay dependencias ocultas entre ficheros? ¿Un **puntero de navegación** lo pondría más fácil? _Úsalo cuando_ la sesión tardó mucho en encontrar una información.
|
|
21
|
+
- **Comprobaciones automáticas**: ¿hay comprobaciones automáticas que pillarían los errores que cometió el agente? Linting, tipos, tests, linters del sistema de ficheros. Lee primero el comando de comprobación del propio repo (los scripts `lint`/`check` de su `package.json` o de su herramienta de build, su workflow de CI), para que el hallazgo sea una comprobación que ya existe pero está sin conectar o rota en silencio, y no una reinvención. Un repo sin ninguna **barrera** (ni hook de pre-commit ni job de CI que ejecute su lint/typecheck/test) es en sí un hallazgo: un repo sin lint es una oportunidad perdida permanente, no un punto de partida neutro. _Úsalo cuando_ el agente cometió un error que una comprobación automática habría pillado, o el repo no tiene ninguna barrera.
|
|
22
|
+
- **Estándares de código**: ¿hay que darle al **agente revisor** una regla nueva que haga cumplir? ¿Hay que quitar o aclarar una regla existente? Clasifica primero la infracción: una **mecánica** (un patrón sintáctico fijo, una API prohibida, una forma de import, una regla de ubicación de ficheros) lleva una comprobación determinista, sin más: una regla propia en el linter del repo, un hook de pre-commit nuevo o un job de CI nuevo, lo que salga más barato según el lenguaje y la barrera que ya tenga el repo. Por defecto, construye la comprobación antes que escribir la regla. Reserva `CODING_STANDARDS.md` para las **decisiones de criterio** de verdad (coherencia entre ficheros, "encaja con el estilo de alrededor", cualquier cosa que ninguna barrera pueda sustituir). _Úsalo cuando_ el agente revisor no pilló un error.
|
|
23
|
+
- **AGENTS.md / CLAUDE.md global**: ¿hay instrucciones de dirección que deberían pasar a los estándares de código (o a comprobaciones automáticas)? _Úsalo cuando_ el fichero es especialmente grande, en el repo O en el ámbito global del usuario.
|
|
24
|
+
- **Economía de herramientas**: ¿hizo el agente llamadas a herramientas caras que se podrían simplificar? ¿Hay alguna herramienta propia (CLIs, MCPs) que gaste muchos tokens? _Úsalo cuando_ el agente hizo una llamada cara.
|
|
25
|
+
- **Instrucciones sin efecto**: busca instrucciones en los ficheros de dirección que no cambian el comportamiento del agente. _Úsalo cuando_ los ficheros de dirección son grandes y difíciles de manejar.
|
|
26
|
+
- **Acceso a la información**: busca formas de darle al agente más acceso a la información. Volcar los logs del servidor de desarrollo, acceso de solo lectura a servicios de terceros. _Úsalo cuando_ el agente no tenía una información clave.
|
|
27
|
+
|
|
28
|
+
4. Presenta estos candidatos al usuario, ordenados por gravedad. Tú no aplicas nada: el usuario decide qué se cambia.
|
|
29
|
+
|
|
30
|
+
## Referencia
|
|
31
|
+
|
|
32
|
+
### Implementación frente a revisión
|
|
33
|
+
|
|
34
|
+
Recuerda que todo el trabajo pasa por dos fases: implementación y revisión. El agente que implementa es el que más **presión de contexto** tiene: se encarga de explorar, escribir el código y depurar los fallos.
|
|
35
|
+
|
|
36
|
+
El agente revisor es el que menos presión de contexto tiene: recibe un diff, así que no tiene que explorar. Muchas veces no necesita escribir código ni depurar.
|
|
37
|
+
|
|
38
|
+
Eso significa que el agente revisor debe ser el responsable de hacer cumplir los estándares de código, no el que implementa.
|
|
39
|
+
|
|
40
|
+
### Ficheros
|
|
41
|
+
|
|
42
|
+
Tienes acceso a varios ficheros en el repo:
|
|
43
|
+
|
|
44
|
+
- `CLAUDE.md`/`AGENTS.md`: estos ficheros entran en la ventana de contexto de cualquier agente que trabaje en el repo. Hay que usarlos con muchísima moderación, normalmente solo para **punteros de navegación** a otros ficheros.
|
|
45
|
+
- `CODING_STANDARDS.md`: este fichero se lee durante la revisión, no durante la implementación. Añade **punteros de navegación** a carpetas de docs si el fichero de estándares pasa de 1.000 líneas.
|
|
46
|
+
- Docs: usa los docs como ficheros de referencia, señalados desde otros ficheros. Busca docs existentes antes de escribir otros nuevos.
|
|
47
|
+
- Skills: usa skills para docs (porque su descripción entra en la ventana de contexto del agente) o para comandos que invoca el usuario. Sigue los consejos de la skill `writing-for-agents`.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
skill: retro
|
|
2
|
+
|
|
3
|
+
should_trigger:
|
|
4
|
+
- prompt: "Haz una retrospectiva de esta sesión."
|
|
5
|
+
why: "Disparador directo de retro."
|
|
6
|
+
- prompt: "Revisa la conversación de ayer y dime qué falló en el entorno del agente."
|
|
7
|
+
why: "Disparador directo de retro: leer sesiones pasadas."
|
|
8
|
+
- prompt: "Run a retro on the last coding session."
|
|
9
|
+
why: "Disparador directo de retro."
|
|
10
|
+
- prompt: "¿Qué hooks o comprobaciones habrían evitado los errores de esta sesión?"
|
|
11
|
+
why: "Disparador directo de retro: comprobaciones automáticas."
|
|
12
|
+
- prompt: "Audita la sesión y propón mejoras para el CLAUDE.md y las herramientas."
|
|
13
|
+
why: "Disparador directo de retro."
|
|
14
|
+
|
|
15
|
+
should_not_trigger:
|
|
16
|
+
- prompt: "Revisa el código de esta rama."
|
|
17
|
+
route_to: "code-review"
|
|
18
|
+
why: "Revisar código es el trabajo de code-review, no de retro."
|
|
19
|
+
- prompt: "Resume la conversación para que otro agente siga."
|
|
20
|
+
route_to: "handoff"
|
|
21
|
+
why: "El traspaso de la sesión es el trabajo de handoff, no de retro."
|
|
22
|
+
- prompt: "Escribe una skill nueva para esto."
|
|
23
|
+
route_to: "writing-for-agents"
|
|
24
|
+
why: "Escribir documentos para agentes es el trabajo de writing-for-agents, no de retro."
|
|
25
|
+
- prompt: "Escribe el cuerpo de la PR de esta rama."
|
|
26
|
+
route_to: "pr"
|
|
27
|
+
why: "El cuerpo de la PR es el trabajo de pr, no de retro."
|
|
28
|
+
|
|
29
|
+
capability:
|
|
30
|
+
- scenario: "El usuario quiere saber qué cambiar en el entorno del agente tras una sesión."
|
|
31
|
+
must_include:
|
|
32
|
+
- "gravedad"
|
package/skills/suggest/SKILL.md
CHANGED
|
@@ -56,7 +56,7 @@ pairs with test-driven development and a verification pass before anything is ca
|
|
|
56
56
|
|
|
57
57
|
Exact skill names and order: `grill-with-docs` or `wayfinder` -> `superpowers:brainstorming` or
|
|
58
58
|
`to-spec` (and `write-adr` for decisions) -> `superpowers:writing-plans` ->
|
|
59
|
-
`superpowers:executing-plans` (
|
|
59
|
+
`superpowers:executing-plans` (big spec: `to-tickets` -> `implement-spec`) -> `code-review` -> `pr`.
|
|
60
60
|
On Claude Code this rule also arrives as a per-turn hook; the brevity here is deduplication,
|
|
61
61
|
not relaxation.
|
|
62
62
|
|
|
@@ -7,4 +7,4 @@ profiles: [core, ui, full]
|
|
|
7
7
|
origin: damiangil
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
-
Espera, no entiendo a dónde has llegado con esto. Vuelve a plantearlo: dame algo de contexto, habla en Inglés Técnico Simplificado ASD-STE100 (aplícalo igual en español: frases cortas, una idea por frase, vocabulario consistente), y usa el lenguaje ubicuo de `
|
|
10
|
+
Espera, no entiendo a dónde has llegado con esto. Vuelve a plantearlo: dame algo de contexto, habla en Inglés Técnico Simplificado ASD-STE100 (aplícalo igual en español: frases cortas, una idea por frase, vocabulario consistente), y usa el lenguaje ubicuo de `GLOSSARY.md` (sigue `GLOSSARY-MAP.md` hasta el correcto si el repo tiene más de uno).
|
package/targets/_md-block.js
CHANGED
|
@@ -21,7 +21,7 @@ export function writeSkill(id, fromDir, toPath) {
|
|
|
21
21
|
export function wireHook(paths, sourceMd, policy = {}) {
|
|
22
22
|
const full = stripFrontmatter(readFileSync(sourceMd, 'utf8'));
|
|
23
23
|
const body = policy.codeHooks === false
|
|
24
|
-
? `# rsc-suggest — always-on operations layer\n\nRead \`docs/wiki/harness/user-profile.md\` before acting. If the user wants to build, add or change something, route it through \`grill-with-docs\` or \`wayfinder\`, then write the spec with \`superpowers:brainstorming\` or \`to-spec\` (decisions go to \`write-adr\`) before any code, then build it with \`superpowers:writing-plans\` (or \`to-tickets\` then \`implement\`
|
|
24
|
+
? `# rsc-suggest — always-on operations layer\n\nRead \`docs/wiki/harness/user-profile.md\` before acting. If the user wants to build, add or change something, route it through \`grill-with-docs\` or \`wayfinder\`, then write the spec with \`superpowers:brainstorming\` or \`to-spec\` (decisions go to \`write-adr\`) before any code, then build it with \`superpowers:writing-plans\` (or \`to-tickets\` then \`implement-spec\` for a big spec or one that came out of \`wayfinder\`), and review it with \`code-review\` before merging, writing the PR body with \`pr\` (si toca pantalla, \`flujo-interfaz\` dice que skill de diseno manda) — unless it is a one-line change, typo, or config bump, which skips the chain. Use \`orient\` to keep the user situated and \`suggest\` to offer a missing skill only when the current task needs it. Close with the configured orientation block.\n`
|
|
25
25
|
: full;
|
|
26
26
|
const block = `${MARK_START}\n${body}\n${MARK_END}`;
|
|
27
27
|
let doc = existsSync(paths.hookTarget) ? readFileSync(paths.hookTarget, 'utf8') : '';
|
package/targets/agents.js
CHANGED
|
@@ -68,7 +68,15 @@ ${REFUTER_FOUR_INPUTS}
|
|
|
68
68
|
3. Did you inspect the relevant **caller, import, and relevant test**?
|
|
69
69
|
4. Can the severity survive the **existing guards** you verified?
|
|
70
70
|
|
|
71
|
-
If an answer is no, lower the severity or omit the finding. Every **
|
|
71
|
+
If an answer is no, lower the severity or omit the finding. Every **blocker or should-fix** needs the line and failure mode in the report. **Zero findings with an attack list is valid.**
|
|
72
|
+
|
|
73
|
+
**Report findings in one shape, so the panel can be consolidated** — a fenced ${'`json findings`'} block holding an array (empty when you found nothing):
|
|
74
|
+
|
|
75
|
+
${'```'}json findings
|
|
76
|
+
[{ "lens": "<your lens>", "severity": "blocker|should-fix|nit|question", "file": "path/from/repo/root", "line": 42, "claim": "one sentence: what is wrong", "failure_scenario": "concrete input + state -> wrong result", "evidence": "the exact changed line, quoted" }]
|
|
77
|
+
${'```'}
|
|
78
|
+
|
|
79
|
+
${'`rsc review consolidate`'} merges what several lenses reported about the same place and sends each unique blocker or should-fix to ${'`finding-verifier`'}, whose job is to prove it false. A blocker or should-fix with no ${'`file`'}, ${'`line`'} or ${'`failure_scenario`'} is downgraded to a question there, mechanically.
|
|
72
80
|
|
|
73
81
|
**Common false positives to reject:** an equivalent mutant with no diverging input; a documented dummy value that never reaches a sink; a deliberate boundary already enforced by a caller; generated/vendor code outside the change; style preference presented as correctness; and a theoretical race with no shared state or overlapping lifetime.
|
|
74
82
|
|
|
@@ -126,8 +134,36 @@ ${REFUTER_CONTRACT}
|
|
|
126
134
|
|
|
127
135
|
**Check the mapping both ways:** every acceptance criterion needs a falsification procedure that can be made to fail, and every test should trace to something someone asked for.`,
|
|
128
136
|
},
|
|
137
|
+
// The second half of ECC's orch-review, adapted: after `rsc review consolidate` collapses the panel's
|
|
138
|
+
// duplicates, every unique blocker/should-fix gets ONE more fresh context whose default is that the
|
|
139
|
+
// finding is false. A refuter attacks the diff; the verifier attacks the finding. Same asymmetry,
|
|
140
|
+
// pointed the other way — which is what stops an exaggerated lens from reaching the author as a blocker.
|
|
141
|
+
{
|
|
142
|
+
name: 'finding-verifier',
|
|
143
|
+
desc: 'Adversarial verifier of review findings: takes ONE consolidated finding and tries to prove it false against the code. Default stance: the finding is false; only a reproduced or traced failure is CONFIRMED. Fresh context, edits nothing.',
|
|
144
|
+
body: `You are the **finding verifier** for this project. ${'`code-review`'} hands you **one** finding that survived ${'`rsc review consolidate`'} — a blocker or should-fix some refuter lens reported against a diff. Your job is to **prove it false**. Your starting position is that it is false; the finding has to beat you.
|
|
145
|
+
|
|
146
|
+
**Your inputs:** the finding (location, claim, failure scenario, evidence) plus the same four the lenses had —
|
|
147
|
+
|
|
148
|
+
${REFUTER_FOUR_INPUTS}
|
|
149
|
+
|
|
150
|
+
**You do NOT get** the refuter's reasoning or the builder's defence. Judge the claim against the code, not against either story.
|
|
151
|
+
|
|
152
|
+
**How to try to kill it:**
|
|
153
|
+
1. Read the cited line **and** its callers, guards and tests — the check that makes it impossible often lives two functions up, or in a caller that never passes that input.
|
|
154
|
+
2. Try the failure scenario for real when you can: run the test, execute the command, or write a throwaway check in a temp directory outside the repo. An observed failure outranks any argument.
|
|
155
|
+
3. Ask whether the change **caused** it. A defect that was already there is real but does not block this delivery.
|
|
156
|
+
|
|
157
|
+
**Verdict — exactly one, with its proof:**
|
|
158
|
+
- **CONFIRMED** — you reproduced it, or traced a reachable path from a concrete input to the wrong result. Quote the output or the path. Only this verdict may block.
|
|
159
|
+
- **REFUTED** — you found what makes it impossible (the guard, the caller, the type, the test). Cite it with file and line.
|
|
160
|
+
- **UNPROVEN** — neither: it becomes a question for the human, and questions never block.
|
|
161
|
+
|
|
162
|
+
Also state whether the severity holds. A CONFIRMED finding whose blast radius is narrow is a should-fix, not a blocker — say so. You fix nothing and edit no file: your output is the verdict, the proof, and the commands you ran.`,
|
|
163
|
+
},
|
|
129
164
|
];
|
|
130
165
|
|
|
166
|
+
|
|
131
167
|
export const BASE_AGENT_NAMES = Object.freeze(AGENTS.map((agent) => agent.name));
|
|
132
168
|
export { stackAgents, stackAgentNames, validateAgentCatalog };
|
|
133
169
|
export const allAgentNames = () => [...BASE_AGENT_NAMES, ...stackAgentNames()];
|
package/targets/claude.js
CHANGED
|
@@ -182,19 +182,6 @@ export function wireHook(paths, sourceMd, policy = {}) {
|
|
|
182
182
|
);
|
|
183
183
|
settings.hooks.PreToolUse.push({ matcher: SHELL_TOOLS, hooks: [{ type: 'command', command: sgCmd }] });
|
|
184
184
|
|
|
185
|
-
// Danger guard: a PreToolUse(Bash) hook that DENIES irreversible foot-gun commands
|
|
186
|
-
// (rm -rf, git push --force, DROP/TRUNCATE, DELETE/UPDATE without WHERE, dd to /dev,
|
|
187
|
-
// curl|bash, …) when the user-profile says the user is NON-technical (default-safe
|
|
188
|
-
// when no profile exists yet; never guards a fully `technical` user). Materialized +
|
|
189
|
-
// node-run (Windows-safe), idempotent, fail-open, opt-out via .rsc/.no-danger-guard.
|
|
190
|
-
const dgDest = join(paths.projectRoot, '.rsc', 'danger-guard.mjs');
|
|
191
|
-
copyFileSync(join(HERE, 'danger-guard.mjs'), dgDest);
|
|
192
|
-
const dgCmd = viaBootstrap('guard', at('.rsc', 'danger-guard.mjs'), `"${P}"`);
|
|
193
|
-
settings.hooks.PreToolUse = settings.hooks.PreToolUse.filter(
|
|
194
|
-
(e) => !hookWiringOf(e).includes('.rsc/danger-guard.'),
|
|
195
|
-
);
|
|
196
|
-
settings.hooks.PreToolUse.push({ matcher: SHELL_TOOLS, hooks: [{ type: 'command', command: dgCmd }] });
|
|
197
|
-
|
|
198
185
|
// Gitmoji guard: a PreToolUse(Bash) hook that DENIES a `git commit` whose message
|
|
199
186
|
// carries no gitmoji (gitmoji.dev) in front of the Conventional Commits header. The
|
|
200
187
|
// convention lives in `git-workflow`; prose asks, this hook decides — at the one
|
|
@@ -226,18 +213,18 @@ export function wireHook(paths, sourceMd, policy = {}) {
|
|
|
226
213
|
(e) => !hookWiringOf(e).includes('.rsc/userprompt-gate.'),
|
|
227
214
|
);
|
|
228
215
|
settings.hooks.UserPromptSubmit.push({ hooks: [{ type: 'command', command: fgCmd }] });
|
|
229
|
-
written.push(sgDest,
|
|
216
|
+
written.push(sgDest, gmDest, fgDest, join(paths.projectRoot, '.rsc', 'sello.mjs'));
|
|
230
217
|
} else {
|
|
231
218
|
for (const event of ['PreToolUse', 'UserPromptSubmit']) {
|
|
232
219
|
if (!settings.hooks[event]) continue;
|
|
233
220
|
settings.hooks[event] = settings.hooks[event].filter((entry) => {
|
|
234
221
|
const body = hookWiringOf(entry);
|
|
235
|
-
return !body.includes('.rsc/ship-guard.') &&
|
|
222
|
+
return !body.includes('.rsc/ship-guard.') &&
|
|
236
223
|
!body.includes('.rsc/gitmoji-guard.') && !body.includes('.rsc/userprompt-gate.');
|
|
237
224
|
});
|
|
238
225
|
if (!settings.hooks[event].length) delete settings.hooks[event];
|
|
239
226
|
}
|
|
240
|
-
for (const name of ['ship-guard.mjs', '
|
|
227
|
+
for (const name of ['ship-guard.mjs', 'gitmoji-guard.mjs', 'userprompt-gate.mjs', 'sello.mjs']) {
|
|
241
228
|
rmSync(join(paths.projectRoot, '.rsc', name), { force: true });
|
|
242
229
|
}
|
|
243
230
|
written.push(operationsSuggest);
|
|
@@ -248,6 +235,28 @@ export function wireHook(paths, sourceMd, policy = {}) {
|
|
|
248
235
|
for (const id of policy.plugins) settings.enabledPlugins[id] = true;
|
|
249
236
|
}
|
|
250
237
|
|
|
238
|
+
// Danger guard: its own decision, not part of the code-hook block above (#273). `init` promises it
|
|
239
|
+
// to non-technical and mixed users whatever the project is, and a plan that predates the field is
|
|
240
|
+
// read as "yes": a non-technical operations install gets the guard it was missing on its next sync,
|
|
241
|
+
// and a technical user gets a guard that stands down at runtime.
|
|
242
|
+
settings.hooks.PreToolUse ||= [];
|
|
243
|
+
settings.hooks.PreToolUse = settings.hooks.PreToolUse.filter((e) => !hookWiringOf(e).includes('.rsc/danger-guard.'));
|
|
244
|
+
if (policy.dangerGuard !== false) {
|
|
245
|
+
// Danger guard: a PreToolUse(Bash) hook that DENIES irreversible foot-gun commands
|
|
246
|
+
// (rm -rf, git push --force, DROP/TRUNCATE, DELETE/UPDATE without WHERE, dd to /dev,
|
|
247
|
+
// curl|bash, …) when the user-profile says the user is NON-technical (default-safe
|
|
248
|
+
// when no profile exists yet; never guards a fully `technical` user). Materialized +
|
|
249
|
+
// node-run (Windows-safe), idempotent, fail-open, opt-out via .rsc/.no-danger-guard.
|
|
250
|
+
const dgDest = join(paths.projectRoot, '.rsc', 'danger-guard.mjs');
|
|
251
|
+
copyFileSync(join(HERE, 'danger-guard.mjs'), dgDest);
|
|
252
|
+
const dgCmd = viaBootstrap('guard', at('.rsc', 'danger-guard.mjs'), `"${P}"`);
|
|
253
|
+
settings.hooks.PreToolUse.push({ matcher: SHELL_TOOLS, hooks: [{ type: 'command', command: dgCmd }] });
|
|
254
|
+
written.push(dgDest);
|
|
255
|
+
} else {
|
|
256
|
+
rmSync(join(paths.projectRoot, '.rsc', 'danger-guard.mjs'), { force: true });
|
|
257
|
+
}
|
|
258
|
+
if (!settings.hooks.PreToolUse.length) delete settings.hooks.PreToolUse;
|
|
259
|
+
|
|
251
260
|
mkdirSync(dirname(file), { recursive: true });
|
|
252
261
|
writeFileSync(file, JSON.stringify(settings, null, 2) + '\n');
|
|
253
262
|
|
package/targets/hook-once.mjs
CHANGED
|
@@ -90,6 +90,7 @@ export function claimOnce(key, { dir = markerDir(), windowMs = 30_000, now = Dat
|
|
|
90
90
|
// definition: the hook emits it and `rsc doctor` measures it from the same constant. Anything
|
|
91
91
|
// that reports a byte count for text it does not own drifts the moment the text changes.
|
|
92
92
|
export const WORKFLOW_GATE_TEXT = `===== workflow gate · metodologia (maxima prioridad) =====
|
|
93
|
+
Responde en el idioma en que escribe la persona, no en el de este texto.
|
|
93
94
|
Si el usuario pide construir, anadir o cambiar algo (en cualquier idioma, se juzga la intencion),
|
|
94
95
|
sigue esta cadena ANTES de escribir codigo. Ninguna skill escribe codigo de una feature hasta que
|
|
95
96
|
el que esta claro y el usuario ha dado el visto bueno.
|
|
@@ -100,10 +101,12 @@ el que esta claro y el usuario ha dado el visto bueno.
|
|
|
100
101
|
Cada decision dificil de revertir se registra con \`write-adr\`, en docs/adr/.
|
|
101
102
|
2. Escribir la spec: \`to-spec\`, o \`superpowers:brainstorming\` para cerrarla por secciones.
|
|
102
103
|
Sin spec escrita no se programa.
|
|
103
|
-
3. Construir: \`superpowers:writing-plans\` -> \`superpowers:executing-plans\`.
|
|
104
|
-
salio de \`wayfinder\`
|
|
104
|
+
3. Construir: \`superpowers:writing-plans\` -> \`superpowers:executing-plans\`. Spec grande o que
|
|
105
|
+
salio de \`wayfinder\` -> \`to-tickets\` -> \`implement-spec\` (subagentes en paralelo sobre el
|
|
106
|
+
grafo de tickets); un solo ticket suelto -> \`implement\`.
|
|
105
107
|
Si la tarea toca pantalla, invoca ademas \`flujo-interfaz\`: dice que skill de diseno manda.
|
|
106
|
-
4. Antes de fusionar: \`code-review\` (estandares del repo + lo que pedia la spec)
|
|
108
|
+
4. Antes de fusionar: \`code-review\` (estandares del repo + lo que pedia la spec), y el cuerpo
|
|
109
|
+
de la PR con \`pr\` (resumen visual, evidencia antes/despues, peligro de fusion).
|
|
107
110
|
Siempre: \`superpowers:test-driven-development\` y \`superpowers:verification-before-completion\`
|
|
108
111
|
antes de dar nada por terminado. Los bugs entran por \`superpowers:systematic-debugging\`.
|
|
109
112
|
Cambio de una linea, typo o ajuste de configuracion -> hazlo y di que te saltas la cadena.
|
package/targets/memory.js
CHANGED
|
@@ -35,7 +35,7 @@ export function memoryEnabledForProject(cwd = process.cwd()) {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
function git(cwd, args) {
|
|
38
|
-
try { return execFileSync('git', args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim(); } catch { return null; }
|
|
38
|
+
try { return execFileSync('git', args, { windowsHide: true, cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim(); } catch { return null; }
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
function rel(cwd, path) {
|
package/targets/sello.mjs
CHANGED
|
@@ -103,7 +103,7 @@ export function isEnabled(root) {
|
|
|
103
103
|
// The CLI can run from any subdirectory; the guard always runs from the project
|
|
104
104
|
// root. Both must compute the same candidate, so both resolve to the git toplevel.
|
|
105
105
|
export function resolveRoot(root) {
|
|
106
|
-
const r = spawnSync('git', ['-C', root, 'rev-parse', '--show-toplevel'], { encoding: 'utf8' });
|
|
106
|
+
const r = spawnSync('git', ['-C', root, 'rev-parse', '--show-toplevel'], { windowsHide: true, encoding: 'utf8' });
|
|
107
107
|
return r.status === 0 ? (r.stdout || '').trim() || root : root;
|
|
108
108
|
}
|
|
109
109
|
|
|
@@ -211,7 +211,7 @@ export function lensesRequired(tier, cfg = null) {
|
|
|
211
211
|
// name matches no file on disk, so the file would hash as deleted and any later
|
|
212
212
|
// mutation of it would sail through the gate. -z gives raw NUL-separated paths.
|
|
213
213
|
function git(root, ...args) {
|
|
214
|
-
const r = spawnSync('git', ['-C', root, '-c', 'core.quotePath=false', ...args], {
|
|
214
|
+
const r = spawnSync('git', ['-C', root, '-c', 'core.quotePath=false', ...args], { windowsHide: true,
|
|
215
215
|
encoding: 'utf8', maxBuffer: 64 * 1024 * 1024,
|
|
216
216
|
});
|
|
217
217
|
return r.status === 0 ? (r.stdout || '') : null;
|
|
@@ -48,7 +48,7 @@ function cleanId(value, prefix = 'session') {
|
|
|
48
48
|
|
|
49
49
|
function git(cwd, args) {
|
|
50
50
|
try {
|
|
51
|
-
return { ok: true, out: execFileSync('git', args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], maxBuffer: 2 * 1024 * 1024 }).trim() };
|
|
51
|
+
return { ok: true, out: execFileSync('git', args, { windowsHide: true, cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], maxBuffer: 2 * 1024 * 1024 }).trim() };
|
|
52
52
|
} catch {
|
|
53
53
|
return { ok: false, out: '' };
|
|
54
54
|
}
|
|
@@ -255,8 +255,8 @@ if (has('.git')) {
|
|
|
255
255
|
// those make the fingerprint and an unchanged one reuses the last notice. Safe because `reap`
|
|
256
256
|
// re-classifies before it removes anything.
|
|
257
257
|
const trunk = W.resolveTrunk(root);
|
|
258
|
-
const tip = trunk ? spawnSync('git', ['-C', root, 'rev-parse', trunk], { encoding: 'utf8' }).stdout : '';
|
|
259
|
-
const list = spawnSync('git', ['-C', root, 'worktree', 'list', '--porcelain'], { encoding: 'utf8' }).stdout;
|
|
258
|
+
const tip = trunk ? spawnSync('git', ['-C', root, 'rev-parse', trunk], { encoding: 'utf8', windowsHide: true }).stdout : '';
|
|
259
|
+
const list = spawnSync('git', ['-C', root, 'worktree', 'list', '--porcelain'], { encoding: 'utf8', windowsHide: true }).stdout;
|
|
260
260
|
const fingerprint = `${W.isCleanupEnabled(root)}\n${trunk}\n${tip}\n${list}`;
|
|
261
261
|
const cacheFile = join(root, '.rsc', 'worktree-sweep.json');
|
|
262
262
|
let cached = null;
|
package/targets/ship-guard.mjs
CHANGED
|
@@ -67,7 +67,7 @@ const MERGE = /\bgit\s+merge\b/;
|
|
|
67
67
|
if (!TRUNK.test(command) && !MERGE.test(command)) allow();
|
|
68
68
|
|
|
69
69
|
const git = (...args) => {
|
|
70
|
-
const r = spawnSync('git', ['-C', root, ...args], { encoding: 'utf8' });
|
|
70
|
+
const r = spawnSync('git', ['-C', root, ...args], { windowsHide: true, encoding: 'utf8' });
|
|
71
71
|
return r.status === 0 ? (r.stdout || '').trim() : null;
|
|
72
72
|
};
|
|
73
73
|
|
|
@@ -73,7 +73,7 @@ function git(cwd, args) {
|
|
|
73
73
|
// 64 MiB, because node's 1 MiB default kills git mid-write on any worktree with a few thousand
|
|
74
74
|
// stray files and hands back a truncated-but-successful-looking result. Measured: in the 1.0-1.15 MB
|
|
75
75
|
// band the same unchanged worktree flipped between verdicts across runs.
|
|
76
|
-
const r = spawnSync('git', ['-C', cwd, ...args], { encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 });
|
|
76
|
+
const r = spawnSync('git', ['-C', cwd, ...args], { windowsHide: true, encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 });
|
|
77
77
|
// `raw` matters for porcelain output: its leading space is a status code, not padding, and
|
|
78
78
|
// trimming it shifted every path by one character — so the message naming the file about to be
|
|
79
79
|
// lost named a file that does not exist. A refusal has to be true to be actionable (P6).
|