ostacky 0.8.5 → 0.8.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -12
- package/assets/agents/ostacky.md +11 -6
- package/assets/commands/install-stack.md +2 -2
- package/assets/mcp/ostacky-controller/index.js +224 -21
- package/assets/mcp/ostacky-controller/package.json +1 -1
- package/assets/plugins/engram.ts +31 -0
- package/assets/plugins/ostacky-plugin.ts +57 -1
- package/assets/skills/execution-mode-evaluation/SKILL.md +2 -0
- package/assets/skills/graceful-degradation/SKILL.md +15 -0
- package/dist/cli.js +32 -32
- package/manifest.json +30 -30
- package/package.json +1 -1
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.8.
|
|
249
|
+
"version": "0.8.7",
|
|
250
250
|
"lockedAt": "2025-01-01T00:00:00.000Z",
|
|
251
251
|
"repo": "JaimeHoracio/Ostacky",
|
|
252
|
-
"tag": "v0.8.
|
|
252
|
+
"tag": "v0.8.7",
|
|
253
253
|
"agents": {
|
|
254
254
|
"ostacky": {
|
|
255
|
-
"version": "0.8.
|
|
255
|
+
"version": "0.8.7",
|
|
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.8.
|
|
262
|
+
"version": "0.8.7",
|
|
263
263
|
"installedAt": "2025-01-01T00:00:00.000Z",
|
|
264
264
|
"sha256": "def456..."
|
|
265
265
|
},
|
|
266
266
|
"opsx-sync": {
|
|
267
|
-
"version": "0.8.
|
|
267
|
+
"version": "0.8.7",
|
|
268
268
|
"installedAt": "2025-01-01T00:00:00.000Z",
|
|
269
269
|
"sha256": "ghi789..."
|
|
270
270
|
}
|
|
271
271
|
},
|
|
272
272
|
"skills": {
|
|
273
|
-
"brainstorming": { "version": "0.8.
|
|
274
|
-
"execution-mode-evaluation": { "version": "0.8.
|
|
275
|
-
"openspec-propose": { "version": "0.8.
|
|
273
|
+
"brainstorming": { "version": "0.8.7", ... },
|
|
274
|
+
"execution-mode-evaluation": { "version": "0.8.7", ... },
|
|
275
|
+
"openspec-propose": { "version": "0.8.7", ... }
|
|
276
276
|
},
|
|
277
277
|
"mcpServers": {
|
|
278
|
-
"ostacky-controller": { "version": "0.8.
|
|
279
|
-
"openspec": { "version": "0.8.
|
|
278
|
+
"ostacky-controller": { "version": "0.8.7", ... },
|
|
279
|
+
"openspec": { "version": "0.8.7", ... }
|
|
280
280
|
}
|
|
281
281
|
}
|
|
282
282
|
```
|
|
@@ -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.8.
|
|
311
|
+
- Las URLs de descarga usan **tags de GitHub** (ej. `v0.8.7`), 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.8.
|
|
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.7 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.8.
|
|
4
|
+
version: 0.8.7
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Sos **Ostacky v0.8.
|
|
7
|
+
Sos **Ostacky v0.8.7**, 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.
|
|
9
|
+
> **Versión:** `0.8.7` (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.7"** (o `v0.8.7` si te piden solo el número). No inventes otra versión.
|
|
10
10
|
|
|
11
11
|
## Reglas innegociables
|
|
12
12
|
|
|
@@ -18,16 +18,20 @@ Sos **Ostacky v0.8.5**, 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)
|
|
21
|
+
## Principios de honestidad (SHALL) — concretos, no genéricos
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
> **Regla de oro:** Nunca escribas instrucciones genéricas tipo `“sé cuidadoso / no introduzcas errores / no inventes”`. Son no-verificables y el LLM las ignora (instruction fatigue). Cada SHALL abajo es **verificable** (comando, evidencia o artefacto). `tasks.md` también SHALL ser verificable — no behavioral.
|
|
24
|
+
|
|
25
|
+
1. **Engram contradiction check SHALL.** Antes de proponer o discutir cambios (Nivel 0+1/1+, **eximido si `isTrivial && DONE`** — ej: "cambiar título" no paga `mem_search`), 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
26
|
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
27
|
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
28
|
4. **Si no hay propuesta honesta SHALL decirlo.** Literal: `“No hay propuesta honesta que aporte vs no hacer nada en este contexto.”`
|
|
27
29
|
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”`.
|
|
30
|
+
6. **Verificar antes de afirmar SHALL.** No acordar sin chequear código/docs. Citar evidencia o admitir `“no pude verificar en disco”`. Equivale a `Read` fresco o `codegraph_explore` antes de `edit`.
|
|
29
31
|
7. **YAGNI scope SHALL.** No proponer refactor oportunista fuera del pedido; si lo ves, mencionar `“fuera de scope: …”` sin implementarlo.
|
|
30
32
|
|
|
33
|
+
**Anti-genérico:** En vez de `“no introduzcas errores”` → SHALL `Read fresco → validate_edit → edit → read verificación → complete_task → verifyIntegrity → bun test` (ver §4 Execution). En vez de `“advierte riesgos”` → SHALL `Riesgo concreto → Mitigación` en `design.md` solo si `level 1+` y riesgo es verificable; si `isTrivial` no inventes riesgos.
|
|
34
|
+
|
|
31
35
|
## Stack
|
|
32
36
|
|
|
33
37
|
- **Controller** (plugin `ostacky-plugin.ts`): state machine in-process, hard gates.
|
|
@@ -95,3 +99,4 @@ Router `brainstorming`↔`OpenSpec` por `level`/`estLines`/`fileCount`/`hasAPI`
|
|
|
95
99
|
- Una pregunta por turno, sin deadlock
|
|
96
100
|
- Fase gate: en EXECUTING/SYNC no volver a DISCOVERY
|
|
97
101
|
- Audit: log antes de gate + `mem_save` solo en gates
|
|
102
|
+
- **Tasks son verificables, no instruccionales:** `tasks.md` SHALL describir `qué` + `cómo verificar` (`bun test`, `grep`, archivo entregado). Nunca SHALL contener instrucciones de comportamiento genéricas (`“sé cuidadoso”, “no inventes”`) — eso vive acá en § Principios y se inyecta automáticamente por tier (ver § Core tiered). Para `Level 0` trivial, tasks SHALL ser de 1 línea sin overhead de honestidad.
|
|
@@ -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.8.
|
|
8
|
+
**Nota:** A partir de v0.8.7, `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.8.
|
|
14
|
+
## Scope de instalación — local vs global (desde v0.8.7)
|
|
15
15
|
|
|
16
16
|
`npx ostacky install` soporta `--scope local|global|auto` (también `--scope=...`):
|
|
17
17
|
|
|
@@ -137,6 +137,45 @@ function isPathInsideProject(filePath, statePath) {
|
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
export function parseTasksMd(changeId, statePath) {
|
|
141
|
+
if (!changeId || typeof changeId !== 'string') return [];
|
|
142
|
+
try {
|
|
143
|
+
const projectRoot = getProjectRoot(statePath);
|
|
144
|
+
const tasksPath = join(projectRoot, 'openspec', 'changes', changeId, 'tasks.md');
|
|
145
|
+
let content = null;
|
|
146
|
+
if (existsSync(tasksPath)) {
|
|
147
|
+
content = readFileSync(tasksPath, 'utf-8');
|
|
148
|
+
} else {
|
|
149
|
+
// fallback to archive: YYYY-MM-DD-<changeId>
|
|
150
|
+
try {
|
|
151
|
+
const archiveDir = join(projectRoot, 'openspec', 'changes', 'archive');
|
|
152
|
+
if (existsSync(archiveDir)) {
|
|
153
|
+
for (const entry of readdirSync(archiveDir)) {
|
|
154
|
+
if (entry.endsWith(`-${changeId}`) || entry === changeId) {
|
|
155
|
+
const alt = join(archiveDir, entry, 'tasks.md');
|
|
156
|
+
if (existsSync(alt)) {
|
|
157
|
+
content = readFileSync(alt, 'utf-8');
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
} catch {}
|
|
164
|
+
if (content === null) return [];
|
|
165
|
+
}
|
|
166
|
+
const re = /^- \[[ x]\]\s+([A-Za-z0-9][A-Za-z0-9\-_.\/:]*)/gm;
|
|
167
|
+
const ids = [];
|
|
168
|
+
let m;
|
|
169
|
+
while ((m = re.exec(content)) !== null) {
|
|
170
|
+
const id = m[1];
|
|
171
|
+
if (isValidTaskId(id)) ids.push(id);
|
|
172
|
+
}
|
|
173
|
+
return ids;
|
|
174
|
+
} catch {
|
|
175
|
+
return [];
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
140
179
|
function isValidTaskId(taskId) {
|
|
141
180
|
return typeof taskId === 'string' && /^[a-zA-Z0-9-_.\/:]+$/.test(taskId);
|
|
142
181
|
}
|
|
@@ -1309,32 +1348,115 @@ class OstackyController {
|
|
|
1309
1348
|
'record_execution_analysis'
|
|
1310
1349
|
);
|
|
1311
1350
|
}
|
|
1312
|
-
// C2: strict contract — recommendation + reasons required
|
|
1351
|
+
// C2: strict contract — recommendation + reasons required (tiered recovery: fix-execution-analysis-validation)
|
|
1352
|
+
// Normalizar aliases para no bloquear snapshot válido enviado con nombre distinto
|
|
1353
|
+
if (snapshot && typeof snapshot === 'string') {
|
|
1354
|
+
try {
|
|
1355
|
+
snapshot = JSON.parse(snapshot);
|
|
1356
|
+
} catch {}
|
|
1357
|
+
}
|
|
1358
|
+
if (snapshot && !snapshot.recommendation && (snapshot.mode || snapshot.executionMode)) {
|
|
1359
|
+
snapshot.recommendation = snapshot.mode || snapshot.executionMode;
|
|
1360
|
+
}
|
|
1361
|
+
if (snapshot && !snapshot.reasons && snapshot.reason) {
|
|
1362
|
+
snapshot.reasons = Array.isArray(snapshot.reason) ? snapshot.reason : [String(snapshot.reason)];
|
|
1363
|
+
}
|
|
1364
|
+
if (snapshot && Array.isArray(snapshot.reasons) && snapshot.reasons.length === 0) {
|
|
1365
|
+
// vacío se considera missing para no dejar pasar snapshot inválido
|
|
1366
|
+
snapshot.reasons = undefined;
|
|
1367
|
+
}
|
|
1368
|
+
let _snapshotDefaulted = false;
|
|
1313
1369
|
if (snapshot && (!snapshot.recommendation || !snapshot.reasons)) {
|
|
1314
|
-
|
|
1370
|
+
const isDegradedOrEarlyExit =
|
|
1371
|
+
this.#degraded || (typeof snapshot.taskCount === 'number' && snapshot.taskCount <= 2);
|
|
1372
|
+
if (isDegradedOrEarlyExit) {
|
|
1373
|
+
snapshot.recommendation = snapshot.recommendation || 'INLINE';
|
|
1374
|
+
snapshot.reasons = snapshot.reasons || ['defaulted: snapshot incompleto en degraded/early-exit'];
|
|
1375
|
+
_snapshotDefaulted = true;
|
|
1376
|
+
} else {
|
|
1377
|
+
const available = (TRANSITIONS[this.#state.state] || []).map((t) => {
|
|
1378
|
+
let desc = t.via;
|
|
1379
|
+
if (t.choice) desc += ` (choice=${t.choice})`;
|
|
1380
|
+
if (t.mode) desc += ` (mode=${t.mode})`;
|
|
1381
|
+
return desc;
|
|
1382
|
+
});
|
|
1383
|
+
return {
|
|
1384
|
+
error: 'Snapshot missing recommendation/reasons',
|
|
1385
|
+
current_state: this.#state.state,
|
|
1386
|
+
attempted_transition: 'record_execution_analysis',
|
|
1387
|
+
available_transitions: available,
|
|
1388
|
+
retryAllowed: true,
|
|
1389
|
+
suggestion:
|
|
1390
|
+
'Reintentá con {recommendation:"INLINE"|"SUBAGENT_DRIVEN", reasons:[...]} — ver skill execution-mode-evaluation',
|
|
1391
|
+
timestamp: new Date().toISOString(),
|
|
1392
|
+
};
|
|
1393
|
+
}
|
|
1315
1394
|
}
|
|
1316
|
-
// 1.7: exigir expectedTaskIds/taskIds/taskCount cuando taskCount>0
|
|
1395
|
+
// 1.7: exigir expectedTaskIds/taskIds/taskCount cuando taskCount>0 — con retryAllowed para no dejar en BLOCKED
|
|
1317
1396
|
if (snapshot && typeof snapshot.taskCount === 'number' && snapshot.taskCount > 0) {
|
|
1318
1397
|
const hasExpectedIds = Array.isArray(snapshot.expectedTaskIds) && snapshot.expectedTaskIds.length > 0;
|
|
1319
1398
|
const hasTaskIds = Array.isArray(snapshot.taskIds) && snapshot.taskIds.length > 0;
|
|
1320
1399
|
const hasCount = typeof snapshot.taskCount === 'number' && snapshot.taskCount > 0;
|
|
1321
1400
|
if (!hasExpectedIds && !hasTaskIds && !hasCount) {
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1401
|
+
const available = (TRANSITIONS[this.#state.state] || []).map((t) => {
|
|
1402
|
+
let desc = t.via;
|
|
1403
|
+
if (t.choice) desc += ` (choice=${t.choice})`;
|
|
1404
|
+
if (t.mode) desc += ` (mode=${t.mode})`;
|
|
1405
|
+
return desc;
|
|
1406
|
+
});
|
|
1407
|
+
return {
|
|
1408
|
+
error: 'Snapshot missing expectedTaskIds/taskIds/taskCount when taskCount>0',
|
|
1409
|
+
current_state: this.#state.state,
|
|
1410
|
+
attempted_transition: 'record_execution_analysis',
|
|
1411
|
+
available_transitions: available,
|
|
1412
|
+
retryAllowed: true,
|
|
1413
|
+
suggestion:
|
|
1414
|
+
'Reintentá con {taskCount, expectedTaskIds:[...]} que matchee tasks.md — ver skill execution-mode-evaluation',
|
|
1415
|
+
timestamp: new Date().toISOString(),
|
|
1416
|
+
};
|
|
1326
1417
|
}
|
|
1327
1418
|
if (!hasExpectedIds && !hasTaskIds) {
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1419
|
+
const available = (TRANSITIONS[this.#state.state] || []).map((t) => {
|
|
1420
|
+
let desc = t.via;
|
|
1421
|
+
if (t.choice) desc += ` (choice=${t.choice})`;
|
|
1422
|
+
if (t.mode) desc += ` (mode=${t.mode})`;
|
|
1423
|
+
return desc;
|
|
1424
|
+
});
|
|
1425
|
+
return {
|
|
1426
|
+
error: 'Snapshot missing expectedTaskIds or taskIds when taskCount>0',
|
|
1427
|
+
current_state: this.#state.state,
|
|
1428
|
+
attempted_transition: 'record_execution_analysis',
|
|
1429
|
+
available_transitions: available,
|
|
1430
|
+
retryAllowed: true,
|
|
1431
|
+
suggestion:
|
|
1432
|
+
'Reintentá con expectedTaskIds/taskIds que matchee tasks.md — ver skill execution-mode-evaluation',
|
|
1433
|
+
timestamp: new Date().toISOString(),
|
|
1434
|
+
};
|
|
1332
1435
|
}
|
|
1333
1436
|
}
|
|
1334
|
-
// C2: capture expected tasks for gate
|
|
1335
|
-
|
|
1336
|
-
|
|
1437
|
+
// C2: capture expected tasks for gate — harden-task-integrity: tasks.md canonical
|
|
1438
|
+
let expectedTasks = snapshot?.expectedTaskIds || snapshot?.taskIds || null;
|
|
1439
|
+
let expectedTaskCount = snapshot?.taskCount ?? (Array.isArray(expectedTasks) ? expectedTasks.length : null);
|
|
1337
1440
|
const isEarlyExitExec = snapshot?.globalRuleTriggered === 'early-exit' && (snapshot?.taskCount ?? 0) <= 2;
|
|
1441
|
+
// harden-task-integrity 1.1/1.2: tasks.md is source of truth
|
|
1442
|
+
let tasksMdIds = [];
|
|
1443
|
+
let taskCountMismatch = false;
|
|
1444
|
+
try {
|
|
1445
|
+
tasksMdIds = parseTasksMd(this.#state.changeId, this.#statePath);
|
|
1446
|
+
} catch {}
|
|
1447
|
+
if (tasksMdIds.length > 0) {
|
|
1448
|
+
const snapshotIds = Array.isArray(expectedTasks) ? expectedTasks : null;
|
|
1449
|
+
const snapshotSet = snapshotIds ? new Set(snapshotIds) : new Set();
|
|
1450
|
+
const tasksMdSet = new Set(tasksMdIds);
|
|
1451
|
+
const mismatch =
|
|
1452
|
+
!snapshotIds ||
|
|
1453
|
+
snapshotIds.length !== tasksMdIds.length ||
|
|
1454
|
+
[...snapshotSet].some((id) => !tasksMdSet.has(id)) ||
|
|
1455
|
+
[...tasksMdSet].some((id) => !snapshotSet.has(id));
|
|
1456
|
+
if (mismatch) taskCountMismatch = true;
|
|
1457
|
+
expectedTasks = [...tasksMdIds];
|
|
1458
|
+
expectedTaskCount = tasksMdIds.length;
|
|
1459
|
+
}
|
|
1338
1460
|
// 8.1/8.2: lastProposal for execution — reasoning con plan
|
|
1339
1461
|
let execShown = false;
|
|
1340
1462
|
let execFiles = [];
|
|
@@ -1377,6 +1499,38 @@ class OstackyController {
|
|
|
1377
1499
|
lastProposal: execLastProposal,
|
|
1378
1500
|
});
|
|
1379
1501
|
await this.#audit('EXECUTION_DECISION_PENDING', 'record_execution_analysis');
|
|
1502
|
+
// fix-execution-analysis-validation: degraded/early-exit defaulted snapshot → WARN
|
|
1503
|
+
if (_snapshotDefaulted) {
|
|
1504
|
+
const auditId = `aud-${Date.now()}-${this.#state.auditSeq}`;
|
|
1505
|
+
log('warn:snapshot_defaulted', { auditId });
|
|
1506
|
+
await this.#audit(
|
|
1507
|
+
'WARN',
|
|
1508
|
+
'snapshot_defaulted',
|
|
1509
|
+
'snapshot missing recommendation/reasons, defaulted to INLINE'
|
|
1510
|
+
);
|
|
1511
|
+
const lastAudit = this.#state.audit?.[this.#state.audit.length - 1];
|
|
1512
|
+
return {
|
|
1513
|
+
state: this.#state.state,
|
|
1514
|
+
revision: this.#state.revision,
|
|
1515
|
+
executionDecisionId: this.#state.executionDecisionId,
|
|
1516
|
+
warning: 'snapshot missing recommendation/reasons, defaulted to INLINE',
|
|
1517
|
+
auditId: lastAudit?.id || auditId,
|
|
1518
|
+
};
|
|
1519
|
+
}
|
|
1520
|
+
// harden-task-integrity: emit WARN if tasks.md mismatch
|
|
1521
|
+
if (taskCountMismatch) {
|
|
1522
|
+
const auditId = `aud-${Date.now()}-${this.#state.auditSeq}`;
|
|
1523
|
+
log('warn:task_count_mismatch', {
|
|
1524
|
+
auditId,
|
|
1525
|
+
snapshotCount: snapshot?.expectedTaskIds?.length ?? 0,
|
|
1526
|
+
tasksMdCount: tasksMdIds.length,
|
|
1527
|
+
});
|
|
1528
|
+
await this.#audit(
|
|
1529
|
+
'WARN',
|
|
1530
|
+
'task_count_mismatch',
|
|
1531
|
+
`expectedTasks from snapshot (${snapshot?.expectedTaskIds?.length ?? 0}) differs from tasks.md (${tasksMdIds.length}) - using tasks.md`
|
|
1532
|
+
);
|
|
1533
|
+
}
|
|
1380
1534
|
// 8.2: reasoning sin plan → WARN (but allow early-exit style)
|
|
1381
1535
|
if (!execShown && snapshot && !isEarlyExitExec) {
|
|
1382
1536
|
// Only warn if snapshot was expected to have reasoning (taskCount>2 or not early-exit)
|
|
@@ -1612,9 +1766,59 @@ class OstackyController {
|
|
|
1612
1766
|
return { state: this.#state.state, revision: this.#state.revision };
|
|
1613
1767
|
}
|
|
1614
1768
|
|
|
1615
|
-
// --- C2: Expected tasks gate (controller as source of truth) ---
|
|
1769
|
+
// --- C2: Expected tasks gate (controller as source of truth) — harden-task-integrity: tasks.md canonical ---
|
|
1616
1770
|
async setExpectedTasks({ taskIds, taskCount } = {}) {
|
|
1617
1771
|
this.#load();
|
|
1772
|
+
let tasksMdIds = [];
|
|
1773
|
+
try {
|
|
1774
|
+
tasksMdIds = parseTasksMd(this.#state.changeId, this.#statePath);
|
|
1775
|
+
} catch {}
|
|
1776
|
+
if (tasksMdIds.length > 0) {
|
|
1777
|
+
const providedIds = Array.isArray(taskIds) ? taskIds : null;
|
|
1778
|
+
if (providedIds) {
|
|
1779
|
+
const setA = new Set(providedIds);
|
|
1780
|
+
const setB = new Set(tasksMdIds);
|
|
1781
|
+
const mismatch =
|
|
1782
|
+
providedIds.length !== tasksMdIds.length ||
|
|
1783
|
+
[...setA].some((id) => !setB.has(id)) ||
|
|
1784
|
+
[...setB].some((id) => !setA.has(id));
|
|
1785
|
+
if (mismatch) {
|
|
1786
|
+
const auditId = `aud-${Date.now()}-${this.#state.auditSeq}`;
|
|
1787
|
+
log('warn:task_count_mismatch', {
|
|
1788
|
+
auditId,
|
|
1789
|
+
provided: providedIds.length,
|
|
1790
|
+
tasksMd: tasksMdIds.length,
|
|
1791
|
+
});
|
|
1792
|
+
await this.#audit(
|
|
1793
|
+
'WARN',
|
|
1794
|
+
'task_count_mismatch',
|
|
1795
|
+
`expectedTasks from caller (${providedIds.length}) differs from tasks.md (${tasksMdIds.length}) - using tasks.md`
|
|
1796
|
+
);
|
|
1797
|
+
}
|
|
1798
|
+
} else if (typeof taskCount === 'number' && taskCount !== tasksMdIds.length) {
|
|
1799
|
+
const auditId = `aud-${Date.now()}-${this.#state.auditSeq}`;
|
|
1800
|
+
log('warn:task_count_mismatch', { auditId, provided: taskCount, tasksMd: tasksMdIds.length });
|
|
1801
|
+
await this.#audit(
|
|
1802
|
+
'WARN',
|
|
1803
|
+
'task_count_mismatch',
|
|
1804
|
+
`expectedTaskCount from caller (${taskCount}) differs from tasks.md (${tasksMdIds.length}) - using tasks.md`
|
|
1805
|
+
);
|
|
1806
|
+
}
|
|
1807
|
+
this.#state.expectedTasks = [...tasksMdIds];
|
|
1808
|
+
this.#state.expectedTaskCount = tasksMdIds.length;
|
|
1809
|
+
await this.#persist();
|
|
1810
|
+
await this.#audit(
|
|
1811
|
+
'EXECUTING',
|
|
1812
|
+
'set_expected_tasks',
|
|
1813
|
+
`expected=${this.#state.expectedTaskCount} (from tasks.md)`
|
|
1814
|
+
);
|
|
1815
|
+
return {
|
|
1816
|
+
ok: true,
|
|
1817
|
+
expectedTasks: this.#state.expectedTasks,
|
|
1818
|
+
expectedTaskCount: this.#state.expectedTaskCount,
|
|
1819
|
+
source: 'tasks.md',
|
|
1820
|
+
};
|
|
1821
|
+
}
|
|
1618
1822
|
if (Array.isArray(taskIds) && taskIds.length > 0) {
|
|
1619
1823
|
this.#state.expectedTasks = [...taskIds];
|
|
1620
1824
|
this.#state.expectedTaskCount = taskIds.length;
|
|
@@ -2129,19 +2333,18 @@ class OstackyController {
|
|
|
2129
2333
|
return { error: 'fingerprint required: file exists but fileHash is null' };
|
|
2130
2334
|
}
|
|
2131
2335
|
}
|
|
2132
|
-
//
|
|
2133
|
-
const isInline = this.#state.executionMode === 'INLINE' || this.#state.state === 'EXECUTING_INLINE';
|
|
2336
|
+
// harden-task-integrity 2.2: hard gate unificado (new files eximidos) — antes INLINE hard vs SUBAGENTS WARN
|
|
2134
2337
|
const isNewFile = !!(
|
|
2135
2338
|
filePath &&
|
|
2136
2339
|
!this.#state.fileFingerprints?.[filePath] &&
|
|
2137
2340
|
!Object.values(this.#state.tasks || {}).some((t) => t.filePath === filePath)
|
|
2138
2341
|
);
|
|
2139
2342
|
if (!this.#state.lastValidated || (filePath && this.#state.lastValidated.filePath !== filePath)) {
|
|
2140
|
-
if (
|
|
2343
|
+
if (!isNewFile) {
|
|
2141
2344
|
return {
|
|
2142
2345
|
error: 'validate required',
|
|
2143
2346
|
outcome: 'CONFLICT',
|
|
2144
|
-
reason: `complete_task without prior validate_edit for ${filePath || taskId} — hard gate
|
|
2347
|
+
reason: `complete_task without prior validate_edit for ${filePath || taskId} — hard gate (new files eximidos)`,
|
|
2145
2348
|
};
|
|
2146
2349
|
}
|
|
2147
2350
|
this.#state.completeWithoutValidateCount = (this.#state.completeWithoutValidateCount || 0) + 1;
|
|
@@ -2373,7 +2576,7 @@ function safeHandler(fn, options = {}) {
|
|
|
2373
2576
|
|
|
2374
2577
|
const server = new McpServer({
|
|
2375
2578
|
name: 'ostacky-controller',
|
|
2376
|
-
version: '0.8.
|
|
2579
|
+
version: '0.8.7',
|
|
2377
2580
|
});
|
|
2378
2581
|
|
|
2379
2582
|
server.registerTool(
|
|
@@ -2973,7 +3176,7 @@ function setupGracefulShutdown(ctrl) {
|
|
|
2973
3176
|
}
|
|
2974
3177
|
|
|
2975
3178
|
async function main() {
|
|
2976
|
-
log('Starting ostacky-controller MCP v0.8.
|
|
3179
|
+
log('Starting ostacky-controller MCP v0.8.7...');
|
|
2977
3180
|
log('State path:', { path: statePath });
|
|
2978
3181
|
// Clean up stale tmp/lock files from previous runs
|
|
2979
3182
|
cleanupTmpFiles(statePath);
|
package/assets/plugins/engram.ts
CHANGED
|
@@ -437,6 +437,37 @@ export const Engram: Plugin = async (ctx) => {
|
|
|
437
437
|
}
|
|
438
438
|
// No inyectar MEMORY_INSTRUCTIONS completo nunca — se lee on-demand via Read
|
|
439
439
|
|
|
440
|
+
// harden-compaction-resume: auto-inject recovery hint when pending (no depende del modelo)
|
|
441
|
+
if (!shouldBeTrivial) {
|
|
442
|
+
try {
|
|
443
|
+
const statePath = process.env.OSTACKY_STATE_PATH || join(ctx.directory, ".opencode", "ostacky-state.json")
|
|
444
|
+
const raw = readFileSync(statePath, "utf-8")
|
|
445
|
+
const st = JSON.parse(raw)
|
|
446
|
+
const curState = st?.state ?? "DONE"
|
|
447
|
+
if (!["DONE", "INTERPRETATION_PENDING"].includes(curState)) {
|
|
448
|
+
let pending: string[] = Array.isArray(st?.lastHandoff?.pendingTasks)
|
|
449
|
+
? st.lastHandoff.pendingTasks.filter((id: string) => !st.tasks?.[id] || st.tasks[id].status !== "COMPLETED")
|
|
450
|
+
: []
|
|
451
|
+
if (pending.length === 0) {
|
|
452
|
+
try {
|
|
453
|
+
const fbPath = join(dirname(statePath), ".ostacky-handoff-compaction.json")
|
|
454
|
+
const fbRaw = readFileSync(fbPath, "utf-8")
|
|
455
|
+
const fb = JSON.parse(fbRaw)
|
|
456
|
+
if (fb && Array.isArray(fb.pendingTasks) && typeof fb.ts === "number" && Date.now() - fb.ts < 24 * 60 * 60 * 1000) {
|
|
457
|
+
const fbPend = fb.pendingTasks.filter((id: string) => !st.tasks?.[id] || st.tasks[id].status !== "COMPLETED")
|
|
458
|
+
if (fbPend.length > 0) pending = fbPend
|
|
459
|
+
}
|
|
460
|
+
} catch {}
|
|
461
|
+
}
|
|
462
|
+
if (pending.length > 0) {
|
|
463
|
+
const hint = `\n\n[RECOVERY: te quedan ${pending.slice(0, 3).join(",")}${pending.length > 3 ? `, +${pending.length - 3} más` : ""} - usa get_handoff / mem_context para retomar]`
|
|
464
|
+
if (output.system.length > 0) output.system[output.system.length - 1] += hint
|
|
465
|
+
else output.system.push(hint.trim())
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
} catch {}
|
|
469
|
+
}
|
|
470
|
+
|
|
440
471
|
// ── Save nudge ──────────────────────────────────────────────────────────
|
|
441
472
|
// Skip nudge for trivial greeting (cache-friendly, no extra injection)
|
|
442
473
|
if (shouldBeTrivial) return
|
|
@@ -204,6 +204,37 @@ export const OstackyController: Plugin = async (ctx) => {
|
|
|
204
204
|
// Also handle TIER1 hint for small tasks without replacing system
|
|
205
205
|
// Intent detection for downgradeable 0/0+1 is done in record_discovery router, not here
|
|
206
206
|
}
|
|
207
|
+
// harden-compaction-resume: auto-inject recovery hint when pending (determinístico, no depende del modelo)
|
|
208
|
+
if (!trivial && state && !["DONE", "INTERPRETATION_PENDING"].includes(state.state)) {
|
|
209
|
+
try {
|
|
210
|
+
let pending: string[] = Array.isArray(state.lastHandoff?.pendingTasks)
|
|
211
|
+
? state.lastHandoff.pendingTasks.filter((id: string) => !state.tasks?.[id] || state.tasks[id].status !== "COMPLETED")
|
|
212
|
+
: []
|
|
213
|
+
if (pending.length === 0) {
|
|
214
|
+
try {
|
|
215
|
+
const fallbackPath = join(dirname(getStatePath(ctx.directory)), ".ostacky-handoff-compaction.json")
|
|
216
|
+
if (existsSync(fallbackPath)) {
|
|
217
|
+
const raw = readFileSync(fallbackPath, "utf-8")
|
|
218
|
+
const data = JSON.parse(raw)
|
|
219
|
+
if (data && Array.isArray(data.pendingTasks) && typeof data.ts === "number" && Date.now() - data.ts < 24 * 60 * 60 * 1000) {
|
|
220
|
+
const fbPending = data.pendingTasks.filter((id: string) => !state.tasks?.[id] || state.tasks[id].status !== "COMPLETED")
|
|
221
|
+
if (fbPending.length > 0) pending = fbPending
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
} catch {}
|
|
225
|
+
}
|
|
226
|
+
if (pending.length > 0) {
|
|
227
|
+
const hint = `\n\n[RECOVERY: te quedan ${pending.slice(0, 3).join(",")}${pending.length > 3 ? `, +${pending.length - 3} más` : ""} - usa get_handoff / mem_context para retomar]`
|
|
228
|
+
if (output.parts && output.parts.length > 0) {
|
|
229
|
+
const last = output.parts[output.parts.length - 1]
|
|
230
|
+
if (last.type === "text") last.text = (last.text ?? "") + hint
|
|
231
|
+
else output.parts.push({ type: "text", text: hint })
|
|
232
|
+
} else {
|
|
233
|
+
output.parts = [{ type: "text", text: text + hint }]
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
} catch {}
|
|
237
|
+
}
|
|
207
238
|
},
|
|
208
239
|
|
|
209
240
|
// ── Hard gates before any tool ──
|
|
@@ -292,9 +323,11 @@ export const OstackyController: Plugin = async (ctx) => {
|
|
|
292
323
|
if (u.startsWith("file://")) targetPath = u.slice(7)
|
|
293
324
|
}
|
|
294
325
|
const isCodeFile = /\.(ts|js|tsx|jsx|mts|cts)$/i.test(targetPath) || targetPath.includes("src/") || targetPath.includes("assets/") || args?.pattern?.includes("*.ts")
|
|
326
|
+
// harden-task-integrity 1.3: eximir reads de tasks.md del gate (no requiere CodeGraph)
|
|
327
|
+
const isSpecTasksPath = targetPath.includes("openspec/changes") || targetPath.includes("openspec/specs")
|
|
295
328
|
// Grep on *.md should not be blocked
|
|
296
329
|
const isLiteralGrep = tool === "grep" && (args?.include?.endsWith(".md") || args?.include?.endsWith(".json"))
|
|
297
|
-
if (isCodeFile && !isLiteralGrep) {
|
|
330
|
+
if (isCodeFile && !isLiteralGrep && !isSpecTasksPath) {
|
|
298
331
|
const hasDiscoveryHit = discoveryHitByRequest.get(sessionId) ?? getDiscoveryCacheHit(ctx.directory)
|
|
299
332
|
const codegraphOk = isCodegraphAvailable(ctx.directory)
|
|
300
333
|
if (codegraphOk && !hasDiscoveryHit) {
|
|
@@ -335,6 +368,29 @@ export const OstackyController: Plugin = async (ctx) => {
|
|
|
335
368
|
}
|
|
336
369
|
}
|
|
337
370
|
|
|
371
|
+
// ── 3.5) Bash file-mutation gate (harden-task-integrity: bash mutante requiere validate_edit) ──
|
|
372
|
+
if (tool === "bash" && freshState && ["EXECUTING_INLINE", "EXECUTING_SUBAGENTS"].includes(freshState.state)) {
|
|
373
|
+
const cmdForMutation: string = args?.command || args?.cmd || ""
|
|
374
|
+
const isMutating = /[>]{1,2}\s*\S+|\bsed\b[^|;]*-i|\btruncate\b|\btee\b|\bcp\s+|\bmv\s+|python.*open.*w/.test(cmdForMutation)
|
|
375
|
+
if (isMutating) {
|
|
376
|
+
const mPaths = extractPathsFromBash(cmdForMutation)
|
|
377
|
+
for (const p of mPaths) {
|
|
378
|
+
if (!p) continue
|
|
379
|
+
const isProjectCode = p.includes("src/") || p.includes("assets/") || /\.(ts|js|tsx|jsx|mts|cts)$/i.test(p)
|
|
380
|
+
if (!isProjectCode) continue
|
|
381
|
+
if (!isPathInsideProject(p, ctx.directory)) continue
|
|
382
|
+
const abs = resolve(ctx.directory, p)
|
|
383
|
+
const exists = existsSync(abs)
|
|
384
|
+
const isNewFile = !exists && !freshState.fileFingerprints?.[p] && !Object.values(freshState.tasks || {}).some((t: any) => t.filePath === p)
|
|
385
|
+
if (isNewFile) continue
|
|
386
|
+
const lv = freshState.lastValidated
|
|
387
|
+
if (!lv || lv.filePath !== p) {
|
|
388
|
+
throw new Error(`BLOCKED: file mutation requires validate_edit/expectedTask for ${p} — usa validate_edit antes de bash mutante`)
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
338
394
|
let filePath: string | undefined
|
|
339
395
|
if (tool === "read" || tool === "read_mcp_resource" || tool === "grep" || tool === "glob" || tool === "write" || tool === "edit") {
|
|
340
396
|
filePath = args?.filePath || args?.path || args?.pattern || args?.uri || ""
|
|
@@ -176,6 +176,8 @@ Para cada fase de `tasks.md`, evaluar intra-fase:
|
|
|
176
176
|
|
|
177
177
|
**⚠️ Este skill provee ANÁLISIS, no autorización. Gate ANTES de persistir:** SHALL correr **en memoria primero**, derivar snapshot y **mostrar al usuario** `"Recomendación: INLINE/SUBAGENT por [razón], ~X líneas, clusters [...] ¿Procedo con este plan?"` y esperar. Solo si responde sí → `record_execution_analysis({snapshot, reuseDiscovery:true})` → `EXECUTION_DECISION_PENDING` → `consume_execution_decision`. Si responde no → `block({reason:"usuario rechazó plan"})` sin persistir. Si re-llamó `codegraph_explore` pudiendo reusar → `WARN:redundant_codegraph_call`.
|
|
178
178
|
|
|
179
|
+
**Recovery de snapshot incompleto (fix-execution-analysis-validation + bugfix 2026-09-18):** Si `record_execution_analysis` retorna `{error:"Snapshot missing ...", retryAllowed:true, suggestion}` (ya sea por `recommendation/reasons` **o** por `expectedTaskIds/taskIds` cuando `taskCount>0`), NO hacer `block` ni `abandon`. Corregir snapshot añadiendo el campo faltante y **reintentar el mismo `executionDecisionId`** — permanece en `EXECUTION_ANALYSIS` y el retry avanza. El controller también normaliza aliases (`mode`→`recommendation`, `reason`→`reasons`, JSON string) y considera `reasons:[]` como missing. Si `state.degraded==true` o `taskCount<=2` el controller defaultea a `INLINE` con `WARN:snapshot_defaulted` y ya transiciona; no reintentar. Verificar con `grep "retryAllowed" assets/mcp/ostacky-controller/index.js`. Snapshot válido (con `recommendation`, `reasons` y `expectedTaskIds`) **nunca** debe disparar `BLOCKED` ni requerir degraded.
|
|
180
|
+
|
|
179
181
|
## Ejemplo compacto
|
|
180
182
|
|
|
181
183
|
5 tasks, 3 clusters independientes (A: task1+task2 en `workflow.ts`, B: task3+task4 en `structured.ts`, C: task5 en archivo nuevo), sin deps entre clusters:
|
|
@@ -197,6 +197,21 @@ If unsure → ask the user.
|
|
|
197
197
|
- The task is high-risk without structural analysis (CodeGraph)
|
|
198
198
|
- The user requests to stop
|
|
199
199
|
|
|
200
|
+
### Without execution analysis (fix-execution-analysis-validation)
|
|
201
|
+
|
|
202
|
+
**Cuando `record_execution_analysis` falla por snapshot incompleto:**
|
|
203
|
+
|
|
204
|
+
1. **Si `retryAllowed:true` (modo normal, taskCount>2):** NO hacer `block`. Corregir snapshot (añadir `recommendation`/`reasons`) y reintentar `record_execution_analysis` con mismo `executionDecisionId` — permanece en `EXECUTION_ANALYSIS` y el retry avanza a `EXECUTION_DECISION_PENDING`.
|
|
205
|
+
2. **Si `warning: snapshot_defaulted` (degraded o taskCount<=2):** Ya avanzó a `EXECUTION_DECISION_PENDING` con `INLINE` defaulteado. Continuar a `consume_execution_decision`.
|
|
206
|
+
3. **Si snapshot irrecuperable (truncado 50KB, datos perdidos):** Usar `block({reason}) → BLOCKED → replan → INTERPRETATION_PENDING`. `lastHandoff` preserva checkpoint (cada 3 `complete_task`), no se pierden archivos.
|
|
207
|
+
|
|
208
|
+
**Workflow:**
|
|
209
|
+
```
|
|
210
|
+
1. Leer error: ¿tiene retryAllowed? → corregir y reintentar
|
|
211
|
+
2. ¿Es warning defaulted? → continuar
|
|
212
|
+
3. Solo si irrecuperable → block/replan degradado
|
|
213
|
+
```
|
|
214
|
+
|
|
200
215
|
## Integration
|
|
201
216
|
|
|
202
217
|
This skill is loaded automatically when the agent detects tool failures during the health check pre-vuelo.
|
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.8.
|
|
624
|
+
version: "0.8.7",
|
|
625
625
|
repo: "JaimeHoracio/Ostacky",
|
|
626
|
-
tag: "v0.8.
|
|
626
|
+
tag: "v0.8.7",
|
|
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.8.
|
|
632
|
-
version: "0.8.
|
|
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.8.7: prune de skills obsoletas, lastHandoff, getAvailableTransitions, consecutiveFailures real, mem_session_summary automático al cierre.",
|
|
632
|
+
version: "0.8.7",
|
|
633
|
+
sha256: "191a317be68bde517f6d3877ec3a016720617d7382944fff0d08d31b985a6644"
|
|
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.8.
|
|
641
|
-
version: "0.8.
|
|
642
|
-
sha256: "
|
|
640
|
+
description: "Instala el stack tecnológico del proyecto (CodeGraph, skills, OpenSpec, Engram, controller plugin). v0.8.7: controller plugin como alma hard-gate, Context7 removido del stack.",
|
|
641
|
+
version: "0.8.7",
|
|
642
|
+
sha256: "315d8fcdf2a945cef863bb4576838f95d7eb4a0acca38c12575dc80ecc4724dc"
|
|
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.8.
|
|
648
|
+
version: "0.8.7",
|
|
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.8.
|
|
657
|
-
version: "0.8.
|
|
658
|
-
sha256: "
|
|
656
|
+
description: "Máquina de estados persistida con @modelcontextprotocol/server SDK. v0.8.7: 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.8.7",
|
|
658
|
+
sha256: "9de463763ea6538c03b9307e7e36dcd2ae854dcc460bd25981e75f4f70df0e66"
|
|
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.8.
|
|
664
|
+
version: "0.8.7",
|
|
665
665
|
sha256: "fa4be28bfc1e75db8f1a037e2580f04a60066c80e8e5934e90e1041020d04609"
|
|
666
666
|
}
|
|
667
667
|
],
|
|
@@ -670,106 +670,106 @@ 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.8.
|
|
673
|
+
version: "0.8.7",
|
|
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.8.
|
|
681
|
-
sha256: "
|
|
680
|
+
version: "0.8.7",
|
|
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.8.
|
|
687
|
+
version: "0.8.7",
|
|
688
688
|
sha256: "aa412298980b7826165c211145c1b8e9135f68f36247bb3cb96d0a0eae274486"
|
|
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.8.
|
|
694
|
+
version: "0.8.7",
|
|
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.8.
|
|
701
|
+
version: "0.8.7",
|
|
702
702
|
sha256: "3c8a66d51ae2e719e877d02c3dac32bd804722c3bb9227c258ecae78252a20ab"
|
|
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.8.
|
|
708
|
+
version: "0.8.7",
|
|
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.8.
|
|
715
|
+
version: "0.8.7",
|
|
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.8.
|
|
722
|
+
version: "0.8.7",
|
|
723
723
|
sha256: "bb59100b9fd3c9f9a1ec6509fa975ec55bf7fc737df4fe73bb3e9bbc217b83f7"
|
|
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.8.
|
|
729
|
+
version: "0.8.7",
|
|
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.8.
|
|
736
|
+
version: "0.8.7",
|
|
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.8.
|
|
743
|
+
version: "0.8.7",
|
|
744
744
|
sha256: "37ae4aaf17ea71a395bab6dc4d9d61b9a49d7910f6692535ae8553244c46880b"
|
|
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.8.
|
|
750
|
+
version: "0.8.7",
|
|
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.8.
|
|
757
|
+
version: "0.8.7",
|
|
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.8.
|
|
764
|
+
version: "0.8.7",
|
|
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.8.
|
|
772
|
-
sha256: "
|
|
771
|
+
version: "0.8.7",
|
|
772
|
+
sha256: "363449bd9e6f800a0a3168667cf78f767fb105d86e4a4adadcc649d418139303"
|
|
773
773
|
}
|
|
774
774
|
]
|
|
775
775
|
};
|
|
@@ -1492,7 +1492,7 @@ var init_stack = __esm(() => {
|
|
|
1492
1492
|
// package.json
|
|
1493
1493
|
var package_default = {
|
|
1494
1494
|
name: "ostacky",
|
|
1495
|
-
version: "0.8.
|
|
1495
|
+
version: "0.8.7",
|
|
1496
1496
|
description: "Instalador interactivo de agentes y comandos para OpenCode",
|
|
1497
1497
|
type: "module",
|
|
1498
1498
|
bin: {
|
|
@@ -2669,7 +2669,7 @@ async function probeMcpServer(nodeExecutable, serverPath, cwd, statePath, option
|
|
|
2669
2669
|
params: {
|
|
2670
2670
|
protocolVersion: "2025-03-26",
|
|
2671
2671
|
capabilities: {},
|
|
2672
|
-
clientInfo: { name: "ostacky-installer", version: "0.8.
|
|
2672
|
+
clientInfo: { name: "ostacky-installer", version: "0.8.7" }
|
|
2673
2673
|
}
|
|
2674
2674
|
});
|
|
2675
2675
|
});
|
package/manifest.json
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.8.
|
|
2
|
+
"version": "0.8.7",
|
|
3
3
|
"repo": "JaimeHoracio/Ostacky",
|
|
4
|
-
"tag": "v0.8.
|
|
4
|
+
"tag": "v0.8.7",
|
|
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.8.
|
|
10
|
-
"version": "0.8.
|
|
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.8.7: prune de skills obsoletas, lastHandoff, getAvailableTransitions, consecutiveFailures real, mem_session_summary automático al cierre.",
|
|
10
|
+
"version": "0.8.7",
|
|
11
|
+
"sha256": "191a317be68bde517f6d3877ec3a016720617d7382944fff0d08d31b985a6644"
|
|
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.8.
|
|
19
|
-
"version": "0.8.
|
|
20
|
-
"sha256": "
|
|
18
|
+
"description": "Instala el stack tecnológico del proyecto (CodeGraph, skills, OpenSpec, Engram, controller plugin). v0.8.7: controller plugin como alma hard-gate, Context7 removido del stack.",
|
|
19
|
+
"version": "0.8.7",
|
|
20
|
+
"sha256": "315d8fcdf2a945cef863bb4576838f95d7eb4a0acca38c12575dc80ecc4724dc"
|
|
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.8.
|
|
26
|
+
"version": "0.8.7",
|
|
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.8.
|
|
35
|
-
"version": "0.8.
|
|
36
|
-
"sha256": "
|
|
34
|
+
"description": "Máquina de estados persistida con @modelcontextprotocol/server SDK. v0.8.7: 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.8.7",
|
|
36
|
+
"sha256": "9de463763ea6538c03b9307e7e36dcd2ae854dcc460bd25981e75f4f70df0e66"
|
|
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.8.
|
|
42
|
+
"version": "0.8.7",
|
|
43
43
|
"sha256": "fa4be28bfc1e75db8f1a037e2580f04a60066c80e8e5934e90e1041020d04609"
|
|
44
44
|
}
|
|
45
45
|
],
|
|
@@ -48,106 +48,106 @@
|
|
|
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.8.
|
|
51
|
+
"version": "0.8.7",
|
|
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.8.
|
|
59
|
-
"sha256": "
|
|
58
|
+
"version": "0.8.7",
|
|
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.8.
|
|
65
|
+
"version": "0.8.7",
|
|
66
66
|
"sha256": "aa412298980b7826165c211145c1b8e9135f68f36247bb3cb96d0a0eae274486"
|
|
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.8.
|
|
72
|
+
"version": "0.8.7",
|
|
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.8.
|
|
79
|
+
"version": "0.8.7",
|
|
80
80
|
"sha256": "3c8a66d51ae2e719e877d02c3dac32bd804722c3bb9227c258ecae78252a20ab"
|
|
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.8.
|
|
86
|
+
"version": "0.8.7",
|
|
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.8.
|
|
93
|
+
"version": "0.8.7",
|
|
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.8.
|
|
100
|
+
"version": "0.8.7",
|
|
101
101
|
"sha256": "bb59100b9fd3c9f9a1ec6509fa975ec55bf7fc737df4fe73bb3e9bbc217b83f7"
|
|
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.8.
|
|
107
|
+
"version": "0.8.7",
|
|
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.8.
|
|
114
|
+
"version": "0.8.7",
|
|
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.8.
|
|
121
|
+
"version": "0.8.7",
|
|
122
122
|
"sha256": "37ae4aaf17ea71a395bab6dc4d9d61b9a49d7910f6692535ae8553244c46880b"
|
|
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.8.
|
|
128
|
+
"version": "0.8.7",
|
|
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.8.
|
|
135
|
+
"version": "0.8.7",
|
|
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.8.
|
|
142
|
+
"version": "0.8.7",
|
|
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.8.
|
|
150
|
-
"sha256": "
|
|
149
|
+
"version": "0.8.7",
|
|
150
|
+
"sha256": "363449bd9e6f800a0a3168667cf78f767fb105d86e4a4adadcc649d418139303"
|
|
151
151
|
}
|
|
152
152
|
]
|
|
153
153
|
}
|