ostacky 0.8.1 → 0.8.3

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
@@ -110,15 +110,13 @@ Detecta automáticamente el directorio `.opencode/` del proyecto (o lo crea) y m
110
110
  ### Instalar todo
111
111
 
112
112
  ```bash
113
- npx ostacky install # local por defecto (pregunta si querés global)
114
- npx ostacky install --scope local # <proyecto>/.opencode
115
- npx ostacky install --scope global # ~/.config/opencode (XDG/APPDATA en Windows)
116
- npx ostacky install --scope auto # local si existe .opencode/.git, si no global
113
+ npx ostacky install # siempre local en <proyecto>/.opencode
114
+ npx ostacky install --scope local # <proyecto>/.opencode (único scope soportado)
117
115
  ```
118
116
 
119
- Descarga todos los agentes y commands definidos en el manifest y los escribe en `.opencode/` (scope `local`) o en `~/.config/opencode` (`global`). Además copia los plugins **Ostacky-owned** `ostacky-plugin.ts` + `engram.ts` en `.opencode/plugins/` (el legacy `ostacky-guard.ts` fue fusionado en `ostacky-plugin.ts` y ya no se instala; `ostacky-controller.ts` legacy se limpia automáticamente si quedó de una instalación previa).
117
+ Descarga todos los agentes y commands definidos en el manifest y los escribe en `<proyecto>/.opencode`. Además copia los plugins **Ostacky-owned** `ostacky-plugin.ts` + `engram.ts` en `.opencode/plugins/` (el legacy `ostacky-guard.ts` fue fusionado en `ostacky-plugin.ts` y ya no se instala; `ostacky-controller.ts` legacy se limpia automáticamente si quedó de una instalación previa) y asegura `.gitignore` con ` .opencode/tools/, .codegraph/, .opencode/cache/, ostacky-state.json*`.
120
118
 
121
- > **Coherencia local/global:** aunque elijas `global`, los plugins y herramientas (`tools/` con CodeGraph/Engram) permanecen siempre en `<proyecto>/.opencode` local para el hard-gate por worktree. En global solo se escribe el scope global + un espejo local de plugins. Para stack completo por proyecto ejecutá `npx ostacky install --scope local` dentro de cada repo.
119
+ > **Instalación siempre local:** Ostacky vive por proyecto/worktree (CodeGraph index `.codegraph/` y `ostacky-state.json` aislados). Si ves `C:\Users\...\ .opencode\tools\codegraph` y esperabas `D:\mi-proyecto\.opencode`, es porque corriste `npx ostacky` desde tu home sin estar dentro del proyecto (sin `.git`). Hacé `cd` al proyecto y re-ejecutá; el log muestra `projectRoot` y `toolsDir` explícitos.
122
120
 
123
121
  ### Agregar agentes o commands individualmente
124
122
 
@@ -150,10 +148,8 @@ Solo descarga los items que cambiaron de versión.
150
148
  ### Desinstalar
151
149
 
152
150
  ```bash
153
- npx ostacky uninstall # pregunta local vs global, luego qué borrar
151
+ npx ostacky uninstall # qué borrar en <proyecto>/.opencode
154
152
  npx ostacky uninstall --scope local # fuerza scope local
155
- npx ostacky uninstall --scope global # fuerza scope global
156
- npx ostacky uninstall --scope auto # auto
157
153
  ```
158
154
 
159
155
  **Safe-delete:** solo borra lo trackeado en `.opencode/ostacky-lock.json` (agentes, commands, skills, MCPs) — nunca toca componentes previos del usuario que no estén en el lockfile. Antes de borrar muestra preview con `Scope: <ruta>` + lista de paths y pide confirmación. Los plugins Ostacky-owned (`ostacky-plugin.ts`, `engram.ts` + legacy `ostacky-guard.ts`/`ostacky-controller.ts` si quedaron) se limpian automáticamente via allowlist, nunca se borran plugins custom.
@@ -163,7 +159,7 @@ npx ostacky uninstall --scope auto # auto
163
159
  #### Desinstalar un agente puntual
164
160
 
165
161
  ```bash
166
- npx ostacky uninstall agent <nombre> # también admite --scope local|global|auto
162
+ npx ostacky uninstall agent <nombre> # también admite --scope local
167
163
  npx ostacky uninstall agent # sin nombre → selector de instalados
168
164
  ```
169
165
 
@@ -176,7 +172,7 @@ npx ostacky uninstall agent ostacky
176
172
  #### Desinstalar un command puntual
177
173
 
178
174
  ```bash
179
- npx ostacky uninstall command <nombre> # también admite --scope
175
+ npx ostacky uninstall command <nombre> # también admite --scope local
180
176
  npx ostacky uninstall command # sin nombre → selector
181
177
  ```
182
178
 
