ostacky 0.10.2 → 0.10.3

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/dist/cli.js CHANGED
@@ -263,17 +263,14 @@ function initGitRepo(dir) {
263
263
  execFileSync("git", ["init"], { cwd: dir, stdio: "pipe" });
264
264
  }
265
265
  function getGlobalOpenCodeDir(opts) {
266
- const platform = opts?.platform ?? process.platform;
267
266
  const env = opts?.env ?? process.env;
268
267
  const home = opts?.home ?? homedir();
268
+ const explicit = env.OPENCODE_CONFIG_DIR?.trim();
269
+ if (explicit)
270
+ return explicit;
269
271
  const xdg = env.XDG_CONFIG_HOME?.trim();
270
272
  if (xdg)
271
273
  return join(xdg, "opencode");
272
- if (platform === "win32") {
273
- const appdata = env.APPDATA?.trim();
274
- if (appdata)
275
- return join(appdata, "opencode");
276
- }
277
274
  return join(home, ".config", "opencode");
278
275
  }
279
276
  function getOpenCodeDirForScope(scope, cwd = process.cwd()) {
@@ -641,31 +638,31 @@ var init_fs = __esm(() => {
641
638
  var manifest_default;
642
639
  var init_manifest = __esm(() => {
643
640
  manifest_default = {
644
- version: "0.10.2",
641
+ version: "0.10.3",
645
642
  repo: "JaimeHoracio/Ostacky",
646
- tag: "v0.10.2",
643
+ tag: "v0.10.3",
647
644
  agents: [
648
645
  {
649
646
  name: "ostacky",
650
647
  file: "assets/agents/ostacky.md",
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.2: prune de skills obsoletas, lastHandoff, getAvailableTransitions, consecutiveFailures real, mem_session_summary automático al cierre.",
652
- version: "0.10.2",
653
- sha256: "b3db2a27d7b8249b4552d4f63353d893ec1b8c2abda44f375de65cd1d4364419"
648
+ 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.3: prune de skills obsoletas, lastHandoff, getAvailableTransitions, consecutiveFailures real, mem_session_summary automático al cierre.",
649
+ version: "0.10.3",
650
+ sha256: "c8f18e81f3cfb2778656bd3e9b391d8521bec7797436a8539e1b7f0b7160597c"
654
651
  }
655
652
  ],
656
653
  commands: [
657
654
  {
658
655
  name: "install-stack",
659
656
  file: "assets/commands/install-stack.md",
660
- description: "Instala el stack tecnológico del proyecto (CodeGraph, skills, OpenSpec, Engram, controller plugin). v0.10.2: controller plugin como alma hard-gate, Context7 removido del stack.",
661
- version: "0.10.2",
662
- sha256: "b4845d7b6306b1a8c4914a4cf5a4390a4d3e79f1226de72b0ef2e95157c1f58e"
657
+ description: "Instala el stack tecnológico del proyecto (CodeGraph, skills, OpenSpec, Engram, controller plugin). v0.10.3: controller plugin como alma hard-gate, Context7 removido del stack.",
658
+ version: "0.10.3",
659
+ sha256: "eefea7d0efb9e89184bf2bbdcd8912928eb168d75e7b129526010fb1a5e81d50"
663
660
  },
664
661
  {
665
662
  name: "opsx-sync",
666
663
  file: "assets/commands/opsx-sync.md",
667
664
  description: "Sincroniza delta specs del change activo sin inicializar CodeGraph si ya existe índice",
668
- version: "0.10.2",
665
+ version: "0.10.3",
669
666
  sha256: "f883f9ad96f7d75778631d5cc13d8a1a9c3e6ec41396bbce8ce13cff2e66fca2"
670
667
  }
671
668
  ],
@@ -673,16 +670,18 @@ var init_manifest = __esm(() => {
673
670
  {
674
671
  name: "ostacky-controller",
675
672
  file: "assets/mcp/ostacky-controller/",
676
- description: "Máquina de estados persistida con @modelcontextprotocol/server SDK. v0.10.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.",
677
- version: "0.10.2",
678
- sha256: "c78d2a8f7ce87d373cbac9135235ef1c90f45380fafc4135fe22c2b55c4423b6"
673
+ description: "Máquina de estados persistida con @modelcontextprotocol/server SDK. v0.10.3: 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.",
674
+ version: "0.10.3",
675
+ sha256: "f2d20b33b02b7a452f389b603c4ae65d5e8c079fc12e38f2641689816b3024f1",
676
+ sourceSha256: "f12314dcce27073f6aa2a83dcb7efe2a82a83404fe43926ec2da49d2c33a17ad"
679
677
  },
680
678
  {
681
679
  name: "openspec",
682
680
  file: "assets/mcp/openspec/",
683
681
  description: "MCP server local para OpenSpec - proposal, apply, archive, sync de cambios",
684
- version: "0.10.2",
685
- sha256: "bdb283fe8ec2dfcba2ec4f38191f4039e58cd7ae12e44e66310783b42c827638"
682
+ version: "0.10.3",
683
+ sha256: "038ef43eaa1816251626610522f874a6c932528c00237f94f5354f18ed59ef53",
684
+ sourceSha256: "bdb283fe8ec2dfcba2ec4f38191f4039e58cd7ae12e44e66310783b42c827638"
686
685
  }
687
686
  ],
688
687
  skills: [
@@ -690,105 +689,105 @@ var init_manifest = __esm(() => {
690
689
  name: "brainstorming",
691
690
  file: "assets/skills/brainstorming/SKILL.md",
692
691
  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)",
693
- version: "0.10.2",
692
+ version: "0.10.3",
694
693
  sha256: "ba76eff6fb5bea08d3b26622089ea4739b7b542b3677a46071b2576f3f2001c7"
695
694
  },
696
695
  {
697
696
  name: "execution-mode-evaluation",
698
697
  file: "assets/skills/execution-mode-evaluation/SKILL.md",
699
698
  description: "Skill de análisis de modo de ejecución — output reconciliado con controller snapshot contract (recommendation field)",
700
- version: "0.10.2",
699
+ version: "0.10.3",
701
700
  sha256: "9090132ccfe31dcf34b6d5f399c1135dcb1044c504d4beb026e0c7d9e09e7c0d"
702
701
  },
703
702
  {
704
703
  name: "tdd",
705
704
  file: "assets/skills/tdd/SKILL.md",
706
705
  description: "Skill de test-driven development (Superpowers)",
707
- version: "0.10.2",
706
+ version: "0.10.3",
708
707
  sha256: "cce54e81cce46ba973066f9b43e74994a7dbf21aa756146c27689241cf62704d"
709
708
  },
710
709
  {
711
710
  name: "subagent-driven-development",
712
711
  file: "assets/skills/subagent-driven-development/SKILL.md",
713
712
  description: "Skill de ejecución con subagentes (Superpowers) — ejecuta solo después de confirmación del coordinador Ostacky",
714
- version: "0.10.2",
713
+ version: "0.10.3",
715
714
  sha256: "1a42d714a9a13faf05f0bf7b580e8d837c2e77a5633839542ccce603023419e8"
716
715
  },
717
716
  {
718
717
  name: "dispatching-parallel-agents",
719
718
  file: "assets/skills/dispatching-parallel-agents/SKILL.md",
720
719
  description: "Skill de dispatch paralelo de agentes (Superpowers)",
721
- version: "0.10.2",
720
+ version: "0.10.3",
722
721
  sha256: "7e7b712ef742d5631e302e6a71583e2bbbd33094da9281abfb84b43505fc3ef1"
723
722
  },
724
723
  {
725
724
  name: "review",
726
725
  file: "assets/skills/review/SKILL.md",
727
726
  description: "Skill de revisión de código (Superpowers)",
728
- version: "0.10.2",
727
+ version: "0.10.3",
729
728
  sha256: "b19650ed4d1d4d9857a4dd5b7e08e91328a1a1fc9c45bcc2fb600fa9d0213279"
730
729
  },
731
730
  {
732
731
  name: "receiving-code-review",
733
732
  file: "assets/skills/receiving-code-review/SKILL.md",
734
733
  description: "Skill de recibir y procesar feedback de code review",
735
- version: "0.10.2",
734
+ version: "0.10.3",
736
735
  sha256: "d761e884e71d8d3476ac734d287cae403a4024506a7d12e7361a448217c0a831"
737
736
  },
738
737
  {
739
738
  name: "openspec-propose",
740
739
  file: "assets/skills/openspec-propose/SKILL.md",
741
740
  description: "Skill de generación de proposal (OpenSpec)",
742
- version: "0.10.2",
741
+ version: "0.10.3",
743
742
  sha256: "9049b944cea88a5428fd5d77b411a0ed32d169ad68acda911b68365fac08f38f"
744
743
  },
745
744
  {
746
745
  name: "openspec-apply-change",
747
746
  file: "assets/skills/openspec-apply-change/SKILL.md",
748
747
  description: "Skill de aplicación de change (OpenSpec)",
749
- version: "0.10.2",
748
+ version: "0.10.3",
750
749
  sha256: "dfc823bf89fc7505e91ab6dee9c1f004be38a410bd3d88fb67b211b1b6cbb1d0"
751
750
  },
752
751
  {
753
752
  name: "openspec-archive-change",
754
753
  file: "assets/skills/openspec-archive-change/SKILL.md",
755
754
  description: "Skill de archivo de change (OpenSpec)",
756
- version: "0.10.2",
755
+ version: "0.10.3",
757
756
  sha256: "16e4b561de7747283663fed602e506abf502452c49a8d4b86d7a5539c40f0195"
758
757
  },
759
758
  {
760
759
  name: "openspec-explore",
761
760
  file: "assets/skills/openspec-explore/SKILL.md",
762
761
  description: "Modo explore para OpenSpec — thinking partner para explorar ideas, investigar problemas y clarificar requisitos antes/durante un cambio",
763
- version: "0.10.2",
762
+ version: "0.10.3",
764
763
  sha256: "9eafc3a8c693b8d9916867ef764d49e5222da717fde501c957bb3f6c312d2415"
765
764
  },
766
765
  {
767
766
  name: "using-git-worktrees",
768
767
  file: "assets/skills/using-git-worktrees/SKILL.md",
769
768
  description: "Skill de uso de git worktrees para aislamiento de trabajo",
770
- version: "0.10.2",
769
+ version: "0.10.3",
771
770
  sha256: "93341bc1b7c053618a8b6dc07e3615b77990d7b549a0201f5835d95fef67ce13"
772
771
  },
773
772
  {
774
773
  name: "using-superpowers",
775
774
  file: "assets/skills/using-superpowers/SKILL.md",
776
775
  description: "Skill de orquestación de Superpowers skills",
777
- version: "0.10.2",
776
+ version: "0.10.3",
778
777
  sha256: "7e54536f96d2a379185a10bfc1e970850caa2561b6d8aca7080f0defea0381a4"
779
778
  },
780
779
  {
781
780
  name: "writing-skills",
782
781
  file: "assets/skills/writing-skills/SKILL.md",
783
782
  description: "Skill de creación y edición de skills",
784
- version: "0.10.2",
783
+ version: "0.10.3",
785
784
  sha256: "3d76b906cee518a2b809febb70db95697a35b9f368986bb504b4120c3bfb437a"
786
785
  },
787
786
  {
788
787
  name: "graceful-degradation",
789
788
  file: "assets/skills/graceful-degradation/SKILL.md",
790
789
  description: "Skill de degradación graceful cuando múltiples tools están indisponibles",
791
- version: "0.10.2",
790
+ version: "0.10.3",
792
791
  sha256: "363449bd9e6f800a0a3168667cf78f767fb105d86e4a4adadcc649d418139303"
793
792
  }
794
793
  ]
@@ -840,9 +839,11 @@ function getBundledMcpPath(name) {
840
839
  validateFilePath(name);
841
840
  return join3(BUNDLED_MCP_DIR, name);
842
841
  }
843
- function getBundledExpectedHash(category, name) {
842
+ function getBundledExpectedHash(category, name, field = "sha256") {
844
843
  const items = manifest_default[category] ?? [];
845
- return items.find((item) => item.name === name)?.sha256 ?? null;
844
+ const found = items.find((item) => item.name === name);
845
+ const value = found?.[field];
846
+ return typeof value === "string" ? value : null;
846
847
  }
847
848
  function getRawUrl(repo, tag, path) {
848
849
  return `${GITHUB_RAW}/${repo}/${tag}/${path}`;
@@ -1579,8 +1580,60 @@ var init_stack = __esm(() => {
1579
1580
  // src/doctor-v2.ts
1580
1581
  import { existsSync as existsSync8, readFileSync as readFileSync6, readdirSync as readdirSync3 } from "fs";
1581
1582
  import { join as join10, delimiter, isAbsolute } from "path";
1583
+ function globalOstackyArtifacts(globalDir = getGlobalOpenCodeDir()) {
1584
+ const found = [];
1585
+ const candidates = [
1586
+ join10(globalDir, "agent", "ostacky.md"),
1587
+ join10(globalDir, "plugins", "ostacky-controller", "index.ts"),
1588
+ join10(globalDir, "plugin", "ostacky-controller", "index.ts"),
1589
+ join10(globalDir, "command", "ostacky.md")
1590
+ ];
1591
+ for (const p of candidates) {
1592
+ try {
1593
+ if (existsSync8(p))
1594
+ found.push(p);
1595
+ } catch {}
1596
+ }
1597
+ for (const cfgName of ["opencode.json", "opencode.jsonc"]) {
1598
+ const cfg = join10(globalDir, cfgName);
1599
+ try {
1600
+ if (!existsSync8(cfg))
1601
+ continue;
1602
+ const raw = readFileSync6(cfg, "utf-8");
1603
+ const json = JSON.parse(raw.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, ""));
1604
+ const servers = json?.mcp?.servers ?? json?.mcp ?? {};
1605
+ if (servers && servers["ostacky-controller"])
1606
+ found.push(cfg);
1607
+ } catch {}
1608
+ }
1609
+ return found;
1610
+ }
1611
+ function globalPluginScopeAware(globalDir = getGlobalOpenCodeDir()) {
1612
+ const candidates = [
1613
+ join10(globalDir, "plugins", "ostacky-controller", "index.ts"),
1614
+ join10(globalDir, "plugin", "ostacky-controller", "index.ts"),
1615
+ join10(globalDir, "plugins", "ostacky-controller", "index.js"),
1616
+ join10(globalDir, "plugin", "ostacky-controller", "index.js")
1617
+ ];
1618
+ for (const p of candidates) {
1619
+ try {
1620
+ if (!existsSync8(p))
1621
+ continue;
1622
+ return readFileSync6(p, "utf-8").includes("ostacky-controller-global");
1623
+ } catch {}
1624
+ }
1625
+ return null;
1626
+ }
1582
1627
  function checkDoctorV2(projectRoot) {
1583
1628
  const lines = [];
1629
+ const globalArtifacts = globalOstackyArtifacts();
1630
+ if (globalArtifacts.length > 0) {
1631
+ if (globalPluginScopeAware() === false) {
1632
+ lines.push(`⚠️ plugin global legacy (id compartido): OpenCode V2 conserva la copia global y deshabilita la local ("Duplicate plugin ID: ostacky-controller") — los gates los aplica el plugin global, no el local. Actualizá el global (npx ostacky install --scope global) o limpiá con: npx ostacky uninstall --scope global`);
1633
+ } else {
1634
+ lines.push(`⚠️ Instalación global convive con la local: ${globalArtifacts.join(", ")}. El plugin global cede la precedencia al local (ids distintos, sin colisión); el resto de los artefactos globales queda como residuo si no los usás. Limpiá con: npx ostacky uninstall --scope global`);
1635
+ }
1636
+ }
1584
1637
  const cwd = projectRoot;
1585
1638
  const opencodeDir = findOpenCodeDir(cwd) || join10(cwd, ".opencode");
1586
1639
  const pluginPaths = [
@@ -1738,7 +1791,7 @@ var init_doctor_v2 = __esm(() => {
1738
1791
  // package.json
1739
1792
  var package_default = {
1740
1793
  name: "ostacky",
1741
- version: "0.10.2",
1794
+ version: "0.10.3",
1742
1795
  description: "Instalador interactivo de agentes y comandos para OpenCode",
1743
1796
  type: "module",
1744
1797
  bin: {
@@ -1747,15 +1800,17 @@ var package_default = {
1747
1800
  scripts: {
1748
1801
  prebuild: "bun run scripts/sync-version.ts && bun run scripts/sync-controller-core.ts",
1749
1802
  dev: "bun run src/cli.ts",
1750
- build: "bun build src/cli.ts --target=node --format=esm --outfile dist/cli.js && bun scripts/add-shebang.ts",
1803
+ build: "bun run build:cli && bun run build:mcp",
1804
+ "build:cli": "bun build src/cli.ts --target=node --format=esm --outfile dist/cli.js && bun scripts/add-shebang.ts",
1805
+ "build:mcp": "bun run scripts/build-mcp-bundles.ts",
1751
1806
  start: "node dist/cli.js",
1752
1807
  test: "bun test --isolate",
1753
1808
  "test:safe": "bun run scripts/run-tests-isolated.ts",
1754
1809
  "check:skills": "bun run scripts/check-skill-sync.ts",
1755
1810
  "hash:check": "bun run scripts/update-manifest-hashes.ts --check",
1756
- "hash:update": "bun run scripts/update-manifest-hashes.ts",
1757
- prepublishOnly: "bun run build",
1758
- update: "bun run scripts/check-skill-sync.ts && bun run scripts/sync-controller-core.ts && bun run scripts/update-manifest-hashes.ts"
1811
+ "hash:update": "bun run build:mcp && bun run scripts/update-manifest-hashes.ts",
1812
+ prepublishOnly: "bun run build && bun run hash:update",
1813
+ update: "bun run scripts/check-skill-sync.ts && bun run scripts/sync-controller-core.ts && bun run hash:update"
1759
1814
  },
1760
1815
  files: [
1761
1816
  "dist/",
@@ -2918,7 +2973,7 @@ async function probeMcpServer(nodeExecutable, serverPath, cwd, statePath, option
2918
2973
  params: {
2919
2974
  protocolVersion: "2025-03-26",
2920
2975
  capabilities: {},
2921
- clientInfo: { name: "ostacky-installer", version: "0.10.2" }
2976
+ clientInfo: { name: "ostacky-installer", version: "0.10.3" }
2922
2977
  }
2923
2978
  });
2924
2979
  });
@@ -2991,8 +3046,8 @@ function upsertLockfile(paths, type, item, manifest, contentHash) {
2991
3046
  existing.lockedAt = new Date().toISOString();
2992
3047
  writeLockfile(paths.root, existing);
2993
3048
  }
2994
- function assertBundledTreeHash(category, name, actualHash) {
2995
- const expected = getBundledExpectedHash(category, name);
3049
+ function assertBundledTreeHash(category, name, actualHash, field = "sha256") {
3050
+ const expected = getBundledExpectedHash(category, name, field);
2996
3051
  if (expected && actualHash !== expected) {
2997
3052
  const labels = {
2998
3053
  skills: "skill",
@@ -3000,7 +3055,7 @@ function assertBundledTreeHash(category, name, actualHash) {
3000
3055
  agents: "agente",
3001
3056
  commands: "command"
3002
3057
  };
3003
- throw new Error(`Tree hash inválido para ${labels[category] ?? category} "${name}"
3058
+ throw new Error(`Hash inválido para ${labels[category] ?? category} "${name}" (${field})
3004
3059
  esperado: ${expected}
3005
3060
  recibido: ${actualHash}`);
3006
3061
  }
@@ -3011,6 +3066,13 @@ function readBundledAsset(relativePath) {
3011
3066
  return null;
3012
3067
  return readFileSync5(fullPath, "utf-8");
3013
3068
  }
3069
+ function mcpArtifactHash(name, packageRoot = PACKAGE_ROOT) {
3070
+ const bundlePath = join6(packageRoot, "dist", "mcp", name, "index.js");
3071
+ if (existsSync5(bundlePath)) {
3072
+ return { field: "sha256", actual: sha256(readFileSync5(bundlePath, "utf-8")) };
3073
+ }
3074
+ return { field: "sourceSha256", actual: computeTreeHash(join6(packageRoot, "assets", "mcp", name)) };
3075
+ }
3014
3076
  async function installAgent(item, manifest, paths) {
3015
3077
  let content;
3016
3078
  try {
@@ -3081,8 +3143,10 @@ async function installMcpServer(item, manifest, paths) {
3081
3143
  if (!existsSync5(src)) {
3082
3144
  throw new Error(`MCP server bundleado no encontrado: ${src}. ` + `¿Falta el directorio assets/mcp/${item.name}/?`);
3083
3145
  }
3084
- const treeHash = computeTreeHash(src);
3085
- assertBundledTreeHash("mcpServers", item.name, treeHash);
3146
+ const bundlePath = join6(PACKAGE_ROOT, "dist", "mcp", item.name, "index.js");
3147
+ const hasBundle = existsSync5(bundlePath);
3148
+ const { field, actual: artifactHash } = mcpArtifactHash(item.name);
3149
+ assertBundledTreeHash("mcpServers", item.name, artifactHash, field);
3086
3150
  const nodeExecutable = getVerifiedNodeExecutable();
3087
3151
  await Promise.resolve().then(() => init_fs());
3088
3152
  const projectRoot = paths.root === getGlobalOpenCodeDir() ? paths.root : dirname4(paths.root);
@@ -3091,9 +3155,8 @@ async function installMcpServer(item, manifest, paths) {
3091
3155
  const statePath = item.name === "ostacky-controller" ? join6(paths.root, "ostacky-state.json") : undefined;
3092
3156
  try {
3093
3157
  mkdirSync3(staging, { recursive: true });
3094
- const bundledPath = join6(PACKAGE_ROOT, "dist", "mcp", item.name, "index.js");
3095
- if (existsSync5(bundledPath)) {
3096
- copyFileSync2(bundledPath, join6(staging, "index.js"));
3158
+ if (hasBundle) {
3159
+ copyFileSync2(bundlePath, join6(staging, "index.js"));
3097
3160
  writeFileSync5(join6(staging, "package.json"), JSON.stringify({ type: "module" }) + `
3098
3161
  `, "utf-8");
3099
3162
  } else {
@@ -3129,7 +3192,7 @@ async function installMcpServer(item, manifest, paths) {
3129
3192
  const promotion = promoteStagedDirectory(staging, dest);
3130
3193
  try {
3131
3194
  setMcpEntryAtProjectRoot(projectRoot, item.name, createMcpConfigEntry(item.name, nodeExecutable, join6(dest, "index.js"), statePath));
3132
- upsertLockfile(paths, "mcpServers", item, manifest, treeHash);
3195
+ upsertLockfile(paths, "mcpServers", item, manifest, artifactHash);
3133
3196
  promotion.commit();
3134
3197
  } catch (error) {
3135
3198
  try {
@@ -4616,6 +4679,7 @@ import { existsSync as existsSync9, statSync as statSync3, readFileSync as readF
4616
4679
  import { join as join11, dirname as dirname8 } from "node:path";
4617
4680
  import { spawnSync } from "node:child_process";
4618
4681
  init_fs();
4682
+ init_github();
4619
4683
 
4620
4684
  // src/version-check.ts
4621
4685
  function parseMajor(version) {
@@ -4903,16 +4967,18 @@ async function runDoctorCommand() {
4903
4967
  check(`tool ${t}: ${existsSync9(p) || existsSync9(pExe) ? "found" : "missing"}`, existsSync9(p) || existsSync9(pExe), true);
4904
4968
  }
4905
4969
  try {
4906
- const manifest = JSON.parse(readFileSync7(join11(cwd, "manifest.json"), "utf-8"));
4907
- const expected = manifest.mcpServers?.find((x) => x.name === "ostacky-controller")?.sha256;
4970
+ const name = "ostacky-controller";
4971
+ const { field, actual } = mcpArtifactHash(name);
4972
+ const expected = getBundledExpectedHash("mcpServers", name, field);
4908
4973
  if (expected) {
4909
- const actual = computeTreeHash(join11(cwd, "assets", "mcp", "ostacky-controller"));
4910
- check(`manifest hash: ${expected.slice(0, 8)} vs actual ${actual.slice(0, 8)}`, expected === actual);
4974
+ check(`manifest ${field}: ${expected.slice(0, 8)} vs actual ${actual.slice(0, 8)}`, expected === actual);
4911
4975
  if (expected !== actual)
4912
4976
  console.log(" Run: bun run hash:update");
4977
+ } else {
4978
+ check(`manifest ${field}: sin entrada para ${name}`, false, true);
4913
4979
  }
4914
4980
  } catch {
4915
- check("manifest: not found", false, true);
4981
+ check("manifest: no se pudo verificar", false, true);
4916
4982
  }
4917
4983
  try {
4918
4984
  await Promise.resolve().then(() => init_doctor_v2());
@@ -5103,9 +5169,15 @@ async function main() {
5103
5169
  await ensureOpencodeInstalled();
5104
5170
  }
5105
5171
  switch (cmd) {
5106
- case "install":
5172
+ case "install": {
5173
+ await Promise.resolve().then(() => init_doctor_v2());
5174
+ const globalArtifacts = globalOstackyArtifacts();
5175
+ if (scope !== "global" && globalArtifacts.length > 0) {
5176
+ console.error(`Aviso: hay artefactos de Ostacky en la instalación global (${globalArtifacts.join(", ")}). ` + `Podés limpiarlos con: npx ostacky uninstall --scope global`);
5177
+ }
5107
5178
  await runInstallCommand(scope, { noStack });
5108
5179
  break;
5180
+ }
5109
5181
  case "install-stack":
5110
5182
  if (scope === "global") {
5111
5183
  console.error(`Aviso: el stack (CodeGraph/Engram) se instala siempre local por proyecto. Ignorando --scope global y usando local.`);