jorgex-stack 1.0.5 → 1.0.7

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 CHANGED
@@ -1,97 +1,97 @@
1
1
  # JorgeX Stack
2
2
 
3
- Harness multi-agente portable: una sola fuente de configuración — 15 agentes, 18 skills, hooks, memoria persistente ([Engram](https://github.com/Gentleman-Programming/engram)), MCPs y system prompt — instalable con un comando en **Claude Code**, **Codex CLI** y **OpenCode**.
3
+ Portable multi-agent harness: one configuration source — 15 agents, 18 skills, hooks, persistent memory ([Engram](https://github.com/Gentleman-Programming/engram)), MCPs, and system prompt — installable with one command in **Claude Code**, **Codex CLI**, and **OpenCode**.
4
4
 
5
- > Inspirado en [gentle-ai](https://github.com/Gentleman-Programming/gentle-ai), reconstruido para el stack JorgeX.
5
+ > Inspired by [gentle-ai](https://github.com/Gentleman-Programming/gentle-ai), rebuilt for the JorgeX stack.
6
6
 
7
- ## Uso
7
+ ## Usage
8
8
 
9
- Instalación y uso vía npm (no requiere clonar el repo):
9
+ Install and run via npm without cloning the repository:
10
10
 
11
11
  ```
12
- pnpm dlx jorgex-stack install # interactivo: elige runtimes y confirma
13
- pnpm dlx jorgex-stack models # picker de modelos por runtime y tier (strong/standard/cheap)
14
- pnpm dlx jorgex-stack sync # re-aplica la config (idempotente; limpia huérfanos)
15
- pnpm dlx jorgex-stack doctor # verifica que todo está sano (Engram, drift, hooks, keys)
16
- pnpm dlx jorgex-stack update # interactivo: scan stack/Engram/skills, multiselect, diff/confirm
17
- # Con --check: solo informe sin cambios
18
- # Con --yes: modo batch (solo informe)
19
- pnpm dlx jorgex-stack restore # restaura un backup
20
- pnpm dlx jorgex-stack uninstall # desinstala lo nuestro y conserva lo del usuario (Engram intacto)
12
+ pnpm dlx jorgex-stack install # interactive: choose runtimes and confirm
13
+ pnpm dlx jorgex-stack models # model picker by runtime and tier (strong/standard/cheap)
14
+ pnpm dlx jorgex-stack sync # reapplies config (idempotent; removes orphans)
15
+ pnpm dlx jorgex-stack doctor # checks that everything is healthy (Engram, drift, hooks, keys)
16
+ pnpm dlx jorgex-stack update # interactive: scans stack/Engram/skills, multiselect, diff/confirm
17
+ # With --check: report only, no changes
18
+ # With --yes: batch mode (report only)
19
+ pnpm dlx jorgex-stack restore # restores a backup
20
+ pnpm dlx jorgex-stack uninstall # uninstalls our files and keeps user data (Engram intact)
21
21
  ```
22
22
 
23
- En desarrollo (desde un clon), los mismos comandos van por `pnpm cli <comando>` (ver [Desarrollo](#desarrollo)).
23
+ For development from a clone, run the same commands through `pnpm cli <command>` (see [Development](#development)).
24
24
 
25
- Todo comando soporta `--dry-run`, `--yes` y `--target-dir <dir>` (pruebas sin tocar la config real). Las escrituras llevan backup automático y verificación de idempotencia; el merge en configs de usuario es quirúrgico (secciones marcadas en markdown, upsert en JSON/TOML) lo tuyo no se toca jamás.
25
+ Every command supports `--dry-run`, `--yes`, and `--target-dir <dir>` for testing without touching the real config. Writes create automatic backups and verify idempotency; merges into user config are surgical (marked markdown sections, JSON/TOML upserts), so user-owned content is never touched.
26
26
 
27
- ### Update: flujo interactivo
27
+ ### Update: Interactive Flow
28
28
 
29
- `update` gestiona tres fuentes:
29
+ `update` manages three sources:
30
30
 
31
- 1. **Stack** (jorgex-stack): detecta si es clon git o instalación global, oferece actualización con confirmación.
32
- 2. **Engram** (binario): detecta la versión instalada, ofrece actualización con **canal nativo** (brew `go install` URL releases). No hace falta parar nada: igual que el upstream en macOS/Linux, los procesos vivos siguen con la versión antigua hasta reiniciar los clientes; en Windows el `.exe` en uso se rota por rename antes de instalar. **Backup automático de la DB antes de actualizar**. La base de datos y las memorias jamás se tocan.
33
- 3. **Skills vendorizadas**: detecta cambios en los upstream registrados en `upstreams.json`, descarga el upstream a temporal, **muestra diff obligatorio** y solicita confirmación. Las skills con cambios locales (`modified: true`) alertan y exigen doble confirmación.
31
+ 1. **Stack** (jorgex-stack): detects whether it is a git clone or a global install, then offers an update with confirmation.
32
+ 2. **Engram** (binary): detects the installed version and offers an update through the **native channel** (brew -> `go install` -> release URL). Nothing needs to be stopped: as in upstream macOS/Linux, live processes keep using the old version until clients restart; on Windows, the in-use `.exe` is rotated by rename before installation. **Automatic DB backup before updating**. The database and memories are never touched.
33
+ 3. **Vendored skills**: detects changes in upstreams registered in `upstreams.json`, downloads the upstream to a temp directory, **shows a mandatory diff**, and asks for confirmation. Skills with local changes (`modified: true`) warn and require double confirmation.
34
34
 
35
- Uso:
36
- - `update --check`: scan de versiones sin aplicar cambios.
37
- - `update` (TTY, sin `--yes`): multiselect interactivo con diffs visibles y confirmaciones paso a paso.
38
- - `update --yes` o sin TTY: se comporta como `--check` (solo informe).
35
+ Usage:
36
+ - `update --check`: scans versions without applying changes.
37
+ - `update` (TTY, without `--yes`): interactive multiselect with visible diffs and step-by-step confirmations.
38
+ - `update --yes` or non-TTY: behaves like `--check` (report only).
39
39
 
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".
40
+ GitHub authentication: requests use `GH_TOKEN`/`GITHUB_TOKEN` from the environment or, if unavailable, the token from your `gh` CLI session (`gh auth token` — local read only, never logged or persisted). Without a token, GitHub limits parallel requests and some upstreams may appear as "offline".
41
41
 
42
- ### Goal Mode de OpenCode
42
+ ### OpenCode Goal Mode
43
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`.
44
+ Goal Mode is an OpenCode plugin for long-running goals: multiple sessions, multiple slices, multiple worktrees, and, when needed, multiple PRs. It is not meant for short tasks. If the change fits without extended autonomy, do not use `/goal`.
45
45
 
46
- Solo vive en OpenCode. Claude Code y Codex no lo reciben.
46
+ It only exists in OpenCode. Claude Code and Codex do not receive it.
47
47
 
48
- Comandos disponibles:
48
+ Available commands:
49
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.
50
+ - `/goal <goal>` — creates a persistent goal.
51
+ - `/goal status` — shows status and next action.
52
+ - `/goal plan` — shows the goal's master plan / PRD.
53
+ - `/goal history` — lists events and transitions.
54
+ - `/goal pause` — pauses the goal.
55
+ - `/goal resume` — resumes the goal.
56
+ - `/goal merged [commit]` — signals that the pending external PR has been merged.
57
+ - `/goal cancel` — cancels the goal.
58
58
 
59
- Lo que no existe:
59
+ What does not exist:
60
60
 
61
61
  - `/goal quick`
62
62
  - `/goal work`
63
63
 
64
- Estado operativo:
64
+ Operational state:
65
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.
66
+ - Separate SQLite database by default at `~/.jorgex-stack/goals/goals.sqlite`.
67
+ - Optional override with `JORGEX_GOAL_DB`, but always inside `~/.jorgex-stack/goals/`.
68
+ - Engram is not the goal's operational store: it remains memory/protocol, not the state database.
69
+ - Goal Mode does not perform automatic merges; when it must wait for an external merge, the state becomes `waiting_for_merge`.
70
+ - The integration uses experimental OpenCode hooks (`experimental.chat.system.transform` and `experimental.session.compacting`), so that surface may change.
71
71
 
72
- ## Estado
72
+ ## Status
73
73
 
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 (D1D9) y el roadmap están en [PRD.md](PRD.md).
74
+ The CLI is complete and the real migration has been executed (F6); the stack is the only configuration source. Versions are published automatically to [npm](https://www.npmjs.com/package/jorgex-stack) according to the flow described in [Publishing](#publishing). The design, decisions (D1-D9), and roadmap are in [PRD.md](PRD.md).
75
75
 
76
- ## Publicación
76
+ ## Publishing
77
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:
78
+ Releases are triggered by push/merge to `main` and GitHub Actions; there is also a recovery `workflow_dispatch` on `main` with an optional `release_sha`. `validate` resolves the target SHA once and exposes it as `target_sha`; `bump` reuses that SHA. If you do not pass `release_sha`, `validate` pins `target_sha` to `origin/main` after `fetch`; if you do pass it, it must be a full 40-hex SHA that belongs to `main` or the workflow fails red with recovery instructions. Running without `release_sha` is only valid to publish `origin/main` when the version does not exist on npm yet; if the version already exists and the tag is missing, the workflow fails and requires `workflow_dispatch` with `release_sha=<published sha>`. If the diff mixes publishable changes with `.github/workflows/*`, auto-release stops before bump/publish because GitHub may reject the tag push without workflow permissions; split the release or use manual publish/tag with elevated permissions. `pnpm publish` is not used and npm login is not required:
79
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.
80
+ - **Automatic patch**: if the push to `main` contains publishable changes and the current `package.json` version already exists on npm, the workflow finds the first free patch (`x+1`, `x+2`, ...), commits `chore(release): bump version to v...`, and publishes. If tag `v<package.version>` already exists, it uses that point as the accumulated base; otherwise, it falls back to `github.event.before`. Obsolete runs are aborted after `git fetch origin main --tags` if `origin/main` no longer matches `GITHUB_SHA`.
81
+ - **Manual recovery**: a manual run on `main` with `release_sha` publishes that SHA if it does not exist on npm yet, without bumping again; if the version already exists on npm but tag `v<version>` is missing, the workflow fails and forces a rerun with `release_sha=<published sha>` to avoid tagging `origin/main`. `release_sha` must be a full 40-hex SHA and belong to `main`; mutable refs (`main`, tags, `main~1`) are rejected. If you do not pass `release_sha`, `validate` resolves `origin/main` once, exposes it as `target_sha`, and `bump` uses that validated SHA. Recovery does not bypass the `.github/workflows/*` guard: if the diff mixes workflows with publishable changes, split the release or perform the tag/publish manually with elevated permissions. If there is no reachable previous release tag to reconstruct the range, the workflow fails closed and requires manual intervention.
82
+ - **No release**: changes only in `work/`, `worktrees/`, tests, or files not listed as publishable (`src/`, `stack/`, `upstreams.json`, `package.json`, `pnpm-lock.yaml`, `tsconfig.json`, `tsup.config.ts`, `README.md`, `PRD.md`) do not create a release.
83
+ - **Manual minor and major**: explicit bump in `package.json` in the PR (the workflow detects that the next patch already exists on npm and requires the bump).
84
+ - **OIDC / trusted publishing**: the publishing job uses `id-token: write` and `setup-node` `registry-url`; the bump/push job only has `contents: write`; `tag-release` only writes `contents` and does not use OIDC. There is no `NPM_TOKEN` or `NODE_AUTH_TOKEN` in any secret. `tag-release` only runs if `publish` was `success` or `skipped` with `tag_needed=true`, and keeps its SHA validation as the final defense. The only exception to the "always pnpm" rule is `npm pack --dry-run --ignore-scripts` and `npm publish --ignore-scripts --provenance` in the final step, for registry compatibility and hardening.
85
85
 
86
- Los detalles de diseño están en [PRD §7.6](PRD.md#76-publicación-automática-en-npm).
86
+ Design details are in [PRD §7.6](PRD.md).
87
87
 
88
- ## Desarrollo
88
+ ## Development
89
89
 
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.
90
+ Requirements: Node >= 22.5 and pnpm (never npm). Goal Mode uses `node:sqlite` in tests/Node CLI and OpenCode uses `bun:sqlite` at runtime.
91
91
 
92
92
  ```
93
93
  pnpm install
94
- pnpm build # tsup dist/
94
+ pnpm build # tsup -> dist/
95
95
  pnpm typecheck
96
96
  pnpm test # vitest
97
97
  pnpm cli --help
package/dist/cli.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import * as p6 from "@clack/prompts";
5
+ import { pathToFileURL as pathToFileURL2 } from "url";
5
6
 
6
7
  // src/install.ts
7
8
  import fs11 from "fs";
@@ -2752,6 +2753,8 @@ function parseFlags(args) {
2752
2753
  agents: [],
2753
2754
  dryRun: false,
2754
2755
  yes: false,
2756
+ help: false,
2757
+ version: false,
2755
2758
  list: false,
2756
2759
  check: false,
2757
2760
  removeEngram: false,
@@ -2765,6 +2768,8 @@ function parseFlags(args) {
2765
2768
  else if (arg.startsWith("--target-dir=")) flags.targetDir = arg.slice(13);
2766
2769
  else if (arg === "--dry-run") flags.dryRun = true;
2767
2770
  else if (arg === "--yes" || arg === "-y") flags.yes = true;
2771
+ else if (arg === "--help" || arg === "-h") flags.help = true;
2772
+ else if (arg === "--version" || arg === "-v") flags.version = true;
2768
2773
  else if (arg === "--list") flags.list = true;
2769
2774
  else if (arg === "--check") flags.check = true;
2770
2775
  else if (arg === "--remove-engram") flags.removeEngram = true;
@@ -2772,6 +2777,23 @@ function parseFlags(args) {
2772
2777
  }
2773
2778
  return flags;
2774
2779
  }
2780
+ function parseCliArgs(argv) {
2781
+ const [first, ...rest] = argv;
2782
+ const isCommand = COMMANDS.includes(first ?? "install");
2783
+ if (first !== void 0 && !isCommand && !first.startsWith("-")) {
2784
+ return {
2785
+ action: "unknown",
2786
+ command: "install",
2787
+ flags: parseFlags(rest),
2788
+ unknownCommand: first
2789
+ };
2790
+ }
2791
+ const command = isCommand ? first ?? "install" : "install";
2792
+ const flags = parseFlags(isCommand ? rest : argv);
2793
+ if (first === "--help" || first === "-h" || flags.help) return { action: "help", command, flags };
2794
+ if (first === "--version" || first === "-v" || flags.version) return { action: "version", command, flags };
2795
+ return { action: "run", command, flags };
2796
+ }
2775
2797
  async function resolveRuntimes(flags) {
2776
2798
  if (flags.agents.length > 0) return flags.agents;
2777
2799
  const detected = Object.values(ADAPTERS).filter((a) => a.detect().installed);
@@ -2812,18 +2834,16 @@ Opciones:
2812
2834
  Ver PRD.md para el dise\xF1o completo.`);
2813
2835
  }
2814
2836
  async function main() {
2815
- const [first, ...rest] = process.argv.slice(2);
2816
- if (first === "--help" || first === "-h") return printHelp();
2817
- if (first === "--version" || first === "-v") return console.log(VERSION);
2818
- const isCommand = COMMANDS.includes(first ?? "install");
2819
- if (first !== void 0 && !isCommand && !first.startsWith("-")) {
2820
- console.error(`Comando desconocido: ${first}`);
2837
+ const parsed = parseCliArgs(process.argv.slice(2));
2838
+ if (parsed.action === "help") return printHelp();
2839
+ if (parsed.action === "version") return console.log(VERSION);
2840
+ if (parsed.action === "unknown") {
2841
+ console.error(`Comando desconocido: ${parsed.unknownCommand}`);
2821
2842
  printHelp();
2822
2843
  process.exitCode = 1;
2823
2844
  return;
2824
2845
  }
2825
- const command = isCommand ? first ?? "install" : "install";
2826
- const flags = parseFlags(isCommand ? rest : process.argv.slice(2));
2846
+ const { command, flags } = parsed;
2827
2847
  if (flags.targetDir !== void 0 && flags.agents.length !== 1) {
2828
2848
  console.error("--target-dir requiere exactamente un runtime en --agents.");
2829
2849
  process.exitCode = 1;
@@ -2929,7 +2949,13 @@ async function main() {
2929
2949
  }
2930
2950
  }
2931
2951
  }
2932
- main().catch((err) => {
2933
- console.error(err instanceof Error ? err.message : String(err));
2934
- process.exitCode = 1;
2935
- });
2952
+ if (process.argv[1] !== void 0 && import.meta.url === pathToFileURL2(process.argv[1]).href) {
2953
+ main().catch((err) => {
2954
+ console.error(err instanceof Error ? err.message : String(err));
2955
+ process.exitCode = 1;
2956
+ });
2957
+ }
2958
+ export {
2959
+ parseCliArgs,
2960
+ parseFlags
2961
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jorgex-stack",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
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",
@@ -174,7 +174,7 @@ function replaceGoalCommandPrompt(input: unknown, output: HookOutput, text: stri
174
174
 
175
175
  if (Array.isArray(output.parts)) {
176
176
  output.parts.splice(0, output.parts.length, {
177
- id: `part_${randomUUID()}`,
177
+ id: createOpenCodeID("prt"),
178
178
  sessionID: extractHookSessionID(input, output),
179
179
  messageID: extractHookMessageID(input, output),
180
180
  type: "text",
@@ -226,7 +226,7 @@ function extractHookSessionID(input: unknown, output: HookOutput): string {
226
226
  if (typeof sessionID === "string" && sessionID.trim()) return sessionID;
227
227
  }
228
228
 
229
- return `session_${randomUUID()}`;
229
+ return createOpenCodeID("ses");
230
230
  }
231
231
 
232
232
  function extractHookMessageID(input: unknown, output: HookOutput): string {
@@ -241,7 +241,7 @@ function extractHookMessageID(input: unknown, output: HookOutput): string {
241
241
  if (typeof id === "string" && id.trim()) return id;
242
242
  }
243
243
 
244
- return `msg_${randomUUID()}`;
244
+ return createOpenCodeID("msg");
245
245
  }
246
246
 
247
247
  function upsertMarkedBlock(text: string, block: string): string {
@@ -288,3 +288,9 @@ function readEventStateSequence(data: unknown): number | undefined {
288
288
  function isRecord(value: unknown): value is Record<string, unknown> {
289
289
  return typeof value === "object" && value !== null && !Array.isArray(value);
290
290
  }
291
+
292
+ // OpenCode validates entity IDs by prefix: parts must start with "prt", sessions with "ses", messages with "msg".
293
+ // The UUID is hex-only (dashes removed) to avoid confusion with uuid format.
294
+ function createOpenCodeID(prefix: "prt" | "ses" | "msg"): string {
295
+ return `${prefix}_${randomUUID().replace(/-/g, "")}`;
296
+ }
package/PRD.md DELETED
@@ -1,310 +0,0 @@
1
- # PRD — JorgeX Stack
2
-
3
- > Harness multi-agente portable: una sola fuente de configuración (agentes, skills, hooks, memoria Engram, MCPs, system prompt) instalable con un comando en **Claude Code**, **Codex CLI** y **OpenCode**.
4
-
5
- **Estado**: v0 — documento vivo. Última actualización: 2026-06-09.
6
-
7
- ---
8
-
9
- ## 1. Problema
10
-
11
- La config actual vive solo en `C:\Users\jorge\.config\opencode` y tiene estos problemas:
12
-
13
- 1. **Atada a OpenCode**: 15 agentes, 18 skills, plugins de Engram/hooks y el system prompt no sirven en Claude Code ni Codex sin porte manual.
14
- 2. **Dependencias frágiles**: `hooks.ts` y `photo-heart-worktree.ts` son re-exports a rutas locales (`file:///C:/Users/jorge/Desktop/jorgex-custom-tools/...`) que no existen en otra máquina.
15
- 3. **Sin gestión de terceros**: Engram (Gentleman-Programming) y varias skills open-source no tienen tracking de versión ni vía de actualización.
16
- 4. **Sin instalación reproducible**: montar este setup en otra máquina (o restaurarlo) es trabajo manual.
17
- 5. **Secretos en claro**: `opencode.json` tiene API keys hardcodeadas (Context7, Hostinger) — inaceptable en un repo versionado.
18
-
19
- ## 2. Visión
20
-
21
- Lo mismo que hace [gentle-ai](https://github.com/Gentleman-Programming/gentle-ai) pero con el stack de Jorge: un repo único con la config canónica + un CLI que la **instala, sincroniza y actualiza** en los tres runtimes, adaptando formatos automáticamente.
22
-
23
- ```
24
- pnpm dlx jorgex-stack → TUI: detecta agentes instalados, eliges uno/varios/los 3, instala
25
- pnpm dlx jorgex-stack sync → re-aplica la config (idempotente)
26
- pnpm dlx jorgex-stack update → actualiza stack + terceros (Engram, skills upstream)
27
- pnpm dlx jorgex-stack doctor → verifica que todo está sano
28
- ```
29
-
30
- ## 3. Decisiones tomadas (cerradas con Jorge, 2026-06-09/10)
31
-
32
- | # | Decisión | Elección |
33
- |---|----------|----------|
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
- | D2 | Plataformas | **Cross-platform desde v1** (Windows + macOS + Linux). Windows es el entorno principal de pruebas. |
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
- | 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. |
38
- | D5 | MCPs incluidos | Solo **engram** (local, sin key) y **context7** (placeholder vacío: cada usuario conecta su cuenta/key al instalar o después). Hostinger eliminado. **Ninguna key personal de la config actual de Jorge pasa a este proyecto, jamás.** |
39
- | D6 | Selección de modelos | Por runtime, en el install: Claude Code ofrece solo modelos Claude (alias auto-actualizables: `fable`/`opus`/`sonnet`/`haiku`), Codex solo OpenAI, OpenCode **detecta y ofrece todos los que el usuario tenga conectados** (`opencode models`). Ver §6.1. |
40
- | D7 | Engram existente | La instalación de Engram (binario + **base de datos de memorias en `~/.engram`**) es **intocable en TODOS los flujos**: `install`/`sync` solo detectan y registran (jamás reinstalan, migran ni escriben en la DB); `update` solo INFORMA de releases (actualizar el binario es acción del usuario); `uninstall` **conserva por defecto** todo lo de Engram (registro MCP, plugin engram.ts) — desregistrarlo exige el sí explícito (`--remove-engram` o confirmación interactiva con default No), y ni con eso se tocan binario o DB. Repo upstream: https://github.com/Gentleman-Programming/engram |
41
- | D8 | Gestor de paquetes | **pnpm siempre, nunca npm** — desarrollo, scripts, instalación de dependencias y cualquier instalación que haga el CLI (`pnpm dlx`, `pnpm add -g`). |
42
- | D9 | work/ vs Engram | **Una sola casa por artefacto, cero duplicación** (v2, 2026-06-11): `work/{nombre}/` (gitignorada, SOLO trabajo en curso) contiene `PRD.md` + `plan.md` — el plan es el único tablero de estado (edits quirúrgicos). Engram guarda lo que consumen los agentes y el historial: spec completa de cada tarea (`work/{nombre}/task/{NN}`, el subagente recibe topic_key + título), resultados de fase (`work/{nombre}/{fase}`), cierre (`work/{nombre}/done`) y el backlog del proyecto en la clave ÚNICA `work/backlog`. Al cerrar: PRD a `docs/` solo si tiene valor duradero y la carpeta se borra. Sin `1-TODOs/` ni `3-finalized/`. Ver §9.11. |
43
-
44
- ## 4. Objetivos
45
-
46
- 1. Un comando instala la config completa (o por componentes) en Claude Code, Codex y/u OpenCode, a elección.
47
- 2. Fuente canónica única: cada agente/skill/hook se define UNA vez; los adapters generan el formato de cada runtime.
48
- 3. Paridad funcional con el setup actual de OpenCode (no perder nada en la migración).
49
- 4. Engram funcionando en los tres runtimes (MCP + protocolo de memoria + captura pasiva donde sea posible).
50
- 5. Hooks funcionando en los tres (nativos en Claude Code y Codex; plugin puente en OpenCode).
51
- 6. `update` gestiona: el propio stack, el binario de Engram y las skills de terceros (con fuente y versión registradas).
52
- 7. Mejorar el harness actual, no solo portarlo (ver §9).
53
-
54
- ### No-objetivos (v1)
55
-
56
- - Soportar más runtimes (Cursor, Gemini CLI, etc.) — la arquitectura adapter lo deja abierto para v2.
57
- - TUI elaborada tipo Bubbletea — prompts simples de clack bastan.
58
- - Self-update agresivo en cada invocación (decisión consciente contra el default de gentle-ai): `update --check` manual o aviso no bloqueante.
59
- - Skill registry con cache por fingerprint (idea buena de gentle-ai → backlog v1.x).
60
- - Instalación scope-proyecto (v1 solo global/usuario; proyecto en v2).
61
-
62
- ## 5. Arquitectura del repo
63
-
64
- ```
65
- JorgeX Stack/
66
- ├── PRD.md
67
- ├── README.md
68
- ├── package.json # bin: jorgex-stack
69
- ├── stack/ # ══ FUENTE CANÓNICA (lo que se instala) ══
70
- │ ├── system-prompt/
71
- │ │ └── AGENTS.md # system prompt global (hoy: ~/.config/opencode/AGENTS.md, mejorado)
72
- │ ├── agents/ # 15 agentes en formato canónico (md + frontmatter propio)
73
- │ │ ├── orchestrator.md
74
- │ │ ├── backend-analyst.md … type-design-analyzer.md
75
- │ ├── skills/ # TODAS las skills vendorizadas (terceros con upstream registrado en upstreams.json)
76
- │ ├── commands/ # xreview.md, lean-audit.md (formato canónico)
77
- │ ├── hooks/
78
- │ │ └── hooks.json # definición canónica de hooks (formato Claude Code como base)
79
- │ ├── scripts/
80
- │ │ └── post-pr-review.cjs
81
- │ ├── mcp/
82
- │ │ └── servers.json # manifiesto MCP canónico (env refs, SIN secretos)
83
- │ └── plugins/
84
- │ └── opencode/ # engram.ts, hooks-bridge.ts, worktree.ts (solo OpenCode)
85
- ├── upstreams.json # terceros: fuente, versión instalada, método de update
86
- ├── src/ # ══ CLI ══
87
- │ ├── cli.ts # entrypoint: install | sync | update | doctor | uninstall | restore
88
- │ ├── adapters/
89
- │ │ ├── types.ts # interface Adapter (rutas + estrategias por runtime)
90
- │ │ ├── claude-code.ts
91
- │ │ ├── codex.ts
92
- │ │ └── opencode.ts
93
- │ ├── components/ # lógica por componente, agnóstica del runtime
94
- │ │ ├── system-prompt.ts agents.ts skills.ts commands.ts
95
- │ │ ├── hooks.ts mcp.ts engram.ts plugins.ts
96
- │ ├── lib/
97
- │ │ ├── filemerge.ts # merge por marcadores (md) + upsert JSON/JSONC/TOML
98
- │ │ ├── backup.ts # snapshot tar.gz con retención + restore
99
- │ │ └── detect.ts # qué runtimes hay instalados (binario en PATH + dir config)
100
- │ └── …
101
- └── tests/ # unit (filemerge, adapters) + paridad entre runtimes
102
- ```
103
-
104
- **Patrón central (de gentle-ai)**: `Adapter` por runtime declara *dónde* (rutas) y *cómo* (estrategias: merge de system prompt, formato de agentes, sintaxis MCP…). Los componentes iteran (componente × runtime) sin un solo `switch`. Añadir un runtime nuevo = un archivo adapter.
105
-
106
- **Pipeline de instalación**: detect → selección → plan (dry-run visible) → **backup** → aplicar componentes → verificar → (si falla) rollback.
107
-
108
- ## 6. Mapeo por runtime
109
-
110
- | Componente | Canónico | Claude Code | Codex CLI | OpenCode |
111
- |---|---|---|---|---|
112
- | System prompt | `stack/system-prompt/AGENTS.md` | sección con marcadores en `~/.claude/CLAUDE.md` | sección en `~/.codex/AGENTS.md` | sección en `~/.config/opencode/AGENTS.md` |
113
- | Agentes (subagentes) | `stack/agents/*.md` | `~/.claude/agents/*.md` (frontmatter `name/description/tools/model`) | `~/.codex/agents/*.toml` (`developer_instructions`, `model_reasoning_effort`, `sandbox_mode`) | `~/.config/opencode/agents/*.md` (`mode/model/tools/permission`) |
114
- | **Orchestrator (primary)** | `stack/agents/orchestrator.md` | **Output style** `~/.claude/output-styles/orchestrator.md` (modifica el system prompt del MAIN agent; se elige con `/config` y persiste) + **skill** `~/.claude/skills/orchestrator/` como activación puntual (`/orchestrator` explícito o carga implícita por description) | **Profile** `~/.codex/orchestrator.config.toml` con `developer_instructions` → `codex --profile orchestrator` + **la misma skill** en `~/.agents/skills/orchestrator/` (los commands de Codex están deprecados; skills es la vía oficial) | **Primary agent** nativo: en el ciclo de Tab junto a build/plan |
115
- | Skills | `stack/skills/` + upstreams | copia espejo en `~/.claude/skills/` (verificado 2026-06: Claude Code NO lee `~/.agents/skills` — sigue agentskills.io solo en formato) | **`~/.agents/skills/`** (estándar agentskills.io — NO `~/.codex/skills`) | **misma copia que Codex**: lee `~/.agents/skills/` global nativo (verificado en código fuente; si una skill existe también en `~/.config/opencode/skills/` esa gana — F6 limpia las legacy de ahí) |
116
- | Commands | `stack/commands/*.md` | `~/.claude/commands/*.md` | como skills (`~/.codex/prompts/` está deprecated) | `~/.config/opencode/commands/*.md` |
117
- | Hooks | `stack/hooks/hooks.json` | merge en `~/.claude/settings.json` → clave `hooks` | `~/.codex/hooks.json` (⚠ requiere trust manual vía `/hooks`) | **plugin puente** `hooks-bridge.ts` (OpenCode no tiene hooks declarativos) |
118
- | MCP | `stack/mcp/servers.json` | `claude mcp add --scope user` o merge en `~/.claude.json` | bloques `[mcp_servers.x]` upsert en `~/.codex/config.toml` | clave `mcp` upsert en `opencode.json` (`command` es **array**, `environment` no `env`) |
119
- | Engram | binario Go + MCP + protocolo | MCP user-scope + protocolo en sección de CLAUDE.md | MCP en config.toml + protocolo en AGENTS.md | MCP + plugin `engram.ts` completo (captura pasiva, compaction, inyección) |
120
- | Plugins TS | `stack/plugins/opencode/` | n/a (funcionalidad cubierta por hooks nativos) | n/a (ídem) | `~/.config/opencode/plugins/` |
121
-
122
- **Notas de skills**: una sola copia física en `~/.agents/skills/` sirve a Codex y OpenCode; para Claude Code el instalador mantiene copia espejo en `~/.claude/skills/` (sin symlinks: en Windows requieren Developer Mode). `sync` mantiene ambas alineadas. Frontmatter común seguro: `name` + `description` (extensiones de Claude como `context: fork` solo en la copia de Claude).
123
-
124
- ### 6.1 Modelos: tiers canónicos + picker por runtime
125
-
126
- La config actual referencia modelos vía OpenCode multi-provider (`openai/gpt-5.4`, `minimax/MiniMax-M3`). Eso no es portable. El formato canónico asigna a cada agente un **tier** (`strong | standard | cheap`) y el install resuelve cada tier a un modelo concreto **por runtime, con un picker**:
127
-
128
- | Runtime | Qué ofrece el picker | ¿Se actualiza solo? |
129
- |---|---|---|
130
- | Claude Code | Solo modelos Claude: alias `fable` / `opus` / `sonnet` / `haiku` / `inherit` (+ ID concreto opcional). `fable` es el nivel nuevo por encima de opus (Fable 5, `claude-fable-5`, 2026) | **Sí** — los alias apuntan siempre al modelo más reciente de cada familia; cuando Anthropic añade una familia nueva (como fable) basta re-ejecutar `jorgex-stack models` |
131
- | Codex | Solo OpenAI: `default` (omitir `model` → usa el default vigente del CLI) o ID concreto + `model_reasoning_effort` (high/medium/low) por tier | **Solo si usas `default`** — el CLI lo actualiza con sus releases. Un ID fijado es manual: se cambia re-ejecutando el picker (`jorgex-stack models`) |
132
- | OpenCode | **Todos los modelos que el usuario tenga conectados**, detectados en vivo con `opencode models` (registry models.dev, verificado en la máquina de Jorge) | **Sí** — la lista refleja providers/modelos conectados en el momento de instalar; nuevos modelos aparecen al re-ejecutar el picker |
133
-
134
- - Defaults sensatos pre-seleccionados por tier (strong → análisis/review/seguridad/orchestrator; standard → implementer/tester; cheap → translator/docs/comments/engram), confirmables con Enter.
135
- - La elección se guarda en `model-map.json` (local del usuario, no en el repo) y `sync` la respeta.
136
- - Comando dedicado `jorgex-stack models` para re-escoger sin reinstalar.
137
-
138
- **Regla del orchestrator (cerrada con Jorge, 2026-06-10)**: el orchestrator es SIEMPRE un modo del agente principal que el usuario pilota — **nunca un subagente que se invoca**. OpenCode lo soporta nativo (primary + Tab). En Claude Code y Codex, que no tienen primary seleccionable, se instalan dos vías generadas de la misma fuente canónica: (a) el **modo persistente** — output style en Claude Code (`/config`), profile en Codex (`codex --profile orchestrator`, `developer_instructions`); y (b) la **skill `orchestrator`** para activación puntual dentro de una sesión — elegida frente al command porque una misma SKILL.md sirve en ambos runtimes (estándar agentskills.io), permite invocación explícita (`/orchestrator` · `$orchestrator`) e implícita por description, y los custom prompts de Codex están deprecados. Verificado contra docs y código (openai/codex): no hay modos custom seleccionables en caliente en ninguno de los dos; si los añaden, se migra a eso.
139
-
140
- ## 7. Componentes en detalle
141
-
142
- ### 7.1 Hooks — la pieza con más fricción
143
-
144
- - **Formato canónico**: el de Claude Code (`hooks.json` con eventos `SessionStart`, `PreToolUse`, `PostToolUse`, `Stop`…). Codex usa un formato casi idéntico (mismos eventos núcleo, añade `commandWindows` para Windows — lo usamos).
145
- - **Claude Code**: merge en `settings.json`. Nativo.
146
- - **Codex**: escribir `~/.codex/hooks.json`. ⚠ Los hooks no-managed exigen aprobación manual con `/hooks` — el instalador no puede activarlos solo. `doctor` lo detecta y lo recuerda.
147
- - **OpenCode**: no hay hooks declarativos → `hooks-bridge.ts` (evolución del `hooks.ts` actual): plugin que lee el `hooks.json` canónico y traduce eventos (`PostToolUse` + matcher bash → `tool.execute.after`, `Stop` → `session.idle`, `SessionStart` → init del plugin).
148
- - **Hook actual a portar**: post-`gh pr create` → ejecuta `post-pr-review.cjs` (routing ligero de subagentes de review sobre `git diff BASE...HEAD`). Debe funcionar igual en los tres.
149
-
150
- ### 7.2 Engram
151
-
152
- - Binario Go de Gentleman-Programming ([repo](https://github.com/Gentleman-Programming/engram)). Sirve CLI + MCP server (`engram mcp --tools=agent`). **El binario y los datos van separados**: el binario donde lo instale el método elegido (brew · `go install` → `~/go/bin` · zip de Releases) y los DATOS siempre en `~/.engram/engram.db` (override: `ENGRAM_DATA_DIR`) — esa carpeta es la que D7 protege.
153
- - **Integración oficial por runtime (verificado 2026-06)**: Engram trae `engram setup <agent>` (claude-code, codex, opencode…) y un plugin de marketplace oficial SOLO para Claude Code (`claude plugin marketplace add Gentleman-Programming/engram` + `claude plugin install engram`: MCP + hooks de sesión + skill memory). En OpenCode, `engram setup opencode` escribe el plugin `engram.ts` (el que este stack vendoriza) + MCP en opencode.json; en Codex escribe `[mcp_servers.engram]` + `engram-instructions.md` + compact prompt. No publica marketplace para Codex.
154
- - **Política del stack**: detectar la integración oficial y respetarla — si existe, NO se registra el MCP (duplicaría las tools `mem_*`) y NO se inyecta la sección `engram-protocol` en el system prompt (el plugin/setup ya inyecta el protocolo). En OpenCode la sección no se inyecta nunca: el plugin `engram.ts` que el propio stack instala la aporta en runtime (consolidación de la duplicación detectada en F1). Donde no haya integración, el stack registra el MCP básico + la sección de protocolo, y `doctor`/install sugieren `engram setup <agent>` para la integración completa.
155
- - **Regla D7 — instalación existente intocable**: si el instalador detecta un Engram ya instalado (binario en PATH o ruta conocida, p.ej. `C:\Users\jorge\go\bin\engram.exe`, y/o base de datos existente), lo usa tal cual: registra el MCP apuntando al binario detectado y NO descarga, NO reinstala, NO migra y NO toca la DB (que en el caso de Jorge está llena de memorias en uso). Solo si NO hay Engram en la máquina: descarga release de GitHub con **SHA256 fail-closed**.
156
- - En todos los casos: registra MCP en los runtimes elegidos → inyecta el protocolo de memoria (sección marcada) en el system prompt de cada uno.
157
- - **Una sola fuente del protocolo**: hoy está duplicado (AGENTS.md + inyección del plugin engram.ts). Se consolida: el texto vive en `stack/system-prompt/` y se inyecta una vez por runtime. En OpenCode el plugin deja de inyectar el bloque largo (o se hace la única vía, pero no ambas).
158
- - En OpenCode se conserva el plugin completo (captura pasiva, session resilience, compaction handling) — es la integración más rica y se mantiene.
159
- - `update` trata Engram como tool gestionada (release de GitHub, comparación de versión), pero **solo actualiza el binario con confirmación explícita** y nunca toca la base de datos.
160
-
161
- ### 7.3 Update: política y flujo
162
-
163
- `upstreams.json` registra cada pieza de terceros (ejemplo de formato):
164
-
165
- ```json
166
- {
167
- "tools": {
168
- "engram": { "kind": "binary", "source": "github:Gentleman-Programming/engram", "verify": "sha256", "policy": "respect-existing — D7" }
169
- },
170
- "skills": {
171
- "skill-name": { "source": "github:org/repo", "commit": "...", "modified": false }
172
- }
173
- }
174
- ```
175
-
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
-
178
- **Política de `update` (F5.x — implementada con flujo interactivo)**:
179
-
180
- 1. **`update --check`** (sin TTY o con `--yes`): compara versión local vs upstream (tags/commits de GitHub) y **solo lista** qué hay nuevo. Respeta D7 y D8: no toca nada sin confirmación explícita.
181
-
182
- 2. **`update` interactivo** (TTY + sin `--yes`):
183
- - **Escanea 3 fuentes en paralelo**: stack (npm), Engram (GitHub releases), skills (commit pins en upstreams.json por repo único).
184
- - **Multiselect**: ofrece marcar lo actualizable (stack, Engram, skills por repo con upstream movido).
185
- - **Stack**: detecta clon git o instalación global; ofrece `git pull + pnpm install + pnpm build` o `pnpm add -g jorgex-stack@latest` con confirmación.
186
- - **Engram** (D7 reforzado):
187
- * Detecta si el proceso está en ejecución (bloquea en Windows) y advierte.
188
- * Ofrece **backup de la DB** (`~/.engram/engram.db`) a `~/.jorgex-stack/` ANTES de actualizar el binario.
189
- * Usa **canal nativo** replicado: brew → `go install` → URL de releases.
190
- * La DB y las memorias **jamás** se tocan; solo el binario se puede actualizar con confirmación explícita.
191
- - **Skills**:
192
- * Descarga upstream a temporal y **muestra diff SIEMPRE** (obligatorio antes de aplicar).
193
- * Las skills `modified: true` alertan y exigen doble confirmación (los cambios locales se sobreescriben con backup automático).
194
- * Reemplaza la copia vendorizada y re-pined el commit en upstreams.json.
195
- - **Backup automático** de `~/.jorgex-stack/manifest.json` antes de cualquier cambio.
196
- - **Verificación post-update**: detecta engram actualizado y reporta la nueva versión.
197
-
198
- 3. **Con `--yes` o sin TTY**: se comporta como `--check` (solo informe).
199
-
200
- ### 7.4 MCPs
201
-
202
- Solo dos MCPs en el stack (D5):
203
-
204
- 1. **engram** — local, apunta al binario detectado. No necesita key.
205
- 2. **context7** — remoto, se instala **vacío** (sin key). Cada usuario conecta su cuenta: el instalador ofrece introducir la key opcionalmente (se escribe SOLO en la config local del runtime) o dejarlo en blanco y configurarla después. Hostinger queda fuera.
206
-
207
- **Regla dura**: el manifiesto canónico (`stack/mcp/servers.json`) solo contiene referencias de entorno/placeholders. Ninguna key personal de la config actual de Jorge (`opencode.json`) se copia a este repo, al instalador ni a sus artefactos — ni siquiera en ejemplos, tests o fixtures. CI check de secretos en F5.
208
-
209
- ### 7.5 Scripts y plugins de jorgex-custom-tools (D4)
210
-
211
- - `hooks.ts` (HooksPlugin) y `worktree-plugin.ts` (WorktreePlugin) de `C:\Users\jorge\Desktop\jorgex-custom-tools\plugins\hooks\src\` se **copian** a `stack/plugins/opencode/` y se adaptan (rutas relativas, sin `file:///C:/Users/jorge/...`).
212
- - Los originales **no se tocan** mientras dure el desarrollo (están en uso).
213
- - F6 (cierre): eliminar de jorgex-custom-tools, reinstalar todo desde JorgeX Stack.
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
-
228
- ## 8. CLI — UX
229
-
230
- ```
231
- pnpm dlx jorgex-stack # = install interactivo
232
- ✔ Detectados: OpenCode ✓ Claude Code ✓ Codex ✗ (no instalado)
233
- ✔ Engram existente detectado: C:\Users\jorge\go\bin\engram.exe → se respeta (D7)
234
- ? ¿Para qué agentes instalar? [multiselect: los detectados]
235
- ? Componentes: [todos | agentes, skills, hooks, engram, mcp, system-prompt…]
236
- ? Modelos por tier (picker por runtime, §6.1):
237
- Claude Code → opus / sonnet / haiku (alias)
238
- Codex → default / ID + reasoning effort
239
- OpenCode → lista en vivo de `opencode models`
240
- ? Context7: ¿key? [input / dejar vacío y conectar después]
241
- → Plan (dry-run) → confirmación → backup → instalación → verificación
242
-
243
- jorgex-stack install --agents claude,opencode --components all --dry-run --yes
244
- jorgex-stack sync # re-aplica config (idempotente, tras editar el repo)
245
- jorgex-stack models # re-escoger modelos por tier sin reinstalar
246
- jorgex-stack update [--check] # stack + engram (solo binario, con confirmación) + skills upstream
247
- jorgex-stack doctor # binarios, MCPs responden, hooks trusted (codex), engram serve vivo, versiones
248
- jorgex-stack restore [--list] # restaurar backup
249
- jorgex-stack uninstall [--agents ...] # quita solo lo nuestro (secciones marcadas + archivos propios)
250
- ```
251
-
252
- Todo comando soporta `--dry-run` y no-interactivo (`--yes` + flags) para CI/scripts.
253
-
254
- ## 9. Mejoras al harness (no solo portar)
255
-
256
- Detectadas en la auditoría de la config actual + ideas de gentle-ai:
257
-
258
- 1. **Orchestrator — handoff explícito**: documentar la secuencia ANALYZE → PLAN → IMPLEMENT (hoy el paso analyst → implementer queda implícito) y que el orchestrator DEBE procesar las líneas de delegación `→ [agente]: …` que devuelven los subagentes.
259
- 2. **Escape valve medible**: sustituir el "if the work turns out to be single scope" por criterios concretos (ej.: <3 archivos, 1 capa, sin cambio de contrato público → se permite saltar PRD).
260
- 3. **Deslindar solapamientos**: `code-reviewer` (bugs + guidelines) vs `code-simplifier` (claridad/estructura); renombrar o re-describir `test-analyzer` → deja claro que NUNCA escribe tests (eso es `tester`).
261
- 4. **Result contract en subagentes** (de gentle-ai): todo subagente termina con `status / summary / artifacts / delegations / risks` + `mem_save` con `topic_key` estable antes de reportar → las cadenas largas sobreviven a cortes de sesión.
262
- 5. **Engram visible cuando falla**: el plugin hoy falla en silencio si `engram serve` no corre. Mínimo: warning una vez por sesión.
263
- 6. **Protocolo de memoria sin duplicar** (ver §7.2).
264
- 7. **Tiers de modelo** en lugar de modelos hardcodeados (§6).
265
- 8. **Secretos fuera de la config** (§7.4).
266
- 9. **Backups con retención** en lugar de los `opencode.json.bak-*` manuales acumulados.
267
- 10. **Limpieza**: `rules/` y `prompts/` vacíos, backups sueltos y archivos de estado no se migran.
268
- 11. **Una sola casa por artefacto: `work/{nombre}/` para lo que revisa el humano, Engram para lo que consumen los agentes (D9 v2)**. Referencia gentle-ai: su default es memory-first puro (topic_keys `sdd/{cambio}/{artefacto}`), con un modo `openspec` de archivos para equipos y un `hybrid` que escribe en ambos (~2x tokens — descartado). El stack toma la partición sin duplicar: **(a)** `work/{nombre}/` (gitignorada — es andamiaje, no producto — y solo existe mientras el trabajo está EN CURSO) con `PRD.md` (lo escribe `to-prd`) y `plan.md` (objetivo, enfoque y tabla de tareas con título + descripción de una línea + estado/wave/deps); el estado vive SOLO en esa tabla y se actualiza con edits quirúrgicos, sin releer el plan tras cada tarea; **(b)** la spec completa de cada tarea atómica → Engram (`work/{nombre}/task/{NN}`): el subagente recibe topic_key + título — prompt fino y visible desde cualquier worktree (un worktree no ve archivos gitignorados del checkout principal); resultados de fase y decisiones → `work/{nombre}/{fase}`; cierre → `work/{nombre}/done`; **(c)** backlog del proyecto en la clave ÚNICA `work/backlog` (una lista upsertada, nunca una clave por idea) o issues (`to-issues`) si el proyecto usa tracker; **(d)** al cerrar, el PRD pasa a `docs/` solo si tiene valor duradero y `work/{nombre}/` se borra — sin `1-TODOs/` ni `3-finalized/`; el historial es memoria + git. La skill `work-lifecycle` es la fuente única del flujo (templates incluidos); `to-prd` escribe el PRD en `work/{nombre}/PRD.md`. Complemento opcional: **vista HTML de revisión bajo demanda** — al presentar PRD o plan, el orquestador la ofrece; si el humano acepta, se genera un render desechable en `work/{nombre}/` (`*.review.html`); los cambios pedidos se aplican SIEMPRE al markdown (única fuente, el HTML se regenera de él) y el HTML se borra al aprobar, antes de ejecutar. Los subagentes nunca lo leen.
269
- 12. **Loop autónomo del orquestador**: el humano decide hasta el plan (idea, PRD y plan se iteran con él); aprobado el plan, EXECUTE → VERIFY → SHIP corren sin intervención dentro de un **worktree** (rama = nombre canónico, el checkout principal no se toca) — **commit por tarea o grupo acotado** (el historial de la rama mapea al plan, nunca un commit gigante), verificación por secciones acotadas (por wave, no por micro-cambio), y al terminar: push + `gh pr create` automáticos. Regla general en AGENTS.md §Git: nunca push directo a ramas de producción; push de rama de trabajo/worktree y creación de PR no piden permiso. El hook post-PR lanza la review de los 7 subagentes; el orquestador procesa el informe por niveles: Critical → se aplican sí o sí, Important → a criterio, Suggestions → solo triviales; lo NO aplicado se documenta en `work/backlog` (una línea: qué + por qué se difiere) y lo aplicado entra como nuevas tasks (plan.md + Engram), se ejecuta y se re-verifica. CLOSE devuelve el control: informe al usuario + recomendación de test manual cuando aplica. **El merge del PR jamás es automático** — siempre orden explícita del usuario; tras el merge se cierra (done, borrar carpeta, retirar worktree). Loop interno blindado (loop engineering): VERIFY valida y marca los **Success criteria** del plan (tests verdes no bastan) y regla **anti-thrashing** — máx. 3 intentos por tarea/criterio fallido; al tercero se documenta el bloqueo bajo el topic_key del trabajo y se re-planifica con otro enfoque o se reporta el bloqueo (única interrupción legítima de la autonomía; reintentar a ciegas jamás).
270
-
271
- ## 10. Seguridad
272
-
273
- - Descargas (Engram, skills) con checksum SHA256 fail-closed; instalación de paquetes siempre con pnpm y versiones pinneadas.
274
- - El repo nunca contiene secretos (CI check simple con patrón regex en F5). Por D5, ninguna key de la config actual de Jorge entra en el proyecto en ningún formato.
275
- - ⚠ **Recomendación aparte del proyecto**: las keys de Context7 y Hostinger llevan tiempo en claro en `opencode.json` — conviene rotarlas aunque aquí no se usen.
276
- - `uninstall` y `restore` siempre disponibles; ningún paso destructivo sin backup previo.
277
-
278
- ## 11. Roadmap
279
-
280
- | Fase | Contenido | Done cuando |
281
- |---|---|---|
282
- | **F0** | Scaffold del repo + este PRD + git init | PRD aprobado |
283
- | **F1** | Fuente canónica: migrar y MEJORAR (§9) agentes, AGENTS.md, hooks, scripts, commands, manifiesto MCP; auditar skills propias vs terceros → `upstreams.json`; copiar plugins de jorgex-custom-tools | `stack/` completo, sin secretos, revisado por Jorge |
284
- | **F2** | CLI core: detect, backup/restore, filemerge (md/JSON/TOML), pipeline + **adapter OpenCode** | install en OpenCode reproduce la config actual (paridad verificada) |
285
- | **F3** | **Adapter Claude Code** (agentes md, skills espejo, hooks en settings.json, MCP user scope, CLAUDE.md) | install funcional en Claude Code real |
286
- | **F4** | **Adapter Codex** (agentes TOML, skills en ~/.agents, hooks.json + aviso trust, MCP TOML, AGENTS.md) | install funcional en Codex real |
287
- | **F5** | `update` (stack + engram + upstreams), `doctor`, `uninstall`, tests de paridad, publicación en el registry npm | `pnpm dlx jorgex-stack` funciona en máquina limpia |
288
- | **F6** | **Migración final**: eliminar plugins de jorgex-custom-tools, desinstalar config legacy de `~/.config/opencode`, reinstalar TODO desde JorgeX Stack | El stack es la única fuente; los re-exports `file:///` han desaparecido |
289
-
290
- Backlog v1.x: skill registry cacheado, scope proyecto, más runtimes (Cursor/Gemini), diff de 3 vías en updates de skills.
291
-
292
- ## 12. Riesgos
293
-
294
- | Riesgo | Mitigación |
295
- |---|---|
296
- | Los formatos de los runtimes cambian (Codex evoluciona rápido) | Adapters aislados; tests de instalación; docs de cada formato enlazadas en el código |
297
- | Hooks de Codex requieren trust manual | `doctor` lo verifica y da la instrucción exacta; documentado en README |
298
- | Capacidades desiguales (OpenCode sin hooks nativos; captura pasiva de Engram solo en OpenCode) | Tabla de paridad documentada; el puente cubre lo crítico; lo no portable se declara, no se simula |
299
- | Symlinks/permisos en Windows | No usamos symlinks: copias gestionadas por `sync` |
300
- | Romper la config en uso durante el desarrollo | Backups automáticos + los originales de jorgex-custom-tools intactos hasta F6 |
301
-
302
- ## 13. Criterios de aceptación (v1)
303
-
304
- 1. En una máquina limpia con los 3 CLIs instalados: `pnpm dlx jorgex-stack install --agents claude,codex,opencode --yes` deja los 3 funcionando con agentes, skills, hooks, Engram y MCPs.
305
- 1b. En la máquina de Jorge: el install detecta su Engram existente, lo respeta (binario y DB intactos) y ninguna key personal aparece en el repo ni en los artefactos generados.
306
- 2. Re-ejecutar `sync` dos veces seguidas produce cero cambios (idempotencia byte a byte en lo gestionado).
307
- 3. Una edición manual del usuario fuera de las secciones marcadas sobrevive a `sync` y `update`.
308
- 4. `update --check` detecta una release nueva de Engram y una skill de terceros desactualizada.
309
- 5. `doctor` detecta: runtime ausente, hook sin trust en Codex, Engram caído, secreto faltante.
310
- 6. `uninstall` + `restore` devuelven cada runtime a su estado previo.