ostacky 0.7.3 → 0.8.0

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