@@ -191,7 +187,7 @@ Si no especificás el nombre, el CLI te muestra un selector con los items instal
191
187
  #### Desinstalar todo el stack (tools + config)
192
188
 
193
189
  ```bash
194
- npx ostacky uninstall-stack # pregunta scope, limpia mcp.codegraph/engram de opencode.json + .codegraph/ + .opencode/tools/
190
+ npx ostacky uninstall-stack # limpia mcp.codegraph/engram de opencode.json + .codegraph/ + .opencode/tools/
195
191
  npx ostacky uninstall-stack --scope local
196
192
  ```
197
193
 
@@ -250,37 +246,37 @@ Tras instalar (`--scope local`), el proyecto queda así:
250
246
 
251
247
  ```json
252
248
  {
253
- "version": "0.8.1",
249
+ "version": "0.8.3",
254
250
  "lockedAt": "2025-01-01T00:00:00.000Z",
255
251
  "repo": "JaimeHoracio/Ostacky",
256
- "tag": "v0.8.1",
252
+ "tag": "v0.8.3",
257
253
  "agents": {
258
254
  "ostacky": {
259
- "version": "0.8.1",
255
+ "version": "0.8.3",
260
256
  "installedAt": "2025-01-01T00:00:00.000Z",
261
257
  "sha256": "abc123..."
262
258
  }
263
259
  },
264
260
  "commands": {
265
261
  "install-stack": {
266
- "version": "0.8.1",
262
+ "version": "0.8.3",
267
263
  "installedAt": "2025-01-01T00:00:00.000Z",
268
264
  "sha256": "def456..."
269
265
  },
270
266
  "opsx-sync": {
271
- "version": "0.8.1",
267
+ "version": "0.8.3",
272
268
  "installedAt": "2025-01-01T00:00:00.000Z",
273
269
  "sha256": "ghi789..."
274
270
  }
275
271
  },
276
272
  "skills": {
277
- "brainstorming": { "version": "0.8.1", ... },
278
- "execution-mode-evaluation": { "version": "0.8.1", ... },
279
- "openspec-propose": { "version": "0.8.1", ... }
273
+ "brainstorming": { "version": "0.8.3", ... },
274
+ "execution-mode-evaluation": { "version": "0.8.3", ... },
275
+ "openspec-propose": { "version": "0.8.3", ... }
280
276
  },
281
277
  "mcpServers": {
282
- "ostacky-controller": { "version": "0.8.1", ... },
283
- "openspec": { "version": "0.8.1", ... }
278
+ "ostacky-controller": { "version": "0.8.3", ... },
279
+ "openspec": { "version": "0.8.3", ... }
284
280
  }
285
281
  }
286
282
  ```
@@ -312,7 +308,7 @@ Es opcional y solo necesario si algo falló durante la instalación o si querés
312
308
  ## Seguridad
313
309
 
314
310
  - `opencode.jsonc` se versiona en el repo para compartir permisos y MCP de forma reproducible.
315
- - Las URLs de descarga usan **tags de GitHub** (ej. `v0.8.1`), nunca `main` — instalaciones reproducibles
311
+ - Las URLs de descarga usan **tags de GitHub** (ej. `v0.8.3`), nunca `main` — instalaciones reproducibles
316
312
  - Cada path de archivo descargado es validado para prevenir **path traversal**
317
313
  - Los archivos incluyen **checksum SHA-256** opcional; si el manifest lo define, el contenido se verifica antes de escribir
318
314
  - El cache local (`.opencode/cache/`) también valida integridad al servir archivos cacheados
@@ -369,6 +365,8 @@ CodeGraph está instalado en `.opencode/tools/codegraph/bin/codegraph` y se conf
369
365
  | `.opencode/tools/codegraph/bin/codegraph status` | Muestra estado del index y archivos pendientes |
370
366
  | `.opencode/tools/codegraph/bin/codegraph install --target opencode --location local --yes` | Configura CodeGraph para OpenCode y genera AGENTS.md |
371
367
 
368
+ > **Windows:** si ves `Command failed: cmd.exe /d /c call ... codegraph.cmd init -i` durante `npx ostacky install`, es un _warning_ no fatal — el binario se instaló pero el índice no se pudo crear. Reintentá con `npx ostacky install-stack --scope local` o ejecutá manualmente `.opencode\tools\codegraph\bin\codegraph.exe init -i` (o `codegraph.cmd` si no hay `.exe`) dentro del proyecto. Desde v0.8.3 el instalador usa el patrón resiliente de Engram (strip 0 + búsqueda recursiva) y loguea `projectRoot|toolsDir` para diagnosticar scope.
369
+
372
370
  ## Licencia
373
371
 
374
372
  MIT
@@ -1,12 +1,12 @@
1
1
  ---
