@damiangilgonzalez/harness 1.3.9 → 1.3.11
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 +5 -4
- package/manifest.json +20 -2
- package/package.json +1 -1
- package/scripts/lib/domains.js +1 -1
- package/skills/flujo-interfaz/SKILL.md +87 -0
- package/skills/flujo-interfaz/evals/cases.yaml +32 -0
- package/skills/suggest/SKILL.md +4 -3
- package/targets/_md-block.js +1 -1
- package/targets/claude.js +6 -3
- package/targets/danger-guard.mjs +2 -2
- package/targets/gitmoji-guard.mjs +2 -2
- package/targets/hook-once.mjs +19 -15
- package/targets/session-start.mjs +37 -11
- package/targets/ship-guard.mjs +2 -2
- package/targets/userprompt-gate.mjs +6 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
[](https://www.npmjs.com/package/@damiangilgonzalez/harness)
|
|
10
10
|
[](https://www.npmjs.com/package/@damiangilgonzalez/harness)
|
|
11
|
-
[](#the-catalog)
|
|
12
12
|
[](LICENSE)
|
|
13
13
|
[](https://github.com/ericrisco/rsc-harness/stargazers)
|
|
14
14
|
|
|
@@ -202,7 +202,7 @@ $ rsc onboard
|
|
|
202
202
|
██████╗ ███████╗ ██████╗ ← animated gradient wordmark
|
|
203
203
|
██╔══██╗██╔════╝██╔════╝
|
|
204
204
|
██████╔╝███████╗██║
|
|
205
|
-
|
|
205
|
+
291 skills · one CLI · zero bloat
|
|
206
206
|
|
|
207
207
|
How technical should the conversation be?
|
|
208
208
|
How much accompaniment do you want?
|
|
@@ -241,7 +241,7 @@ rsc add youtube-api remotion-video # …grow a channel, edit with Remotion
|
|
|
241
241
|
rsc add fastapi --target claude,codex # install into several assistants at once
|
|
242
242
|
rsc install --profile minimal # the base: orient + suggest + bro + unslop + show-me + eli5 + harness + init + teach
|
|
243
243
|
rsc install --profile core # floor + the full SDD workflow
|
|
244
|
-
rsc install --profile full # everything (all
|
|
244
|
+
rsc install --profile full # everything (all 291 skills)
|
|
245
245
|
rsc install --profile full --without go
|
|
246
246
|
rsc consult "I want to launch a SaaS" # recommend only, no install
|
|
247
247
|
rsc registry refresh # write .rsc/skill-registry.{json,md}
|
|
@@ -383,7 +383,7 @@ just asks in plain language.
|
|
|
383
383
|
|
|
384
384
|
## The catalog
|
|
385
385
|
|
|
386
|
-
|
|
386
|
+
291 skills, grouped by what you're trying to do. Click any skill to read its
|
|
387
387
|
`SKILL.md`. It fires on its own when a task matches.
|
|
388
388
|
|
|
389
389
|
### 🧭 Core & control plane
|
|
@@ -572,6 +572,7 @@ When the work touches a screen. Sixteen overlapping skills; pick the one that ma
|
|
|
572
572
|
|
|
573
573
|
| Skill | What it does |
|
|
574
574
|
|---|---|
|
|
575
|
+
| [flujo-interfaz](skills/flujo-interfaz/) | The router: which design skill leads and in what order the rest follow. Start here when the work touches a screen. |
|
|
575
576
|
| [ingeniero-diseno-web](skills/ingeniero-diseno-web/) | Runs the whole job: art direction, a declared design system, checkpoints and a scored critique. |
|
|
576
577
|
| [diseno-landing](skills/diseno-landing/) | A conversion landing end to end: structure, copy and a strict visual system. |
|
|
577
578
|
| [sitios-calidad-premio](skills/sitios-calidad-premio/) | Cinematic sites: GSAP, one smooth-scroll engine, Three.js only with a purpose. |
|
package/manifest.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.3.
|
|
2
|
+
"version": "1.3.11",
|
|
3
3
|
"counts": {
|
|
4
|
-
"skills":
|
|
4
|
+
"skills": 291,
|
|
5
5
|
"agents": 33,
|
|
6
6
|
"commands": 43,
|
|
7
7
|
"fixedCommands": 10
|
|
@@ -1861,6 +1861,24 @@
|
|
|
1861
1861
|
],
|
|
1862
1862
|
"profiles": []
|
|
1863
1863
|
},
|
|
1864
|
+
{
|
|
1865
|
+
"id": "flujo-interfaz",
|
|
1866
|
+
"description": "Use when work touches a screen — building or reshaping a landing, dashboard, app screen, component or prototype — to pick WHICH design skill leads and in what order the rest follow. Routes: direction first (diseno-landing or ingeniero-diseno-web), then motion, polish, originality and perception. NOT the review itself (that is `revision-interfaz`), NOT how the interface is wired (that is `ui-engineering`).",
|
|
1867
|
+
"tags": [
|
|
1868
|
+
"ui-design",
|
|
1869
|
+
"routing",
|
|
1870
|
+
"workflow"
|
|
1871
|
+
],
|
|
1872
|
+
"recommends": [
|
|
1873
|
+
"ingeniero-diseno-web",
|
|
1874
|
+
"diseno-landing",
|
|
1875
|
+
"revision-interfaz"
|
|
1876
|
+
],
|
|
1877
|
+
"profiles": [
|
|
1878
|
+
"ui",
|
|
1879
|
+
"full"
|
|
1880
|
+
]
|
|
1881
|
+
},
|
|
1864
1882
|
{
|
|
1865
1883
|
"id": "flutter",
|
|
1866
1884
|
"description": "Use when building, structuring, testing or optimizing a Flutter app — feature-first layering, Riverpod 3 or Bloc, typed go_router, freezed models, a dio data layer, Material 3, jank hunting, widget/golden tests. Targets Flutter 3.44 / Dart 3.12. NOT React Native (that is `react-native`), NOT Compose Multiplatform (that is `compose-multiplatform`).",
|
package/package.json
CHANGED
package/scripts/lib/domains.js
CHANGED
|
@@ -28,7 +28,7 @@ export const DOMAINS = [
|
|
|
28
28
|
{ title: 'Design & content craft', ids: ['design-loop', 'design', 'design-dna', 'presentations', 'course-storytelling', 'course-builder', 'technical-writing', 'translation-l10n'] },
|
|
29
29
|
{ title: 'Knowledge & meta', ids: ['knowledge-ops', 'codebase-onboarding', 'research-ops', 'continuous-learning', 'skill-scout', 'context-budget', 'roast-me', 'show-me', 'eli5', 'fable-operator'] },
|
|
30
30
|
{ title: 'Personal catalog — workflow (Spanish)', ids: ['grill-me', 'grill-with-docs', 'grilling', 'wayfinder', 'to-spec', 'to-tickets', 'to-questionnaire', 'implement', 'revision-de-cambios', 'code-review', 'research', 'prototype', 'domain-modeling', 'handoff', 'write-adr', 'wait-what', 'writing-for-agents', 'claude-project-setup', 'muscle-memory', 'teach'] },
|
|
31
|
-
{ title: 'Personal catalog — interface design (Spanish)', ids: ['animar', 'diseno-apple', 'diseno-landing', 'ingeniero-diseno-web', 'leyes-de-percepcion', 'leyes-de-retencion', 'mejor-accesibilidad', 'mejor-colores', 'mejor-layout', 'mejor-redaccion', 'mejor-tipografia', 'mejor-ui', 'sitios-calidad-premio', 'tastemaker', 'video-a-superprompt', 'vocabulario-animacion', 'revision-interfaz'] },
|
|
31
|
+
{ title: 'Personal catalog — interface design (Spanish)', ids: ['flujo-interfaz', 'animar', 'diseno-apple', 'diseno-landing', 'ingeniero-diseno-web', 'leyes-de-percepcion', 'leyes-de-retencion', 'mejor-accesibilidad', 'mejor-colores', 'mejor-layout', 'mejor-redaccion', 'mejor-tipografia', 'mejor-ui', 'sitios-calidad-premio', 'tastemaker', 'video-a-superprompt', 'vocabulario-animacion', 'revision-interfaz'] },
|
|
32
32
|
];
|
|
33
33
|
|
|
34
34
|
export function allDomainIds() {
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flujo-interfaz
|
|
3
|
+
description: "Use when work touches a screen — building or reshaping a landing, dashboard, app screen, component or prototype — to pick WHICH design skill leads and in what order the rest follow. Routes: direction first (diseno-landing or ingeniero-diseno-web), then motion, polish, originality and perception. NOT the review itself (that is `revision-interfaz`), NOT how the interface is wired (that is `ui-engineering`)."
|
|
4
|
+
tags: ["ui-design", "routing", "workflow"]
|
|
5
|
+
profiles: [ui, full]
|
|
6
|
+
recommends: [ingeniero-diseno-web, diseno-landing, revision-interfaz]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Flujo de interfaz — quien manda en cada paso
|
|
10
|
+
|
|
11
|
+
Hay dieciseis skills de diseno y se solapan. Esta skill no disena nada: decide **cual toca** y en
|
|
12
|
+
que orden, para que ninguna pantalla se construya a ojo.
|
|
13
|
+
|
|
14
|
+
**El orden, en una linea:** que dice -> como se ve -> como se mueve -> pulido -> originalidad ->
|
|
15
|
+
psicologia.
|
|
16
|
+
|
|
17
|
+
La direccion la marca una skill, nunca el criterio del agente. Empezar a escribir CSS sin haber
|
|
18
|
+
elegido direccion es el error que esta skill existe para evitar.
|
|
19
|
+
|
|
20
|
+
## Paso 1 · Direccion (obligatorio, elige UNA)
|
|
21
|
+
|
|
22
|
+
| Construyes | Skill | Por que esa |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| Una landing, una web de marketing | `diseno-landing` | Es la unica que habla de negocio: oferta, objeciones, FAQ, reversion de riesgo, SEO. Su Parte B ya trae las reglas visuales. |
|
|
25
|
+
| Cualquier otra cosa: dashboard, pantalla de app, prototipo, presentacion, visualizacion | `ingeniero-diseno-web` | Direccion de arte, design system declarado, 25 recetas ancladas y puntos de control que frenan a confirmar. |
|
|
26
|
+
|
|
27
|
+
Para una landing no uses `ingeniero-diseno-web`: `diseno-landing` ya la cubre entera. Se solapan
|
|
28
|
+
porque son de autores distintos que resolvieron lo mismo.
|
|
29
|
+
|
|
30
|
+
Si el usuario no sabe que aspecto quiere, `ingeniero-diseno-web` propone tres escuelas con
|
|
31
|
+
referencias reales antes de construir nada.
|
|
32
|
+
|
|
33
|
+
## Paso 2 · Espectaculo, solo si toca
|
|
34
|
+
|
|
35
|
+
`sitios-calidad-premio` para un lanzamiento, un portafolio o algo que tiene que impresionar: GSAP,
|
|
36
|
+
un unico motor de scroll suave, Three.js solo con proposito.
|
|
37
|
+
|
|
38
|
+
En un dashboard o una herramienta de uso diario, saltatela: el espectaculo se paga en cada uso.
|
|
39
|
+
|
|
40
|
+
## Paso 3 · Movimiento
|
|
41
|
+
|
|
42
|
+
- `animar` — decide en orden si debe animarse, con que proposito, herramienta, propiedades, curva y
|
|
43
|
+
duracion. Su puerta de frecuencia puede concluir que lo correcto es no animar.
|
|
44
|
+
- `diseno-apple` — solo si hay gestos, arrastre u hojas deslizantes: muelles con fisica real,
|
|
45
|
+
interrumpibilidad, traspaso de velocidad.
|
|
46
|
+
- `vocabulario-animacion` — cuando el usuario describe un efecto y no sabe como se llama.
|
|
47
|
+
- `video-a-superprompt` — cuando aporta un video de referencia.
|
|
48
|
+
|
|
49
|
+
## Paso 4 · Pulido
|
|
50
|
+
|
|
51
|
+
`revision-interfaz` orquesta las seis `mejor-*` (layout, tipografia, colores, accesibilidad, ui,
|
|
52
|
+
redaccion) y consolida un unico informe ordenado por severidad. Invoca una `mejor-*` suelta solo
|
|
53
|
+
para una duda puntual de su terreno.
|
|
54
|
+
|
|
55
|
+
## Paso 5 · Que no parezca de IA
|
|
56
|
+
|
|
57
|
+
`tastemaker` va despues del pulido, porque necesita algo que auditar, y antes que las leyes, porque
|
|
58
|
+
cambia estructura y no solo detalles. Tiene un modo auditar que no toca nada. Necesita Python.
|
|
59
|
+
|
|
60
|
+
## Paso 6 · Psicologia
|
|
61
|
+
|
|
62
|
+
- `leyes-de-percepcion` — que ve el ojo primero y como se agrupan las cosas.
|
|
63
|
+
- `leyes-de-retencion` — por que se abandona un flujo a mitad. Su tabla sintoma -> ley es lo mas
|
|
64
|
+
rentable de las dieciseis.
|
|
65
|
+
|
|
66
|
+
## Como se construye por dentro
|
|
67
|
+
|
|
68
|
+
La arquitectura de la interfaz —limites de componentes, estado en la URL o en el componente,
|
|
69
|
+
estados de carga, vacio y error, formularios, actualizacion optimista— es de `ui-engineering`, no
|
|
70
|
+
de las skills de diseno. Las mecanicas del framework son de `angular`, `react` o `nextjs`.
|
|
71
|
+
|
|
72
|
+
## Atajos
|
|
73
|
+
|
|
74
|
+
| Situacion | Cadena |
|
|
75
|
+
|---|---|
|
|
76
|
+
| Landing nueva | `diseno-landing` -> `animar` -> `revision-interfaz` |
|
|
77
|
+
| Dashboard o pantalla nueva | `ingeniero-diseno-web` -> `revision-interfaz` |
|
|
78
|
+
| Pantalla que parece hecha por IA | `tastemaker` (auditar) -> `leyes-de-percepcion` |
|
|
79
|
+
| Un flujo pierde usuarios a mitad | `leyes-de-retencion` -> `mejor-redaccion` -> `mejor-accesibilidad` |
|
|
80
|
+
| Solo pulir lo que ya hay | `revision-interfaz` |
|
|
81
|
+
|
|
82
|
+
## Donde encaja en la metodologia
|
|
83
|
+
|
|
84
|
+
Esta cadena vive **dentro** del paso de construir, no lo sustituye. El trabajo sigue llegando con
|
|
85
|
+
su spec escrita y su plan, y al cerrar se revisa igual: `revision-de-cambios` (que se ha roto),
|
|
86
|
+
`revision-interfaz` (como esta la pantalla) y `code-review` (si hace lo que pedia la spec), antes
|
|
87
|
+
de fusionar.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
skill: flujo-interfaz
|
|
2
|
+
|
|
3
|
+
should_trigger:
|
|
4
|
+
- prompt: "Vamos a construir la pantalla de detalle de incidencia, por donde empiezo?"
|
|
5
|
+
why: "Trabajo que toca pantalla sin direccion elegida: flujo-interfaz decide que skill manda."
|
|
6
|
+
- prompt: "Tengo dieciseis skills de diseno y no se cual toca para esta pantalla."
|
|
7
|
+
why: "Disparador directo de flujo-interfaz: enrutar entre las skills de diseno."
|
|
8
|
+
- prompt: "Que skill de diseno uso para un dashboard nuevo?"
|
|
9
|
+
why: "La eleccion de direccion entre diseno-landing e ingeniero-diseno-web es de flujo-interfaz."
|
|
10
|
+
- prompt: "Empezamos el frontend de este modulo, cual es el orden de trabajo?"
|
|
11
|
+
why: "Pide el orden de la cadena de interfaz, que es lo que flujo-interfaz define."
|
|
12
|
+
- prompt: "Which design skill should lead this new app screen, and what comes after?"
|
|
13
|
+
why: "Disparador directo de flujo-interfaz en ingles: direccion primero, resto despues."
|
|
14
|
+
|
|
15
|
+
should_not_trigger:
|
|
16
|
+
- prompt: "Revisa esta pantalla y dame un informe ordenado por severidad."
|
|
17
|
+
route_to: "revision-interfaz"
|
|
18
|
+
why: "Revisar la pantalla es el trabajo de revision-interfaz, no de flujo-interfaz, que solo enruta."
|
|
19
|
+
- prompt: "Donde guardo el estado de este formulario, en la URL o en el componente?"
|
|
20
|
+
route_to: "ui-engineering"
|
|
21
|
+
why: "La arquitectura de la interfaz es el trabajo de ui-engineering, no de flujo-interfaz."
|
|
22
|
+
- prompt: "Genera la paleta de colores de la marca desde cero."
|
|
23
|
+
route_to: "mejor-colores"
|
|
24
|
+
why: "Generar la paleta es el trabajo de mejor-colores, no de flujo-interfaz."
|
|
25
|
+
- prompt: "Implementa el endpoint que devuelve las incidencias del tecnico."
|
|
26
|
+
route_to: "implement"
|
|
27
|
+
why: "Un ticket de backend es el trabajo de implement, no de flujo-interfaz."
|
|
28
|
+
|
|
29
|
+
capability:
|
|
30
|
+
- scenario: "El usuario va a construir una pantalla nueva y no sabe que skill de diseno manda."
|
|
31
|
+
must_include:
|
|
32
|
+
- "direccion"
|
package/skills/suggest/SKILL.md
CHANGED
|
@@ -36,7 +36,7 @@ The shape of the chain:
|
|
|
36
36
|
- Once the shape is known, capture it as a spec, and give any call that would be costly to undo
|
|
37
37
|
its own ADR.
|
|
38
38
|
- Build from a written plan. Tickets are the exception: only work that grew out of the
|
|
39
|
-
wayfinding step is broken into them first.
|
|
39
|
+
wayfinding step is broken into them first. A screen also routes through `flujo-interfaz`.
|
|
40
40
|
- Review the finished change against the repo's standards and against the spec before merging.
|
|
41
41
|
|
|
42
42
|
Two exceptions, and say out loud when you take one:
|
|
@@ -57,8 +57,9 @@ pairs with test-driven development and a verification pass before anything is ca
|
|
|
57
57
|
|
|
58
58
|
Exact skill names and order: `grill-with-docs` or `wayfinder` -> `superpowers:brainstorming` or
|
|
59
59
|
`to-spec` (and `write-adr` for decisions) -> `superpowers:writing-plans` ->
|
|
60
|
-
`superpowers:executing-plans` (after `wayfinder`: `to-tickets` -> `implement`) -> `code-review`.
|
|
61
|
-
deduplication,
|
|
60
|
+
`superpowers:executing-plans` (after `wayfinder`: `to-tickets` -> `implement`) -> `code-review`.
|
|
61
|
+
On Claude Code this rule also arrives as a per-turn hook; the brevity here is deduplication,
|
|
62
|
+
not relaxation.
|
|
62
63
|
|
|
63
64
|
---
|
|
64
65
|
|
package/targets/_md-block.js
CHANGED
|
@@ -20,7 +20,7 @@ export function writeSkill(id, fromDir, toPath) {
|
|
|
20
20
|
export function wireHook(paths, sourceMd, policy = {}) {
|
|
21
21
|
const full = stripFrontmatter(readFileSync(sourceMd, 'utf8'));
|
|
22
22
|
const body = policy.codeHooks === false
|
|
23
|
-
? `# 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\` when it came out of \`wayfinder\`), and review it with \`code-review\` before merging — 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`
|
|
23
|
+
? `# 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\` when it came out of \`wayfinder\`), and review it with \`code-review\` before merging (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`
|
|
24
24
|
: full;
|
|
25
25
|
const block = `${MARK_START}\n${body}\n${MARK_END}`;
|
|
26
26
|
let doc = existsSync(paths.hookTarget) ? readFileSync(paths.hookTarget, 'utf8') : '';
|
package/targets/claude.js
CHANGED
|
@@ -4,6 +4,9 @@ import { fileURLToPath } from 'node:url';
|
|
|
4
4
|
import { linkOrCopy } from './index.js';
|
|
5
5
|
|
|
6
6
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
// Shell tools the PreToolUse guards listen on. On Windows the primary shell is the PowerShell tool,
|
|
8
|
+
// so a `Bash`-only matcher let `git push --force` or a commit without gitmoji through unasked.
|
|
9
|
+
const SHELL_TOOLS = 'Bash|PowerShell';
|
|
7
10
|
|
|
8
11
|
export function writeSkill(id, fromDir, toPath) {
|
|
9
12
|
// Migrate away from the legacy nested layout (.claude/skills/rsc/<id>) that
|
|
@@ -142,7 +145,7 @@ export function wireHook(paths, sourceMd, policy = {}) {
|
|
|
142
145
|
settings.hooks.PreToolUse = settings.hooks.PreToolUse.filter(
|
|
143
146
|
(e) => !hookWiringOf(e).includes('.rsc/ship-guard.'),
|
|
144
147
|
);
|
|
145
|
-
settings.hooks.PreToolUse.push({ matcher:
|
|
148
|
+
settings.hooks.PreToolUse.push({ matcher: SHELL_TOOLS, hooks: [{ type: 'command', command: sgCmd }] });
|
|
146
149
|
|
|
147
150
|
// Danger guard: a PreToolUse(Bash) hook that DENIES irreversible foot-gun commands
|
|
148
151
|
// (rm -rf, git push --force, DROP/TRUNCATE, DELETE/UPDATE without WHERE, dd to /dev,
|
|
@@ -155,7 +158,7 @@ export function wireHook(paths, sourceMd, policy = {}) {
|
|
|
155
158
|
settings.hooks.PreToolUse = settings.hooks.PreToolUse.filter(
|
|
156
159
|
(e) => !hookWiringOf(e).includes('.rsc/danger-guard.'),
|
|
157
160
|
);
|
|
158
|
-
settings.hooks.PreToolUse.push({ matcher:
|
|
161
|
+
settings.hooks.PreToolUse.push({ matcher: SHELL_TOOLS, hooks: [{ type: 'command', command: dgCmd }] });
|
|
159
162
|
|
|
160
163
|
// Gitmoji guard: a PreToolUse(Bash) hook that DENIES a `git commit` whose message
|
|
161
164
|
// carries no gitmoji (gitmoji.dev) in front of the Conventional Commits header. The
|
|
@@ -170,7 +173,7 @@ export function wireHook(paths, sourceMd, policy = {}) {
|
|
|
170
173
|
settings.hooks.PreToolUse = settings.hooks.PreToolUse.filter(
|
|
171
174
|
(e) => !hookWiringOf(e).includes('.rsc/gitmoji-guard.'),
|
|
172
175
|
);
|
|
173
|
-
settings.hooks.PreToolUse.push({ matcher:
|
|
176
|
+
settings.hooks.PreToolUse.push({ matcher: SHELL_TOOLS, hooks: [{ type: 'command', command: gmCmd }] });
|
|
174
177
|
|
|
175
178
|
// New-feature gate: a UserPromptSubmit hook that re-injects the SDD new-feature gate as
|
|
176
179
|
// the most-recent instruction on every user turn. SessionStart injects the full gate (the
|
package/targets/danger-guard.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// rsc Danger guard (claude). Wired by targets/claude.js onto PreToolUse (matcher Bash)
|
|
2
|
+
// rsc Danger guard (claude). Wired by targets/claude.js onto PreToolUse (matcher Bash|PowerShell)
|
|
3
3
|
// as `node ...` so it runs on every platform including Windows.
|
|
4
4
|
// argv[2] = absolute project root stdin = PreToolUse hook JSON
|
|
5
5
|
//
|
|
@@ -46,7 +46,7 @@ if (technicalLevel() === 'technical') allow();
|
|
|
46
46
|
// Only Bash commands can be dangerous here.
|
|
47
47
|
let input = {};
|
|
48
48
|
try { input = JSON.parse(readFileSync(0, 'utf8') || '{}'); } catch { allow(); }
|
|
49
|
-
if ((input.tool_name || input.toolName)
|
|
49
|
+
if (!['Bash', 'PowerShell'].includes(input.tool_name || input.toolName)) allow();
|
|
50
50
|
const cmd = input.tool_input?.command || input.toolInput?.command || '';
|
|
51
51
|
if (typeof cmd !== 'string' || !cmd) allow();
|
|
52
52
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// rsc Gitmoji guard (claude). Wired by targets/claude.js onto PreToolUse (matcher Bash)
|
|
2
|
+
// rsc Gitmoji guard (claude). Wired by targets/claude.js onto PreToolUse (matcher Bash|PowerShell)
|
|
3
3
|
// as `node ...` so it runs on every platform including Windows.
|
|
4
4
|
// argv[2] = absolute project root stdin = PreToolUse hook JSON
|
|
5
5
|
//
|
|
@@ -230,7 +230,7 @@ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href)
|
|
|
230
230
|
|
|
231
231
|
let input = {};
|
|
232
232
|
try { input = JSON.parse(readFileSync(0, 'utf8') || '{}'); } catch { allow(); }
|
|
233
|
-
if ((input.tool_name || input.toolName)
|
|
233
|
+
if (!['Bash', 'PowerShell'].includes(input.tool_name || input.toolName)) allow();
|
|
234
234
|
const command = input.tool_input?.command || input.toolInput?.command || '';
|
|
235
235
|
if (typeof command !== 'string' || !command) allow();
|
|
236
236
|
|
package/targets/hook-once.mjs
CHANGED
|
@@ -89,21 +89,25 @@ export function claimOnce(key, { dir = markerDir(), windowMs = 30_000, now = Dat
|
|
|
89
89
|
// The workflow gate text lives here, not inline in userprompt-gate.mjs, so it has exactly one
|
|
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
|
-
export const WORKFLOW_GATE_TEXT = `===== workflow gate (
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
1.
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
92
|
+
export const WORKFLOW_GATE_TEXT = `===== workflow gate · metodologia (maxima prioridad) =====
|
|
93
|
+
Si el usuario pide construir, anadir o cambiar algo (en cualquier idioma, se juzga la intencion),
|
|
94
|
+
sigue esta cadena ANTES de escribir codigo. Ninguna skill escribe codigo de una feature hasta que
|
|
95
|
+
el que esta claro y el usuario ha dado el visto bueno.
|
|
96
|
+
1. Aclarar el que:
|
|
97
|
+
- Peticion concreta y acotada -> \`grill-with-docs\`.
|
|
98
|
+
- Tema amplio, o el usuario no sabe como abordarlo -> recomiendale \`wayfinder\`.
|
|
99
|
+
- \`superpowers:brainstorming\` vale para lo mismo si el usuario lo prefiere.
|
|
100
|
+
Cada decision dificil de revertir se registra con \`write-adr\`, en docs/adr/.
|
|
101
|
+
2. Escribir la spec: \`to-spec\`, o \`superpowers:brainstorming\` para cerrarla por secciones.
|
|
102
|
+
Sin spec escrita no se programa.
|
|
103
|
+
3. Construir: \`superpowers:writing-plans\` -> \`superpowers:executing-plans\`. Solo el trabajo que
|
|
104
|
+
salio de \`wayfinder\` va con \`to-tickets\` -> \`implement\`, ticket a ticket.
|
|
105
|
+
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).
|
|
107
|
+
Siempre: \`superpowers:test-driven-development\` y \`superpowers:verification-before-completion\`
|
|
108
|
+
antes de dar nada por terminado. Los bugs entran por \`superpowers:systematic-debugging\`.
|
|
109
|
+
Cambio de una linea, typo o ajuste de configuracion -> hazlo y di que te saltas la cadena.
|
|
110
|
+
==========================================================
|
|
107
111
|
`;
|
|
108
112
|
|
|
109
113
|
/**
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { readFileSync, existsSync, readdirSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
9
9
|
import { join, resolve } from 'node:path';
|
|
10
10
|
import { homedir } from 'node:os';
|
|
11
|
+
import { spawnSync } from 'node:child_process';
|
|
11
12
|
import { claimOnce, readHookInput } from './hook-once.mjs';
|
|
12
13
|
|
|
13
14
|
const STALE_AUDIT_DAYS = 14; // periodic skill-audit cadence (kept in sync with scripts/audit.js)
|
|
@@ -27,6 +28,19 @@ const emitBody = hook.session_id
|
|
|
27
28
|
|
|
28
29
|
const has = (...p) => existsSync(join(root, ...p));
|
|
29
30
|
|
|
31
|
+
// The npm lookup (up to 1.5 s) starts now and runs while the local checks below do their work;
|
|
32
|
+
// the update block at the end only awaits it.
|
|
33
|
+
const latestVersion = process.env.RSC_NO_UPDATE_CHECK ? Promise.resolve(null)
|
|
34
|
+
: process.env.RSC_LATEST ? Promise.resolve(process.env.RSC_LATEST)
|
|
35
|
+
: (async () => {
|
|
36
|
+
const ctrl = new AbortController();
|
|
37
|
+
const timer = setTimeout(() => ctrl.abort(), 1500);
|
|
38
|
+
try {
|
|
39
|
+
const res = await fetch('https://registry.npmjs.org/@damiangilgonzalez%2fharness/latest', { signal: ctrl.signal });
|
|
40
|
+
return (await res.json()).version;
|
|
41
|
+
} catch { return null; } finally { clearTimeout(timer); }
|
|
42
|
+
})();
|
|
43
|
+
|
|
30
44
|
// Is a Context7 MCP server configured for this project (.mcp.json → mcpServers.context7)?
|
|
31
45
|
function hasContext7() {
|
|
32
46
|
try {
|
|
@@ -221,10 +235,24 @@ if (has('.git')) {
|
|
|
221
235
|
// off, so a second check could never fail and would only look like protection (P2). Test 36 holds
|
|
222
236
|
// the behaviour end to end, through this hook.
|
|
223
237
|
const W = await import('./worktree-reaper.mjs');
|
|
224
|
-
|
|
238
|
+
// Classifying spawns several git processes per worktree (1.2 s with a dozen of them, on every
|
|
239
|
+
// session start). A verdict only moves when a worktree HEAD, the trunk or the opt-out moves, so
|
|
240
|
+
// those make the fingerprint and an unchanged one reuses the last notice. Safe because `reap`
|
|
241
|
+
// re-classifies before it removes anything.
|
|
242
|
+
const trunk = W.resolveTrunk(root);
|
|
243
|
+
const tip = trunk ? spawnSync('git', ['-C', root, 'rev-parse', trunk], { encoding: 'utf8' }).stdout : '';
|
|
244
|
+
const list = spawnSync('git', ['-C', root, 'worktree', 'list', '--porcelain'], { encoding: 'utf8' }).stdout;
|
|
245
|
+
const fingerprint = `${W.isCleanupEnabled(root)}\n${trunk}\n${tip}\n${list}`;
|
|
246
|
+
const cacheFile = join(root, '.rsc', 'worktree-sweep.json');
|
|
247
|
+
let cached = null;
|
|
248
|
+
try { cached = JSON.parse(readFileSync(cacheFile, 'utf8')); } catch { /* first run */ }
|
|
249
|
+
if (cached && cached.fingerprint === fingerprint && typeof cached.text === 'string') {
|
|
250
|
+
process.stdout.write(cached.text);
|
|
251
|
+
} else {
|
|
252
|
+
let text = '';
|
|
225
253
|
const candidates = W.classifyWorktrees(root).filter((c) => c.verdict !== 'skip');
|
|
226
254
|
if (candidates.length) {
|
|
227
|
-
|
|
255
|
+
text = `
|
|
228
256
|
===== rsc worktree cleanup =====
|
|
229
257
|
${candidates.length} worktree(s) hold work that is already in the trunk:
|
|
230
258
|
${candidates.map((c) => W.summarize(c, root)).join('\n')}
|
|
@@ -234,8 +262,13 @@ ACTION: offer to retire them in ONE line and wait for a yes. On a yes run:
|
|
|
234
262
|
A yes in bulk covers only the safe ones; anything marked \`ask\` is confirmed on its own.
|
|
235
263
|
A no holds for this session. Permanent off: .rsc/.no-worktree-cleanup
|
|
236
264
|
================================
|
|
237
|
-
|
|
265
|
+
`;
|
|
238
266
|
}
|
|
267
|
+
process.stdout.write(text);
|
|
268
|
+
try {
|
|
269
|
+
mkdirSync(join(root, '.rsc'), { recursive: true });
|
|
270
|
+
writeFileSync(cacheFile, JSON.stringify({ fingerprint, text }) + '\n');
|
|
271
|
+
} catch { /* unwritable → scan again next session */ }
|
|
239
272
|
}
|
|
240
273
|
} catch { /* reaper missing or git unhappy → say nothing; this is never worth breaking startup for */ }
|
|
241
274
|
}
|
|
@@ -257,14 +290,7 @@ function isNewer(a, b) {
|
|
|
257
290
|
if (!process.env.RSC_NO_UPDATE_CHECK) {
|
|
258
291
|
try {
|
|
259
292
|
const installed = readFileSync(join(root, '.rsc', '.version'), 'utf8').trim();
|
|
260
|
-
|
|
261
|
-
if (!latest) {
|
|
262
|
-
const ctrl = new AbortController();
|
|
263
|
-
const timer = setTimeout(() => ctrl.abort(), 1500);
|
|
264
|
-
const res = await fetch('https://registry.npmjs.org/@damiangilgonzalez%2fharness/latest', { signal: ctrl.signal });
|
|
265
|
-
clearTimeout(timer);
|
|
266
|
-
latest = (await res.json()).version;
|
|
267
|
-
}
|
|
293
|
+
const latest = await latestVersion;
|
|
268
294
|
if (installed && latest && isNewer(latest, installed)) {
|
|
269
295
|
process.stdout.write(`
|
|
270
296
|
===== rsc update available =====
|
package/targets/ship-guard.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// rsc Ship guard (claude). Wired by targets/claude.js onto PreToolUse (matcher Bash)
|
|
2
|
+
// rsc Ship guard (claude). Wired by targets/claude.js onto PreToolUse (matcher Bash|PowerShell)
|
|
3
3
|
// as `node ...` so it runs on every platform including Windows.
|
|
4
4
|
// argv[2] = absolute project root stdin = PreToolUse hook JSON
|
|
5
5
|
//
|
|
@@ -39,7 +39,7 @@ if (!existsSync(join(root, '.git'))) allow();
|
|
|
39
39
|
// Read the tool call. Only Bash commands can move branches.
|
|
40
40
|
let input = {};
|
|
41
41
|
try { input = JSON.parse(readFileSync(0, 'utf8') || '{}'); } catch { allow(); }
|
|
42
|
-
if ((input.tool_name || input.toolName)
|
|
42
|
+
if (!['Bash', 'PowerShell'].includes(input.tool_name || input.toolName)) allow();
|
|
43
43
|
const command = input.tool_input?.command || input.toolInput?.command || '';
|
|
44
44
|
if (typeof command !== 'string' || !command) allow();
|
|
45
45
|
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
// Fail-open (never blocks a turn). Opt out per project with .rsc/.no-feature-gate.
|
|
14
14
|
import { existsSync } from 'node:fs';
|
|
15
15
|
import { join } from 'node:path';
|
|
16
|
+
import { homedir } from 'node:os';
|
|
16
17
|
import { claimOnce, readHookInput, WORKFLOW_GATE_TEXT } from './hook-once.mjs';
|
|
17
18
|
|
|
18
19
|
const root = process.argv[2] || process.cwd();
|
|
@@ -28,4 +29,8 @@ const hook = readHookInput();
|
|
|
28
29
|
const turnKey = hook.prompt_id || (hook.prompt ? `p${hook.prompt.length}:${hook.prompt.slice(0, 64)}` : null);
|
|
29
30
|
if (hook.session_id && turnKey && !claimOnce(`up:${hook.session_id}:${turnKey}`)) process.exit(0);
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
// The gate routes screen work through `flujo-interfaz`, but that skill is optional. Naming a skill
|
|
33
|
+
// that is not installed sends the agent looking for something that does not exist, so the line only
|
|
34
|
+
// ships when the skill is present in this project or in the user scope.
|
|
35
|
+
const hasFlujo = [root, homedir()].some((r) => existsSync(join(r, '.claude', 'skills', 'flujo-interfaz', 'SKILL.md')));
|
|
36
|
+
process.stdout.write(hasFlujo ? WORKFLOW_GATE_TEXT : WORKFLOW_GATE_TEXT.split('\n').filter((l) => !l.includes('flujo-interfaz')).join('\n'));
|