ostacky 0.9.0 → 0.9.2
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 +17 -17
- package/assets/agents/ostacky.md +5 -5
- package/assets/commands/install-stack.md +3 -3
- package/assets/docs/ostacky-reference.md +7 -3
- package/assets/mcp/ostacky-controller/index.js +96 -11
- package/assets/mcp/ostacky-controller/package.json +1 -1
- package/assets/plugins/engram.ts +7 -3
- package/assets/plugins/{ostacky-plugin.ts → ostacky-controller/index.ts} +31 -5
- package/assets/plugins/{tiered.ts → ostacky-controller/tiered.ts} +1 -1
- package/dist/cli.js +139 -73
- package/manifest.json +28 -28
- package/package.json +3 -9
- /package/assets/plugins/{controller-core.ts → ostacky-controller/controller-core.ts} +0 -0
- /package/assets/plugins/{security.ts → ostacky-controller/security.ts} +0 -0
package/README.md
CHANGED
|
@@ -246,37 +246,37 @@ Tras instalar (`--scope local`), el proyecto queda así:
|
|
|
246
246
|
|
|
247
247
|
```json
|
|
248
248
|
{
|
|
249
|
-
"version": "0.9.
|
|
249
|
+
"version": "0.9.2",
|
|
250
250
|
"lockedAt": "2025-01-01T00:00:00.000Z",
|
|
251
251
|
"repo": "JaimeHoracio/Ostacky",
|
|
252
|
-
"tag": "v0.9.
|
|
252
|
+
"tag": "v0.9.2",
|
|
253
253
|
"agents": {
|
|
254
254
|
"ostacky": {
|
|
255
|
-
"version": "0.9.
|
|
255
|
+
"version": "0.9.2",
|
|
256
256
|
"installedAt": "2025-01-01T00:00:00.000Z",
|
|
257
257
|
"sha256": "abc123..."
|
|
258
258
|
}
|
|
259
259
|
},
|
|
260
260
|
"commands": {
|
|
261
261
|
"install-stack": {
|
|
262
|
-
"version": "0.9.
|
|
262
|
+
"version": "0.9.2",
|
|
263
263
|
"installedAt": "2025-01-01T00:00:00.000Z",
|
|
264
264
|
"sha256": "def456..."
|
|
265
265
|
},
|
|
266
266
|
"opsx-sync": {
|
|
267
|
-
"version": "0.9.
|
|
267
|
+
"version": "0.9.2",
|
|
268
268
|
"installedAt": "2025-01-01T00:00:00.000Z",
|
|
269
269
|
"sha256": "ghi789..."
|
|
270
270
|
}
|
|
271
271
|
},
|
|
272
272
|
"skills": {
|
|
273
|
-
"brainstorming": { "version": "0.9.
|
|
274
|
-
"execution-mode-evaluation": { "version": "0.9.
|
|
275
|
-
"openspec-propose": { "version": "0.9.
|
|
273
|
+
"brainstorming": { "version": "0.9.2", ... },
|
|
274
|
+
"execution-mode-evaluation": { "version": "0.9.2", ... },
|
|
275
|
+
"openspec-propose": { "version": "0.9.2", ... }
|
|
276
276
|
},
|
|
277
277
|
"mcpServers": {
|
|
278
|
-
"ostacky-controller": { "version": "0.9.
|
|
279
|
-
"openspec": { "version": "0.9.
|
|
278
|
+
"ostacky-controller": { "version": "0.9.2", ... },
|
|
279
|
+
"openspec": { "version": "0.9.2", ... }
|
|
280
280
|
}
|
|
281
281
|
}
|
|
282
282
|
```
|
|
@@ -290,17 +290,17 @@ Se recomienda agregar `ostacky-lock.json` al control de versiones para que el eq
|
|
|
290
290
|
Al terminar la instalación, el flujo normal es:
|
|
291
291
|
|
|
292
292
|
1. **Iniciar OpenCode** (si no está corriendo):
|
|
293
|
-
- **TUI:** `opencode` en tu terminal →
|
|
294
|
-
- **Web:** `opencode web --port 4096` y abrí `http://localhost:4096` →
|
|
293
|
+
- **TUI:** `opencode` en tu terminal → seleccioná el agente con `/agents` (o atajos **Shift+Tab** / **Ctrl+X A**)
|
|
294
|
+
- **Web:** `opencode web --port 4096` y abrí `http://localhost:4096` → abrí el selector de agentes de la interfaz y elegí `ostacky`
|
|
295
295
|
2. OpenCode detecta automáticamente los archivos nuevos en `.opencode/` al iniciar, no necesita recarga manual.
|
|
296
|
-
3. Para usar el agente
|
|
296
|
+
3. Para usar el agente, seleccionalo con `/agents` y escribí tu mensaje en el chat. (En OpenCode v2, `@` es solo para referenciar archivos, no agentes.)
|
|
297
297
|
|
|
298
298
|
### Regenerar el stack manualmente
|
|
299
299
|
|
|
300
|
-
El comando `/install-stack` también está disponible **dentro del chat de OpenCode** (TUI o web) por si querés regenerar el stack local sin salir de la sesión
|
|
300
|
+
El comando `/install-stack` también está disponible **dentro del chat de OpenCode** (TUI o web) por si querés regenerar el stack local sin salir de la sesión. Seleccioná primero el agente `ostacky` (con `/agents`) y ejecutá:
|
|
301
301
|
|
|
302
302
|
```text
|
|
303
|
-
|
|
303
|
+
/install-stack
|
|
304
304
|
```
|
|
305
305
|
|
|
306
306
|
Es opcional y solo necesario si algo falló durante la instalación o si querés verificar que todo esté en orden.
|
|
@@ -308,7 +308,7 @@ Es opcional y solo necesario si algo falló durante la instalación o si querés
|
|
|
308
308
|
## Seguridad
|
|
309
309
|
|
|
310
310
|
- `opencode.jsonc` se versiona en el repo para compartir permisos y MCP de forma reproducible.
|
|
311
|
-
- Las URLs de descarga usan **tags de GitHub** (ej. `v0.9.
|
|
311
|
+
- Las URLs de descarga usan **tags de GitHub** (ej. `v0.9.2`), nunca `main` — instalaciones reproducibles
|
|
312
312
|
- Cada path de archivo descargado es validado para prevenir **path traversal**
|
|
313
313
|
- Los archivos incluyen **checksum SHA-256** opcional; si el manifest lo define, el contenido se verifica antes de escribir
|
|
314
314
|
- El cache local (`.opencode/cache/`) también valida integridad al servir archivos cacheados
|
|
@@ -365,7 +365,7 @@ CodeGraph está instalado en `.opencode/tools/codegraph/bin/codegraph` y se conf
|
|
|
365
365
|
| `.opencode/tools/codegraph/bin/codegraph status` | Muestra estado del index y archivos pendientes |
|
|
366
366
|
| `.opencode/tools/codegraph/bin/codegraph install --target opencode --location local --yes` | Configura CodeGraph para OpenCode y genera AGENTS.md |
|
|
367
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.9.
|
|
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.9.2 el instalador usa el patrón resiliente de Engram (strip 0 + búsqueda recursiva) y loguea `projectRoot|toolsDir` para diagnosticar scope.
|
|
369
369
|
|
|
370
370
|
## Licencia
|
|
371
371
|
|
package/assets/agents/ostacky.md
CHANGED
|
@@ -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.9.
|
|
4
|
+
version: 0.9.2
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Sos **Ostacky v0.9.
|
|
7
|
+
Sos **Ostacky v0.9.2**, orquestás, no implementás. Interpretás, clasificás (0/0+1/1+), ruteás y coordinás.
|
|
8
8
|
|
|
9
|
-
> **Versión:** `0.9.
|
|
9
|
+
> **Versión:** `0.9.2` (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.9.2"** (o `v0.9.2` si te piden solo el número). No inventes otra versión.
|
|
10
10
|
|
|
11
11
|
## Reglas innegociables
|
|
12
12
|
|
|
@@ -85,13 +85,13 @@ Router `brainstorming`↔`OpenSpec` por `level`/`estLines`/`fileCount`/`hasAPI`
|
|
|
85
85
|
|
|
86
86
|
1. `skill(execution-mode-evaluation)` en memoria, reusa discovery.
|
|
87
87
|
2. Mostrar análisis → `¿Procedo?` → `record_execution_analysis` → `consume_execution_decision`.
|
|
88
|
-
3. Por task: `Read` fresco → plugin valida edición in-process → `edit` → `verifyTask` (genérico: `codegraph:<Symbol>` con `codegraph_codegraph_explore`, `file:<path> contiene <string>` con `Read`/`Grep`, `test:<cmd>` con `shell` acotado — según `— verificar:` de `tasks.md`) → solo si `verifyTask.ok` → `complete_task`. **NUNCA marques `complete_task` a ojo**; si `verifyTask` falla, reintentá el fix.
|
|
88
|
+
3. Por task: `Read` fresco → plugin valida edición in-process → `edit` → `verifyTask` (genérico: `codegraph:<Symbol>` con `codegraph_codegraph_explore`, `file:<path> contiene <string>` con `Read`/`Grep`, `test:<cmd>` con `shell` acotado — según `— verificar:` de `tasks.md`) → solo si `verifyTask.ok` → `complete_task`. **NUNCA marques `complete_task` a ojo**; si `verifyTask` falla, reintentá el fix. Vale para SPEC y DIRECT, INLINE y SUBAGENTS.
|
|
89
89
|
|
|
90
90
|
### 5. Sync y cierre
|
|
91
91
|
|
|
92
92
|
1. Tests + review
|
|
93
93
|
2. `saveSessionClose` → `mem_session_summary` + `set_handoff` paralelo
|
|
94
|
-
3. `verifyIntegrity` → **si `pending.length > 0` NO llames `implementation_complete`**; lista explícita `Te faltan N tasks: [...]` y pide `complete_task`. Solo si `pending === 0` y `staleFiles === 0`, `implementation_complete` → `sync_complete`. **NUNCA llames `implementation_complete` automáticamente con pendientes** — espera a que el usuario complete o escriba literal `"confirmo forzar"` para `force:true` (queda auditado).
|
|
94
|
+
3. `verifyIntegrity` → **si `pending.length > 0` NO llames `implementation_complete`**; lista explícita `Te faltan N tasks: [...]` y pide `complete_task`. Si `staleFiles.length > 0` por refino post-complete (archivo ya correcto, sin edición pendiente): llamá `refresh_fingerprint({filePath})` por cada archivo y reintentá `implementation_complete` sin `force`. Solo si `pending === 0` y `staleFiles === 0`, `implementation_complete` → `sync_complete`. **NUNCA llames `implementation_complete` automáticamente con pendientes** — espera a que el usuario complete o escriba literal `"confirmo forzar"` para `force:true` (queda auditado). Vale para SPEC y DIRECT, INLINE y SUBAGENTS.
|
|
95
95
|
|
|
96
96
|
## Guardrails
|
|
97
97
|
|
|
@@ -5,13 +5,13 @@ agent: build
|
|
|
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.9.
|
|
8
|
+
**Nota:** A partir de v0.9.2, `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
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.9.
|
|
14
|
+
## Scope de instalación — local vs global (desde v0.9.2)
|
|
15
15
|
|
|
16
16
|
`npx ostacky install` soporta `--scope local|global|auto` (también `--scope=...`):
|
|
17
17
|
|
|
@@ -126,7 +126,7 @@ El controller MCP se configura como server local en `opencode.json`. Si el contr
|
|
|
126
126
|
Después de instalar, verificá que el controller responde:
|
|
127
127
|
|
|
128
128
|
```bash
|
|
129
|
-
# Desde el prompt de OpenCode con el agente
|
|
129
|
+
# Desde el prompt de OpenCode con el agente ostacky activo (seleccionalo con /agents):
|
|
130
130
|
ostacky-controller_ping
|
|
131
131
|
```
|
|
132
132
|
|
|
@@ -48,6 +48,10 @@ Allowlist: `.env.example/.template/.sample` nunca bloquea. `extractPathsFromBash
|
|
|
48
48
|
|
|
49
49
|
`content` requerido salvo `content:"hash:"+fastFingerprint` cuando `lastValidated.filePath==filePath && fingerprint==hash` y fingerprint coincide con disco → EDITABLE sin body. Hash stale → CONFLICT `stale fingerprint`.
|
|
50
50
|
|
|
51
|
+
## refresh_fingerprint (revalidación sin churn)
|
|
52
|
+
|
|
53
|
+
Tras refino post-complete (archivo ya correcto, sin edición pendiente): `refresh_fingerprint({filePath})` o `refresh_fingerprint({taskId})` re-sincroniza `tasks[].fileHash` + `fileFingerprints` con disco, sin `oldString/newString`. Vale para SPEC y DIRECT, en `EXECUTING_INLINE`, `EXECUTING_SUBAGENTS` y `SPECIFICATION`. Luego `verifyIntegrity.ok` → `implementation_complete` sin `force`.
|
|
54
|
+
|
|
51
55
|
## Enforcement (plugin)
|
|
52
56
|
|
|
53
57
|
El plugin hace cumplir `PENDING` en `ctx.tool.hook("execute.before")`; `lastCheck={revision, result}` cachea ALLOW por revisión, revalida si cambia o >5 tools; `BLOCKED` nunca cacheado. Métricas `stateCheckCount` cuentan checks del plugin (no del LLM).
|
|
@@ -64,15 +68,15 @@ Principio: **eficacia > recorte** — si recorte rompiera caché y saliera más
|
|
|
64
68
|
|
|
65
69
|
## Security single-source
|
|
66
70
|
|
|
67
|
-
`src/security.ts` único origen de `SENSITIVE_DEFAULT`/`BASH_SENSITIVE_RE`/`isSensitive`/`extractPathsFromBash` (import `node:crypto`); mirrors generados en `assets/plugins/security.ts` (solo Sensitive guard, sin crypto) y `assets/mcp/ostacky-controller/security.js` vía `bun run scripts/sync-controller-core.ts`. `ostacky-
|
|
71
|
+
`src/security.ts` único origen de `SENSITIVE_DEFAULT`/`BASH_SENSITIVE_RE`/`isSensitive`/`extractPathsFromBash` (import `node:crypto`); mirrors generados en `assets/plugins/ostacky-controller/security.ts` (solo Sensitive guard, sin crypto) y `assets/mcp/ostacky-controller/security.js` vía `bun run scripts/sync-controller-core.ts`. `assets/plugins/ostacky-controller/index.ts` importa de `./security.ts` self-contained (no `../../src`). `BASH_SENSITIVE_RE` arriba. `OSTACKY_SENSITIVE_PATTERNS` override.
|
|
68
72
|
|
|
69
73
|
## Tiered single-source
|
|
70
74
|
|
|
71
|
-
`src/tiered.ts` único origen de `isTrivial(msg,state)` + `getControllerState(dir)`. `
|
|
75
|
+
`src/tiered.ts` único origen de `isTrivial(msg,state)` + `getControllerState(dir)`. `index.ts` importa de `./tiered.ts` mirror (generado vía `sync-controller-core.ts`), `engram.ts` inlined (misma regex, evita import dinámico). No duplicar regex. Ver `assets/plugins/ostacky-controller/` y `assets/plugins/engram.ts`.
|
|
72
76
|
|
|
73
77
|
## Controller single-source
|
|
74
78
|
|
|
75
|
-
`src/controller-core.ts` CANÓNICO para `STATES/TRANSITIONS/DEFAULT_STATE`; mirrors generados en `assets/plugins/controller-core.ts` (TS) y `assets/mcp/ostacky-controller/controller-core.js` (JS strippado) vía `bun run scripts/sync-controller-core.ts` (prebuild). No editar mirrors. `
|
|
79
|
+
`src/controller-core.ts` CANÓNICO para `STATES/TRANSITIONS/DEFAULT_STATE`; mirrors generados en `assets/plugins/ostacky-controller/controller-core.ts` (TS) y `assets/mcp/ostacky-controller/controller-core.js` (JS strippado) vía `bun run scripts/sync-controller-core.ts` (prebuild). No editar mirrors. `index.ts` importa de `./controller-core.ts` self-contained. Import siempre `./security.ts` (el server V2 no resuelve `.js→.ts`).
|
|
76
80
|
|
|
77
81
|
## Prompt-efficiency
|
|
78
82
|
|
|
@@ -411,6 +411,22 @@ function fastFingerprint(filePath) {
|
|
|
411
411
|
}
|
|
412
412
|
}
|
|
413
413
|
|
|
414
|
+
// Refresh honesto sin churn: resuelve a absoluto vía projectRoot para no depender de cwd.
|
|
415
|
+
// Usado por stale-checks, complete_task y refreshFingerprint (fix post-complete refine).
|
|
416
|
+
function fingerprintFor(filePath, statePath) {
|
|
417
|
+
try {
|
|
418
|
+
if (!filePath) return null;
|
|
419
|
+
const projectRoot = getProjectRoot(statePath);
|
|
420
|
+
const abs =
|
|
421
|
+
filePath.startsWith('/') || /^[A-Za-z]:/.test(filePath)
|
|
422
|
+
? resolve(filePath)
|
|
423
|
+
: resolve(projectRoot, filePath);
|
|
424
|
+
return fastFingerprint(abs);
|
|
425
|
+
} catch {
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
414
430
|
class OstackyController {
|
|
415
431
|
#statePath;
|
|
416
432
|
#state;
|
|
@@ -1697,14 +1713,14 @@ class OstackyController {
|
|
|
1697
1713
|
try {
|
|
1698
1714
|
for (const [taskId, info] of Object.entries(this.#state.tasks || {})) {
|
|
1699
1715
|
if (info.status !== 'COMPLETED' || !info.filePath || !info.fileHash) continue;
|
|
1700
|
-
const current =
|
|
1716
|
+
const current = fingerprintFor(info.filePath, this.#statePath);
|
|
1701
1717
|
if (!current) staleFiles.push(`${taskId}:${info.filePath} (missing)`);
|
|
1702
1718
|
else if (current !== info.fileHash) staleFiles.push(`${taskId}:${info.filePath} (stale fingerprint)`);
|
|
1703
1719
|
seenFp2.add(info.filePath);
|
|
1704
1720
|
}
|
|
1705
1721
|
for (const [fp, stored] of Object.entries(this.#state.fileFingerprints || {})) {
|
|
1706
1722
|
if (seenFp2.has(fp)) continue;
|
|
1707
|
-
const cur =
|
|
1723
|
+
const cur = fingerprintFor(fp, this.#statePath);
|
|
1708
1724
|
if (!cur) staleFiles.push(`${fp} (missing)`);
|
|
1709
1725
|
else if (cur !== stored) staleFiles.push(`${fp} (stale fingerprint)`);
|
|
1710
1726
|
}
|
|
@@ -1764,7 +1780,7 @@ class OstackyController {
|
|
|
1764
1780
|
verificationFailed: verificationFailed.length ? verificationFailed : undefined,
|
|
1765
1781
|
current_state: this.#state.state,
|
|
1766
1782
|
attempted_transition: 'implementation_complete',
|
|
1767
|
-
suggestion: `${detail}
|
|
1783
|
+
suggestion: `${detail} Si refinaste archivos tras el complete (fingerprint stale), llamá refresh_fingerprint({filePath}) por cada archivo y reintentá implementation_complete sin force. Solo si hay edición real pendiente: Read fresco → validate_edit → edit → complete_task. Solo si querés descartar trabajo pendiente, escribí literal "confirmo forzar" en tu mensaje y reintentá con {force:true}. No propongo cancelar; propongo completar.`,
|
|
1768
1784
|
};
|
|
1769
1785
|
}
|
|
1770
1786
|
if (hasBlocking && force) {
|
|
@@ -1785,7 +1801,7 @@ class OstackyController {
|
|
|
1785
1801
|
verificationFailed: verificationFailed.length ? verificationFailed : undefined,
|
|
1786
1802
|
current_state: this.#state.state,
|
|
1787
1803
|
attempted_transition: 'implementation_complete',
|
|
1788
|
-
suggestion: `${detail} Para forzar el cierre, escribí literal "confirmo forzar" en tu mensaje (quedará auditado) y reintentá con {force:true}. Si no querés forzar,
|
|
1804
|
+
suggestion: `${detail} Para forzar el cierre, escribí literal "confirmo forzar" en tu mensaje (quedará auditado) y reintentá con {force:true}. Si no querés forzar y es solo stale por refino, llamá refresh_fingerprint({filePath}) y reintentá sin force.`,
|
|
1789
1805
|
};
|
|
1790
1806
|
}
|
|
1791
1807
|
const all = [...pending, ...staleFiles, ...verificationFailed].join(',');
|
|
@@ -1969,14 +1985,14 @@ class OstackyController {
|
|
|
1969
1985
|
try {
|
|
1970
1986
|
for (const [taskId, info] of Object.entries(this.#state.tasks || {})) {
|
|
1971
1987
|
if (info.status !== 'COMPLETED' || !info.filePath || !info.fileHash) continue;
|
|
1972
|
-
const current =
|
|
1988
|
+
const current = fingerprintFor(info.filePath, this.#statePath);
|
|
1973
1989
|
if (!current) staleFiles.push(`${taskId}:${info.filePath} (missing)`);
|
|
1974
1990
|
else if (current !== info.fileHash) staleFiles.push(`${taskId}:${info.filePath} (stale fingerprint)`);
|
|
1975
1991
|
seenFp.add(info.filePath);
|
|
1976
1992
|
}
|
|
1977
1993
|
for (const [fp, stored] of Object.entries(this.#state.fileFingerprints || {})) {
|
|
1978
1994
|
if (seenFp.has(fp)) continue;
|
|
1979
|
-
const current =
|
|
1995
|
+
const current = fingerprintFor(fp, this.#statePath);
|
|
1980
1996
|
if (!current) staleFiles.push(`${fp} (missing)`);
|
|
1981
1997
|
else if (current !== stored) staleFiles.push(`${fp} (stale fingerprint)`);
|
|
1982
1998
|
}
|
|
@@ -2448,11 +2464,11 @@ class OstackyController {
|
|
|
2448
2464
|
}
|
|
2449
2465
|
if (!this.#state.tasks) this.#state.tasks = {};
|
|
2450
2466
|
|
|
2451
|
-
// O6: Use fast fingerprint if no hash provided
|
|
2452
|
-
const effectiveHash = fileHash || (filePath ?
|
|
2467
|
+
// O6: Use fast fingerprint if no hash provided (absoluto vía projectRoot)
|
|
2468
|
+
const effectiveHash = fileHash || (filePath ? fingerprintFor(filePath, this.#statePath) : null);
|
|
2453
2469
|
// 1.6: fingerprint obligatorio si archivo existe
|
|
2454
2470
|
if (filePath) {
|
|
2455
|
-
const existsCheck =
|
|
2471
|
+
const existsCheck = fingerprintFor(filePath, this.#statePath);
|
|
2456
2472
|
if (existsCheck && !effectiveHash) {
|
|
2457
2473
|
return { error: 'fingerprint required: file exists but fileHash is null' };
|
|
2458
2474
|
}
|
|
@@ -2561,6 +2577,58 @@ class OstackyController {
|
|
|
2561
2577
|
};
|
|
2562
2578
|
}
|
|
2563
2579
|
|
|
2580
|
+
/**
|
|
2581
|
+
* Refresh honesto sin churn: re-sincroniza fingerprints con disco tras refino
|
|
2582
|
+
* post-complete. Vale para SPEC y DIRECT, en EXECUTING_INLINE, EXECUTING_SUBAGENTS
|
|
2583
|
+
* y SPECIFICATION. No exige oldString/newString ni reeditar.
|
|
2584
|
+
*/
|
|
2585
|
+
async refreshFingerprint({ filePath, taskId } = {}) {
|
|
2586
|
+
this.#load();
|
|
2587
|
+
const allowed = ['EXECUTING_INLINE', 'EXECUTING_SUBAGENTS', 'SPECIFICATION'];
|
|
2588
|
+
if (!allowed.includes(this.#state.state)) {
|
|
2589
|
+
return { error: `Cannot refresh fingerprint from state ${this.#state.state}`, ok: false };
|
|
2590
|
+
}
|
|
2591
|
+
if (taskId && !isValidTaskId(taskId)) return { error: `invalid taskId: ${taskId}`, ok: false };
|
|
2592
|
+
let target = filePath || null;
|
|
2593
|
+
if (!target && taskId && this.#state.tasks?.[taskId]?.filePath) {
|
|
2594
|
+
target = this.#state.tasks[taskId].filePath;
|
|
2595
|
+
}
|
|
2596
|
+
if (!target) return { error: 'filePath or taskId with file is required', ok: false };
|
|
2597
|
+
if (!isPathInsideProject(target, this.#statePath)) {
|
|
2598
|
+
return { error: `filePath outside projectRoot: ${target}`, ok: false, filePath: target };
|
|
2599
|
+
}
|
|
2600
|
+
if (this.isSensitiveFile(target) && !this.#state.allowedFiles?.[target]) {
|
|
2601
|
+
return { error: `BLOCKED: File ${target} requires check_file_access`, ok: false, filePath: target };
|
|
2602
|
+
}
|
|
2603
|
+
const projectRoot = getProjectRoot(this.#statePath);
|
|
2604
|
+
const abs =
|
|
2605
|
+
target.startsWith('/') || /^[A-Za-z]:/.test(target) ? resolve(target) : resolve(projectRoot, target);
|
|
2606
|
+
if (!existsSync(abs)) return { error: `file not found: ${target}`, ok: false, filePath: target };
|
|
2607
|
+
const current = fingerprintFor(target, this.#statePath);
|
|
2608
|
+
if (!current) return { error: `cannot fingerprint: ${target}`, ok: false, filePath: target };
|
|
2609
|
+
const updatedTasks = [];
|
|
2610
|
+
if (!this.#state.fileFingerprints) this.#state.fileFingerprints = {};
|
|
2611
|
+
this.#state.fileFingerprints[target] = current;
|
|
2612
|
+
if (taskId && this.#state.tasks?.[taskId]) {
|
|
2613
|
+
this.#state.tasks[taskId].fileHash = current;
|
|
2614
|
+
if (!this.#state.tasks[taskId].filePath) this.#state.tasks[taskId].filePath = target;
|
|
2615
|
+
updatedTasks.push(taskId);
|
|
2616
|
+
}
|
|
2617
|
+
for (const [tid, info] of Object.entries(this.#state.tasks || {})) {
|
|
2618
|
+
if (info.filePath === target && !updatedTasks.includes(tid)) {
|
|
2619
|
+
info.fileHash = current;
|
|
2620
|
+
updatedTasks.push(tid);
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2623
|
+
await this.#persist();
|
|
2624
|
+
await this.#audit(
|
|
2625
|
+
'EXECUTING',
|
|
2626
|
+
'refresh_fingerprint',
|
|
2627
|
+
`filePath=${target} tasks=${updatedTasks.join(',') || '-'}`
|
|
2628
|
+
);
|
|
2629
|
+
return { ok: true, filePath: target, fingerprint: current, updatedTasks };
|
|
2630
|
+
}
|
|
2631
|
+
|
|
2564
2632
|
async verifyTask({ taskId, filePath: filePathParam } = {}) {
|
|
2565
2633
|
this.#load();
|
|
2566
2634
|
if (!taskId) return { error: 'taskId required' };
|
|
@@ -2785,7 +2853,7 @@ function safeHandler(fn, options = {}) {
|
|
|
2785
2853
|
|
|
2786
2854
|
const server = new McpServer({
|
|
2787
2855
|
name: 'ostacky-controller',
|
|
2788
|
-
version: '0.9.
|
|
2856
|
+
version: '0.9.2',
|
|
2789
2857
|
});
|
|
2790
2858
|
|
|
2791
2859
|
server.registerTool(
|
|
@@ -3326,6 +3394,23 @@ server.registerTool(
|
|
|
3326
3394
|
})
|
|
3327
3395
|
);
|
|
3328
3396
|
|
|
3397
|
+
server.registerTool(
|
|
3398
|
+
'refresh_fingerprint',
|
|
3399
|
+
{
|
|
3400
|
+
description:
|
|
3401
|
+
'Re-sincroniza fingerprints con disco tras refino post-complete, sin reeditar. ' +
|
|
3402
|
+
'Vale para SPEC y DIRECT, en EXECUTING_INLINE, EXECUTING_SUBAGENTS y SPECIFICATION.',
|
|
3403
|
+
inputSchema: z.object({
|
|
3404
|
+
filePath: z.string().optional().describe('File path refinado tras complete.'),
|
|
3405
|
+
taskId: z.string().optional().describe('Task ID (si se omite filePath se usa su archivo).'),
|
|
3406
|
+
}),
|
|
3407
|
+
},
|
|
3408
|
+
safeHandler(async ({ filePath, taskId }) => {
|
|
3409
|
+
log('tool:refresh_fingerprint', { filePath, taskId });
|
|
3410
|
+
return await controller.refreshFingerprint({ filePath, taskId });
|
|
3411
|
+
})
|
|
3412
|
+
);
|
|
3413
|
+
|
|
3329
3414
|
/**
|
|
3330
3415
|
* Graceful shutdown: clean up tmp/lock files and flush state.
|
|
3331
3416
|
*/
|
|
@@ -3357,7 +3442,7 @@ function setupGracefulShutdown(ctrl) {
|
|
|
3357
3442
|
}
|
|
3358
3443
|
|
|
3359
3444
|
async function main() {
|
|
3360
|
-
log('Starting ostacky-controller MCP v0.9.
|
|
3445
|
+
log('Starting ostacky-controller MCP v0.9.2...');
|
|
3361
3446
|
log('State path:', { path: statePath });
|
|
3362
3447
|
// Clean up stale tmp/lock files from previous runs
|
|
3363
3448
|
cleanupTmpFiles(statePath);
|
package/assets/plugins/engram.ts
CHANGED
|
@@ -14,7 +14,10 @@
|
|
|
14
14
|
* hooks (event.sessionID) rather than relying on a session.created event.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import { Plugin }
|
|
17
|
+
// NOTA: sin `import { Plugin }` runtime a propósito — el server V2 no resuelve
|
|
18
|
+
// `@opencode/plugin` desde `.opencode/plugins/` y `Plugin.define()` es
|
|
19
|
+
// passthrough ({id, setup}). `import type` se borra al transpilar.
|
|
20
|
+
import type { Plugin } from "@opencode/plugin"
|
|
18
21
|
import { join, dirname, basename, delimiter } from "node:path"
|
|
19
22
|
import { readFileSync, writeFileSync, renameSync, mkdirSync, existsSync } from "node:fs"
|
|
20
23
|
import { spawn, spawnSync } from "node:child_process"
|
|
@@ -197,7 +200,8 @@ function stripJsoncComments(text: string): string {
|
|
|
197
200
|
|
|
198
201
|
// ─── Plugin ──────────────────────────────────────────────────────────────────
|
|
199
202
|
|
|
200
|
-
|
|
203
|
+
// Objeto literal directo (ver nota en imports).
|
|
204
|
+
export default {
|
|
201
205
|
id: "engram",
|
|
202
206
|
async setup(ctx) {
|
|
203
207
|
const directory = ctx.location.directory
|
|
@@ -645,4 +649,4 @@ export default Plugin.define({
|
|
|
645
649
|
|
|
646
650
|
return () => eventController.abort()
|
|
647
651
|
},
|
|
648
|
-
}
|
|
652
|
+
}
|
|
@@ -11,7 +11,11 @@
|
|
|
11
11
|
* CodeGraph preventivo: bloquea Read/Grep masivo sin Discovery hit.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import { Plugin } from "@opencode/plugin"
|
|
14
|
+
// NOTA: sin `import { Plugin } from "@opencode/plugin"` a propósito — el server
|
|
15
|
+
// V2 no resuelve ese paquete desde `.opencode/plugins/` (Die ResolveMessage) y
|
|
16
|
+
// `Plugin.define()` es passthrough ({id, setup}). Objeto literal + `import type`
|
|
17
|
+
// (solo tipos, borrado al transpilar) cargan sin dependencias externas.
|
|
18
|
+
import type { Plugin } from "@opencode/plugin"
|
|
15
19
|
import { readFileSync, writeFileSync, renameSync, mkdirSync, existsSync, statSync, readdirSync, unlinkSync } from "node:fs"
|
|
16
20
|
import { join, dirname, basename, resolve, relative, delimiter } from "node:path"
|
|
17
21
|
import { SENSITIVE_DEFAULT, BASH_SENSITIVE_RE, isSensitive, extractPathsFromBash } from "./security.ts"
|
|
@@ -178,7 +182,9 @@ function extractPathsFromPatch(patchText: string): string[] {
|
|
|
178
182
|
|
|
179
183
|
// ─── Plugin ──────────────────────────────────────────────────────────────────
|
|
180
184
|
|
|
181
|
-
|
|
185
|
+
// Objeto literal directo (ver nota en imports): el server V2 acepta
|
|
186
|
+
// `{ id, setup }` sin pasar por `Plugin.define()`.
|
|
187
|
+
export default {
|
|
182
188
|
id: "ostacky-controller",
|
|
183
189
|
async setup(ctx) {
|
|
184
190
|
const directory = ctx.location.directory
|
|
@@ -374,9 +380,27 @@ export default Plugin.define({
|
|
|
374
380
|
const isCodeFile = /\.(ts|js|tsx|jsx|mts|cts)$/i.test(targetPath) || targetPath.includes("src/") || targetPath.includes("assets/") || args?.pattern?.includes("*.ts")
|
|
375
381
|
// harden-task-integrity 1.3: eximir reads de tasks.md del gate (no requiere CodeGraph)
|
|
376
382
|
const isSpecTasksPath = targetPath.includes("openspec/changes") || targetPath.includes("openspec/specs")
|
|
383
|
+
// Docs y markdown nunca son código: no exigir Discovery (fix over-blocking assets/docs)
|
|
384
|
+
const isDocsPath = targetPath.includes("assets/docs") || targetPath.includes("/docs/") || /\.md$/i.test(targetPath) || /\.json$/i.test(targetPath)
|
|
377
385
|
// Grep on *.md should not be blocked
|
|
378
386
|
const isLiteralGrep = tool === "grep" && (args?.include?.endsWith(".md") || args?.include?.endsWith(".json"))
|
|
379
|
-
|
|
387
|
+
// Refresh honesto: revalidación post-complete necesita Read fresco sin Discovery hit.
|
|
388
|
+
// Vale para SPEC y DIRECT, INLINE y SUBAGENTS: si el path está trackeado en
|
|
389
|
+
// tasks/fileFingerprints/lastValidated, no exigir Discovery.
|
|
390
|
+
let isTaskTracked = false
|
|
391
|
+
try {
|
|
392
|
+
const st = freshState
|
|
393
|
+
if (st && targetPath) {
|
|
394
|
+
const fps = Object.keys(st.fileFingerprints || {})
|
|
395
|
+
const taskFiles = Object.values(st.tasks || {}).map((t: any) => t.filePath).filter(Boolean)
|
|
396
|
+
const lvFile = (st.lastValidated as any)?.filePath || null
|
|
397
|
+
isTaskTracked =
|
|
398
|
+
fps.some((fp) => targetPath.endsWith(fp) || fp.endsWith(targetPath) || targetPath.includes(fp) || fp.includes(targetPath)) ||
|
|
399
|
+
taskFiles.some((fp: string) => targetPath.endsWith(fp) || fp.endsWith(targetPath) || targetPath.includes(fp) || fp.includes(targetPath)) ||
|
|
400
|
+
(lvFile ? targetPath.endsWith(lvFile) || lvFile.endsWith(targetPath) : false)
|
|
401
|
+
}
|
|
402
|
+
} catch {}
|
|
403
|
+
if (isCodeFile && !isLiteralGrep && !isSpecTasksPath && !isDocsPath && !isTaskTracked) {
|
|
380
404
|
const hasDiscoveryHit = discoveryHitByRequest.get(sessionId) ?? getDiscoveryCacheHit(directory)
|
|
381
405
|
const codegraphOk = isCodegraphAvailable(directory)
|
|
382
406
|
if (codegraphOk && !hasDiscoveryHit) {
|
|
@@ -420,7 +444,9 @@ export default Plugin.define({
|
|
|
420
444
|
// ── 3.5) Shell file-mutation gate (harden-task-integrity: shell mutante requiere validate_edit) ──
|
|
421
445
|
if (tool === "shell" && freshState && ["EXECUTING_INLINE", "EXECUTING_SUBAGENTS"].includes(freshState.state)) {
|
|
422
446
|
const cmdForMutation: string = args?.command || args?.cmd || ""
|
|
423
|
-
|
|
447
|
+
// Fix falso positivo: `2>&1`, `1>&2`, `2>/dev/null` son redirects de stderr, no mutación.
|
|
448
|
+
const cmdSansStderr = cmdForMutation.replace(/2>&1/g, "").replace(/1>&2/g, "").replace(/2>\s*\/dev\/null/g, "")
|
|
449
|
+
const isMutating = /(^|[^0-9&])>{1,2}\s*(?!&)\S+|\bsed\b[^|;]*-i|\btruncate\b|\btee\b|\bcp\s+|\bmv\s+|python.*open.*w/.test(cmdSansStderr)
|
|
424
450
|
if (isMutating) {
|
|
425
451
|
const mPaths = extractPathsFromBash(cmdForMutation)
|
|
426
452
|
for (const p of mPaths) {
|
|
@@ -711,4 +737,4 @@ export default Plugin.define({
|
|
|
711
737
|
eventController.abort()
|
|
712
738
|
}
|
|
713
739
|
},
|
|
714
|
-
}
|
|
740
|
+
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
/**
|
|
7
7
|
* Tiered helpers — single source of truth for trivial detection y niveles.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
9
|
+
* Consumido por el plugin ostacky-controller/index.ts y engram.ts (mirror en package dir).
|
|
10
10
|
* No duplicar regex. Cache-friendly: isTrivial no muta system[0], solo decide hint.
|
|
11
11
|
* LEVEL_THRESHOLDS es la única definición de niveles (D6).
|
|
12
12
|
*/
|
package/dist/cli.js
CHANGED
|
@@ -621,31 +621,31 @@ var init_fs = __esm(() => {
|
|
|
621
621
|
var manifest_default;
|
|
622
622
|
var init_manifest = __esm(() => {
|
|
623
623
|
manifest_default = {
|
|
624
|
-
version: "0.9.
|
|
624
|
+
version: "0.9.2",
|
|
625
625
|
repo: "JaimeHoracio/Ostacky",
|
|
626
|
-
tag: "v0.9.
|
|
626
|
+
tag: "v0.9.2",
|
|
627
627
|
agents: [
|
|
628
628
|
{
|
|
629
629
|
name: "ostacky",
|
|
630
630
|
file: "assets/agents/ostacky.md",
|
|
631
|
-
description: "Orquestador con recuperación automática (nunca se congela), ruteo por nivel de impacto, controller MCP con SDK oficial, edición segura con fallback inline, y delegación en OpenSpec + Superpowers. v0.9.
|
|
632
|
-
version: "0.9.
|
|
633
|
-
sha256: "
|
|
631
|
+
description: "Orquestador con recuperación automática (nunca se congela), ruteo por nivel de impacto, controller MCP con SDK oficial, edición segura con fallback inline, y delegación en OpenSpec + Superpowers. v0.9.2: prune de skills obsoletas, lastHandoff, getAvailableTransitions, consecutiveFailures real, mem_session_summary automático al cierre.",
|
|
632
|
+
version: "0.9.2",
|
|
633
|
+
sha256: "f588cd07f67f73393f23601b025d1f5980a7db4efe194f248ff4d09cc98b04d4"
|
|
634
634
|
}
|
|
635
635
|
],
|
|
636
636
|
commands: [
|
|
637
637
|
{
|
|
638
638
|
name: "install-stack",
|
|
639
639
|
file: "assets/commands/install-stack.md",
|
|
640
|
-
description: "Instala el stack tecnológico del proyecto (CodeGraph, skills, OpenSpec, Engram, controller plugin). v0.9.
|
|
641
|
-
version: "0.9.
|
|
642
|
-
sha256: "
|
|
640
|
+
description: "Instala el stack tecnológico del proyecto (CodeGraph, skills, OpenSpec, Engram, controller plugin). v0.9.2: controller plugin como alma hard-gate, Context7 removido del stack.",
|
|
641
|
+
version: "0.9.2",
|
|
642
|
+
sha256: "288e2fdd0df24378b92c15a169b846bd9cbc3290dfaf09597293e6f5240f562e"
|
|
643
643
|
},
|
|
644
644
|
{
|
|
645
645
|
name: "opsx-sync",
|
|
646
646
|
file: "assets/commands/opsx-sync.md",
|
|
647
647
|
description: "Sincroniza delta specs del change activo sin inicializar CodeGraph si ya existe índice",
|
|
648
|
-
version: "0.9.
|
|
648
|
+
version: "0.9.2",
|
|
649
649
|
sha256: "fe0158478f2ca63b315037a85fc1632b77868532e319af6c7384285441767d64"
|
|
650
650
|
}
|
|
651
651
|
],
|
|
@@ -653,15 +653,15 @@ var init_manifest = __esm(() => {
|
|
|
653
653
|
{
|
|
654
654
|
name: "ostacky-controller",
|
|
655
655
|
file: "assets/mcp/ostacky-controller/",
|
|
656
|
-
description: "Máquina de estados persistida con @modelcontextprotocol/server SDK. v0.9.
|
|
657
|
-
version: "0.9.
|
|
658
|
-
sha256: "
|
|
656
|
+
description: "Máquina de estados persistida con @modelcontextprotocol/server SDK. v0.9.2: 22 tools (incluye set_handoff, get_handoff, clear_handoff, get_available_transitions funcional). Bugfixes: consecutiveFailures real, lastHandoff state, defaultChoice persistido. Robustez: degraded mode automático tras 3 fallos, persistence condicional para Nivel 0, prune de skills obsoletas.",
|
|
657
|
+
version: "0.9.2",
|
|
658
|
+
sha256: "1fa9251efbf98552a8eb5f88a2f741021b82b21bc37f96d5253849f9209c949a"
|
|
659
659
|
},
|
|
660
660
|
{
|
|
661
661
|
name: "openspec",
|
|
662
662
|
file: "assets/mcp/openspec/",
|
|
663
663
|
description: "MCP server local para OpenSpec - proposal, apply, archive, sync de cambios",
|
|
664
|
-
version: "0.9.
|
|
664
|
+
version: "0.9.2",
|
|
665
665
|
sha256: "fa4be28bfc1e75db8f1a037e2580f04a60066c80e8e5934e90e1041020d04609"
|
|
666
666
|
}
|
|
667
667
|
],
|
|
@@ -670,105 +670,105 @@ var init_manifest = __esm(() => {
|
|
|
670
670
|
name: "brainstorming",
|
|
671
671
|
file: "assets/skills/brainstorming/SKILL.md",
|
|
672
672
|
description: "Skill unificado de pensamiento con dos modos: creative-design (producción de diseño → transición a implementación directa o openspec-propose) y open-exploration (exploración libre)",
|
|
673
|
-
version: "0.9.
|
|
673
|
+
version: "0.9.2",
|
|
674
674
|
sha256: "f1013a86a8f27914d85a716e23c7fd32db1786d93615e8b0834b13d184308549"
|
|
675
675
|
},
|
|
676
676
|
{
|
|
677
677
|
name: "execution-mode-evaluation",
|
|
678
678
|
file: "assets/skills/execution-mode-evaluation/SKILL.md",
|
|
679
679
|
description: "Skill de análisis de modo de ejecución — output reconciliado con controller snapshot contract (recommendation field)",
|
|
680
|
-
version: "0.9.
|
|
680
|
+
version: "0.9.2",
|
|
681
681
|
sha256: "2158a6864c0912e868a9d720db7fd79441e6cc298e3320833d2527c9727c852e"
|
|
682
682
|
},
|
|
683
683
|
{
|
|
684
684
|
name: "tdd",
|
|
685
685
|
file: "assets/skills/tdd/SKILL.md",
|
|
686
686
|
description: "Skill de test-driven development (Superpowers)",
|
|
687
|
-
version: "0.9.
|
|
687
|
+
version: "0.9.2",
|
|
688
688
|
sha256: "f0ebfbbc43a8868d31a3fd2df37ae7f60fe8add09c6475862395a68269e5ff7f"
|
|
689
689
|
},
|
|
690
690
|
{
|
|
691
691
|
name: "subagent-driven-development",
|
|
692
692
|
file: "assets/skills/subagent-driven-development/SKILL.md",
|
|
693
693
|
description: "Skill de ejecución con subagentes (Superpowers) — ejecuta solo después de confirmación del coordinador Ostacky",
|
|
694
|
-
version: "0.9.
|
|
694
|
+
version: "0.9.2",
|
|
695
695
|
sha256: "1a42d714a9a13faf05f0bf7b580e8d837c2e77a5633839542ccce603023419e8"
|
|
696
696
|
},
|
|
697
697
|
{
|
|
698
698
|
name: "dispatching-parallel-agents",
|
|
699
699
|
file: "assets/skills/dispatching-parallel-agents/SKILL.md",
|
|
700
700
|
description: "Skill de dispatch paralelo de agentes (Superpowers)",
|
|
701
|
-
version: "0.9.
|
|
701
|
+
version: "0.9.2",
|
|
702
702
|
sha256: "7e7b712ef742d5631e302e6a71583e2bbbd33094da9281abfb84b43505fc3ef1"
|
|
703
703
|
},
|
|
704
704
|
{
|
|
705
705
|
name: "review",
|
|
706
706
|
file: "assets/skills/review/SKILL.md",
|
|
707
707
|
description: "Skill de revisión de código (Superpowers)",
|
|
708
|
-
version: "0.9.
|
|
708
|
+
version: "0.9.2",
|
|
709
709
|
sha256: "b19650ed4d1d4d9857a4dd5b7e08e91328a1a1fc9c45bcc2fb600fa9d0213279"
|
|
710
710
|
},
|
|
711
711
|
{
|
|
712
712
|
name: "receiving-code-review",
|
|
713
713
|
file: "assets/skills/receiving-code-review/SKILL.md",
|
|
714
714
|
description: "Skill de recibir y procesar feedback de code review",
|
|
715
|
-
version: "0.9.
|
|
715
|
+
version: "0.9.2",
|
|
716
716
|
sha256: "d761e884e71d8d3476ac734d287cae403a4024506a7d12e7361a448217c0a831"
|
|
717
717
|
},
|
|
718
718
|
{
|
|
719
719
|
name: "openspec-propose",
|
|
720
720
|
file: "assets/skills/openspec-propose/SKILL.md",
|
|
721
721
|
description: "Skill de generación de proposal (OpenSpec)",
|
|
722
|
-
version: "0.9.
|
|
722
|
+
version: "0.9.2",
|
|
723
723
|
sha256: "6a517f750f24c008208156a2d653e97ed30b1e53878aeac5ce394e573db0678f"
|
|
724
724
|
},
|
|
725
725
|
{
|
|
726
726
|
name: "openspec-apply-change",
|
|
727
727
|
file: "assets/skills/openspec-apply-change/SKILL.md",
|
|
728
728
|
description: "Skill de aplicación de change (OpenSpec)",
|
|
729
|
-
version: "0.9.
|
|
729
|
+
version: "0.9.2",
|
|
730
730
|
sha256: "dfc823bf89fc7505e91ab6dee9c1f004be38a410bd3d88fb67b211b1b6cbb1d0"
|
|
731
731
|
},
|
|
732
732
|
{
|
|
733
733
|
name: "openspec-archive-change",
|
|
734
734
|
file: "assets/skills/openspec-archive-change/SKILL.md",
|
|
735
735
|
description: "Skill de archivo de change (OpenSpec)",
|
|
736
|
-
version: "0.9.
|
|
736
|
+
version: "0.9.2",
|
|
737
737
|
sha256: "16e4b561de7747283663fed602e506abf502452c49a8d4b86d7a5539c40f0195"
|
|
738
738
|
},
|
|
739
739
|
{
|
|
740
740
|
name: "openspec-explore",
|
|
741
741
|
file: "assets/skills/openspec-explore/SKILL.md",
|
|
742
742
|
description: "Modo explore para OpenSpec — thinking partner para explorar ideas, investigar problemas y clarificar requisitos antes/durante un cambio",
|
|
743
|
-
version: "0.9.
|
|
743
|
+
version: "0.9.2",
|
|
744
744
|
sha256: "9eafc3a8c693b8d9916867ef764d49e5222da717fde501c957bb3f6c312d2415"
|
|
745
745
|
},
|
|
746
746
|
{
|
|
747
747
|
name: "using-git-worktrees",
|
|
748
748
|
file: "assets/skills/using-git-worktrees/SKILL.md",
|
|
749
749
|
description: "Skill de uso de git worktrees para aislamiento de trabajo",
|
|
750
|
-
version: "0.9.
|
|
750
|
+
version: "0.9.2",
|
|
751
751
|
sha256: "93341bc1b7c053618a8b6dc07e3615b77990d7b549a0201f5835d95fef67ce13"
|
|
752
752
|
},
|
|
753
753
|
{
|
|
754
754
|
name: "using-superpowers",
|
|
755
755
|
file: "assets/skills/using-superpowers/SKILL.md",
|
|
756
756
|
description: "Skill de orquestación de Superpowers skills",
|
|
757
|
-
version: "0.9.
|
|
757
|
+
version: "0.9.2",
|
|
758
758
|
sha256: "7e54536f96d2a379185a10bfc1e970850caa2561b6d8aca7080f0defea0381a4"
|
|
759
759
|
},
|
|
760
760
|
{
|
|
761
761
|
name: "writing-skills",
|
|
762
762
|
file: "assets/skills/writing-skills/SKILL.md",
|
|
763
763
|
description: "Skill de creación y edición de skills",
|
|
764
|
-
version: "0.9.
|
|
764
|
+
version: "0.9.2",
|
|
765
765
|
sha256: "3d76b906cee518a2b809febb70db95697a35b9f368986bb504b4120c3bfb437a"
|
|
766
766
|
},
|
|
767
767
|
{
|
|
768
768
|
name: "graceful-degradation",
|
|
769
769
|
file: "assets/skills/graceful-degradation/SKILL.md",
|
|
770
770
|
description: "Skill de degradación graceful cuando múltiples tools están indisponibles",
|
|
771
|
-
version: "0.9.
|
|
771
|
+
version: "0.9.2",
|
|
772
772
|
sha256: "363449bd9e6f800a0a3168667cf78f767fb105d86e4a4adadcc649d418139303"
|
|
773
773
|
}
|
|
774
774
|
]
|
|
@@ -820,6 +820,10 @@ function getBundledMcpPath(name) {
|
|
|
820
820
|
validateFilePath(name);
|
|
821
821
|
return join3(BUNDLED_MCP_DIR, name);
|
|
822
822
|
}
|
|
823
|
+
function getBundledExpectedHash(category, name) {
|
|
824
|
+
const items = manifest_default[category] ?? [];
|
|
825
|
+
return items.find((item) => item.name === name)?.sha256 ?? null;
|
|
826
|
+
}
|
|
823
827
|
function getRawUrl(repo, tag, path) {
|
|
824
828
|
return `${GITHUB_RAW}/${repo}/${tag}/${path}`;
|
|
825
829
|
}
|
|
@@ -1174,22 +1178,25 @@ function copyEngramPlugin(projectRoot) {
|
|
|
1174
1178
|
copyFileSync3(pluginSource, join7(pluginsDir, "engram.ts"));
|
|
1175
1179
|
}
|
|
1176
1180
|
function copyOstackyControllerPlugin(projectRoot) {
|
|
1177
|
-
const
|
|
1178
|
-
const coreSource = join7(PACKAGE_ROOT, "assets", "plugins", "controller-core.ts");
|
|
1179
|
-
const securitySource = join7(PACKAGE_ROOT, "assets", "plugins", "security.ts");
|
|
1180
|
-
const tieredSource = join7(PACKAGE_ROOT, "assets", "plugins", "tiered.ts");
|
|
1181
|
+
const packageSource = join7(PACKAGE_ROOT, "assets", "plugins", "ostacky-controller");
|
|
1181
1182
|
const pluginsDir = join7(projectRoot, ".opencode", "plugins");
|
|
1182
|
-
|
|
1183
|
-
|
|
1183
|
+
const packageDest = join7(pluginsDir, "ostacky-controller");
|
|
1184
|
+
if (!existsSync6(join7(packageSource, "index.ts"))) {
|
|
1185
|
+
throw new Error(`Plugin bundleado de OstackyController no encontrado: ${packageSource}`);
|
|
1186
|
+
}
|
|
1187
|
+
mkdirSync4(packageDest, { recursive: true });
|
|
1188
|
+
for (const f of ["index.ts", "controller-core.ts", "security.ts", "tiered.ts"]) {
|
|
1189
|
+
const src = join7(packageSource, f);
|
|
1190
|
+
if (existsSync6(src))
|
|
1191
|
+
copyFileSync3(src, join7(packageDest, f));
|
|
1192
|
+
}
|
|
1193
|
+
for (const f of ["ostacky-plugin.ts", "controller-core.ts", "security.ts", "tiered.ts"]) {
|
|
1194
|
+
const legacy = join7(pluginsDir, f);
|
|
1195
|
+
try {
|
|
1196
|
+
if (existsSync6(legacy))
|
|
1197
|
+
rmSync4(legacy, { force: true });
|
|
1198
|
+
} catch {}
|
|
1184
1199
|
}
|
|
1185
|
-
mkdirSync4(pluginsDir, { recursive: true });
|
|
1186
|
-
copyFileSync3(pluginSource, join7(pluginsDir, "ostacky-plugin.ts"));
|
|
1187
|
-
if (existsSync6(coreSource))
|
|
1188
|
-
copyFileSync3(coreSource, join7(pluginsDir, "controller-core.ts"));
|
|
1189
|
-
if (existsSync6(securitySource))
|
|
1190
|
-
copyFileSync3(securitySource, join7(pluginsDir, "security.ts"));
|
|
1191
|
-
if (existsSync6(tieredSource))
|
|
1192
|
-
copyFileSync3(tieredSource, join7(pluginsDir, "tiered.ts"));
|
|
1193
1200
|
}
|
|
1194
1201
|
function buildEngramDownloadUrl(tag, platform = process.platform, arch = process.arch) {
|
|
1195
1202
|
const target = getEngramReleaseTarget(platform, arch);
|
|
@@ -1502,6 +1509,23 @@ function uninstallStackConfig(paths) {
|
|
|
1502
1509
|
removed.push(".opencode/tools/");
|
|
1503
1510
|
} catch {}
|
|
1504
1511
|
}
|
|
1512
|
+
const ownedPlugins = [
|
|
1513
|
+
"ostacky-controller",
|
|
1514
|
+
"engram.ts",
|
|
1515
|
+
"ostacky-plugin.ts",
|
|
1516
|
+
"controller-core.ts",
|
|
1517
|
+
"security.ts",
|
|
1518
|
+
"tiered.ts"
|
|
1519
|
+
];
|
|
1520
|
+
for (const name of ownedPlugins) {
|
|
1521
|
+
const p = join7(paths.plugins, name);
|
|
1522
|
+
if (existsSync6(p)) {
|
|
1523
|
+
try {
|
|
1524
|
+
rmSync4(p, { recursive: true, force: true });
|
|
1525
|
+
removed.push(`.opencode/plugins/${name}`);
|
|
1526
|
+
} catch {}
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1505
1529
|
if (removed.length === 0) {
|
|
1506
1530
|
return { success: true, message: "No había configuración del stack para remover" };
|
|
1507
1531
|
}
|
|
@@ -1518,13 +1542,15 @@ var init_stack = __esm(() => {
|
|
|
1518
1542
|
});
|
|
1519
1543
|
|
|
1520
1544
|
// src/doctor-v2.ts
|
|
1521
|
-
import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
|
|
1545
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6, readdirSync as readdirSync3 } from "fs";
|
|
1522
1546
|
import { join as join10, delimiter, isAbsolute } from "path";
|
|
1523
1547
|
function checkDoctorV2(projectRoot) {
|
|
1524
1548
|
const lines = [];
|
|
1525
1549
|
const cwd = projectRoot;
|
|
1526
1550
|
const opencodeDir = findOpenCodeDir(cwd) || join10(cwd, ".opencode");
|
|
1527
1551
|
const pluginPaths = [
|
|
1552
|
+
join10(opencodeDir, "plugins", "ostacky-controller", "index.ts"),
|
|
1553
|
+
join10(cwd, ".opencode", "plugins", "ostacky-controller", "index.ts"),
|
|
1528
1554
|
join10(opencodeDir, "plugins", "ostacky-plugin.ts"),
|
|
1529
1555
|
join10(cwd, ".opencode", "plugins", "ostacky-plugin.ts"),
|
|
1530
1556
|
join10(cwd, "assets", "plugins", "ostacky-plugin.ts")
|
|
@@ -1542,6 +1568,33 @@ function checkDoctorV2(projectRoot) {
|
|
|
1542
1568
|
lines.push(`⚠️ controller: no se pudo leer ${found}`);
|
|
1543
1569
|
}
|
|
1544
1570
|
}
|
|
1571
|
+
const pluginsDir = join10(opencodeDir, "plugins");
|
|
1572
|
+
for (const f of ["controller-core.ts", "security.ts", "tiered.ts"]) {
|
|
1573
|
+
try {
|
|
1574
|
+
const p = join10(pluginsDir, f);
|
|
1575
|
+
if (existsSync8(p) && !readFileSync6(p, "utf-8").includes("export default")) {
|
|
1576
|
+
lines.push(`⚠️ plugins/${f} suelto sin default — el server lo carga como plugin y falla; mové a plugins/ostacky-controller/ o re-corré install (doctor no lo modifica)`);
|
|
1577
|
+
}
|
|
1578
|
+
} catch {}
|
|
1579
|
+
}
|
|
1580
|
+
try {
|
|
1581
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
1582
|
+
const globalPlugins = home ? join10(home, ".config", "opencode", "plugins") : "";
|
|
1583
|
+
if (globalPlugins) {
|
|
1584
|
+
let entries = [];
|
|
1585
|
+
try {
|
|
1586
|
+
entries = readdirSync3(globalPlugins).filter((e) => e.endsWith(".ts"));
|
|
1587
|
+
} catch {}
|
|
1588
|
+
for (const e of entries) {
|
|
1589
|
+
try {
|
|
1590
|
+
const src = readFileSync6(join10(globalPlugins, e), "utf-8");
|
|
1591
|
+
if (src.includes("@opencode-ai/") || !src.includes("export default")) {
|
|
1592
|
+
lines.push(`⚠️ global ${e} con formato V1 (@opencode-ai/* o sin default) — no corre en server V2; actualizalo o borralo a mano (doctor no lo modifica)`);
|
|
1593
|
+
}
|
|
1594
|
+
} catch {}
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
} catch {}
|
|
1545
1598
|
const configPath = findOpenCodeConfig(cwd);
|
|
1546
1599
|
if (!configPath) {
|
|
1547
1600
|
lines.push("ℹ️ config: no hay opencode.json ni opencode.jsonc");
|
|
@@ -1621,7 +1674,7 @@ var init_doctor_v2 = __esm(() => {
|
|
|
1621
1674
|
// package.json
|
|
1622
1675
|
var package_default = {
|
|
1623
1676
|
name: "ostacky",
|
|
1624
|
-
version: "0.9.
|
|
1677
|
+
version: "0.9.2",
|
|
1625
1678
|
description: "Instalador interactivo de agentes y comandos para OpenCode",
|
|
1626
1679
|
type: "module",
|
|
1627
1680
|
bin: {
|
|
@@ -1633,13 +1686,7 @@ var package_default = {
|
|
|
1633
1686
|
build: "bun build src/cli.ts --target=node --format=esm --outfile dist/cli.js && bun scripts/add-shebang.ts",
|
|
1634
1687
|
start: "node dist/cli.js",
|
|
1635
1688
|
test: "bun test",
|
|
1636
|
-
|
|
1637
|
-
"check:skills:fix": "bun run scripts/check-skill-sync.ts --fix",
|
|
1638
|
-
"hash:check": "bun run scripts/update-manifest-hashes.ts --check",
|
|
1639
|
-
"hash:update": "bun run scripts/update-manifest-hashes.ts",
|
|
1640
|
-
"manifest:prepare": "bun run scripts/update-manifest-hashes.ts",
|
|
1641
|
-
"manifest:check": "bun run scripts/update-manifest-hashes.ts --check",
|
|
1642
|
-
prepublishOnly: "bun run scripts/sync-controller-core.ts && bun run manifest:prepare && bun run build"
|
|
1689
|
+
update: "bun run scripts/check-skill-sync.ts && bun run scripts/sync-controller-core.ts && bun run scripts/update-manifest-hashes.ts"
|
|
1643
1690
|
},
|
|
1644
1691
|
files: [
|
|
1645
1692
|
"dist/",
|
|
@@ -1655,7 +1702,7 @@ var package_default = {
|
|
|
1655
1702
|
],
|
|
1656
1703
|
license: "MIT",
|
|
1657
1704
|
dependencies: {
|
|
1658
|
-
"@clack/prompts": "^0.9.
|
|
1705
|
+
"@clack/prompts": "^0.9.2"
|
|
1659
1706
|
},
|
|
1660
1707
|
devDependencies: {
|
|
1661
1708
|
"@types/bun": "latest",
|
|
@@ -2800,7 +2847,7 @@ async function probeMcpServer(nodeExecutable, serverPath, cwd, statePath, option
|
|
|
2800
2847
|
params: {
|
|
2801
2848
|
protocolVersion: "2025-03-26",
|
|
2802
2849
|
capabilities: {},
|
|
2803
|
-
clientInfo: { name: "ostacky-installer", version: "0.9.
|
|
2850
|
+
clientInfo: { name: "ostacky-installer", version: "0.9.2" }
|
|
2804
2851
|
}
|
|
2805
2852
|
});
|
|
2806
2853
|
});
|
|
@@ -2868,6 +2915,20 @@ function upsertLockfile(paths, type, item, manifest, contentHash) {
|
|
|
2868
2915
|
existing.lockedAt = new Date().toISOString();
|
|
2869
2916
|
writeLockfile(paths.root, existing);
|
|
2870
2917
|
}
|
|
2918
|
+
function assertBundledTreeHash(category, name, actualHash) {
|
|
2919
|
+
const expected = getBundledExpectedHash(category, name);
|
|
2920
|
+
if (expected && actualHash !== expected) {
|
|
2921
|
+
const labels = {
|
|
2922
|
+
skills: "skill",
|
|
2923
|
+
mcpServers: "MCP server",
|
|
2924
|
+
agents: "agente",
|
|
2925
|
+
commands: "command"
|
|
2926
|
+
};
|
|
2927
|
+
throw new Error(`Tree hash inválido para ${labels[category] ?? category} "${name}"
|
|
2928
|
+
esperado: ${expected}
|
|
2929
|
+
recibido: ${actualHash}`);
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2871
2932
|
function readBundledAsset(relativePath) {
|
|
2872
2933
|
const fullPath = join6(PACKAGE_ROOT, relativePath);
|
|
2873
2934
|
if (!existsSync5(fullPath))
|
|
@@ -2908,11 +2969,7 @@ async function installSkill(item, manifest, paths) {
|
|
|
2908
2969
|
throw new Error(`Skill bundleada no encontrada: ${src}. ¿Falta el directorio assets/skills/${item.name}/?`);
|
|
2909
2970
|
}
|
|
2910
2971
|
const treeHash = computeTreeHash(src);
|
|
2911
|
-
|
|
2912
|
-
throw new Error(`Tree hash inválido para skill "${item.name}"
|
|
2913
|
-
esperado: ${item.sha256}
|
|
2914
|
-
recibido: ${treeHash}`);
|
|
2915
|
-
}
|
|
2972
|
+
assertBundledTreeHash("skills", item.name, treeHash);
|
|
2916
2973
|
const dest = join6(paths.skills, item.name);
|
|
2917
2974
|
if (existsSync5(dest)) {
|
|
2918
2975
|
rmSync3(dest, { recursive: true, force: true });
|
|
@@ -2949,11 +3006,7 @@ async function installMcpServer(item, manifest, paths) {
|
|
|
2949
3006
|
throw new Error(`MCP server bundleado no encontrado: ${src}. ` + `¿Falta el directorio assets/mcp/${item.name}/?`);
|
|
2950
3007
|
}
|
|
2951
3008
|
const treeHash = computeTreeHash(src);
|
|
2952
|
-
|
|
2953
|
-
throw new Error(`Tree hash inválido para MCP server "${item.name}"
|
|
2954
|
-
esperado: ${item.sha256}
|
|
2955
|
-
recibido: ${treeHash}`);
|
|
2956
|
-
}
|
|
3009
|
+
assertBundledTreeHash("mcpServers", item.name, treeHash);
|
|
2957
3010
|
const nodeExecutable = getVerifiedNodeExecutable();
|
|
2958
3011
|
const projectRoot = dirname4(paths.root);
|
|
2959
3012
|
const dest = join6(paths.mcp, item.name);
|
|
@@ -3135,6 +3188,12 @@ function uninstallAll(paths) {
|
|
|
3135
3188
|
} catch {}
|
|
3136
3189
|
}
|
|
3137
3190
|
}
|
|
3191
|
+
const packageDir = join6(paths.plugins, "ostacky-controller");
|
|
3192
|
+
if (existsSync5(packageDir)) {
|
|
3193
|
+
try {
|
|
3194
|
+
rmSync3(packageDir, { recursive: true, force: true });
|
|
3195
|
+
} catch {}
|
|
3196
|
+
}
|
|
3138
3197
|
clearLockfile(paths.root);
|
|
3139
3198
|
}
|
|
3140
3199
|
|
|
@@ -3168,7 +3227,7 @@ function printPostInstallSteps() {
|
|
|
3168
3227
|
"Cerrá y reiniciá OpenCode si ya estaba corriendo (los MCP no recargan su config en caliente):",
|
|
3169
3228
|
" TUI → opencode",
|
|
3170
3229
|
" Web → opencode web --port 4096",
|
|
3171
|
-
"
|
|
3230
|
+
"Seleccioná el agente: `/agents` → `ostacky` (o Shift+Tab / Ctrl+X A)",
|
|
3172
3231
|
"Skills bundleadas en .opencode/skills/ — revisá cuáles activás",
|
|
3173
3232
|
"¿Errores en el stack? Ejecutá /install-stack desde el chat de OpenCode"
|
|
3174
3233
|
].join(`
|
|
@@ -3367,13 +3426,17 @@ async function doInstallAll(manifest, paths) {
|
|
|
3367
3426
|
const { join } = await import("path");
|
|
3368
3427
|
await Promise.resolve().then(() => init_github());
|
|
3369
3428
|
const { findProjectRoot } = await Promise.resolve().then(() => (init_fs(), {}));
|
|
3370
|
-
const
|
|
3371
|
-
const
|
|
3372
|
-
if (existsSync(
|
|
3373
|
-
mkdirSync(
|
|
3374
|
-
|
|
3429
|
+
const packageSrc = join(PACKAGE_ROOT, "assets", "plugins", "ostacky-controller");
|
|
3430
|
+
const packageDest = join(paths.plugins, "ostacky-controller");
|
|
3431
|
+
if (existsSync(join(packageSrc, "index.ts"))) {
|
|
3432
|
+
mkdirSync(packageDest, { recursive: true });
|
|
3433
|
+
for (const f of ["index.ts", "controller-core.ts", "security.ts", "tiered.ts"]) {
|
|
3434
|
+
const s = join(packageSrc, f);
|
|
3435
|
+
if (existsSync(s))
|
|
3436
|
+
copyFileSync(s, join(packageDest, f));
|
|
3437
|
+
}
|
|
3375
3438
|
}
|
|
3376
|
-
for (const legacy of ["ostacky-guard.ts", "ostacky-controller.ts"]) {
|
|
3439
|
+
for (const legacy of ["ostacky-plugin.ts", "controller-core.ts", "security.ts", "tiered.ts", "ostacky-guard.ts", "ostacky-controller.ts"]) {
|
|
3377
3440
|
const lp = join(paths.plugins, legacy);
|
|
3378
3441
|
if (existsSync(lp))
|
|
3379
3442
|
try {
|
|
@@ -4402,7 +4465,7 @@ async function runUninstallMcpCommand(name, scope) {
|
|
|
4402
4465
|
|
|
4403
4466
|
// src/cli.ts
|
|
4404
4467
|
init_fs();
|
|
4405
|
-
import { existsSync as existsSync9, statSync as statSync3, readFileSync as readFileSync7, readdirSync as
|
|
4468
|
+
import { existsSync as existsSync9, statSync as statSync3, readFileSync as readFileSync7, readdirSync as readdirSync4 } from "node:fs";
|
|
4406
4469
|
import { join as join11, dirname as dirname8 } from "node:path";
|
|
4407
4470
|
var HELP = `
|
|
4408
4471
|
ostacky — Instalador de agentes, comandos, skills y MCPs para OpenCode
|
|
@@ -4490,6 +4553,9 @@ async function runDoctorCommand() {
|
|
|
4490
4553
|
}
|
|
4491
4554
|
};
|
|
4492
4555
|
const pluginPaths = [
|
|
4556
|
+
join11(cwd, "assets", "plugins", "ostacky-controller", "index.ts"),
|
|
4557
|
+
join11(opencodeDir, "plugins", "ostacky-controller", "index.ts"),
|
|
4558
|
+
join11(cwd, ".opencode", "plugins", "ostacky-controller", "index.ts"),
|
|
4493
4559
|
join11(cwd, "assets", "plugins", "ostacky-plugin.ts"),
|
|
4494
4560
|
join11(opencodeDir, "plugins", "ostacky-plugin.ts"),
|
|
4495
4561
|
join11(cwd, ".opencode", "plugins", "ostacky-plugin.ts"),
|
|
@@ -4633,7 +4699,7 @@ async function runDoctorCommand() {
|
|
|
4633
4699
|
if (!existsSync9(cacheDir)) {
|
|
4634
4700
|
console.log("ℹ️ cache: no cache dir yet (ok)");
|
|
4635
4701
|
} else {
|
|
4636
|
-
const files =
|
|
4702
|
+
const files = readdirSync4(cacheDir);
|
|
4637
4703
|
let total = 0;
|
|
4638
4704
|
for (const f of files) {
|
|
4639
4705
|
try {
|
|
@@ -4697,7 +4763,7 @@ async function runDoctorCommand() {
|
|
|
4697
4763
|
check("honesty: 7 SHALL", hasHonesty);
|
|
4698
4764
|
if (!hasHonesty)
|
|
4699
4765
|
console.log(" Expected: ## Principios de honestidad (SHALL) in ostacky.md");
|
|
4700
|
-
const pluginPath = join11(cwd, "assets", "plugins", "ostacky-
|
|
4766
|
+
const pluginPath = join11(cwd, "assets", "plugins", "ostacky-controller", "index.ts");
|
|
4701
4767
|
if (existsSync9(pluginPath)) {
|
|
4702
4768
|
const plugin = readFileSync7(pluginPath, "utf-8");
|
|
4703
4769
|
const hasSpecGuard = plugin.includes("getDiscoverySnapshot") && plugin.includes("specSnapshot");
|
package/manifest.json
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.9.
|
|
2
|
+
"version": "0.9.2",
|
|
3
3
|
"repo": "JaimeHoracio/Ostacky",
|
|
4
|
-
"tag": "v0.9.
|
|
4
|
+
"tag": "v0.9.2",
|
|
5
5
|
"agents": [
|
|
6
6
|
{
|
|
7
7
|
"name": "ostacky",
|
|
8
8
|
"file": "assets/agents/ostacky.md",
|
|
9
|
-
"description": "Orquestador con recuperación automática (nunca se congela), ruteo por nivel de impacto, controller MCP con SDK oficial, edición segura con fallback inline, y delegación en OpenSpec + Superpowers. v0.9.
|
|
10
|
-
"version": "0.9.
|
|
11
|
-
"sha256": "
|
|
9
|
+
"description": "Orquestador con recuperación automática (nunca se congela), ruteo por nivel de impacto, controller MCP con SDK oficial, edición segura con fallback inline, y delegación en OpenSpec + Superpowers. v0.9.2: prune de skills obsoletas, lastHandoff, getAvailableTransitions, consecutiveFailures real, mem_session_summary automático al cierre.",
|
|
10
|
+
"version": "0.9.2",
|
|
11
|
+
"sha256": "f588cd07f67f73393f23601b025d1f5980a7db4efe194f248ff4d09cc98b04d4"
|
|
12
12
|
}
|
|
13
13
|
],
|
|
14
14
|
"commands": [
|
|
15
15
|
{
|
|
16
16
|
"name": "install-stack",
|
|
17
17
|
"file": "assets/commands/install-stack.md",
|
|
18
|
-
"description": "Instala el stack tecnológico del proyecto (CodeGraph, skills, OpenSpec, Engram, controller plugin). v0.9.
|
|
19
|
-
"version": "0.9.
|
|
20
|
-
"sha256": "
|
|
18
|
+
"description": "Instala el stack tecnológico del proyecto (CodeGraph, skills, OpenSpec, Engram, controller plugin). v0.9.2: controller plugin como alma hard-gate, Context7 removido del stack.",
|
|
19
|
+
"version": "0.9.2",
|
|
20
|
+
"sha256": "288e2fdd0df24378b92c15a169b846bd9cbc3290dfaf09597293e6f5240f562e"
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
23
|
"name": "opsx-sync",
|
|
24
24
|
"file": "assets/commands/opsx-sync.md",
|
|
25
25
|
"description": "Sincroniza delta specs del change activo sin inicializar CodeGraph si ya existe índice",
|
|
26
|
-
"version": "0.9.
|
|
26
|
+
"version": "0.9.2",
|
|
27
27
|
"sha256": "fe0158478f2ca63b315037a85fc1632b77868532e319af6c7384285441767d64"
|
|
28
28
|
}
|
|
29
29
|
],
|
|
@@ -31,15 +31,15 @@
|
|
|
31
31
|
{
|
|
32
32
|
"name": "ostacky-controller",
|
|
33
33
|
"file": "assets/mcp/ostacky-controller/",
|
|
34
|
-
"description": "Máquina de estados persistida con @modelcontextprotocol/server SDK. v0.9.
|
|
35
|
-
"version": "0.9.
|
|
36
|
-
"sha256": "
|
|
34
|
+
"description": "Máquina de estados persistida con @modelcontextprotocol/server SDK. v0.9.2: 22 tools (incluye set_handoff, get_handoff, clear_handoff, get_available_transitions funcional). Bugfixes: consecutiveFailures real, lastHandoff state, defaultChoice persistido. Robustez: degraded mode automático tras 3 fallos, persistence condicional para Nivel 0, prune de skills obsoletas.",
|
|
35
|
+
"version": "0.9.2",
|
|
36
|
+
"sha256": "1fa9251efbf98552a8eb5f88a2f741021b82b21bc37f96d5253849f9209c949a"
|
|
37
37
|
},
|
|
38
38
|
{
|
|
39
39
|
"name": "openspec",
|
|
40
40
|
"file": "assets/mcp/openspec/",
|
|
41
41
|
"description": "MCP server local para OpenSpec - proposal, apply, archive, sync de cambios",
|
|
42
|
-
"version": "0.9.
|
|
42
|
+
"version": "0.9.2",
|
|
43
43
|
"sha256": "fa4be28bfc1e75db8f1a037e2580f04a60066c80e8e5934e90e1041020d04609"
|
|
44
44
|
}
|
|
45
45
|
],
|
|
@@ -48,105 +48,105 @@
|
|
|
48
48
|
"name": "brainstorming",
|
|
49
49
|
"file": "assets/skills/brainstorming/SKILL.md",
|
|
50
50
|
"description": "Skill unificado de pensamiento con dos modos: creative-design (producción de diseño → transición a implementación directa o openspec-propose) y open-exploration (exploración libre)",
|
|
51
|
-
"version": "0.9.
|
|
51
|
+
"version": "0.9.2",
|
|
52
52
|
"sha256": "f1013a86a8f27914d85a716e23c7fd32db1786d93615e8b0834b13d184308549"
|
|
53
53
|
},
|
|
54
54
|
{
|
|
55
55
|
"name": "execution-mode-evaluation",
|
|
56
56
|
"file": "assets/skills/execution-mode-evaluation/SKILL.md",
|
|
57
57
|
"description": "Skill de análisis de modo de ejecución — output reconciliado con controller snapshot contract (recommendation field)",
|
|
58
|
-
"version": "0.9.
|
|
58
|
+
"version": "0.9.2",
|
|
59
59
|
"sha256": "2158a6864c0912e868a9d720db7fd79441e6cc298e3320833d2527c9727c852e"
|
|
60
60
|
},
|
|
61
61
|
{
|
|
62
62
|
"name": "tdd",
|
|
63
63
|
"file": "assets/skills/tdd/SKILL.md",
|
|
64
64
|
"description": "Skill de test-driven development (Superpowers)",
|
|
65
|
-
"version": "0.9.
|
|
65
|
+
"version": "0.9.2",
|
|
66
66
|
"sha256": "f0ebfbbc43a8868d31a3fd2df37ae7f60fe8add09c6475862395a68269e5ff7f"
|
|
67
67
|
},
|
|
68
68
|
{
|
|
69
69
|
"name": "subagent-driven-development",
|
|
70
70
|
"file": "assets/skills/subagent-driven-development/SKILL.md",
|
|
71
71
|
"description": "Skill de ejecución con subagentes (Superpowers) — ejecuta solo después de confirmación del coordinador Ostacky",
|
|
72
|
-
"version": "0.9.
|
|
72
|
+
"version": "0.9.2",
|
|
73
73
|
"sha256": "1a42d714a9a13faf05f0bf7b580e8d837c2e77a5633839542ccce603023419e8"
|
|
74
74
|
},
|
|
75
75
|
{
|
|
76
76
|
"name": "dispatching-parallel-agents",
|
|
77
77
|
"file": "assets/skills/dispatching-parallel-agents/SKILL.md",
|
|
78
78
|
"description": "Skill de dispatch paralelo de agentes (Superpowers)",
|
|
79
|
-
"version": "0.9.
|
|
79
|
+
"version": "0.9.2",
|
|
80
80
|
"sha256": "7e7b712ef742d5631e302e6a71583e2bbbd33094da9281abfb84b43505fc3ef1"
|
|
81
81
|
},
|
|
82
82
|
{
|
|
83
83
|
"name": "review",
|
|
84
84
|
"file": "assets/skills/review/SKILL.md",
|
|
85
85
|
"description": "Skill de revisión de código (Superpowers)",
|
|
86
|
-
"version": "0.9.
|
|
86
|
+
"version": "0.9.2",
|
|
87
87
|
"sha256": "b19650ed4d1d4d9857a4dd5b7e08e91328a1a1fc9c45bcc2fb600fa9d0213279"
|
|
88
88
|
},
|
|
89
89
|
{
|
|
90
90
|
"name": "receiving-code-review",
|
|
91
91
|
"file": "assets/skills/receiving-code-review/SKILL.md",
|
|
92
92
|
"description": "Skill de recibir y procesar feedback de code review",
|
|
93
|
-
"version": "0.9.
|
|
93
|
+
"version": "0.9.2",
|
|
94
94
|
"sha256": "d761e884e71d8d3476ac734d287cae403a4024506a7d12e7361a448217c0a831"
|
|
95
95
|
},
|
|
96
96
|
{
|
|
97
97
|
"name": "openspec-propose",
|
|
98
98
|
"file": "assets/skills/openspec-propose/SKILL.md",
|
|
99
99
|
"description": "Skill de generación de proposal (OpenSpec)",
|
|
100
|
-
"version": "0.9.
|
|
100
|
+
"version": "0.9.2",
|
|
101
101
|
"sha256": "6a517f750f24c008208156a2d653e97ed30b1e53878aeac5ce394e573db0678f"
|
|
102
102
|
},
|
|
103
103
|
{
|
|
104
104
|
"name": "openspec-apply-change",
|
|
105
105
|
"file": "assets/skills/openspec-apply-change/SKILL.md",
|
|
106
106
|
"description": "Skill de aplicación de change (OpenSpec)",
|
|
107
|
-
"version": "0.9.
|
|
107
|
+
"version": "0.9.2",
|
|
108
108
|
"sha256": "dfc823bf89fc7505e91ab6dee9c1f004be38a410bd3d88fb67b211b1b6cbb1d0"
|
|
109
109
|
},
|
|
110
110
|
{
|
|
111
111
|
"name": "openspec-archive-change",
|
|
112
112
|
"file": "assets/skills/openspec-archive-change/SKILL.md",
|
|
113
113
|
"description": "Skill de archivo de change (OpenSpec)",
|
|
114
|
-
"version": "0.9.
|
|
114
|
+
"version": "0.9.2",
|
|
115
115
|
"sha256": "16e4b561de7747283663fed602e506abf502452c49a8d4b86d7a5539c40f0195"
|
|
116
116
|
},
|
|
117
117
|
{
|
|
118
118
|
"name": "openspec-explore",
|
|
119
119
|
"file": "assets/skills/openspec-explore/SKILL.md",
|
|
120
120
|
"description": "Modo explore para OpenSpec — thinking partner para explorar ideas, investigar problemas y clarificar requisitos antes/durante un cambio",
|
|
121
|
-
"version": "0.9.
|
|
121
|
+
"version": "0.9.2",
|
|
122
122
|
"sha256": "9eafc3a8c693b8d9916867ef764d49e5222da717fde501c957bb3f6c312d2415"
|
|
123
123
|
},
|
|
124
124
|
{
|
|
125
125
|
"name": "using-git-worktrees",
|
|
126
126
|
"file": "assets/skills/using-git-worktrees/SKILL.md",
|
|
127
127
|
"description": "Skill de uso de git worktrees para aislamiento de trabajo",
|
|
128
|
-
"version": "0.9.
|
|
128
|
+
"version": "0.9.2",
|
|
129
129
|
"sha256": "93341bc1b7c053618a8b6dc07e3615b77990d7b549a0201f5835d95fef67ce13"
|
|
130
130
|
},
|
|
131
131
|
{
|
|
132
132
|
"name": "using-superpowers",
|
|
133
133
|
"file": "assets/skills/using-superpowers/SKILL.md",
|
|
134
134
|
"description": "Skill de orquestación de Superpowers skills",
|
|
135
|
-
"version": "0.9.
|
|
135
|
+
"version": "0.9.2",
|
|
136
136
|
"sha256": "7e54536f96d2a379185a10bfc1e970850caa2561b6d8aca7080f0defea0381a4"
|
|
137
137
|
},
|
|
138
138
|
{
|
|
139
139
|
"name": "writing-skills",
|
|
140
140
|
"file": "assets/skills/writing-skills/SKILL.md",
|
|
141
141
|
"description": "Skill de creación y edición de skills",
|
|
142
|
-
"version": "0.9.
|
|
142
|
+
"version": "0.9.2",
|
|
143
143
|
"sha256": "3d76b906cee518a2b809febb70db95697a35b9f368986bb504b4120c3bfb437a"
|
|
144
144
|
},
|
|
145
145
|
{
|
|
146
146
|
"name": "graceful-degradation",
|
|
147
147
|
"file": "assets/skills/graceful-degradation/SKILL.md",
|
|
148
148
|
"description": "Skill de degradación graceful cuando múltiples tools están indisponibles",
|
|
149
|
-
"version": "0.9.
|
|
149
|
+
"version": "0.9.2",
|
|
150
150
|
"sha256": "363449bd9e6f800a0a3168667cf78f767fb105d86e4a4adadcc649d418139303"
|
|
151
151
|
}
|
|
152
152
|
]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ostacky",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.2",
|
|
4
4
|
"description": "Instalador interactivo de agentes y comandos para OpenCode",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -12,13 +12,7 @@
|
|
|
12
12
|
"build": "bun build src/cli.ts --target=node --format=esm --outfile dist/cli.js && bun scripts/add-shebang.ts",
|
|
13
13
|
"start": "node dist/cli.js",
|
|
14
14
|
"test": "bun test",
|
|
15
|
-
"
|
|
16
|
-
"check:skills:fix": "bun run scripts/check-skill-sync.ts --fix",
|
|
17
|
-
"hash:check": "bun run scripts/update-manifest-hashes.ts --check",
|
|
18
|
-
"hash:update": "bun run scripts/update-manifest-hashes.ts",
|
|
19
|
-
"manifest:prepare": "bun run scripts/update-manifest-hashes.ts",
|
|
20
|
-
"manifest:check": "bun run scripts/update-manifest-hashes.ts --check",
|
|
21
|
-
"prepublishOnly": "bun run scripts/sync-controller-core.ts && bun run manifest:prepare && bun run build"
|
|
15
|
+
"update": "bun run scripts/check-skill-sync.ts && bun run scripts/sync-controller-core.ts && bun run scripts/update-manifest-hashes.ts"
|
|
22
16
|
},
|
|
23
17
|
"files": [
|
|
24
18
|
"dist/",
|
|
@@ -34,7 +28,7 @@
|
|
|
34
28
|
],
|
|
35
29
|
"license": "MIT",
|
|
36
30
|
"dependencies": {
|
|
37
|
-
"@clack/prompts": "^0.9.
|
|
31
|
+
"@clack/prompts": "^0.9.2"
|
|
38
32
|
},
|
|
39
33
|
"devDependencies": {
|
|
40
34
|
"@types/bun": "latest",
|
|
File without changes
|
|
File without changes
|