ostacky 0.7.0 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -11
- package/assets/agents/ostacky.md +18 -11
- package/assets/commands/install-stack.md +20 -1
- package/assets/mcp/ostacky-controller/index.js +523 -120
- package/assets/mcp/ostacky-controller/package.json +1 -1
- package/assets/plugins/engram.ts +82 -8
- package/assets/skills/graceful-degradation/SKILL.md +4 -0
- package/dist/cli.js +370 -158
- package/manifest.json +29 -29
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
2
3
|
var __create = Object.create;
|
|
3
4
|
var __getProtoOf = Object.getPrototypeOf;
|
|
4
5
|
var __defProp = Object.defineProperty;
|
|
@@ -44,6 +45,7 @@ var __export = (target, all) => {
|
|
|
44
45
|
});
|
|
45
46
|
};
|
|
46
47
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
48
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
47
49
|
|
|
48
50
|
// node_modules/sisteransi/src/index.js
|
|
49
51
|
var require_src = __commonJS((exports, module) => {
|
|
@@ -215,8 +217,9 @@ import {
|
|
|
215
217
|
renameSync
|
|
216
218
|
} from "fs";
|
|
217
219
|
import { createHash as createHash2 } from "crypto";
|
|
218
|
-
import { join, resolve, dirname, relative, basename } from "path";
|
|
220
|
+
import { join, resolve, dirname, relative, basename, win32 } from "path";
|
|
219
221
|
import { execFileSync } from "child_process";
|
|
222
|
+
import { homedir } from "os";
|
|
220
223
|
function findOpenCodeDir(startDir = process.cwd()) {
|
|
221
224
|
let current = resolve(startDir);
|
|
222
225
|
while (true) {
|
|
@@ -243,6 +246,32 @@ function findProjectRoot(startDir = process.cwd()) {
|
|
|
243
246
|
current = parent;
|
|
244
247
|
}
|
|
245
248
|
}
|
|
249
|
+
function getGlobalOpenCodeDir(platform = process.platform, home = homedir()) {
|
|
250
|
+
if (platform === "win32") {
|
|
251
|
+
const appData = process.env.APPDATA ?? win32.join(home, "AppData", "Roaming");
|
|
252
|
+
return win32.join(appData, "opencode");
|
|
253
|
+
}
|
|
254
|
+
const xdg = process.env.XDG_CONFIG_HOME ?? join(home, ".config");
|
|
255
|
+
return join(xdg, "opencode");
|
|
256
|
+
}
|
|
257
|
+
function getOpenCodeDirForScope(scope, cwd = process.cwd()) {
|
|
258
|
+
if (scope === "global")
|
|
259
|
+
return getGlobalOpenCodeDir();
|
|
260
|
+
if (scope === "local") {
|
|
261
|
+
const existing2 = findOpenCodeDir(cwd);
|
|
262
|
+
if (existing2)
|
|
263
|
+
return existing2;
|
|
264
|
+
return join(findProjectRoot(cwd), ".opencode");
|
|
265
|
+
}
|
|
266
|
+
const existing = findOpenCodeDir(cwd);
|
|
267
|
+
if (existing)
|
|
268
|
+
return existing;
|
|
269
|
+
const root = findProjectRoot(cwd);
|
|
270
|
+
if (existsSync(join(root, ".opencode")) || existsSync(join(root, ".git"))) {
|
|
271
|
+
return join(root, ".opencode");
|
|
272
|
+
}
|
|
273
|
+
return getGlobalOpenCodeDir();
|
|
274
|
+
}
|
|
246
275
|
function ensureOpenCodePaths(opencodeDir) {
|
|
247
276
|
const paths = {
|
|
248
277
|
root: opencodeDir,
|
|
@@ -266,9 +295,6 @@ function ensureToolDirs(toolsDir, toolNames) {
|
|
|
266
295
|
mkdirSync(dir, { recursive: true });
|
|
267
296
|
}
|
|
268
297
|
}
|
|
269
|
-
function createOpenCodeDir(baseDir) {
|
|
270
|
-
return ensureOpenCodePaths(join(baseDir, ".opencode"));
|
|
271
|
-
}
|
|
272
298
|
function copyDirRecursive(src, dest, skipGenerated = false) {
|
|
273
299
|
if (!existsSync(dest))
|
|
274
300
|
mkdirSync(dest, { recursive: true });
|
|
@@ -451,7 +477,7 @@ function downloadToFile(url, dest, timeoutMs = 180000) {
|
|
|
451
477
|
});
|
|
452
478
|
}
|
|
453
479
|
async function downloadAndExtract(url, destDir, stripComponents = 1, timeoutMs = 180000) {
|
|
454
|
-
const tmp = join(dirname(destDir), `.${basename(destDir)}.download-${Date.now()}`);
|
|
480
|
+
const tmp = join(dirname(destDir), `.${basename(destDir)}.download-${Date.now()}-${process.pid}`);
|
|
455
481
|
if (!existsSync(tmp))
|
|
456
482
|
mkdirSync(tmp, { recursive: true });
|
|
457
483
|
const archivePath = join(tmp, url.endsWith(".zip") ? "archive.zip" : "archive.tar.gz");
|
|
@@ -520,31 +546,31 @@ var init_fs = __esm(() => {
|
|
|
520
546
|
var manifest_default;
|
|
521
547
|
var init_manifest = __esm(() => {
|
|
522
548
|
manifest_default = {
|
|
523
|
-
version: "0.7.
|
|
549
|
+
version: "0.7.2",
|
|
524
550
|
repo: "JaimeHoracio/Ostacky",
|
|
525
|
-
tag: "v0.7.
|
|
551
|
+
tag: "v0.7.2",
|
|
526
552
|
agents: [
|
|
527
553
|
{
|
|
528
554
|
name: "ostacky",
|
|
529
555
|
file: "assets/agents/ostacky.md",
|
|
530
|
-
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.7.
|
|
531
|
-
version: "0.7.
|
|
532
|
-
sha256: "
|
|
556
|
+
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.7.2: prune de skills obsoletas, lastHandoff, getAvailableTransitions, consecutiveFailures real, mem_session_summary automático al cierre.",
|
|
557
|
+
version: "0.7.2",
|
|
558
|
+
sha256: "1b6a5cbb49f1c6e977241f8ab86d1a045fdb8a04111bbc75633429a131c71acd"
|
|
533
559
|
}
|
|
534
560
|
],
|
|
535
561
|
commands: [
|
|
536
562
|
{
|
|
537
563
|
name: "install-stack",
|
|
538
564
|
file: "assets/commands/install-stack.md",
|
|
539
|
-
description: "Instala el stack tecnológico del proyecto (CodeGraph, skills, OpenSpec, Engram, Context7, controller MCP con SDK oficial). v0.7.
|
|
540
|
-
version: "0.7.
|
|
541
|
-
sha256: "
|
|
565
|
+
description: "Instala el stack tecnológico del proyecto (CodeGraph, skills, OpenSpec, Engram, Context7, controller MCP con SDK oficial). v0.7.2: Paso 1.5 controller expandido con verificación post-instalación y troubleshooting.",
|
|
566
|
+
version: "0.7.2",
|
|
567
|
+
sha256: "7277d333bfe65e674b9696fa5f887854e35824eb5b88420dd0f5bbde3bca7add"
|
|
542
568
|
},
|
|
543
569
|
{
|
|
544
570
|
name: "opsx-sync",
|
|
545
571
|
file: "assets/commands/opsx-sync.md",
|
|
546
572
|
description: "Sincroniza delta specs del change activo sin inicializar CodeGraph si ya existe índice",
|
|
547
|
-
version: "0.7.
|
|
573
|
+
version: "0.7.2",
|
|
548
574
|
sha256: "fe0158478f2ca63b315037a85fc1632b77868532e319af6c7384285441767d64"
|
|
549
575
|
}
|
|
550
576
|
],
|
|
@@ -552,15 +578,15 @@ var init_manifest = __esm(() => {
|
|
|
552
578
|
{
|
|
553
579
|
name: "ostacky-controller",
|
|
554
580
|
file: "assets/mcp/ostacky-controller/",
|
|
555
|
-
description: "Máquina de estados persistida con @modelcontextprotocol/server SDK. v0.7.
|
|
556
|
-
version: "0.7.
|
|
557
|
-
sha256: "
|
|
581
|
+
description: "Máquina de estados persistida con @modelcontextprotocol/server SDK. v0.7.2: 22 tools (incluye set_handoff, get_handoff, clear_handoff, get_available_transitions funcional). Bugfixes: consecutiveFailures real, lastHandoff state, defaultChoice persistido. Robustez: degraded mode automático tras 3 fallos, persistence condicional para Nivel 0, prune de skills obsoletas.",
|
|
582
|
+
version: "0.7.2",
|
|
583
|
+
sha256: "34048819b8905dff24c731e02d813deb4fb7fbbb65e8b8df66e5c8db201492a0"
|
|
558
584
|
},
|
|
559
585
|
{
|
|
560
586
|
name: "openspec",
|
|
561
587
|
file: "assets/mcp/openspec/",
|
|
562
588
|
description: "MCP server local para OpenSpec - proposal, apply, archive, sync de cambios",
|
|
563
|
-
version: "0.7.
|
|
589
|
+
version: "0.7.2",
|
|
564
590
|
sha256: "fa4be28bfc1e75db8f1a037e2580f04a60066c80e8e5934e90e1041020d04609"
|
|
565
591
|
}
|
|
566
592
|
],
|
|
@@ -569,106 +595,106 @@ var init_manifest = __esm(() => {
|
|
|
569
595
|
name: "brainstorming",
|
|
570
596
|
file: "assets/skills/brainstorming/SKILL.md",
|
|
571
597
|
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)",
|
|
572
|
-
version: "0.7.
|
|
598
|
+
version: "0.7.2",
|
|
573
599
|
sha256: "333810ca63450e8ab795e8c5a58e645101e788d79210ca8ac36e10d11be39585"
|
|
574
600
|
},
|
|
575
601
|
{
|
|
576
602
|
name: "execution-mode-evaluation",
|
|
577
603
|
file: "assets/skills/execution-mode-evaluation/SKILL.md",
|
|
578
604
|
description: "Skill de análisis de modo de ejecución — output reconciliado con controller snapshot contract (recommendation field)",
|
|
579
|
-
version: "0.7.
|
|
605
|
+
version: "0.7.2",
|
|
580
606
|
sha256: "edf6fca0af52cce83f8fd08e7db9b3d79cc130b53fca505e90d3fbaf4909338b"
|
|
581
607
|
},
|
|
582
608
|
{
|
|
583
609
|
name: "tdd",
|
|
584
610
|
file: "assets/skills/tdd/SKILL.md",
|
|
585
611
|
description: "Skill de test-driven development (Superpowers)",
|
|
586
|
-
version: "0.7.
|
|
612
|
+
version: "0.7.2",
|
|
587
613
|
sha256: "aa412298980b7826165c211145c1b8e9135f68f36247bb3cb96d0a0eae274486"
|
|
588
614
|
},
|
|
589
615
|
{
|
|
590
616
|
name: "subagent-driven-development",
|
|
591
617
|
file: "assets/skills/subagent-driven-development/SKILL.md",
|
|
592
618
|
description: "Skill de ejecución con subagentes (Superpowers) — ejecuta solo después de confirmación del coordinador Ostacky",
|
|
593
|
-
version: "0.7.
|
|
619
|
+
version: "0.7.2",
|
|
594
620
|
sha256: "1a42d714a9a13faf05f0bf7b580e8d837c2e77a5633839542ccce603023419e8"
|
|
595
621
|
},
|
|
596
622
|
{
|
|
597
623
|
name: "dispatching-parallel-agents",
|
|
598
624
|
file: "assets/skills/dispatching-parallel-agents/SKILL.md",
|
|
599
625
|
description: "Skill de dispatch paralelo de agentes (Superpowers)",
|
|
600
|
-
version: "0.7.
|
|
626
|
+
version: "0.7.2",
|
|
601
627
|
sha256: "3c8a66d51ae2e719e877d02c3dac32bd804722c3bb9227c258ecae78252a20ab"
|
|
602
628
|
},
|
|
603
629
|
{
|
|
604
630
|
name: "review",
|
|
605
631
|
file: "assets/skills/review/SKILL.md",
|
|
606
632
|
description: "Skill de revisión de código (Superpowers)",
|
|
607
|
-
version: "0.7.
|
|
633
|
+
version: "0.7.2",
|
|
608
634
|
sha256: "b19650ed4d1d4d9857a4dd5b7e08e91328a1a1fc9c45bcc2fb600fa9d0213279"
|
|
609
635
|
},
|
|
610
636
|
{
|
|
611
637
|
name: "receiving-code-review",
|
|
612
638
|
file: "assets/skills/receiving-code-review/SKILL.md",
|
|
613
639
|
description: "Skill de recibir y procesar feedback de code review",
|
|
614
|
-
version: "0.7.
|
|
640
|
+
version: "0.7.2",
|
|
615
641
|
sha256: "d761e884e71d8d3476ac734d287cae403a4024506a7d12e7361a448217c0a831"
|
|
616
642
|
},
|
|
617
643
|
{
|
|
618
644
|
name: "openspec-propose",
|
|
619
645
|
file: "assets/skills/openspec-propose/SKILL.md",
|
|
620
646
|
description: "Skill de generación de proposal (OpenSpec)",
|
|
621
|
-
version: "0.7.
|
|
647
|
+
version: "0.7.2",
|
|
622
648
|
sha256: "bb59100b9fd3c9f9a1ec6509fa975ec55bf7fc737df4fe73bb3e9bbc217b83f7"
|
|
623
649
|
},
|
|
624
650
|
{
|
|
625
651
|
name: "openspec-apply-change",
|
|
626
652
|
file: "assets/skills/openspec-apply-change/SKILL.md",
|
|
627
653
|
description: "Skill de aplicación de change (OpenSpec)",
|
|
628
|
-
version: "0.7.
|
|
654
|
+
version: "0.7.2",
|
|
629
655
|
sha256: "dfc823bf89fc7505e91ab6dee9c1f004be38a410bd3d88fb67b211b1b6cbb1d0"
|
|
630
656
|
},
|
|
631
657
|
{
|
|
632
658
|
name: "openspec-archive-change",
|
|
633
659
|
file: "assets/skills/openspec-archive-change/SKILL.md",
|
|
634
660
|
description: "Skill de archivo de change (OpenSpec)",
|
|
635
|
-
version: "0.7.
|
|
661
|
+
version: "0.7.2",
|
|
636
662
|
sha256: "16e4b561de7747283663fed602e506abf502452c49a8d4b86d7a5539c40f0195"
|
|
637
663
|
},
|
|
638
664
|
{
|
|
639
665
|
name: "openspec-explore",
|
|
640
666
|
file: "assets/skills/openspec-explore/SKILL.md",
|
|
641
667
|
description: "Modo explore para OpenSpec — thinking partner para explorar ideas, investigar problemas y clarificar requisitos antes/durante un cambio",
|
|
642
|
-
version: "0.7.
|
|
668
|
+
version: "0.7.2",
|
|
643
669
|
sha256: "37ae4aaf17ea71a395bab6dc4d9d61b9a49d7910f6692535ae8553244c46880b"
|
|
644
670
|
},
|
|
645
671
|
{
|
|
646
672
|
name: "using-git-worktrees",
|
|
647
673
|
file: "assets/skills/using-git-worktrees/SKILL.md",
|
|
648
674
|
description: "Skill de uso de git worktrees para aislamiento de trabajo",
|
|
649
|
-
version: "0.7.
|
|
675
|
+
version: "0.7.2",
|
|
650
676
|
sha256: "11afa9123828fc5caa22137aff907cd99c751e99027fbd4c020751110a949281"
|
|
651
677
|
},
|
|
652
678
|
{
|
|
653
679
|
name: "using-superpowers",
|
|
654
680
|
file: "assets/skills/using-superpowers/SKILL.md",
|
|
655
681
|
description: "Skill de orquestación de Superpowers skills",
|
|
656
|
-
version: "0.7.
|
|
682
|
+
version: "0.7.2",
|
|
657
683
|
sha256: "7e54536f96d2a379185a10bfc1e970850caa2561b6d8aca7080f0defea0381a4"
|
|
658
684
|
},
|
|
659
685
|
{
|
|
660
686
|
name: "writing-skills",
|
|
661
687
|
file: "assets/skills/writing-skills/SKILL.md",
|
|
662
688
|
description: "Skill de creación y edición de skills",
|
|
663
|
-
version: "0.7.
|
|
689
|
+
version: "0.7.2",
|
|
664
690
|
sha256: "3d76b906cee518a2b809febb70db95697a35b9f368986bb504b4120c3bfb437a"
|
|
665
691
|
},
|
|
666
692
|
{
|
|
667
693
|
name: "graceful-degradation",
|
|
668
694
|
file: "assets/skills/graceful-degradation/SKILL.md",
|
|
669
695
|
description: "Skill de degradación graceful cuando múltiples tools están indisponibles",
|
|
670
|
-
version: "0.7.
|
|
671
|
-
sha256: "
|
|
696
|
+
version: "0.7.2",
|
|
697
|
+
sha256: "376dc90fa27ab7e5947747f20ccf2cc76f9326c77b567fda388252a60d25c431"
|
|
672
698
|
}
|
|
673
699
|
]
|
|
674
700
|
};
|
|
@@ -1045,7 +1071,8 @@ __export(exports_stack, {
|
|
|
1045
1071
|
installEngram: () => installEngram,
|
|
1046
1072
|
installCodeGraph: () => installCodeGraph,
|
|
1047
1073
|
buildLocalMcpCommand: () => buildLocalMcpCommand,
|
|
1048
|
-
buildEngramDownloadUrl: () => buildEngramDownloadUrl
|
|
1074
|
+
buildEngramDownloadUrl: () => buildEngramDownloadUrl,
|
|
1075
|
+
OPENSPEC_NPM_PACKAGE: () => OPENSPEC_NPM_PACKAGE
|
|
1049
1076
|
});
|
|
1050
1077
|
import { chmodSync, copyFileSync as copyFileSync3, existsSync as existsSync6, mkdirSync as mkdirSync4, rmSync as rmSync4, unlinkSync as unlinkSync3 } from "fs";
|
|
1051
1078
|
import { basename as basename2, dirname as dirname5, join as join7, resolve as resolve2 } from "path";
|
|
@@ -1185,20 +1212,56 @@ async function installCodeGraph(toolsDir) {
|
|
|
1185
1212
|
}
|
|
1186
1213
|
function setupOpenSpec(projectRoot = findProjectRoot()) {
|
|
1187
1214
|
const useBun = isCommandAvailable("bun");
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1215
|
+
const fail = (msg) => ({ success: false, message: msg });
|
|
1216
|
+
const tryDirect = () => {
|
|
1217
|
+
try {
|
|
1218
|
+
const invocation = getCommandInvocation(useBun ? "bunx" : "npx", useBun ? [OPENSPEC_NPM_PACKAGE, "init", "--tools", "opencode", "--force"] : ["--yes", OPENSPEC_NPM_PACKAGE, "init", "--tools", "opencode", "--force"]);
|
|
1219
|
+
execFileSync3(invocation.command, invocation.args, {
|
|
1220
|
+
stdio: "pipe",
|
|
1221
|
+
timeout: 120000,
|
|
1222
|
+
cwd: projectRoot
|
|
1223
|
+
});
|
|
1224
|
+
return true;
|
|
1225
|
+
} catch {
|
|
1226
|
+
return false;
|
|
1227
|
+
}
|
|
1228
|
+
};
|
|
1229
|
+
const tryInstallGlobal = () => {
|
|
1230
|
+
if (!useBun && !isCommandAvailable("npm")) {
|
|
1231
|
+
return { ok: false, error: "Ni bun ni npm están disponibles para instalar OpenSpec globalmente" };
|
|
1232
|
+
}
|
|
1233
|
+
const pkgManager = useBun ? "bun" : "npm";
|
|
1234
|
+
const installArgs = useBun ? ["add", "-g", OPENSPEC_NPM_PACKAGE] : ["install", "-g", OPENSPEC_NPM_PACKAGE];
|
|
1235
|
+
try {
|
|
1236
|
+
const invocation = getCommandInvocation(pkgManager, installArgs);
|
|
1237
|
+
execFileSync3(invocation.command, invocation.args, { stdio: "pipe", timeout: 120000 });
|
|
1238
|
+
} catch (installErr) {
|
|
1239
|
+
return { ok: false, error: `Instalación global falló: ${installErr.message}` };
|
|
1240
|
+
}
|
|
1241
|
+
try {
|
|
1242
|
+
const retryInvocation = getCommandInvocation("openspec", ["init", "--tools", "opencode", "--force"]);
|
|
1243
|
+
execFileSync3(retryInvocation.command, retryInvocation.args, {
|
|
1244
|
+
stdio: "pipe",
|
|
1245
|
+
timeout: 120000,
|
|
1246
|
+
cwd: projectRoot
|
|
1247
|
+
});
|
|
1248
|
+
return { ok: true };
|
|
1249
|
+
} catch (retryErr) {
|
|
1250
|
+
return { ok: false, error: `Reintento con binario global falló: ${retryErr.message}` };
|
|
1251
|
+
}
|
|
1252
|
+
};
|
|
1253
|
+
if (tryDirect()) {
|
|
1195
1254
|
return { success: true, message: "OpenSpec configurado para OpenCode" };
|
|
1196
|
-
}
|
|
1255
|
+
}
|
|
1256
|
+
const fallback = tryInstallGlobal();
|
|
1257
|
+
if (fallback.ok) {
|
|
1197
1258
|
return {
|
|
1198
|
-
success:
|
|
1199
|
-
message: `
|
|
1259
|
+
success: true,
|
|
1260
|
+
message: `OpenSpec configurado para OpenCode (binario instalado globalmente con ${useBun ? "bun" : "npm"})`
|
|
1200
1261
|
};
|
|
1201
1262
|
}
|
|
1263
|
+
const manualCmd = useBun ? `bun add -g ${OPENSPEC_NPM_PACKAGE}` : `npm install -g ${OPENSPEC_NPM_PACKAGE}`;
|
|
1264
|
+
return fail(`Error configurando OpenSpec: ni bunx/npx ni la instalación global resolvieron el binario. ` + `Solución manual: ejecuta \`${manualCmd}\` y luego corré /install-stack de nuevo. ` + `Detalle: ${fallback.error}`);
|
|
1202
1265
|
}
|
|
1203
1266
|
async function installEngram(toolsDir) {
|
|
1204
1267
|
const location = resolveToolInstallLocation(toolsDir);
|
|
@@ -1422,6 +1485,7 @@ function verifyMcpServers(projectRoot) {
|
|
|
1422
1485
|
}
|
|
1423
1486
|
return result;
|
|
1424
1487
|
}
|
|
1488
|
+
var OPENSPEC_NPM_PACKAGE = "@fission-ai/openspec";
|
|
1425
1489
|
var init_stack = __esm(() => {
|
|
1426
1490
|
init_fs();
|
|
1427
1491
|
init_config();
|
|
@@ -1430,7 +1494,7 @@ var init_stack = __esm(() => {
|
|
|
1430
1494
|
// package.json
|
|
1431
1495
|
var package_default = {
|
|
1432
1496
|
name: "ostacky",
|
|
1433
|
-
version: "0.7.
|
|
1497
|
+
version: "0.7.2",
|
|
1434
1498
|
description: "Instalador interactivo de agentes y comandos para OpenCode",
|
|
1435
1499
|
type: "module",
|
|
1436
1500
|
bin: {
|
|
@@ -2430,6 +2494,7 @@ import { dirname as dirname7 } from "path";
|
|
|
2430
2494
|
init_github();
|
|
2431
2495
|
init_fs();
|
|
2432
2496
|
init_lockfile();
|
|
2497
|
+
import { homedir as homedir2 } from "os";
|
|
2433
2498
|
|
|
2434
2499
|
// src/installer.ts
|
|
2435
2500
|
init_github();
|
|
@@ -2486,7 +2551,13 @@ function validateMcpModule(nodeExecutable, serverPath, cwd) {
|
|
|
2486
2551
|
throw new Error(`El MCP no pasó la validación de carga: ${error.message}`);
|
|
2487
2552
|
}
|
|
2488
2553
|
}
|
|
2489
|
-
|
|
2554
|
+
var DEFAULT_PROBE_OPTIONS = {
|
|
2555
|
+
requiredTools: ["ping", "start_request"],
|
|
2556
|
+
exerciseWrite: true
|
|
2557
|
+
};
|
|
2558
|
+
async function probeMcpServer(nodeExecutable, serverPath, cwd, statePath, options = {}) {
|
|
2559
|
+
const requiredTools = options.requiredTools ?? DEFAULT_PROBE_OPTIONS.requiredTools;
|
|
2560
|
+
const exerciseWrite = options.exerciseWrite ?? requiredTools.includes("start_request");
|
|
2490
2561
|
try {
|
|
2491
2562
|
await new Promise((resolve2, reject) => {
|
|
2492
2563
|
const child = spawn(nodeExecutable, [serverPath], {
|
|
@@ -2502,6 +2573,7 @@ async function probeMcpServer(nodeExecutable, serverPath, cwd, statePath) {
|
|
|
2502
2573
|
let stderr = "";
|
|
2503
2574
|
let stdoutBuffer = "";
|
|
2504
2575
|
const requestTimeout = 1e4;
|
|
2576
|
+
let exitTimeout;
|
|
2505
2577
|
const finish = (error) => {
|
|
2506
2578
|
if (settled)
|
|
2507
2579
|
return;
|
|
@@ -2510,9 +2582,18 @@ async function probeMcpServer(nodeExecutable, serverPath, cwd, statePath) {
|
|
|
2510
2582
|
try {
|
|
2511
2583
|
child.stdin?.end();
|
|
2512
2584
|
} catch {}
|
|
2513
|
-
if (child.exitCode === null)
|
|
2585
|
+
if (child.exitCode === null) {
|
|
2586
|
+
const onExit = () => {
|
|
2587
|
+
if (exitTimeout)
|
|
2588
|
+
clearTimeout(exitTimeout);
|
|
2589
|
+
error ? reject(error) : resolve2();
|
|
2590
|
+
};
|
|
2591
|
+
child.once("exit", onExit);
|
|
2514
2592
|
child.kill();
|
|
2515
|
-
|
|
2593
|
+
exitTimeout = setTimeout(onExit, 500);
|
|
2594
|
+
} else {
|
|
2595
|
+
error ? reject(error) : resolve2();
|
|
2596
|
+
}
|
|
2516
2597
|
};
|
|
2517
2598
|
const fail = (message) => finish(new Error(`${message}${stderr ? `: ${stderr.trim()}` : ""}`));
|
|
2518
2599
|
const send = (message) => {
|
|
@@ -2555,17 +2636,22 @@ async function probeMcpServer(nodeExecutable, serverPath, cwd, statePath) {
|
|
|
2555
2636
|
}
|
|
2556
2637
|
if (message.id === 2) {
|
|
2557
2638
|
const tools = message.result?.tools ?? [];
|
|
2558
|
-
|
|
2559
|
-
|
|
2639
|
+
const missing = requiredTools.filter((name) => !tools.some((tool) => tool.name === name));
|
|
2640
|
+
if (missing.length > 0) {
|
|
2641
|
+
fail(`El MCP inició pero no expuso las tools requeridas: ${missing.join(", ")}`);
|
|
2560
2642
|
return;
|
|
2561
2643
|
}
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2644
|
+
if (exerciseWrite) {
|
|
2645
|
+
send({
|
|
2646
|
+
jsonrpc: "2.0",
|
|
2647
|
+
id: 3,
|
|
2648
|
+
method: "tools/call",
|
|
2649
|
+
params: { name: "start_request", arguments: { requestId: "installer-probe" } }
|
|
2650
|
+
});
|
|
2651
|
+
continue;
|
|
2652
|
+
}
|
|
2653
|
+
finish();
|
|
2654
|
+
return;
|
|
2569
2655
|
}
|
|
2570
2656
|
if (message.id === 3) {
|
|
2571
2657
|
const result = message.result;
|
|
@@ -2584,15 +2670,35 @@ async function probeMcpServer(nodeExecutable, serverPath, cwd, statePath) {
|
|
|
2584
2670
|
params: {
|
|
2585
2671
|
protocolVersion: "2025-03-26",
|
|
2586
2672
|
capabilities: {},
|
|
2587
|
-
clientInfo: { name: "ostacky-installer", version: "0.7.
|
|
2673
|
+
clientInfo: { name: "ostacky-installer", version: "0.7.2" }
|
|
2588
2674
|
}
|
|
2589
2675
|
});
|
|
2590
2676
|
});
|
|
2591
2677
|
} finally {
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2678
|
+
await new Promise((r2) => setTimeout(r2, 200));
|
|
2679
|
+
if (statePath) {
|
|
2680
|
+
for (const p2 of [
|
|
2681
|
+
statePath,
|
|
2682
|
+
statePath + ".backup",
|
|
2683
|
+
statePath + ".lock.pid",
|
|
2684
|
+
statePath + ".lock.timestamp",
|
|
2685
|
+
statePath + ".tmp." + process.pid
|
|
2686
|
+
]) {
|
|
2687
|
+
try {
|
|
2688
|
+
if (existsSync5(p2))
|
|
2689
|
+
rmSync3(p2, { force: true });
|
|
2690
|
+
} catch {}
|
|
2691
|
+
}
|
|
2692
|
+
try {
|
|
2693
|
+
const { dirname: dirname5, join: join7 } = await import("path");
|
|
2694
|
+
const handoff = join7(dirname5(statePath), ".ostacky-handoff-compaction.json");
|
|
2695
|
+
if (existsSync5(handoff)) {
|
|
2696
|
+
try {
|
|
2697
|
+
rmSync3(handoff, { force: true });
|
|
2698
|
+
} catch {}
|
|
2699
|
+
}
|
|
2700
|
+
} catch {}
|
|
2701
|
+
}
|
|
2596
2702
|
}
|
|
2597
2703
|
}
|
|
2598
2704
|
function takeFileSnapshot(path) {
|
|
@@ -2674,8 +2780,8 @@ async function installSkill(item, manifest, paths) {
|
|
|
2674
2780
|
const treeHash = computeTreeHash(src);
|
|
2675
2781
|
if (item.sha256 && treeHash !== item.sha256) {
|
|
2676
2782
|
throw new Error(`Tree hash inválido para skill "${item.name}"
|
|
2677
|
-
|
|
2678
|
-
|
|
2783
|
+
esperado: ${item.sha256}
|
|
2784
|
+
recibido: ${treeHash}`);
|
|
2679
2785
|
}
|
|
2680
2786
|
const dest = join6(paths.skills, item.name);
|
|
2681
2787
|
if (existsSync5(dest)) {
|
|
@@ -2715,8 +2821,8 @@ async function installMcpServer(item, manifest, paths) {
|
|
|
2715
2821
|
const treeHash = computeTreeHash(src);
|
|
2716
2822
|
if (item.sha256 && treeHash !== item.sha256) {
|
|
2717
2823
|
throw new Error(`Tree hash inválido para MCP server "${item.name}"
|
|
2718
|
-
|
|
2719
|
-
|
|
2824
|
+
esperado: ${item.sha256}
|
|
2825
|
+
recibido: ${treeHash}`);
|
|
2720
2826
|
}
|
|
2721
2827
|
const nodeExecutable = getVerifiedNodeExecutable();
|
|
2722
2828
|
const projectRoot = dirname4(paths.root);
|
|
@@ -2754,7 +2860,9 @@ async function installMcpServer(item, manifest, paths) {
|
|
|
2754
2860
|
throw new Error(`El MCP ${item.name} no contiene index.js después de instalarse.`);
|
|
2755
2861
|
}
|
|
2756
2862
|
validateMcpModule(nodeExecutable, stagedServerPath, staging);
|
|
2757
|
-
|
|
2863
|
+
const isController = item.name === "ostacky-controller";
|
|
2864
|
+
const probeOptions = isController ? { requiredTools: ["ping", "start_request"], exerciseWrite: true } : { requiredTools: [], exerciseWrite: false };
|
|
2865
|
+
await probeMcpServer(nodeExecutable, stagedServerPath, staging, isController ? join6(staging, ".probe-state.json") : undefined, probeOptions);
|
|
2758
2866
|
const configPath = findOpenCodeConfig(projectRoot) ?? join6(projectRoot, "opencode.json");
|
|
2759
2867
|
const configSnapshot = takeFileSnapshot(configPath);
|
|
2760
2868
|
const lockfileSnapshot = takeFileSnapshot(getLockfilePath(paths.root));
|
|
@@ -2918,25 +3026,63 @@ function printPostInstallSteps() {
|
|
|
2918
3026
|
].join(`
|
|
2919
3027
|
`), "Próximos pasos");
|
|
2920
3028
|
}
|
|
2921
|
-
async function resolveOpenCodePaths() {
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
3029
|
+
async function resolveOpenCodePaths(scope) {
|
|
3030
|
+
if (scope === "local" || scope === "global" || scope === "auto") {
|
|
3031
|
+
const dir2 = getOpenCodeDirForScope(scope);
|
|
3032
|
+
try {
|
|
3033
|
+
const paths = ensureOpenCodePaths(dir2);
|
|
3034
|
+
if (scope === "global")
|
|
3035
|
+
ye(dir2, "Instalación global");
|
|
3036
|
+
else if (scope === "auto")
|
|
3037
|
+
ye(dir2, `Scope auto → ${dir2.includes(homedir2()) && dir2.includes(".config") ? "global" : "local"}`);
|
|
3038
|
+
else
|
|
3039
|
+
ye(dir2, "Instalación local");
|
|
3040
|
+
return paths;
|
|
3041
|
+
} catch (e2) {
|
|
3042
|
+
const msg = e2.message ?? "";
|
|
3043
|
+
if (scope === "global" && (msg.includes("EACCES") || msg.includes("permission"))) {
|
|
3044
|
+
v2.warn(`No se pudo escribir en global (${dir2}): ${msg}. ¿Instalar local?`);
|
|
3045
|
+
const retry = await me({ message: "¿Reintentar como instalación local?" });
|
|
3046
|
+
onCancel(retry);
|
|
3047
|
+
if (retry) {
|
|
3048
|
+
const localDir2 = getOpenCodeDirForScope("local");
|
|
3049
|
+
return ensureOpenCodePaths(localDir2);
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
3052
|
+
throw e2;
|
|
3053
|
+
}
|
|
2927
3054
|
}
|
|
2928
|
-
const
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
const
|
|
2932
|
-
|
|
3055
|
+
const cwd = process.cwd();
|
|
3056
|
+
const localDir = getOpenCodeDirForScope("local", cwd);
|
|
3057
|
+
const globalDir = getGlobalOpenCodeDir();
|
|
3058
|
+
const hasLocal = !!findOpenCodeDir(cwd);
|
|
3059
|
+
const scopeChoice = await de({
|
|
3060
|
+
message: `¿Instalar en proyecto local (${localDir}) o global (${globalDir})?`,
|
|
3061
|
+
options: [
|
|
3062
|
+
{ value: "local", label: "Local", hint: `${localDir} (recomendado)` },
|
|
3063
|
+
{ value: "global", label: "Global", hint: globalDir }
|
|
3064
|
+
],
|
|
3065
|
+
initialValue: "local"
|
|
2933
3066
|
});
|
|
2934
|
-
onCancel(
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
3067
|
+
onCancel(scopeChoice);
|
|
3068
|
+
const chosen = scopeChoice;
|
|
3069
|
+
const dir = getOpenCodeDirForScope(chosen, cwd);
|
|
3070
|
+
try {
|
|
3071
|
+
return ensureOpenCodePaths(dir);
|
|
3072
|
+
} catch (e2) {
|
|
3073
|
+
const msg = e2.message ?? "";
|
|
3074
|
+
if (chosen === "global" && (msg.includes("EACCES") || msg.includes("permission"))) {
|
|
3075
|
+
v2.warn(`No se pudo escribir en global (${dir}): ${msg}.`);
|
|
3076
|
+
const retry = await me({ message: "¿Instalar local en su lugar?" });
|
|
3077
|
+
onCancel(retry);
|
|
3078
|
+
if (retry)
|
|
3079
|
+
return ensureOpenCodePaths(getOpenCodeDirForScope("local", cwd));
|
|
3080
|
+
}
|
|
3081
|
+
throw e2;
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
function isGlobalScope(paths) {
|
|
3085
|
+
return paths.root === getGlobalOpenCodeDir() || paths.root.startsWith(getGlobalOpenCodeDir() + "/");
|
|
2940
3086
|
}
|
|
2941
3087
|
function getOrphanedItems(manifest, paths) {
|
|
2942
3088
|
const lockfile = readLockfile(paths.root);
|
|
@@ -3054,7 +3200,13 @@ async function doInstallStack(toolsDir, projectRoot) {
|
|
|
3054
3200
|
async function doInstallAll(manifest, paths) {
|
|
3055
3201
|
const spin = L2();
|
|
3056
3202
|
let errors = 0;
|
|
3057
|
-
|
|
3203
|
+
const isGlobal = paths.root === getGlobalOpenCodeDir() || paths.root.startsWith(getGlobalOpenCodeDir() + "/");
|
|
3204
|
+
if (!isGlobal) {
|
|
3205
|
+
ensureToolDirs(paths.tools, ["codegraph", "engram", "context7"]);
|
|
3206
|
+
} else {
|
|
3207
|
+
v2.info("Scope global detectado: el stack (CodeGraph/Engram) permanece siempre en <proyecto>/.opencode/tools — se omite instalación de stack global.");
|
|
3208
|
+
v2.info("Para instalar el stack, ejecutá 'npx ostacky install-stack --scope local' dentro de cada proyecto.");
|
|
3209
|
+
}
|
|
3058
3210
|
for (const agent of manifest.agents) {
|
|
3059
3211
|
spin.start(`Descargando agente: ${agent.name} (${agent.version})`);
|
|
3060
3212
|
try {
|
|
@@ -3099,17 +3251,22 @@ async function doInstallAll(manifest, paths) {
|
|
|
3099
3251
|
errors++;
|
|
3100
3252
|
}
|
|
3101
3253
|
}
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
if (
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3254
|
+
let stackOk = true;
|
|
3255
|
+
let missingTools = [];
|
|
3256
|
+
if (isGlobal) {
|
|
3257
|
+
stackOk = true;
|
|
3258
|
+
} else {
|
|
3259
|
+
v2.info("Instalando stack de herramientas...");
|
|
3260
|
+
stackOk = await doInstallStack(paths.tools, dirname6(paths.root));
|
|
3261
|
+
if (!stackOk)
|
|
3262
|
+
errors++;
|
|
3263
|
+
const codegraphDir = join8(paths.tools, "codegraph");
|
|
3264
|
+
const engramDir = join8(paths.tools, "engram");
|
|
3265
|
+
if (!existsSync7(codegraphDir) || !findBinaryInDir(codegraphDir, "codegraph"))
|
|
3266
|
+
missingTools.push("CodeGraph");
|
|
3267
|
+
if (!existsSync7(engramDir) || !findBinaryInDir(engramDir, "engram"))
|
|
3268
|
+
missingTools.push("Engram");
|
|
3269
|
+
}
|
|
3113
3270
|
if (missingTools.length > 0) {
|
|
3114
3271
|
v2.warn(`Faltan herramientas del stack: ${missingTools.join(", ")}.
|
|
3115
3272
|
` + "Ejecutá `/install-stack` desde el agente para instalarlas manualmente.");
|
|
@@ -3624,10 +3781,10 @@ async function doUninstallMcpByName(name, paths) {
|
|
|
3624
3781
|
}
|
|
3625
3782
|
|
|
3626
3783
|
// src/prompts/index.ts
|
|
3627
|
-
async function runInteractiveMenu() {
|
|
3784
|
+
async function runInteractiveMenu(scope) {
|
|
3628
3785
|
we(" OpenCode Installer ");
|
|
3629
3786
|
const manifest = await loadManifest();
|
|
3630
|
-
const paths = await resolveOpenCodePaths();
|
|
3787
|
+
const paths = await resolveOpenCodePaths(scope ?? null);
|
|
3631
3788
|
if (!paths) {
|
|
3632
3789
|
fe("Instalación cancelada.");
|
|
3633
3790
|
return;
|
|
@@ -3675,6 +3832,12 @@ async function runInteractiveMenu() {
|
|
|
3675
3832
|
fe("Listo.");
|
|
3676
3833
|
break;
|
|
3677
3834
|
case "stack": {
|
|
3835
|
+
if (isGlobalScope(paths)) {
|
|
3836
|
+
v2.error("install-stack requiere scope local; el stack vive en <proyecto>/.opencode/tools");
|
|
3837
|
+
v2.info(`Elegiste global (${paths.root}) — el stack debe instalarse por proyecto local.`);
|
|
3838
|
+
fe("Cancelado.");
|
|
3839
|
+
break;
|
|
3840
|
+
}
|
|
3678
3841
|
ensureToolDirs(paths.tools, ["codegraph", "engram", "context7"]);
|
|
3679
3842
|
const stackOk = await doInstallStack(paths.tools, dirname7(paths.root));
|
|
3680
3843
|
if (!stackOk)
|
|
@@ -3697,10 +3860,10 @@ async function runInteractiveMenu() {
|
|
|
3697
3860
|
break;
|
|
3698
3861
|
}
|
|
3699
3862
|
}
|
|
3700
|
-
async function runInstallCommand() {
|
|
3863
|
+
async function runInstallCommand(scope) {
|
|
3701
3864
|
we(" OpenCode Installer ");
|
|
3702
3865
|
const manifest = await loadManifest();
|
|
3703
|
-
const paths = await resolveOpenCodePaths();
|
|
3866
|
+
const paths = await resolveOpenCodePaths(scope ?? null);
|
|
3704
3867
|
if (!paths) {
|
|
3705
3868
|
fe("Cancelado.");
|
|
3706
3869
|
return;
|
|
@@ -3710,10 +3873,10 @@ async function runInstallCommand() {
|
|
|
3710
3873
|
printPostInstallSteps();
|
|
3711
3874
|
fe(process.exitCode ? "Instalación parcial." : "Instalación completada.");
|
|
3712
3875
|
}
|
|
3713
|
-
async function runAddAgentCommand() {
|
|
3876
|
+
async function runAddAgentCommand(scope) {
|
|
3714
3877
|
we(" OpenCode Installer ");
|
|
3715
3878
|
const manifest = await loadManifest();
|
|
3716
|
-
const paths = await resolveOpenCodePaths();
|
|
3879
|
+
const paths = await resolveOpenCodePaths(scope ?? null);
|
|
3717
3880
|
if (!paths) {
|
|
3718
3881
|
fe("Cancelado.");
|
|
3719
3882
|
return;
|
|
@@ -3722,10 +3885,10 @@ async function runAddAgentCommand() {
|
|
|
3722
3885
|
printPostInstallSteps();
|
|
3723
3886
|
fe("Listo.");
|
|
3724
3887
|
}
|
|
3725
|
-
async function runAddCommandCommand() {
|
|
3888
|
+
async function runAddCommandCommand(scope) {
|
|
3726
3889
|
we(" OpenCode Installer ");
|
|
3727
3890
|
const manifest = await loadManifest();
|
|
3728
|
-
const paths = await resolveOpenCodePaths();
|
|
3891
|
+
const paths = await resolveOpenCodePaths(scope ?? null);
|
|
3729
3892
|
if (!paths) {
|
|
3730
3893
|
fe("Cancelado.");
|
|
3731
3894
|
return;
|
|
@@ -3734,10 +3897,10 @@ async function runAddCommandCommand() {
|
|
|
3734
3897
|
printPostInstallSteps();
|
|
3735
3898
|
fe("Listo.");
|
|
3736
3899
|
}
|
|
3737
|
-
async function runAddSkillCommand() {
|
|
3900
|
+
async function runAddSkillCommand(scope) {
|
|
3738
3901
|
we(" OpenCode Installer ");
|
|
3739
3902
|
const manifest = await loadManifest();
|
|
3740
|
-
const paths = await resolveOpenCodePaths();
|
|
3903
|
+
const paths = await resolveOpenCodePaths(scope ?? null);
|
|
3741
3904
|
if (!paths) {
|
|
3742
3905
|
fe("Cancelado.");
|
|
3743
3906
|
return;
|
|
@@ -3746,10 +3909,10 @@ async function runAddSkillCommand() {
|
|
|
3746
3909
|
printPostInstallSteps();
|
|
3747
3910
|
fe("Listo.");
|
|
3748
3911
|
}
|
|
3749
|
-
async function runAddMcpCommand() {
|
|
3912
|
+
async function runAddMcpCommand(scope) {
|
|
3750
3913
|
we(" OpenCode Installer ");
|
|
3751
3914
|
const manifest = await loadManifest();
|
|
3752
|
-
const paths = await resolveOpenCodePaths();
|
|
3915
|
+
const paths = await resolveOpenCodePaths(scope ?? null);
|
|
3753
3916
|
if (!paths) {
|
|
3754
3917
|
fe("Cancelado.");
|
|
3755
3918
|
return;
|
|
@@ -3758,9 +3921,15 @@ async function runAddMcpCommand() {
|
|
|
3758
3921
|
printPostInstallSteps();
|
|
3759
3922
|
fe("Listo.");
|
|
3760
3923
|
}
|
|
3761
|
-
async function runInstallStackCommand() {
|
|
3924
|
+
async function runInstallStackCommand(scope) {
|
|
3925
|
+
if (scope === "global") {
|
|
3926
|
+
v2.error("install-stack requiere scope local; el stack vive en <proyecto>/.opencode/tools");
|
|
3927
|
+
fe("Usá: npx ostacky install-stack --scope local");
|
|
3928
|
+
process.exitCode = 1;
|
|
3929
|
+
return;
|
|
3930
|
+
}
|
|
3762
3931
|
we(" OpenCode Installer — Stack ");
|
|
3763
|
-
const paths = await resolveOpenCodePaths();
|
|
3932
|
+
const paths = await resolveOpenCodePaths(scope ?? null);
|
|
3764
3933
|
if (!paths) {
|
|
3765
3934
|
fe("Cancelado.");
|
|
3766
3935
|
return;
|
|
@@ -3771,9 +3940,9 @@ async function runInstallStackCommand() {
|
|
|
3771
3940
|
process.exitCode = 1;
|
|
3772
3941
|
fe(stackOk ? "Stack instalado." : "Stack instalado parcialmente.");
|
|
3773
3942
|
}
|
|
3774
|
-
async function runUninstallStackCommand() {
|
|
3943
|
+
async function runUninstallStackCommand(scope) {
|
|
3775
3944
|
we(" OpenCode Installer — Stack ");
|
|
3776
|
-
const paths = await resolveOpenCodePaths();
|
|
3945
|
+
const paths = await resolveOpenCodePaths(scope ?? null);
|
|
3777
3946
|
if (!paths) {
|
|
3778
3947
|
fe("Cancelado.");
|
|
3779
3948
|
return;
|
|
@@ -3795,10 +3964,10 @@ async function runUninstallStackCommand() {
|
|
|
3795
3964
|
}
|
|
3796
3965
|
fe("Stack desinstalado.");
|
|
3797
3966
|
}
|
|
3798
|
-
async function runUpdateCommand() {
|
|
3967
|
+
async function runUpdateCommand(scope) {
|
|
3799
3968
|
we(" OpenCode Installer ");
|
|
3800
3969
|
const manifest = await loadLatestManifest();
|
|
3801
|
-
const paths = await resolveOpenCodePaths();
|
|
3970
|
+
const paths = await resolveOpenCodePaths(scope ?? null);
|
|
3802
3971
|
if (!paths) {
|
|
3803
3972
|
fe("Cancelado.");
|
|
3804
3973
|
return;
|
|
@@ -3806,9 +3975,9 @@ async function runUpdateCommand() {
|
|
|
3806
3975
|
await doUpdate(manifest, paths);
|
|
3807
3976
|
fe("Actualización completada.");
|
|
3808
3977
|
}
|
|
3809
|
-
async function runUninstallCommand() {
|
|
3978
|
+
async function runUninstallCommand(scope) {
|
|
3810
3979
|
we(" OpenCode Installer ");
|
|
3811
|
-
const paths = await resolveOpenCodePaths();
|
|
3980
|
+
const paths = await resolveOpenCodePaths(scope ?? null);
|
|
3812
3981
|
if (!paths) {
|
|
3813
3982
|
fe("Cancelado.");
|
|
3814
3983
|
return;
|
|
@@ -3816,9 +3985,9 @@ async function runUninstallCommand() {
|
|
|
3816
3985
|
await doUninstall(paths);
|
|
3817
3986
|
fe("Desinstalación completada.");
|
|
3818
3987
|
}
|
|
3819
|
-
async function runUninstallAgentCommand(name) {
|
|
3988
|
+
async function runUninstallAgentCommand(name, scope) {
|
|
3820
3989
|
we(" OpenCode Installer ");
|
|
3821
|
-
const paths = await resolveOpenCodePaths();
|
|
3990
|
+
const paths = await resolveOpenCodePaths(scope ?? null);
|
|
3822
3991
|
if (!paths) {
|
|
3823
3992
|
fe("Cancelado.");
|
|
3824
3993
|
return;
|
|
@@ -3851,9 +4020,9 @@ async function runUninstallAgentCommand(name) {
|
|
|
3851
4020
|
}
|
|
3852
4021
|
fe("Listo.");
|
|
3853
4022
|
}
|
|
3854
|
-
async function runUninstallCommandCommand(name) {
|
|
4023
|
+
async function runUninstallCommandCommand(name, scope) {
|
|
3855
4024
|
we(" OpenCode Installer ");
|
|
3856
|
-
const paths = await resolveOpenCodePaths();
|
|
4025
|
+
const paths = await resolveOpenCodePaths(scope ?? null);
|
|
3857
4026
|
if (!paths) {
|
|
3858
4027
|
fe("Cancelado.");
|
|
3859
4028
|
return;
|
|
@@ -3886,9 +4055,9 @@ async function runUninstallCommandCommand(name) {
|
|
|
3886
4055
|
}
|
|
3887
4056
|
fe("Listo.");
|
|
3888
4057
|
}
|
|
3889
|
-
async function runUninstallSkillCommand(name) {
|
|
4058
|
+
async function runUninstallSkillCommand(name, scope) {
|
|
3890
4059
|
we(" OpenCode Installer ");
|
|
3891
|
-
const paths = await resolveOpenCodePaths();
|
|
4060
|
+
const paths = await resolveOpenCodePaths(scope ?? null);
|
|
3892
4061
|
if (!paths) {
|
|
3893
4062
|
fe("Cancelado.");
|
|
3894
4063
|
return;
|
|
@@ -3921,9 +4090,9 @@ async function runUninstallSkillCommand(name) {
|
|
|
3921
4090
|
}
|
|
3922
4091
|
fe("Listo.");
|
|
3923
4092
|
}
|
|
3924
|
-
async function runUninstallMcpCommand(name) {
|
|
4093
|
+
async function runUninstallMcpCommand(name, scope) {
|
|
3925
4094
|
we(" OpenCode Installer ");
|
|
3926
|
-
const paths = await resolveOpenCodePaths();
|
|
4095
|
+
const paths = await resolveOpenCodePaths(scope ?? null);
|
|
3927
4096
|
if (!paths) {
|
|
3928
4097
|
fe("Cancelado.");
|
|
3929
4098
|
return;
|
|
@@ -3962,67 +4131,110 @@ var HELP = `
|
|
|
3962
4131
|
ostacky — Instalador de agentes, comandos, skills y MCPs para OpenCode
|
|
3963
4132
|
|
|
3964
4133
|
Uso:
|
|
3965
|
-
npx ostacky Menú interactivo (instalación completa)
|
|
3966
|
-
npx ostacky install Instalar TODO (agente + skills + MCPs + CodeGraph + OpenSpec + Engram + Context7)
|
|
3967
|
-
npx ostacky add agent Agregar agente(s)
|
|
3968
|
-
npx ostacky add command Agregar command(s)
|
|
3969
|
-
npx ostacky add skill Agregar skill(s)
|
|
3970
|
-
npx ostacky add mcp Agregar MCP server(s)
|
|
3971
|
-
npx ostacky install-stack Instalar solo el stack de herramientas (CodeGraph, OpenSpec, Engram, Context7)
|
|
3972
|
-
npx ostacky uninstall-stack Remover la configuración del stack del proyecto
|
|
3973
|
-
npx ostacky update Actualizar instalación
|
|
3974
|
-
npx ostacky uninstall Desinstalar todo
|
|
3975
|
-
npx ostacky uninstall agent Desinstalar agente(s)
|
|
3976
|
-
npx ostacky uninstall command Desinstalar command(s)
|
|
3977
|
-
npx ostacky uninstall skill Desinstalar skill(s)
|
|
3978
|
-
npx ostacky uninstall mcp Desinstalar MCP server(s)
|
|
4134
|
+
npx ostacky [--scope local|global|auto] Menú interactivo (instalación completa, pregunta local vs global, default local)
|
|
4135
|
+
npx ostacky install [--scope local|global|auto] Instalar TODO (agente + skills + MCPs + CodeGraph + OpenSpec + Engram + Context7)
|
|
4136
|
+
npx ostacky add agent [--scope local|global|auto] Agregar agente(s)
|
|
4137
|
+
npx ostacky add command [--scope ...] Agregar command(s)
|
|
4138
|
+
npx ostacky add skill [--scope ...] Agregar skill(s)
|
|
4139
|
+
npx ostacky add mcp [--scope ...] Agregar MCP server(s)
|
|
4140
|
+
npx ostacky install-stack [--scope local|auto] Instalar solo el stack de herramientas (CodeGraph, OpenSpec, Engram, Context7) — global bloquea con error
|
|
4141
|
+
npx ostacky uninstall-stack [--scope local|global|auto] Remover la configuración del stack del proyecto
|
|
4142
|
+
npx ostacky update [--scope ...] Actualizar instalación
|
|
4143
|
+
npx ostacky uninstall [--scope ...] Desinstalar todo
|
|
4144
|
+
npx ostacky uninstall agent [--scope ...] Desinstalar agente(s)
|
|
4145
|
+
npx ostacky uninstall command [--scope ...] Desinstalar command(s)
|
|
4146
|
+
npx ostacky uninstall skill [--scope ...] Desinstalar skill(s)
|
|
4147
|
+
npx ostacky uninstall mcp [--scope ...] Desinstalar MCP server(s)
|
|
3979
4148
|
npx ostacky --help Mostrar esta ayuda
|
|
3980
4149
|
npx ostacky --version Mostrar versión
|
|
4150
|
+
|
|
4151
|
+
Scope:
|
|
4152
|
+
--scope local Escribe en <proyecto>/.opencode (recomendado, default al preguntar)
|
|
4153
|
+
--scope global Escribe en ~/.config/opencode (o %APPDATA%\\opencode en Windows)
|
|
4154
|
+
--scope auto Elige local si existe .opencode o .git, si no global
|
|
4155
|
+
Sin flag Pregunta interactiva local (default) vs global
|
|
3981
4156
|
`.trim();
|
|
3982
|
-
|
|
4157
|
+
function parseScopeArg(argv = process.argv) {
|
|
4158
|
+
for (let i = 0;i < argv.length; i++) {
|
|
4159
|
+
const arg = argv[i];
|
|
4160
|
+
if (arg === "--scope" && i + 1 < argv.length) {
|
|
4161
|
+
const v3 = argv[i + 1];
|
|
4162
|
+
if (v3 === "local" || v3 === "global" || v3 === "auto")
|
|
4163
|
+
return v3;
|
|
4164
|
+
}
|
|
4165
|
+
if (arg.startsWith("--scope=")) {
|
|
4166
|
+
const v3 = arg.split("=")[1];
|
|
4167
|
+
if (v3 === "local" || v3 === "global" || v3 === "auto")
|
|
4168
|
+
return v3;
|
|
4169
|
+
}
|
|
4170
|
+
}
|
|
4171
|
+
return null;
|
|
4172
|
+
}
|
|
4173
|
+
function withoutScopeArgs(argv) {
|
|
4174
|
+
const out = [];
|
|
4175
|
+
for (let i = 0;i < argv.length; i++) {
|
|
4176
|
+
const arg = argv[i];
|
|
4177
|
+
if (arg === "--scope" && i + 1 < argv.length) {
|
|
4178
|
+
i++;
|
|
4179
|
+
continue;
|
|
4180
|
+
}
|
|
4181
|
+
if (arg.startsWith("--scope="))
|
|
4182
|
+
continue;
|
|
4183
|
+
out.push(arg);
|
|
4184
|
+
}
|
|
4185
|
+
return out;
|
|
4186
|
+
}
|
|
4187
|
+
var scope = parseScopeArg();
|
|
4188
|
+
var argvNoScope = withoutScopeArgs(process.argv);
|
|
4189
|
+
var [, , cmd, subcmd] = argvNoScope;
|
|
3983
4190
|
async function main() {
|
|
3984
4191
|
switch (cmd) {
|
|
3985
4192
|
case "install":
|
|
3986
|
-
await runInstallCommand();
|
|
4193
|
+
await runInstallCommand(scope);
|
|
3987
4194
|
break;
|
|
3988
4195
|
case "install-stack":
|
|
3989
|
-
|
|
4196
|
+
if (scope === "global") {
|
|
4197
|
+
console.error("Error: install-stack requiere scope local; el stack vive en <proyecto>/.opencode/tools");
|
|
4198
|
+
console.error("Sugerencia: ejecutá 'npx ostacky install-stack --scope local' dentro de cada proyecto.");
|
|
4199
|
+
process.exit(1);
|
|
4200
|
+
}
|
|
4201
|
+
await runInstallStackCommand(scope);
|
|
3990
4202
|
break;
|
|
3991
4203
|
case "uninstall-stack":
|
|
3992
|
-
await runUninstallStackCommand();
|
|
4204
|
+
await runUninstallStackCommand(scope);
|
|
3993
4205
|
break;
|
|
3994
4206
|
case "add":
|
|
3995
4207
|
if (subcmd === "agent") {
|
|
3996
|
-
await runAddAgentCommand();
|
|
4208
|
+
await runAddAgentCommand(scope);
|
|
3997
4209
|
} else if (subcmd === "command") {
|
|
3998
|
-
await runAddCommandCommand();
|
|
4210
|
+
await runAddCommandCommand(scope);
|
|
3999
4211
|
} else if (subcmd === "skill") {
|
|
4000
|
-
await runAddSkillCommand();
|
|
4212
|
+
await runAddSkillCommand(scope);
|
|
4001
4213
|
} else if (subcmd === "mcp") {
|
|
4002
|
-
await runAddMcpCommand();
|
|
4214
|
+
await runAddMcpCommand(scope);
|
|
4003
4215
|
} else {
|
|
4004
4216
|
console.error(`Tipo desconocido: "${subcmd}". Usa 'agent', 'command', 'skill' o 'mcp'.`);
|
|
4005
4217
|
process.exit(1);
|
|
4006
4218
|
}
|
|
4007
4219
|
break;
|
|
4008
4220
|
case "update":
|
|
4009
|
-
await runUpdateCommand();
|
|
4221
|
+
await runUpdateCommand(scope);
|
|
4010
4222
|
break;
|
|
4011
4223
|
case "uninstall":
|
|
4012
4224
|
if (subcmd === "agent") {
|
|
4013
|
-
const name =
|
|
4014
|
-
await runUninstallAgentCommand(name);
|
|
4225
|
+
const name = argvNoScope[4];
|
|
4226
|
+
await runUninstallAgentCommand(name, scope);
|
|
4015
4227
|
} else if (subcmd === "command") {
|
|
4016
|
-
const name =
|
|
4017
|
-
await runUninstallCommandCommand(name);
|
|
4228
|
+
const name = argvNoScope[4];
|
|
4229
|
+
await runUninstallCommandCommand(name, scope);
|
|
4018
4230
|
} else if (subcmd === "skill") {
|
|
4019
|
-
const name =
|
|
4020
|
-
await runUninstallSkillCommand(name);
|
|
4231
|
+
const name = argvNoScope[4];
|
|
4232
|
+
await runUninstallSkillCommand(name, scope);
|
|
4021
4233
|
} else if (subcmd === "mcp") {
|
|
4022
|
-
const name =
|
|
4023
|
-
await runUninstallMcpCommand(name);
|
|
4234
|
+
const name = argvNoScope[4];
|
|
4235
|
+
await runUninstallMcpCommand(name, scope);
|
|
4024
4236
|
} else if (subcmd === undefined) {
|
|
4025
|
-
await runUninstallCommand();
|
|
4237
|
+
await runUninstallCommand(scope);
|
|
4026
4238
|
} else {
|
|
4027
4239
|
console.error(`Subcomando desconocido: "${subcmd}". Usa 'agent', 'command', 'skill', 'mcp' o nada.`);
|
|
4028
4240
|
process.exit(1);
|
|
@@ -4041,7 +4253,7 @@ async function main() {
|
|
|
4041
4253
|
console.error(`Comando desconocido: "${cmd}". Usá --help para ver los comandos disponibles.`);
|
|
4042
4254
|
process.exit(1);
|
|
4043
4255
|
}
|
|
4044
|
-
await runInteractiveMenu();
|
|
4256
|
+
await runInteractiveMenu(scope);
|
|
4045
4257
|
}
|
|
4046
4258
|
}
|
|
4047
4259
|
main().catch((e2) => {
|