2
2
  description: Orquestador principal — rutea por nivel, orquesta CodeGraph + OpenSpec + Superpowers.
3
3
  mode: primary
4
- version: 0.8.1
4
+ version: 0.8.3
5
5
  ---
6
6
 
7
- Sos **Ostacky v0.8.1**, orquestás, no implementás. Interpretás, clasificás (0/0+1/1+), ruteás y coordinás.
7
+ Sos **Ostacky v0.8.3**, orquestás, no implementás. Interpretás, clasificás (0/0+1/1+), ruteás y coordinás.
8
8
 
9
- > **Versión:** `0.8.1` (sincronizada desde `package.json` vía `scripts/sync-version.ts`). Cuando te pregunten qué versión tenés, qué versión sos, o `¿qué versión tenés?` / `version` / `¿en qué versión estás?`, respondé exactamente: **"Ostacky v0.8.1"** (o `v0.8.1` si te piden solo el número). No inventes otra versión.
9
+ > **Versión:** `0.8.3` (sincronizada desde `package.json` vía `scripts/sync-version.ts`). Cuando te pregunten qué versión tenés, qué versión sos, o `¿qué versión tenés?` / `version` / `¿en qué versión estás?`, respondé exactamente: **"Ostacky v0.8.3"** (o `v0.8.3` si te piden solo el número). No inventes otra versión.
10
10
 
11
11
  ## Reglas innegociables
12
12
 
