ostacky 0.8.6 → 0.8.8

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.
@@ -0,0 +1,118 @@
1
+ /**
2
+ * security.ts — mirror de src/security.ts para plugin (self-contained, solo Sensitive guard)
3
+ * Generado desde src/security.ts — NO EDITAR. Ejecutá `bun run scripts/sync-controller-core.ts`.
4
+ */
5
+
6
+ // ─── Sensitive guard — source-of-truth (hardening-v2 D1) ───────────────
7
+
8
+ export const SENSITIVE_DEFAULT: string[] = [
9
+ "**/.env*",
10
+ "**/.secrets/**",
11
+ "**/*.pem",
12
+ "**/*.key",
13
+ "**/.aws/**",
14
+ "**/.ssh/**",
15
+ "**/credentials.json",
16
+ "**/.npmrc",
17
+ ];
18
+
19
+ /**
20
+ * Regex que detecta acceso sensible en comandos bash.
21
+ * Cubre .env, .secrets, .pem, .key, .aws, .ssh, credentials.json, .npmrc
22
+ * Normalizar antes (strip quotes/backslashes) y testear con `BASH_SENSITIVE_RE.test(normalized)`.
23
+ */
24
+ export const BASH_SENSITIVE_RE =
25
+ /(?:^|[^a-zA-Z0-9_.-])(\.env(\b|[_.-])|\.secrets\b|\.pem\b|\.key\b|credentials\.json|\.aws\b|\.ssh\b|\.npmrc\b)/i;
26
+
27
+ /**
28
+ * Returns true if filePath is considered sensitive given patterns.
29
+ * - Allowlist: .env.example / .env.template / .env.sample nunca son sensibles.
30
+ * - Normaliza backslashes a "/" para Windows.
31
+ * - patterns: array de globs como SENSITIVE_DEFAULT; si no se pasa usa SENSITIVE_DEFAULT.
32
+ */
33
+ export function isSensitive(filePath: string, patterns: string[] = SENSITIVE_DEFAULT): boolean {
34
+ if (!filePath) return false;
35
+ const normalized = filePath.replace(/\\/g, "/");
36
+ const lower = normalized.toLowerCase();
37
+ if (
38
+ lower.endsWith(".env.example") ||
39
+ lower.endsWith(".env.template") ||
40
+ lower.endsWith(".env.sample")
41
+ )
42
+ return false;
43
+ const base = lower.split("/").pop() || "";
44
+ for (const pat of patterns) {
45
+ if (pat.includes(".env") && base.startsWith(".env")) return true;
46
+ if (pat.includes(".secrets") && lower.includes(".secrets")) return true;
47
+ if (pat.includes("*.pem") && lower.endsWith(".pem")) return true;
48
+ if (pat.includes("*.key") && lower.endsWith(".key")) return true;
49
+ if (pat.includes(".aws") && lower.includes(".aws")) return true;
50
+ if (pat.includes(".ssh") && lower.includes(".ssh")) return true;
51
+ if (pat.includes("credentials.json") && lower.endsWith("credentials.json")) return true;
52
+ if (pat.includes(".npmrc") && lower.endsWith(".npmrc")) return true;
53
+ }
54
+ // fallback genérico por extensión
55
+ if (/\.(pem|key)$/i.test(normalized)) return true;
56
+ if (base.startsWith(".env")) return true;
57
+ return false;
58
+ }
59
+
60
+ /**
61
+ * Extrae paths candidatos de un comando bash para chequear sensibilidad.
62
+ * Tokeniza por separadores shell `| ; && || > >> <` y respeta quotes.
63
+ * Normaliza quitando quotes y backslashes para detectar obfuscaciones como .e""nv.
64
+ */
65
+ export function extractPathsFromBash(cmd: string): string[] {
66
+ if (!cmd) return [];
67
+ // Normalizar && y || a ; para split único
68
+ const normalized = cmd.replace(/&&/g, ";").replace(/\|\|/g, ";");
69
+ // Split por separadores shell
70
+ const segments = normalized.split(/[|;><\n]+/);
71
+ const paths: string[] = [];
72
+ for (const seg of segments) {
73
+ const trimmed = seg.trim();
74
+ if (!trimmed) continue;
75
+ // Tokeniza respetando quotes — captura tokens entre quotes o sin espacios
76
+ const tokens = trimmed.match(/(?:[^\s"'`\\]+|"[^"]*"|'[^']*'|`[^`]*`)+/g) || [];
77
+ for (let token of tokens) {
78
+ // Normalización anti-obfuscación: quitar quotes y backslashes
79
+ const stripped = token.replace(/["'`]/g, "").replace(/\\/g, "");
80
+ if (!stripped) continue;
81
+ // Filtrar comandos comunes y flags
82
+ if (["cat", "grep", "ls", "echo", "awk", "sed", "cut", "head", "tail", "wc", "find", "xargs", "bash", "sh", "zsh", "env", "printenv", "node", "bun", "npm", "npx", "ls"].includes(stripped)) continue;
83
+ if (stripped.startsWith("-")) continue;
84
+ // Heurística de path: contiene / o . o es .env* o sensible
85
+ const lower = stripped.toLowerCase();
86
+ if (
87
+ stripped.includes("/") ||
88
+ stripped.includes(".") ||
89
+ lower.startsWith(".env") ||
90
+ lower.includes(".secrets") ||
91
+ lower.endsWith(".pem") ||
92
+ lower.endsWith(".key") ||
93
+ lower.includes(".aws") ||
94
+ lower.includes(".ssh") ||
95
+ lower.endsWith("credentials.json") ||
96
+ lower.endsWith(".npmrc")
97
+ ) {
98
+ // Limpiar trailing chars como : , )
99
+ const cleaned = stripped.replace(/[,:;)\]]+$/, "");
100
+ if (cleaned) paths.push(cleaned);
101
+ } else if (stripped === ".env") {
102
+ paths.push(stripped);
103
+ }
104
+ }
105
+ }
106
+ // También buscar paths con regex global como fallback
107
+ // (detecta .env en strings complejos)
108
+ return [...new Set(paths)];
109
+ }
110
+
111
+ /**
112
+ * Lee patrones sensibles desde env OSTACKY_SENSITIVE_PATTERNS o usa default.
113
+ */
114
+ export function getSensitivePatterns(): string[] {
115
+ const raw = process.env.OSTACKY_SENSITIVE_PATTERNS;
116
+ if (!raw) return SENSITIVE_DEFAULT;
117
+ return raw.split(",").map((s) => s.trim()).filter(Boolean);
118
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * tiered.ts — mirror de src/tiered.ts para plugin (self-contained)
3
+ * Generado desde src/tiered.ts — NO EDITAR. Ejecutá `bun run scripts/sync-controller-core.ts`.
4
+ */
5
+
6
+ /**
7
+ * Tiered helpers — single source of truth for trivial detection y niveles.
8
+ *
9
+ * Ambos plugins (ostacky-plugin.ts y engram.ts) importan de acá.
10
+ * No duplicar regex. Cache-friendly: isTrivial no muta system[0], solo decide hint.
11
+ * LEVEL_THRESHOLDS es la única definición de niveles (D6).
12
+ */
13
+
14
+ export const LEVEL_THRESHOLDS = {
15
+ "0": { maxFiles: 1, maxLines: 15, hasAPI: false, desc: "1 archivo, sin API, <15 líneas" },
16
+ "0+1": { maxFiles: 2, maxLines: 30, hasAPI: false, desc: "1-2 archivos, sin API, <30 líneas" },
17
+ "1+": { maxFiles: Infinity, maxLines: Infinity, hasAPI: true, desc: "API, deps, >30 líneas, cross-module" },
18
+ } as const;
19
+
20
+ export function classifyLevel({ fileCount, estLines, hasAPI }: { fileCount: number; estLines: number; hasAPI: boolean }): "0" | "0+1" | "1+" {
21
+ if (hasAPI || estLines > 30 || fileCount > 2) return "1+";
22
+ if (estLines > 15 || fileCount > 1) return "0+1";
23
+ return "0";
24
+ }
25
+
26
+ export function isTrivial(msg: string, state: string): boolean {
27
+ if (!msg || state !== "DONE") return false
28
+ if (msg.trim().length >= 30) return false
29
+ if (!/^(hola|hey|gracias|buenas|hi|hello)\b/i.test(msg.trim())) return false
30
+ if (/(necesito|quiero|agregá|fix|bug|feature|auth|spec|implementar)/i.test(msg)) return false
31
+ return true
32
+ }
33
+
34
+ export function getControllerState(directory: string): string {
35
+ try {
36
+ const { readFileSync } = require("node:fs") as typeof import("node:fs")
37
+ const { join } = require("node:path") as typeof import("node:path")
38
+ const statePath =
39
+ process.env.OSTACKY_STATE_PATH || join(directory, ".opencode", "ostacky-state.json")
40
+ const raw = readFileSync(statePath, "utf-8")
41
+ const j = JSON.parse(raw)
42
+ return j.state ?? "DONE"
43
+ } catch {
44
+ return "DONE"
45
+ }
46
+ }
@@ -129,6 +129,7 @@ Return: Summary of what you found and what you fixed.
129
129
  **Need full context:** Understanding requires seeing entire system
130
130
  **Exploratory debugging:** You don't know what's broken yet
131
131
  **Shared state:** Agents would interfere (editing same files, using same resources)
132
+ **Ostacky execution:** If `execution-mode-evaluation` already produced `fileClusters/sharedFiles` — **DO NOT use this skill**; use `subagent-driven-development` dispatch por clusters (respeta `sharedFiles` y `sequentialDeps` del Controller, evita pisar mismo archivo desde 2 agentes).
132
133
 
133
134
  ## Real Example from Session
134
135
 
@@ -176,7 +176,7 @@ 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):** Si `record_execution_analysis` retorna `{error:"Snapshot missing recommendation/reasons", retryAllowed:true, suggestion}`, NO hacer `block`. Corregir snapshot añadiendo `recommendation`/`reasons` y **reintentar el mismo `executionDecisionId`** — permanece en `EXECUTION_ANALYSIS` y el retry avanza. 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`.
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
180
 
181
181
  ## Ejemplo compacto
182
182
 
@@ -39,10 +39,11 @@ Depending on what the user brings, you might:
39
39
  - Find analogies
40
40
 
41
41
  **Investigate the codebase**
42
- - Map existing architecture relevant to the discussion
42
+ - Map existing architecture relevant to the discussion (ground with `codegraph_codegraph_explore` + `engram_mem_search` if pre-1+ or without active change)
43
43
  - Find integration points
44
44
  - Identify patterns already in use
45
45
  - Surface hidden complexity
46
+ - **Level note:** Si necesitás ruteo por nivel Ostacky (0/0+1/1+) usa `brainstorming` en vez de este stance; este skill asume ya estás en flujo OpenSpec (1+).
46
47
 
47
48
  **Compare options**
48
49
  - Brainstorm multiple approaches
@@ -26,7 +26,9 @@ When ready to implement, run /opsx-apply
26
26
 
27
27
  **Steps**
28
28
 
29
- 1. **Check Engram for similar changes** — `engram_mem_search` with keywords from the user's description. If a similar change was proposed or implemented before, surface it to the user. They may want to extend the existing work instead of starting fresh.
29
+ 0. **Ground with CodeGraph + Engram (SHALL — Ostacky)** — Antes de `openspec new`, verifica `getDiscoverySnapshot(query)` + `getEngramDedup`. Si miss → `codegraph_codegraph_explore` + `engram_mem_search` + `putDiscoverySnapshot`. Cita símbolos existentes en `proposal/design`; sin esto el `design.md` puede alucinar API inexistente y dispara `WARN:codegraphBypass` en `doctor`.
30
+
31
+ 1. **Check Engram for similar changes** — `engram_mem_search` con keywords del *paso 0* (reusar si ya fue hit; no duplicar `mem_search` mismo `requestId`). Si hay change similar previo, surfacealo. Pueden extender trabajo existente en vez de empezar de cero.
30
32
 
31
33
  2. **If no clear input provided, ask what they want to build**
32
34
 
@@ -28,6 +28,8 @@ Write the test first. Watch it fail. Write minimal code to pass.
28
28
 
29
29
  Thinking "skip TDD just this once"? Stop. That's rationalization.
30
30
 
31
+ > **Ostacky ground (SHALL):** Antes de RED, verifica `getDiscoverySnapshot(query)` + `getEngramDedup`. Si hit → reusar símbolos/decisiones; si miss → `codegraph_codegraph_explore` + `engram_mem_search` + `putDiscoverySnapshot`. Evita testear API inexistente y respeta `discovery-cache (único)` de `ostacky.md §1`. Ver `execution-mode-evaluation` para cache TTL.
32
+
31
33
  ## The Iron Law
32
34
 
33
35
  ```
package/dist/cli.js CHANGED
@@ -186,7 +186,7 @@ var require_picocolors = __commonJS(function(exports, module) {
186
186
  });
187
187
 
188
188
  // src/security.ts
189
- import { createHash } from "crypto";
189
+ import { createHash } from "node:crypto";
190
190
  function validateFilePath(filePath) {
191
191
  if (filePath.includes("..") || filePath.startsWith("/") || filePath.startsWith("\\") || /^[a-zA-Z]:/.test(filePath)) {
192
192
  throw new Error(`Ruta de archivo inválida: "${filePath}"`);
@@ -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.6",
624
+ version: "0.8.8",
625
625
  repo: "JaimeHoracio/Ostacky",
626
- tag: "v0.8.6",
626
+ tag: "v0.8.8",
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.6: prune de skills obsoletas, lastHandoff, getAvailableTransitions, consecutiveFailures real, mem_session_summary automático al cierre.",
632
- version: "0.8.6",
633
- sha256: "51bb5f57b493791c27e8134c3eb4e8bfb61a7672c40bf648c79f15c7ec2f6319"
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.8: prune de skills obsoletas, lastHandoff, getAvailableTransitions, consecutiveFailures real, mem_session_summary automático al cierre.",
632
+ version: "0.8.8",
633
+ sha256: "bac9b7bc91d9d958bee8c6b2e094b1e1124bcc7f3e23cd13769ebbc5766f4747"
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.6: controller plugin como alma hard-gate, Context7 removido del stack.",
641
- version: "0.8.6",
642
- sha256: "52f7af2a59fb3db19c33dccde7b284afdb3270e68696c721ad3303dcc85a645c"
640
+ description: "Instala el stack tecnológico del proyecto (CodeGraph, skills, OpenSpec, Engram, controller plugin). v0.8.8: controller plugin como alma hard-gate, Context7 removido del stack.",
641
+ version: "0.8.8",
642
+ sha256: "28ddf7ac8a2472faa1a5dbd14e22bc11e0b8715971907b3fab739b9933aa7dad"
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.6",
648
+ version: "0.8.8",
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.6: 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.6",
658
- sha256: "d832bcff83fb6b6334a494ff78029f379a977784b9a93675d1c5ea428f45cbc8"
656
+ description: "Máquina de estados persistida con @modelcontextprotocol/server SDK. v0.8.8: 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.8",
658
+ sha256: "a1d9744ffe70e976c7c96343bf101c1dedf615b27058559a376c0e559a341328"
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.6",
664
+ version: "0.8.8",
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.8.6",
673
+ version: "0.8.8",
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.6",
681
- sha256: "7a670f8ae9d5479840d9fc4a54fa6ae75809b2c8883414d39300fa6289b82e9a"
680
+ version: "0.8.8",
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.6",
688
- sha256: "aa412298980b7826165c211145c1b8e9135f68f36247bb3cb96d0a0eae274486"
687
+ version: "0.8.8",
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.8.6",
694
+ version: "0.8.8",
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.6",
702
- sha256: "3c8a66d51ae2e719e877d02c3dac32bd804722c3bb9227c258ecae78252a20ab"
701
+ version: "0.8.8",
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.8.6",
708
+ version: "0.8.8",
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.6",
715
+ version: "0.8.8",
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.6",
723
- sha256: "bb59100b9fd3c9f9a1ec6509fa975ec55bf7fc737df4fe73bb3e9bbc217b83f7"
722
+ version: "0.8.8",
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.8.6",
729
+ version: "0.8.8",
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.6",
736
+ version: "0.8.8",
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.6",
744
- sha256: "37ae4aaf17ea71a395bab6dc4d9d61b9a49d7910f6692535ae8553244c46880b"
743
+ version: "0.8.8",
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.8.6",
750
+ version: "0.8.8",
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.6",
757
+ version: "0.8.8",
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.6",
764
+ version: "0.8.8",
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.6",
771
+ version: "0.8.8",
772
772
  sha256: "363449bd9e6f800a0a3168667cf78f767fb105d86e4a4adadcc649d418139303"
773
773
  }
774
774
  ]
@@ -1155,6 +1155,8 @@ function copyEngramPlugin(projectRoot) {
1155
1155
  function copyOstackyControllerPlugin(projectRoot) {
1156
1156
  const pluginSource = join7(PACKAGE_ROOT, "assets", "plugins", "ostacky-plugin.ts");
1157
1157
  const coreSource = join7(PACKAGE_ROOT, "assets", "plugins", "controller-core.ts");
1158
+ const securitySource = join7(PACKAGE_ROOT, "assets", "plugins", "security.ts");
1159
+ const tieredSource = join7(PACKAGE_ROOT, "assets", "plugins", "tiered.ts");
1158
1160
  const pluginsDir = join7(projectRoot, ".opencode", "plugins");
1159
1161
  if (!existsSync6(pluginSource)) {
1160
1162
  throw new Error(`Plugin bundleado de OstackyController no encontrado: ${pluginSource}`);
@@ -1163,6 +1165,10 @@ function copyOstackyControllerPlugin(projectRoot) {
1163
1165
  copyFileSync3(pluginSource, join7(pluginsDir, "ostacky-plugin.ts"));
1164
1166
  if (existsSync6(coreSource))
1165
1167
  copyFileSync3(coreSource, join7(pluginsDir, "controller-core.ts"));
1168
+ if (existsSync6(securitySource))
1169
+ copyFileSync3(securitySource, join7(pluginsDir, "security.ts"));
1170
+ if (existsSync6(tieredSource))
1171
+ copyFileSync3(tieredSource, join7(pluginsDir, "tiered.ts"));
1166
1172
  }
1167
1173
  function buildEngramDownloadUrl(tag, platform = process.platform, arch = process.arch) {
1168
1174
  const target = getEngramReleaseTarget(platform, arch);
@@ -1492,14 +1498,14 @@ var init_stack = __esm(() => {
1492
1498
  // package.json
1493
1499
  var package_default = {
1494
1500
  name: "ostacky",
1495
- version: "0.8.6",
1501
+ version: "0.8.8",
1496
1502
  description: "Instalador interactivo de agentes y comandos para OpenCode",
1497
1503
  type: "module",
1498
1504
  bin: {
1499
1505
  ostacky: "./dist/cli.js"
1500
1506
  },
1501
1507
  scripts: {
1502
- prebuild: "bun run scripts/sync-version.ts",
1508
+ prebuild: "bun run scripts/sync-version.ts && bun run scripts/sync-controller-core.ts",
1503
1509
  dev: "bun run src/cli.ts",
1504
1510
  build: "bun build src/cli.ts --target=node --format=esm --outfile dist/cli.js && bun scripts/add-shebang.ts",
1505
1511
  start: "node dist/cli.js",
@@ -1510,7 +1516,7 @@ var package_default = {
1510
1516
  "hash:update": "bun run scripts/update-manifest-hashes.ts",
1511
1517
  "manifest:prepare": "bun run scripts/update-manifest-hashes.ts",
1512
1518
  "manifest:check": "bun run scripts/update-manifest-hashes.ts --check",
1513
- prepublishOnly: "bun run manifest:prepare && bun run build"
1519
+ prepublishOnly: "bun run scripts/sync-controller-core.ts && bun run manifest:prepare && bun run build"
1514
1520
  },
1515
1521
  files: [
1516
1522
  "dist/",
@@ -2669,7 +2675,7 @@ async function probeMcpServer(nodeExecutable, serverPath, cwd, statePath, option
2669
2675
  params: {
2670
2676
  protocolVersion: "2025-03-26",
2671
2677
  capabilities: {},
2672
- clientInfo: { name: "ostacky-installer", version: "0.8.6" }
2678
+ clientInfo: { name: "ostacky-installer", version: "0.8.8" }
2673
2679
  }
2674
2680
  });
2675
2681
  });
@@ -2978,7 +2984,15 @@ function uninstallAll(paths) {
2978
2984
  for (const name of Object.keys(lockfile.mcpServers ?? {})) {
2979
2985
  uninstallMcpServer(name, paths);
2980
2986
  }
2981
- const ostackyPlugins = ["ostacky-plugin.ts", "engram.ts", "ostacky-guard.ts", "ostacky-controller.ts"];
2987
+ const ostackyPlugins = [
2988
+ "ostacky-plugin.ts",
2989
+ "engram.ts",
2990
+ "ostacky-guard.ts",
2991
+ "ostacky-controller.ts",
2992
+ "controller-core.ts",
2993
+ "security.ts",
2994
+ "tiered.ts"
2995
+ ];
2982
2996
  for (const file of ostackyPlugins) {
2983
2997
  const fp = join6(paths.plugins, file);
2984
2998
  if (existsSync5(fp)) {
@@ -4347,6 +4361,8 @@ async function runDoctorCommand() {
4347
4361
  console.log(`⚠️ degraded: confirmation not audited in controller (degradedEditsCount=${parsed.degradedEditsCount})`);
4348
4362
  if (parsed.codegraphBypassCount > 0)
4349
4363
  console.log(`⚠️ codegraphBypassCount=${parsed.codegraphBypassCount} (inefficient: codegraph bypass)`);
4364
+ if (parsed.engramBypassCount > 0)
4365
+ console.log(`⚠️ engramBypassCount=${parsed.engramBypassCount} (contradiction check bypassed — sin mem_search)`);
4350
4366
  if (parsed.stateOversizedCount > 0)
4351
4367
  console.log(`⚠️ stateOversizedCount=${parsed.stateOversizedCount} snapshots perdidos`);
4352
4368
  if (parsed.sensitiveAccess)