ostacky 0.7.3 → 0.8.0
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 +29 -24
- package/assets/agents/ostacky.md +82 -525
- package/assets/commands/install-stack.md +2 -2
- package/assets/docs/engram-protocol.md +79 -0
- package/assets/docs/ostacky-reference.md +79 -0
- package/assets/mcp/ostacky-controller/index.js +698 -228
- package/assets/mcp/ostacky-controller/package.json +1 -1
- package/assets/mcp/ostacky-controller/security.js +87 -0
- package/assets/plugins/engram.ts +47 -79
- package/assets/plugins/ostacky-guard.ts +11 -124
- package/assets/plugins/ostacky-plugin.ts +646 -0
- package/assets/skills/brainstorming/SKILL.md +198 -197
- package/assets/skills/execution-mode-evaluation/SKILL.md +9 -9
- package/assets/skills/graceful-degradation/SKILL.md +251 -248
- package/dist/cli.js +432 -135
- package/manifest.json +31 -31
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -20,12 +20,14 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
20
20
|
}
|
|
21
21
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
22
22
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
if (mod && typeof mod === "object" || typeof mod === "function") {
|
|
24
|
+
for (let key of __getOwnPropNames(mod))
|
|
25
|
+
if (!__hasOwnProp.call(to, key))
|
|
26
|
+
__defProp(to, key, {
|
|
27
|
+
get: __accessProp.bind(mod, key),
|
|
28
|
+
enumerable: true
|
|
29
|
+
});
|
|
30
|
+
}
|
|
29
31
|
if (canCache)
|
|
30
32
|
cache.set(mod, to);
|
|
31
33
|
return to;
|
|
@@ -48,7 +50,7 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
|
48
50
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
49
51
|
|
|
50
52
|
// node_modules/sisteransi/src/index.js
|
|
51
|
-
var require_src = __commonJS((exports, module)
|
|
53
|
+
var require_src = __commonJS(function(exports, module) {
|
|
52
54
|
var ESC = "\x1B";
|
|
53
55
|
var CSI = `${ESC}[`;
|
|
54
56
|
var beep = "\x07";
|
|
@@ -106,7 +108,7 @@ var require_src = __commonJS((exports, module) => {
|
|
|
106
108
|
});
|
|
107
109
|
|
|
108
110
|
// node_modules/picocolors/picocolors.js
|
|
109
|
-
var require_picocolors = __commonJS((exports, module)
|
|
111
|
+
var require_picocolors = __commonJS(function(exports, module) {
|
|
110
112
|
var p = process || {};
|
|
111
113
|
var argv = p.argv || [];
|
|
112
114
|
var env = p.env || {};
|
|
@@ -178,9 +180,14 @@ var require_picocolors = __commonJS((exports, module) => {
|
|
|
178
180
|
// src/security.ts
|
|
179
181
|
var exports_security = {};
|
|
180
182
|
__export(exports_security, {
|
|
181
|
-
|
|
183
|
+
BASH_SENSITIVE_RE: () => BASH_SENSITIVE_RE,
|
|
184
|
+
SENSITIVE_DEFAULT: () => SENSITIVE_DEFAULT,
|
|
185
|
+
extractPathsFromBash: () => extractPathsFromBash,
|
|
186
|
+
getSensitivePatterns: () => getSensitivePatterns,
|
|
187
|
+
isSensitive: () => isSensitive,
|
|
188
|
+
sha256: () => sha256,
|
|
182
189
|
validateFilePath: () => validateFilePath,
|
|
183
|
-
|
|
190
|
+
verifyChecksum: () => verifyChecksum
|
|
184
191
|
});
|
|
185
192
|
import { createHash } from "crypto";
|
|
186
193
|
function validateFilePath(filePath) {
|
|
@@ -201,9 +208,121 @@ function verifyChecksum(content, expectedHash, label) {
|
|
|
201
208
|
recibido: ${actual}`);
|
|
202
209
|
}
|
|
203
210
|
}
|
|
204
|
-
|
|
211
|
+
function isSensitive(filePath, patterns = SENSITIVE_DEFAULT) {
|
|
212
|
+
if (!filePath)
|
|
213
|
+
return false;
|
|
214
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
215
|
+
const lower = normalized.toLowerCase();
|
|
216
|
+
if (lower.endsWith(".env.example") || lower.endsWith(".env.template") || lower.endsWith(".env.sample"))
|
|
217
|
+
return false;
|
|
218
|
+
const base = lower.split("/").pop() || "";
|
|
219
|
+
for (const pat of patterns) {
|
|
220
|
+
if (pat.includes(".env") && base.startsWith(".env"))
|
|
221
|
+
return true;
|
|
222
|
+
if (pat.includes(".secrets") && lower.includes(".secrets"))
|
|
223
|
+
return true;
|
|
224
|
+
if (pat.includes("*.pem") && lower.endsWith(".pem"))
|
|
225
|
+
return true;
|
|
226
|
+
if (pat.includes("*.key") && lower.endsWith(".key"))
|
|
227
|
+
return true;
|
|
228
|
+
if (pat.includes(".aws") && lower.includes(".aws"))
|
|
229
|
+
return true;
|
|
230
|
+
if (pat.includes(".ssh") && lower.includes(".ssh"))
|
|
231
|
+
return true;
|
|
232
|
+
if (pat.includes("credentials.json") && lower.endsWith("credentials.json"))
|
|
233
|
+
return true;
|
|
234
|
+
if (pat.includes(".npmrc") && lower.endsWith(".npmrc"))
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
if (/\.(pem|key)$/i.test(normalized))
|
|
238
|
+
return true;
|
|
239
|
+
if (base.startsWith(".env"))
|
|
240
|
+
return true;
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
function extractPathsFromBash(cmd) {
|
|
244
|
+
if (!cmd)
|
|
245
|
+
return [];
|
|
246
|
+
const normalized = cmd.replace(/&&/g, ";").replace(/\|\|/g, ";");
|
|
247
|
+
const segments = normalized.split(/[|;><\n]+/);
|
|
248
|
+
const paths = [];
|
|
249
|
+
for (const seg of segments) {
|
|
250
|
+
const trimmed = seg.trim();
|
|
251
|
+
if (!trimmed)
|
|
252
|
+
continue;
|
|
253
|
+
const tokens = trimmed.match(/(?:[^\s"'`\\]+|"[^"]*"|'[^']*'|`[^`]*`)+/g) || [];
|
|
254
|
+
for (let token of tokens) {
|
|
255
|
+
const stripped = token.replace(/["'`]/g, "").replace(/\\/g, "");
|
|
256
|
+
if (!stripped)
|
|
257
|
+
continue;
|
|
258
|
+
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))
|
|
259
|
+
continue;
|
|
260
|
+
if (stripped.startsWith("-"))
|
|
261
|
+
continue;
|
|
262
|
+
const lower = stripped.toLowerCase();
|
|
263
|
+
if (stripped.includes("/") || stripped.includes(".") || lower.startsWith(".env") || lower.includes(".secrets") || lower.endsWith(".pem") || lower.endsWith(".key") || lower.includes(".aws") || lower.includes(".ssh") || lower.endsWith("credentials.json") || lower.endsWith(".npmrc")) {
|
|
264
|
+
const cleaned = stripped.replace(/[,:;)\]]+$/, "");
|
|
265
|
+
if (cleaned)
|
|
266
|
+
paths.push(cleaned);
|
|
267
|
+
} else if (stripped === ".env") {
|
|
268
|
+
paths.push(stripped);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return [...new Set(paths)];
|
|
273
|
+
}
|
|
274
|
+
function getSensitivePatterns() {
|
|
275
|
+
const raw = process.env.OSTACKY_SENSITIVE_PATTERNS;
|
|
276
|
+
if (!raw)
|
|
277
|
+
return SENSITIVE_DEFAULT;
|
|
278
|
+
return raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
279
|
+
}
|
|
280
|
+
var SENSITIVE_DEFAULT, BASH_SENSITIVE_RE;
|
|
281
|
+
var init_security = __esm(() => {
|
|
282
|
+
SENSITIVE_DEFAULT = [
|
|
283
|
+
"**/.env*",
|
|
284
|
+
"**/.secrets/**",
|
|
285
|
+
"**/*.pem",
|
|
286
|
+
"**/*.key",
|
|
287
|
+
"**/.aws/**",
|
|
288
|
+
"**/.ssh/**",
|
|
289
|
+
"**/credentials.json",
|
|
290
|
+
"**/.npmrc"
|
|
291
|
+
];
|
|
292
|
+
BASH_SENSITIVE_RE = /(?:^|[^a-zA-Z0-9_.-])(\.env(\b|[_.-])|\.secrets\b|\.pem\b|\.key\b|credentials\.json|\.aws\b|\.ssh\b|\.npmrc\b)/i;
|
|
293
|
+
});
|
|
205
294
|
|
|
206
295
|
// src/fs.ts
|
|
296
|
+
var exports_fs = {};
|
|
297
|
+
__export(exports_fs, {
|
|
298
|
+
USER_AGENT: () => USER_AGENT,
|
|
299
|
+
checkBunAvailability: () => checkBunAvailability,
|
|
300
|
+
computeTreeHash: () => computeTreeHash,
|
|
301
|
+
copyDirRecursive: () => copyDirRecursive,
|
|
302
|
+
createOpenCodeDir: () => createOpenCodeDir,
|
|
303
|
+
detectPlatformTarget: () => detectPlatformTarget,
|
|
304
|
+
downloadAndExtract: () => downloadAndExtract,
|
|
305
|
+
downloadAndExtractWithRetry: () => downloadAndExtractWithRetry,
|
|
306
|
+
downloadToFile: () => downloadToFile,
|
|
307
|
+
downloadWithRetry: () => downloadWithRetry,
|
|
308
|
+
ensureOpenCodePaths: () => ensureOpenCodePaths,
|
|
309
|
+
ensureToolDirs: () => ensureToolDirs,
|
|
310
|
+
findBinaryInDir: () => findBinaryInDir,
|
|
311
|
+
findExecutablePath: () => findExecutablePath,
|
|
312
|
+
findOpenCodeDir: () => findOpenCodeDir,
|
|
313
|
+
findProjectRoot: () => findProjectRoot,
|
|
314
|
+
getBunInstallCommand: () => getBunInstallCommand,
|
|
315
|
+
getCommandInvocation: () => getCommandInvocation,
|
|
316
|
+
getEngramReleaseTarget: () => getEngramReleaseTarget,
|
|
317
|
+
getExecutableName: () => getExecutableName,
|
|
318
|
+
getExecutableNames: () => getExecutableNames,
|
|
319
|
+
getGlobalOpenCodeDir: () => getGlobalOpenCodeDir,
|
|
320
|
+
getOpenCodeDirForScope: () => getOpenCodeDirForScope,
|
|
321
|
+
isCommandAvailable: () => isCommandAvailable,
|
|
322
|
+
parseScopeArg: () => parseScopeArg,
|
|
323
|
+
promoteStagedDirectory: () => promoteStagedDirectory,
|
|
324
|
+
shouldRetryDownload: () => shouldRetryDownload
|
|
325
|
+
});
|
|
207
326
|
import {
|
|
208
327
|
existsSync,
|
|
209
328
|
mkdirSync,
|
|
@@ -277,6 +396,22 @@ function getOpenCodeDirForScope(scope, cwd = process.cwd()) {
|
|
|
277
396
|
}
|
|
278
397
|
return getGlobalOpenCodeDir();
|
|
279
398
|
}
|
|
399
|
+
function parseScopeArg(argv = process.argv) {
|
|
400
|
+
for (let i = 0;i < argv.length; i++) {
|
|
401
|
+
const arg = argv[i];
|
|
402
|
+
if (arg === "--scope" && i + 1 < argv.length) {
|
|
403
|
+
const v3 = argv[i + 1];
|
|
404
|
+
if (v3 === "local" || v3 === "global" || v3 === "auto")
|
|
405
|
+
return v3;
|
|
406
|
+
}
|
|
407
|
+
if (arg.startsWith("--scope=")) {
|
|
408
|
+
const v3 = arg.split("=")[1];
|
|
409
|
+
if (v3 === "local" || v3 === "global" || v3 === "auto")
|
|
410
|
+
return v3;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
280
415
|
function ensureOpenCodePaths(opencodeDir) {
|
|
281
416
|
const paths = {
|
|
282
417
|
root: opencodeDir,
|
|
@@ -300,6 +435,9 @@ function ensureToolDirs(toolsDir, toolNames) {
|
|
|
300
435
|
mkdirSync(dir, { recursive: true });
|
|
301
436
|
}
|
|
302
437
|
}
|
|
438
|
+
function createOpenCodeDir(baseDir) {
|
|
439
|
+
return ensureOpenCodePaths(join(baseDir, ".opencode"));
|
|
440
|
+
}
|
|
303
441
|
function copyDirRecursive(src, dest, skipGenerated = false) {
|
|
304
442
|
if (!existsSync(dest))
|
|
305
443
|
mkdirSync(dest, { recursive: true });
|
|
@@ -360,6 +498,44 @@ function findExecutablePath(cmd) {
|
|
|
360
498
|
return null;
|
|
361
499
|
}
|
|
362
500
|
}
|
|
501
|
+
function getBunInstallCommand() {
|
|
502
|
+
const platform = process.platform;
|
|
503
|
+
if (platform === "darwin" || platform === "linux") {
|
|
504
|
+
return {
|
|
505
|
+
command: "curl -fsSL https://bun.com/install | bash",
|
|
506
|
+
note: platform === "linux" ? "Requires 'unzip' package (sudo apt install unzip)" : undefined
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
if (platform === "win32") {
|
|
510
|
+
return {
|
|
511
|
+
command: 'powershell -c "irm bun.sh/install.ps1|iex"',
|
|
512
|
+
note: "Requires Windows 10 v1809 or later"
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
return {
|
|
516
|
+
command: "npm install -g bun",
|
|
517
|
+
note: "Cross-platform fallback"
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
function checkBunAvailability() {
|
|
521
|
+
const bunPath = findExecutablePath("bun");
|
|
522
|
+
if (bunPath) {
|
|
523
|
+
let version;
|
|
524
|
+
try {
|
|
525
|
+
version = execFileSync(bunPath, ["--version"], {
|
|
526
|
+
encoding: "utf-8",
|
|
527
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
528
|
+
}).trim();
|
|
529
|
+
} catch {}
|
|
530
|
+
return { available: true, path: bunPath, version };
|
|
531
|
+
}
|
|
532
|
+
const { command, note } = getBunInstallCommand();
|
|
533
|
+
return {
|
|
534
|
+
available: false,
|
|
535
|
+
installCommand: command,
|
|
536
|
+
installNote: note
|
|
537
|
+
};
|
|
538
|
+
}
|
|
363
539
|
function detectPlatformTarget(platform = process.platform, arch = process.arch) {
|
|
364
540
|
let os;
|
|
365
541
|
let cpu;
|
|
@@ -481,6 +657,23 @@ function downloadToFile(url, dest, timeoutMs = 180000) {
|
|
|
481
657
|
});
|
|
482
658
|
});
|
|
483
659
|
}
|
|
660
|
+
async function downloadWithRetry(url, dest, maxRetries = 2, timeoutMs = 180000) {
|
|
661
|
+
let lastError = null;
|
|
662
|
+
for (let attempt = 0;attempt <= maxRetries; attempt++) {
|
|
663
|
+
try {
|
|
664
|
+
await downloadToFile(url, dest, timeoutMs);
|
|
665
|
+
return;
|
|
666
|
+
} catch (err) {
|
|
667
|
+
lastError = err;
|
|
668
|
+
if (attempt < maxRetries && shouldRetryDownload(lastError)) {
|
|
669
|
+
const delay = 1000 * Math.pow(2, attempt);
|
|
670
|
+
console.error(`[download] Intento ${attempt + 1} falló: ${lastError.message}. Reintentando en ${delay}ms...`);
|
|
671
|
+
await new Promise((r2) => setTimeout(r2, delay));
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
throw lastError;
|
|
676
|
+
}
|
|
484
677
|
async function downloadAndExtract(url, destDir, stripComponents = 1, timeoutMs = 180000) {
|
|
485
678
|
const tmp = join(dirname(destDir), `.${basename(destDir)}.download-${Date.now()}-${process.pid}`);
|
|
486
679
|
if (!existsSync(tmp))
|
|
@@ -551,31 +744,31 @@ var init_fs = __esm(() => {
|
|
|
551
744
|
var manifest_default;
|
|
552
745
|
var init_manifest = __esm(() => {
|
|
553
746
|
manifest_default = {
|
|
554
|
-
version: "0.
|
|
747
|
+
version: "0.8.0",
|
|
555
748
|
repo: "JaimeHoracio/Ostacky",
|
|
556
|
-
tag: "v0.
|
|
749
|
+
tag: "v0.8.0",
|
|
557
750
|
agents: [
|
|
558
751
|
{
|
|
559
752
|
name: "ostacky",
|
|
560
753
|
file: "assets/agents/ostacky.md",
|
|
561
|
-
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.
|
|
562
|
-
version: "0.
|
|
563
|
-
sha256: "
|
|
754
|
+
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.0: prune de skills obsoletas, lastHandoff, getAvailableTransitions, consecutiveFailures real, mem_session_summary automático al cierre.",
|
|
755
|
+
version: "0.8.0",
|
|
756
|
+
sha256: "d9f94bb05a33a98d6ff0ea0b56c8eb6c3c8d456d1de1ccaac3f76ee2150b3c7b"
|
|
564
757
|
}
|
|
565
758
|
],
|
|
566
759
|
commands: [
|
|
567
760
|
{
|
|
568
761
|
name: "install-stack",
|
|
569
762
|
file: "assets/commands/install-stack.md",
|
|
570
|
-
description: "Instala el stack tecnológico del proyecto (CodeGraph, skills, OpenSpec, Engram,
|
|
571
|
-
version: "0.
|
|
572
|
-
sha256: "
|
|
763
|
+
description: "Instala el stack tecnológico del proyecto (CodeGraph, skills, OpenSpec, Engram, controller plugin). v0.8.0: controller plugin como alma hard-gate, Context7 removido del stack.",
|
|
764
|
+
version: "0.8.0",
|
|
765
|
+
sha256: "1a8acef8dd59d80aa54fa0febb4bd86a31ed615dea5257ea7fa8ed4fd51836a5"
|
|
573
766
|
},
|
|
574
767
|
{
|
|
575
768
|
name: "opsx-sync",
|
|
576
769
|
file: "assets/commands/opsx-sync.md",
|
|
577
770
|
description: "Sincroniza delta specs del change activo sin inicializar CodeGraph si ya existe índice",
|
|
578
|
-
version: "0.
|
|
771
|
+
version: "0.8.0",
|
|
579
772
|
sha256: "fe0158478f2ca63b315037a85fc1632b77868532e319af6c7384285441767d64"
|
|
580
773
|
}
|
|
581
774
|
],
|
|
@@ -583,15 +776,15 @@ var init_manifest = __esm(() => {
|
|
|
583
776
|
{
|
|
584
777
|
name: "ostacky-controller",
|
|
585
778
|
file: "assets/mcp/ostacky-controller/",
|
|
586
|
-
description: "Máquina de estados persistida con @modelcontextprotocol/server SDK. v0.
|
|
587
|
-
version: "0.
|
|
588
|
-
sha256: "
|
|
779
|
+
description: "Máquina de estados persistida con @modelcontextprotocol/server SDK. v0.8.0: 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.",
|
|
780
|
+
version: "0.8.0",
|
|
781
|
+
sha256: "5c4787001143ad517714e9c44f85b20fac00ba4146631d68486a90b93aa58dcd"
|
|
589
782
|
},
|
|
590
783
|
{
|
|
591
784
|
name: "openspec",
|
|
592
785
|
file: "assets/mcp/openspec/",
|
|
593
786
|
description: "MCP server local para OpenSpec - proposal, apply, archive, sync de cambios",
|
|
594
|
-
version: "0.
|
|
787
|
+
version: "0.8.0",
|
|
595
788
|
sha256: "fa4be28bfc1e75db8f1a037e2580f04a60066c80e8e5934e90e1041020d04609"
|
|
596
789
|
}
|
|
597
790
|
],
|
|
@@ -600,106 +793,106 @@ var init_manifest = __esm(() => {
|
|
|
600
793
|
name: "brainstorming",
|
|
601
794
|
file: "assets/skills/brainstorming/SKILL.md",
|
|
602
795
|
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)",
|
|
603
|
-
version: "0.
|
|
604
|
-
sha256: "
|
|
796
|
+
version: "0.8.0",
|
|
797
|
+
sha256: "87f8d6ed28205f9272792d3865d8cd3232ab5e511a604d6d63d41e3a02a5f422"
|
|
605
798
|
},
|
|
606
799
|
{
|
|
607
800
|
name: "execution-mode-evaluation",
|
|
608
801
|
file: "assets/skills/execution-mode-evaluation/SKILL.md",
|
|
609
802
|
description: "Skill de análisis de modo de ejecución — output reconciliado con controller snapshot contract (recommendation field)",
|
|
610
|
-
version: "0.
|
|
611
|
-
sha256: "
|
|
803
|
+
version: "0.8.0",
|
|
804
|
+
sha256: "7b89dda29927c88f1eb13ffcea541a12423384c172f66c4f3d9b61efe0c7a163"
|
|
612
805
|
},
|
|
613
806
|
{
|
|
614
807
|
name: "tdd",
|
|
615
808
|
file: "assets/skills/tdd/SKILL.md",
|
|
616
809
|
description: "Skill de test-driven development (Superpowers)",
|
|
617
|
-
version: "0.
|
|
810
|
+
version: "0.8.0",
|
|
618
811
|
sha256: "aa412298980b7826165c211145c1b8e9135f68f36247bb3cb96d0a0eae274486"
|
|
619
812
|
},
|
|
620
813
|
{
|
|
621
814
|
name: "subagent-driven-development",
|
|
622
815
|
file: "assets/skills/subagent-driven-development/SKILL.md",
|
|
623
816
|
description: "Skill de ejecución con subagentes (Superpowers) — ejecuta solo después de confirmación del coordinador Ostacky",
|
|
624
|
-
version: "0.
|
|
817
|
+
version: "0.8.0",
|
|
625
818
|
sha256: "1a42d714a9a13faf05f0bf7b580e8d837c2e77a5633839542ccce603023419e8"
|
|
626
819
|
},
|
|
627
820
|
{
|
|
628
821
|
name: "dispatching-parallel-agents",
|
|
629
822
|
file: "assets/skills/dispatching-parallel-agents/SKILL.md",
|
|
630
823
|
description: "Skill de dispatch paralelo de agentes (Superpowers)",
|
|
631
|
-
version: "0.
|
|
824
|
+
version: "0.8.0",
|
|
632
825
|
sha256: "3c8a66d51ae2e719e877d02c3dac32bd804722c3bb9227c258ecae78252a20ab"
|
|
633
826
|
},
|
|
634
827
|
{
|
|
635
828
|
name: "review",
|
|
636
829
|
file: "assets/skills/review/SKILL.md",
|
|
637
830
|
description: "Skill de revisión de código (Superpowers)",
|
|
638
|
-
version: "0.
|
|
831
|
+
version: "0.8.0",
|
|
639
832
|
sha256: "b19650ed4d1d4d9857a4dd5b7e08e91328a1a1fc9c45bcc2fb600fa9d0213279"
|
|
640
833
|
},
|
|
641
834
|
{
|
|
642
835
|
name: "receiving-code-review",
|
|
643
836
|
file: "assets/skills/receiving-code-review/SKILL.md",
|
|
644
837
|
description: "Skill de recibir y procesar feedback de code review",
|
|
645
|
-
version: "0.
|
|
838
|
+
version: "0.8.0",
|
|
646
839
|
sha256: "d761e884e71d8d3476ac734d287cae403a4024506a7d12e7361a448217c0a831"
|
|
647
840
|
},
|
|
648
841
|
{
|
|
649
842
|
name: "openspec-propose",
|
|
650
843
|
file: "assets/skills/openspec-propose/SKILL.md",
|
|
651
844
|
description: "Skill de generación de proposal (OpenSpec)",
|
|
652
|
-
version: "0.
|
|
845
|
+
version: "0.8.0",
|
|
653
846
|
sha256: "bb59100b9fd3c9f9a1ec6509fa975ec55bf7fc737df4fe73bb3e9bbc217b83f7"
|
|
654
847
|
},
|
|
655
848
|
{
|
|
656
849
|
name: "openspec-apply-change",
|
|
657
850
|
file: "assets/skills/openspec-apply-change/SKILL.md",
|
|
658
851
|
description: "Skill de aplicación de change (OpenSpec)",
|
|
659
|
-
version: "0.
|
|
852
|
+
version: "0.8.0",
|
|
660
853
|
sha256: "dfc823bf89fc7505e91ab6dee9c1f004be38a410bd3d88fb67b211b1b6cbb1d0"
|
|
661
854
|
},
|
|
662
855
|
{
|
|
663
856
|
name: "openspec-archive-change",
|
|
664
857
|
file: "assets/skills/openspec-archive-change/SKILL.md",
|
|
665
858
|
description: "Skill de archivo de change (OpenSpec)",
|
|
666
|
-
version: "0.
|
|
859
|
+
version: "0.8.0",
|
|
667
860
|
sha256: "16e4b561de7747283663fed602e506abf502452c49a8d4b86d7a5539c40f0195"
|
|
668
861
|
},
|
|
669
862
|
{
|
|
670
863
|
name: "openspec-explore",
|
|
671
864
|
file: "assets/skills/openspec-explore/SKILL.md",
|
|
672
865
|
description: "Modo explore para OpenSpec — thinking partner para explorar ideas, investigar problemas y clarificar requisitos antes/durante un cambio",
|
|
673
|
-
version: "0.
|
|
866
|
+
version: "0.8.0",
|
|
674
867
|
sha256: "37ae4aaf17ea71a395bab6dc4d9d61b9a49d7910f6692535ae8553244c46880b"
|
|
675
868
|
},
|
|
676
869
|
{
|
|
677
870
|
name: "using-git-worktrees",
|
|
678
871
|
file: "assets/skills/using-git-worktrees/SKILL.md",
|
|
679
872
|
description: "Skill de uso de git worktrees para aislamiento de trabajo",
|
|
680
|
-
version: "0.
|
|
873
|
+
version: "0.8.0",
|
|
681
874
|
sha256: "93341bc1b7c053618a8b6dc07e3615b77990d7b549a0201f5835d95fef67ce13"
|
|
682
875
|
},
|
|
683
876
|
{
|
|
684
877
|
name: "using-superpowers",
|
|
685
878
|
file: "assets/skills/using-superpowers/SKILL.md",
|
|
686
879
|
description: "Skill de orquestación de Superpowers skills",
|
|
687
|
-
version: "0.
|
|
880
|
+
version: "0.8.0",
|
|
688
881
|
sha256: "7e54536f96d2a379185a10bfc1e970850caa2561b6d8aca7080f0defea0381a4"
|
|
689
882
|
},
|
|
690
883
|
{
|
|
691
884
|
name: "writing-skills",
|
|
692
885
|
file: "assets/skills/writing-skills/SKILL.md",
|
|
693
886
|
description: "Skill de creación y edición de skills",
|
|
694
|
-
version: "0.
|
|
887
|
+
version: "0.8.0",
|
|
695
888
|
sha256: "3d76b906cee518a2b809febb70db95697a35b9f368986bb504b4120c3bfb437a"
|
|
696
889
|
},
|
|
697
890
|
{
|
|
698
891
|
name: "graceful-degradation",
|
|
699
892
|
file: "assets/skills/graceful-degradation/SKILL.md",
|
|
700
893
|
description: "Skill de degradación graceful cuando múltiples tools están indisponibles",
|
|
701
|
-
version: "0.
|
|
702
|
-
sha256: "
|
|
894
|
+
version: "0.8.0",
|
|
895
|
+
sha256: "40f929e8032b70d51b3548bddcc383b931d6044fd9d4acce8a5b9f7acd014f59"
|
|
703
896
|
}
|
|
704
897
|
]
|
|
705
898
|
};
|
|
@@ -740,6 +933,19 @@ var init_cache = __esm(() => {
|
|
|
740
933
|
});
|
|
741
934
|
|
|
742
935
|
// src/github.ts
|
|
936
|
+
var exports_github = {};
|
|
937
|
+
__export(exports_github, {
|
|
938
|
+
BUNDLED_MCP_DIR: () => BUNDLED_MCP_DIR,
|
|
939
|
+
BUNDLED_SKILLS_DIR: () => BUNDLED_SKILLS_DIR,
|
|
940
|
+
PACKAGE_ROOT: () => PACKAGE_ROOT,
|
|
941
|
+
downloadFile: () => downloadFile,
|
|
942
|
+
fetchLatestManifest: () => fetchLatestManifest,
|
|
943
|
+
fetchLatestReleaseTag: () => fetchLatestReleaseTag,
|
|
944
|
+
fetchManifest: () => fetchManifest,
|
|
945
|
+
getBundledMcpPath: () => getBundledMcpPath,
|
|
946
|
+
getBundledSkillPath: () => getBundledSkillPath,
|
|
947
|
+
getRawUrl: () => getRawUrl
|
|
948
|
+
});
|
|
743
949
|
import { fileURLToPath } from "url";
|
|
744
950
|
import { dirname as dirname3, join as join3 } from "path";
|
|
745
951
|
function getBundledSkillPath(name) {
|
|
@@ -840,12 +1046,12 @@ var init_github = __esm(() => {
|
|
|
840
1046
|
// src/lockfile.ts
|
|
841
1047
|
var exports_lockfile = {};
|
|
842
1048
|
__export(exports_lockfile, {
|
|
843
|
-
|
|
844
|
-
removeFromLockfile: () => removeFromLockfile,
|
|
845
|
-
readLockfile: () => readLockfile,
|
|
846
|
-
getLockfilePath: () => getLockfilePath,
|
|
1049
|
+
clearLockfile: () => clearLockfile,
|
|
847
1050
|
getInstalledVersion: () => getInstalledVersion,
|
|
848
|
-
|
|
1051
|
+
getLockfilePath: () => getLockfilePath,
|
|
1052
|
+
readLockfile: () => readLockfile,
|
|
1053
|
+
removeFromLockfile: () => removeFromLockfile,
|
|
1054
|
+
writeLockfile: () => writeLockfile
|
|
849
1055
|
});
|
|
850
1056
|
import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
851
1057
|
import { join as join4 } from "path";
|
|
@@ -910,16 +1116,16 @@ var init_lockfile = __esm(() => {
|
|
|
910
1116
|
// src/config.ts
|
|
911
1117
|
var exports_config = {};
|
|
912
1118
|
__export(exports_config, {
|
|
913
|
-
|
|
914
|
-
stripJsoncComments: () => stripJsoncComments,
|
|
915
|
-
setMcpEntryAtProjectRoot: () => setMcpEntryAtProjectRoot,
|
|
916
|
-
setMcpEntry: () => setMcpEntry,
|
|
917
|
-
readOpenCodeConfig: () => readOpenCodeConfig,
|
|
918
|
-
patchOpenCodeConfig: () => patchOpenCodeConfig,
|
|
919
|
-
findOpenCodeConfig: () => findOpenCodeConfig,
|
|
920
|
-
ensureOpenCodeConfig: () => ensureOpenCodeConfig,
|
|
1119
|
+
ensureMcpEntry: () => ensureMcpEntry,
|
|
921
1120
|
ensureMcpEntryAtProjectRoot: () => ensureMcpEntryAtProjectRoot,
|
|
922
|
-
|
|
1121
|
+
ensureOpenCodeConfig: () => ensureOpenCodeConfig,
|
|
1122
|
+
findOpenCodeConfig: () => findOpenCodeConfig,
|
|
1123
|
+
patchOpenCodeConfig: () => patchOpenCodeConfig,
|
|
1124
|
+
readOpenCodeConfig: () => readOpenCodeConfig,
|
|
1125
|
+
setMcpEntry: () => setMcpEntry,
|
|
1126
|
+
setMcpEntryAtProjectRoot: () => setMcpEntryAtProjectRoot,
|
|
1127
|
+
stripJsoncComments: () => stripJsoncComments,
|
|
1128
|
+
writeOpenCodeConfig: () => writeOpenCodeConfig
|
|
923
1129
|
});
|
|
924
1130
|
import { existsSync as existsSync4, readFileSync as readFileSync4, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
925
1131
|
import { join as join5 } from "path";
|
|
@@ -1054,6 +1260,12 @@ function patchOpenCodeConfig(projectRoot = findProjectRoot()) {
|
|
|
1054
1260
|
delete config.plugin;
|
|
1055
1261
|
changed = true;
|
|
1056
1262
|
}
|
|
1263
|
+
if (config.mcp && typeof config.mcp === "object" && "context7" in config.mcp) {
|
|
1264
|
+
delete config.mcp.context7;
|
|
1265
|
+
if (Object.keys(config.mcp).length === 0)
|
|
1266
|
+
delete config.mcp;
|
|
1267
|
+
changed = true;
|
|
1268
|
+
}
|
|
1057
1269
|
if (changed) {
|
|
1058
1270
|
writeOpenCodeConfig(configPath, config);
|
|
1059
1271
|
return { success: true, message: "Config actualizada (plugin legacy eliminado)" };
|
|
@@ -1067,17 +1279,16 @@ var init_config = __esm(() => {
|
|
|
1067
1279
|
// src/stack.ts
|
|
1068
1280
|
var exports_stack = {};
|
|
1069
1281
|
__export(exports_stack, {
|
|
1070
|
-
|
|
1071
|
-
uninstallStackConfig: () => uninstallStackConfig,
|
|
1072
|
-
uninstallEngramConfig: () => uninstallEngramConfig,
|
|
1073
|
-
setupOpenSpec: () => setupOpenSpec,
|
|
1074
|
-
setupContext7: () => setupContext7,
|
|
1075
|
-
installStack: () => installStack,
|
|
1076
|
-
installEngram: () => installEngram,
|
|
1077
|
-
installCodeGraph: () => installCodeGraph,
|
|
1078
|
-
buildLocalMcpCommand: () => buildLocalMcpCommand,
|
|
1282
|
+
OPENSPEC_NPM_PACKAGE: () => OPENSPEC_NPM_PACKAGE,
|
|
1079
1283
|
buildEngramDownloadUrl: () => buildEngramDownloadUrl,
|
|
1080
|
-
|
|
1284
|
+
buildLocalMcpCommand: () => buildLocalMcpCommand,
|
|
1285
|
+
installCodeGraph: () => installCodeGraph,
|
|
1286
|
+
installEngram: () => installEngram,
|
|
1287
|
+
installStack: () => installStack,
|
|
1288
|
+
setupOpenSpec: () => setupOpenSpec,
|
|
1289
|
+
uninstallEngramConfig: () => uninstallEngramConfig,
|
|
1290
|
+
uninstallStackConfig: () => uninstallStackConfig,
|
|
1291
|
+
verifyMcpServers: () => verifyMcpServers
|
|
1081
1292
|
});
|
|
1082
1293
|
import { chmodSync, copyFileSync as copyFileSync3, existsSync as existsSync6, mkdirSync as mkdirSync4, rmSync as rmSync4, unlinkSync as unlinkSync3 } from "fs";
|
|
1083
1294
|
import { basename as basename2, dirname as dirname5, join as join7, resolve as resolve2 } from "path";
|
|
@@ -1127,6 +1338,15 @@ function copyEngramPlugin(projectRoot) {
|
|
|
1127
1338
|
mkdirSync4(pluginsDir, { recursive: true });
|
|
1128
1339
|
copyFileSync3(pluginSource, join7(pluginsDir, "engram.ts"));
|
|
1129
1340
|
}
|
|
1341
|
+
function copyOstackyControllerPlugin(projectRoot) {
|
|
1342
|
+
const pluginSource = join7(PACKAGE_ROOT, "assets", "plugins", "ostacky-plugin.ts");
|
|
1343
|
+
const pluginsDir = join7(projectRoot, ".opencode", "plugins");
|
|
1344
|
+
if (!existsSync6(pluginSource)) {
|
|
1345
|
+
throw new Error(`Plugin bundleado de OstackyController no encontrado: ${pluginSource}`);
|
|
1346
|
+
}
|
|
1347
|
+
mkdirSync4(pluginsDir, { recursive: true });
|
|
1348
|
+
copyFileSync3(pluginSource, join7(pluginsDir, "ostacky-plugin.ts"));
|
|
1349
|
+
}
|
|
1130
1350
|
function buildEngramDownloadUrl(tag, platform = process.platform, arch = process.arch) {
|
|
1131
1351
|
const target = getEngramReleaseTarget(platform, arch);
|
|
1132
1352
|
if (!target)
|
|
@@ -1350,6 +1570,7 @@ async function installEngram(toolsDir) {
|
|
|
1350
1570
|
try {
|
|
1351
1571
|
runTool(localBin, ["--version"], projectRoot, 1e4);
|
|
1352
1572
|
copyEngramPlugin(projectRoot);
|
|
1573
|
+
copyOstackyControllerPlugin(projectRoot);
|
|
1353
1574
|
configureLocalTool(projectRoot, "engram", buildLocalMcpCommand(localBin, ["mcp"]));
|
|
1354
1575
|
} catch (error) {
|
|
1355
1576
|
return failAfterExtraction(`Engram fue instalado pero no se pudo verificar o configurar: ${error.message}`);
|
|
@@ -1357,44 +1578,15 @@ async function installEngram(toolsDir) {
|
|
|
1357
1578
|
archivePromotion?.commit();
|
|
1358
1579
|
return { success: true, message: "Engram instalado localmente y configurado para OpenCode (MCP + plugin)" };
|
|
1359
1580
|
}
|
|
1360
|
-
function setupContext7(toolsDir) {
|
|
1361
|
-
const location = resolveToolInstallLocation(toolsDir);
|
|
1362
|
-
const { projectRoot } = location;
|
|
1363
|
-
const ctx7ToolDir = join7(location.toolsDir, "context7");
|
|
1364
|
-
if (!existsSync6(ctx7ToolDir))
|
|
1365
|
-
mkdirSync4(ctx7ToolDir, { recursive: true });
|
|
1366
|
-
try {
|
|
1367
|
-
ensureMcpEntryAtProjectRoot(projectRoot, "context7", {
|
|
1368
|
-
type: "remote",
|
|
1369
|
-
url: "https://mcp.context7.com/mcp",
|
|
1370
|
-
enabled: true
|
|
1371
|
-
});
|
|
1372
|
-
} catch (error) {
|
|
1373
|
-
return { success: false, message: `No se pudo registrar Context7: ${error.message}` };
|
|
1374
|
-
}
|
|
1375
|
-
const useBun = isCommandAvailable("bun");
|
|
1376
|
-
try {
|
|
1377
|
-
const invocation = getCommandInvocation(useBun ? "bunx" : "npx", useBun ? ["ctx7", "setup", "--opencode"] : ["--yes", "ctx7", "setup", "--opencode"]);
|
|
1378
|
-
execFileSync3(invocation.command, invocation.args, {
|
|
1379
|
-
stdio: "pipe",
|
|
1380
|
-
timeout: 60000,
|
|
1381
|
-
cwd: projectRoot
|
|
1382
|
-
});
|
|
1383
|
-
return { success: true, message: "Context7 configurado (MCP + skill instalado)" };
|
|
1384
|
-
} catch {
|
|
1385
|
-
return {
|
|
1386
|
-
success: true,
|
|
1387
|
-
message: "Context7 MCP registrado (skill opcional no instalada — corrí `npx ctx7 setup --opencode` manualmente si la querés)"
|
|
1388
|
-
};
|
|
1389
|
-
}
|
|
1390
|
-
}
|
|
1391
1581
|
async function installStack(toolsDir) {
|
|
1392
1582
|
const { projectRoot } = resolveToolInstallLocation(toolsDir);
|
|
1583
|
+
try {
|
|
1584
|
+
copyOstackyControllerPlugin(projectRoot);
|
|
1585
|
+
} catch {}
|
|
1393
1586
|
return {
|
|
1394
1587
|
codegraph: await installCodeGraph(toolsDir),
|
|
1395
1588
|
openspec: setupOpenSpec(projectRoot),
|
|
1396
1589
|
engram: await installEngram(toolsDir),
|
|
1397
|
-
context7: setupContext7(toolsDir),
|
|
1398
1590
|
config: patchOpenCodeConfig(projectRoot)
|
|
1399
1591
|
};
|
|
1400
1592
|
}
|
|
@@ -1429,7 +1621,7 @@ function uninstallStackConfig(paths) {
|
|
|
1429
1621
|
const mcp = config.mcp;
|
|
1430
1622
|
let changed = false;
|
|
1431
1623
|
if (mcp) {
|
|
1432
|
-
for (const name of ["codegraph", "
|
|
1624
|
+
for (const name of ["codegraph", "engram", "context7"]) {
|
|
1433
1625
|
if (name in mcp) {
|
|
1434
1626
|
delete mcp[name];
|
|
1435
1627
|
removed.push(`mcp.${name}`);
|
|
@@ -1499,7 +1691,7 @@ var init_stack = __esm(() => {
|
|
|
1499
1691
|
// package.json
|
|
1500
1692
|
var package_default = {
|
|
1501
1693
|
name: "ostacky",
|
|
1502
|
-
version: "0.
|
|
1694
|
+
version: "0.8.0",
|
|
1503
1695
|
description: "Instalador interactivo de agentes y comandos para OpenCode",
|
|
1504
1696
|
type: "module",
|
|
1505
1697
|
bin: {
|
|
@@ -1548,23 +1740,23 @@ var package_default = {
|
|
|
1548
1740
|
// node_modules/@clack/prompts/dist/index.mjs
|
|
1549
1741
|
var exports_dist = {};
|
|
1550
1742
|
__export(exports_dist, {
|
|
1551
|
-
|
|
1552
|
-
text: () => ue,
|
|
1553
|
-
tasks: () => xe,
|
|
1554
|
-
spinner: () => L2,
|
|
1555
|
-
selectKey: () => he,
|
|
1556
|
-
select: () => de,
|
|
1557
|
-
password: () => $e,
|
|
1558
|
-
outro: () => fe,
|
|
1559
|
-
note: () => ye,
|
|
1560
|
-
multiselect: () => pe,
|
|
1561
|
-
log: () => v2,
|
|
1562
|
-
isCancel: () => BD,
|
|
1563
|
-
intro: () => we,
|
|
1564
|
-
groupMultiselect: () => ge,
|
|
1565
|
-
group: () => be,
|
|
1743
|
+
cancel: () => ve,
|
|
1566
1744
|
confirm: () => me,
|
|
1567
|
-
|
|
1745
|
+
group: () => be,
|
|
1746
|
+
groupMultiselect: () => ge,
|
|
1747
|
+
intro: () => we,
|
|
1748
|
+
isCancel: () => BD,
|
|
1749
|
+
log: () => v2,
|
|
1750
|
+
multiselect: () => pe,
|
|
1751
|
+
note: () => ye,
|
|
1752
|
+
outro: () => fe,
|
|
1753
|
+
password: () => $e,
|
|
1754
|
+
select: () => de,
|
|
1755
|
+
selectKey: () => he,
|
|
1756
|
+
spinner: () => L2,
|
|
1757
|
+
tasks: () => xe,
|
|
1758
|
+
text: () => ue,
|
|
1759
|
+
updateSettings: () => hD
|
|
1568
1760
|
});
|
|
1569
1761
|
import { stripVTControlCharacters as T2 } from "node:util";
|
|
1570
1762
|
|
|
@@ -2674,7 +2866,7 @@ async function probeMcpServer(nodeExecutable, serverPath, cwd, statePath, option
|
|
|
2674
2866
|
params: {
|
|
2675
2867
|
protocolVersion: "2025-03-26",
|
|
2676
2868
|
capabilities: {},
|
|
2677
|
-
clientInfo: { name: "ostacky-installer", version: "0.
|
|
2869
|
+
clientInfo: { name: "ostacky-installer", version: "0.8.0" }
|
|
2678
2870
|
}
|
|
2679
2871
|
});
|
|
2680
2872
|
});
|
|
@@ -3188,11 +3380,6 @@ async function doInstallStack(toolsDir, projectRoot) {
|
|
|
3188
3380
|
spin.stop(eng.success ? `✓ ${eng.message}` : `✗ ${eng.message}`);
|
|
3189
3381
|
if (!eng.success)
|
|
3190
3382
|
allOk = false;
|
|
3191
|
-
spin.start("Configurando Context7...");
|
|
3192
|
-
const ctx = setupContext7(toolsDir);
|
|
3193
|
-
spin.stop(ctx.success ? `✓ ${ctx.message}` : `✗ ${ctx.message}`);
|
|
3194
|
-
if (!ctx.success)
|
|
3195
|
-
allOk = false;
|
|
3196
3383
|
spin.start("Verificando configuración...");
|
|
3197
3384
|
const { patchOpenCodeConfig: patchOpenCodeConfig2 } = await Promise.resolve().then(() => (init_config(), exports_config));
|
|
3198
3385
|
const cfg = patchOpenCodeConfig2(resolvedProjectRoot);
|
|
@@ -3209,9 +3396,9 @@ async function doInstallAll(manifest, paths) {
|
|
|
3209
3396
|
let errors = 0;
|
|
3210
3397
|
const isGlobal = isGlobalScope(paths);
|
|
3211
3398
|
if (!isGlobal) {
|
|
3212
|
-
ensureToolDirs(paths.tools, ["codegraph", "engram"
|
|
3399
|
+
ensureToolDirs(paths.tools, ["codegraph", "engram"]);
|
|
3213
3400
|
} else {
|
|
3214
|
-
v2.info("Scope global detectado: el stack (CodeGraph/Engram)
|
|
3401
|
+
v2.info("Scope global detectado: el stack (CodeGraph/Engram) y plugins (ostacky-controller, engram) permanecen siempre en <proyecto>/.opencode — se omite instalación de stack global.");
|
|
3215
3402
|
v2.info("Para instalar el stack, ejecutá 'npx ostacky install-stack --scope local' dentro de cada proyecto.");
|
|
3216
3403
|
}
|
|
3217
3404
|
for (const agent of manifest.agents) {
|
|
@@ -3258,6 +3445,42 @@ async function doInstallAll(manifest, paths) {
|
|
|
3258
3445
|
errors++;
|
|
3259
3446
|
}
|
|
3260
3447
|
}
|
|
3448
|
+
try {
|
|
3449
|
+
const { copyFileSync: copyFileSync4, mkdirSync: mkdirSync5, existsSync: existsSync8 } = await import("fs");
|
|
3450
|
+
const { join: join9 } = await import("path");
|
|
3451
|
+
const { PACKAGE_ROOT: PACKAGE_ROOT2 } = await Promise.resolve().then(() => (init_github(), exports_github));
|
|
3452
|
+
const { findProjectRoot: findProjectRoot3 } = await Promise.resolve().then(() => (init_fs(), exports_fs));
|
|
3453
|
+
const src = join9(PACKAGE_ROOT2, "assets", "plugins", "ostacky-plugin.ts");
|
|
3454
|
+
const dest = join9(paths.plugins, "ostacky-plugin.ts");
|
|
3455
|
+
if (existsSync8(src)) {
|
|
3456
|
+
mkdirSync5(paths.plugins, { recursive: true });
|
|
3457
|
+
copyFileSync4(src, dest);
|
|
3458
|
+
}
|
|
3459
|
+
const legacySrc = join9(PACKAGE_ROOT2, "assets", "plugins", "ostacky-controller.ts");
|
|
3460
|
+
if (!existsSync8(src) && existsSync8(legacySrc)) {
|
|
3461
|
+
mkdirSync5(paths.plugins, { recursive: true });
|
|
3462
|
+
copyFileSync4(legacySrc, join9(paths.plugins, "ostacky-controller.ts"));
|
|
3463
|
+
}
|
|
3464
|
+
const srcEng = join9(PACKAGE_ROOT2, "assets", "plugins", "engram.ts");
|
|
3465
|
+
const destEng = join9(paths.plugins, "engram.ts");
|
|
3466
|
+
if (existsSync8(srcEng)) {
|
|
3467
|
+
mkdirSync5(paths.plugins, { recursive: true });
|
|
3468
|
+
copyFileSync4(srcEng, destEng);
|
|
3469
|
+
}
|
|
3470
|
+
if (isGlobal) {
|
|
3471
|
+
try {
|
|
3472
|
+
const projRoot = findProjectRoot3();
|
|
3473
|
+
const localPlugins = join9(projRoot, ".opencode", "plugins");
|
|
3474
|
+
mkdirSync5(localPlugins, { recursive: true });
|
|
3475
|
+
if (existsSync8(src))
|
|
3476
|
+
copyFileSync4(src, join9(localPlugins, "ostacky-plugin.ts"));
|
|
3477
|
+
else if (existsSync8(legacySrc))
|
|
3478
|
+
copyFileSync4(legacySrc, join9(localPlugins, "ostacky-controller.ts"));
|
|
3479
|
+
if (existsSync8(srcEng))
|
|
3480
|
+
copyFileSync4(srcEng, join9(localPlugins, "engram.ts"));
|
|
3481
|
+
} catch {}
|
|
3482
|
+
}
|
|
3483
|
+
} catch {}
|
|
3261
3484
|
let stackOk = true;
|
|
3262
3485
|
let missingTools = [];
|
|
3263
3486
|
if (isGlobal) {
|
|
@@ -3804,7 +4027,7 @@ async function runInteractiveMenu(scope) {
|
|
|
3804
4027
|
{ value: "command", label: "Instalar command" },
|
|
3805
4028
|
{ value: "skill", label: "Instalar skill" },
|
|
3806
4029
|
{ value: "mcp", label: "Instalar MCP server" },
|
|
3807
|
-
{ value: "stack", label: "Instalar stack de herramientas (CodeGraph, Engram
|
|
4030
|
+
{ value: "stack", label: "Instalar stack de herramientas (CodeGraph, Engram)" },
|
|
3808
4031
|
{ value: "update", label: "Actualizar instalación" },
|
|
3809
4032
|
{ value: "uninstall", label: "Desinstalar" },
|
|
3810
4033
|
{ value: "exit", label: "Salir" }
|
|
@@ -3845,7 +4068,7 @@ async function runInteractiveMenu(scope) {
|
|
|
3845
4068
|
fe("Cancelado.");
|
|
3846
4069
|
break;
|
|
3847
4070
|
}
|
|
3848
|
-
ensureToolDirs(paths.tools, ["codegraph", "engram"
|
|
4071
|
+
ensureToolDirs(paths.tools, ["codegraph", "engram"]);
|
|
3849
4072
|
const stackOk = await doInstallStack(paths.tools, dirname7(paths.root));
|
|
3850
4073
|
if (!stackOk)
|
|
3851
4074
|
process.exitCode = 1;
|
|
@@ -3948,7 +4171,7 @@ async function runInstallStackCommand(scope) {
|
|
|
3948
4171
|
process.exitCode = 1;
|
|
3949
4172
|
return;
|
|
3950
4173
|
}
|
|
3951
|
-
ensureToolDirs(paths.tools, ["codegraph", "engram"
|
|
4174
|
+
ensureToolDirs(paths.tools, ["codegraph", "engram"]);
|
|
3952
4175
|
const stackOk = await doInstallStack(paths.tools, dirname7(paths.root));
|
|
3953
4176
|
if (!stackOk)
|
|
3954
4177
|
process.exitCode = 1;
|
|
@@ -3962,7 +4185,7 @@ async function runUninstallStackCommand(scope) {
|
|
|
3962
4185
|
return;
|
|
3963
4186
|
}
|
|
3964
4187
|
const confirm = await me({
|
|
3965
|
-
message: "¿Remover la configuración del stack (CodeGraph, Engram
|
|
4188
|
+
message: "¿Remover la configuración del stack (CodeGraph, Engram) del proyecto? Los binarios globales no se tocan."
|
|
3966
4189
|
});
|
|
3967
4190
|
onCancel(confirm);
|
|
3968
4191
|
if (!confirm) {
|
|
@@ -4142,19 +4365,19 @@ async function runUninstallMcpCommand(name, scope) {
|
|
|
4142
4365
|
|
|
4143
4366
|
// src/cli.ts
|
|
4144
4367
|
init_fs();
|
|
4145
|
-
import { existsSync as existsSync8, statSync as statSync3, readFileSync as readFileSync6 } from "node:fs";
|
|
4368
|
+
import { existsSync as existsSync8, statSync as statSync3, readFileSync as readFileSync6, readdirSync as readdirSync3 } from "node:fs";
|
|
4146
4369
|
import { join as join11, dirname as dirname8 } from "node:path";
|
|
4147
4370
|
var HELP = `
|
|
4148
4371
|
ostacky — Instalador de agentes, comandos, skills y MCPs para OpenCode
|
|
4149
4372
|
|
|
4150
4373
|
Uso:
|
|
4151
4374
|
npx ostacky [--scope local|global|auto] Menú interactivo (instalación completa, pregunta local vs global, default local)
|
|
4152
|
-
npx ostacky install [--scope local|global|auto] Instalar TODO (agente + skills + MCPs + CodeGraph + OpenSpec + Engram
|
|
4375
|
+
npx ostacky install [--scope local|global|auto] Instalar TODO (agente + skills + MCPs + CodeGraph + OpenSpec + Engram)
|
|
4153
4376
|
npx ostacky add agent [--scope local|global|auto] Agregar agente(s)
|
|
4154
4377
|
npx ostacky add command [--scope ...] Agregar command(s)
|
|
4155
4378
|
npx ostacky add skill [--scope ...] Agregar skill(s)
|
|
4156
4379
|
npx ostacky add mcp [--scope ...] Agregar MCP server(s)
|
|
4157
|
-
npx ostacky install-stack [--scope local|auto] Instalar solo el stack de herramientas (CodeGraph, OpenSpec, Engram
|
|
4380
|
+
npx ostacky install-stack [--scope local|auto] Instalar solo el stack de herramientas (CodeGraph, OpenSpec, Engram) — global bloquea con error
|
|
4158
4381
|
npx ostacky uninstall-stack [--scope local|global|auto] Remover la configuración del stack del proyecto
|
|
4159
4382
|
npx ostacky doctor Diagnostica locks, tools, state health
|
|
4160
4383
|
npx ostacky status [--json] Muestra estado del controller sin MCP
|
|
@@ -4173,7 +4396,7 @@ Scope:
|
|
|
4173
4396
|
--scope auto Elige local si existe .opencode o .git, si no global
|
|
4174
4397
|
Sin flag Pregunta interactiva local (default) vs global
|
|
4175
4398
|
`.trim();
|
|
4176
|
-
function
|
|
4399
|
+
function parseScopeArg2(argv = process.argv) {
|
|
4177
4400
|
for (let i = 0;i < argv.length; i++) {
|
|
4178
4401
|
const arg = argv[i];
|
|
4179
4402
|
if (arg === "--scope" && i + 1 < argv.length) {
|
|
@@ -4203,7 +4426,7 @@ function withoutScopeArgs(argv) {
|
|
|
4203
4426
|
}
|
|
4204
4427
|
return out;
|
|
4205
4428
|
}
|
|
4206
|
-
var scope =
|
|
4429
|
+
var scope = parseScopeArg2();
|
|
4207
4430
|
var argvNoScope = withoutScopeArgs(process.argv);
|
|
4208
4431
|
var [, , cmd, subcmd] = argvNoScope;
|
|
4209
4432
|
async function runDoctorCommand() {
|
|
@@ -4223,14 +4446,30 @@ async function runDoctorCommand() {
|
|
|
4223
4446
|
hasError = true;
|
|
4224
4447
|
}
|
|
4225
4448
|
};
|
|
4449
|
+
const pluginPaths = [
|
|
4450
|
+
join11(cwd, "assets", "plugins", "ostacky-plugin.ts"),
|
|
4451
|
+
join11(opencodeDir, "plugins", "ostacky-plugin.ts"),
|
|
4452
|
+
join11(cwd, ".opencode", "plugins", "ostacky-plugin.ts"),
|
|
4453
|
+
join11(cwd, "assets", "plugins", "ostacky-controller.ts"),
|
|
4454
|
+
join11(opencodeDir, "plugins", "ostacky-controller.ts"),
|
|
4455
|
+
join11(cwd, ".opencode", "plugins", "ostacky-controller.ts")
|
|
4456
|
+
];
|
|
4457
|
+
const pluginActive = pluginPaths.some((p2) => existsSync8(p2));
|
|
4226
4458
|
try {
|
|
4227
4459
|
if (!existsSync8(statePath)) {
|
|
4228
|
-
|
|
4460
|
+
if (pluginActive)
|
|
4461
|
+
console.log(`✅ controller: plugin active (no state yet)`);
|
|
4462
|
+
else
|
|
4463
|
+
check("controller: state file missing", false, true);
|
|
4229
4464
|
} else {
|
|
4230
4465
|
const stat = statSync3(statePath);
|
|
4231
4466
|
const raw = readFileSync6(statePath, "utf-8");
|
|
4232
4467
|
const parsed = JSON.parse(raw);
|
|
4233
|
-
|
|
4468
|
+
if (pluginActive) {
|
|
4469
|
+
console.log(`✅ controller: plugin active (rev ${parsed.revision || 0} state ${parsed.state || "unknown"})`);
|
|
4470
|
+
} else {
|
|
4471
|
+
check(`controller: OK (rev ${parsed.revision || 0} state ${parsed.state || "unknown"})`, true);
|
|
4472
|
+
}
|
|
4234
4473
|
if (parsed.degraded) {
|
|
4235
4474
|
console.log("⚠️ degraded: true (persistido)");
|
|
4236
4475
|
hasWarn = true;
|
|
@@ -4243,6 +4482,10 @@ async function runDoctorCommand() {
|
|
|
4243
4482
|
console.log(`⚠️ stateOversizedCount=${parsed.stateOversizedCount} snapshots perdidos`);
|
|
4244
4483
|
if (parsed.sensitiveAccess)
|
|
4245
4484
|
console.log(`ℹ️ sensitiveAccess: allowed=${parsed.sensitiveAccess.allowed || 0} denied=${parsed.sensitiveAccess.denied || 0} blocked=${parsed.sensitiveAccess.blockedAttempts || 0}`);
|
|
4485
|
+
if (parsed.sensitivePatterns)
|
|
4486
|
+
console.log(`ℹ️ sensitivePatterns: ${parsed.sensitivePatterns.join(", ")}`);
|
|
4487
|
+
if (parsed.allowedFiles && Object.keys(parsed.allowedFiles).length)
|
|
4488
|
+
console.log(`ℹ️ allowedFiles: ${Object.keys(parsed.allowedFiles).join(", ")}`);
|
|
4246
4489
|
if (parsed.deniedFiles && Object.keys(parsed.deniedFiles).length) {
|
|
4247
4490
|
console.log(`ℹ️ denied files: ${Object.keys(parsed.deniedFiles).join(", ")} (denied by user)`);
|
|
4248
4491
|
}
|
|
@@ -4330,6 +4573,60 @@ async function runDoctorCommand() {
|
|
|
4330
4573
|
if (s.allowedFiles || s.deniedFiles) {}
|
|
4331
4574
|
} catch {}
|
|
4332
4575
|
}
|
|
4576
|
+
try {
|
|
4577
|
+
const cacheDir = join11(opencodeDir, "cache", "codegraph");
|
|
4578
|
+
if (!existsSync8(cacheDir)) {
|
|
4579
|
+
console.log("ℹ️ cache: no cache dir yet (ok)");
|
|
4580
|
+
} else {
|
|
4581
|
+
const files = readdirSync3(cacheDir);
|
|
4582
|
+
let total = 0;
|
|
4583
|
+
for (const f2 of files) {
|
|
4584
|
+
try {
|
|
4585
|
+
total += statSync3(join11(cacheDir, f2)).size;
|
|
4586
|
+
} catch {}
|
|
4587
|
+
}
|
|
4588
|
+
const totalMB = (total / 1048576).toFixed(2);
|
|
4589
|
+
if (total > 52428800) {
|
|
4590
|
+
console.log(`⚠️ cache: ${totalMB}MB >50MB — LRU cleanup needed`);
|
|
4591
|
+
hasWarn = true;
|
|
4592
|
+
} else {
|
|
4593
|
+
console.log(`✅ cache: OK (${files.length} files, ${totalMB}MB)`);
|
|
4594
|
+
}
|
|
4595
|
+
try {
|
|
4596
|
+
const s = JSON.parse(readFileSync6(statePath, "utf-8"));
|
|
4597
|
+
if (s.cacheHitCount !== undefined) {
|
|
4598
|
+
console.log(`ℹ️ cacheHitCount=${s.cacheHitCount} cacheMissCount=${s.cacheMissCount || 0} tokenSavingEstimate=${s.tokenSavingEstimate || 0}`);
|
|
4599
|
+
}
|
|
4600
|
+
} catch {}
|
|
4601
|
+
}
|
|
4602
|
+
} catch (e2) {
|
|
4603
|
+
console.log(`⚠️ cache: check failed ${e2.message}`);
|
|
4604
|
+
}
|
|
4605
|
+
try {
|
|
4606
|
+
const secPath = join11(cwd, "src", "security.ts");
|
|
4607
|
+
if (!existsSync8(secPath)) {
|
|
4608
|
+
console.log("⚠️ src/security.ts: missing (source-of-truth)");
|
|
4609
|
+
hasWarn = true;
|
|
4610
|
+
} else {
|
|
4611
|
+
const sec = readFileSync6(secPath, "utf-8");
|
|
4612
|
+
const hasSensitiveDefault = sec.includes("SENSITIVE_DEFAULT");
|
|
4613
|
+
const hasBashRe = sec.includes("BASH_SENSITIVE_RE");
|
|
4614
|
+
const hasIsSensitive = sec.includes("function isSensitive");
|
|
4615
|
+
const hasExtract = sec.includes("extractPathsFromBash");
|
|
4616
|
+
if (hasSensitiveDefault && hasBashRe && hasIsSensitive && hasExtract) {
|
|
4617
|
+
console.log("✅ src/security.ts: source-of-truth OK");
|
|
4618
|
+
} else {
|
|
4619
|
+
console.log("⚠️ src/security.ts: missing exports (SENSITIVE_DEFAULT/BASH_SENSITIVE_RE/isSensitive/extractPathsFromBash)");
|
|
4620
|
+
hasWarn = true;
|
|
4621
|
+
}
|
|
4622
|
+
}
|
|
4623
|
+
} catch {}
|
|
4624
|
+
try {
|
|
4625
|
+
const s = JSON.parse(readFileSync6(statePath, "utf-8"));
|
|
4626
|
+
if (s.sensitiveAccess?.blockedAttempts > 0) {
|
|
4627
|
+
console.log(`ℹ️ sensitiveAccess: blockedAttempts includes bash (${s.sensitiveAccess.blockedAttempts})`);
|
|
4628
|
+
}
|
|
4629
|
+
} catch {}
|
|
4333
4630
|
if (hasError)
|
|
4334
4631
|
process.exit(1);
|
|
4335
4632
|
if (hasWarn)
|