@@ -18,6 +18,16 @@ Sos **Ostacky v0.8.1**, orquestás, no implementás. Interpretás, clasificás (
18
18
 
19
19
  > Ver `assets/docs/ostacky-reference.md` para TRANSITIONS, TTL y métricas. Tiered LITE/TIER1/FULL vía suffix hint.
20
20
 
21
+ ## Principios de honestidad (SHALL)
22
+
23
+ 1. **Engram contradiction check SHALL.** Antes de proponer o discutir cambios (Nivel 0+1/1+, eximido si `isTrivial && DONE`), SHALL `mem_search` por keywords del pedido (max 5 keywords = sustantivos del pedido + `topic_key` del change activo si lo hay); si hit `type:decision|architecture` con contradicción semántica (ej: pide Zustand y hay #571 “usar Redux por X”), SHALL mostrar diff `“Antes decidimos X el <fecha> por Z (topic_key), ahora pedís ¬X”`, llamar `request_clarification` y esperar. Si Engram `degraded`/timeout → SHALL advertir “sin memoria” y continuar (no block). Si usuario confirma override → SHALL `mem_save` mismo `topic_key` + `mem_compare supersedes` + `record_user_confirmation`.
24
+ 2. **Solo propuestas que ayudan SHALL.** Toda propuesta SHALL incluir `por qué ayuda + tradeoff + evidencia` (CodeGraph symbol, Engram hit) o literal `“no verificado”` + best practice 2026. No sugerir por sugerir.
25
+ 3. **Pocas honestas > muchas de relleno SHALL.** Si solo hay una opción honesta, dar una. Si hay 2-3, tabla `coste|riesgo|complejidad`. No inventar para llenar.
26
+ 4. **Si no hay propuesta honesta SHALL decirlo.** Literal: `“No hay propuesta honesta que aporte vs no hacer nada en este contexto.”`
27
+ 5. **Si dudás SHALL preguntar.** Ante ambigüedad aunque el pedido parezca claro, SHALL una pregunta clarificadora (una por turno, natural, sin tool, STOP) y esperar. No asumir.
28
+ 6. **Verificar antes de afirmar SHALL.** No acordar sin chequear código/docs. Citar evidencia o admitir `“no pude verificar en disco”`.
29
+ 7. **YAGNI scope SHALL.** No proponer refactor oportunista fuera del pedido; si lo ves, mencionar `“fuera de scope: …”` sin implementarlo.
30
+
21
31
  ## Stack
22
32
 
23
33
  - **Controller** (plugin `ostacky-plugin.ts`): state machine in-process, hard gates.
@@ -62,6 +72,11 @@ Preguntar nivel y `consume_route_decision`.
62
72
 
63
73
  Router `brainstorming`↔`OpenSpec` por `level`/`estLines`/`fileCount`/`hasAPI` (no keywords). `1+` no-downgradeable → `skill(brainstorming)` genera `design.md ## Alternatives`; downgradeable → `docs/...` + `DIRECT`.
64
74
 
75
+ **SHALL sin ambigüedad (spec iterativo + sync proactiva):**
76
+
77
+ - Antes de crear/editar `openspec/changes/<id>/{proposal.md,design.md,tasks.md}` **O** `docs/superpowers/specs/*.md`: si existe → SHALL `read` fresco de TODOS y SHALL `edit` (no `write`); `write` solo si no existía. Para `open-explore` sin archivo → SHALL `mem_save topic_key:brainstorm/<hash>` por iteración. `getDiscoverySnapshot` no aplica a specs/docs.
78
+ - Al final de cada turno de brainstorming/spec con decisiones nuevas no reflejadas en disco: SHALL listar cálido `Noté que lo que acordamos (X por Z) aún no está en <archivo>: 1) ...` (max 3) y SHALL proponer en UNA sola pregunta `¿Querés que agregue [X, Y] a <archivo> e implemente <mejor propuesta> —la recomiendo por <tradeoff/evidencia>—?` (respetando Regla 5). Si no hay delta, no proponer. Eximido si `isTrivial`.
79
+
65
80
  ### 4. Execution
66
81
 
67
82
  1. `skill(execution-mode-evaluation)` en memoria, reusa discovery.
@@ -1,17 +1,17 @@
1
1
  ---
2
- description: Instala CodeGraph, skills curadas, OpenSpec, Engram y Context7 localmente para OpenCode únicamente
2
+ description: Instala CodeGraph, skills curadas, OpenSpec y Engram localmente para OpenCode únicamente
3
3
  agent: build
4
4
  ---
5
5
 
6
6
  Instala el stack tecnológico de desarrollo para OpenCode. **IMPORTANTE:** las herramientas se instalan por separado (cada una con su propio CLI/comando). `npx ostacky install` solo instala el agente y commands de Ostacky en `.opencode/`. Este comando (`/install-stack`) es la guía de referencia para la instalación manual completa paso a paso.
7
7
 
8
- **Nota:** A partir de v0.8.1, `npx ostacky install` ya instala automáticamente el stack completo (CodeGraph, OpenSpec, Engram, Context7, MCPs bundleados) además del agente y skills. Este comando es útil para instalación manual, verificación, o cuando algo falló y necesita reinstalarse.
8
+ **Nota:** A partir de v0.8.3, `npx ostacky install` ya instala automáticamente el stack completo (CodeGraph, OpenSpec, Engram, MCPs bundleados) además del agente y skills. Este comando es útil para instalación manual, verificación, o cuando algo falló y necesita reinstalarse.
9
9
 
10
10
  **RESTRICCIÓN ABSOLUTA:** instalar ÚNICAMENTE para OpenCode. Está terminantemente prohibido crear o modificar archivos en `.claude/`, `.kiro/`, `.cursor/`, `.gemini/`, `.codex/`, `.antigravity/`, `.windsurf/` o cualquier otro directorio de plataformas externas.
11
11
 
12
- **Origen del set curado:** el set de 15 skills referenciado en `assets/agents/ostacky.md` está bundleado en `assets/skills/` dentro del paquete npm. Context7 agrega su propio skill vía `npx ctx7 setup --opencode`. La definición del set y su trazabilidad viven en `manifest.json` y `.opencode/ostacky-lock.json`.
12
+ **Origen del set curado:** el set de 15 skills referenciado en `assets/agents/ostacky.md` está bundleado en `assets/skills/` dentro del paquete npm. La definición del set y su trazabilidad viven en `manifest.json` y `.opencode/ostacky-lock.json`.
13
13
 
14
- ## Scope de instalación — local vs global (desde v0.8.1)
14
+ ## Scope de instalación — local vs global (desde v0.8.3)
15
15
 
16
16
  `npx ostacky install` soporta `--scope local|global|auto` (también `--scope=...`):
17
17
 
@@ -164,7 +164,7 @@ Ostacky automáticamente detecta la ausencia del controller y opera en modo degr
164
164
 
165
165
  ## Paso 2 — Skills curadas (bundleadas)
166
166
 
167
- Las **15 skills curadas** están bundleadas dentro del paquete Ostacky en `assets/skills/`. No se descargan ni clonan; ya vienen en el paquete npm. Context7 agrega su propia skill aparte (Paso 6).
167
+ Las **15 skills curadas** están bundleadas dentro del paquete Ostacky en `assets/skills/`. No se descargan ni clonan; ya vienen en el paquete npm.
168
168
 
169
169
  Copiá cada skill bundleada a `.opencode/skills/<nombre>/` preservando la estructura interna (incluyendo `SKILL.md` y cualquier subdirectorio como `scripts/` o `references/`):
170
170
 
@@ -215,11 +215,6 @@ Leé `opencode.json` (o `opencode.jsonc`) en la raíz del proyecto.
215
215
  "command": [".opencode/tools/engram/bin/engram", "mcp"],
216
216
  "enabled": true
217
217
  },
218
- "context7": {
219
- "type": "remote",
220
- "url": "https://mcp.context7.com/mcp",
221
- "enabled": true
222
- },
223
218
  "ostacky-controller": {
224
219
  "type": "local",
225
220
  "command": ["node", ".opencode/mcp/ostacky-controller/index.js"],
@@ -353,99 +348,6 @@ rm -rf ~/.engram/
353
348
 
354
349
  ---
355
350
 
356
- ## Paso 6 — Context7
357
-
358
- [Context7](https://context7.com) provee documentación actualizada de librerías y APIs directamente en el contexto del agente. Se instala como un CLI vía `npx`, sin dependencias globales ni MCP server necesario (aunque también soporta MCP).
359
-
360
- **RESTRICCIÓN:** configurar ÚNICAMENTE para OpenCode. Está terminantemente prohibido crear archivos en `.claude/`, `.kiro/`, `.cursor/`, `.gemini/`, `.codex/`, `.antigravity/`, `.windsurf/` o cualquier otro directorio de plataformas externas.
361
-
362
- ### Verificar instalación actual
363
-
364
- Chequeá si Context7 ya está configurado para OpenCode:
365
-
366
- ```bash
367
- ls -la .opencode/skills/context7/ 2>/dev/null || echo "no-instalado"
368
- ```
369
-
370
- También podés verificar si el MCP server de Context7 está en `opencode.json`:
371
-
372
- ```bash
373
- grep -c "context7" opencode.json 2>/dev/null || echo "no-configurado"
374
- ```
375
-
376
- ### Instalar
377
-
378
- **Opción recomendada — setup automático (CLI + skills):**
379
-
380
- ```bash
381
- npx ctx7 setup --opencode
382
- ```
383
-
384
- Este comando:
385
-
386
- - Autentica vía OAuth y genera una API key
387
- - Instala un skill en `.opencode/skills/context7/` que el agente Ostacky usa automáticamente
388
- - Pregunta si preferís modo CLI + Skills o MCP
389
- - **NO** toca `.claude/`, `.cursor/`, `.gemini/` ni ninguna otra plataforma
390
-
391
- **Alternativa — solo MCP (si preferís el server remoto):**
392
-
393
- Agregá manualmente la entrada MCP en `opencode.json`:
394
-
395
- ```json
396
- {
397
- "mcp": {
398
- "context7": {
399
- "type": "remote",
400
- "url": "https://mcp.context7.com/mcp"
401
- }
402
- }
403
- }
404
- ```
405
-
406
- Si tenés una API key de Context7 (recomendado para mejores rate limits):
407
-
408
- ```json
409
- {
410
- "mcp": {
411
- "context7": {
412
- "type": "remote",
413
- "url": "https://mcp.context7.com/mcp",
414
- "headers": {
415
- "CONTEXT7_API_KEY": "{env:CONTEXT7_API_KEY}"
416
- }
417
- }
418
- }
419
- }
420
- ```
421
-
422
- ### Desinstalar
423
-
424
- Para remover la configuración generada por `npx ctx7 setup`:
425
-
426
- ```bash
427
- npx ctx7 remove
428
- ```
429
-
430
- Esto elimina el skill y la configuración de Context7. Si instalaste el CLI globalmente con `npm install -g ctx7`, también necesitás:
431
-
432
- ```bash
433
- npm uninstall -g ctx7
434
- ```
435
-
436
- Para remover la entrada MCP manual, editá `opencode.json` y eliminá el bloque `mcp.context7`.
437
-
438
- ### Cómo lo usa Ostacky
439
-
440
- El skill de Context7 instalado le indica al agente Ostacky que use Context7 automáticamente cuando necesite documentación de librerías, APIs o frameworks. También se puede invocar explícitamente:
441
-
442
- ```
443
- usá context7 para mostrarme la API de autenticación de Supabase
444
- use context7 to find Next.js 15 app router examples
445
- ```
446
-
447
- ---
448
-
449
351
  ## Estructura de herramientas
450
352
 
451
353
  Cada herramienta se instala en su propia carpeta dentro de `.opencode/` para mantener una instalación limpia y aislada. El installer crea automáticamente `.opencode/tools/<nombre>/` para cada herramienta externa:
@@ -481,10 +383,9 @@ Cada herramienta se instala en su propia carpeta dentro de `.opencode/` para man
481
383
  │ ├── using-superpowers/
482
384
  │ ├── writing-skills/
483
385
  │ └── graceful-degradation/
484
- ├── tools/ # Config y archivos de herramientas externas
485
- │ ├── codegraph/ # Config de CodeGraph (AGENTS.md si codegraph lo crea)
486
- ├── engram/ # Config project-local de Engram
487
- │ └── context7/ # Config de Context7
386
+ ├── tools/ # Binarios locales de herramientas externas
387
+ │ ├── codegraph/ # Binario local de CodeGraph
388
+ └── engram/ # Binario local de Engram
488
389
  └── plugins/ # Plugins de OpenCode
489
390
  ```
490
391
 
@@ -493,24 +394,23 @@ Cada herramienta se instala en su propia carpeta dentro de `.opencode/` para man
493
394
  - `.codegraph/` (índice de CodeGraph) vive en la raíz del proyecto — CodeGraph lo espera ahí.
494
395
  - El binario de CodeGraph es **local al proyecto** en `.opencode/tools/codegraph/bin/`; en Windows puede ser `codegraph.cmd`. No se instala globalmente.
495
396
  - El binario de Engram es **local al proyecto** en `.opencode/tools/engram/bin/engram` (o `engram.exe` en Windows). No se instala globalmente.
496
- - Context7 se registra como MCP remoto en `opencode.jsonc`. Su skill opcional se instala via `npx ctx7 setup --opencode` en `.opencode/skills/context7/`.
397
+ - El binary de CodeGraph y Engram vive siempre en `<proyecto>/.opencode/tools/` por reproducibilidad, nunca global.
497
398
  - El controller publicado se bundlea como un único `index.js`; durante desarrollo el installer instala sus dependencias en staging y valida el servidor antes de activarlo.
498
399
 
499
400
  ## Verificación final
500
401
 
501
402
  Confirmá que:
502
403
 
503
- 1. `.opencode/skills/` contiene las 15 skills curadas y opcionalmente `context7/` si se instaló con `npx ctx7 setup --opencode`
404
+ 1. `.opencode/skills/` contiene las 15 skills curadas
504
405
  2. `.opencode/commands/` contiene los commands bundleados por Ostacky (`install-stack`, `opsx-sync`) y los 4 commands generados por OpenSpec (`opsx-apply`, `opsx-archive`, `opsx-explore`, `opsx-propose`) si OpenSpec fue inicializado
505
- 3. `opencode.json` (o `.jsonc`) tiene los bloques MCP: `codegraph`, `engram`, `context7`, `ostacky-controller` — sin campo `plugin`
406
+ 3. `opencode.json` (o `.jsonc`) tiene los bloques MCP: `codegraph`, `engram`, `ostacky-controller` — sin campo `plugin`
506
407
  4. `.codegraph/` existe en la raíz del proyecto (índice local de CodeGraph)
507
408
  5. El launcher local de CodeGraph funciona (`codegraph.cmd --version` en Windows o `codegraph --version` en Unix)
508
409
  6. El binario local de Engram funciona (`engram.exe --version` en Windows o `engram --version` en Unix)
509
- 7. Context7 configurado: `mcp.context7` presente en `opencode.jsonc` y/o `.opencode/skills/context7/SKILL.md` existe
510
- 8. OpenSpec generó sus commands/skills para OpenCode mediante `openspec init --tools opencode`
511
- 9. `.opencode/tools/` contiene subdirectorios para `codegraph/`, `engram/` y `context7/`
512
- 10. **NO existen** archivos generados en `.claude/`, `.kiro/`, `.cursor/`, `.gemini/`, `.codex/` ni ningún directorio de otra plataforma
513
- 11. Un `AGENTS.md` preexistente en la raíz del proyecto se preserva sin cambios
410
+ 7. OpenSpec generó sus commands/skills para OpenCode mediante `openspec init --tools opencode` (si se usó)
411
+ 8. `.opencode/tools/` contiene subdirectorios para `codegraph/` y `engram/`
412
+ 9. **NO existen** archivos generados en `.claude/`, `.kiro/`, `.cursor/`, `.gemini/`, `.codex/` ni ningún directorio de otra plataforma
413
+ 10. Un `AGENTS.md` preexistente en la raíz del proyecto se preserva sin cambios
514
414
 
515
415
  Reportá el estado de cada componente con ✓ o ✗.
516
416
 
@@ -26,7 +26,6 @@ BLOCKED: replan→INTERPRETATION_PENDING, abandon→DONE
26
26
  | codegraph_* | 10s | 1 | Engram → Read+Glob |
27
27
  | controller_* | 5s | 1 | degraded |
28
28
  | engram_* | 5s | 1 | sin memoria |
29
- | context7_* | 10s | 1 | sin docs |
30
29
 
31
30
  ## Cache
32
31
 
@@ -75,5 +74,4 @@ Principio: **eficacia > recorte** — si recorte rompiera caché y saliera más
75
74
 
76
75
  - `ostacky.md` 109→72 líneas (diet estable cacheable). Tiered vía suffix, no reemplazo `system[0]`.
77
76
  - `controller` descriptions <150 chars
78
- - `context7` lazy: `enabled:false` default, trigger `docs|api|lib|Context7` habilita
79
77
  - `MEMORY_INSTRUCTIONS` lazy: siempre pointer (~1 línea) en `system.transform`; full vive en `assets/docs/engram-protocol.md` on-demand via `Read` (ahorro ~1.2k en FULL, trivial ya era pointer)
@@ -0,0 +1,206 @@
1
+ /**
2
+ * controller-core — Single source of truth para TRANSITIONS, STATES, DEFAULT_STATE y helpers.
3
+ * Extraído de assets/mcp/ostacky-controller/index.js y assets/plugins/ostacky-plugin.ts
4
+ * para eliminar duplicación (D1). Ambos importan de acá.
5
+ */
6
+
7
+ import { SENSITIVE_DEFAULT } from "./security.js";
8
+
9
+ // --- Constants (headroom generoso) ---
10
+ export const MAX_TASKS = 100;
11
+ export const MAX_TASKS_DEFAULT = 100;
12
+ export const MAX_TASKS_CAP = 500;
13
+ export const MAX_SNAPSHOT_JSON_LENGTH = 50 * 1024;
14
+ export const MAX_STATE_FILE_SIZE = 2 * 1024 * 1024;
15
+ export const DEGRADED_THRESHOLD = 3;
16
+
17
+ export const STATES = Object.freeze({
18
+ INTERPRETATION_PENDING: "INTERPRETATION_PENDING",
19
+ CLARIFICATION_PENDING: "CLARIFICATION_PENDING",
20
+ DISCOVERY: "DISCOVERY",
21
+ ROUTE_DECISION_PENDING: "ROUTE_DECISION_PENDING",
22
+ SPECIFICATION: "SPECIFICATION",
23
+ EXECUTION_ANALYSIS: "EXECUTION_ANALYSIS",
24
+ EXECUTION_DECISION_PENDING: "EXECUTION_DECISION_PENDING",
25
+ EXECUTING_INLINE: "EXECUTING_INLINE",
26
+ EXECUTING_SUBAGENTS: "EXECUTING_SUBAGENTS",
27
+ SYNC: "SYNC",
28
+ DONE: "DONE",
29
+ BLOCKED: "BLOCKED",
30
+ });
31
+
32
+ export const TRANSITIONS = {
33
+ INTERPRETATION_PENDING: [
34
+ { via: "request_clarification", to: "CLARIFICATION_PENDING" },
35
+ { via: "proceed_to_discovery", to: "DISCOVERY" },
36
+ { via: "record_discovery", to: "ROUTE_DECISION_PENDING" },
37
+ { via: "block", to: "BLOCKED" },
38
+ ],
39
+ CLARIFICATION_PENDING: [
40
+ { via: "record_clarification", to: "DISCOVERY" },
41
+ { via: "block", to: "BLOCKED" },
42
+ { via: "abandon", to: "BLOCKED" },
43
+ ],
44
+ DISCOVERY: [
45
+ { via: "record_discovery", to: "ROUTE_DECISION_PENDING" },
46
+ { via: "block", to: "BLOCKED" },
47
+ { via: "abandon", to: "BLOCKED" },
48
+ ],
49
+ ROUTE_DECISION_PENDING: [
50
+ { via: "consume_route_decision", to: "SPECIFICATION", choice: "SPEC" },
51
+ { via: "consume_route_decision", to: "EXECUTION_ANALYSIS", choice: "DIRECT" },
52
+ { via: "block", to: "BLOCKED" },
53
+ { via: "abandon", to: "BLOCKED" },
54
+ ],
55
+ SPECIFICATION: [
56
+ { via: "spec_complete", to: "EXECUTION_ANALYSIS" },
57
+ { via: "block", to: "BLOCKED" },
58
+ { via: "abandon", to: "BLOCKED" },
59
+ ],
60
+ EXECUTION_ANALYSIS: [
61
+ { via: "record_execution_analysis", to: "EXECUTION_DECISION_PENDING" },
62
+ { via: "block", to: "BLOCKED" },
63
+ { via: "abandon", to: "BLOCKED" },
64
+ ],
65
+ EXECUTION_DECISION_PENDING: [
66
+ { via: "consume_execution_decision", to: "EXECUTING_INLINE", mode: "INLINE" },
67
+ { via: "consume_execution_decision", to: "EXECUTING_SUBAGENTS", mode: "SUBAGENT_DRIVEN" },
68
+ { via: "block", to: "BLOCKED" },
69
+ { via: "abandon", to: "BLOCKED" },
70
+ ],
71
+ EXECUTING_INLINE: [
72
+ { via: "implementation_complete", to: "SYNC" },
73
+ { via: "block", to: "BLOCKED" },
74
+ ],
75
+ EXECUTING_SUBAGENTS: [
76
+ { via: "implementation_complete", to: "SYNC" },
77
+ { via: "block", to: "BLOCKED" },
78
+ ],
79
+ BLOCKED: [
80
+ { via: "replan", to: "INTERPRETATION_PENDING" },
81
+ { via: "abandon", to: "DONE" },
82
+ ],
83
+ SYNC: [
84
+ { via: "sync_complete", to: "DONE" },
85
+ { via: "block", to: "BLOCKED" },
86
+ ],
87
+ DONE: [],
88
+ };
89
+
90
+ export const TERMINAL_STATES = Object.freeze([
91
+ STATES.INTERPRETATION_PENDING,
92
+ STATES.CLARIFICATION_PENDING,
93
+ STATES.BLOCKED,
94
+ STATES.DONE,
95
+ ]);
96
+
97
+ export const DEFAULT_STATE = {
98
+ state: STATES.INTERPRETATION_PENDING,
99
+ revision: 0,
100
+ requestId: null,
101
+ changeId: null,
102
+ routeDecisionId: null,
103
+ routeChoice: null,
104
+ level: null,
105
+ executionDecisionId: null,
106
+ executionMode: null,
107
+ snapshots: { codegraph: null, execution: null },
108
+ tasks: {},
109
+ fileFingerprints: {},
110
+ error: null,
111
+ lastHandoff: null,
112
+ expectedTasks: null,
113
+ expectedTaskCount: null,
114
+ auditSeq: 0,
115
+ degraded: false,
116
+ schemaVersion: 2,
117
+ stateOversizedCount: 0,
118
+ codegraphBypassCount: 0,
119
+ degradedEditsCount: 0,
120
+ cacheHitCount: 0,
121
+ cacheMissCount: 0,
122
+ tokenSavingEstimate: 0,
123
+ discoveryCacheHitCount: 0,
124
+ redundantCallCount: 0,
125
+ cacheMissWithoutPutCount: 0,
126
+ stateCheckCount: 0,
127
+ toolCallCount: 0,
128
+ lastProposal: null,
129
+ allowedFiles: {},
130
+ deniedFiles: {},
131
+ sensitivePatterns: [...SENSITIVE_DEFAULT],
132
+ sensitiveAccess: { allowed: 0, denied: 0, blockedAttempts: 0 },
133
+ staleContentAttempts: 0,
134
+ completeWithoutValidateCount: 0,
135
+ toolTimeoutCount: 0,
136
+ lastToolDurationMs: 0,
137
+ stateDurationMs: 0,
138
+ subagentFailedCount: 0,
139
+ lastValidated: null,
140
+ pendingFileAccess: {},
141
+ lastHeartbeat: 0,
142
+ watchdogEnabled: true,
143
+ ts: Date.now(),
144
+ specNotInSync: false,
145
+ };
146
+
147
+ export function safeJsonStringify(obj, pretty = false) {
148
+ const seen = new WeakSet();
149
+ try {
150
+ return JSON.stringify(
151
+ obj,
152
+ (key, value) => {
153
+ if (typeof value === "object" && value !== null) {
154
+ if (seen.has(value)) return "[Circular]";
155
+ seen.add(value);
156
+ }
157
+ return value;
158
+ },
159
+ pretty ? 2 : undefined
160
+ );
161
+ } catch (e) {
162
+ return `[Unstringifiable: ${e.message}]`;
163
+ }
164
+ }
165
+
166
+ const SENSITIVE_REDACT_RE = /(apiKey|secret|token|password|api_key)/i;
167
+
168
+ export function redactForLog(data) {
169
+ if (!data || typeof data !== "object") return data;
170
+ try {
171
+ const str = safeJsonStringify(data);
172
+ if (SENSITIVE_REDACT_RE.test(str)) {
173
+ const copy = JSON.parse(str);
174
+ const redactRecursively = (obj) => {
175
+ if (!obj || typeof obj !== "object") return;
176
+ for (const k of Object.keys(obj)) {
177
+ if (SENSITIVE_REDACT_RE.test(k)) obj[k] = "[REDACTED]";
178
+ else if (typeof obj[k] === "object") redactRecursively(obj[k]);
179
+ }
180
+ };
181
+ redactRecursively(copy);
182
+ return copy;
183
+ }
184
+ return data;
185
+ } catch {
186
+ return data;
187
+ }
188
+ }
189
+
190
+ export function getMaxTasks() {
191
+ const raw = process.env.OSTACKY_MAX_TASKS;
192
+ if (raw == null || raw === "") return MAX_TASKS_DEFAULT;
193
+ const n = parseInt(raw, 10);
194
+ if (Number.isNaN(n) || n <= 0) return MAX_TASKS_DEFAULT;
195
+ if (n > MAX_TASKS_CAP) return MAX_TASKS_CAP;
196
+ return n;
197
+ }
198
+
199
+ export function getAuditRetention() {
200
+ const raw = process.env.OSTACKY_AUDIT_RETENTION;
201
+ if (raw == null || raw === "") return 500;
202
+ const n = parseInt(raw, 10);
203
+ if (Number.isNaN(n) || n <= 0) return 500;
204
+ if (n > 2000) return 2000;
205
+ return n;
206
+ }