ostacky 0.9.6 → 0.10.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 +95 -291
- package/assets/agents/ostacky.md +4 -4
- package/assets/commands/install-stack.md +11 -11
- package/assets/commands/opsx-sync.md +1 -0
- package/assets/mcp/ostacky-controller/controller-core.js +4 -0
- package/assets/mcp/ostacky-controller/index.js +227 -9
- package/assets/plugins/ostacky-controller/controller-core.ts +4 -0
- package/assets/plugins/ostacky-controller/index.ts +136 -23
- package/dist/cli.js +261 -86
- package/manifest.json +29 -29
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -222,6 +222,7 @@ import {
|
|
|
222
222
|
renameSync
|
|
223
223
|
} from "fs";
|
|
224
224
|
import { createHash as createHash2 } from "crypto";
|
|
225
|
+
import { homedir } from "os";
|
|
225
226
|
import { join, resolve, dirname, relative, basename } from "path";
|
|
226
227
|
import { execFileSync } from "child_process";
|
|
227
228
|
function findOpenCodeDir(startDir = process.cwd()) {
|
|
@@ -261,7 +262,23 @@ function isGitRepo(dir = process.cwd()) {
|
|
|
261
262
|
function initGitRepo(dir) {
|
|
262
263
|
execFileSync("git", ["init"], { cwd: dir, stdio: "pipe" });
|
|
263
264
|
}
|
|
265
|
+
function getGlobalOpenCodeDir(opts) {
|
|
266
|
+
const platform = opts?.platform ?? process.platform;
|
|
267
|
+
const env = opts?.env ?? process.env;
|
|
268
|
+
const home = opts?.home ?? homedir();
|
|
269
|
+
const xdg = env.XDG_CONFIG_HOME?.trim();
|
|
270
|
+
if (xdg)
|
|
271
|
+
return join(xdg, "opencode");
|
|
272
|
+
if (platform === "win32") {
|
|
273
|
+
const appdata = env.APPDATA?.trim();
|
|
274
|
+
if (appdata)
|
|
275
|
+
return join(appdata, "opencode");
|
|
276
|
+
}
|
|
277
|
+
return join(home, ".config", "opencode");
|
|
278
|
+
}
|
|
264
279
|
function getOpenCodeDirForScope(scope, cwd = process.cwd()) {
|
|
280
|
+
if (scope === "global")
|
|
281
|
+
return getGlobalOpenCodeDir();
|
|
265
282
|
return join(findProjectRoot(cwd), ".opencode");
|
|
266
283
|
}
|
|
267
284
|
function ensureOpenCodePaths(opencodeDir) {
|
|
@@ -624,47 +641,47 @@ var init_fs = __esm(() => {
|
|
|
624
641
|
var manifest_default;
|
|
625
642
|
var init_manifest = __esm(() => {
|
|
626
643
|
manifest_default = {
|
|
627
|
-
version: "0.
|
|
644
|
+
version: "0.10.0",
|
|
628
645
|
repo: "JaimeHoracio/Ostacky",
|
|
629
|
-
tag: "v0.
|
|
646
|
+
tag: "v0.10.0",
|
|
630
647
|
agents: [
|
|
631
648
|
{
|
|
632
649
|
name: "ostacky",
|
|
633
650
|
file: "assets/agents/ostacky.md",
|
|
634
|
-
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.
|
|
635
|
-
version: "0.
|
|
636
|
-
sha256: "
|
|
651
|
+
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.10.0: prune de skills obsoletas, lastHandoff, getAvailableTransitions, consecutiveFailures real, mem_session_summary automático al cierre.",
|
|
652
|
+
version: "0.10.0",
|
|
653
|
+
sha256: "d092ac75eb83f56354e5accf479ae6f0309253077b7071b2cd93bcc959f9dc63"
|
|
637
654
|
}
|
|
638
655
|
],
|
|
639
656
|
commands: [
|
|
640
657
|
{
|
|
641
658
|
name: "install-stack",
|
|
642
659
|
file: "assets/commands/install-stack.md",
|
|
643
|
-
description: "Instala el stack tecnológico del proyecto (CodeGraph, skills, OpenSpec, Engram, controller plugin). v0.
|
|
644
|
-
version: "0.
|
|
645
|
-
sha256: "
|
|
660
|
+
description: "Instala el stack tecnológico del proyecto (CodeGraph, skills, OpenSpec, Engram, controller plugin). v0.10.0: controller plugin como alma hard-gate, Context7 removido del stack.",
|
|
661
|
+
version: "0.10.0",
|
|
662
|
+
sha256: "2ed68d2451bfad3c142c943ca8cb4dba86616e6be8ef89f9af3849bcdc5d41c3"
|
|
646
663
|
},
|
|
647
664
|
{
|
|
648
665
|
name: "opsx-sync",
|
|
649
666
|
file: "assets/commands/opsx-sync.md",
|
|
650
667
|
description: "Sincroniza delta specs del change activo sin inicializar CodeGraph si ya existe índice",
|
|
651
|
-
version: "0.
|
|
652
|
-
sha256: "
|
|
668
|
+
version: "0.10.0",
|
|
669
|
+
sha256: "f883f9ad96f7d75778631d5cc13d8a1a9c3e6ec41396bbce8ce13cff2e66fca2"
|
|
653
670
|
}
|
|
654
671
|
],
|
|
655
672
|
mcpServers: [
|
|
656
673
|
{
|
|
657
674
|
name: "ostacky-controller",
|
|
658
675
|
file: "assets/mcp/ostacky-controller/",
|
|
659
|
-
description: "Máquina de estados persistida con @modelcontextprotocol/server SDK. v0.
|
|
660
|
-
version: "0.
|
|
661
|
-
sha256: "
|
|
676
|
+
description: "Máquina de estados persistida con @modelcontextprotocol/server SDK. v0.10.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.",
|
|
677
|
+
version: "0.10.0",
|
|
678
|
+
sha256: "9e6647f1540c21063d32bc3e163a7c0a379bbc1859416c3e7f55af39f2dd8e4e"
|
|
662
679
|
},
|
|
663
680
|
{
|
|
664
681
|
name: "openspec",
|
|
665
682
|
file: "assets/mcp/openspec/",
|
|
666
683
|
description: "MCP server local para OpenSpec - proposal, apply, archive, sync de cambios",
|
|
667
|
-
version: "0.
|
|
684
|
+
version: "0.10.0",
|
|
668
685
|
sha256: "bdb283fe8ec2dfcba2ec4f38191f4039e58cd7ae12e44e66310783b42c827638"
|
|
669
686
|
}
|
|
670
687
|
],
|
|
@@ -673,105 +690,105 @@ var init_manifest = __esm(() => {
|
|
|
673
690
|
name: "brainstorming",
|
|
674
691
|
file: "assets/skills/brainstorming/SKILL.md",
|
|
675
692
|
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)",
|
|
676
|
-
version: "0.
|
|
693
|
+
version: "0.10.0",
|
|
677
694
|
sha256: "f1013a86a8f27914d85a716e23c7fd32db1786d93615e8b0834b13d184308549"
|
|
678
695
|
},
|
|
679
696
|
{
|
|
680
697
|
name: "execution-mode-evaluation",
|
|
681
698
|
file: "assets/skills/execution-mode-evaluation/SKILL.md",
|
|
682
699
|
description: "Skill de análisis de modo de ejecución — output reconciliado con controller snapshot contract (recommendation field)",
|
|
683
|
-
version: "0.
|
|
700
|
+
version: "0.10.0",
|
|
684
701
|
sha256: "2158a6864c0912e868a9d720db7fd79441e6cc298e3320833d2527c9727c852e"
|
|
685
702
|
},
|
|
686
703
|
{
|
|
687
704
|
name: "tdd",
|
|
688
705
|
file: "assets/skills/tdd/SKILL.md",
|
|
689
706
|
description: "Skill de test-driven development (Superpowers)",
|
|
690
|
-
version: "0.
|
|
707
|
+
version: "0.10.0",
|
|
691
708
|
sha256: "f0ebfbbc43a8868d31a3fd2df37ae7f60fe8add09c6475862395a68269e5ff7f"
|
|
692
709
|
},
|
|
693
710
|
{
|
|
694
711
|
name: "subagent-driven-development",
|
|
695
712
|
file: "assets/skills/subagent-driven-development/SKILL.md",
|
|
696
713
|
description: "Skill de ejecución con subagentes (Superpowers) — ejecuta solo después de confirmación del coordinador Ostacky",
|
|
697
|
-
version: "0.
|
|
714
|
+
version: "0.10.0",
|
|
698
715
|
sha256: "1a42d714a9a13faf05f0bf7b580e8d837c2e77a5633839542ccce603023419e8"
|
|
699
716
|
},
|
|
700
717
|
{
|
|
701
718
|
name: "dispatching-parallel-agents",
|
|
702
719
|
file: "assets/skills/dispatching-parallel-agents/SKILL.md",
|
|
703
720
|
description: "Skill de dispatch paralelo de agentes (Superpowers)",
|
|
704
|
-
version: "0.
|
|
721
|
+
version: "0.10.0",
|
|
705
722
|
sha256: "7e7b712ef742d5631e302e6a71583e2bbbd33094da9281abfb84b43505fc3ef1"
|
|
706
723
|
},
|
|
707
724
|
{
|
|
708
725
|
name: "review",
|
|
709
726
|
file: "assets/skills/review/SKILL.md",
|
|
710
727
|
description: "Skill de revisión de código (Superpowers)",
|
|
711
|
-
version: "0.
|
|
728
|
+
version: "0.10.0",
|
|
712
729
|
sha256: "b19650ed4d1d4d9857a4dd5b7e08e91328a1a1fc9c45bcc2fb600fa9d0213279"
|
|
713
730
|
},
|
|
714
731
|
{
|
|
715
732
|
name: "receiving-code-review",
|
|
716
733
|
file: "assets/skills/receiving-code-review/SKILL.md",
|
|
717
734
|
description: "Skill de recibir y procesar feedback de code review",
|
|
718
|
-
version: "0.
|
|
735
|
+
version: "0.10.0",
|
|
719
736
|
sha256: "d761e884e71d8d3476ac734d287cae403a4024506a7d12e7361a448217c0a831"
|
|
720
737
|
},
|
|
721
738
|
{
|
|
722
739
|
name: "openspec-propose",
|
|
723
740
|
file: "assets/skills/openspec-propose/SKILL.md",
|
|
724
741
|
description: "Skill de generación de proposal (OpenSpec)",
|
|
725
|
-
version: "0.
|
|
742
|
+
version: "0.10.0",
|
|
726
743
|
sha256: "6a517f750f24c008208156a2d653e97ed30b1e53878aeac5ce394e573db0678f"
|
|
727
744
|
},
|
|
728
745
|
{
|
|
729
746
|
name: "openspec-apply-change",
|
|
730
747
|
file: "assets/skills/openspec-apply-change/SKILL.md",
|
|
731
748
|
description: "Skill de aplicación de change (OpenSpec)",
|
|
732
|
-
version: "0.
|
|
749
|
+
version: "0.10.0",
|
|
733
750
|
sha256: "dfc823bf89fc7505e91ab6dee9c1f004be38a410bd3d88fb67b211b1b6cbb1d0"
|
|
734
751
|
},
|
|
735
752
|
{
|
|
736
753
|
name: "openspec-archive-change",
|
|
737
754
|
file: "assets/skills/openspec-archive-change/SKILL.md",
|
|
738
755
|
description: "Skill de archivo de change (OpenSpec)",
|
|
739
|
-
version: "0.
|
|
756
|
+
version: "0.10.0",
|
|
740
757
|
sha256: "16e4b561de7747283663fed602e506abf502452c49a8d4b86d7a5539c40f0195"
|
|
741
758
|
},
|
|
742
759
|
{
|
|
743
760
|
name: "openspec-explore",
|
|
744
761
|
file: "assets/skills/openspec-explore/SKILL.md",
|
|
745
762
|
description: "Modo explore para OpenSpec — thinking partner para explorar ideas, investigar problemas y clarificar requisitos antes/durante un cambio",
|
|
746
|
-
version: "0.
|
|
763
|
+
version: "0.10.0",
|
|
747
764
|
sha256: "9eafc3a8c693b8d9916867ef764d49e5222da717fde501c957bb3f6c312d2415"
|
|
748
765
|
},
|
|
749
766
|
{
|
|
750
767
|
name: "using-git-worktrees",
|
|
751
768
|
file: "assets/skills/using-git-worktrees/SKILL.md",
|
|
752
769
|
description: "Skill de uso de git worktrees para aislamiento de trabajo",
|
|
753
|
-
version: "0.
|
|
770
|
+
version: "0.10.0",
|
|
754
771
|
sha256: "93341bc1b7c053618a8b6dc07e3615b77990d7b549a0201f5835d95fef67ce13"
|
|
755
772
|
},
|
|
756
773
|
{
|
|
757
774
|
name: "using-superpowers",
|
|
758
775
|
file: "assets/skills/using-superpowers/SKILL.md",
|
|
759
776
|
description: "Skill de orquestación de Superpowers skills",
|
|
760
|
-
version: "0.
|
|
777
|
+
version: "0.10.0",
|
|
761
778
|
sha256: "7e54536f96d2a379185a10bfc1e970850caa2561b6d8aca7080f0defea0381a4"
|
|
762
779
|
},
|
|
763
780
|
{
|
|
764
781
|
name: "writing-skills",
|
|
765
782
|
file: "assets/skills/writing-skills/SKILL.md",
|
|
766
783
|
description: "Skill de creación y edición de skills",
|
|
767
|
-
version: "0.
|
|
784
|
+
version: "0.10.0",
|
|
768
785
|
sha256: "3d76b906cee518a2b809febb70db95697a35b9f368986bb504b4120c3bfb437a"
|
|
769
786
|
},
|
|
770
787
|
{
|
|
771
788
|
name: "graceful-degradation",
|
|
772
789
|
file: "assets/skills/graceful-degradation/SKILL.md",
|
|
773
790
|
description: "Skill de degradación graceful cuando múltiples tools están indisponibles",
|
|
774
|
-
version: "0.
|
|
791
|
+
version: "0.10.0",
|
|
775
792
|
sha256: "363449bd9e6f800a0a3168667cf78f767fb105d86e4a4adadcc649d418139303"
|
|
776
793
|
}
|
|
777
794
|
]
|
|
@@ -1121,13 +1138,13 @@ var init_config = __esm(() => {
|
|
|
1121
1138
|
|
|
1122
1139
|
// src/stack.ts
|
|
1123
1140
|
import { chmodSync, copyFileSync as copyFileSync3, existsSync as existsSync6, mkdirSync as mkdirSync4, rmSync as rmSync4, unlinkSync as unlinkSync3 } from "fs";
|
|
1124
|
-
import { basename as
|
|
1141
|
+
import { basename as basename3, dirname as dirname5, join as join7, resolve as resolve3 } from "path";
|
|
1125
1142
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
1126
1143
|
function resolveToolInstallLocation(toolsDir) {
|
|
1127
1144
|
const resolvedToolsDir = resolve3(toolsDir ?? join7(findProjectRoot(), ".opencode", "tools"));
|
|
1128
1145
|
const possibleOpenCodeDir = dirname5(resolvedToolsDir);
|
|
1129
1146
|
return {
|
|
1130
|
-
projectRoot:
|
|
1147
|
+
projectRoot: basename3(possibleOpenCodeDir) === ".opencode" ? dirname5(possibleOpenCodeDir) : findProjectRoot(),
|
|
1131
1148
|
toolsDir: resolvedToolsDir
|
|
1132
1149
|
};
|
|
1133
1150
|
}
|
|
@@ -1566,8 +1583,9 @@ function checkDoctorV2(projectRoot) {
|
|
|
1566
1583
|
if (found) {
|
|
1567
1584
|
try {
|
|
1568
1585
|
const src = readFileSync6(found, "utf-8");
|
|
1569
|
-
const
|
|
1570
|
-
|
|
1586
|
+
const hasDefault = /export\s+default/.test(src);
|
|
1587
|
+
const hasLegacyPkg = src.includes("@opencode-ai/");
|
|
1588
|
+
if (hasDefault && !hasLegacyPkg)
|
|
1571
1589
|
lines.push(`✅ controller: plugin V2 (${found})`);
|
|
1572
1590
|
else
|
|
1573
1591
|
lines.push(`⚠️ controller: plugin legacy V1 en ${found} — no corre en V2, reinstalá con ostacky`);
|
|
@@ -1647,12 +1665,40 @@ function checkDoctorV2(projectRoot) {
|
|
|
1647
1665
|
for (const [name, entry] of Object.entries(servers)) {
|
|
1648
1666
|
if (!entry || typeof entry !== "object")
|
|
1649
1667
|
continue;
|
|
1650
|
-
const
|
|
1651
|
-
if (
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1668
|
+
const e = entry;
|
|
1669
|
+
if (e.type !== "local" && e.type !== "remote") {
|
|
1670
|
+
lines.push(`⚠️ mcp.servers.${name} sin type local|remote — V2 lo requiere (ver mcp-servers)`);
|
|
1671
|
+
continue;
|
|
1672
|
+
}
|
|
1673
|
+
if ("enabled" in e) {
|
|
1674
|
+
lines.push(`⚠️ mcp.servers.${name} usa enabled (forma V1) — V2 usa disabled invertido`);
|
|
1675
|
+
}
|
|
1676
|
+
if (typeof e.timeout === "number") {
|
|
1677
|
+
lines.push(`⚠️ mcp.servers.${name} timeout numérico (forma V1) — V2 usa timeout: { catalog, execution }`);
|
|
1678
|
+
}
|
|
1679
|
+
if (e.type === "local") {
|
|
1680
|
+
const cmd = e.command;
|
|
1681
|
+
if (!Array.isArray(cmd) || typeof cmd[0] !== "string") {
|
|
1682
|
+
lines.push(`⚠️ mcp.servers.${name} local sin command array — V2 requiere command: [bin, ...args]`);
|
|
1683
|
+
} else {
|
|
1684
|
+
const bin = cmd[0];
|
|
1685
|
+
const ok = isAbsolute(bin) ? existsSync8(bin) : lookupOnPath(bin) !== null;
|
|
1686
|
+
if (!ok)
|
|
1687
|
+
lines.push(`⚠️ mcp.servers.${name} command no existe (${bin}) — ¿proyecto movido? re-corré install`);
|
|
1688
|
+
}
|
|
1689
|
+
} else {
|
|
1690
|
+
if (typeof e.url !== "string" || !/^https?:\/\//.test(e.url)) {
|
|
1691
|
+
lines.push(`⚠️ mcp.servers.${name} remote sin url absoluta — V2 requiere url https://...`);
|
|
1692
|
+
}
|
|
1693
|
+
const oauth = e.oauth;
|
|
1694
|
+
if (oauth && typeof oauth === "object") {
|
|
1695
|
+
for (const k of ["clientId", "clientSecret", "callbackPort", "redirectUri"]) {
|
|
1696
|
+
if (k in oauth) {
|
|
1697
|
+
lines.push(`⚠️ mcp.servers.${name} oauth.${k} camelCase (forma V1) — V2 usa snake_case`);
|
|
1698
|
+
break;
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1656
1702
|
}
|
|
1657
1703
|
}
|
|
1658
1704
|
return lines;
|
|
@@ -1681,7 +1727,7 @@ var init_doctor_v2 = __esm(() => {
|
|
|
1681
1727
|
// package.json
|
|
1682
1728
|
var package_default = {
|
|
1683
1729
|
name: "ostacky",
|
|
1684
|
-
version: "0.
|
|
1730
|
+
version: "0.10.0",
|
|
1685
1731
|
description: "Instalador interactivo de agentes y comandos para OpenCode",
|
|
1686
1732
|
type: "module",
|
|
1687
1733
|
bin: {
|
|
@@ -2702,7 +2748,7 @@ import {
|
|
|
2702
2748
|
rmSync as rmSync3
|
|
2703
2749
|
} from "fs";
|
|
2704
2750
|
import { execFileSync as execFileSync2, spawn } from "child_process";
|
|
2705
|
-
import { dirname as dirname4, join as join6 } from "path";
|
|
2751
|
+
import { dirname as dirname4, join as join6, basename as basename2 } from "path";
|
|
2706
2752
|
import { pathToFileURL } from "url";
|
|
2707
2753
|
function createMcpConfigEntry(name, nodeExecutable, serverPath, statePath) {
|
|
2708
2754
|
const entry = {
|
|
@@ -2860,20 +2906,25 @@ async function probeMcpServer(nodeExecutable, serverPath, cwd, statePath, option
|
|
|
2860
2906
|
params: {
|
|
2861
2907
|
protocolVersion: "2025-03-26",
|
|
2862
2908
|
capabilities: {},
|
|
2863
|
-
clientInfo: { name: "ostacky-installer", version: "0.
|
|
2909
|
+
clientInfo: { name: "ostacky-installer", version: "0.10.0" }
|
|
2864
2910
|
}
|
|
2865
2911
|
});
|
|
2866
2912
|
});
|
|
2867
2913
|
} finally {
|
|
2868
2914
|
await new Promise((r) => setTimeout(r, 200));
|
|
2869
2915
|
if (statePath) {
|
|
2870
|
-
|
|
2916
|
+
const probeDir = dirname4(statePath);
|
|
2917
|
+
const probeBase = basename2(statePath);
|
|
2918
|
+
const probePrefix = probeBase + ".";
|
|
2919
|
+
for (const p of readdirSync2(probeDir).filter((entry) => entry === probeBase || entry.startsWith(probePrefix)).map((entry) => join6(probeDir, entry)).concat([
|
|
2871
2920
|
statePath,
|
|
2872
2921
|
statePath + ".backup",
|
|
2922
|
+
statePath + ".backup.1",
|
|
2923
|
+
statePath + ".backup.2",
|
|
2873
2924
|
statePath + ".lock.pid",
|
|
2874
2925
|
statePath + ".lock.timestamp",
|
|
2875
2926
|
statePath + ".tmp." + process.pid
|
|
2876
|
-
]) {
|
|
2927
|
+
])) {
|
|
2877
2928
|
try {
|
|
2878
2929
|
if (existsSync5(p))
|
|
2879
2930
|
rmSync3(p, { force: true });
|
|
@@ -3021,7 +3072,8 @@ async function installMcpServer(item, manifest, paths) {
|
|
|
3021
3072
|
const treeHash = computeTreeHash(src);
|
|
3022
3073
|
assertBundledTreeHash("mcpServers", item.name, treeHash);
|
|
3023
3074
|
const nodeExecutable = getVerifiedNodeExecutable();
|
|
3024
|
-
|
|
3075
|
+
await Promise.resolve().then(() => init_fs());
|
|
3076
|
+
const projectRoot = paths.root === getGlobalOpenCodeDir() ? paths.root : dirname4(paths.root);
|
|
3025
3077
|
const dest = join6(paths.mcp, item.name);
|
|
3026
3078
|
const staging = `${dest}.staging-${process.pid}-${Date.now()}`;
|
|
3027
3079
|
const statePath = item.name === "ostacky-controller" ? join6(paths.root, "ostacky-state.json") : undefined;
|
|
@@ -3269,11 +3321,23 @@ function printPostInstallSteps() {
|
|
|
3269
3321
|
`), "Próximos pasos");
|
|
3270
3322
|
}
|
|
3271
3323
|
async function resolveOpenCodePaths(scope, opts) {
|
|
3324
|
+
const effective = scope ?? "local";
|
|
3325
|
+
if (effective === "global") {
|
|
3326
|
+
await Promise.resolve().then(() => init_fs());
|
|
3327
|
+
const dir = getGlobalOpenCodeDir();
|
|
3328
|
+
try {
|
|
3329
|
+
const paths = ensureOpenCodePaths(dir);
|
|
3330
|
+
ye(dir, "Instalación global");
|
|
3331
|
+
return paths;
|
|
3332
|
+
} catch (e) {
|
|
3333
|
+
throw e;
|
|
3334
|
+
}
|
|
3335
|
+
}
|
|
3272
3336
|
const cwd = process.cwd();
|
|
3273
3337
|
const base = opts?.gitPreflight ? (await runGitPreflight(cwd)).projectRoot : cwd;
|
|
3274
3338
|
const dir = getOpenCodeDirForScope("local", base);
|
|
3275
|
-
if (scope && scope !== "local") {
|
|
3276
|
-
v2.warn(`Scope ${scope}
|
|
3339
|
+
if (scope && scope !== "local" && scope !== "global") {
|
|
3340
|
+
v2.warn(`Scope ${scope} no soportado; usando local en ${dir}`);
|
|
3277
3341
|
}
|
|
3278
3342
|
try {
|
|
3279
3343
|
const paths = ensureOpenCodePaths(dir);
|
|
@@ -3366,6 +3430,15 @@ async function doInstallStack(toolsDir, projectRoot) {
|
|
|
3366
3430
|
let allOk = true;
|
|
3367
3431
|
const resolvedProjectRoot = projectRoot ?? dirname6(dirname6(toolsDir ?? join8(process.cwd(), ".opencode", "tools")));
|
|
3368
3432
|
const resolvedToolsDir = toolsDir ?? join8(resolvedProjectRoot, ".opencode", "tools");
|
|
3433
|
+
try {
|
|
3434
|
+
await Promise.resolve().then(() => init_fs());
|
|
3435
|
+
const globalDir = getGlobalOpenCodeDir().replace(/\\/g, "/").toLowerCase();
|
|
3436
|
+
const toolsNorm = resolvedToolsDir.replace(/\\/g, "/").toLowerCase();
|
|
3437
|
+
if (toolsNorm === globalDir || toolsNorm.startsWith(globalDir + "/")) {
|
|
3438
|
+
v2.warn("Scope global: el stack (CodeGraph/Engram) es siempre local por proyecto — omitido aquí. Instalalo por proyecto con `npx ostacky install-stack --scope local`.");
|
|
3439
|
+
return true;
|
|
3440
|
+
}
|
|
3441
|
+
} catch {}
|
|
3369
3442
|
v2.info(`Stack → projectRoot: ${resolvedProjectRoot} | toolsDir: ${resolvedToolsDir}`);
|
|
3370
3443
|
try {
|
|
3371
3444
|
const { homedir } = await import("os");
|
|
@@ -3411,8 +3484,11 @@ async function doInstallStack(toolsDir, projectRoot) {
|
|
|
3411
3484
|
async function doInstallAll(manifest, paths, opts) {
|
|
3412
3485
|
const spin = L2();
|
|
3413
3486
|
let errors = 0;
|
|
3414
|
-
|
|
3415
|
-
|
|
3487
|
+
const isGlobal = opts?.scope === "global";
|
|
3488
|
+
v2.info(`Scope → ${isGlobal ? "global" : "local"} | opencodeDir: ${paths.root} | tools: ${paths.tools}`);
|
|
3489
|
+
if (isGlobal) {
|
|
3490
|
+
v2.info("Scope global: núcleo (agente/commands/skills/MCPs/plugins) en dir global; el stack (CodeGraph/Engram) es siempre local por proyecto — omitido aquí. Instalalo por proyecto con `npx ostacky install-stack --scope local`.");
|
|
3491
|
+
} else if (!opts?.noStack) {
|
|
3416
3492
|
ensureToolDirs(paths.tools, ["codegraph", "engram"]);
|
|
3417
3493
|
}
|
|
3418
3494
|
for (const agent of manifest.agents) {
|
|
@@ -3490,7 +3566,9 @@ async function doInstallAll(manifest, paths, opts) {
|
|
|
3490
3566
|
} catch {}
|
|
3491
3567
|
let stackOk = true;
|
|
3492
3568
|
let missingTools = [];
|
|
3493
|
-
if (
|
|
3569
|
+
if (isGlobal) {
|
|
3570
|
+
v2.info("Scope global: stack omitido (siempre local por proyecto).");
|
|
3571
|
+
} else if (opts?.noStack) {
|
|
3494
3572
|
v2.info("Stack omitido (--no-stack): solo núcleo (agente/commands/skills/MCPs/plugins).");
|
|
3495
3573
|
} else {
|
|
3496
3574
|
v2.info("Instalando stack de herramientas...");
|
|
@@ -3500,7 +3578,7 @@ async function doInstallAll(manifest, paths, opts) {
|
|
|
3500
3578
|
}
|
|
3501
3579
|
const codegraphDir = join8(paths.tools, "codegraph");
|
|
3502
3580
|
const engramDir = join8(paths.tools, "engram");
|
|
3503
|
-
if (!opts?.noStack) {
|
|
3581
|
+
if (!isGlobal && !opts?.noStack) {
|
|
3504
3582
|
if (!existsSync7(codegraphDir) || !findBinaryInDir(codegraphDir, "codegraph"))
|
|
3505
3583
|
missingTools.push("CodeGraph");
|
|
3506
3584
|
if (!existsSync7(engramDir) || !findBinaryInDir(engramDir, "engram"))
|
|
@@ -3523,12 +3601,14 @@ async function doInstallAll(manifest, paths, opts) {
|
|
|
3523
3601
|
v2.warn("Además el stack quedó parcial: reintentá con `npx ostacky install-stack --scope local`.");
|
|
3524
3602
|
}
|
|
3525
3603
|
try {
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3604
|
+
if (!isGlobal) {
|
|
3605
|
+
const projectRoot = dirname6(paths.root);
|
|
3606
|
+
const gi = ensureGitignore(projectRoot);
|
|
3607
|
+
if (gi.created)
|
|
3608
|
+
v2.info(`.gitignore creado con patrones Ostacky`);
|
|
3609
|
+
else if (gi.updated)
|
|
3610
|
+
v2.info(`.gitignore actualizado: ${gi.patternsAdded.join(", ")}`);
|
|
3611
|
+
}
|
|
3532
3612
|
} catch {}
|
|
3533
3613
|
return coreOk && !stackPartial;
|
|
3534
3614
|
}
|
|
@@ -4177,7 +4257,7 @@ async function runInteractiveMenu(scope) {
|
|
|
4177
4257
|
we(" OpenCode Installer ");
|
|
4178
4258
|
await ensureOpencodeInstalled();
|
|
4179
4259
|
const manifest = await loadManifest();
|
|
4180
|
-
const paths = await resolveOpenCodePaths(scope ?? null, { gitPreflight:
|
|
4260
|
+
const paths = await resolveOpenCodePaths(scope ?? null, { gitPreflight: scope !== "global" });
|
|
4181
4261
|
if (!paths) {
|
|
4182
4262
|
fe("Instalación cancelada.");
|
|
4183
4263
|
return;
|
|
@@ -4199,7 +4279,7 @@ async function runInteractiveMenu(scope) {
|
|
|
4199
4279
|
onCancel(action);
|
|
4200
4280
|
switch (action) {
|
|
4201
4281
|
case "all":
|
|
4202
|
-
if (!await doInstallAll(manifest, paths))
|
|
4282
|
+
if (!await doInstallAll(manifest, paths, { scope: scope ?? "local" }))
|
|
4203
4283
|
process.exitCode = 1;
|
|
4204
4284
|
printPostInstallSteps();
|
|
4205
4285
|
fe(process.exitCode ? "Instalación parcial." : "Listo.");
|
|
@@ -4225,6 +4305,11 @@ async function runInteractiveMenu(scope) {
|
|
|
4225
4305
|
fe("Listo.");
|
|
4226
4306
|
break;
|
|
4227
4307
|
case "stack": {
|
|
4308
|
+
if ((scope ?? "local") === "global") {
|
|
4309
|
+
v2.warn("El stack (CodeGraph/Engram) es siempre local por proyecto. En scope global se omite; usá `npx ostacky install-stack --scope local` dentro del proyecto.");
|
|
4310
|
+
fe("Listo.");
|
|
4311
|
+
break;
|
|
4312
|
+
}
|
|
4228
4313
|
ensureToolDirs(paths.tools, ["codegraph", "engram"]);
|
|
4229
4314
|
const stackOk = await doInstallStack(paths.tools, dirname7(paths.root));
|
|
4230
4315
|
if (!stackOk)
|
|
@@ -4251,12 +4336,12 @@ async function runInstallCommand(scope, opts) {
|
|
|
4251
4336
|
we(" OpenCode Installer ");
|
|
4252
4337
|
await ensureOpencodeInstalled();
|
|
4253
4338
|
const manifest = await loadManifest();
|
|
4254
|
-
const paths = await resolveOpenCodePaths(scope ?? null, { gitPreflight:
|
|
4339
|
+
const paths = await resolveOpenCodePaths(scope ?? null, { gitPreflight: scope !== "global" });
|
|
4255
4340
|
if (!paths) {
|
|
4256
4341
|
fe("Cancelado.");
|
|
4257
4342
|
return;
|
|
4258
4343
|
}
|
|
4259
|
-
if (!await doInstallAll(manifest, paths, opts))
|
|
4344
|
+
if (!await doInstallAll(manifest, paths, { ...opts, scope: scope ?? "local" }))
|
|
4260
4345
|
process.exitCode = 1;
|
|
4261
4346
|
printPostInstallSteps();
|
|
4262
4347
|
fe(process.exitCode ? "Instalación parcial." : "Instalación completada.");
|
|
@@ -4515,59 +4600,142 @@ async function runUninstallMcpCommand(name, scope) {
|
|
|
4515
4600
|
}
|
|
4516
4601
|
|
|
4517
4602
|
// src/cli.ts
|
|
4518
|
-
init_fs();
|
|
4519
4603
|
import { existsSync as existsSync9, statSync as statSync3, readFileSync as readFileSync7, readdirSync as readdirSync4 } from "node:fs";
|
|
4520
4604
|
import { join as join11, dirname as dirname8 } from "node:path";
|
|
4605
|
+
import { spawnSync } from "node:child_process";
|
|
4606
|
+
init_fs();
|
|
4607
|
+
|
|
4608
|
+
// src/version-check.ts
|
|
4609
|
+
function parseMajor(version) {
|
|
4610
|
+
if (!version)
|
|
4611
|
+
return null;
|
|
4612
|
+
const m = /v?(\d+)\.\d+\.\d+/.exec(version.trim());
|
|
4613
|
+
if (!m)
|
|
4614
|
+
return null;
|
|
4615
|
+
const major = parseInt(m[1], 10);
|
|
4616
|
+
return Number.isNaN(major) ? null : major;
|
|
4617
|
+
}
|
|
4618
|
+
function compareVersions(a, b) {
|
|
4619
|
+
const pa = a.replace(/^v/, "").split(".").map((n) => parseInt(n, 10) || 0);
|
|
4620
|
+
const pb = b.replace(/^v/, "").split(".").map((n) => parseInt(n, 10) || 0);
|
|
4621
|
+
for (let i = 0;i < 3; i++) {
|
|
4622
|
+
if ((pa[i] ?? 0) < (pb[i] ?? 0))
|
|
4623
|
+
return -1;
|
|
4624
|
+
if ((pa[i] ?? 0) > (pb[i] ?? 0))
|
|
4625
|
+
return 1;
|
|
4626
|
+
}
|
|
4627
|
+
return 0;
|
|
4628
|
+
}
|
|
4629
|
+
async function fetchLatestNpmVersion(pkg = "ostacky", timeoutMs = 5000) {
|
|
4630
|
+
try {
|
|
4631
|
+
const res = await fetch(`https://registry.npmjs.org/${pkg}/latest`, {
|
|
4632
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
4633
|
+
headers: { "User-Agent": "ostacky-installer" }
|
|
4634
|
+
});
|
|
4635
|
+
if (!res.ok)
|
|
4636
|
+
return null;
|
|
4637
|
+
const data = await res.json();
|
|
4638
|
+
return typeof data.version === "string" ? data.version : null;
|
|
4639
|
+
} catch {
|
|
4640
|
+
return null;
|
|
4641
|
+
}
|
|
4642
|
+
}
|
|
4643
|
+
function decideOffer(installed, latest) {
|
|
4644
|
+
if (!latest || compareVersions(installed, latest) >= 0) {
|
|
4645
|
+
return { kind: "none", installed, latest };
|
|
4646
|
+
}
|
|
4647
|
+
if (parseMajor(installed) === 1) {
|
|
4648
|
+
return { kind: "migrate-v1", installed, latest };
|
|
4649
|
+
}
|
|
4650
|
+
return { kind: "update-available", installed, latest };
|
|
4651
|
+
}
|
|
4652
|
+
function canPrompt(argv = process.argv, isTTY = process.stdin.isTTY === true) {
|
|
4653
|
+
if (!isTTY)
|
|
4654
|
+
return false;
|
|
4655
|
+
return !argv.includes("--yes") && !argv.includes("--no-interactive") && !argv.includes("--non-interactive");
|
|
4656
|
+
}
|
|
4657
|
+
|
|
4658
|
+
// src/cli.ts
|
|
4659
|
+
async function maybeOfferUpdateOrMigrate(installed, scope) {
|
|
4660
|
+
const latest = await fetchLatestNpmVersion();
|
|
4661
|
+
const offer = decideOffer(installed, latest);
|
|
4662
|
+
if (offer.kind === "none" || !offer.latest)
|
|
4663
|
+
return;
|
|
4664
|
+
if (!canPrompt()) {
|
|
4665
|
+
console.error(`Hay una versión más nueva de ostacky: ${offer.latest} (instalada: ${installed}). Re-ejecutá con npx ostacky@latest.`);
|
|
4666
|
+
return;
|
|
4667
|
+
}
|
|
4668
|
+
if (offer.kind === "migrate-v1") {
|
|
4669
|
+
const confirm = await me({
|
|
4670
|
+
message: `Tenés Ostacky v1 instalada (${installed}) y la última es ${offer.latest} (v2). ¿Desinstalar v1 e instalar v2? (los artefactos legacy se limpian automáticamente)${scope === "global" ? " (scope global)" : ""}`,
|
|
4671
|
+
initialValue: true
|
|
4672
|
+
});
|
|
4673
|
+
if (BD(confirm) || !confirm)
|
|
4674
|
+
return;
|
|
4675
|
+
} else {
|
|
4676
|
+
const confirm = await me({
|
|
4677
|
+
message: `Hay una versión más nueva de ostacky: ${offer.latest} (instalada: ${installed}). ¿Actualizar y re-ejecutar con @latest?`,
|
|
4678
|
+
initialValue: true
|
|
4679
|
+
});
|
|
4680
|
+
if (BD(confirm) || !confirm)
|
|
4681
|
+
return;
|
|
4682
|
+
}
|
|
4683
|
+
const userArgs = process.argv.slice(2);
|
|
4684
|
+
const invocation = getCommandInvocation("npx", ["ostacky@latest", ...userArgs]);
|
|
4685
|
+
const res = spawnSync(invocation.command, invocation.args, { stdio: "inherit" });
|
|
4686
|
+
process.exit(res.status ?? 0);
|
|
4687
|
+
}
|
|
4521
4688
|
var HELP = `
|
|
4522
4689
|
ostacky — Instalador de agentes, comandos, skills y MCPs para OpenCode
|
|
4523
4690
|
|
|
4524
4691
|
Uso:
|
|
4525
|
-
npx ostacky [--scope local]
|
|
4526
|
-
npx ostacky install [--scope local] [--no-stack] Instalar TODO (agente + skills + MCPs + CodeGraph + OpenSpec + Engram; --no-stack omite el stack)
|
|
4527
|
-
npx ostacky add agent [--scope local]
|
|
4528
|
-
npx ostacky add command [--scope local]
|
|
4529
|
-
npx ostacky add skill [--scope local]
|
|
4530
|
-
npx ostacky add mcp [--scope local]
|
|
4531
|
-
npx ostacky install-stack [--scope local] Instalar solo el stack de herramientas (CodeGraph, OpenSpec, Engram)
|
|
4692
|
+
npx ostacky [--scope local|global] Menú interactivo (default: local en <proyecto>/.opencode)
|
|
4693
|
+
npx ostacky install [--scope local|global] [--no-stack] Instalar TODO (agente + skills + MCPs + CodeGraph + OpenSpec + Engram; --no-stack omite el stack)
|
|
4694
|
+
npx ostacky add agent [--scope local|global] Agregar agente(s)
|
|
4695
|
+
npx ostacky add command [--scope local|global] Agregar command(s)
|
|
4696
|
+
npx ostacky add skill [--scope local|global] Agregar skill(s)
|
|
4697
|
+
npx ostacky add mcp [--scope local|global] Agregar MCP server(s)
|
|
4698
|
+
npx ostacky install-stack [--scope local] Instalar solo el stack de herramientas (CodeGraph, OpenSpec, Engram — siempre local por proyecto)
|
|
4532
4699
|
npx ostacky uninstall-stack [--scope local] Remover la configuración del stack del proyecto
|
|
4533
4700
|
npx ostacky doctor Diagnostica locks, tools, state health
|
|
4534
4701
|
npx ostacky status [--json] Muestra estado del controller sin MCP
|
|
4535
|
-
npx ostacky update [--scope local]
|
|
4536
|
-
npx ostacky uninstall [--scope local]
|
|
4537
|
-
npx ostacky uninstall agent [--scope local] Desinstalar agente(s)
|
|
4538
|
-
npx ostacky uninstall command [--scope local] Desinstalar command(s)
|
|
4539
|
-
npx ostacky uninstall skill [--scope local] Desinstalar skill(s)
|
|
4540
|
-
npx ostacky uninstall mcp [--scope local] Desinstalar MCP server(s)
|
|
4702
|
+
npx ostacky update [--scope local|global] Actualizar instalación
|
|
4703
|
+
npx ostacky uninstall [--scope local|global] Desinstalar todo
|
|
4704
|
+
npx ostacky uninstall agent [--scope local|global] Desinstalar agente(s)
|
|
4705
|
+
npx ostacky uninstall command [--scope local|global] Desinstalar command(s)
|
|
4706
|
+
npx ostacky uninstall skill [--scope local|global] Desinstalar skill(s)
|
|
4707
|
+
npx ostacky uninstall mcp [--scope local|global] Desinstalar MCP server(s)
|
|
4541
4708
|
npx ostacky --help Mostrar esta ayuda
|
|
4542
4709
|
npx ostacky --version Mostrar versión
|
|
4543
4710
|
|
|
4544
4711
|
Scope:
|
|
4545
|
-
--scope local
|
|
4546
|
-
|
|
4712
|
+
--scope local Escribe en <proyecto>/.opencode (default; no pregunta global)
|
|
4713
|
+
--scope global Escribe en el config dir global de OpenCode (~/.config/opencode, respeta XDG_CONFIG_HOME)
|
|
4714
|
+
Sin flag Asume local implícito
|
|
4547
4715
|
`.trim();
|
|
4548
4716
|
function parseScopeArg(argv = process.argv) {
|
|
4549
4717
|
for (let i = 0;i < argv.length; i++) {
|
|
4550
4718
|
const arg = argv[i];
|
|
4551
4719
|
if (arg === "--scope" && i + 1 < argv.length) {
|
|
4552
4720
|
const v = argv[i + 1];
|
|
4553
|
-
if (v === "local")
|
|
4721
|
+
if (v === "local" || v === "global")
|
|
4554
4722
|
return v;
|
|
4555
|
-
if (v === "
|
|
4556
|
-
console.error(`Error: --scope
|
|
4723
|
+
if (v === "auto") {
|
|
4724
|
+
console.error(`Error: --scope auto removido; usá --scope local o --scope global explícito.`);
|
|
4557
4725
|
process.exit(1);
|
|
4558
4726
|
}
|
|
4559
4727
|
}
|
|
4560
4728
|
if (arg.startsWith("--scope=")) {
|
|
4561
4729
|
const v = arg.split("=")[1];
|
|
4562
|
-
if (v === "local")
|
|
4730
|
+
if (v === "local" || v === "global")
|
|
4563
4731
|
return v;
|
|
4564
|
-
if (v === "
|
|
4565
|
-
console.error(`Error: --scope
|
|
4732
|
+
if (v === "auto") {
|
|
4733
|
+
console.error(`Error: --scope auto removido; usá --scope local o --scope global explícito.`);
|
|
4566
4734
|
process.exit(1);
|
|
4567
4735
|
}
|
|
4568
4736
|
}
|
|
4569
4737
|
if (arg === "-g") {
|
|
4570
|
-
console.error(`Error: -g
|
|
4738
|
+
console.error(`Error: -g removido; usá --scope global (instalación global) o --scope local (default por proyecto).`);
|
|
4571
4739
|
process.exit(1);
|
|
4572
4740
|
}
|
|
4573
4741
|
}
|
|
@@ -4928,10 +5096,16 @@ async function main() {
|
|
|
4928
5096
|
await runInstallCommand(scope, { noStack });
|
|
4929
5097
|
break;
|
|
4930
5098
|
case "install-stack":
|
|
4931
|
-
|
|
5099
|
+
if (scope === "global") {
|
|
5100
|
+
console.error(`Aviso: el stack (CodeGraph/Engram) se instala siempre local por proyecto. Ignorando --scope global y usando local.`);
|
|
5101
|
+
}
|
|
5102
|
+
await runInstallStackCommand(null);
|
|
4932
5103
|
break;
|
|
4933
5104
|
case "uninstall-stack":
|
|
4934
|
-
|
|
5105
|
+
if (scope === "global") {
|
|
5106
|
+
console.error(`Aviso: el stack es siempre local por proyecto. Ignorando --scope global y usando local.`);
|
|
5107
|
+
}
|
|
5108
|
+
await runUninstallStackCommand(null);
|
|
4935
5109
|
break;
|
|
4936
5110
|
case "add":
|
|
4937
5111
|
if (subcmd === "agent") {
|
|
@@ -4989,6 +5163,7 @@ async function main() {
|
|
|
4989
5163
|
console.error(`Comando desconocido: "${cmd}". Usá --help para ver los comandos disponibles.`);
|
|
4990
5164
|
process.exit(1);
|
|
4991
5165
|
}
|
|
5166
|
+
await maybeOfferUpdateOrMigrate(package_default.version, scope);
|
|
4992
5167
|
await runInteractiveMenu(scope);
|
|
4993
5168
|
}
|
|
4994
5169
|
}
|