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.
- package/README.md +13 -13
- package/assets/agents/ostacky.md +3 -3
- package/assets/commands/install-stack.md +2 -2
- package/assets/docs/ostacky-reference.md +10 -2
- package/assets/mcp/ostacky-controller/controller-core.js +2 -2
- package/assets/mcp/ostacky-controller/index.js +96 -20
- package/assets/mcp/ostacky-controller/package.json +1 -1
- package/assets/mcp/ostacky-controller/security.js +33 -2
- package/assets/plugins/controller-core.ts +2 -2
- package/assets/plugins/ostacky-plugin.ts +35 -3
- package/assets/plugins/security.ts +118 -0
- package/assets/plugins/tiered.ts +46 -0
- package/assets/skills/dispatching-parallel-agents/SKILL.md +1 -0
- package/assets/skills/execution-mode-evaluation/SKILL.md +1 -1
- package/assets/skills/openspec-explore/SKILL.md +2 -1
- package/assets/skills/openspec-propose/SKILL.md +3 -1
- package/assets/skills/tdd/SKILL.md +2 -0
- package/dist/cli.js +55 -39
- package/manifest.json +33 -33
- package/package.json +3 -3
|
@@ -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
|
|
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
|
-
|
|
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.
|
|
624
|
+
version: "0.8.8",
|
|
625
625
|
repo: "JaimeHoracio/Ostacky",
|
|
626
|
-
tag: "v0.8.
|
|
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.
|
|
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.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.
|
|
641
|
-
version: "0.8.
|
|
642
|
-
sha256: "
|
|
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.
|
|
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.
|
|
657
|
-
version: "0.8.
|
|
658
|
-
sha256: "
|
|
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.
|
|
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.
|
|
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.
|
|
681
|
-
sha256: "
|
|
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.
|
|
688
|
-
sha256: "
|
|
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.
|
|
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.
|
|
702
|
-
sha256: "
|
|
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.
|
|
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.
|
|
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.
|
|
723
|
-
sha256: "
|
|
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.
|
|
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.
|
|
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.
|
|
744
|
-
sha256: "
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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 = [
|
|
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)
|