jorgex-stack 1.0.2 → 1.0.4
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/PRD.md +16 -3
- package/README.md +44 -2
- package/dist/cli.js +32 -4
- package/package.json +2 -2
- package/stack/agents/code-simplifier.md +21 -10
- package/stack/agents/implementer.md +1 -0
- package/stack/agents/orchestrator.md +2 -0
- package/stack/agents/security-auditor.md +7 -0
- package/stack/agents/silent-failure-hunter.md +7 -0
- package/stack/agents/test-analyzer.md +7 -0
- package/stack/agents/type-design-analyzer.md +1 -1
- package/stack/commands/lean-audit.md +59 -0
- package/stack/commands/xreview.md +4 -2
- package/stack/plugins/opencode/goal/artifacts.ts +142 -0
- package/stack/plugins/opencode/goal/command.ts +255 -0
- package/stack/plugins/opencode/goal/db.ts +68 -0
- package/stack/plugins/opencode/goal/opencode-hooks.ts +272 -0
- package/stack/plugins/opencode/goal/state.ts +85 -0
- package/stack/plugins/opencode/goal/store.ts +906 -0
- package/stack/plugins/opencode/goal/supervisor.ts +269 -0
- package/stack/plugins/opencode/goal/types.ts +187 -0
- package/stack/plugins/opencode/goal-plugin.ts +176 -0
- package/stack/scripts/post-pr-review.cjs +6 -3
- package/stack/skills/lean-code/SKILL.md +69 -0
package/PRD.md
CHANGED
|
@@ -31,7 +31,7 @@ pnpm dlx jorgex-stack doctor → verifica que todo está sano
|
|
|
31
31
|
|
|
32
32
|
| # | Decisión | Elección |
|
|
33
33
|
|---|----------|----------|
|
|
34
|
-
| D1 | Tecnología del instalador | **TypeScript + Node (≥
|
|
34
|
+
| D1 | Tecnología del instalador | **TypeScript + Node (≥22.5)**, bundle único (tsup/esbuild), prompts con `@clack/prompts`, publicado en el registry npm y ejecutado con `pnpm dlx jorgex-stack`. Sin Go, sin binarios propios. |
|
|
35
35
|
| D2 | Plataformas | **Cross-platform desde v1** (Windows + macOS + Linux). Windows es el entorno principal de pruebas. |
|
|
36
36
|
| D3 | Estrategia de despliegue | **Merge idempotente con marcadores** (`<!-- jorgex:seccion -->` en markdown, upsert quirúrgico en JSON/TOML). Backup automático antes de tocar nada + rollback. Nunca machaca contenido manual del usuario. |
|
|
37
37
|
| D4 | Plugins de jorgex-custom-tools | Se **copian** al nuevo repo ahora (los originales NO se tocan porque están en uso). **Cuando el proyecto esté completo e instalado**: se eliminan de `C:\Users\jorge\Desktop\jorgex-custom-tools` y pasan a vivir/instalarse SOLO desde JorgeX Stack. Ver §11 F6. |
|
|
@@ -73,7 +73,7 @@ JorgeX Stack/
|
|
|
73
73
|
│ │ ├── orchestrator.md
|
|
74
74
|
│ │ ├── backend-analyst.md … type-design-analyzer.md
|
|
75
75
|
│ ├── skills/ # TODAS las skills vendorizadas (terceros con upstream registrado en upstreams.json)
|
|
76
|
-
│ ├── commands/ # xreview.md (formato canónico)
|
|
76
|
+
│ ├── commands/ # xreview.md, lean-audit.md (formato canónico)
|
|
77
77
|
│ ├── hooks/
|
|
78
78
|
│ │ └── hooks.json # definición canónica de hooks (formato Claude Code como base)
|
|
79
79
|
│ ├── scripts/
|
|
@@ -173,7 +173,7 @@ La config actual referencia modelos vía OpenCode multi-provider (`openai/gpt-5.
|
|
|
173
173
|
}
|
|
174
174
|
```
|
|
175
175
|
|
|
176
|
-
**Auditoría (F1, 2026-06-10)**: las 18 skills están vendorizadas en `stack/skills/` con upstream registrado. Solo `agent-delegation` y `
|
|
176
|
+
**Auditoría (F1, 2026-06-10)**: las 18 skills están vendorizadas en `stack/skills/` con upstream registrado. Solo `agent-delegation`, `work-lifecycle` y `lean-code` son propias; `tdd`, `to-prd`, `to-issues` y `diagnose` de **mattpocock/skills** tienen modificaciones locales (`modified: true`). Resto: anthropics/skills, supabase/agent-skills, vercel(-labs), kepano/obsidian-skills, millionco/react-doctor, safishamsi/graphify.
|
|
177
177
|
|
|
178
178
|
**Política de `update` (F5.x — implementada con flujo interactivo)**:
|
|
179
179
|
|
|
@@ -212,6 +212,19 @@ Solo dos MCPs en el stack (D5):
|
|
|
212
212
|
- Los originales **no se tocan** mientras dure el desarrollo (están en uso).
|
|
213
213
|
- F6 (cierre): eliminar de jorgex-custom-tools, reinstalar todo desde JorgeX Stack.
|
|
214
214
|
|
|
215
|
+
### 7.6 Publicación automática en npm
|
|
216
|
+
|
|
217
|
+
El paquete `jorgex-stack` se publica solo, sin acción del usuario. El workflow `.github/workflows/publish.yml` corre en cada push a `main` y aplica esta política:
|
|
218
|
+
|
|
219
|
+
1. **Detección de cambios publicables**: se compara `HEAD` contra `v<package.version>` si ese tag existe; si no, cae a `github.event.before`. Son publicables los de `src/`, `stack/` y la lista exacta (`upstreams.json`, `package.json`, `pnpm-lock.yaml`, `tsconfig.json`, `tsup.config.ts`, `README.md`, `PRD.md`). **No** son publicables los cambios solo en `work/`, `worktrees/`, tests, ni en archivos fuera de la lista. La política está modelada y testeada en `src/lib/release.ts` (`classifyReleasePaths`); el workflow la replica inline para no ejecutar código generado por el repo en el job privilegiado.
|
|
220
|
+
2. **Patch automático + recuperación manual**: si hay cambios publicables y la versión de `package.json` ya existe en npm, el workflow busca el primer patch libre (`x+1`, `x+2`, …) con `bumpPatch` (`src/lib/release.ts`), commitea `chore(release): bump version to v…` con el actor `github-actions[bot]` y publica la nueva versión. `validate` resuelve una sola vez la SHA objetivo y la expone como `target_sha`; `bump` la reutiliza y solo falla verde en `stale_run` cuando una run de push quedó vieja tras `git fetch origin main --tags` y `origin/main` ya no coincide con la SHA validada. Un `workflow_dispatch` sobre `main` recupera una publicación fallida: si no se pasa `release_sha`, `validate` fija `target_sha` a `origin/main` tras el fetch; si se pasa, debe ser una SHA completa de 40 hex perteneciente a `main` y se valida su `package.json.version` antes de publicar o tagear. Si la `release_sha` no es válida o no pertenece a `main`, el job falla en rojo con mensaje accionable; `workflow_dispatch` nunca emite `skip_reason=stale_run`. Si el diff mezcla cambios publicables con `.github/workflows/*`, el auto-release se aborta antes de bump/publish porque GitHub puede rechazar el push del tag sin permisos para workflows; hay que separar la release o usar una publicación/tag manual con permisos elevados. Si no hay un tag de release previo alcanzable para reconstruir el rango de recovery, el workflow falla cerrado y exige intervención manual. Si no hay cambios publicables en un push normal: no hace nada.
|
|
221
|
+
3. **Minor y major manuales**: cuando el siguiente patch ya existe en npm (p.ej. el workflow detectó que `1.0.3` está ocupado), falla con mensaje claro y exige bump manual de `package.json` en un PR. Minor y major siguen siendo decisiones humanas.
|
|
222
|
+
4. **Guarda anti-loop**: `isReleaseBumpCommit` reconoce solo `chore(release):`, semver puro (`1.0.3`, `v1.0.3`) y commits de actores que terminan en `[bot]` y mencionan release/publish/bump/version. `release:` genérico y `chore:` a secas no cuentan. Cuando detecta uno, no vuelve a bumpear ni a publicar.
|
|
223
|
+
5. **OIDC / trusted publishing**: el job de bump/push usa solo `contents: write`; el job de publish usa `permissions: id-token: write` + `contents: read` y `setup-node` con `registry-url: https://registry.npmjs.org`; el `tag-release` solo usa `contents: write` y no necesita OIDC. **No** se usan `NPM_TOKEN` ni `NODE_AUTH_TOKEN` — los únicos secretos del repo son los de GitHub. La excepción a la regla D8 ("pnpm siempre") son `npm pack --dry-run --ignore-scripts` y el `npm publish --ignore-scripts --provenance` final: el cliente npm permite fijar `--ignore-scripts` y publicar con OIDC/provenance contra el registry oficial.
|
|
224
|
+
6. **Versión del CLI sincronizada**: `src/cli.ts --version` lee `package.json` directamente (`readPackageMetadata` en `src/lib/release.ts`); no hay constante `VERSION` hardcodeada que pueda quedar desincronizada.
|
|
225
|
+
|
|
226
|
+
Los detalles de política (criterios de publicabilidad, lista exacta, semántica del bump commit) se prueban en `src/lib/release.ts`; el YAML mantiene una copia inline mínima por seguridad, separando bump/push (`contents: write`) de publish (`id-token: write` + `contents: read`).
|
|
227
|
+
|
|
215
228
|
## 8. CLI — UX
|
|
216
229
|
|
|
217
230
|
```
|
package/README.md
CHANGED
|
@@ -39,13 +39,55 @@ Uso:
|
|
|
39
39
|
|
|
40
40
|
Autenticación con GitHub: las consultas usan `GH_TOKEN`/`GITHUB_TOKEN` del entorno o, si no existen, el token de tu sesión de `gh` CLI (`gh auth token` — solo lectura local, nunca se loguea ni persiste). Sin token, GitHub limita las consultas en paralelo y algunos upstreams pueden salir como "sin conexión".
|
|
41
41
|
|
|
42
|
+
### Goal Mode de OpenCode
|
|
43
|
+
|
|
44
|
+
Goal Mode es un plugin de OpenCode para objetivos largos: varias sesiones, varios slices, varios worktrees y, si hace falta, varios PRs. No está pensado para tareas cortas. Si el cambio cabe sin autonomía prolongada, no uses `/goal`.
|
|
45
|
+
|
|
46
|
+
Solo vive en OpenCode. Claude Code y Codex no lo reciben.
|
|
47
|
+
|
|
48
|
+
Comandos disponibles:
|
|
49
|
+
|
|
50
|
+
- `/goal <objetivo>` — crea un goal persistente.
|
|
51
|
+
- `/goal status` — muestra estado y siguiente acción.
|
|
52
|
+
- `/goal plan` — enseña el plan maestro / PRD del goal.
|
|
53
|
+
- `/goal history` — lista eventos y transiciones.
|
|
54
|
+
- `/goal pause` — pausa el goal.
|
|
55
|
+
- `/goal resume` — reanuda el goal.
|
|
56
|
+
- `/goal merged [commit]` — señala que el PR externo pendiente ya se ha mergeado.
|
|
57
|
+
- `/goal cancel` — cancela el goal.
|
|
58
|
+
|
|
59
|
+
Lo que no existe:
|
|
60
|
+
|
|
61
|
+
- `/goal quick`
|
|
62
|
+
- `/goal work`
|
|
63
|
+
|
|
64
|
+
Estado operativo:
|
|
65
|
+
|
|
66
|
+
- SQLite separada por defecto en `~/.jorgex-stack/goals/goals.sqlite`.
|
|
67
|
+
- Override opcional con `JORGEX_GOAL_DB`, pero siempre dentro de `~/.jorgex-stack/goals/`.
|
|
68
|
+
- Engram no es el store operativo del goal: sigue siendo memoria/protocolo, no base de estado.
|
|
69
|
+
- Goal Mode no hace merges automáticos; cuando toca esperar un merge externo, el estado pasa a `waiting_for_merge`.
|
|
70
|
+
- La integración usa hooks experimentales de OpenCode (`experimental.chat.system.transform` y `experimental.session.compacting`), así que esa superficie puede cambiar.
|
|
71
|
+
|
|
42
72
|
## Estado
|
|
43
73
|
|
|
44
|
-
|
|
74
|
+
CLI completo y migración real ejecutada (F6); el stack es la única fuente de configuración. Las versiones se publican automáticamente en [npm](https://www.npmjs.com/package/jorgex-stack) según el flujo descrito en [Publicación](#publicación). El diseño, las decisiones (D1–D9) y el roadmap están en [PRD.md](PRD.md).
|
|
75
|
+
|
|
76
|
+
## Publicación
|
|
77
|
+
|
|
78
|
+
La release la dispara el push/merge a `main` y GitHub Actions; también hay `workflow_dispatch` de recuperación sobre `main` con `release_sha` opcional. `validate` resuelve una sola vez la SHA objetivo y la expone como `target_sha`; `bump` reutiliza esa SHA. Si no pasas `release_sha`, `validate` fija `target_sha` a `origin/main` tras `fetch`; si la pasas, debe ser una SHA completa de 40 hex perteneciente a `main` o falla en rojo con instrucción de recuperación. El modo sin `release_sha` solo es válido para publicar `origin/main` cuando la versión aún no existe en npm; si la versión ya existe y falta el tag, el workflow falla y exige `workflow_dispatch` con `release_sha=<sha publicada>`. Si el diff mezcla cambios publicables con `.github/workflows/*`, el auto-release se corta antes de bump/publish porque GitHub puede rechazar el push del tag sin permisos para workflows; hay que separar la release o usar un publish/tag manual con permisos elevados. No se usa `pnpm publish` ni hace falta login de npm:
|
|
79
|
+
|
|
80
|
+
- **Patch automático**: si el push a `main` contiene cambios publicables y la versión actual de `package.json` ya está en npm, el workflow busca el primer patch libre (`x+1`, `x+2`, …), commitea `chore(release): bump version to v…` y publica. Si ya existe el tag `v<package.version>`, usa ese punto como base acumulada; si no, cae a `github.event.before`. Las runs obsoletas se abortan tras `git fetch origin main --tags` si `origin/main` ya no coincide con `GITHUB_SHA`.
|
|
81
|
+
- **Recuperación manual**: una ejecución manual sobre `main` con `release_sha` publica esa SHA si todavía no existe en npm, sin volver a bumpear; si la versión ya está en npm pero falta el tag `v<version>`, el workflow falla y te obliga a relanzar con `release_sha=<sha publicada>` para no tagear `origin/main`. `release_sha` debe ser una SHA completa de 40 hex y pertenecer a `main`; refs mutables (`main`, tags, `main~1`) se rechazan. Si no pasas `release_sha`, `validate` resuelve `origin/main` una vez, lo expone como `target_sha` y `bump` usa esa SHA validada. La recuperación no salta la guarda de `.github/workflows/*`: si el diff mezcla workflows con cambios publicables, hay que separar la release o hacer el tag/publish con permisos elevados. Si no existe un tag de release previo alcanzable para reconstruir el rango, el workflow falla cerrado y exige intervención manual.
|
|
82
|
+
- **Sin release**: cambios solo en `work/`, `worktrees/`, tests o archivos no listados como publicables (`src/`, `stack/`, `upstreams.json`, `package.json`, `pnpm-lock.yaml`, `tsconfig.json`, `tsup.config.ts`, `README.md`, `PRD.md`) no generan release.
|
|
83
|
+
- **Minor y major manuales**: bump explícito de `package.json` en el PR (el workflow detecta que el siguiente patch ya existe en npm y exige el bump).
|
|
84
|
+
- **OIDC / trusted publishing**: el job de publicación usa `id-token: write` y `registry-url` de `setup-node`; el job de bump/push solo tiene `contents: write`; el `tag-release` solo escribe `contents` y no usa OIDC. No hay `NPM_TOKEN` ni `NODE_AUTH_TOKEN` en ningún secreto. El `tag-release` solo corre si `publish` fue `success` o `skipped` con `tag_needed=true` y conserva su validación SHA como última defensa. La única excepción a la regla "pnpm siempre" son `npm pack --dry-run --ignore-scripts` y `npm publish --ignore-scripts --provenance` en el paso final, por compatibilidad y hardening del registry.
|
|
85
|
+
|
|
86
|
+
Los detalles de diseño están en [PRD §7.6](PRD.md#76-publicación-automática-en-npm).
|
|
45
87
|
|
|
46
88
|
## Desarrollo
|
|
47
89
|
|
|
48
|
-
Requisitos: Node ≥
|
|
90
|
+
Requisitos: Node ≥ 22.5 y pnpm (nunca npm). Goal Mode usa `node:sqlite` en tests/CLI Node y OpenCode usa `bun:sqlite` en runtime.
|
|
49
91
|
|
|
50
92
|
```
|
|
51
93
|
pnpm install
|
package/dist/cli.js
CHANGED
|
@@ -1271,9 +1271,8 @@ function planPlugins(adapter, ctx) {
|
|
|
1271
1271
|
if (pluginsDir === null) return [];
|
|
1272
1272
|
const source = path16.join(ctx.stackDir, "plugins", adapter.id);
|
|
1273
1273
|
if (!fs10.existsSync(source)) return [];
|
|
1274
|
-
return
|
|
1275
|
-
const
|
|
1276
|
-
const target = path16.join(pluginsDir, f);
|
|
1274
|
+
return listFilesRecursive(source).filter((f) => f.endsWith(".ts")).map((sourceFile) => {
|
|
1275
|
+
const target = path16.join(pluginsDir, path16.relative(source, sourceFile));
|
|
1277
1276
|
const raw = fs10.readFileSync(sourceFile, "utf8");
|
|
1278
1277
|
let content = raw;
|
|
1279
1278
|
if (content.includes('"{{ENGRAM_BIN}}"')) {
|
|
@@ -2708,8 +2707,37 @@ function cancelled() {
|
|
|
2708
2707
|
return 1;
|
|
2709
2708
|
}
|
|
2710
2709
|
|
|
2710
|
+
// src/lib/release.ts
|
|
2711
|
+
import fs17 from "fs";
|
|
2712
|
+
import path24 from "path";
|
|
2713
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
2714
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2715
|
+
function findPackageJson() {
|
|
2716
|
+
let dir = path24.dirname(fileURLToPath2(import.meta.url));
|
|
2717
|
+
for (let i = 0; i < 6; i++) {
|
|
2718
|
+
const candidate = path24.join(dir, "package.json");
|
|
2719
|
+
if (fs17.existsSync(candidate)) return candidate;
|
|
2720
|
+
dir = path24.dirname(dir);
|
|
2721
|
+
}
|
|
2722
|
+
throw new Error("No se encontr\xF3 package.json cerca del CLI.");
|
|
2723
|
+
}
|
|
2724
|
+
function readPackageVersion() {
|
|
2725
|
+
return readPackageMetadata().version;
|
|
2726
|
+
}
|
|
2727
|
+
function readPackageMetadata() {
|
|
2728
|
+
const packageJson = findPackageJson();
|
|
2729
|
+
const raw = fs17.readFileSync(packageJson, "utf8");
|
|
2730
|
+
const parsed = JSON.parse(raw);
|
|
2731
|
+
const name = typeof parsed.name === "string" ? parsed.name.trim() : "";
|
|
2732
|
+
const version = typeof parsed.version === "string" ? parsed.version.trim() : "";
|
|
2733
|
+
if (name === "" || version === "") {
|
|
2734
|
+
throw new Error("package.json no expone nombre o versi\xF3n v\xE1lidos.");
|
|
2735
|
+
}
|
|
2736
|
+
return { name, version };
|
|
2737
|
+
}
|
|
2738
|
+
|
|
2711
2739
|
// src/cli.ts
|
|
2712
|
-
var VERSION =
|
|
2740
|
+
var VERSION = readPackageVersion();
|
|
2713
2741
|
var COMMANDS = ["install", "sync", "models", "update", "doctor", "restore", "uninstall"];
|
|
2714
2742
|
function parseFlags(args) {
|
|
2715
2743
|
const flags = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jorgex-stack",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Harness multi-agente portable: instala la config JorgeX (agentes, skills, hooks, Engram, MCPs) en Claude Code, Codex CLI y OpenCode",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"PRD.md"
|
|
29
29
|
],
|
|
30
30
|
"engines": {
|
|
31
|
-
"node": ">=
|
|
31
|
+
"node": ">=22.5"
|
|
32
32
|
},
|
|
33
33
|
"packageManager": "pnpm@11.1.1",
|
|
34
34
|
"scripts": {
|
|
@@ -9,14 +9,15 @@ bash: git-read
|
|
|
9
9
|
|
|
10
10
|
# Code Simplifier
|
|
11
11
|
|
|
12
|
-
You are an expert code simplification specialist focused on enhancing code clarity, consistency, and maintainability while preserving exact functionality.
|
|
12
|
+
You are an expert code simplification specialist focused on enhancing code clarity, consistency, and maintainability while preserving exact functionality. Use the `lean-code` skill as your anti-bloat lens and source of truth for when code should disappear, shrink, or reuse existing helpers. You prioritize readable, explicit code over overly compact solutions.
|
|
13
13
|
|
|
14
14
|
You are read-only: you analyze recently modified code and **propose** refinements as concrete suggestions (with file path, line and a before/after snippet). You never write or edit files yourself.
|
|
15
15
|
|
|
16
16
|
**First actions, in order**:
|
|
17
17
|
|
|
18
|
-
1. **
|
|
19
|
-
2. Load the `
|
|
18
|
+
1. **Resolve scope.** If you're given an audit scope (repo/path root), audit only that path and do not fall back to `git diff`. Otherwise, when you're given BASE and HEAD branches, review only `git diff <BASE>...HEAD` using exactly those branches — never assume `main`. If no audit scope or branches are given, review the working diff (`git diff`).
|
|
19
|
+
2. Load the `lean-code` skill.
|
|
20
|
+
3. Load the `agent-delegation` skill.
|
|
20
21
|
|
|
21
22
|
**Final output, last of all**: your final report (ending with the Result contract) must be the very last thing you emit. If you need to save anything to memory, do it BEFORE that output — never after.
|
|
22
23
|
|
|
@@ -24,6 +25,13 @@ You are read-only: you analyze recently modified code and **propose** refinement
|
|
|
24
25
|
|
|
25
26
|
Your lane: behavior-preserving clarity and structure of the CODE itself — nothing else. Bugs, guideline violations and comment quality belong to other specialists (the `agent-delegation` skill has the map): don't report them — note them as delegations in your Result contract. Don't propose adding, rewriting or deleting comments.
|
|
26
27
|
|
|
28
|
+
## 4R Readability Lens
|
|
29
|
+
|
|
30
|
+
- Propose simplifications only when they clearly reduce cognitive load: magic numbers with business meaning, long parameter lists, duplicated logic, dead code, naming drift, or deep nesting.
|
|
31
|
+
- Back proposals with evidence from the code shape, not taste; avoid subjective style nits.
|
|
32
|
+
- Keep the scope strictly readability/maintainability: do not turn bug fixes, security concerns, test gaps, or error-handling problems into simplification suggestions.
|
|
33
|
+
- Prefer small, local clarifications over structural rewrites that change how the code is organized without a clear readability win.
|
|
34
|
+
|
|
27
35
|
Your proposed refinements must:
|
|
28
36
|
|
|
29
37
|
1. **Preserve Functionality**: Never change what the code does - only how it does it. All original features, outputs, and behaviors must remain intact.
|
|
@@ -46,20 +54,23 @@ Your proposed refinements must:
|
|
|
46
54
|
- Prioritize "fewer lines" over readability (e.g., nested ternaries, dense one-liners)
|
|
47
55
|
- Make the code harder to debug or extend
|
|
48
56
|
|
|
49
|
-
5. **Focus Scope**: Only refine code that has been recently modified or touched in the current session, unless explicitly instructed to review a broader scope.
|
|
57
|
+
5. **Focus Scope**: Only refine code that has been recently modified or touched in the current session, unless explicitly instructed to review a broader audit scope.
|
|
50
58
|
|
|
51
59
|
Your process:
|
|
52
60
|
|
|
53
61
|
1. Identify the recently modified code sections
|
|
54
|
-
2.
|
|
55
|
-
3.
|
|
56
|
-
4.
|
|
57
|
-
5.
|
|
58
|
-
6.
|
|
62
|
+
2. Run a lean deletion pass first: what can disappear, become stdlib/native/platform code, reuse existing project code, or lose a premature abstraction?
|
|
63
|
+
3. Analyze remaining opportunities to improve elegance and consistency
|
|
64
|
+
4. Check proposals against project-specific best practices and coding standards
|
|
65
|
+
5. Ensure proposed changes keep all functionality unchanged
|
|
66
|
+
6. Verify the proposed code is simpler and more maintainable
|
|
67
|
+
7. Report only significant changes that affect understanding
|
|
59
68
|
|
|
60
69
|
## Output format
|
|
61
70
|
|
|
62
|
-
For each suggestion provide: file path and line, what to simplify and why, and a before/after snippet
|
|
71
|
+
For each suggestion provide: file path and line, what to simplify and why, and a before/after snippet when useful. Prefix each lean finding with the matching `lean-code` tag (for example `shrink:` or `delete:`).
|
|
72
|
+
|
|
73
|
+
End lean-heavy reports with `net: -<N> lines possible` when you can estimate it. If nothing meaningful can be simplified, say so briefly. Your goal is to surface refinements that meet the highest standards of elegance and maintainability while preserving complete functionality — the implementer applies them.
|
|
63
74
|
|
|
64
75
|
## Types of refinement to propose
|
|
65
76
|
|
|
@@ -23,6 +23,7 @@ You usually receive a clear design (often from an analyst), and the project's st
|
|
|
23
23
|
|
|
24
24
|
1. **Confirm the libraries you'll actually use** when you're unsure of the exact one or its API: check `package.json` (or the equivalent manifest) and the touched files — e.g. state (Zustand, Redux), data-fetching (TanStack Query, SWR), forms, styling, ORM. Use each library's real API and patterns; don't hand-roll what a present library already does.
|
|
25
25
|
2. **Mirror existing conventions**: look at the files you'll touch and their neighbors, and follow their style, patterns and imports. Don't introduce a new pattern without need.
|
|
26
|
+
3. **Load `lean-code` before non-trivial code**: use it as the ladder before you add a helper, wrapper, abstraction, or dependency. Ask whether the code is needed at all, whether stdlib/native/project helpers already solve it, and whether a smaller change works.
|
|
26
27
|
|
|
27
28
|
## Contract
|
|
28
29
|
|
|
@@ -60,6 +60,7 @@ The goal is not ceremony: it is one responsible coordinator, one writer per scop
|
|
|
60
60
|
- Synthesize findings.
|
|
61
61
|
- Propose a simple approach.
|
|
62
62
|
- Clarify only the real ambiguities.
|
|
63
|
+
- Apply the `lean-code` skill as a scope gate for any code-bearing task: ask whether the code is needed at all, whether stdlib/native/project helpers already solve it, and whether the smallest obvious change is enough.
|
|
63
64
|
- Backlog items phrased as "consider/evaluate X" are questions, not requirements: answer them HERE — who consumes it, what real case needs it — before they enter the PRD as committed scope. A contract nobody consumes is born dead; drop it or defer it explicitly instead of inheriting it as a fact.
|
|
64
65
|
- Create the PRD before moving to PLAN (see PRD rules).
|
|
65
66
|
|
|
@@ -86,6 +87,7 @@ If the work is large enough to benefit from explicit vertical slices, use the `t
|
|
|
86
87
|
- If a slice breakdown exists from `to-issues`, use it as the structure for planning and task sequencing.
|
|
87
88
|
- Divide the work into clear tasks.
|
|
88
89
|
- One task = one agent = one scope.
|
|
90
|
+
- For tasks that add or grow code, record the lean-code outcome in the task spec/acceptance criteria so implementer and simplifier apply the same ladder.
|
|
89
91
|
- The PRD does not replace the plan or task breakdown: the PRD captures decisions; the plan and tasks turn those decisions into executable work.
|
|
90
92
|
- Materialize the plan per the Work state rules: `work/{name}/plan.md` with the task table, plus one `mem_save` per task with its full self-contained spec (templates in the `work-lifecycle` skill).
|
|
91
93
|
- When presenting the plan for review, offer a disposable HTML view (rules in the `work-lifecycle` skill). Requested changes go to plan.md; delete the HTML once the plan is approved, before EXECUTE.
|
|
@@ -20,6 +20,13 @@ bash: git-read
|
|
|
20
20
|
|
|
21
21
|
Focus only on the changed code and the context needed to judge its security impact — do not audit the whole codebase.
|
|
22
22
|
|
|
23
|
+
## 4R Risk Lens
|
|
24
|
+
|
|
25
|
+
- Report only concrete risks with evidence: file/line, data flow, trust boundary, and why the issue is exploitable or security-relevant.
|
|
26
|
+
- Treat authz/backend surfaces, secrets, cookies/sessions, DOM sinks, injections by concatenation, and webhooks as priority review areas.
|
|
27
|
+
- Do not broaden into readability, test coverage, or resilience cleanup; delegate those lanes instead.
|
|
28
|
+
- A neutral-looking file can still carry risk if it changes control flow, permissions, data handling, or external input paths.
|
|
29
|
+
|
|
23
30
|
## What to Audit
|
|
24
31
|
|
|
25
32
|
- **Authentication & sessions**: login/logout flows, token handling, session invalidation, JWT usage and claims.
|
|
@@ -22,6 +22,13 @@ You are an elite error handling auditor with zero tolerance for silent failures
|
|
|
22
22
|
|
|
23
23
|
Your lane: error-handling quality — silent failures, swallowed errors, broad catches, unjustified fallbacks, useless error messages. General bugs, exploitability and test coverage are other specialists' lanes (the `agent-delegation` skill has the map): report them as delegations in your Result contract, don't audit them yourself.
|
|
24
24
|
|
|
25
|
+
## 4R Resilience Lens
|
|
26
|
+
|
|
27
|
+
- Look for fallback, retry, degradation, timeout, backoff, rate-limit, idempotency, observability, rollback, and fix-forward behavior in the diff.
|
|
28
|
+
- Fallbacks must be explicit and justified; if they hide the real failure or skip user-visible feedback, call it out.
|
|
29
|
+
- Prefer concrete evidence of silent failure paths over generic concerns: what is swallowed, where it disappears, and how the user will notice.
|
|
30
|
+
- When performance is part of the failure mode, require a measurable signal or a clear reason why the chosen mitigation is safe.
|
|
31
|
+
|
|
25
32
|
## Before auditing
|
|
26
33
|
|
|
27
34
|
Don't assume a logging library or error-tracking tool. The project's error-handling rules may already be in your context; when they aren't, detect the real setup from the code: the logger in use (pino, winston, slog, logging, console, etc.) and the existing error-handling patterns.
|
|
@@ -22,6 +22,13 @@ You are an expert test coverage analyst. Your primary responsibility is to ensur
|
|
|
22
22
|
|
|
23
23
|
You are read-only: you analyze coverage and recommend what to test, but you NEVER write tests. Report each gap worth fixing as a delegation in your Result contract so the orchestrator routes it to the writing specialist (the `agent-delegation` skill has the map). General code quality and error-handling audits are other lanes — delegate, don't absorb.
|
|
24
24
|
|
|
25
|
+
## 4R Reliability Lens
|
|
26
|
+
|
|
27
|
+
- Prioritize tests that protect external contracts, critical branches, and regressions users would actually notice.
|
|
28
|
+
- Flag brittle or non-deterministic tests, accidental `test.only`/exclusive-focus slips, and selectors that depend on implementation instead of stable UI semantics.
|
|
29
|
+
- Call out missing negative cases, edge cases, async/concurrency behavior, and examples that document API contracts.
|
|
30
|
+
- Keep the focus on reliability evidence: if the test suite would still pass while behavior breaks, that gap matters.
|
|
31
|
+
|
|
25
32
|
**Your Core Responsibilities:**
|
|
26
33
|
|
|
27
34
|
1. **Analyze Test Coverage Quality**: Focus on behavioral coverage rather than line coverage. Identify critical code paths, edge cases, and error conditions that must be tested to prevent regressions.
|
|
@@ -13,7 +13,7 @@ You are a type design expert with extensive experience in large-scale software a
|
|
|
13
13
|
|
|
14
14
|
**First actions, in order**:
|
|
15
15
|
|
|
16
|
-
1. **
|
|
16
|
+
1. **Resolve scope.** If you're given an audit scope (repo/path root), inspect only type/interface/schema/contract definitions in that path and do not fall back to `git diff`. Otherwise, when you're given BASE and HEAD branches, review only `git diff <BASE>...HEAD` using exactly those branches — never assume `main`. If no audit scope or branches are given, review the working diff (`git diff`).
|
|
17
17
|
2. Load the `agent-delegation` skill.
|
|
18
18
|
|
|
19
19
|
**Final output, last of all**: your final report (ending with the Result contract) must be the very last thing you emit. If you need to save anything to memory, do it BEFORE that output — never after.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Manual read-only lean audit — resolves repo/path scope, runs a cheap scope/routing scan, launches code-simplifier always and the relevant analysts conditionally, then reports ranked lean findings.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Run a manual lean audit. Your job as the main agent: determine WHAT to audit, resolve the exact repo/path scope, decide which subagents apply, and launch them in parallel.
|
|
6
|
+
|
|
7
|
+
## 0. Determine the audit target
|
|
8
|
+
|
|
9
|
+
User input (may be empty): {{input}}
|
|
10
|
+
|
|
11
|
+
- If the input names a repo or a path → audit that repo/subtree.
|
|
12
|
+
- If the input is empty or ambiguous: do a quick, cheap situation scan first — current repo root (`git rev-parse --show-toplevel`), current branch (`git rev-parse --abbrev-ref HEAD`), and the most likely changed area from the user's prompt — then ASK the user what to audit before launching anything, offering only the options that actually apply.
|
|
13
|
+
- If the input points to a PR or a diff → stop: `/lean-audit` is not a PR review command; use `/xreview` for diffs and PRs.
|
|
14
|
+
|
|
15
|
+
## 1. Resolve scope
|
|
16
|
+
|
|
17
|
+
- Print the chosen repo/path scope and why it was selected.
|
|
18
|
+
- If the input is a path, audit that subtree.
|
|
19
|
+
- If the input is a repo root, audit the repo from that root downward.
|
|
20
|
+
- If you are inside a worktree, use that worktree as the scope unless the user named a different path.
|
|
21
|
+
|
|
22
|
+
## 2. Cheap scope/routing scan
|
|
23
|
+
|
|
24
|
+
Before launching subagents, do a quick scan for obvious routing signals in the scope:
|
|
25
|
+
|
|
26
|
+
- changed file names and top-level directories
|
|
27
|
+
- obvious code/test/docs boundaries
|
|
28
|
+
- areas that likely need code-simplifier vs analyst passes
|
|
29
|
+
- anything that changes which subagents should run
|
|
30
|
+
|
|
31
|
+
Use the scan to route work, not to judge bloat or rewrite anything. Code-simplifier owns the lean/anti-bloat findings.
|
|
32
|
+
|
|
33
|
+
## 3. Launch the remaining subagents in PARALLEL
|
|
34
|
+
|
|
35
|
+
All subagents are CONDITIONAL and read-only. Launch one only when the scope indicates it applies. Pass every one EXACTLY:
|
|
36
|
+
|
|
37
|
+
- the audit scope: repo root / path root, verbatim
|
|
38
|
+
- the instruction: audit only that scope — never broaden it, and never apply fixes
|
|
39
|
+
|
|
40
|
+
Subagents and their triggers:
|
|
41
|
+
|
|
42
|
+
1. Task(subagent_type='code-simplifier') — always; this is the lean/anti-bloat pass
|
|
43
|
+
2. Task(subagent_type='backend-analyst') — if the scope includes backend, DB, APIs, server logic, or data flows
|
|
44
|
+
3. Task(subagent_type='frontend-analyst') — if the scope includes UI, hooks, state, rendering, or client-side flows
|
|
45
|
+
4. Task(subagent_type='type-design-analyzer') — if the scope changes types, interfaces, schemas, or contracts
|
|
46
|
+
|
|
47
|
+
If none of a subagent's triggers are present, skip it and note that it was skipped. Always state which subagents ran and which were skipped and why.
|
|
48
|
+
|
|
49
|
+
## 4. Synthesize
|
|
50
|
+
|
|
51
|
+
After the relevant subagents complete, synthesize their findings into a unified report:
|
|
52
|
+
|
|
53
|
+
- Scope used and how it was chosen
|
|
54
|
+
- Subagents run vs skipped (with reason)
|
|
55
|
+
- Ranked findings: delete, stdlib, native/platform, reuse, yagni, shrink
|
|
56
|
+
- What should be deferred because it is out of scope or too risky for this audit
|
|
57
|
+
- Positive findings
|
|
58
|
+
|
|
59
|
+
Do not apply fixes. This command only audits and reports.
|
|
@@ -62,14 +62,16 @@ Subagents and their triggers:
|
|
|
62
62
|
2. Task(subagent_type='silent-failure-hunter') — only if the diff includes error handling, try/catch, fallbacks, or async flows
|
|
63
63
|
3. Task(subagent_type='type-design-analyzer') — only if the diff changes types, interfaces, schemas, or public contracts
|
|
64
64
|
4. Task(subagent_type='code-reviewer') — for general code quality whenever non-trivial source code changed
|
|
65
|
-
5. Task(subagent_type='code-simplifier') — only if the diff introduces complexity worth simplifying
|
|
65
|
+
5. Task(subagent_type='code-simplifier') — only if the diff introduces complexity worth simplifying; this is the lean/anti-bloat pass for diffs and PRs
|
|
66
66
|
6. Task(subagent_type='security-auditor') — only if the diff touches auth, authorization, permissions, secrets/credentials, sensitive data, input validation, webhooks, or other security-critical flows
|
|
67
67
|
|
|
68
68
|
If none of a subagent's triggers are present, skip it and note that it was skipped. Always state which subagents ran and which were skipped and why.
|
|
69
69
|
|
|
70
|
+
`/lean-audit` is a separate manual repo/path command, not post-PR automation. Do not route it from here.
|
|
71
|
+
|
|
70
72
|
## 5. Synthesize
|
|
71
73
|
|
|
72
|
-
After the relevant subagents complete, synthesize their findings into a unified report
|
|
74
|
+
After the relevant subagents complete, synthesize their findings into a unified report. Use 4R internally (Reliability / Resilience / Readability / Risk) as a checklist while synthesizing; do not add a separate 4R section or taxonomy to the final report.
|
|
73
75
|
|
|
74
76
|
- Review scope used (BASE/HEAD or working diff) and how it was chosen
|
|
75
77
|
- Subagents run vs skipped (with reason)
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import type { GoalStore } from "./types.js";
|
|
4
|
+
|
|
5
|
+
export interface MasterArtifactsInput {
|
|
6
|
+
store: GoalStore;
|
|
7
|
+
goalId: string;
|
|
8
|
+
rootDir: string;
|
|
9
|
+
allowedRootDir?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface MasterArtifactsResult {
|
|
13
|
+
created: boolean;
|
|
14
|
+
preserved: boolean;
|
|
15
|
+
prdPath: string;
|
|
16
|
+
planPath: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function createMasterArtifacts(input: MasterArtifactsInput): MasterArtifactsResult {
|
|
20
|
+
const goal = input.store.getGoal(input.goalId);
|
|
21
|
+
if (!goal) {
|
|
22
|
+
throw new Error(`Goal ${input.goalId} not found.`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const allowedRootDir = input.allowedRootDir ?? input.rootDir;
|
|
26
|
+
assertSafeArtifactPath(input.rootDir, allowedRootDir, "Goal artifact root");
|
|
27
|
+
fs.mkdirSync(input.rootDir, { recursive: true });
|
|
28
|
+
|
|
29
|
+
const prdPath = path.join(input.rootDir, "PRD.md");
|
|
30
|
+
const planPath = path.join(input.rootDir, "plan.md");
|
|
31
|
+
assertSafeArtifactPath(prdPath, allowedRootDir, "Goal PRD artifact");
|
|
32
|
+
assertSafeArtifactPath(planPath, allowedRootDir, "Goal plan artifact");
|
|
33
|
+
const prdCreated = writeIfMissing(prdPath, renderMasterPrd(goal.objective));
|
|
34
|
+
const planCreated = writeIfMissing(planPath, renderMasterPlan(goal.objective));
|
|
35
|
+
|
|
36
|
+
input.store.recordArtifact(goal.id, { kind: "prd", path: prdPath });
|
|
37
|
+
input.store.recordArtifact(goal.id, { kind: "plan", path: planPath });
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
created: prdCreated || planCreated,
|
|
41
|
+
preserved: !prdCreated || !planCreated,
|
|
42
|
+
prdPath,
|
|
43
|
+
planPath,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function assertSafeArtifactPath(filePath: string, allowedRootDir: string, label = "Goal artifact"): void {
|
|
48
|
+
const resolvedAllowedRoot = resolveExistingPathWithoutSymlinks(allowedRootDir, `${label} root`);
|
|
49
|
+
const resolvedPath = resolveExistingPathWithoutSymlinks(filePath, label);
|
|
50
|
+
if (!isContainedIn(resolvedPath, resolvedAllowedRoot)) {
|
|
51
|
+
throw new Error(`${label} must stay inside ${allowedRootDir}. Refusing: ${filePath}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const stats = lstatIfExists(filePath);
|
|
55
|
+
if (stats?.isFile() && stats.nlink > 1) {
|
|
56
|
+
throw new Error(`${label} must not be a hard link.`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function writeIfMissing(filePath: string, content: string): boolean {
|
|
61
|
+
if (fs.existsSync(filePath)) return false;
|
|
62
|
+
fs.writeFileSync(filePath, content, "utf8");
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function resolveExistingPathWithoutSymlinks(input: string, label: string): string {
|
|
67
|
+
assertNoSymlinkInExistingPath(input, label);
|
|
68
|
+
let current = path.resolve(input);
|
|
69
|
+
const missing: string[] = [];
|
|
70
|
+
while (!fs.existsSync(current)) {
|
|
71
|
+
missing.push(path.basename(current));
|
|
72
|
+
const parent = path.dirname(current);
|
|
73
|
+
if (parent === current) break;
|
|
74
|
+
current = parent;
|
|
75
|
+
}
|
|
76
|
+
const real = fs.existsSync(current) ? fs.realpathSync(current) : current;
|
|
77
|
+
return missing.reduceRight((base, part) => path.join(base, part), real);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function assertNoSymlinkInExistingPath(input: string, label: string): void {
|
|
81
|
+
const resolved = path.resolve(input);
|
|
82
|
+
const root = path.parse(resolved).root;
|
|
83
|
+
const relativeParts = path.relative(root, resolved).split(path.sep).filter(Boolean);
|
|
84
|
+
let current = root;
|
|
85
|
+
for (const part of relativeParts) {
|
|
86
|
+
current = path.join(current, part);
|
|
87
|
+
const stats = lstatIfExists(current);
|
|
88
|
+
if (!stats) return;
|
|
89
|
+
if (stats.isSymbolicLink()) {
|
|
90
|
+
throw new Error(`${label} must not include symlinks.`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function lstatIfExists(input: string): fs.Stats | undefined {
|
|
96
|
+
try {
|
|
97
|
+
return fs.lstatSync(input);
|
|
98
|
+
} catch (error) {
|
|
99
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return undefined;
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function isContainedIn(candidate: string, root: string): boolean {
|
|
105
|
+
const relative = path.relative(root, candidate);
|
|
106
|
+
return relative === "" || (!!relative && !relative.startsWith("..") && !path.isAbsolute(relative));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function renderMasterPrd(objective: string): string {
|
|
110
|
+
return [
|
|
111
|
+
"# PRD maestro",
|
|
112
|
+
"",
|
|
113
|
+
"## Objetivo",
|
|
114
|
+
"",
|
|
115
|
+
objective,
|
|
116
|
+
"",
|
|
117
|
+
"## Alcance",
|
|
118
|
+
"",
|
|
119
|
+
"- Mantener el objetivo global del Goal Mode.",
|
|
120
|
+
"- Dividir el trabajo en slices ejecutables por el orquestador.",
|
|
121
|
+
"- Esperar merges manuales antes de continuar.",
|
|
122
|
+
"",
|
|
123
|
+
].join("\n");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function renderMasterPlan(objective: string): string {
|
|
127
|
+
return [
|
|
128
|
+
"# Plan maestro",
|
|
129
|
+
"",
|
|
130
|
+
"## Objetivo",
|
|
131
|
+
"",
|
|
132
|
+
objective,
|
|
133
|
+
"",
|
|
134
|
+
"## Fases",
|
|
135
|
+
"",
|
|
136
|
+
"1. Preparar PRD/plan maestro.",
|
|
137
|
+
"2. Ejecutar slices acotados con el orquestador.",
|
|
138
|
+
"3. Procesar reviews y esperar merge externo cuando corresponda.",
|
|
139
|
+
"4. Verificar criterios globales antes de cerrar.",
|
|
140
|
+
"",
|
|
141
|
+
].join("\n");
|
|
142
|
+
}
|