ostacky 0.8.0 → 0.8.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 +57 -17
- package/assets/agents/ostacky.md +3 -3
- package/assets/commands/install-stack.md +15 -115
- package/assets/docs/ostacky-reference.md +0 -2
- package/assets/mcp/ostacky-controller/index.js +2 -2
- package/assets/mcp/ostacky-controller/package.json +1 -1
- package/assets/plugins/ostacky-plugin.ts +48 -0
- package/assets/skills/brainstorming/SKILL.md +1 -1
- package/assets/skills/graceful-degradation/SKILL.md +0 -9
- package/dist/cli.js +251 -85
- package/manifest.json +30 -30
- package/package.json +1 -1
- package/assets/plugins/ostacky-guard.ts +0 -18
package/dist/cli.js
CHANGED
|
@@ -744,31 +744,31 @@ var init_fs = __esm(() => {
|
|
|
744
744
|
var manifest_default;
|
|
745
745
|
var init_manifest = __esm(() => {
|
|
746
746
|
manifest_default = {
|
|
747
|
-
version: "0.8.
|
|
747
|
+
version: "0.8.2",
|
|
748
748
|
repo: "JaimeHoracio/Ostacky",
|
|
749
|
-
tag: "v0.8.
|
|
749
|
+
tag: "v0.8.2",
|
|
750
750
|
agents: [
|
|
751
751
|
{
|
|
752
752
|
name: "ostacky",
|
|
753
753
|
file: "assets/agents/ostacky.md",
|
|
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.
|
|
755
|
-
version: "0.8.
|
|
756
|
-
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.2: prune de skills obsoletas, lastHandoff, getAvailableTransitions, consecutiveFailures real, mem_session_summary automático al cierre.",
|
|
755
|
+
version: "0.8.2",
|
|
756
|
+
sha256: "7b2be94d05cd371fab5be5844ef7d82e542e84c2869d8171ccd63dc14e922dc3"
|
|
757
757
|
}
|
|
758
758
|
],
|
|
759
759
|
commands: [
|
|
760
760
|
{
|
|
761
761
|
name: "install-stack",
|
|
762
762
|
file: "assets/commands/install-stack.md",
|
|
763
|
-
description: "Instala el stack tecnológico del proyecto (CodeGraph, skills, OpenSpec, Engram, controller plugin). v0.8.
|
|
764
|
-
version: "0.8.
|
|
765
|
-
sha256: "
|
|
763
|
+
description: "Instala el stack tecnológico del proyecto (CodeGraph, skills, OpenSpec, Engram, controller plugin). v0.8.2: controller plugin como alma hard-gate, Context7 removido del stack.",
|
|
764
|
+
version: "0.8.2",
|
|
765
|
+
sha256: "24802dc29675a584dacf4d0ac8a944dd935ee0f96a4e804a9b1c5bdb3709438c"
|
|
766
766
|
},
|
|
767
767
|
{
|
|
768
768
|
name: "opsx-sync",
|
|
769
769
|
file: "assets/commands/opsx-sync.md",
|
|
770
770
|
description: "Sincroniza delta specs del change activo sin inicializar CodeGraph si ya existe índice",
|
|
771
|
-
version: "0.8.
|
|
771
|
+
version: "0.8.2",
|
|
772
772
|
sha256: "fe0158478f2ca63b315037a85fc1632b77868532e319af6c7384285441767d64"
|
|
773
773
|
}
|
|
774
774
|
],
|
|
@@ -776,15 +776,15 @@ var init_manifest = __esm(() => {
|
|
|
776
776
|
{
|
|
777
777
|
name: "ostacky-controller",
|
|
778
778
|
file: "assets/mcp/ostacky-controller/",
|
|
779
|
-
description: "Máquina de estados persistida con @modelcontextprotocol/server SDK. v0.8.
|
|
780
|
-
version: "0.8.
|
|
781
|
-
sha256: "
|
|
779
|
+
description: "Máquina de estados persistida con @modelcontextprotocol/server SDK. v0.8.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.",
|
|
780
|
+
version: "0.8.2",
|
|
781
|
+
sha256: "a9d45b027a88e8037a4e29704b84f78a1d03d41654ba7445a3091a0c6767d094"
|
|
782
782
|
},
|
|
783
783
|
{
|
|
784
784
|
name: "openspec",
|
|
785
785
|
file: "assets/mcp/openspec/",
|
|
786
786
|
description: "MCP server local para OpenSpec - proposal, apply, archive, sync de cambios",
|
|
787
|
-
version: "0.8.
|
|
787
|
+
version: "0.8.2",
|
|
788
788
|
sha256: "fa4be28bfc1e75db8f1a037e2580f04a60066c80e8e5934e90e1041020d04609"
|
|
789
789
|
}
|
|
790
790
|
],
|
|
@@ -793,106 +793,106 @@ var init_manifest = __esm(() => {
|
|
|
793
793
|
name: "brainstorming",
|
|
794
794
|
file: "assets/skills/brainstorming/SKILL.md",
|
|
795
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)",
|
|
796
|
-
version: "0.8.
|
|
797
|
-
sha256: "
|
|
796
|
+
version: "0.8.2",
|
|
797
|
+
sha256: "4c54604d4680bc695b7049c70cf8680296d8d2602e5790ddfc1cce7ba7c6f1fc"
|
|
798
798
|
},
|
|
799
799
|
{
|
|
800
800
|
name: "execution-mode-evaluation",
|
|
801
801
|
file: "assets/skills/execution-mode-evaluation/SKILL.md",
|
|
802
802
|
description: "Skill de análisis de modo de ejecución — output reconciliado con controller snapshot contract (recommendation field)",
|
|
803
|
-
version: "0.8.
|
|
803
|
+
version: "0.8.2",
|
|
804
804
|
sha256: "7b89dda29927c88f1eb13ffcea541a12423384c172f66c4f3d9b61efe0c7a163"
|
|
805
805
|
},
|
|
806
806
|
{
|
|
807
807
|
name: "tdd",
|
|
808
808
|
file: "assets/skills/tdd/SKILL.md",
|
|
809
809
|
description: "Skill de test-driven development (Superpowers)",
|
|
810
|
-
version: "0.8.
|
|
810
|
+
version: "0.8.2",
|
|
811
811
|
sha256: "aa412298980b7826165c211145c1b8e9135f68f36247bb3cb96d0a0eae274486"
|
|
812
812
|
},
|
|
813
813
|
{
|
|
814
814
|
name: "subagent-driven-development",
|
|
815
815
|
file: "assets/skills/subagent-driven-development/SKILL.md",
|
|
816
816
|
description: "Skill de ejecución con subagentes (Superpowers) — ejecuta solo después de confirmación del coordinador Ostacky",
|
|
817
|
-
version: "0.8.
|
|
817
|
+
version: "0.8.2",
|
|
818
818
|
sha256: "1a42d714a9a13faf05f0bf7b580e8d837c2e77a5633839542ccce603023419e8"
|
|
819
819
|
},
|
|
820
820
|
{
|
|
821
821
|
name: "dispatching-parallel-agents",
|
|
822
822
|
file: "assets/skills/dispatching-parallel-agents/SKILL.md",
|
|
823
823
|
description: "Skill de dispatch paralelo de agentes (Superpowers)",
|
|
824
|
-
version: "0.8.
|
|
824
|
+
version: "0.8.2",
|
|
825
825
|
sha256: "3c8a66d51ae2e719e877d02c3dac32bd804722c3bb9227c258ecae78252a20ab"
|
|
826
826
|
},
|
|
827
827
|
{
|
|
828
828
|
name: "review",
|
|
829
829
|
file: "assets/skills/review/SKILL.md",
|
|
830
830
|
description: "Skill de revisión de código (Superpowers)",
|
|
831
|
-
version: "0.8.
|
|
831
|
+
version: "0.8.2",
|
|
832
832
|
sha256: "b19650ed4d1d4d9857a4dd5b7e08e91328a1a1fc9c45bcc2fb600fa9d0213279"
|
|
833
833
|
},
|
|
834
834
|
{
|
|
835
835
|
name: "receiving-code-review",
|
|
836
836
|
file: "assets/skills/receiving-code-review/SKILL.md",
|
|
837
837
|
description: "Skill de recibir y procesar feedback de code review",
|
|
838
|
-
version: "0.8.
|
|
838
|
+
version: "0.8.2",
|
|
839
839
|
sha256: "d761e884e71d8d3476ac734d287cae403a4024506a7d12e7361a448217c0a831"
|
|
840
840
|
},
|
|
841
841
|
{
|
|
842
842
|
name: "openspec-propose",
|
|
843
843
|
file: "assets/skills/openspec-propose/SKILL.md",
|
|
844
844
|
description: "Skill de generación de proposal (OpenSpec)",
|
|
845
|
-
version: "0.8.
|
|
845
|
+
version: "0.8.2",
|
|
846
846
|
sha256: "bb59100b9fd3c9f9a1ec6509fa975ec55bf7fc737df4fe73bb3e9bbc217b83f7"
|
|
847
847
|
},
|
|
848
848
|
{
|
|
849
849
|
name: "openspec-apply-change",
|
|
850
850
|
file: "assets/skills/openspec-apply-change/SKILL.md",
|
|
851
851
|
description: "Skill de aplicación de change (OpenSpec)",
|
|
852
|
-
version: "0.8.
|
|
852
|
+
version: "0.8.2",
|
|
853
853
|
sha256: "dfc823bf89fc7505e91ab6dee9c1f004be38a410bd3d88fb67b211b1b6cbb1d0"
|
|
854
854
|
},
|
|
855
855
|
{
|
|
856
856
|
name: "openspec-archive-change",
|
|
857
857
|
file: "assets/skills/openspec-archive-change/SKILL.md",
|
|
858
858
|
description: "Skill de archivo de change (OpenSpec)",
|
|
859
|
-
version: "0.8.
|
|
859
|
+
version: "0.8.2",
|
|
860
860
|
sha256: "16e4b561de7747283663fed602e506abf502452c49a8d4b86d7a5539c40f0195"
|
|
861
861
|
},
|
|
862
862
|
{
|
|
863
863
|
name: "openspec-explore",
|
|
864
864
|
file: "assets/skills/openspec-explore/SKILL.md",
|
|
865
865
|
description: "Modo explore para OpenSpec — thinking partner para explorar ideas, investigar problemas y clarificar requisitos antes/durante un cambio",
|
|
866
|
-
version: "0.8.
|
|
866
|
+
version: "0.8.2",
|
|
867
867
|
sha256: "37ae4aaf17ea71a395bab6dc4d9d61b9a49d7910f6692535ae8553244c46880b"
|
|
868
868
|
},
|
|
869
869
|
{
|
|
870
870
|
name: "using-git-worktrees",
|
|
871
871
|
file: "assets/skills/using-git-worktrees/SKILL.md",
|
|
872
872
|
description: "Skill de uso de git worktrees para aislamiento de trabajo",
|
|
873
|
-
version: "0.8.
|
|
873
|
+
version: "0.8.2",
|
|
874
874
|
sha256: "93341bc1b7c053618a8b6dc07e3615b77990d7b549a0201f5835d95fef67ce13"
|
|
875
875
|
},
|
|
876
876
|
{
|
|
877
877
|
name: "using-superpowers",
|
|
878
878
|
file: "assets/skills/using-superpowers/SKILL.md",
|
|
879
879
|
description: "Skill de orquestación de Superpowers skills",
|
|
880
|
-
version: "0.8.
|
|
880
|
+
version: "0.8.2",
|
|
881
881
|
sha256: "7e54536f96d2a379185a10bfc1e970850caa2561b6d8aca7080f0defea0381a4"
|
|
882
882
|
},
|
|
883
883
|
{
|
|
884
884
|
name: "writing-skills",
|
|
885
885
|
file: "assets/skills/writing-skills/SKILL.md",
|
|
886
886
|
description: "Skill de creación y edición de skills",
|
|
887
|
-
version: "0.8.
|
|
887
|
+
version: "0.8.2",
|
|
888
888
|
sha256: "3d76b906cee518a2b809febb70db95697a35b9f368986bb504b4120c3bfb437a"
|
|
889
889
|
},
|
|
890
890
|
{
|
|
891
891
|
name: "graceful-degradation",
|
|
892
892
|
file: "assets/skills/graceful-degradation/SKILL.md",
|
|
893
893
|
description: "Skill de degradación graceful cuando múltiples tools están indisponibles",
|
|
894
|
-
version: "0.8.
|
|
895
|
-
sha256: "
|
|
894
|
+
version: "0.8.2",
|
|
895
|
+
sha256: "79d0eeb2116382cb5db5b753277a333b7ca5682f4a541170b4a6158125c2cb50"
|
|
896
896
|
}
|
|
897
897
|
]
|
|
898
898
|
};
|
|
@@ -1260,12 +1260,6 @@ function patchOpenCodeConfig(projectRoot = findProjectRoot()) {
|
|
|
1260
1260
|
delete config.plugin;
|
|
1261
1261
|
changed = true;
|
|
1262
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
|
-
}
|
|
1269
1263
|
if (changed) {
|
|
1270
1264
|
writeOpenCodeConfig(configPath, config);
|
|
1271
1265
|
return { success: true, message: "Config actualizada (plugin legacy eliminado)" };
|
|
@@ -1303,11 +1297,21 @@ function resolveToolInstallLocation(toolsDir) {
|
|
|
1303
1297
|
}
|
|
1304
1298
|
function runTool(binary, args, cwd, timeout = 30000) {
|
|
1305
1299
|
const invocation = getCommandInvocation(binary, args);
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1300
|
+
try {
|
|
1301
|
+
execFileSync3(invocation.command, invocation.args, {
|
|
1302
|
+
cwd,
|
|
1303
|
+
stdio: "pipe",
|
|
1304
|
+
timeout
|
|
1305
|
+
});
|
|
1306
|
+
} catch (error) {
|
|
1307
|
+
const err = error;
|
|
1308
|
+
const stdout = err.stdout ? Buffer.from(err.stdout).toString().trim() : "";
|
|
1309
|
+
const stderr = err.stderr ? Buffer.from(err.stderr).toString().trim() : "";
|
|
1310
|
+
const detail = [stderr, stdout].filter(Boolean).join(`
|
|
1311
|
+
`) || err.message;
|
|
1312
|
+
const invocationStr = `${invocation.command} ${invocation.args.join(" ")}`;
|
|
1313
|
+
throw new Error(`${detail} (invocación: ${invocationStr}, cwd: ${cwd ?? process.cwd()})`);
|
|
1314
|
+
}
|
|
1311
1315
|
}
|
|
1312
1316
|
function findToolBinary(toolDir, name) {
|
|
1313
1317
|
const binDir = join7(toolDir, "bin");
|
|
@@ -1396,18 +1400,36 @@ async function installCodeGraph(toolsDir) {
|
|
|
1396
1400
|
const ext = process.platform === "win32" ? "zip" : "tar.gz";
|
|
1397
1401
|
const url = `https://github.com/colbymchenry/codegraph/releases/download/${tag}/codegraph-${target}.${ext}`;
|
|
1398
1402
|
try {
|
|
1399
|
-
archivePromotion = await downloadAndExtractWithRetry(url, cgToolDir,
|
|
1403
|
+
archivePromotion = await downloadAndExtractWithRetry(url, cgToolDir, 0, 180000, 2);
|
|
1400
1404
|
} catch (e2) {
|
|
1401
1405
|
return {
|
|
1402
1406
|
success: false,
|
|
1403
1407
|
message: `Error descargando CodeGraph ${tag}: ${e2.message}`
|
|
1404
1408
|
};
|
|
1405
1409
|
}
|
|
1406
|
-
|
|
1407
|
-
|
|
1410
|
+
const expectedBin = join7(cgToolDir, "bin", getExecutableName("codegraph"));
|
|
1411
|
+
let found = findToolBinary(cgToolDir, "codegraph");
|
|
1412
|
+
if (!found)
|
|
1413
|
+
found = findBinaryInDir(cgToolDir, "codegraph");
|
|
1414
|
+
if (!found) {
|
|
1408
1415
|
return failAfterExtraction(`Descarga de CodeGraph ${tag} completada pero no se encontró el binario en ${join7(cgToolDir, "bin")}.`);
|
|
1409
1416
|
}
|
|
1410
|
-
|
|
1417
|
+
try {
|
|
1418
|
+
if (found !== expectedBin) {
|
|
1419
|
+
mkdirSync4(join7(cgToolDir, "bin"), { recursive: true });
|
|
1420
|
+
if (found.toLowerCase().endsWith(".cmd") && expectedBin.toLowerCase().endsWith(".exe")) {
|
|
1421
|
+
localBin = found;
|
|
1422
|
+
} else {
|
|
1423
|
+
copyFileSync3(found, expectedBin);
|
|
1424
|
+
localBin = expectedBin;
|
|
1425
|
+
}
|
|
1426
|
+
} else {
|
|
1427
|
+
localBin = found;
|
|
1428
|
+
}
|
|
1429
|
+
} catch (error) {
|
|
1430
|
+
return failAfterExtraction(`CodeGraph fue descargado pero no se pudo materializar el binario local: ${error.message}`);
|
|
1431
|
+
}
|
|
1432
|
+
if (process.platform !== "win32" && localBin) {
|
|
1411
1433
|
try {
|
|
1412
1434
|
chmodSync(localBin, 493);
|
|
1413
1435
|
} catch {}
|
|
@@ -1422,7 +1444,8 @@ async function installCodeGraph(toolsDir) {
|
|
|
1422
1444
|
try {
|
|
1423
1445
|
runTool(localBin, ["init", "-i"], projectRoot, 120000);
|
|
1424
1446
|
} catch (error) {
|
|
1425
|
-
|
|
1447
|
+
const msg = error.message;
|
|
1448
|
+
indexWarning = `El índice no se pudo inicializar todavía: ${msg}. Sugerencia: ejecutá manualmente \`${localBin} init -i\` en ${projectRoot} o \`npx ostacky install-stack --scope local\` para reintentar. Si el path del binario (${localBin}) apunta a ${cgToolDir} y esperabas otro proyecto, verificá que corriste el comando dentro del proyecto correcto (con .git) y con --scope local.`;
|
|
1426
1449
|
}
|
|
1427
1450
|
try {
|
|
1428
1451
|
configureLocalTool(projectRoot, "codegraph", buildLocalMcpCommand(localBin, ["serve", "--mcp"]));
|
|
@@ -1621,7 +1644,7 @@ function uninstallStackConfig(paths) {
|
|
|
1621
1644
|
const mcp = config.mcp;
|
|
1622
1645
|
let changed = false;
|
|
1623
1646
|
if (mcp) {
|
|
1624
|
-
for (const name of ["codegraph", "engram"
|
|
1647
|
+
for (const name of ["codegraph", "engram"]) {
|
|
1625
1648
|
if (name in mcp) {
|
|
1626
1649
|
delete mcp[name];
|
|
1627
1650
|
removed.push(`mcp.${name}`);
|
|
@@ -1691,7 +1714,7 @@ var init_stack = __esm(() => {
|
|
|
1691
1714
|
// package.json
|
|
1692
1715
|
var package_default = {
|
|
1693
1716
|
name: "ostacky",
|
|
1694
|
-
version: "0.8.
|
|
1717
|
+
version: "0.8.2",
|
|
1695
1718
|
description: "Instalador interactivo de agentes y comandos para OpenCode",
|
|
1696
1719
|
type: "module",
|
|
1697
1720
|
bin: {
|
|
@@ -2866,7 +2889,7 @@ async function probeMcpServer(nodeExecutable, serverPath, cwd, statePath, option
|
|
|
2866
2889
|
params: {
|
|
2867
2890
|
protocolVersion: "2025-03-26",
|
|
2868
2891
|
capabilities: {},
|
|
2869
|
-
clientInfo: { name: "ostacky-installer", version: "0.8.
|
|
2892
|
+
clientInfo: { name: "ostacky-installer", version: "0.8.2" }
|
|
2870
2893
|
}
|
|
2871
2894
|
});
|
|
2872
2895
|
});
|
|
@@ -3175,12 +3198,13 @@ function uninstallAll(paths) {
|
|
|
3175
3198
|
for (const name of Object.keys(lockfile.mcpServers ?? {})) {
|
|
3176
3199
|
uninstallMcpServer(name, paths);
|
|
3177
3200
|
}
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3201
|
+
const ostackyPlugins = ["ostacky-plugin.ts", "engram.ts", "ostacky-guard.ts", "ostacky-controller.ts"];
|
|
3202
|
+
for (const file of ostackyPlugins) {
|
|
3203
|
+
const fp = join6(paths.plugins, file);
|
|
3204
|
+
if (existsSync5(fp)) {
|
|
3205
|
+
try {
|
|
3206
|
+
unlinkSync2(fp);
|
|
3207
|
+
} catch {}
|
|
3184
3208
|
}
|
|
3185
3209
|
}
|
|
3186
3210
|
clearLockfile(paths.root);
|
|
@@ -3365,6 +3389,16 @@ async function doInstallStack(toolsDir, projectRoot) {
|
|
|
3365
3389
|
const spin = L2();
|
|
3366
3390
|
let allOk = true;
|
|
3367
3391
|
const resolvedProjectRoot = projectRoot ?? dirname6(dirname6(toolsDir ?? join8(process.cwd(), ".opencode", "tools")));
|
|
3392
|
+
const resolvedToolsDir = toolsDir ?? join8(resolvedProjectRoot, ".opencode", "tools");
|
|
3393
|
+
v2.info(`Stack → projectRoot: ${resolvedProjectRoot} | toolsDir: ${resolvedToolsDir}`);
|
|
3394
|
+
try {
|
|
3395
|
+
const { homedir: homedir2 } = await import("os");
|
|
3396
|
+
const home = homedir2();
|
|
3397
|
+
const inHome = resolvedProjectRoot.replace(/\\/g, "/") === home.replace(/\\/g, "/");
|
|
3398
|
+
if (inHome && !existsSync7(join8(home, ".git"))) {
|
|
3399
|
+
v2.warn(`Estás instalando el stack en tu home (${home}). Si esperabas instalar en un proyecto, hacé cd al proyecto y usá --scope local.`);
|
|
3400
|
+
}
|
|
3401
|
+
} catch {}
|
|
3368
3402
|
spin.start("Instalando CodeGraph...");
|
|
3369
3403
|
const cg = await installCodeGraph(toolsDir);
|
|
3370
3404
|
spin.stop(cg.success ? `✓ ${cg.message}` : `✗ ${cg.message}`);
|
|
@@ -3395,6 +3429,7 @@ async function doInstallAll(manifest, paths) {
|
|
|
3395
3429
|
const spin = L2();
|
|
3396
3430
|
let errors = 0;
|
|
3397
3431
|
const isGlobal = isGlobalScope(paths);
|
|
3432
|
+
v2.info(`Scope → ${isGlobal ? "global" : "local"} | opencodeDir: ${paths.root} | tools: ${paths.tools}`);
|
|
3398
3433
|
if (!isGlobal) {
|
|
3399
3434
|
ensureToolDirs(paths.tools, ["codegraph", "engram"]);
|
|
3400
3435
|
} else {
|
|
@@ -3446,7 +3481,7 @@ async function doInstallAll(manifest, paths) {
|
|
|
3446
3481
|
}
|
|
3447
3482
|
}
|
|
3448
3483
|
try {
|
|
3449
|
-
const { copyFileSync: copyFileSync4, mkdirSync: mkdirSync5, existsSync: existsSync8 } = await import("fs");
|
|
3484
|
+
const { copyFileSync: copyFileSync4, mkdirSync: mkdirSync5, existsSync: existsSync8, rmSync: rmSync5 } = await import("fs");
|
|
3450
3485
|
const { join: join9 } = await import("path");
|
|
3451
3486
|
const { PACKAGE_ROOT: PACKAGE_ROOT2 } = await Promise.resolve().then(() => (init_github(), exports_github));
|
|
3452
3487
|
const { findProjectRoot: findProjectRoot3 } = await Promise.resolve().then(() => (init_fs(), exports_fs));
|
|
@@ -3456,10 +3491,12 @@ async function doInstallAll(manifest, paths) {
|
|
|
3456
3491
|
mkdirSync5(paths.plugins, { recursive: true });
|
|
3457
3492
|
copyFileSync4(src, dest);
|
|
3458
3493
|
}
|
|
3459
|
-
const
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3494
|
+
for (const legacy of ["ostacky-guard.ts", "ostacky-controller.ts"]) {
|
|
3495
|
+
const lp = join9(paths.plugins, legacy);
|
|
3496
|
+
if (existsSync8(lp))
|
|
3497
|
+
try {
|
|
3498
|
+
rmSync5(lp, { force: true });
|
|
3499
|
+
} catch {}
|
|
3463
3500
|
}
|
|
3464
3501
|
const srcEng = join9(PACKAGE_ROOT2, "assets", "plugins", "engram.ts");
|
|
3465
3502
|
const destEng = join9(paths.plugins, "engram.ts");
|
|
@@ -3474,10 +3511,15 @@ async function doInstallAll(manifest, paths) {
|
|
|
3474
3511
|
mkdirSync5(localPlugins, { recursive: true });
|
|
3475
3512
|
if (existsSync8(src))
|
|
3476
3513
|
copyFileSync4(src, join9(localPlugins, "ostacky-plugin.ts"));
|
|
3477
|
-
else if (existsSync8(legacySrc))
|
|
3478
|
-
copyFileSync4(legacySrc, join9(localPlugins, "ostacky-controller.ts"));
|
|
3479
3514
|
if (existsSync8(srcEng))
|
|
3480
3515
|
copyFileSync4(srcEng, join9(localPlugins, "engram.ts"));
|
|
3516
|
+
for (const legacy of ["ostacky-guard.ts", "ostacky-controller.ts"]) {
|
|
3517
|
+
const lp = join9(localPlugins, legacy);
|
|
3518
|
+
if (existsSync8(lp))
|
|
3519
|
+
try {
|
|
3520
|
+
rmSync5(lp, { force: true });
|
|
3521
|
+
} catch {}
|
|
3522
|
+
}
|
|
3481
3523
|
} catch {}
|
|
3482
3524
|
}
|
|
3483
3525
|
} catch {}
|
|
@@ -3718,10 +3760,10 @@ async function doUninstallTotal(paths) {
|
|
|
3718
3760
|
for (const name of Object.keys(lockfile.mcpServers ?? {})) {
|
|
3719
3761
|
pathsToDelete.push(join9(paths.mcp, name));
|
|
3720
3762
|
}
|
|
3721
|
-
ye(pathsToDelete.join(`
|
|
3722
|
-
`), `Se eliminarán ${pathsToDelete.length}
|
|
3763
|
+
ye([`Scope: ${paths.root}`, ...pathsToDelete].join(`
|
|
3764
|
+
`), `Se eliminarán ${pathsToDelete.length} item(s) trackeados en lockfile (safe-delete)`);
|
|
3723
3765
|
const confirm = await me({
|
|
3724
|
-
message: `¿Confirmar desinstalación de ${pathsToDelete.length} item(s)?`
|
|
3766
|
+
message: `¿Confirmar desinstalación de ${pathsToDelete.length} item(s) en ${paths.root}?`
|
|
3725
3767
|
});
|
|
3726
3768
|
onCancel(confirm);
|
|
3727
3769
|
if (!confirm) {
|
|
@@ -3729,7 +3771,7 @@ async function doUninstallTotal(paths) {
|
|
|
3729
3771
|
return;
|
|
3730
3772
|
}
|
|
3731
3773
|
uninstallAll(paths);
|
|
3732
|
-
v2.success(`${pathsToDelete.length} item(s) eliminado(s).`);
|
|
3774
|
+
v2.success(`${pathsToDelete.length} item(s) eliminado(s) + plugins Ostacky-owned limpiados.`);
|
|
3733
3775
|
const cleanEngram = await me({
|
|
3734
3776
|
message: "¿Deseas remover la configuración de Engram del proyecto (mcp.engram)?"
|
|
3735
3777
|
});
|
|
@@ -4010,9 +4052,122 @@ async function doUninstallMcpByName(name, paths) {
|
|
|
4010
4052
|
}
|
|
4011
4053
|
}
|
|
4012
4054
|
|
|
4055
|
+
// src/opencode.ts
|
|
4056
|
+
init_fs();
|
|
4057
|
+
import { execSync, execFileSync as execFileSync4 } from "node:child_process";
|
|
4058
|
+
function isOpencodeInstalled() {
|
|
4059
|
+
if (isCommandAvailable("opencode"))
|
|
4060
|
+
return true;
|
|
4061
|
+
try {
|
|
4062
|
+
execFileSync4("opencode", ["--version"], { stdio: "ignore" });
|
|
4063
|
+
return true;
|
|
4064
|
+
} catch {
|
|
4065
|
+
return false;
|
|
4066
|
+
}
|
|
4067
|
+
}
|
|
4068
|
+
function getOpencodeInstallCommand(platform = process.platform, _arch = process.arch) {
|
|
4069
|
+
if (platform === "win32") {
|
|
4070
|
+
return {
|
|
4071
|
+
display: "npm install -g opencode-ai",
|
|
4072
|
+
command: "npm",
|
|
4073
|
+
args: ["install", "-g", "opencode-ai"],
|
|
4074
|
+
note: "Alternativas Windows: choco install opencode | scoop install opencode | mise use -g github:anomalyco/opencode (ver https://opencode.ai/download). WSL recomendado."
|
|
4075
|
+
};
|
|
4076
|
+
}
|
|
4077
|
+
if (platform === "darwin") {
|
|
4078
|
+
return {
|
|
4079
|
+
display: "curl -fsSL https://opencode.ai/install | bash",
|
|
4080
|
+
command: "bash",
|
|
4081
|
+
args: ["-c", "curl -fsSL https://opencode.ai/install | bash"],
|
|
4082
|
+
note: "Alternativa macOS: brew install anomalyco/tap/opencode (tap oficial, más actualizado que brew install opencode)"
|
|
4083
|
+
};
|
|
4084
|
+
}
|
|
4085
|
+
return {
|
|
4086
|
+
display: "curl -fsSL https://opencode.ai/install | bash",
|
|
4087
|
+
command: "bash",
|
|
4088
|
+
args: ["-c", "curl -fsSL https://opencode.ai/install | bash"],
|
|
4089
|
+
note: "Alternativas Linux: npm install -g opencode-ai | bun add -g opencode-ai | brew install anomalyco/tap/opencode | paru -S opencode (Arch)"
|
|
4090
|
+
};
|
|
4091
|
+
}
|
|
4092
|
+
async function ensureOpencodeInstalled() {
|
|
4093
|
+
if (isOpencodeInstalled())
|
|
4094
|
+
return;
|
|
4095
|
+
const platform = process.platform;
|
|
4096
|
+
const info = getOpencodeInstallCommand(platform);
|
|
4097
|
+
v2.warn("OpenCode no detectado en este sistema.");
|
|
4098
|
+
ye(`${info.display}
|
|
4099
|
+
Docs: https://opencode.ai/download${info.note ? `
|
|
4100
|
+
${info.note}` : ""}`, `Instalación requerida (${platform})`);
|
|
4101
|
+
const shouldInstall = await me({
|
|
4102
|
+
message: "Ostacky no funciona sin OpenCode. ¿Querés instalar OpenCode ahora?",
|
|
4103
|
+
initialValue: true
|
|
4104
|
+
});
|
|
4105
|
+
if (BD(shouldInstall) || !shouldInstall) {
|
|
4106
|
+
fe("Instalación cancelada. Instalá OpenCode manualmente y volvé a ejecutar: https://opencode.ai/download");
|
|
4107
|
+
process.exit(1);
|
|
4108
|
+
}
|
|
4109
|
+
const spin = L2();
|
|
4110
|
+
spin.start(`Instalando OpenCode (${info.display})...`);
|
|
4111
|
+
try {
|
|
4112
|
+
if (platform === "win32") {
|
|
4113
|
+
execSync("npm install -g opencode-ai", { stdio: "inherit", shell: "cmd.exe" });
|
|
4114
|
+
} else {
|
|
4115
|
+
if (!isCommandAvailable("curl")) {
|
|
4116
|
+
spin.stop("curl no disponible");
|
|
4117
|
+
v2.warn("curl no está instalado — se intentará con npm como fallback.");
|
|
4118
|
+
execSync("npm install -g opencode-ai", { stdio: "inherit", shell: "/bin/bash" });
|
|
4119
|
+
} else {
|
|
4120
|
+
execSync("curl -fsSL https://opencode.ai/install | bash", { stdio: "inherit", shell: "/bin/bash" });
|
|
4121
|
+
}
|
|
4122
|
+
}
|
|
4123
|
+
spin.stop("Instalación ejecutada, verificando...");
|
|
4124
|
+
} catch (e2) {
|
|
4125
|
+
spin.stop("Fallo la instalación automática.");
|
|
4126
|
+
const msg = e2.message ?? String(e2);
|
|
4127
|
+
v2.error(`No se pudo instalar OpenCode automáticamente: ${msg}`);
|
|
4128
|
+
if (platform === "win32") {
|
|
4129
|
+
ye([
|
|
4130
|
+
"Intentá manualmente una de estas opciones:",
|
|
4131
|
+
" npm install -g opencode-ai",
|
|
4132
|
+
" choco install opencode",
|
|
4133
|
+
" scoop install opencode",
|
|
4134
|
+
" (WSL) curl -fsSL https://opencode.ai/install | bash",
|
|
4135
|
+
"Luego verificá: opencode --version",
|
|
4136
|
+
"Docs: https://opencode.ai/download"
|
|
4137
|
+
].join(`
|
|
4138
|
+
`), "Acción manual requerida");
|
|
4139
|
+
} else {
|
|
4140
|
+
ye([
|
|
4141
|
+
"Intentá manualmente:",
|
|
4142
|
+
` ${info.display}`,
|
|
4143
|
+
" npm install -g opencode-ai (si no tenés curl)",
|
|
4144
|
+
" brew install anomalyco/tap/opencode (macOS/Linux con brew)",
|
|
4145
|
+
"Luego verificá: opencode --version",
|
|
4146
|
+
"Docs: https://opencode.ai/download"
|
|
4147
|
+
].join(`
|
|
4148
|
+
`), "Acción manual requerida");
|
|
4149
|
+
}
|
|
4150
|
+
process.exit(1);
|
|
4151
|
+
}
|
|
4152
|
+
if (!isOpencodeInstalled()) {
|
|
4153
|
+
v2.error("OpenCode aún no está disponible en el PATH después de la instalación.");
|
|
4154
|
+
ye([
|
|
4155
|
+
"Probá cerrar y reabrir la terminal y ejecutar:",
|
|
4156
|
+
" opencode --version",
|
|
4157
|
+
"Si persiste, instalá manualmente:",
|
|
4158
|
+
` ${info.display}`,
|
|
4159
|
+
"Docs: https://opencode.ai/download"
|
|
4160
|
+
].join(`
|
|
4161
|
+
`), "Verificación falló");
|
|
4162
|
+
process.exit(1);
|
|
4163
|
+
}
|
|
4164
|
+
v2.success("OpenCode instalado correctamente ✓");
|
|
4165
|
+
}
|
|
4166
|
+
|
|
4013
4167
|
// src/prompts/index.ts
|
|
4014
4168
|
async function runInteractiveMenu(scope) {
|
|
4015
4169
|
we(" OpenCode Installer ");
|
|
4170
|
+
await ensureOpencodeInstalled();
|
|
4016
4171
|
const manifest = await loadManifest();
|
|
4017
4172
|
const paths = await resolveOpenCodePaths(scope ?? null);
|
|
4018
4173
|
if (!paths) {
|
|
@@ -4092,6 +4247,7 @@ async function runInteractiveMenu(scope) {
|
|
|
4092
4247
|
}
|
|
4093
4248
|
async function runInstallCommand(scope) {
|
|
4094
4249
|
we(" OpenCode Installer ");
|
|
4250
|
+
await ensureOpencodeInstalled();
|
|
4095
4251
|
const manifest = await loadManifest();
|
|
4096
4252
|
const paths = await resolveOpenCodePaths(scope ?? null);
|
|
4097
4253
|
if (!paths) {
|
|
@@ -4105,6 +4261,7 @@ async function runInstallCommand(scope) {
|
|
|
4105
4261
|
}
|
|
4106
4262
|
async function runAddAgentCommand(scope) {
|
|
4107
4263
|
we(" OpenCode Installer ");
|
|
4264
|
+
await ensureOpencodeInstalled();
|
|
4108
4265
|
const manifest = await loadManifest();
|
|
4109
4266
|
const paths = await resolveOpenCodePaths(scope ?? null);
|
|
4110
4267
|
if (!paths) {
|
|
@@ -4117,6 +4274,7 @@ async function runAddAgentCommand(scope) {
|
|
|
4117
4274
|
}
|
|
4118
4275
|
async function runAddCommandCommand(scope) {
|
|
4119
4276
|
we(" OpenCode Installer ");
|
|
4277
|
+
await ensureOpencodeInstalled();
|
|
4120
4278
|
const manifest = await loadManifest();
|
|
4121
4279
|
const paths = await resolveOpenCodePaths(scope ?? null);
|
|
4122
4280
|
if (!paths) {
|
|
@@ -4129,6 +4287,7 @@ async function runAddCommandCommand(scope) {
|
|
|
4129
4287
|
}
|
|
4130
4288
|
async function runAddSkillCommand(scope) {
|
|
4131
4289
|
we(" OpenCode Installer ");
|
|
4290
|
+
await ensureOpencodeInstalled();
|
|
4132
4291
|
const manifest = await loadManifest();
|
|
4133
4292
|
const paths = await resolveOpenCodePaths(scope ?? null);
|
|
4134
4293
|
if (!paths) {
|
|
@@ -4141,6 +4300,7 @@ async function runAddSkillCommand(scope) {
|
|
|
4141
4300
|
}
|
|
4142
4301
|
async function runAddMcpCommand(scope) {
|
|
4143
4302
|
we(" OpenCode Installer ");
|
|
4303
|
+
await ensureOpencodeInstalled();
|
|
4144
4304
|
const manifest = await loadManifest();
|
|
4145
4305
|
const paths = await resolveOpenCodePaths(scope ?? null);
|
|
4146
4306
|
if (!paths) {
|
|
@@ -4152,6 +4312,7 @@ async function runAddMcpCommand(scope) {
|
|
|
4152
4312
|
fe("Listo.");
|
|
4153
4313
|
}
|
|
4154
4314
|
async function runInstallStackCommand(scope) {
|
|
4315
|
+
await ensureOpencodeInstalled();
|
|
4155
4316
|
if (scope === "global") {
|
|
4156
4317
|
v2.error("install-stack requiere scope local; el stack vive en <proyecto>/.opencode/tools");
|
|
4157
4318
|
fe("Usá: npx ostacky install-stack --scope local");
|
|
@@ -4203,6 +4364,7 @@ async function runUninstallStackCommand(scope) {
|
|
|
4203
4364
|
}
|
|
4204
4365
|
async function runUpdateCommand(scope) {
|
|
4205
4366
|
we(" OpenCode Installer ");
|
|
4367
|
+
await ensureOpencodeInstalled();
|
|
4206
4368
|
const manifest = await loadLatestManifest();
|
|
4207
4369
|
const paths = await resolveOpenCodePaths(scope ?? null);
|
|
4208
4370
|
if (!paths) {
|
|
@@ -4366,7 +4528,7 @@ async function runUninstallMcpCommand(name, scope) {
|
|
|
4366
4528
|
// src/cli.ts
|
|
4367
4529
|
init_fs();
|
|
4368
4530
|
import { existsSync as existsSync8, statSync as statSync3, readFileSync as readFileSync6, readdirSync as readdirSync3 } from "node:fs";
|
|
4369
|
-
import { join as
|
|
4531
|
+
import { join as join10, dirname as dirname8 } from "node:path";
|
|
4370
4532
|
var HELP = `
|
|
4371
4533
|
ostacky — Instalador de agentes, comandos, skills y MCPs para OpenCode
|
|
4372
4534
|
|
|
@@ -4431,8 +4593,8 @@ var argvNoScope = withoutScopeArgs(process.argv);
|
|
|
4431
4593
|
var [, , cmd, subcmd] = argvNoScope;
|
|
4432
4594
|
async function runDoctorCommand() {
|
|
4433
4595
|
const cwd = process.cwd();
|
|
4434
|
-
const opencodeDir = findOpenCodeDir(cwd) ||
|
|
4435
|
-
const statePath =
|
|
4596
|
+
const opencodeDir = findOpenCodeDir(cwd) || join10(cwd, ".opencode");
|
|
4597
|
+
const statePath = join10(opencodeDir, "ostacky-state.json");
|
|
4436
4598
|
let hasError = false;
|
|
4437
4599
|
let hasWarn = false;
|
|
4438
4600
|
const check = (label, ok, warn = false) => {
|
|
@@ -4447,12 +4609,12 @@ async function runDoctorCommand() {
|
|
|
4447
4609
|
}
|
|
4448
4610
|
};
|
|
4449
4611
|
const pluginPaths = [
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4612
|
+
join10(cwd, "assets", "plugins", "ostacky-plugin.ts"),
|
|
4613
|
+
join10(opencodeDir, "plugins", "ostacky-plugin.ts"),
|
|
4614
|
+
join10(cwd, ".opencode", "plugins", "ostacky-plugin.ts"),
|
|
4615
|
+
join10(cwd, "assets", "plugins", "ostacky-controller.ts"),
|
|
4616
|
+
join10(opencodeDir, "plugins", "ostacky-controller.ts"),
|
|
4617
|
+
join10(cwd, ".opencode", "plugins", "ostacky-controller.ts")
|
|
4456
4618
|
];
|
|
4457
4619
|
const pluginActive = pluginPaths.some((p2) => existsSync8(p2));
|
|
4458
4620
|
try {
|
|
@@ -4525,8 +4687,8 @@ async function runDoctorCommand() {
|
|
|
4525
4687
|
check(`controller: ${e2.message}`, false);
|
|
4526
4688
|
}
|
|
4527
4689
|
try {
|
|
4528
|
-
const lockPid =
|
|
4529
|
-
const lockTs =
|
|
4690
|
+
const lockPid = join10(opencodeDir, "ostacky-state.json.lock.pid");
|
|
4691
|
+
const lockTs = join10(opencodeDir, "ostacky-state.json.lock.timestamp");
|
|
4530
4692
|
if (existsSync8(lockPid) || existsSync8(lockTs)) {
|
|
4531
4693
|
let ageStr = "";
|
|
4532
4694
|
try {
|
|
@@ -4551,15 +4713,15 @@ async function runDoctorCommand() {
|
|
|
4551
4713
|
}
|
|
4552
4714
|
const tools = ["codegraph", "engram"];
|
|
4553
4715
|
for (const t of tools) {
|
|
4554
|
-
const p2 =
|
|
4716
|
+
const p2 = join10(opencodeDir, "tools", t, "bin", t);
|
|
4555
4717
|
const pExe = p2 + ".exe";
|
|
4556
4718
|
check(`tool ${t}: ${existsSync8(p2) || existsSync8(pExe) ? "found" : "missing"}`, existsSync8(p2) || existsSync8(pExe), true);
|
|
4557
4719
|
}
|
|
4558
4720
|
try {
|
|
4559
|
-
const manifest = JSON.parse(readFileSync6(
|
|
4721
|
+
const manifest = JSON.parse(readFileSync6(join10(cwd, "manifest.json"), "utf-8"));
|
|
4560
4722
|
const expected = manifest.mcpServers?.find((x2) => x2.name === "ostacky-controller")?.sha256;
|
|
4561
4723
|
if (expected) {
|
|
4562
|
-
const actual = computeTreeHash(
|
|
4724
|
+
const actual = computeTreeHash(join10(cwd, "assets", "mcp", "ostacky-controller"));
|
|
4563
4725
|
check(`manifest hash: ${expected.slice(0, 8)} vs actual ${actual.slice(0, 8)}`, expected === actual);
|
|
4564
4726
|
if (expected !== actual)
|
|
4565
4727
|
console.log(" Run: bun run hash:update");
|
|
@@ -4574,7 +4736,7 @@ async function runDoctorCommand() {
|
|
|
4574
4736
|
} catch {}
|
|
4575
4737
|
}
|
|
4576
4738
|
try {
|
|
4577
|
-
const cacheDir =
|
|
4739
|
+
const cacheDir = join10(opencodeDir, "cache", "codegraph");
|
|
4578
4740
|
if (!existsSync8(cacheDir)) {
|
|
4579
4741
|
console.log("ℹ️ cache: no cache dir yet (ok)");
|
|
4580
4742
|
} else {
|
|
@@ -4582,7 +4744,7 @@ async function runDoctorCommand() {
|
|
|
4582
4744
|
let total = 0;
|
|
4583
4745
|
for (const f2 of files) {
|
|
4584
4746
|
try {
|
|
4585
|
-
total += statSync3(
|
|
4747
|
+
total += statSync3(join10(cacheDir, f2)).size;
|
|
4586
4748
|
} catch {}
|
|
4587
4749
|
}
|
|
4588
4750
|
const totalMB = (total / 1048576).toFixed(2);
|
|
@@ -4603,7 +4765,7 @@ async function runDoctorCommand() {
|
|
|
4603
4765
|
console.log(`⚠️ cache: check failed ${e2.message}`);
|
|
4604
4766
|
}
|
|
4605
4767
|
try {
|
|
4606
|
-
const secPath =
|
|
4768
|
+
const secPath = join10(cwd, "src", "security.ts");
|
|
4607
4769
|
if (!existsSync8(secPath)) {
|
|
4608
4770
|
console.log("⚠️ src/security.ts: missing (source-of-truth)");
|
|
4609
4771
|
hasWarn = true;
|
|
@@ -4635,8 +4797,8 @@ async function runDoctorCommand() {
|
|
|
4635
4797
|
async function runStatusCommand(args) {
|
|
4636
4798
|
const isJson = args.includes("--json");
|
|
4637
4799
|
const cwd = process.cwd();
|
|
4638
|
-
const opencodeDir = findOpenCodeDir(cwd) ||
|
|
4639
|
-
const statePath =
|
|
4800
|
+
const opencodeDir = findOpenCodeDir(cwd) || join10(cwd, ".opencode");
|
|
4801
|
+
const statePath = join10(opencodeDir, "ostacky-state.json");
|
|
4640
4802
|
if (!existsSync8(statePath)) {
|
|
4641
4803
|
console.log(isJson ? JSON.stringify({ error: "no state" }) : "No state file");
|
|
4642
4804
|
return;
|
|
@@ -4659,6 +4821,10 @@ async function runStatusCommand(args) {
|
|
|
4659
4821
|
}
|
|
4660
4822
|
}
|
|
4661
4823
|
async function main() {
|
|
4824
|
+
const needsOpencode = !cmd || cmd === "install" || cmd === "install-stack" || cmd === "update" || cmd === "add" && ["agent", "command", "skill", "mcp"].includes(subcmd ?? "");
|
|
4825
|
+
if (needsOpencode) {
|
|
4826
|
+
await ensureOpencodeInstalled();
|
|
4827
|
+
}
|
|
4662
4828
|
switch (cmd) {
|
|
4663
4829
|
case "install":
|
|
4664
4830
|
await runInstallCommand(scope);
|