jorgex-stack 1.9.38 → 1.9.39

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
@@ -15,12 +15,12 @@ import {
15
15
 
16
16
  // src/cli.ts
17
17
  import * as p6 from "@clack/prompts";
18
- import fs26 from "fs";
18
+ import fs27 from "fs";
19
19
  import { pathToFileURL as pathToFileURL2 } from "url";
20
20
 
21
21
  // src/install.ts
22
- import fs16 from "fs";
23
- import path21 from "path";
22
+ import fs17 from "fs";
23
+ import path22 from "path";
24
24
  import * as p from "@clack/prompts";
25
25
 
26
26
  // src/lib/writing-style.ts
@@ -498,15 +498,81 @@ function applyWritingStyle(plan, dryRun = false) {
498
498
  writeText(plan.sourcePath, plan.installedContent, mode);
499
499
  }
500
500
 
501
+ // src/lib/system-prompt-sections.ts
502
+ import fs4 from "fs";
503
+ import path4 from "path";
504
+ var SYSTEM_PROMPT_SECTIONS = [
505
+ "system-prompt",
506
+ "engram-protocol",
507
+ "context7",
508
+ "playwright",
509
+ "chrome-devtools",
510
+ "browser",
511
+ "writing-style"
512
+ ];
513
+ function assertSystemPromptMarkers(content, target) {
514
+ if (content === null) return;
515
+ const ranges = [];
516
+ for (const section of SYSTEM_PROMPT_SECTIONS) {
517
+ const markers2 = [...content.matchAll(new RegExp(`<!--\\s*\\/?jorgex:${section}(?=[\\s>]|-->|$)`, "g"))];
518
+ if (markers2.length === 0) continue;
519
+ if (markers2.length !== 2 || !hasHealthyManagedMarkdownMarkers(content, section)) {
520
+ throw new Error(`Marcadores jorgex:${section} ambiguos en ${target}. Repara el bloque antes de reintentar; su contenido se conserva.`);
521
+ }
522
+ ranges.push({
523
+ section,
524
+ start: content.indexOf(`<!-- jorgex:${section} -->`),
525
+ end: content.indexOf(`<!-- /jorgex:${section} -->`) + `<!-- /jorgex:${section} -->`.length
526
+ });
527
+ }
528
+ ranges.sort((a, b) => a.start - b.start);
529
+ for (let index = 1; index < ranges.length; index++) {
530
+ if (ranges[index].start < ranges[index - 1].end) {
531
+ throw new Error(`Marcadores jorgex:${ranges[index].section} anidados en ${target}. Separa los bloques antes de reintentar; su contenido se conserva.`);
532
+ }
533
+ }
534
+ }
535
+ function readSystemPromptFile(target, rootDir) {
536
+ if (rootDir !== void 0) {
537
+ const probe = fs4.existsSync(target) ? target : path4.dirname(target);
538
+ if (fs4.existsSync(probe) && fs4.existsSync(rootDir)) {
539
+ const resolved = fs4.realpathSync(probe);
540
+ const root = fs4.realpathSync(rootDir);
541
+ if (resolved !== root && !isContainedIn(resolved, root)) {
542
+ throw new Error(`El prompt ${target} sale del destino aislado.`);
543
+ }
544
+ }
545
+ }
546
+ let bytes;
547
+ try {
548
+ bytes = fs4.readFileSync(target);
549
+ } catch (error) {
550
+ if (error.code === "ENOENT") return null;
551
+ throw new Error(`No se puede leer el prompt ${target}. Revisa los permisos y el tipo de archivo.`, { cause: error });
552
+ }
553
+ try {
554
+ return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
555
+ } catch (error) {
556
+ throw new Error(`El prompt ${target} debe usar UTF-8 v\xE1lido.`, { cause: error });
557
+ }
558
+ }
559
+ function assertSystemPromptFile(target, rootDir) {
560
+ assertSystemPromptMarkers(readSystemPromptFile(target, rootDir), target);
561
+ }
562
+ function removeSystemPromptSections(content) {
563
+ assertSystemPromptMarkers(content, "system prompt");
564
+ return SYSTEM_PROMPT_SECTIONS.reduce((current, section) => removeMarkdownSection(current, section), content);
565
+ }
566
+
501
567
  // src/adapters/opencode.ts
502
- import path8 from "path";
503
- import fs5 from "fs";
568
+ import path9 from "path";
569
+ import fs6 from "fs";
504
570
  import { isDeepStrictEqual } from "util";
505
571
  import { pathToFileURL } from "url";
506
572
 
507
573
  // src/lib/canonical.ts
508
- import path4 from "path";
509
- import fs4 from "fs";
574
+ import path5 from "path";
575
+ import fs5 from "fs";
510
576
  function parseCanonicalAgent(source, file) {
511
577
  const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(source);
512
578
  if (!match) throw new Error(`Agente sin frontmatter: ${file}`);
@@ -532,9 +598,9 @@ function parseCanonicalAgent(source, file) {
532
598
  };
533
599
  }
534
600
  function loadCanonicalAgents(agentsDir) {
535
- return fs4.readdirSync(agentsDir).filter((f) => f.endsWith(".md") && f.toLowerCase() !== "readme.md").map((f) => {
536
- const file = path4.join(agentsDir, f);
537
- const source = fs4.readFileSync(file, "utf8").replace(/\r\n/g, "\n");
601
+ return fs5.readdirSync(agentsDir).filter((f) => f.endsWith(".md") && f.toLowerCase() !== "readme.md").map((f) => {
602
+ const file = path5.join(agentsDir, f);
603
+ const source = fs5.readFileSync(file, "utf8").replace(/\r\n/g, "\n");
538
604
  return parseCanonicalAgent(source, file);
539
605
  });
540
606
  }
@@ -543,21 +609,21 @@ function isCanonicalMcpServerEnabled(name, server, enabledServers) {
543
609
  return !server.optional || server.defaultEnabled === true || enabledServers?.has(name) === true;
544
610
  }
545
611
  function loadCanonicalMcp(stackDir) {
546
- return JSON.parse(fs4.readFileSync(path4.join(stackDir, "mcp", "servers.json"), "utf8"));
612
+ return JSON.parse(fs5.readFileSync(path5.join(stackDir, "mcp", "servers.json"), "utf8"));
547
613
  }
548
614
  function loadCanonicalHooks(stackDir) {
549
- return JSON.parse(fs4.readFileSync(path4.join(stackDir, "hooks", "hooks.json"), "utf8"));
615
+ return JSON.parse(fs5.readFileSync(path5.join(stackDir, "hooks", "hooks.json"), "utf8"));
550
616
  }
551
617
  function loadCanonicalDefaults(stackDir) {
552
- const file = path4.join(stackDir, "config", "defaults.json");
553
- if (!fs4.existsSync(file)) return {};
554
- const parsed = JSON.parse(fs4.readFileSync(file, "utf8"));
618
+ const file = path5.join(stackDir, "config", "defaults.json");
619
+ if (!fs5.existsSync(file)) return {};
620
+ const parsed = JSON.parse(fs5.readFileSync(file, "utf8"));
555
621
  delete parsed["$comment"];
556
622
  return parsed;
557
623
  }
558
624
 
559
625
  // src/lib/model-map.ts
560
- import path5 from "path";
626
+ import path6 from "path";
561
627
  import { existsSync } from "fs";
562
628
  function resolveAgentModel(models, agentName, tier) {
563
629
  const base = models[tier];
@@ -584,7 +650,7 @@ var DEFAULT_MODEL_MAP = {
584
650
  }
585
651
  };
586
652
  function modelMapFile() {
587
- return path5.join(dataDir(), "model-map.json");
653
+ return path6.join(dataDir(), "model-map.json");
588
654
  }
589
655
  function loadModelMap() {
590
656
  const file = modelMapFile();
@@ -612,15 +678,15 @@ function ensureModelMapFile() {
612
678
  }
613
679
 
614
680
  // src/lib/detect.ts
615
- import path6 from "path";
681
+ import path7 from "path";
616
682
  import { existsSync as existsSync2, statSync } from "fs";
617
683
  import { execFileSync } from "child_process";
618
684
  function lookPath(cmd) {
619
685
  const exts = process.platform === "win32" ? [".exe", ".cmd", ".bat", ".ps1", ""] : [""];
620
- for (const dir of (process.env.PATH ?? "").split(path6.delimiter)) {
686
+ for (const dir of (process.env.PATH ?? "").split(path7.delimiter)) {
621
687
  if (!dir) continue;
622
688
  for (const ext of exts) {
623
- const candidate = path6.join(dir, cmd + ext);
689
+ const candidate = path7.join(dir, cmd + ext);
624
690
  if (statSync(candidate, { throwIfNoEntry: false })?.isFile()) return candidate;
625
691
  }
626
692
  }
@@ -650,7 +716,7 @@ function runDetectedBin(bin, args, timeoutMs, envOverrides) {
650
716
  }
651
717
  }
652
718
  function detectOpenCode() {
653
- const configDir = process.env.OPENCODE_CONFIG_DIR ?? path6.join(HOME, ".config", "opencode");
719
+ const configDir = process.env.OPENCODE_CONFIG_DIR ?? path7.join(HOME, ".config", "opencode");
654
720
  const binPath = lookPath("opencode");
655
721
  return {
656
722
  id: "opencode",
@@ -661,7 +727,7 @@ function detectOpenCode() {
661
727
  };
662
728
  }
663
729
  function detectClaudeCode() {
664
- const configDir = path6.join(HOME, ".claude");
730
+ const configDir = path7.join(HOME, ".claude");
665
731
  const binPath = lookPath("claude");
666
732
  return {
667
733
  id: "claude-code",
@@ -672,7 +738,7 @@ function detectClaudeCode() {
672
738
  };
673
739
  }
674
740
  function detectCodex() {
675
- const configDir = process.env.CODEX_HOME ?? path6.join(HOME, ".codex");
741
+ const configDir = process.env.CODEX_HOME ?? path7.join(HOME, ".codex");
676
742
  const binPath = lookPath("codex");
677
743
  return {
678
744
  id: "codex",
@@ -688,19 +754,19 @@ function detectEngram() {
688
754
  const onPath = lookPath("engram");
689
755
  if (onPath) return onPath;
690
756
  const candidates = [
691
- path6.join(HOME, "go", "bin", "engram.exe"),
692
- path6.join(HOME, "go", "bin", "engram"),
693
- path6.join(HOME, ".local", "bin", "engram.exe"),
694
- path6.join(HOME, ".local", "bin", "engram")
757
+ path7.join(HOME, "go", "bin", "engram.exe"),
758
+ path7.join(HOME, "go", "bin", "engram"),
759
+ path7.join(HOME, ".local", "bin", "engram.exe"),
760
+ path7.join(HOME, ".local", "bin", "engram")
695
761
  ];
696
762
  for (const c of candidates) if (existsSync2(c)) return c;
697
763
  return null;
698
764
  }
699
765
 
700
766
  // src/lib/hooks-format.ts
701
- import path7 from "path";
767
+ import path8 from "path";
702
768
  function hookScriptNames(canonical) {
703
- return Object.values(canonical.hooks).flat().flatMap((entry) => entry.hooks).map((h) => /\{\{SCRIPTS_DIR\}\}[/\\]([\w./-]+)/.exec(h.command)?.[1]).filter((s) => s !== void 0).map((s) => path7.basename(s));
769
+ return Object.values(canonical.hooks).flat().flatMap((entry) => entry.hooks).map((h) => /\{\{SCRIPTS_DIR\}\}[/\\]([\w./-]+)/.exec(h.command)?.[1]).filter((s) => s !== void 0).map((s) => path8.basename(s));
704
770
  }
705
771
  function plainHookCommands(canonical) {
706
772
  return new Set(
@@ -747,7 +813,7 @@ function upsertNativeHooks(existing, canonical, scriptsDir, mapMatcher = (m) =>
747
813
  ...h.timeout !== void 0 ? { timeout: h.timeout } : {}
748
814
  }))
749
815
  };
750
- const scriptNames = entry.hooks.map((h) => /\{\{SCRIPTS_DIR\}\}[/\\]([\w./-]+)/.exec(h.command)?.[1]).filter((s) => s !== void 0).map((s) => path7.basename(s));
816
+ const scriptNames = entry.hooks.map((h) => /\{\{SCRIPTS_DIR\}\}[/\\]([\w./-]+)/.exec(h.command)?.[1]).filter((s) => s !== void 0).map((s) => path8.basename(s));
751
817
  const plainCommands = entry.hooks.map((h) => h.command).filter((c) => !c.includes("{{SCRIPTS_DIR}}"));
752
818
  const index = list.findIndex((existingEntry) => {
753
819
  const e = existingEntry;
@@ -891,7 +957,7 @@ function pruneEmpty(parent, key) {
891
957
  if (value !== null && Object.keys(value).length === 0) delete parent[key];
892
958
  }
893
959
  function hasOpenCodeManualApproval(configDir) {
894
- const content = readTextIfExists(path8.join(configDir, "opencode.json"));
960
+ const content = readTextIfExists(path9.join(configDir, "opencode.json"));
895
961
  if (content === null) return false;
896
962
  try {
897
963
  const root = JSON.parse(content);
@@ -907,7 +973,7 @@ var opencodeAdapter = {
907
973
  name: "OpenCode",
908
974
  detect: detectOpenCode,
909
975
  reportCapabilities(configDir) {
910
- const prompt = readTextIfExists(path8.join(configDir, "AGENTS.md"));
976
+ const prompt = readTextIfExists(path9.join(configDir, "AGENTS.md"));
911
977
  return createLocalCapabilityReport("opencode", [
912
978
  ...hasManagedMarkdownSection(prompt, "system-prompt") ? [{
913
979
  id: "policy-guidance",
@@ -929,16 +995,16 @@ var opencodeAdapter = {
929
995
  paths(configDir) {
930
996
  const isRealConfigDir = samePath(
931
997
  configDir,
932
- process.env.OPENCODE_CONFIG_DIR ?? path8.join(HOME, ".config", "opencode")
998
+ process.env.OPENCODE_CONFIG_DIR ?? path9.join(HOME, ".config", "opencode")
933
999
  );
934
- const agentsHome = isRealConfigDir ? HOME : path8.dirname(configDir);
1000
+ const agentsHome = isRealConfigDir ? HOME : path9.dirname(configDir);
935
1001
  return {
936
- systemPromptFile: path8.join(configDir, "AGENTS.md"),
937
- agentsDir: path8.join(configDir, "agents"),
938
- skillsDir: path8.join(agentsHome, ".agents", "skills"),
939
- commandsDir: path8.join(configDir, "commands"),
940
- pluginsDir: path8.join(configDir, "plugins"),
941
- scriptsDir: path8.join(configDir, "scripts"),
1002
+ systemPromptFile: path9.join(configDir, "AGENTS.md"),
1003
+ agentsDir: path9.join(configDir, "agents"),
1004
+ skillsDir: path9.join(agentsHome, ".agents", "skills"),
1005
+ commandsDir: path9.join(configDir, "commands"),
1006
+ pluginsDir: path9.join(configDir, "plugins"),
1007
+ scriptsDir: path9.join(configDir, "scripts"),
942
1008
  outputStylesDir: null,
943
1009
  profilesDir: null
944
1010
  };
@@ -996,12 +1062,12 @@ ${agent.body}`,
996
1062
  ctx.warnings.push(`opencode: hook sin {{SCRIPTS_DIR}} no traducible: ${hook.command}`);
997
1063
  continue;
998
1064
  }
999
- const script = `scripts/${path8.basename(match[1])}`;
1065
+ const script = `scripts/${path9.basename(match[1])}`;
1000
1066
  (bashEntries[includes] ??= []).push(script);
1001
1067
  }
1002
1068
  }
1003
1069
  }
1004
- const hooksFile = path8.join(ctx.configDir, "hooks.json");
1070
+ const hooksFile = path9.join(ctx.configDir, "hooks.json");
1005
1071
  const content = upsertJson(readTextIfExists(hooksFile), (root) => {
1006
1072
  const afterValue = root["tool.execute.after"] ??= {};
1007
1073
  if (afterValue === null || typeof afterValue !== "object" || Array.isArray(afterValue)) {
@@ -1036,16 +1102,16 @@ ${agent.body}`,
1036
1102
  }
1037
1103
  });
1038
1104
  actions.push({ kind: "write", target: hooksFile, content });
1039
- const scriptsSource = path8.join(ctx.stackDir, "scripts");
1040
- if (fs5.existsSync(scriptsSource)) {
1041
- for (const f of fs5.readdirSync(scriptsSource)) {
1042
- actions.push({ kind: "copy", source: path8.join(scriptsSource, f), target: path8.join(scriptsDir, f) });
1105
+ const scriptsSource = path9.join(ctx.stackDir, "scripts");
1106
+ if (fs6.existsSync(scriptsSource)) {
1107
+ for (const f of fs6.readdirSync(scriptsSource)) {
1108
+ actions.push({ kind: "copy", source: path9.join(scriptsSource, f), target: path9.join(scriptsDir, f) });
1043
1109
  }
1044
1110
  }
1045
1111
  return actions;
1046
1112
  },
1047
1113
  planMainConfig(canonical, ctx) {
1048
- const file = path8.join(ctx.configDir, "opencode.json");
1114
+ const file = path9.join(ctx.configDir, "opencode.json");
1049
1115
  const { pluginsDir } = this.paths(ctx.configDir);
1050
1116
  const original = readTextIfExists(file);
1051
1117
  const contentSource = original === null || original.trim() === "" ? null : original;
@@ -1152,13 +1218,10 @@ ${agent.body}`,
1152
1218
  const { systemPromptFile, pluginsDir } = this.paths(ctx.configDir);
1153
1219
  const prompt = readTextIfExists(systemPromptFile);
1154
1220
  if (prompt !== null) {
1155
- let content = removeMarkdownSection(prompt, "system-prompt");
1156
- content = removeMarkdownSection(content, "engram-protocol");
1157
- content = removeMarkdownSection(content, "browser");
1158
- content = removeMarkdownSection(content, "writing-style");
1221
+ const content = removeSystemPromptSections(prompt);
1159
1222
  actions.push({ kind: "write", target: systemPromptFile, content });
1160
1223
  }
1161
- const configFile = path8.join(ctx.configDir, "opencode.json");
1224
+ const configFile = path9.join(ctx.configDir, "opencode.json");
1162
1225
  const config = readTextIfExists(configFile);
1163
1226
  if (config !== null) {
1164
1227
  const mcpOwnership = [];
@@ -1226,7 +1289,7 @@ ${agent.body}`,
1226
1289
  ...primaryModelOwnership.length > 0 ? { primaryModelOwnership } : {}
1227
1290
  });
1228
1291
  }
1229
- const hooksFile = path8.join(ctx.configDir, "hooks.json");
1292
+ const hooksFile = path9.join(ctx.configDir, "hooks.json");
1230
1293
  const hooksJson = readTextIfExists(hooksFile);
1231
1294
  if (hooksJson !== null) {
1232
1295
  const ourScripts = hookScriptNames(hooks);
@@ -1252,8 +1315,8 @@ ${agent.body}`,
1252
1315
  };
1253
1316
 
1254
1317
  // src/adapters/claude-code.ts
1255
- import path9 from "path";
1256
- import fs6 from "fs";
1318
+ import path10 from "path";
1319
+ import fs7 from "fs";
1257
1320
  import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
1258
1321
  function yamlString2(value) {
1259
1322
  return JSON.stringify(value);
@@ -1269,7 +1332,7 @@ function isRecord(value) {
1269
1332
  return value !== null && typeof value === "object" && !Array.isArray(value);
1270
1333
  }
1271
1334
  function hasClaudeManualApproval(configDir) {
1272
- const content = readTextIfExists(path9.join(configDir, "settings.json"));
1335
+ const content = readTextIfExists(path10.join(configDir, "settings.json"));
1273
1336
  if (content === null) return false;
1274
1337
  try {
1275
1338
  const root = JSON.parse(content);
@@ -1289,8 +1352,8 @@ function toolsFor(agent) {
1289
1352
  return tools.join(", ");
1290
1353
  }
1291
1354
  function hasEngramPlugin(configDir) {
1292
- if (fs6.existsSync(path9.join(configDir, "plugins", "marketplaces", "engram"))) return true;
1293
- const registry = readTextIfExists(path9.join(configDir, "plugins", "installed_plugins.json"));
1355
+ if (fs7.existsSync(path10.join(configDir, "plugins", "marketplaces", "engram"))) return true;
1356
+ const registry = readTextIfExists(path10.join(configDir, "plugins", "installed_plugins.json"));
1294
1357
  if (registry === null) return false;
1295
1358
  try {
1296
1359
  const parsed = JSON.parse(registry);
@@ -1313,7 +1376,7 @@ var claudeCodeAdapter = {
1313
1376
  name: "Claude Code",
1314
1377
  detect: detectClaudeCode,
1315
1378
  reportCapabilities(configDir) {
1316
- const prompt = readTextIfExists(path9.join(configDir, "CLAUDE.md"));
1379
+ const prompt = readTextIfExists(path10.join(configDir, "CLAUDE.md"));
1317
1380
  return createLocalCapabilityReport("claude-code", [
1318
1381
  ...hasManagedMarkdownSection(prompt, "system-prompt") ? [{
1319
1382
  id: "policy-guidance",
@@ -1334,13 +1397,13 @@ var claudeCodeAdapter = {
1334
1397
  },
1335
1398
  paths(configDir) {
1336
1399
  return {
1337
- systemPromptFile: path9.join(configDir, "CLAUDE.md"),
1338
- agentsDir: path9.join(configDir, "agents"),
1339
- skillsDir: path9.join(configDir, "skills"),
1340
- commandsDir: path9.join(configDir, "commands"),
1400
+ systemPromptFile: path10.join(configDir, "CLAUDE.md"),
1401
+ agentsDir: path10.join(configDir, "agents"),
1402
+ skillsDir: path10.join(configDir, "skills"),
1403
+ commandsDir: path10.join(configDir, "commands"),
1341
1404
  pluginsDir: null,
1342
- scriptsDir: path9.join(configDir, "scripts"),
1343
- outputStylesDir: path9.join(configDir, "output-styles"),
1405
+ scriptsDir: path10.join(configDir, "scripts"),
1406
+ outputStylesDir: path10.join(configDir, "output-styles"),
1344
1407
  profilesDir: null
1345
1408
  };
1346
1409
  },
@@ -1386,9 +1449,9 @@ ${agent.body}`,
1386
1449
  planHooks(canonical, ctx) {
1387
1450
  const actions = [];
1388
1451
  const { scriptsDir } = this.paths(ctx.configDir);
1389
- const original = readTextIfExists(path9.join(ctx.configDir, "settings.json"));
1452
+ const original = readTextIfExists(path10.join(ctx.configDir, "settings.json"));
1390
1453
  const contentSource = original === null || original.trim() === "" ? null : original;
1391
- const settingsFile = path9.join(ctx.configDir, "settings.json");
1454
+ const settingsFile = path10.join(ctx.configDir, "settings.json");
1392
1455
  let content = upsertNativeHooks(contentSource, canonical, scriptsDir);
1393
1456
  const defaults = loadCanonicalDefaults(ctx.stackDir)["claude-code"];
1394
1457
  if (contentSource === null && defaults?.["permissions"] !== void 0) {
@@ -1402,16 +1465,16 @@ ${agent.body}`,
1402
1465
  });
1403
1466
  }
1404
1467
  actions.push({ kind: "write", target: settingsFile, content });
1405
- const scriptsSource = path9.join(ctx.stackDir, "scripts");
1406
- if (fs6.existsSync(scriptsSource)) {
1407
- for (const f of fs6.readdirSync(scriptsSource)) {
1408
- actions.push({ kind: "copy", source: path9.join(scriptsSource, f), target: path9.join(scriptsDir, f) });
1468
+ const scriptsSource = path10.join(ctx.stackDir, "scripts");
1469
+ if (fs7.existsSync(scriptsSource)) {
1470
+ for (const f of fs7.readdirSync(scriptsSource)) {
1471
+ actions.push({ kind: "copy", source: path10.join(scriptsSource, f), target: path10.join(scriptsDir, f) });
1409
1472
  }
1410
1473
  }
1411
1474
  return actions;
1412
1475
  },
1413
1476
  planMainConfig(canonical, ctx) {
1414
- const file = path9.join(path9.dirname(ctx.configDir), `${path9.basename(ctx.configDir)}.json`);
1477
+ const file = path10.join(path10.dirname(ctx.configDir), `${path10.basename(ctx.configDir)}.json`);
1415
1478
  const mcpOwnership = [];
1416
1479
  const content = upsertJson(readTextIfExists(file), (root) => {
1417
1480
  const servers = root["mcpServers"] ??= {};
@@ -1471,13 +1534,10 @@ ${agent.body}`,
1471
1534
  const { systemPromptFile } = this.paths(ctx.configDir);
1472
1535
  const prompt = readTextIfExists(systemPromptFile);
1473
1536
  if (prompt !== null) {
1474
- let content = removeMarkdownSection(prompt, "system-prompt");
1475
- content = removeMarkdownSection(content, "engram-protocol");
1476
- content = removeMarkdownSection(content, "browser");
1477
- content = removeMarkdownSection(content, "writing-style");
1537
+ const content = removeSystemPromptSections(prompt);
1478
1538
  actions.push({ kind: "write", target: systemPromptFile, content });
1479
1539
  }
1480
- const settingsFile = path9.join(ctx.configDir, "settings.json");
1540
+ const settingsFile = path10.join(ctx.configDir, "settings.json");
1481
1541
  const settings = readTextIfExists(settingsFile);
1482
1542
  if (settings !== null) {
1483
1543
  const content = removeNativeHooks(settings, hooks);
@@ -1485,7 +1545,7 @@ ${agent.body}`,
1485
1545
  actions.push({ kind: "write", target: settingsFile, content: content.trim() === "{}" ? "" : content });
1486
1546
  }
1487
1547
  }
1488
- const mainFile = path9.join(path9.dirname(ctx.configDir), `${path9.basename(ctx.configDir)}.json`);
1548
+ const mainFile = path10.join(path10.dirname(ctx.configDir), `${path10.basename(ctx.configDir)}.json`);
1489
1549
  const main2 = readTextIfExists(mainFile);
1490
1550
  if (main2 !== null) {
1491
1551
  const mcpOwnership = [];
@@ -1511,8 +1571,8 @@ ${agent.body}`,
1511
1571
  };
1512
1572
 
1513
1573
  // src/adapters/codex.ts
1514
- import path10 from "path";
1515
- import fs7 from "fs";
1574
+ import path11 from "path";
1575
+ import fs8 from "fs";
1516
1576
  function tomlString(value) {
1517
1577
  return JSON.stringify(value);
1518
1578
  }
@@ -1535,15 +1595,15 @@ function isManagedOptionalStdioServer3(server, section) {
1535
1595
  return server.optional === true && server.transport === "stdio" && section?.trim() === stdioMcpSection(server);
1536
1596
  }
1537
1597
  function hasActiveEngramPlugin(configDir) {
1538
- const config = readTextIfExists(path10.join(configDir, "config.toml"));
1598
+ const config = readTextIfExists(path11.join(configDir, "config.toml"));
1539
1599
  if (config === null) return false;
1540
1600
  const match = /\[plugins\."engram@[^"]*"\]([^[]*)/.exec(config);
1541
1601
  return match !== null && !/enabled\s*=\s*false/.test(match[1]);
1542
1602
  }
1543
1603
  function hasEngramProtocol(configDir) {
1544
1604
  if (hasActiveEngramPlugin(configDir)) return true;
1545
- if (fs7.existsSync(path10.join(configDir, "engram-instructions.md"))) return true;
1546
- const config = readTextIfExists(path10.join(configDir, "config.toml"));
1605
+ if (fs8.existsSync(path11.join(configDir, "engram-instructions.md"))) return true;
1606
+ const config = readTextIfExists(path11.join(configDir, "config.toml"));
1547
1607
  return config !== null && /engram-instructions\.md/.test(config);
1548
1608
  }
1549
1609
  var CODEX_JSON_STRING = String.raw`"(?:\\.|[^"\\\r\n])*"`;
@@ -1668,7 +1728,7 @@ function hasCanonicalCodexPermissionProfile(scan) {
1668
1728
  return true;
1669
1729
  }
1670
1730
  function hasCodexManualApproval(configDir) {
1671
- const config = readTextIfExists(path10.join(configDir, "config.toml"));
1731
+ const config = readTextIfExists(path11.join(configDir, "config.toml"));
1672
1732
  if (config === null) return false;
1673
1733
  const scan = scanCodexToml(config);
1674
1734
  if (scan === null) return false;
@@ -1686,7 +1746,7 @@ var codexAdapter = {
1686
1746
  name: "Codex CLI",
1687
1747
  detect: detectCodex,
1688
1748
  reportCapabilities(configDir) {
1689
- const prompt = readTextIfExists(path10.join(configDir, "AGENTS.md"));
1749
+ const prompt = readTextIfExists(path11.join(configDir, "AGENTS.md"));
1690
1750
  return createLocalCapabilityReport("codex", [
1691
1751
  ...hasManagedMarkdownSection(prompt, "system-prompt") ? [{
1692
1752
  id: "policy-guidance",
@@ -1711,18 +1771,18 @@ var codexAdapter = {
1711
1771
  return !hasEngramProtocol(ctx.configDir);
1712
1772
  },
1713
1773
  paths(configDir) {
1714
- const isRealConfigDir = samePath(configDir, process.env.CODEX_HOME ?? path10.join(HOME, ".codex"));
1715
- const agentsHome = isRealConfigDir ? HOME : path10.dirname(configDir);
1716
- const skillsDir = path10.join(agentsHome, ".agents", "skills");
1774
+ const isRealConfigDir = samePath(configDir, process.env.CODEX_HOME ?? path11.join(HOME, ".codex"));
1775
+ const agentsHome = isRealConfigDir ? HOME : path11.dirname(configDir);
1776
+ const skillsDir = path11.join(agentsHome, ".agents", "skills");
1717
1777
  return {
1718
- systemPromptFile: path10.join(configDir, "AGENTS.md"),
1719
- agentsDir: path10.join(configDir, "agents"),
1778
+ systemPromptFile: path11.join(configDir, "AGENTS.md"),
1779
+ agentsDir: path11.join(configDir, "agents"),
1720
1780
  skillsDir,
1721
1781
  // Los custom prompts de Codex están deprecados: los commands se
1722
1782
  // instalan como skills (renderCommand produce <nombre>/SKILL.md).
1723
1783
  commandsDir: skillsDir,
1724
1784
  pluginsDir: null,
1725
- scriptsDir: path10.join(configDir, "scripts"),
1785
+ scriptsDir: path11.join(configDir, "scripts"),
1726
1786
  outputStylesDir: null,
1727
1787
  profilesDir: configDir
1728
1788
  };
@@ -1770,7 +1830,7 @@ ${body}`
1770
1830
  planHooks(canonical, ctx) {
1771
1831
  const actions = [];
1772
1832
  const { scriptsDir } = this.paths(ctx.configDir);
1773
- const hooksFile = path10.join(ctx.configDir, "hooks.json");
1833
+ const hooksFile = path11.join(ctx.configDir, "hooks.json");
1774
1834
  const content = upsertNativeHooks(
1775
1835
  readTextIfExists(hooksFile),
1776
1836
  canonical,
@@ -1781,16 +1841,16 @@ ${body}`
1781
1841
  ctx.warnings.push(
1782
1842
  "Codex: los hooks no-managed requieren aprobaci\xF3n manual \u2014 ejecuta /hooks dentro de codex para activarlos."
1783
1843
  );
1784
- const scriptsSource = path10.join(ctx.stackDir, "scripts");
1785
- if (fs7.existsSync(scriptsSource)) {
1786
- for (const f of fs7.readdirSync(scriptsSource)) {
1787
- actions.push({ kind: "copy", source: path10.join(scriptsSource, f), target: path10.join(scriptsDir, f) });
1844
+ const scriptsSource = path11.join(ctx.stackDir, "scripts");
1845
+ if (fs8.existsSync(scriptsSource)) {
1846
+ for (const f of fs8.readdirSync(scriptsSource)) {
1847
+ actions.push({ kind: "copy", source: path11.join(scriptsSource, f), target: path11.join(scriptsDir, f) });
1788
1848
  }
1789
1849
  }
1790
1850
  return actions;
1791
1851
  },
1792
1852
  planMainConfig(canonical, ctx) {
1793
- const file = path10.join(ctx.configDir, "config.toml");
1853
+ const file = path11.join(ctx.configDir, "config.toml");
1794
1854
  const original = readTextIfExists(file);
1795
1855
  const contentSource = original === null || original.trim() === "" ? null : original;
1796
1856
  let content = contentSource;
@@ -1904,13 +1964,10 @@ args = [${(server.args ?? []).map(tomlString).join(", ")}]`);
1904
1964
  const { systemPromptFile } = this.paths(ctx.configDir);
1905
1965
  const prompt = readTextIfExists(systemPromptFile);
1906
1966
  if (prompt !== null) {
1907
- let content = removeMarkdownSection(prompt, "system-prompt");
1908
- content = removeMarkdownSection(content, "engram-protocol");
1909
- content = removeMarkdownSection(content, "browser");
1910
- content = removeMarkdownSection(content, "writing-style");
1967
+ const content = removeSystemPromptSections(prompt);
1911
1968
  actions.push({ kind: "write", target: systemPromptFile, content });
1912
1969
  }
1913
- const configFile = path10.join(ctx.configDir, "config.toml");
1970
+ const configFile = path11.join(ctx.configDir, "config.toml");
1914
1971
  const config = readTextIfExists(configFile);
1915
1972
  if (config !== null) {
1916
1973
  let content = config;
@@ -1945,7 +2002,7 @@ args = [${(server.args ?? []).map(tomlString).join(", ")}]`);
1945
2002
  ...primaryModelOwnership.length > 0 ? { primaryModelOwnership } : {}
1946
2003
  });
1947
2004
  }
1948
- const hooksFile = path10.join(ctx.configDir, "hooks.json");
2005
+ const hooksFile = path11.join(ctx.configDir, "hooks.json");
1949
2006
  const hooksJson = readTextIfExists(hooksFile);
1950
2007
  if (hooksJson !== null) {
1951
2008
  const content = removeNativeHooks(hooksJson, hooks);
@@ -1958,8 +2015,8 @@ args = [${(server.args ?? []).map(tomlString).join(", ")}]`);
1958
2015
  };
1959
2016
 
1960
2017
  // src/lib/install-mode.ts
1961
- import fs8 from "fs";
1962
- import path11 from "path";
2018
+ import fs9 from "fs";
2019
+ import path12 from "path";
1963
2020
  var INSTALL_MODES = /* @__PURE__ */ new Set(["human", "programmatic"]);
1964
2021
  var SUBAGENT_CONCURRENCIES = /* @__PURE__ */ new Set(["serial", "parallel"]);
1965
2022
  var DEFAULT_INSTALL_MODE_PREFERENCE = {
@@ -1967,10 +2024,10 @@ var DEFAULT_INSTALL_MODE_PREFERENCE = {
1967
2024
  subagentConcurrency: "serial"
1968
2025
  };
1969
2026
  function installModePreferenceFile() {
1970
- return path11.join(dataDir(), "install-mode.json");
2027
+ return path12.join(dataDir(), "install-mode.json");
1971
2028
  }
1972
2029
  function hasInstallModePreference(file = installModePreferenceFile()) {
1973
- return fs8.existsSync(file);
2030
+ return fs9.existsSync(file);
1974
2031
  }
1975
2032
  function isInstallMode(value) {
1976
2033
  return INSTALL_MODES.has(value);
@@ -2001,9 +2058,9 @@ function normalizeInstallModePreference(value) {
2001
2058
  throw new Error("Preferencia de instalaci\xF3n inv\xE1lida o corrupta.");
2002
2059
  }
2003
2060
  function loadInstallModePreference(file = installModePreferenceFile()) {
2004
- if (!fs8.existsSync(file)) return DEFAULT_INSTALL_MODE_PREFERENCE;
2061
+ if (!fs9.existsSync(file)) return DEFAULT_INSTALL_MODE_PREFERENCE;
2005
2062
  try {
2006
- const raw = fs8.readFileSync(file, "utf8");
2063
+ const raw = fs9.readFileSync(file, "utf8");
2007
2064
  return normalizeInstallModePreference(JSON.parse(raw));
2008
2065
  } catch (error) {
2009
2066
  const message = error instanceof Error ? error.message : String(error);
@@ -2041,10 +2098,10 @@ function parseInstallModePreferenceFlags(mode, subagentConcurrency) {
2041
2098
  }
2042
2099
 
2043
2100
  // src/lib/manifest.ts
2044
- import fs9 from "fs";
2045
- import path12 from "path";
2101
+ import fs10 from "fs";
2102
+ import path13 from "path";
2046
2103
  function manifestFile() {
2047
- return path12.join(dataDir(), "manifest.json");
2104
+ return path13.join(dataDir(), "manifest.json");
2048
2105
  }
2049
2106
  function readManifest(file = manifestFile()) {
2050
2107
  const raw = readTextIfExists(file);
@@ -2068,24 +2125,24 @@ function removeRuntimeManifest(id, file = manifestFile()) {
2068
2125
  writeText(file, JSON.stringify(manifest, null, 2) + "\n");
2069
2126
  }
2070
2127
  function findOrphans(prevOwned, currentTargets, root = HOME) {
2071
- return prevOwned.map((f) => path12.resolve(f)).filter(
2072
- (f) => !currentTargets.has(f) && path12.basename(f) !== "engram.ts" && isContainedIn(f, root) && fs9.existsSync(f)
2128
+ return prevOwned.map((f) => path13.resolve(f)).filter(
2129
+ (f) => !currentTargets.has(f) && path13.basename(f) !== "engram.ts" && isContainedIn(f, root) && fs10.existsSync(f)
2073
2130
  );
2074
2131
  }
2075
2132
 
2076
2133
  // src/components/system-prompt.ts
2077
- import path14 from "path";
2078
- import fs11 from "fs";
2134
+ import path15 from "path";
2135
+ import fs12 from "fs";
2079
2136
 
2080
2137
  // src/lib/mode-composition.ts
2081
- import fs10 from "fs";
2082
- import path13 from "path";
2138
+ import fs11 from "fs";
2139
+ import path14 from "path";
2083
2140
  var PROGRAMMATIC_ROOT = ["modes", "programmatic"];
2084
2141
  var PROGRAMMATIC_MARKER = "<!-- jorgex:programmatic-mode -->";
2085
2142
  var LEGACY_RESULT_CONTRACT_SECTION = /\n?##\s+Result contract[\s\S]*$/;
2086
2143
  var normalize = (value) => value.replace(/\r\n/g, "\n");
2087
2144
  function loadProgrammaticAddendum(stackDir, fileName) {
2088
- return normalize(fs10.readFileSync(path13.join(stackDir, ...PROGRAMMATIC_ROOT, fileName), "utf8")).trim();
2145
+ return normalize(fs11.readFileSync(path14.join(stackDir, ...PROGRAMMATIC_ROOT, fileName), "utf8")).trim();
2089
2146
  }
2090
2147
  function appendAddendum(base, addendum) {
2091
2148
  const normalizedBase = normalize(base);
@@ -2128,30 +2185,30 @@ function composeProgrammaticAgentBody(stackDir, agent, mode, concurrency) {
2128
2185
  var normalize2 = (s) => s.replace(/\r\n/g, "\n");
2129
2186
  function planSystemPrompt(adapter, ctx) {
2130
2187
  const target = adapter.paths(ctx.configDir).systemPromptFile;
2131
- const agentsMd = normalize2(fs11.readFileSync(path14.join(ctx.stackDir, "system-prompt", "AGENTS.md"), "utf8"));
2132
- const protocol = stripLeadingHtmlComments(
2133
- normalize2(fs11.readFileSync(path14.join(ctx.stackDir, "system-prompt", "engram-protocol.md"), "utf8"))
2188
+ const existing = readSystemPromptFile(target);
2189
+ assertSystemPromptMarkers(existing, target);
2190
+ const readModule = (file) => stripLeadingHtmlComments(
2191
+ normalize2(fs12.readFileSync(path15.join(ctx.stackDir, "system-prompt", file), "utf8"))
2134
2192
  );
2135
- const composedAgentsMd = composeProgrammaticSystemPrompt(ctx.stackDir, agentsMd, ctx.mode);
2136
- const browser = [
2137
- ctx.playwrightCliEnabled ? "browser-playwright.md" : null,
2138
- ctx.enabledMcpServers?.has(DEVTOOLS_MCP_SERVER) ? "browser-chrome-devtools.md" : null
2139
- ].filter((file) => file !== null).map((file) => normalize2(fs11.readFileSync(path14.join(ctx.stackDir, "system-prompt", file), "utf8"))).join("\n\n");
2140
- let content = readTextIfExists(target);
2141
- content = upsertMarkdownSection(content, "system-prompt", composedAgentsMd);
2142
- if (adapter.injectEngramProtocol(ctx)) {
2143
- content = upsertMarkdownSection(content, "engram-protocol", protocol);
2144
- } else {
2145
- content = removeMarkdownSection(content, "engram-protocol");
2193
+ const modules = {
2194
+ "system-prompt": composeProgrammaticSystemPrompt(ctx.stackDir, readModule("AGENTS.md"), ctx.mode),
2195
+ "engram-protocol": adapter.injectEngramProtocol(ctx) ? readModule("engram-protocol.md") : void 0,
2196
+ context7: readModule("context7.md"),
2197
+ playwright: ctx.playwrightCliEnabled ? readModule("browser-playwright.md") : void 0,
2198
+ "chrome-devtools": ctx.enabledMcpServers?.has(DEVTOOLS_MCP_SERVER) ? readModule("browser-chrome-devtools.md") : void 0,
2199
+ "writing-style": ctx.mode === "programmatic" ? void 0 : ctx.writingStyle?.content ?? void 0
2200
+ };
2201
+ const sections = adapter.adaptSystemPromptSections?.(modules) ?? modules;
2202
+ let content = existing ?? "";
2203
+ for (const section of SYSTEM_PROMPT_SECTIONS) {
2204
+ const body = sections[section];
2205
+ content = body ? upsertMarkdownSection(content, section, body) : removeMarkdownSection(content, section);
2146
2206
  }
2147
- content = browser === "" ? removeMarkdownSection(content, "browser") : upsertMarkdownSection(content, "browser", browser);
2148
- const style = ctx.mode === "programmatic" ? null : ctx.writingStyle?.content;
2149
- content = style ? upsertMarkdownSection(content, "writing-style", style) : removeMarkdownSection(content, "writing-style");
2150
2207
  return [{ kind: "write", target, content }];
2151
2208
  }
2152
2209
 
2153
2210
  // src/components/agents.ts
2154
- import path15 from "path";
2211
+ import path16 from "path";
2155
2212
  function planAgents(adapter, ctx) {
2156
2213
  const { agentsDir, commandsDir, outputStylesDir, profilesDir, scriptsDir } = adapter.paths(ctx.configDir);
2157
2214
  const dirFor = {
@@ -2161,7 +2218,7 @@ function planAgents(adapter, ctx) {
2161
2218
  profile: profilesDir
2162
2219
  };
2163
2220
  const scriptsBase = scriptsDir.replace(/\\/g, "/");
2164
- return loadCanonicalAgents(path15.join(ctx.stackDir, "agents")).flatMap((agent) => {
2221
+ return loadCanonicalAgents(path16.join(ctx.stackDir, "agents")).flatMap((agent) => {
2165
2222
  const composedAgent = {
2166
2223
  ...agent,
2167
2224
  body: composeProgrammaticAgentBody(ctx.stackDir, agent, ctx.mode, ctx.subagentConcurrency)
@@ -2173,42 +2230,42 @@ function planAgents(adapter, ctx) {
2173
2230
  return [];
2174
2231
  }
2175
2232
  const content = rendered.content.replace(/\{\{SCRIPTS_DIR\}\}/g, scriptsBase);
2176
- return [{ kind: "write", target: path15.join(dir, rendered.file), content }];
2233
+ return [{ kind: "write", target: path16.join(dir, rendered.file), content }];
2177
2234
  });
2178
2235
  });
2179
2236
  }
2180
2237
 
2181
2238
  // src/components/skills.ts
2182
- import path16 from "path";
2239
+ import path17 from "path";
2183
2240
  function planSkills(adapter, ctx) {
2184
2241
  const { skillsDir } = adapter.paths(ctx.configDir);
2185
- const source = path16.join(ctx.stackDir, "skills");
2242
+ const source = path17.join(ctx.stackDir, "skills");
2186
2243
  return listFilesRecursive(source).map((file) => {
2187
- const relative = path16.relative(source, file);
2188
- return { kind: "copy", source: file, target: path16.join(skillsDir, relative) };
2244
+ const relative = path17.relative(source, file);
2245
+ return { kind: "copy", source: file, target: path17.join(skillsDir, relative) };
2189
2246
  });
2190
2247
  }
2191
2248
 
2192
2249
  // src/components/commands.ts
2193
- import path17 from "path";
2194
- import fs12 from "fs";
2250
+ import path18 from "path";
2251
+ import fs13 from "fs";
2195
2252
  function planCommands(adapter, ctx) {
2196
2253
  const { commandsDir } = adapter.paths(ctx.configDir);
2197
- const source = path17.join(ctx.stackDir, "commands");
2198
- if (!fs12.existsSync(source)) return [];
2254
+ const source = path18.join(ctx.stackDir, "commands");
2255
+ if (!fs13.existsSync(source)) return [];
2199
2256
  const commandFiles = [
2200
2257
  ...listMarkdownFiles(source),
2201
- ...listMarkdownFiles(path17.join(source, adapter.id))
2258
+ ...listMarkdownFiles(path18.join(source, adapter.id))
2202
2259
  ];
2203
2260
  return commandFiles.map(({ file, fullPath }) => {
2204
- const raw = fs12.readFileSync(fullPath, "utf8").replace(/\r\n/g, "\n");
2261
+ const raw = fs13.readFileSync(fullPath, "utf8").replace(/\r\n/g, "\n");
2205
2262
  const rendered = adapter.renderCommand(file, raw);
2206
- return { kind: "write", target: path17.join(commandsDir, rendered.file), content: rendered.content };
2263
+ return { kind: "write", target: path18.join(commandsDir, rendered.file), content: rendered.content };
2207
2264
  });
2208
2265
  }
2209
2266
  function listMarkdownFiles(dir) {
2210
- if (!fs12.existsSync(dir)) return [];
2211
- return fs12.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => ({ file: entry.name, fullPath: path17.join(dir, entry.name) }));
2267
+ if (!fs13.existsSync(dir)) return [];
2268
+ return fs13.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => ({ file: entry.name, fullPath: path18.join(dir, entry.name) }));
2212
2269
  }
2213
2270
 
2214
2271
  // src/components/hooks.ts
@@ -2222,23 +2279,23 @@ function planMcp(adapter, ctx) {
2222
2279
  }
2223
2280
 
2224
2281
  // src/components/plugins.ts
2225
- import path18 from "path";
2226
- import fs13 from "fs";
2282
+ import path19 from "path";
2283
+ import fs14 from "fs";
2227
2284
  function planPlugins(adapter, ctx) {
2228
2285
  const { pluginsDir } = adapter.paths(ctx.configDir);
2229
2286
  if (pluginsDir === null) return [];
2230
- const source = path18.join(ctx.stackDir, "plugins", adapter.id);
2231
- if (!fs13.existsSync(source)) return [];
2287
+ const source = path19.join(ctx.stackDir, "plugins", adapter.id);
2288
+ if (!fs14.existsSync(source)) return [];
2232
2289
  return listFilesRecursive(source).filter((f) => f.endsWith(".ts")).map((sourceFile) => {
2233
- const target = path18.join(pluginsDir, path18.relative(source, sourceFile));
2234
- const raw = fs13.readFileSync(sourceFile, "utf8");
2290
+ const target = path19.join(pluginsDir, path19.relative(source, sourceFile));
2291
+ const raw = fs14.readFileSync(sourceFile, "utf8");
2235
2292
  let content = raw;
2236
2293
  if (content.includes('"{{ENGRAM_BIN}}"')) {
2237
2294
  content = content.replace(/"\{\{ENGRAM_BIN\}\}"/g, JSON.stringify(ctx.engramBin ?? "engram"));
2238
2295
  }
2239
2296
  if (content.includes('"{{ENGRAM_PROTOCOL}}"')) {
2240
2297
  const protocol = stripLeadingHtmlComments(
2241
- fs13.readFileSync(path18.join(ctx.stackDir, "system-prompt", "engram-protocol.md"), "utf8").replace(/\r\n/g, "\n")
2298
+ fs14.readFileSync(path19.join(ctx.stackDir, "system-prompt", "engram-protocol.md"), "utf8").replace(/\r\n/g, "\n")
2242
2299
  );
2243
2300
  content = content.replace(/"\{\{ENGRAM_PROTOCOL\}\}"/g, JSON.stringify(protocol));
2244
2301
  }
@@ -2249,9 +2306,9 @@ function planPlugins(adapter, ctx) {
2249
2306
  }
2250
2307
 
2251
2308
  // src/lib/external-tools.ts
2252
- import fs14 from "fs";
2309
+ import fs15 from "fs";
2253
2310
  import os from "os";
2254
- import path19 from "path";
2311
+ import path20 from "path";
2255
2312
  import { execFileSync as execFileSync2 } from "child_process";
2256
2313
  var PLAYWRIGHT_CLI = {
2257
2314
  packageName: "@playwright/cli",
@@ -2296,9 +2353,9 @@ function detectPlaywrightCli() {
2296
2353
  if (binPath === null) {
2297
2354
  const pnpmHome = resolvePnpmHome();
2298
2355
  const name = process.platform === "win32" ? `${PLAYWRIGHT_CLI.bin}.cmd` : PLAYWRIGHT_CLI.bin;
2299
- const known = [path19.join(pnpmHome, name), path19.join(pnpmHome, "bin", name)].find((file) => {
2356
+ const known = [path20.join(pnpmHome, name), path20.join(pnpmHome, "bin", name)].find((file) => {
2300
2357
  try {
2301
- return fs14.statSync(file).isFile();
2358
+ return fs15.statSync(file).isFile();
2302
2359
  } catch {
2303
2360
  return false;
2304
2361
  }
@@ -2317,7 +2374,7 @@ function resolvePnpmBin() {
2317
2374
  return pnpmCmd !== null && !pnpmCmd.toLowerCase().endsWith(".ps1") ? pnpmCmd : null;
2318
2375
  }
2319
2376
  function resolvePnpmHome(env = process.env, platform = process.platform, homeDir = os.homedir()) {
2320
- const paths = platform === "win32" ? path19.win32 : path19.posix;
2377
+ const paths = platform === "win32" ? path20.win32 : path20.posix;
2321
2378
  if (env.PNPM_HOME) return env.PNPM_HOME;
2322
2379
  if (env.XDG_DATA_HOME) return paths.join(env.XDG_DATA_HOME, "pnpm");
2323
2380
  if (platform === "darwin") return paths.join(homeDir, "Library", "pnpm");
@@ -2326,7 +2383,7 @@ function resolvePnpmHome(env = process.env, platform = process.platform, homeDir
2326
2383
  }
2327
2384
  function setupPnpmGlobal(pnpmBin, env = process.env, platform = process.platform, homeDir = os.homedir()) {
2328
2385
  const pnpmHome = resolvePnpmHome(env, platform, homeDir);
2329
- const paths = platform === "win32" ? path19.win32 : path19.posix;
2386
+ const paths = platform === "win32" ? path20.win32 : path20.posix;
2330
2387
  if (!paths.isAbsolute(pnpmHome)) return { ok: false, reason: "PNPM_HOME debe ser una ruta absoluta; corrige la configuraci\xF3n antes de reintentar." };
2331
2388
  const pathEntries = [pnpmHome, paths.join(pnpmHome, "bin")];
2332
2389
  if (env.PATH !== void 0) pathEntries.push(env.PATH);
@@ -2346,10 +2403,10 @@ function setupPnpmGlobal(pnpmBin, env = process.env, platform = process.platform
2346
2403
  }
2347
2404
  function isPlaywrightBrowserReady(env = process.env, platform = process.platform, homeDir = os.homedir()) {
2348
2405
  const configuredPath = env.PLAYWRIGHT_BROWSERS_PATH;
2349
- const cacheDir = configuredPath ?? (platform === "win32" ? path19.join(env.LOCALAPPDATA ?? path19.join(homeDir, "AppData", "Local"), "ms-playwright") : platform === "darwin" ? path19.join(homeDir, "Library", "Caches", "ms-playwright") : path19.join(env.XDG_CACHE_HOME ?? path19.join(homeDir, ".cache"), "ms-playwright"));
2406
+ const cacheDir = configuredPath ?? (platform === "win32" ? path20.join(env.LOCALAPPDATA ?? path20.join(homeDir, "AppData", "Local"), "ms-playwright") : platform === "darwin" ? path20.join(homeDir, "Library", "Caches", "ms-playwright") : path20.join(env.XDG_CACHE_HOME ?? path20.join(homeDir, ".cache"), "ms-playwright"));
2350
2407
  if (configuredPath === "0") return { status: "missing", path: cacheDir, errorCode: "DISABLED" };
2351
2408
  try {
2352
- const ready = fs14.readdirSync(cacheDir, { withFileTypes: true }).some(
2409
+ const ready = fs15.readdirSync(cacheDir, { withFileTypes: true }).some(
2353
2410
  (entry) => entry.isDirectory() && /^chromium(?:_headless_shell)?-/.test(entry.name)
2354
2411
  );
2355
2412
  return ready ? { status: "ready", path: cacheDir } : { status: "missing", path: cacheDir };
@@ -2381,9 +2438,9 @@ function verifyPlaywrightBrowser(pnpmBin, env = process.env, cwd) {
2381
2438
  cwd,
2382
2439
  stdio: ["ignore", "pipe", "inherit"]
2383
2440
  }).trim();
2384
- if (!path19.isAbsolute(root)) return false;
2385
- const packageFile = fs14.realpathSync(path19.join(root, "@playwright", "cli", "package.json"));
2386
- const installed = JSON.parse(fs14.readFileSync(packageFile, "utf8"));
2441
+ if (!path20.isAbsolute(root)) return false;
2442
+ const packageFile = fs15.realpathSync(path20.join(root, "@playwright", "cli", "package.json"));
2443
+ const installed = JSON.parse(fs15.readFileSync(packageFile, "utf8"));
2387
2444
  if (installed.name !== PLAYWRIGHT_CLI.packageName || installed.version !== PLAYWRIGHT_CLI.version) return false;
2388
2445
  const probe = `
2389
2446
  const { createRequire } = require('node:module');
@@ -2426,7 +2483,7 @@ function executePlaywrightToolAction(action, pnpmBin = resolvePnpmBin(), env) {
2426
2483
  const command = planPlaywrightCliCommand(action, pnpmBin);
2427
2484
  const invocation = planDetectedBinCommand(command.command, command.args);
2428
2485
  if (invocation === null) return { ok: false, reason: "pnpm-command" };
2429
- const cwd = action === "install-browser" ? fs14.mkdtempSync(path19.join(os.tmpdir(), "jorgex-playwright-")) : void 0;
2486
+ const cwd = action === "install-browser" ? fs15.mkdtempSync(path20.join(os.tmpdir(), "jorgex-playwright-")) : void 0;
2430
2487
  const childEnv = action === "install-browser" ? { ...env ?? process.env, NO_UPDATE_NOTIFIER: "1" } : env ?? process.env;
2431
2488
  try {
2432
2489
  execFileSync2(invocation.command, invocation.args, {
@@ -2441,27 +2498,27 @@ function executePlaywrightToolAction(action, pnpmBin = resolvePnpmBin(), env) {
2441
2498
  } catch {
2442
2499
  return { ok: false, reason: "action-failed" };
2443
2500
  } finally {
2444
- if (cwd !== void 0) fs14.rmSync(cwd, { recursive: true, force: true });
2501
+ if (cwd !== void 0) fs15.rmSync(cwd, { recursive: true, force: true });
2445
2502
  }
2446
2503
  }
2447
2504
 
2448
2505
  // src/lib/tool-preferences.ts
2449
- import fs15 from "fs";
2450
- import path20 from "path";
2506
+ import fs16 from "fs";
2507
+ import path21 from "path";
2451
2508
  var PLAYWRIGHT_CLI_PREFERENCE_VERSION = 1;
2452
2509
  var DEVTOOLS_MCP_PREFERENCE_VERSION = 1;
2453
2510
  var PRIMARY_MODEL_OWNERSHIP_VERSION = 1;
2454
2511
  var PLAYWRIGHT_RUNTIMES = ["opencode", "claude-code", "codex", "pi"];
2455
2512
  function readPreference(file) {
2456
2513
  try {
2457
- return { raw: fs15.readFileSync(file, "utf8"), errorCode: null };
2514
+ return { raw: fs16.readFileSync(file, "utf8"), errorCode: null };
2458
2515
  } catch (error) {
2459
2516
  const code = error instanceof Error && "code" in error && typeof error.code === "string" ? error.code : "UNKNOWN";
2460
2517
  return code === "ENOENT" ? { raw: null, errorCode: null } : { raw: null, errorCode: code };
2461
2518
  }
2462
2519
  }
2463
2520
  function playwrightCliPreferenceFile(stateDir = dataDir()) {
2464
- return path20.join(stateDir, "playwright-cli.json");
2521
+ return path21.join(stateDir, "playwright-cli.json");
2465
2522
  }
2466
2523
  function parsePlaywrightCliPreference(raw) {
2467
2524
  try {
@@ -2507,10 +2564,10 @@ function savePlaywrightCliPreference(file, enabled, selection) {
2507
2564
  writeText(file, content);
2508
2565
  }
2509
2566
  function devtoolsMcpPreferenceFile(stateDir = dataDir()) {
2510
- return path20.join(stateDir, "devtools-mcp.json");
2567
+ return path21.join(stateDir, "devtools-mcp.json");
2511
2568
  }
2512
2569
  function primaryModelOwnershipFile(stateDir = dataDir()) {
2513
- return path20.join(stateDir, "primary-model.json");
2570
+ return path21.join(stateDir, "primary-model.json");
2514
2571
  }
2515
2572
  function isRecord2(value) {
2516
2573
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -2620,7 +2677,7 @@ function primaryModelOwnershipError(file = primaryModelOwnershipFile()) {
2620
2677
  return `Primary model: ownership inv\xE1lido en ${file}. Corrige o borra ese archivo antes de reintentar.`;
2621
2678
  }
2622
2679
  function primaryModelConfigKey(configDir) {
2623
- const resolved = path20.resolve(configDir);
2680
+ const resolved = path21.resolve(configDir);
2624
2681
  return process.platform === "win32" ? resolved.toLowerCase() : resolved;
2625
2682
  }
2626
2683
  function loadPrimaryModelOwnership(file, runtime, configDir) {
@@ -2740,7 +2797,7 @@ function diffPlan(plan) {
2740
2797
  if (current === null) return { action, status: "create" };
2741
2798
  return { action, status: current === action.content ? "unchanged" : "update" };
2742
2799
  }
2743
- if (!fs16.existsSync(action.target)) return { action, status: "create" };
2800
+ if (!fs17.existsSync(action.target)) return { action, status: "create" };
2744
2801
  return { action, status: sameFileContent(action.source, action.target) ? "unchanged" : "update" };
2745
2802
  });
2746
2803
  }
@@ -2766,7 +2823,7 @@ function collectAllCurrentTargets(mode = DEFAULT_INSTALL_MODE_PREFERENCE) {
2766
2823
  continue;
2767
2824
  }
2768
2825
  try {
2769
- for (const action of buildPlan(adapter, ctx)) targets.add(path21.resolve(action.target));
2826
+ for (const action of buildPlan(adapter, ctx)) targets.add(path22.resolve(action.target));
2770
2827
  } catch (error) {
2771
2828
  complete = false;
2772
2829
  warnings.push(
@@ -2779,6 +2836,18 @@ function collectAllCurrentTargets(mode = DEFAULT_INSTALL_MODE_PREFERENCE) {
2779
2836
  async function runInstall(opts) {
2780
2837
  const showSummary = opts.showSummary !== false;
2781
2838
  if (showSummary) p.intro(`jorgex-stack ${opts.dryRun ? "install (dry-run)" : "install"}`);
2839
+ try {
2840
+ for (const id of opts.runtimes) {
2841
+ const adapter = ADAPTERS[id];
2842
+ if (!adapter) continue;
2843
+ const detection = adapter.detect();
2844
+ if (opts.targetDir === void 0 && !detection.installed) continue;
2845
+ assertSystemPromptFile(adapter.paths(opts.targetDir ?? detection.configDir).systemPromptFile, opts.targetDir);
2846
+ }
2847
+ } catch (error) {
2848
+ p.log.error(error instanceof Error ? error.message : String(error));
2849
+ return 1;
2850
+ }
2782
2851
  let writingStyle;
2783
2852
  let preparedStyle;
2784
2853
  try {
@@ -2889,10 +2958,10 @@ async function runInstall(opts) {
2889
2958
  }
2890
2959
  const writeManifest = () => {
2891
2960
  if (!useManifest) return;
2892
- const unmergeTargets = new Set(adapter.planUnmerge(canonicalMcp, canonicalHooks, ctx).map((a) => path21.resolve(a.target)));
2961
+ const unmergeTargets = new Set(adapter.planUnmerge(canonicalMcp, canonicalHooks, ctx).map((a) => path22.resolve(a.target)));
2893
2962
  const keepTarget = (target) => !unmergeTargets.has(target);
2894
- const liveOwned = plan.map((a) => path21.resolve(a.target)).filter(keepTarget);
2895
- const previousOwned = (prevManifest?.owned ?? []).map((target) => path21.resolve(target)).filter(keepTarget);
2963
+ const liveOwned = plan.map((a) => path22.resolve(a.target)).filter(keepTarget);
2964
+ const previousOwned = (prevManifest?.owned ?? []).map((target) => path22.resolve(target)).filter(keepTarget);
2896
2965
  const owned = canOrphan ? liveOwned : [.../* @__PURE__ */ new Set([...previousOwned, ...liveOwned])];
2897
2966
  writeRuntimeManifest(id, { configDir, owned, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
2898
2967
  };
@@ -2921,9 +2990,9 @@ async function runInstall(opts) {
2921
2990
  const backup = useManifest ? createBackup([...updates.map((c) => c.action.target), ...orphans], `install-${id}`) : null;
2922
2991
  if (backup) p.log.info(`Backup: ${backup.id} (${backup.files.length} archivos)`);
2923
2992
  applyChanges(changes, useManifest ? (action) => persistConfigurationOwnershipChanges(id, configDir, [action]) : void 0);
2924
- const pruneRoot = useManifest ? HOME : path21.dirname(configDir);
2993
+ const pruneRoot = useManifest ? HOME : path22.dirname(configDir);
2925
2994
  for (const orphan of orphans) {
2926
- fs16.rmSync(orphan, { force: true });
2995
+ fs17.rmSync(orphan, { force: true });
2927
2996
  pruneEmptyDirs(orphan, pruneRoot);
2928
2997
  }
2929
2998
  const verifyCtx = { ...ctx, warnings: [] };
@@ -3048,29 +3117,33 @@ async function runInstall(opts) {
3048
3117
  }
3049
3118
 
3050
3119
  // src/uninstall.ts
3051
- import fs18 from "fs";
3052
- import path25 from "path";
3120
+ import fs19 from "fs";
3121
+ import path26 from "path";
3053
3122
  import * as p2 from "@clack/prompts";
3054
3123
 
3055
3124
  // src/lib/pi-projection-lifecycle.ts
3056
- import fs17 from "fs";
3057
- import path24 from "path";
3125
+ import fs18 from "fs";
3126
+ import path25 from "path";
3058
3127
  import { createHash } from "crypto";
3059
3128
 
3060
3129
  // src/adapters/pi.ts
3061
- import path22 from "path";
3130
+ import path23 from "path";
3131
+ function piSystemPromptFile(targetDir) {
3132
+ const configDir = targetDir === void 0 ? process.env.PI_CODING_AGENT_DIR ?? path23.join(HOME, ".pi", "agent") : path23.join(path23.resolve(targetDir), "pi-agent");
3133
+ return path23.join(configDir, "AGENTS.md");
3134
+ }
3062
3135
  var piAdapter = {
3063
3136
  id: "pi",
3064
3137
  paths(configDir) {
3065
- const piConfigDir = process.env.PI_CODING_AGENT_DIR ?? path22.join(HOME, ".pi", "agent");
3066
- const agentsHome = samePath(configDir, piConfigDir) ? HOME : path22.join(path22.dirname(configDir), "home");
3138
+ const piConfigDir = path23.dirname(piSystemPromptFile());
3139
+ const agentsHome = samePath(configDir, piConfigDir) ? HOME : path23.join(path23.dirname(configDir), "home");
3067
3140
  return {
3068
- systemPromptFile: path22.join(configDir, "AGENTS.md"),
3069
- agentsDir: path22.join(configDir, "agents"),
3070
- skillsDir: path22.join(agentsHome, ".agents", "skills"),
3071
- commandsDir: path22.join(configDir, "prompts"),
3141
+ systemPromptFile: path23.join(configDir, "AGENTS.md"),
3142
+ agentsDir: path23.join(configDir, "agents"),
3143
+ skillsDir: path23.join(agentsHome, ".agents", "skills"),
3144
+ commandsDir: path23.join(configDir, "prompts"),
3072
3145
  pluginsDir: null,
3073
- scriptsDir: path22.join(configDir, "scripts"),
3146
+ scriptsDir: path23.join(configDir, "scripts"),
3074
3147
  outputStylesDir: null,
3075
3148
  profilesDir: null
3076
3149
  };
@@ -3080,11 +3153,20 @@ var piAdapter = {
3080
3153
  },
3081
3154
  injectEngramProtocol() {
3082
3155
  return true;
3156
+ },
3157
+ // Pi 0.8.18 recompone estos marcadores desde su snapshot: no conoce los nuevos.
3158
+ adaptSystemPromptSections(sections) {
3159
+ const { context7, playwright, "chrome-devtools": devtools, ...legacy } = sections;
3160
+ return {
3161
+ ...legacy,
3162
+ "system-prompt": [legacy["system-prompt"], context7].filter(Boolean).join("\n\n"),
3163
+ browser: [playwright, devtools].filter(Boolean).join("\n\n")
3164
+ };
3083
3165
  }
3084
3166
  };
3085
3167
 
3086
3168
  // src/lib/pi-package-lifecycle.ts
3087
- import path23 from "path";
3169
+ import path24 from "path";
3088
3170
  var REQUIRED_CAPABILITIES = /* @__PURE__ */ new Set([
3089
3171
  "foundation-contract-v1",
3090
3172
  "runner-json-v1",
@@ -3216,7 +3298,7 @@ function parseReceiptShape(receiptJson) {
3216
3298
  return null;
3217
3299
  }
3218
3300
  if (engram === void 0) return "upgrade-required";
3219
- if (engram === null || typeof engram !== "object" || Array.isArray(engram) || typeof Reflect.get(engram, "binary") !== "string" || !path23.isAbsolute(Reflect.get(engram, "binary"))) {
3301
+ if (engram === null || typeof engram !== "object" || Array.isArray(engram) || typeof Reflect.get(engram, "binary") !== "string" || !path24.isAbsolute(Reflect.get(engram, "binary"))) {
3220
3302
  return null;
3221
3303
  }
3222
3304
  return parsed;
@@ -3253,7 +3335,7 @@ function planPiPackageLifecycle(input) {
3253
3335
  if (input.receiptJson !== null) {
3254
3336
  const parsedReceipt = parseReceipt(input.receiptJson, input.candidate, {
3255
3337
  kind: input.scope.kind,
3256
- codingAgentDir: path23.resolve(input.scope.codingAgentDir)
3338
+ codingAgentDir: path24.resolve(input.scope.codingAgentDir)
3257
3339
  }, input.engramBin);
3258
3340
  if (parsedReceipt === "upgrade-required") return blocked(input, "receipt-upgrade-required");
3259
3341
  if (parsedReceipt === null) return blocked(input, "receipt-corrupt");
@@ -3288,7 +3370,7 @@ function planPiPackageLifecycle(input) {
3288
3370
  },
3289
3371
  receipt: expectedReceipt(input.candidate, "installing", {
3290
3372
  kind: input.scope.kind,
3291
- codingAgentDir: path23.resolve(input.scope.codingAgentDir)
3373
+ codingAgentDir: path24.resolve(input.scope.codingAgentDir)
3292
3374
  }, input.engramBin),
3293
3375
  ownership: ownership(true)
3294
3376
  };
@@ -3303,7 +3385,7 @@ function parseRunnerRecord(stdout, stderr, command, candidate, packageRunner) {
3303
3385
  const parsed = JSON.parse(body);
3304
3386
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return null;
3305
3387
  const record = parsed;
3306
- if (record.schemaVersion !== candidate.contract.runner.schemaVersion || record.command !== command || record.ok !== true || record.package === null || typeof record.package !== "object" || record.package.name !== candidate.package.name || record.package.version !== candidate.package.version || typeof record.package.root !== "string" || !path23.isAbsolute(record.package.root) || path23.resolve(packageRunner) !== path23.resolve(record.package.root, "bin", "jorgex-pi.mjs")) {
3388
+ if (record.schemaVersion !== candidate.contract.runner.schemaVersion || record.command !== command || record.ok !== true || record.package === null || typeof record.package !== "object" || record.package.name !== candidate.package.name || record.package.version !== candidate.package.version || typeof record.package.root !== "string" || !path24.isAbsolute(record.package.root) || path24.resolve(packageRunner) !== path24.resolve(record.package.root, "bin", "jorgex-pi.mjs")) {
3307
3389
  return null;
3308
3390
  }
3309
3391
  return record;
@@ -3389,10 +3471,10 @@ function validateOwnedOperationState(input) {
3389
3471
  }))) {
3390
3472
  return { kind: "blocked", reason: "receipt-untrusted" };
3391
3473
  }
3392
- if (receipt.scope.kind !== (input.paths.targetDir ? "target-dir" : "real") || path23.resolve(receipt.scope.codingAgentDir) !== path23.resolve(input.paths.codingAgentDir)) {
3474
+ if (receipt.scope.kind !== (input.paths.targetDir ? "target-dir" : "real") || path24.resolve(receipt.scope.codingAgentDir) !== path24.resolve(input.paths.codingAgentDir)) {
3393
3475
  return { kind: "blocked", reason: "source-divergent" };
3394
3476
  }
3395
- if (input.engramBin !== null && path23.resolve(receipt.engram.binary) !== path23.resolve(input.engramBin)) {
3477
+ if (input.engramBin !== null && path24.resolve(receipt.engram.binary) !== path24.resolve(input.engramBin)) {
3396
3478
  return { kind: "blocked", reason: "receipt-corrupt" };
3397
3479
  }
3398
3480
  const source = receipt.candidate.package.source;
@@ -3499,7 +3581,7 @@ var RETIRED_PLAYWRIGHT_FILES = [
3499
3581
  "references/video-recording.md"
3500
3582
  ];
3501
3583
  function retiredPlaywrightFiles(scope) {
3502
- return RETIRED_PLAYWRIGHT_FILES.map((file) => path24.resolve(scope.home, ".agents", "skills", "playwright-cli", file));
3584
+ return RETIRED_PLAYWRIGHT_FILES.map((file) => path25.resolve(scope.home, ".agents", "skills", "playwright-cli", file));
3503
3585
  }
3504
3586
  function handoffEnabled(input, kind) {
3505
3587
  return (kind === "devtools" ? input.devtoolsMcpEnabled : input.playwrightHandoffEnabled) === true;
@@ -3508,18 +3590,18 @@ var preparedPiProjectionUninstalls = /* @__PURE__ */ new WeakMap();
3508
3590
  function projectionScope(scope) {
3509
3591
  return {
3510
3592
  ...scope,
3511
- settingsFile: path24.join(scope.codingAgentDir, "settings.json")
3593
+ settingsFile: path25.join(scope.codingAgentDir, "settings.json")
3512
3594
  };
3513
3595
  }
3514
3596
  function isInside(root, file) {
3515
- const relative = path24.relative(path24.resolve(root), path24.resolve(file));
3516
- return relative === "" || relative !== ".." && !relative.startsWith(`..${path24.sep}`) && !path24.isAbsolute(relative);
3597
+ const relative = path25.relative(path25.resolve(root), path25.resolve(file));
3598
+ return relative === "" || relative !== ".." && !relative.startsWith(`..${path25.sep}`) && !path25.isAbsolute(relative);
3517
3599
  }
3518
3600
  function isManagedPath(scope, file) {
3519
3601
  return isInside(scope.home, file) || isInside(scope.codingAgentDir, file);
3520
3602
  }
3521
3603
  function isAllowedPath(scope, file) {
3522
- return isManagedPath(scope, file) || path24.resolve(file) === path24.resolve(scope.receiptFile);
3604
+ return isManagedPath(scope, file) || path25.resolve(file) === path25.resolve(scope.receiptFile);
3523
3605
  }
3524
3606
  function projectedPiAdapter(scope) {
3525
3607
  const paths = piAdapter.paths(scope.codingAgentDir);
@@ -3527,11 +3609,11 @@ function projectedPiAdapter(scope) {
3527
3609
  ...piAdapter,
3528
3610
  paths: () => ({
3529
3611
  ...paths,
3530
- skillsDir: path24.join(scope.home, ".agents", "skills")
3612
+ skillsDir: path25.join(scope.home, ".agents", "skills")
3531
3613
  })
3532
3614
  };
3533
3615
  }
3534
- function projectionPlan(input, scope) {
3616
+ function projectionPlan(input, scope, includePrompt = true) {
3535
3617
  const ctx = {
3536
3618
  writingStyle: input.writingStyle,
3537
3619
  stackDir: input.stackDir,
@@ -3544,7 +3626,7 @@ function projectionPlan(input, scope) {
3544
3626
  };
3545
3627
  const adapter = projectedPiAdapter(scope);
3546
3628
  const actions = [
3547
- ...planSystemPrompt(adapter, ctx),
3629
+ ...includePrompt ? planSystemPrompt(adapter, ctx) : [],
3548
3630
  ...planSkills(adapter, ctx),
3549
3631
  ...planCommands(adapter, ctx)
3550
3632
  ];
@@ -3571,20 +3653,20 @@ function projectionPlan(input, scope) {
3571
3653
  return actions;
3572
3654
  }
3573
3655
  function handoffPath(scope, kind) {
3574
- return path24.resolve(scope.codingAgentDir, "jorgex-pi", `${kind}.v1.json`);
3656
+ return path25.resolve(scope.codingAgentDir, "jorgex-pi", `${kind}.v1.json`);
3575
3657
  }
3576
3658
  function contentHash(content) {
3577
3659
  return createHash("sha256").update(content).digest("hex");
3578
3660
  }
3579
3661
  function canonicalActionContent(action) {
3580
- return action.kind === "write" ? action.content : fs17.readFileSync(action.source, "utf8");
3662
+ return action.kind === "write" ? action.content : fs18.readFileSync(action.source, "utf8");
3581
3663
  }
3582
3664
  function hasActionDrift(action, deps) {
3583
3665
  return deps.readText(action.target) !== canonicalActionContent(action);
3584
3666
  }
3585
3667
  function applyActions(actions, deps, handoffs = []) {
3586
3668
  for (const action of actions) {
3587
- const handoff = handoffs.find((entry) => path24.resolve(action.target) === entry.file);
3669
+ const handoff = handoffs.find((entry) => path25.resolve(action.target) === entry.file);
3588
3670
  if (handoff) {
3589
3671
  try {
3590
3672
  if (deps.readText(handoff.file) !== handoff.previous) return handoffConflict(handoff.kind, handoff.file);
@@ -3604,9 +3686,9 @@ function assertPlanContained(plan, scope) {
3604
3686
  }
3605
3687
  }
3606
3688
  function receiptFor(plan, scope) {
3607
- const systemPrompt = path24.join(scope.codingAgentDir, "AGENTS.md");
3689
+ const systemPrompt = path25.join(scope.codingAgentDir, "AGENTS.md");
3608
3690
  const owned = [...new Set(
3609
- plan.map((action) => path24.resolve(action.target)).filter((target) => target !== systemPrompt)
3691
+ plan.map((action) => path25.resolve(action.target)).filter((target) => target !== systemPrompt)
3610
3692
  )];
3611
3693
  const receipt = {
3612
3694
  schemaVersion: 1,
@@ -3619,7 +3701,7 @@ function receiptFor(plan, scope) {
3619
3701
  owned
3620
3702
  };
3621
3703
  for (const kind of HANDOFF_KINDS) {
3622
- const action = plan.find((entry) => path24.resolve(entry.target) === handoffPath(scope, kind));
3704
+ const action = plan.find((entry) => path25.resolve(entry.target) === handoffPath(scope, kind));
3623
3705
  if (action) receipt[kind] = { sha256: contentHash(canonicalActionContent(action)) };
3624
3706
  }
3625
3707
  return receipt;
@@ -3660,7 +3742,7 @@ function parseReceipt2(raw, expected) {
3660
3742
  if (matches(expectedOwned)) return { schemaVersion: 1, scope: expected.scope, owned: expectedOwned, ...digests };
3661
3743
  const retired = retiredPlaywrightFiles(expected.scope);
3662
3744
  if (retired.some((file) => expectedOwned.includes(file))) return null;
3663
- const skillsRoot = `${path24.resolve(expected.scope.home, ".agents", "skills")}${path24.sep}`;
3745
+ const skillsRoot = `${path25.resolve(expected.scope.home, ".agents", "skills")}${path25.sep}`;
3664
3746
  const insertion = expectedOwned.findIndex((file) => !file.startsWith(skillsRoot) || file > retired[0]);
3665
3747
  const legacyOwned = [...expectedOwned];
3666
3748
  legacyOwned.splice(insertion === -1 ? legacyOwned.length : insertion, 0, ...retired);
@@ -3671,7 +3753,7 @@ function parseReceipt2(raw, expected) {
3671
3753
  }
3672
3754
  }
3673
3755
  function expectedProjectionReceipt(input, scope) {
3674
- const plan = projectionPlan(input, scope);
3756
+ const plan = projectionPlan(input, scope, false);
3675
3757
  assertPlanContained(plan, scope);
3676
3758
  return receiptFor(plan, scope);
3677
3759
  }
@@ -3679,13 +3761,13 @@ function realPiProjectionScope() {
3679
3761
  return {
3680
3762
  kind: "real",
3681
3763
  home: HOME,
3682
- codingAgentDir: process.env.PI_CODING_AGENT_DIR ?? path24.join(HOME, ".pi", "agent"),
3683
- receiptFile: path24.join(dataDir(), "pi-projection-receipt.json")
3764
+ codingAgentDir: path25.dirname(piSystemPromptFile()),
3765
+ receiptFile: path25.join(dataDir(), "pi-projection-receipt.json")
3684
3766
  };
3685
3767
  }
3686
3768
  function readTextOnlyIfMissing(file) {
3687
3769
  try {
3688
- return fs17.readFileSync(file, "utf8");
3770
+ return fs18.readFileSync(file, "utf8");
3689
3771
  } catch (error) {
3690
3772
  if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "ENOENT") return null;
3691
3773
  throw error;
@@ -3718,13 +3800,13 @@ function manifestOwned(manifest) {
3718
3800
  return new Set([
3719
3801
  ...manifest.runtimes.codex?.owned ?? [],
3720
3802
  ...manifest.runtimes.opencode?.owned ?? []
3721
- ].map((file) => path24.resolve(file)));
3803
+ ].map((file) => path25.resolve(file)));
3722
3804
  }
3723
3805
  function withoutManagedPromptSections(content) {
3724
- return ["system-prompt", "engram-protocol", "browser", "writing-style"].reduce((current, section) => removeMarkdownSection(current, section), content);
3806
+ return removeSystemPromptSections(content);
3725
3807
  }
3726
3808
  function uniquePaths(paths) {
3727
- return [...new Set(paths.map((file) => path24.resolve(file)))];
3809
+ return [...new Set(paths.map((file) => path25.resolve(file)))];
3728
3810
  }
3729
3811
  function blocked2(reason, paths, remedy) {
3730
3812
  return { kind: "blocked", reason, paths: uniquePaths(paths), remedy };
@@ -3831,15 +3913,20 @@ function preparePiProjectionUninstall(input, deps) {
3831
3913
  }
3832
3914
  const scope = projectionScope(input.scope);
3833
3915
  const expectedReceipt2 = expectedProjectionReceipt(input, scope);
3834
- const prompt = path24.join(scope.codingAgentDir, "AGENTS.md");
3916
+ const prompt = path25.join(scope.codingAgentDir, "AGENTS.md");
3835
3917
  let existingPrompt;
3836
3918
  try {
3837
3919
  existingPrompt = deps.readText(prompt);
3838
3920
  } catch {
3839
3921
  return cleanupFailure([prompt]);
3840
3922
  }
3923
+ try {
3924
+ assertSystemPromptMarkers(existingPrompt, prompt);
3925
+ } catch (error) {
3926
+ return blocked2("projection-prompt-markers", [prompt], error instanceof Error ? error.message : String(error));
3927
+ }
3841
3928
  const promptContent = existingPrompt === null ? null : withoutManagedPromptSections(existingPrompt);
3842
- const promptUpdate = promptContent === null || promptContent === existingPrompt ? null : { file: path24.resolve(prompt), content: promptContent };
3929
+ const promptUpdate = promptContent === null || promptContent === existingPrompt ? null : { file: path25.resolve(prompt), content: promptContent };
3843
3930
  const receiptRead = readProjectionReceipt(scope.receiptFile, deps);
3844
3931
  if (receiptRead.kind === "unreadable") return receiptUnreadable(scope.receiptFile);
3845
3932
  let ownedToRemove = [];
@@ -3857,7 +3944,7 @@ function preparePiProjectionUninstall(input, deps) {
3857
3944
  return cleanupFailure([scope.receiptFile]);
3858
3945
  }
3859
3946
  ownedToRemove = receipt.owned.filter((owned) => !retained.has(owned));
3860
- receiptFile = path24.resolve(scope.receiptFile);
3947
+ receiptFile = path25.resolve(scope.receiptFile);
3861
3948
  backupTargets = [...backupTargets, ...receipt.owned, receiptFile];
3862
3949
  }
3863
3950
  const handoffs = [];
@@ -3935,10 +4022,16 @@ function runPiProjectionLifecycle(input, deps) {
3935
4022
  const prepared = preparePiProjectionUninstall(input, deps);
3936
4023
  return prepared.kind === "blocked" ? prepared : completePiProjectionUninstall(prepared.plan, deps);
3937
4024
  }
3938
- if (input.devtoolsMcpEnabled && (!input.pnpmBin || !path24.isAbsolute(input.pnpmBin))) {
4025
+ const prompt = path25.join(scope.codingAgentDir, "AGENTS.md");
4026
+ try {
4027
+ assertSystemPromptMarkers(deps.readText(prompt), prompt);
4028
+ } catch (error) {
4029
+ return blocked2("projection-prompt-markers", [prompt], error instanceof Error ? error.message : String(error));
4030
+ }
4031
+ if (input.devtoolsMcpEnabled && (!input.pnpmBin || !path25.isAbsolute(input.pnpmBin))) {
3939
4032
  return blocked2("projection-devtools-command", [handoffPath(scope, "devtools")], "DevTools requiere un ejecutable pnpm absoluto disponible en PATH.");
3940
4033
  }
3941
- if (input.playwrightHandoffEnabled && (!input.playwrightCliCommand || !path24.isAbsolute(input.playwrightCliCommand) || /[\u0000-\u001f\u007f]/.test(input.playwrightCliCommand))) {
4034
+ if (input.playwrightHandoffEnabled && (!input.playwrightCliCommand || !path25.isAbsolute(input.playwrightCliCommand) || /[\u0000-\u001f\u007f]/.test(input.playwrightCliCommand))) {
3942
4035
  return blocked2("projection-playwright-command", [handoffPath(scope, "playwright")], "Playwright requiere un ejecutable absoluto verificado. Abre una terminal nueva tras pnpm setup y reintenta install --playwright.");
3943
4036
  }
3944
4037
  const plan = projectionPlan(input, scope);
@@ -3951,12 +4044,12 @@ function runPiProjectionLifecycle(input, deps) {
3951
4044
  const expectedReceipt2 = receiptContent(receipt);
3952
4045
  const drifted = plan.filter((action) => hasActionDrift(action, deps));
3953
4046
  for (const { kind, file } of checked.handoffs) {
3954
- if (handoffEnabled(input, kind) && checked.previous?.[kind]?.sha256 !== receipt[kind]?.sha256 && !drifted.some((action) => path24.resolve(action.target) === file)) {
4047
+ if (handoffEnabled(input, kind) && checked.previous?.[kind]?.sha256 !== receipt[kind]?.sha256 && !drifted.some((action) => path25.resolve(action.target) === file)) {
3955
4048
  return input.operation === "doctor" ? { kind: "drift", paths: [file] } : handoffConflict(kind, file);
3956
4049
  }
3957
4050
  }
3958
4051
  if (input.operation === "doctor") {
3959
- const paths = drifted.map((action) => path24.resolve(action.target));
4052
+ const paths = drifted.map((action) => path25.resolve(action.target));
3960
4053
  paths.push(...removedHandoffs.map(({ file }) => file), ...retiredSkills);
3961
4054
  const currentSettings2 = deps.readText(scope.settingsFile);
3962
4055
  if (currentSettings2 === null || filterProjectedPiPackage(currentSettings2, input.packageSource) !== currentSettings2) {
@@ -4015,12 +4108,12 @@ function runPiProjectionLifecycle(input, deps) {
4015
4108
  return { kind: "synced", changed: drifted.length > 0 || packageWillChange || receiptChanged || removedHandoffs.length > 0 };
4016
4109
  }
4017
4110
  function systemProjectionLifecycle(input) {
4018
- const targetRoot = input.targetDir === void 0 ? null : path24.resolve(input.targetDir);
4111
+ const targetRoot = input.targetDir === void 0 ? null : path25.resolve(input.targetDir);
4019
4112
  const scope = targetRoot === null ? realPiProjectionScope() : {
4020
4113
  kind: "target-dir",
4021
- home: path24.join(targetRoot, "home"),
4022
- codingAgentDir: path24.join(targetRoot, "pi-agent"),
4023
- receiptFile: path24.join(targetRoot, "state", "pi-projection-receipt.json")
4114
+ home: path25.join(targetRoot, "home"),
4115
+ codingAgentDir: path25.dirname(piSystemPromptFile(targetRoot)),
4116
+ receiptFile: path25.join(targetRoot, "state", "pi-projection-receipt.json")
4024
4117
  };
4025
4118
  return {
4026
4119
  input: {
@@ -4041,14 +4134,14 @@ function systemProjectionLifecycle(input) {
4041
4134
  backup: (paths) => createBackup(
4042
4135
  paths,
4043
4136
  `pi-projection-${input.operation}`,
4044
- targetRoot === null ? void 0 : path24.join(targetRoot, "backups")
4137
+ targetRoot === null ? void 0 : path25.join(targetRoot, "backups")
4045
4138
  ),
4046
4139
  writeText,
4047
4140
  copyFile,
4048
4141
  removeFile: (file) => {
4049
- fs17.rmSync(file, { force: true });
4050
- if (retiredPlaywrightFiles(scope).includes(path24.resolve(file))) {
4051
- pruneEmptyDirs(file, path24.join(scope.home, ".agents", "skills"));
4142
+ fs18.rmSync(file, { force: true });
4143
+ if (retiredPlaywrightFiles(scope).includes(path25.resolve(file))) {
4144
+ pruneEmptyDirs(file, path25.join(scope.home, ".agents", "skills"));
4052
4145
  }
4053
4146
  },
4054
4147
  readManifest: targetRoot === null ? readManifest : () => ({ runtimes: {} })
@@ -4056,6 +4149,8 @@ function systemProjectionLifecycle(input) {
4056
4149
  };
4057
4150
  }
4058
4151
  function preparePiProjectionUninstallSystem(input) {
4152
+ const error = systemPromptPreflight(input.targetDir);
4153
+ if (error) return error;
4059
4154
  const lifecycle = systemProjectionLifecycle(input);
4060
4155
  return preparePiProjectionUninstall(lifecycle.input, lifecycle.deps);
4061
4156
  }
@@ -4064,9 +4159,19 @@ function completePiProjectionUninstallSystem(plan, input) {
4064
4159
  return completePiProjectionUninstall(plan, lifecycle.deps);
4065
4160
  }
4066
4161
  function runPiProjectionLifecycleSystem(input) {
4162
+ const error = systemPromptPreflight(input.targetDir);
4163
+ if (error) return error;
4067
4164
  const lifecycle = systemProjectionLifecycle(input);
4068
4165
  return runPiProjectionLifecycle(lifecycle.input, lifecycle.deps);
4069
4166
  }
4167
+ function systemPromptPreflight(targetDir) {
4168
+ const target = piSystemPromptFile(targetDir);
4169
+ try {
4170
+ assertSystemPromptFile(target, targetDir);
4171
+ } catch (error) {
4172
+ return blocked2("projection-prompt-markers", [target], error instanceof Error ? error.message : String(error));
4173
+ }
4174
+ }
4070
4175
 
4071
4176
  // src/uninstall.ts
4072
4177
  function resolvePlaywrightUninstallPlan(input) {
@@ -4077,6 +4182,18 @@ function resolvePlaywrightUninstallPlan(input) {
4077
4182
  }
4078
4183
  async function runUninstall(opts) {
4079
4184
  p2.intro(`jorgex-stack ${opts.dryRun ? "uninstall (dry-run)" : "uninstall"}`);
4185
+ try {
4186
+ for (const id of opts.runtimes) {
4187
+ const adapter = ADAPTERS[id];
4188
+ if (!adapter) continue;
4189
+ const detection = adapter.detect();
4190
+ if (opts.targetDir === void 0 && !detection.installed) continue;
4191
+ assertSystemPromptFile(adapter.paths(opts.targetDir ?? detection.configDir).systemPromptFile, opts.targetDir);
4192
+ }
4193
+ } catch (error) {
4194
+ p2.log.error(error instanceof Error ? error.message : String(error));
4195
+ return 1;
4196
+ }
4080
4197
  const useBrowserPreferences = opts.targetDir === void 0;
4081
4198
  const preferenceErrors = useBrowserPreferences ? [...browserPreferenceErrors(), primaryModelOwnershipError()].filter((error) => error !== null) : [];
4082
4199
  if (preferenceErrors.length > 0) {
@@ -4127,7 +4244,7 @@ async function runUninstall(opts) {
4127
4244
  if (!detection.installed) continue;
4128
4245
  const keepCtx = makeContext(keep, detection.configDir);
4129
4246
  if (!keepCtx) continue;
4130
- for (const action of buildPlan(keep, keepCtx)) retained.add(path25.resolve(action.target));
4247
+ for (const action of buildPlan(keep, keepCtx)) retained.add(path26.resolve(action.target));
4131
4248
  }
4132
4249
  }
4133
4250
  for (const id of opts.runtimes) {
@@ -4146,15 +4263,15 @@ async function runUninstall(opts) {
4146
4263
  if (!ctx) continue;
4147
4264
  ctx.preserveEngram = !removeEngram;
4148
4265
  const unmerge = adapter.planUnmerge(mcpForUnmerge, hooks, ctx);
4149
- const mergedTargets = new Set(unmerge.map((a) => path25.resolve(a.target)));
4266
+ const mergedTargets = new Set(unmerge.map((a) => path26.resolve(a.target)));
4150
4267
  const usingRealConfig = opts.targetDir === void 0;
4151
4268
  const prevOwned = usingRealConfig ? readManifest().runtimes[id]?.owned ?? [] : [];
4152
- const pruneRoot = usingRealConfig ? HOME : path25.dirname(configDir);
4269
+ const pruneRoot = usingRealConfig ? HOME : path26.dirname(configDir);
4153
4270
  const planTargets = [
4154
- .../* @__PURE__ */ new Set([...buildPlan(adapter, ctx).map((a) => path25.resolve(a.target)), ...prevOwned.map((t) => path25.resolve(t))])
4155
- ].filter((t) => !mergedTargets.has(t) && fs18.existsSync(t));
4271
+ .../* @__PURE__ */ new Set([...buildPlan(adapter, ctx).map((a) => path26.resolve(a.target)), ...prevOwned.map((t) => path26.resolve(t))])
4272
+ ].filter((t) => !mergedTargets.has(t) && fs19.existsSync(t));
4156
4273
  const deleteTargets = planTargets.filter(
4157
- (t) => !retained.has(t) && !(ctx.preserveEngram && path25.basename(t) === "engram.ts") && isContainedIn(t, pruneRoot)
4274
+ (t) => !retained.has(t) && !(ctx.preserveEngram && path26.basename(t) === "engram.ts") && isContainedIn(t, pruneRoot)
4158
4275
  );
4159
4276
  const sharedKept = planTargets.length - deleteTargets.length;
4160
4277
  p2.log.step(`${adapter.name} \u2192 ${configDir}`);
@@ -4164,7 +4281,7 @@ async function runUninstall(opts) {
4164
4281
  let backup;
4165
4282
  try {
4166
4283
  backup = createBackup(
4167
- [...deleteTargets, ...unmerge.map((a) => a.target).filter((t) => fs18.existsSync(t))],
4284
+ [...deleteTargets, ...unmerge.map((a) => a.target).filter((t) => fs19.existsSync(t))],
4168
4285
  `uninstall-${id}`
4169
4286
  );
4170
4287
  } catch (error) {
@@ -4174,13 +4291,13 @@ async function runUninstall(opts) {
4174
4291
  }
4175
4292
  if (backup) p2.log.info(`Backup: ${backup.id} (${backup.files.length} archivos)`);
4176
4293
  for (const target of deleteTargets) {
4177
- fs18.rmSync(target, { force: true });
4294
+ fs19.rmSync(target, { force: true });
4178
4295
  pruneEmptyDirs(target, pruneRoot);
4179
4296
  }
4180
4297
  for (const action of unmerge) {
4181
4298
  if (action.kind !== "write") continue;
4182
4299
  if (action.content.trim() === "") {
4183
- fs18.rmSync(action.target, { force: true });
4300
+ fs19.rmSync(action.target, { force: true });
4184
4301
  } else {
4185
4302
  writeText(action.target, action.content);
4186
4303
  }
@@ -4234,12 +4351,12 @@ async function runUninstall(opts) {
4234
4351
  }
4235
4352
 
4236
4353
  // src/doctor.ts
4237
- import path26 from "path";
4238
- import fs19 from "fs";
4354
+ import path27 from "path";
4355
+ import fs20 from "fs";
4239
4356
  import * as p3 from "@clack/prompts";
4240
4357
  function readDoctorTextIfExists(file) {
4241
4358
  try {
4242
- return fs19.readFileSync(file, "utf8");
4359
+ return fs20.readFileSync(file, "utf8");
4243
4360
  } catch (error) {
4244
4361
  if (error.code === "ENOENT") return null;
4245
4362
  throw error;
@@ -4263,7 +4380,7 @@ function resolvePlaywrightDoctorState(input) {
4263
4380
  return { status: "healthy" };
4264
4381
  }
4265
4382
  function context7KeyConfigured(id, configDir) {
4266
- const file = id === "codex" ? path26.join(configDir, "config.toml") : id === "claude-code" ? path26.join(path26.dirname(configDir), `${path26.basename(configDir)}.json`) : path26.join(configDir, "opencode.json");
4383
+ const file = id === "codex" ? path27.join(configDir, "config.toml") : id === "claude-code" ? path27.join(path27.dirname(configDir), `${path27.basename(configDir)}.json`) : path27.join(configDir, "opencode.json");
4267
4384
  const content = readTextIfExists(file);
4268
4385
  if (content === null) return null;
4269
4386
  const match = /CONTEXT7_API_KEY"?\s*[:=]\s*"([^"]*)"/.exec(content);
@@ -4283,7 +4400,7 @@ function reportWritingStyle(options, style, mode) {
4283
4400
  for (const id of runtimes) {
4284
4401
  const adapter = id === "pi" ? piAdapter : ADAPTERS[id];
4285
4402
  if (adapter === void 0) continue;
4286
- const configDir = id === "pi" ? options.targetDir === void 0 ? process.env.PI_CODING_AGENT_DIR ?? path26.join(HOME, ".pi", "agent") : path26.join(options.targetDir, "pi-agent") : options.targetDir ?? ADAPTERS[id].detect().configDir;
4403
+ const configDir = id === "pi" ? options.targetDir === void 0 ? process.env.PI_CODING_AGENT_DIR ?? path27.join(HOME, ".pi", "agent") : path27.join(options.targetDir, "pi-agent") : options.targetDir ?? ADAPTERS[id].detect().configDir;
4287
4404
  const file = adapter.paths(configDir).systemPromptFile;
4288
4405
  try {
4289
4406
  const content = readDoctorTextIfExists(file) ?? "";
@@ -4298,7 +4415,7 @@ function reportWritingStyle(options, style, mode) {
4298
4415
  problems++;
4299
4416
  }
4300
4417
  if (id === "codex") {
4301
- const override = path26.join(configDir, "AGENTS.override.md");
4418
+ const override = path27.join(configDir, "AGENTS.override.md");
4302
4419
  if ((readDoctorTextIfExists(override) ?? "").trim() !== "") {
4303
4420
  p3.log.warn(`Codex: ${override} no vac\xEDo puede ocultar el AGENTS.md gestionado; rev\xEDsalo sin modificarlo autom\xE1ticamente.`);
4304
4421
  problems++;
@@ -4341,13 +4458,13 @@ async function runDoctor(options = {}) {
4341
4458
  p3.log.success(`Engram: ${version} (${engramBin})`);
4342
4459
  }
4343
4460
  }
4344
- const engramDataDir = process.env.ENGRAM_DATA_DIR ?? path26.join(HOME, ".engram");
4345
- const engramDb = path26.join(engramDataDir, "engram.db");
4346
- if (fs19.existsSync(engramDb)) {
4347
- const sizeMb = (fs19.statSync(engramDb).size / 1024 / 1024).toFixed(1);
4461
+ const engramDataDir = process.env.ENGRAM_DATA_DIR ?? path27.join(HOME, ".engram");
4462
+ const engramDb = path27.join(engramDataDir, "engram.db");
4463
+ if (fs20.existsSync(engramDb)) {
4464
+ const sizeMb = (fs20.statSync(engramDb).size / 1024 / 1024).toFixed(1);
4348
4465
  p3.log.info(`Engram DB: ${engramDb} (${sizeMb} MB de memorias \u2014 el stack no la toca JAM\xC1S).`);
4349
4466
  }
4350
- if (!fs19.existsSync(modelMapFile())) p3.log.info("model-map: a\xFAn no creado (se crea en el primer install o con 'models').");
4467
+ if (!fs20.existsSync(modelMapFile())) p3.log.info("model-map: a\xFAn no creado (se crea en el primer install o con 'models').");
4351
4468
  const preferenceErrors = browserPreferenceErrors();
4352
4469
  const primaryOwnershipError = primaryModelOwnershipError();
4353
4470
  if (primaryOwnershipError !== null) {
@@ -4429,7 +4546,7 @@ async function runDoctor(options = {}) {
4429
4546
  p3.log.warn(`${adapter.name}: ${orphans.length} archivos hu\xE9rfanos de versiones previas \u2192 ejecuta 'sync'.`);
4430
4547
  problems++;
4431
4548
  }
4432
- if (adapter.id === "codex" && fs19.existsSync(path26.join(detection.configDir, "hooks.json"))) {
4549
+ if (adapter.id === "codex" && fs20.existsSync(path27.join(detection.configDir, "hooks.json"))) {
4433
4550
  p3.log.info("Codex: recuerda que los hooks requieren aprobaci\xF3n manual \u2014 verifica con /hooks dentro de codex.");
4434
4551
  }
4435
4552
  const key = context7KeyConfigured(adapter.id, detection.configDir);
@@ -4440,15 +4557,15 @@ async function runDoctor(options = {}) {
4440
4557
  }
4441
4558
 
4442
4559
  // src/update.ts
4443
- import fs22 from "fs";
4444
- import path29 from "path";
4560
+ import fs23 from "fs";
4561
+ import path30 from "path";
4445
4562
  import os3 from "os";
4446
4563
  import { execFileSync as execFileSync5 } from "child_process";
4447
4564
  import * as p4 from "@clack/prompts";
4448
4565
 
4449
4566
  // src/lib/github.ts
4450
- import fs20 from "fs";
4451
- import path27 from "path";
4567
+ import fs21 from "fs";
4568
+ import path28 from "path";
4452
4569
  import { execFileSync as execFileSync3 } from "child_process";
4453
4570
  import os2 from "os";
4454
4571
  import { Readable } from "stream";
@@ -4518,19 +4635,19 @@ async function latestGithubCommit(repo) {
4518
4635
  }
4519
4636
  }
4520
4637
  function validateExtractedTree(destDir) {
4521
- const resolved = path27.resolve(destDir);
4638
+ const resolved = path28.resolve(destDir);
4522
4639
  const walk = (dir) => {
4523
4640
  let entries;
4524
4641
  try {
4525
- entries = fs20.readdirSync(dir, { withFileTypes: true });
4642
+ entries = fs21.readdirSync(dir, { withFileTypes: true });
4526
4643
  } catch {
4527
4644
  return false;
4528
4645
  }
4529
4646
  for (const entry of entries) {
4530
- const full = path27.join(dir, entry.name);
4647
+ const full = path28.join(dir, entry.name);
4531
4648
  let stat;
4532
4649
  try {
4533
- stat = fs20.lstatSync(full);
4650
+ stat = fs21.lstatSync(full);
4534
4651
  } catch {
4535
4652
  return false;
4536
4653
  }
@@ -4546,15 +4663,15 @@ function validateExtractedTree(destDir) {
4546
4663
  }
4547
4664
  function resolveTarBin() {
4548
4665
  if (process.platform !== "win32") return "tar";
4549
- const winTar = path27.join(process.env["SystemRoot"] ?? "C:\\Windows", "System32", "tar.exe");
4550
- return fs20.existsSync(winTar) ? winTar : "tar";
4666
+ const winTar = path28.join(process.env["SystemRoot"] ?? "C:\\Windows", "System32", "tar.exe");
4667
+ return fs21.existsSync(winTar) ? winTar : "tar";
4551
4668
  }
4552
4669
  async function downloadRepoTarball(repo, sha, destDir, validateSubdir) {
4553
4670
  const url = `https://codeload.github.com/${repo}/tar.gz/${sha}`;
4554
- const tmp = path27.join(os2.tmpdir(), `jorgex-tarball-${Date.now()}.tar.gz`);
4671
+ const tmp = path28.join(os2.tmpdir(), `jorgex-tarball-${Date.now()}.tar.gz`);
4555
4672
  const fail = (reason) => {
4556
4673
  try {
4557
- fs20.rmSync(destDir, { recursive: true, force: true });
4674
+ fs21.rmSync(destDir, { recursive: true, force: true });
4558
4675
  } catch {
4559
4676
  }
4560
4677
  return { ok: false, reason };
@@ -4573,10 +4690,10 @@ async function downloadRepoTarball(repo, sha, destDir, validateSubdir) {
4573
4690
  if (!res.body) return fail("respuesta HTTP sin cuerpo");
4574
4691
  await pipeline(
4575
4692
  Readable.fromWeb(res.body),
4576
- fs20.createWriteStream(tmp)
4693
+ fs21.createWriteStream(tmp)
4577
4694
  );
4578
- fs20.rmSync(destDir, { recursive: true, force: true });
4579
- fs20.mkdirSync(destDir, { recursive: true });
4695
+ fs21.rmSync(destDir, { recursive: true, force: true });
4696
+ fs21.mkdirSync(destDir, { recursive: true });
4580
4697
  try {
4581
4698
  execFileSync3(resolveTarBin(), ["-xzf", tmp, "--strip-components=1", "-C", destDir], { stdio: "pipe" });
4582
4699
  } catch (err) {
@@ -4584,30 +4701,30 @@ async function downloadRepoTarball(repo, sha, destDir, validateSubdir) {
4584
4701
  const detail = (e.stderr?.toString().trim() || e.message || "").split("\n")[0];
4585
4702
  return fail(detail ? `tar fall\xF3: ${detail}` : "tar no disponible o fall\xF3 la extracci\xF3n");
4586
4703
  }
4587
- const resolvedDest = path27.resolve(destDir);
4588
- const validateRoot = validateSubdir ? path27.resolve(resolvedDest, validateSubdir) : resolvedDest;
4704
+ const resolvedDest = path28.resolve(destDir);
4705
+ const validateRoot = validateSubdir ? path28.resolve(resolvedDest, validateSubdir) : resolvedDest;
4589
4706
  if (validateRoot !== resolvedDest && !isContainedIn(validateRoot, resolvedDest)) {
4590
4707
  return fail(`la ruta de validaci\xF3n "${validateSubdir}" escapa del destino`);
4591
4708
  }
4592
- if (fs20.existsSync(validateRoot) && !validateExtractedTree(validateRoot)) {
4709
+ if (fs21.existsSync(validateRoot) && !validateExtractedTree(validateRoot)) {
4593
4710
  return fail("el \xE1rbol extra\xEDdo contiene symlinks o rutas fuera del destino");
4594
4711
  }
4595
- const validated = fs20.existsSync(validateRoot);
4712
+ const validated = fs21.existsSync(validateRoot);
4596
4713
  return { ok: true, validated };
4597
4714
  } catch (err) {
4598
4715
  const timedOut = err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError");
4599
4716
  return fail(timedOut ? "timeout de descarga (120s)" : err instanceof Error ? `fallo de red: ${err.message}` : "error desconocido");
4600
4717
  } finally {
4601
4718
  try {
4602
- fs20.rmSync(tmp, { force: true });
4719
+ fs21.rmSync(tmp, { force: true });
4603
4720
  } catch {
4604
4721
  }
4605
4722
  }
4606
4723
  }
4607
4724
 
4608
4725
  // src/lib/skill-update.ts
4609
- import fs21 from "fs";
4610
- import path28 from "path";
4726
+ import fs22 from "fs";
4727
+ import path29 from "path";
4611
4728
  import { execFileSync as execFileSync4 } from "child_process";
4612
4729
  var PROTECTED_SKILLS = /* @__PURE__ */ new Set([
4613
4730
  "agent-delegation",
@@ -4618,8 +4735,8 @@ var PROTECTED_SKILLS = /* @__PURE__ */ new Set([
4618
4735
  "xreview"
4619
4736
  ]);
4620
4737
  function sameTextContentNormalized(a, b) {
4621
- const ba = fs21.readFileSync(a);
4622
- const bb = fs21.readFileSync(b);
4738
+ const ba = fs22.readFileSync(a);
4739
+ const bb = fs22.readFileSync(b);
4623
4740
  if (ba.equals(bb)) return true;
4624
4741
  const sa = ba.toString("utf8").replace(/\r\n/g, "\n");
4625
4742
  const sb = bb.toString("utf8").replace(/\r\n/g, "\n");
@@ -4627,10 +4744,10 @@ function sameTextContentNormalized(a, b) {
4627
4744
  }
4628
4745
  function diffSkillDirs(upstreamDir, localDir) {
4629
4746
  const upstreamFiles = new Set(
4630
- listFilesRecursive(upstreamDir).map((f) => path28.relative(upstreamDir, f))
4747
+ listFilesRecursive(upstreamDir).map((f) => path29.relative(upstreamDir, f))
4631
4748
  );
4632
4749
  const localFiles = new Set(
4633
- listFilesRecursive(localDir).map((f) => path28.relative(localDir, f))
4750
+ listFilesRecursive(localDir).map((f) => path29.relative(localDir, f))
4634
4751
  );
4635
4752
  const added = [];
4636
4753
  const modified = [];
@@ -4638,7 +4755,7 @@ function diffSkillDirs(upstreamDir, localDir) {
4638
4755
  for (const rel of upstreamFiles) {
4639
4756
  if (!localFiles.has(rel)) {
4640
4757
  added.push(rel);
4641
- } else if (!sameTextContentNormalized(path28.join(upstreamDir, rel), path28.join(localDir, rel))) {
4758
+ } else if (!sameTextContentNormalized(path29.join(upstreamDir, rel), path29.join(localDir, rel))) {
4642
4759
  modified.push(rel);
4643
4760
  }
4644
4761
  }
@@ -4700,8 +4817,8 @@ function replaceSkill(name, upstreamSkillDir, newCommit, opts) {
4700
4817
  if (PROTECTED_SKILLS.has(name)) {
4701
4818
  throw new Error(`La skill "${name}" es propia del stack y no se actualiza desde upstream.`);
4702
4819
  }
4703
- const upstreamsFile = upstreamsFilePath ?? path28.join(path28.dirname(stackRoot()), "upstreams.json");
4704
- const raw = fs21.readFileSync(upstreamsFile, "utf8");
4820
+ const upstreamsFile = upstreamsFilePath ?? path29.join(path29.dirname(stackRoot()), "upstreams.json");
4821
+ const raw = fs22.readFileSync(upstreamsFile, "utf8");
4705
4822
  const data = JSON.parse(raw);
4706
4823
  const skillEntry = data?.skills?.[name];
4707
4824
  if (!skillEntry) {
@@ -4710,8 +4827,8 @@ function replaceSkill(name, upstreamSkillDir, newCommit, opts) {
4710
4827
  if (skillEntry.kind === "release") {
4711
4828
  throw new Error(`La skill "${name}" es de tipo release y no se actualiza con replaceSkill.`);
4712
4829
  }
4713
- const skillsRoot = localSkillsRoot ?? path28.join(stackRoot(), "skills");
4714
- const localSkillDir = path28.join(skillsRoot, name);
4830
+ const skillsRoot = localSkillsRoot ?? path29.join(stackRoot(), "skills");
4831
+ const localSkillDir = path29.join(skillsRoot, name);
4715
4832
  const localFiles = listFilesRecursive(localSkillDir);
4716
4833
  if (localFiles.length > 0) {
4717
4834
  createBackup(localFiles, `skill-update-${name}`, backupsRoot2);
@@ -4720,24 +4837,24 @@ function replaceSkill(name, upstreamSkillDir, newCommit, opts) {
4720
4837
  try {
4721
4838
  const upstreamFiles = listFilesRecursive(upstreamSkillDir);
4722
4839
  for (const src of upstreamFiles) {
4723
- const st = fs21.lstatSync(src);
4840
+ const st = fs22.lstatSync(src);
4724
4841
  if (st.isSymbolicLink()) {
4725
4842
  throw new Error(`Symlink rechazado en upstream de skill "${name}": ${src}`);
4726
4843
  }
4727
- const rel = path28.relative(upstreamSkillDir, src);
4728
- const dest = path28.join(stagingDir, rel);
4729
- ensureDir(path28.dirname(dest));
4844
+ const rel = path29.relative(upstreamSkillDir, src);
4845
+ const dest = path29.join(stagingDir, rel);
4846
+ ensureDir(path29.dirname(dest));
4730
4847
  copyFile(src, dest);
4731
4848
  }
4732
4849
  const oldDir = `${localSkillDir}.old-${process.pid}`;
4733
- if (fs21.existsSync(localSkillDir)) {
4734
- fs21.renameSync(localSkillDir, oldDir);
4850
+ if (fs22.existsSync(localSkillDir)) {
4851
+ fs22.renameSync(localSkillDir, oldDir);
4735
4852
  }
4736
- fs21.renameSync(stagingDir, localSkillDir);
4737
- fs21.rmSync(oldDir, { recursive: true, force: true });
4853
+ fs22.renameSync(stagingDir, localSkillDir);
4854
+ fs22.rmSync(oldDir, { recursive: true, force: true });
4738
4855
  } catch (err) {
4739
4856
  try {
4740
- fs21.rmSync(stagingDir, { recursive: true, force: true });
4857
+ fs22.rmSync(stagingDir, { recursive: true, force: true });
4741
4858
  } catch {
4742
4859
  }
4743
4860
  throw err;
@@ -4751,8 +4868,8 @@ function rateLimitHint(prefix) {
4751
4868
  return ghPresentButTokenFailed() ? `${prefix} Tienes gh instalado pero \`gh auth token\` no devolvi\xF3 credencial (\xBFsesi\xF3n caducada?) \u2014 prueba \`gh auth login\` o define GH_TOKEN.` : `${prefix} Define GH_TOKEN o inicia sesi\xF3n en gh CLI.`;
4752
4869
  }
4753
4870
  function loadUpstreams() {
4754
- const file = path29.join(path29.dirname(stackRoot()), "upstreams.json");
4755
- return JSON.parse(fs22.readFileSync(file, "utf8"));
4871
+ const file = path30.join(path30.dirname(stackRoot()), "upstreams.json");
4872
+ return JSON.parse(fs23.readFileSync(file, "utf8"));
4756
4873
  }
4757
4874
  function skillsToScan(maintainer, upstreams) {
4758
4875
  return maintainer ? Object.keys(upstreams.skills) : [];
@@ -4901,8 +5018,8 @@ function isEngramRunning() {
4901
5018
  return null;
4902
5019
  }
4903
5020
  }
4904
- function isGitClone(projectRoot = path29.dirname(stackRoot())) {
4905
- return fs22.existsSync(path29.join(projectRoot, ".git"));
5021
+ function isGitClone(projectRoot = path30.dirname(stackRoot())) {
5022
+ return fs23.existsSync(path30.join(projectRoot, ".git"));
4906
5023
  }
4907
5024
  var STACK_METHOD_CLONE = "git pull + pnpm install + pnpm build";
4908
5025
  function resolvePnpm() {
@@ -4912,12 +5029,12 @@ function resolvePnpm() {
4912
5029
  }
4913
5030
  function cleanupTmp(dir) {
4914
5031
  try {
4915
- fs22.rmSync(dir, { recursive: true, force: true });
5032
+ fs23.rmSync(dir, { recursive: true, force: true });
4916
5033
  } catch {
4917
5034
  }
4918
5035
  }
4919
5036
  function updateStackGitClone() {
4920
- const projectRoot = path29.dirname(stackRoot());
5037
+ const projectRoot = path30.dirname(stackRoot());
4921
5038
  const git = lookPath("git");
4922
5039
  if (!git) throw new Error("git no encontrado en PATH.");
4923
5040
  const pnpm = resolvePnpm();
@@ -4934,7 +5051,7 @@ function updateStackGlobal() {
4934
5051
  execFileSync5(pnpm, ["add", "-g", "jorgex-stack@latest"], { stdio: "inherit" });
4935
5052
  }
4936
5053
  async function downloadSkillToTemp(repo, sha, skillPath) {
4937
- const root = fs22.mkdtempSync(path29.join(os3.tmpdir(), "jorgex-skill-"));
5054
+ const root = fs23.mkdtempSync(path30.join(os3.tmpdir(), "jorgex-skill-"));
4938
5055
  try {
4939
5056
  const result = await downloadRepoTarball(repo, sha, root, skillPath);
4940
5057
  if (!result.ok) {
@@ -4942,15 +5059,15 @@ async function downloadSkillToTemp(repo, sha, skillPath) {
4942
5059
  return { error: result.reason };
4943
5060
  }
4944
5061
  if (skillPath) {
4945
- const sub = path29.resolve(path29.join(root, skillPath));
5062
+ const sub = path30.resolve(path30.join(root, skillPath));
4946
5063
  if (!isContainedIn(sub, root)) {
4947
5064
  cleanupTmp(root);
4948
5065
  return { error: `la ruta "${skillPath}" escapa del directorio temporal` };
4949
5066
  }
4950
- if (fs22.existsSync(sub)) return { dir: sub, root };
5067
+ if (fs23.existsSync(sub)) return { dir: sub, root };
4951
5068
  const lastSeg = skillPath.split("/").pop();
4952
- const sub2 = path29.resolve(path29.join(root, lastSeg));
4953
- if (isContainedIn(sub2, root) && fs22.existsSync(sub2)) {
5069
+ const sub2 = path30.resolve(path30.join(root, lastSeg));
5070
+ if (isContainedIn(sub2, root) && fs23.existsSync(sub2)) {
4954
5071
  if (!validateExtractedTree(sub2)) {
4955
5072
  cleanupTmp(root);
4956
5073
  return { error: `el sub\xE1rbol "${lastSeg}" contiene symlinks o rutas fuera del destino` };
@@ -4969,11 +5086,11 @@ async function downloadSkillToTemp(repo, sha, skillPath) {
4969
5086
  function pruneEngramDbBackups() {
4970
5087
  try {
4971
5088
  const dir = dataDir();
4972
- if (!fs22.existsSync(dir)) return;
4973
- const backups = fs22.readdirSync(dir).filter((f) => f.startsWith("engram-db-backup-") && f.endsWith(".db")).map((f) => ({ name: f, mtime: fs22.statSync(path29.join(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
5089
+ if (!fs23.existsSync(dir)) return;
5090
+ const backups = fs23.readdirSync(dir).filter((f) => f.startsWith("engram-db-backup-") && f.endsWith(".db")).map((f) => ({ name: f, mtime: fs23.statSync(path30.join(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
4974
5091
  for (const old of backups.slice(3)) {
4975
5092
  try {
4976
- fs22.rmSync(path29.join(dir, old.name));
5093
+ fs23.rmSync(path30.join(dir, old.name));
4977
5094
  } catch {
4978
5095
  }
4979
5096
  }
@@ -4981,18 +5098,18 @@ function pruneEngramDbBackups() {
4981
5098
  }
4982
5099
  }
4983
5100
  function rotateLockedBinary(binPath, sweepRoot = HOME) {
4984
- if (!fs22.existsSync(binPath)) return null;
4985
- const dir = path29.dirname(binPath);
4986
- const base = path29.basename(binPath);
5101
+ if (!fs23.existsSync(binPath)) return null;
5102
+ const dir = path30.dirname(binPath);
5103
+ const base = path30.basename(binPath);
4987
5104
  const escapedBase = base.replace(/[.*+?^$()|[\]{}\\]/g, "\\$&");
4988
5105
  const oldPattern = new RegExp("^" + escapedBase + "\\.old-\\d+$");
4989
- const resolvedDir = path29.resolve(dir);
4990
- if (resolvedDir === path29.resolve(sweepRoot) || isContainedIn(resolvedDir, sweepRoot)) {
5106
+ const resolvedDir = path30.resolve(dir);
5107
+ if (resolvedDir === path30.resolve(sweepRoot) || isContainedIn(resolvedDir, sweepRoot)) {
4991
5108
  try {
4992
- for (const entry of fs22.readdirSync(dir)) {
5109
+ for (const entry of fs23.readdirSync(dir)) {
4993
5110
  if (oldPattern.test(entry)) {
4994
5111
  try {
4995
- fs22.rmSync(path29.join(dir, entry), { force: true });
5112
+ fs23.rmSync(path30.join(dir, entry), { force: true });
4996
5113
  } catch {
4997
5114
  }
4998
5115
  }
@@ -5000,8 +5117,8 @@ function rotateLockedBinary(binPath, sweepRoot = HOME) {
5000
5117
  } catch {
5001
5118
  }
5002
5119
  }
5003
- const rotated = path29.join(dir, `${base}.old-${Date.now()}`);
5004
- fs22.renameSync(binPath, rotated);
5120
+ const rotated = path30.join(dir, `${base}.old-${Date.now()}`);
5121
+ fs23.renameSync(binPath, rotated);
5005
5122
  return rotated;
5006
5123
  }
5007
5124
  async function updateEngram(engramRepo, latestVersion) {
@@ -5010,19 +5127,19 @@ async function updateEngram(engramRepo, latestVersion) {
5010
5127
  "Engram est\xE1 en ejecuci\xF3n: los procesos vivos seguir\xE1n usando la versi\xF3n antigua hasta que reinicies los clientes (Claude Code/OpenCode/Codex)."
5011
5128
  );
5012
5129
  }
5013
- const engramDataDir = process.env.ENGRAM_DATA_DIR ?? path29.join(HOME, ".engram");
5014
- const engramDb = path29.join(engramDataDir, "engram.db");
5015
- if (fs22.existsSync(engramDb)) {
5130
+ const engramDataDir = process.env.ENGRAM_DATA_DIR ?? path30.join(HOME, ".engram");
5131
+ const engramDb = path30.join(engramDataDir, "engram.db");
5132
+ if (fs23.existsSync(engramDb)) {
5016
5133
  const doBackup = await p4.confirm({
5017
5134
  message: `\xBFHacer backup de la DB de Engram antes de actualizar? (${engramDb})`,
5018
5135
  initialValue: true
5019
5136
  });
5020
5137
  if (!p4.isCancel(doBackup) && doBackup) {
5021
5138
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
5022
- const dest = path29.join(dataDir(), `engram-db-backup-${ts}.db`);
5139
+ const dest = path30.join(dataDir(), `engram-db-backup-${ts}.db`);
5023
5140
  try {
5024
- if (!fs22.existsSync(dataDir())) fs22.mkdirSync(dataDir(), { recursive: true });
5025
- fs22.copyFileSync(engramDb, dest);
5141
+ if (!fs23.existsSync(dataDir())) fs23.mkdirSync(dataDir(), { recursive: true });
5142
+ fs23.copyFileSync(engramDb, dest);
5026
5143
  p4.log.success(`DB respaldada en ${dest} (la DB original NO se modifica jam\xE1s).`);
5027
5144
  pruneEngramDbBackups();
5028
5145
  } catch (err) {
@@ -5072,10 +5189,10 @@ async function updateEngram(engramRepo, latestVersion) {
5072
5189
  ["install", `github.com/Gentleman-Programming/engram/cmd/engram@v${latestVersion}`],
5073
5190
  { stdio: "inherit" }
5074
5191
  );
5075
- const rollbackOk = resolveEngramRollback({ installOk: true, rotated, bin, binExists: fs22.existsSync(bin ?? "") });
5192
+ const rollbackOk = resolveEngramRollback({ installOk: true, rotated, bin, binExists: fs23.existsSync(bin ?? "") });
5076
5193
  if (rollbackOk.action === "restore") {
5077
5194
  try {
5078
- fs22.renameSync(rotated, bin);
5195
+ fs23.renameSync(rotated, bin);
5079
5196
  p4.log.warn(rollbackOk.messages.onRestore);
5080
5197
  } catch {
5081
5198
  p4.log.warn(rollbackOk.messages.onRenameFail);
@@ -5083,10 +5200,10 @@ async function updateEngram(engramRepo, latestVersion) {
5083
5200
  }
5084
5201
  return true;
5085
5202
  } catch (err) {
5086
- const rollbackFail = resolveEngramRollback({ installOk: false, rotated, bin, binExists: fs22.existsSync(bin ?? "") });
5203
+ const rollbackFail = resolveEngramRollback({ installOk: false, rotated, bin, binExists: fs23.existsSync(bin ?? "") });
5087
5204
  if (rollbackFail.action === "restore") {
5088
5205
  try {
5089
- fs22.renameSync(rotated, bin);
5206
+ fs23.renameSync(rotated, bin);
5090
5207
  p4.log.info(rollbackFail.messages.onRestore);
5091
5208
  } catch {
5092
5209
  p4.log.error(rollbackFail.messages.onRenameFail);
@@ -5356,7 +5473,7 @@ async function runInteractiveUpdate(localVersion, yes, dryRun = false, includeBr
5356
5473
  continue;
5357
5474
  }
5358
5475
  const { dir: tmpDir, root: tmpRoot } = tmpResult;
5359
- const localSkillDir = path29.join(stackRoot(), "skills", skillInfo.name);
5476
+ const localSkillDir = path30.join(stackRoot(), "skills", skillInfo.name);
5360
5477
  const diff = renderSkillDiff(tmpDir, localSkillDir);
5361
5478
  if (diff) {
5362
5479
  p4.log.info(`Diff de ${skillInfo.name}:
@@ -5442,7 +5559,7 @@ ${diff}`);
5442
5559
  }
5443
5560
 
5444
5561
  // src/models-picker.ts
5445
- import path30 from "path";
5562
+ import path31 from "path";
5446
5563
  import * as p5 from "@clack/prompts";
5447
5564
  var TIERS = ["strong", "standard", "cheap"];
5448
5565
  var EFFORTS = ["low", "medium", "high", "xhigh"];
@@ -5468,7 +5585,7 @@ function opencodeLiveModels(binPath) {
5468
5585
  }
5469
5586
  function agentsByTier() {
5470
5587
  const grouped = { strong: [], standard: [], cheap: [] };
5471
- for (const agent of loadCanonicalAgents(path30.join(stackRoot(), "agents"))) {
5588
+ for (const agent of loadCanonicalAgents(path31.join(stackRoot(), "agents"))) {
5472
5589
  if (agent.mode === "subagent") grouped[agent.tier].push(agent.name);
5473
5590
  }
5474
5591
  return grouped;
@@ -5653,16 +5770,16 @@ function cancelled() {
5653
5770
  }
5654
5771
 
5655
5772
  // src/lib/release.ts
5656
- import fs23 from "fs";
5657
- import path31 from "path";
5773
+ import fs24 from "fs";
5774
+ import path32 from "path";
5658
5775
  import { execFileSync as execFileSync6 } from "child_process";
5659
5776
  import { fileURLToPath } from "url";
5660
5777
  function findPackageJson() {
5661
- let dir = path31.dirname(fileURLToPath(import.meta.url));
5778
+ let dir = path32.dirname(fileURLToPath(import.meta.url));
5662
5779
  for (let i = 0; i < 6; i++) {
5663
- const candidate = path31.join(dir, "package.json");
5664
- if (fs23.existsSync(candidate)) return candidate;
5665
- dir = path31.dirname(dir);
5780
+ const candidate = path32.join(dir, "package.json");
5781
+ if (fs24.existsSync(candidate)) return candidate;
5782
+ dir = path32.dirname(dir);
5666
5783
  }
5667
5784
  throw new Error("No se encontr\xF3 package.json cerca del CLI.");
5668
5785
  }
@@ -5671,7 +5788,7 @@ function readPackageVersion() {
5671
5788
  }
5672
5789
  function readPackageMetadata() {
5673
5790
  const packageJson = findPackageJson();
5674
- const raw = fs23.readFileSync(packageJson, "utf8");
5791
+ const raw = fs24.readFileSync(packageJson, "utf8");
5675
5792
  const parsed = JSON.parse(raw);
5676
5793
  const name = typeof parsed.name === "string" ? parsed.name.trim() : "";
5677
5794
  const version = typeof parsed.version === "string" ? parsed.version.trim() : "";
@@ -5683,8 +5800,8 @@ function readPackageMetadata() {
5683
5800
 
5684
5801
  // src/lib/pi-runtime.ts
5685
5802
  import os4 from "os";
5686
- import path32 from "path";
5687
- import fs24 from "fs";
5803
+ import path33 from "path";
5804
+ import fs25 from "fs";
5688
5805
  import { spawnSync } from "child_process";
5689
5806
  import { createHash as createHash2 } from "crypto";
5690
5807
 
@@ -5851,14 +5968,14 @@ function healthyDoctor(stdout, stderr, packageRunner, candidate) {
5851
5968
  if (record === null || typeof record !== "object" || Array.isArray(record)) return false;
5852
5969
  const packageValue = Reflect.get(record, "package");
5853
5970
  const result = Reflect.get(record, "result");
5854
- return Reflect.get(record, "schemaVersion") === 1 && Reflect.get(record, "command") === "doctor" && Reflect.get(record, "ok") === true && packageValue !== null && typeof packageValue === "object" && Reflect.get(packageValue, "name") === "jorgex-pi" && Reflect.get(packageValue, "version") === (candidate.package?.version ?? /^npm:jorgex-pi@([^\s]+)$/.exec(candidate.source)?.[1]) && path32.resolve(packageRunner) === path32.resolve(String(Reflect.get(packageValue, "root")), "bin", "jorgex-pi.mjs") && result !== null && typeof result === "object" && Reflect.get(result, "healthy") === true;
5971
+ return Reflect.get(record, "schemaVersion") === 1 && Reflect.get(record, "command") === "doctor" && Reflect.get(record, "ok") === true && packageValue !== null && typeof packageValue === "object" && Reflect.get(packageValue, "name") === "jorgex-pi" && Reflect.get(packageValue, "version") === (candidate.package?.version ?? /^npm:jorgex-pi@([^\s]+)$/.exec(candidate.source)?.[1]) && path33.resolve(packageRunner) === path33.resolve(String(Reflect.get(packageValue, "root")), "bin", "jorgex-pi.mjs") && result !== null && typeof result === "object" && Reflect.get(result, "healthy") === true;
5855
5972
  } catch {
5856
5973
  return false;
5857
5974
  }
5858
5975
  }
5859
5976
  function installPiFromVerifiedTarball(input, deps) {
5860
5977
  const paths = input.targetDir === void 0 ? userPaths(input.engramBin, input.piExecutable) : targetPaths(input.targetDir, input.engramBin, input.piExecutable);
5861
- const destination = input.targetDir === void 0 ? path32.join(dataDir(), "packages", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`) : path32.join(path32.resolve(input.targetDir), "downloads", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`);
5978
+ const destination = input.targetDir === void 0 ? path33.join(dataDir(), "packages", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`) : path33.join(path33.resolve(input.targetDir), "downloads", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`);
5862
5979
  const artifact = deps.download(destination);
5863
5980
  if (artifact.bytes !== input.candidate.bytes || artifact.sha256 !== input.candidate.sha256 || artifact.sha512 !== input.candidate.sha512) {
5864
5981
  return { kind: "blocked", reason: "tarball-integrity" };
@@ -5866,7 +5983,7 @@ function installPiFromVerifiedTarball(input, deps) {
5866
5983
  deps.backupSettings();
5867
5984
  const scope = {
5868
5985
  kind: input.targetDir === void 0 ? "real" : "target-dir",
5869
- codingAgentDir: path32.resolve(paths.codingAgentDir)
5986
+ codingAgentDir: path33.resolve(paths.codingAgentDir)
5870
5987
  };
5871
5988
  const installing = flatCandidateReceipt(input.candidate, scope, "installing", input.engramBin);
5872
5989
  deps.writeReceiptAtomic(`${JSON.stringify(installing)}
@@ -5895,30 +6012,30 @@ function installPiFromVerifiedTarball(input, deps) {
5895
6012
  return { kind: "installed", receipt };
5896
6013
  }
5897
6014
  function runtimePath(piExecutable) {
5898
- const entries = process.platform === "win32" ? [piExecutable === void 0 ? null : path32.dirname(piExecutable), path32.dirname(process.execPath), process.env.SystemRoot ? path32.join(process.env.SystemRoot, "System32") : null] : [piExecutable === void 0 ? null : path32.dirname(piExecutable), path32.dirname(process.execPath), "/usr/local/bin", "/usr/bin", "/bin"];
5899
- return [...new Set(entries.filter((entry) => entry !== null))].join(path32.delimiter);
6015
+ const entries = process.platform === "win32" ? [piExecutable === void 0 ? null : path33.dirname(piExecutable), path33.dirname(process.execPath), process.env.SystemRoot ? path33.join(process.env.SystemRoot, "System32") : null] : [piExecutable === void 0 ? null : path33.dirname(piExecutable), path33.dirname(process.execPath), "/usr/local/bin", "/usr/bin", "/bin"];
6016
+ return [...new Set(entries.filter((entry) => entry !== null))].join(path33.delimiter);
5900
6017
  }
5901
6018
  function targetPaths(targetDir, engramBin, piExecutable) {
5902
- const root = path32.resolve(targetDir);
5903
- const codingAgentDir = path32.join(root, "pi-agent");
5904
- const home = path32.join(root, "home");
5905
- const temporary = path32.join(root, "tmp");
6019
+ const root = path33.resolve(targetDir);
6020
+ const codingAgentDir = path33.join(root, "pi-agent");
6021
+ const home = path33.join(root, "home");
6022
+ const temporary = path33.join(root, "tmp");
5906
6023
  return {
5907
6024
  codingAgentDir,
5908
- receiptPath: path32.join(root, "state", "pi-receipt.json"),
5909
- packageRunner: path32.join(codingAgentDir, "npm", "node_modules", "jorgex-pi", "bin", "jorgex-pi.mjs"),
6025
+ receiptPath: path33.join(root, "state", "pi-receipt.json"),
6026
+ packageRunner: path33.join(codingAgentDir, "npm", "node_modules", "jorgex-pi", "bin", "jorgex-pi.mjs"),
5910
6027
  environment: {
5911
6028
  HOME: home,
5912
6029
  USERPROFILE: home,
5913
- APPDATA: path32.join(root, "appdata"),
5914
- LOCALAPPDATA: path32.join(root, "localappdata"),
5915
- XDG_CONFIG_HOME: path32.join(root, "xdg-config"),
5916
- XDG_DATA_HOME: path32.join(root, "xdg-data"),
5917
- XDG_CACHE_HOME: path32.join(root, "xdg-cache"),
6030
+ APPDATA: path33.join(root, "appdata"),
6031
+ LOCALAPPDATA: path33.join(root, "localappdata"),
6032
+ XDG_CONFIG_HOME: path33.join(root, "xdg-config"),
6033
+ XDG_DATA_HOME: path33.join(root, "xdg-data"),
6034
+ XDG_CACHE_HOME: path33.join(root, "xdg-cache"),
5918
6035
  TEMP: temporary,
5919
6036
  TMP: temporary,
5920
6037
  TMPDIR: temporary,
5921
- npm_config_cache: path32.join(root, "npm-cache"),
6038
+ npm_config_cache: path33.join(root, "npm-cache"),
5922
6039
  NPM_CONFIG_IGNORE_SCRIPTS: "true",
5923
6040
  NPM_CONFIG_UPDATE_NOTIFIER: "false",
5924
6041
  PI_CODING_AGENT_DIR: codingAgentDir,
@@ -5929,15 +6046,15 @@ function targetPaths(targetDir, engramBin, piExecutable) {
5929
6046
  }
5930
6047
  function userPaths(engramBin, piExecutable) {
5931
6048
  const home = os4.homedir();
5932
- const codingAgentDir = process.env.PI_CODING_AGENT_DIR ?? path32.join(home, ".pi", "agent");
6049
+ const codingAgentDir = process.env.PI_CODING_AGENT_DIR ?? path33.join(home, ".pi", "agent");
5933
6050
  return {
5934
6051
  codingAgentDir,
5935
- receiptPath: path32.join(dataDir(), "pi-receipt.json"),
5936
- packageRunner: path32.join(codingAgentDir, "npm", "node_modules", "jorgex-pi", "bin", "jorgex-pi.mjs"),
6052
+ receiptPath: path33.join(dataDir(), "pi-receipt.json"),
6053
+ packageRunner: path33.join(codingAgentDir, "npm", "node_modules", "jorgex-pi", "bin", "jorgex-pi.mjs"),
5937
6054
  environment: {
5938
6055
  HOME: home,
5939
- XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME ?? path32.join(home, ".config"),
5940
- XDG_CACHE_HOME: process.env.XDG_CACHE_HOME ?? path32.join(home, ".cache"),
6056
+ XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME ?? path33.join(home, ".config"),
6057
+ XDG_CACHE_HOME: process.env.XDG_CACHE_HOME ?? path33.join(home, ".cache"),
5941
6058
  TMPDIR: os4.tmpdir(),
5942
6059
  NPM_CONFIG_IGNORE_SCRIPTS: "true",
5943
6060
  NPM_CONFIG_UPDATE_NOTIFIER: "false",
@@ -5965,7 +6082,7 @@ function runPiRuntime(input, deps) {
5965
6082
  return { kind: "blocked", reason: "tarball-integrity" };
5966
6083
  }
5967
6084
  const paths = input.targetDir === void 0 ? userPaths(input.engramBin, input.detected.executable) : targetPaths(input.targetDir, input.engramBin, input.detected.executable);
5968
- const settingsJson = deps.readSettings(path32.join(paths.codingAgentDir, "settings.json"));
6085
+ const settingsJson = deps.readSettings(path33.join(paths.codingAgentDir, "settings.json"));
5969
6086
  const receiptJson = deps.readReceipt(paths.receiptPath);
5970
6087
  const lifecycleInput = {
5971
6088
  candidate: PI_RUNTIME_CANDIDATE,
@@ -6023,19 +6140,19 @@ function runPiRuntime(input, deps) {
6023
6140
  return result;
6024
6141
  }
6025
6142
  function readJsonFile(file) {
6026
- return JSON.parse(fs24.readFileSync(file, "utf8"));
6143
+ return JSON.parse(fs25.readFileSync(file, "utf8"));
6027
6144
  }
6028
6145
  function packageVersionFromExecutable(executable) {
6029
6146
  let current;
6030
6147
  try {
6031
- current = path32.dirname(fs24.realpathSync(executable));
6148
+ current = path33.dirname(fs25.realpathSync(executable));
6032
6149
  } catch {
6033
6150
  return null;
6034
6151
  }
6035
6152
  for (let depth = 0; depth < 8; depth++) {
6036
6153
  const manifests = [
6037
- path32.join(current, "package.json"),
6038
- path32.join(current, "node_modules", "@earendil-works", "pi-coding-agent", "package.json")
6154
+ path33.join(current, "package.json"),
6155
+ path33.join(current, "node_modules", "@earendil-works", "pi-coding-agent", "package.json")
6039
6156
  ];
6040
6157
  for (const manifest of manifests) {
6041
6158
  try {
@@ -6046,7 +6163,7 @@ function packageVersionFromExecutable(executable) {
6046
6163
  } catch {
6047
6164
  }
6048
6165
  }
6049
- const parent = path32.dirname(current);
6166
+ const parent = path33.dirname(current);
6050
6167
  if (parent === current) break;
6051
6168
  current = parent;
6052
6169
  }
@@ -6061,35 +6178,35 @@ function detectPiRuntime() {
6061
6178
  installed: executable !== null,
6062
6179
  executable,
6063
6180
  version: executable === null ? null : packageVersionFromExecutable(executable),
6064
- codingAgentDir: process.env.PI_CODING_AGENT_DIR ?? path32.join(home, ".pi", "agent")
6181
+ codingAgentDir: process.env.PI_CODING_AGENT_DIR ?? path33.join(home, ".pi", "agent")
6065
6182
  };
6066
6183
  }
6067
6184
  function hasManagedPiRuntime(targetDir) {
6068
- const receipt = targetDir === void 0 ? path32.join(dataDir(), "pi-receipt.json") : path32.join(path32.resolve(targetDir), "state", "pi-receipt.json");
6069
- return fs24.statSync(receipt, { throwIfNoEntry: false })?.isFile() === true;
6185
+ const receipt = targetDir === void 0 ? path33.join(dataDir(), "pi-receipt.json") : path33.join(path33.resolve(targetDir), "state", "pi-receipt.json");
6186
+ return fs25.statSync(receipt, { throwIfNoEntry: false })?.isFile() === true;
6070
6187
  }
6071
6188
  function resolvePiEngramBin(targetDir) {
6072
6189
  if (targetDir === void 0) return detectEngram();
6073
- const candidate = path32.join(path32.resolve(targetDir), "bin", process.platform === "win32" ? "engram.exe" : "engram");
6074
- return fs24.statSync(candidate, { throwIfNoEntry: false })?.isFile() ? candidate : null;
6190
+ const candidate = path33.join(path33.resolve(targetDir), "bin", process.platform === "win32" ? "engram.exe" : "engram");
6191
+ return fs25.statSync(candidate, { throwIfNoEntry: false })?.isFile() ? candidate : null;
6075
6192
  }
6076
6193
  function readOptional(file, fallback) {
6077
6194
  try {
6078
- return fs24.readFileSync(file, "utf8");
6195
+ return fs25.readFileSync(file, "utf8");
6079
6196
  } catch (error) {
6080
6197
  if (error.code === "ENOENT") return fallback;
6081
6198
  throw error;
6082
6199
  }
6083
6200
  }
6084
6201
  function hashPiTarball(file) {
6085
- const descriptor = fs24.openSync(file, "r");
6202
+ const descriptor = fs25.openSync(file, "r");
6086
6203
  const sha2562 = createHash2("sha256");
6087
6204
  const sha512 = createHash2("sha512");
6088
6205
  const buffer = Buffer.allocUnsafe(1024 * 1024);
6089
6206
  let bytes = 0;
6090
6207
  try {
6091
6208
  while (true) {
6092
- const read = fs24.readSync(descriptor, buffer, 0, buffer.length, null);
6209
+ const read = fs25.readSync(descriptor, buffer, 0, buffer.length, null);
6093
6210
  if (read === 0) break;
6094
6211
  bytes += read;
6095
6212
  const chunk = buffer.subarray(0, read);
@@ -6097,27 +6214,27 @@ function hashPiTarball(file) {
6097
6214
  sha512.update(chunk);
6098
6215
  }
6099
6216
  } finally {
6100
- fs24.closeSync(descriptor);
6217
+ fs25.closeSync(descriptor);
6101
6218
  }
6102
6219
  return { path: file, bytes, sha256: sha2562.digest("hex"), sha512: sha512.digest("hex") };
6103
6220
  }
6104
6221
  async function acquirePiTarball(destination) {
6105
- const existing = fs24.statSync(destination, { throwIfNoEntry: false });
6222
+ const existing = fs25.statSync(destination, { throwIfNoEntry: false });
6106
6223
  if (existing?.isFile()) {
6107
6224
  const observed = hashPiTarball(destination);
6108
6225
  if (observed.bytes === PI_RUNTIME_CANDIDATE.tarball.bytes && observed.sha256 === PI_RUNTIME_CANDIDATE.tarball.sha256 && observed.sha512 === PI_RUNTIME_CANDIDATE.tarball.sha512) {
6109
6226
  return observed;
6110
6227
  }
6111
- fs24.rmSync(destination, { force: true });
6228
+ fs25.rmSync(destination, { force: true });
6112
6229
  }
6113
- fs24.mkdirSync(path32.dirname(destination), { recursive: true });
6230
+ fs25.mkdirSync(path33.dirname(destination), { recursive: true });
6114
6231
  const partial = `${destination}.partial-${process.pid}`;
6115
6232
  const response = await fetch(`https://registry.npmjs.org/jorgex-pi/-/jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`, {
6116
6233
  redirect: "error",
6117
6234
  headers: { accept: "application/octet-stream" }
6118
6235
  });
6119
6236
  if (!response.ok || response.body === null) throw new Error(`No se pudo descargar jorgex-pi@${PI_RUNTIME_CANDIDATE.package.version} (${response.status}).`);
6120
- const descriptor = fs24.openSync(partial, "wx", 384);
6237
+ const descriptor = fs25.openSync(partial, "wx", 384);
6121
6238
  let bytes = 0;
6122
6239
  try {
6123
6240
  for await (const chunk of response.body) {
@@ -6125,15 +6242,15 @@ async function acquirePiTarball(destination) {
6125
6242
  bytes += buffer.length;
6126
6243
  if (bytes > PI_RUNTIME_CANDIDATE.tarball.bytes) throw new Error("El tarball de jorgex-pi excede el tama\xF1o fijado.");
6127
6244
  let offset = 0;
6128
- while (offset < buffer.length) offset += fs24.writeSync(descriptor, buffer, offset);
6245
+ while (offset < buffer.length) offset += fs25.writeSync(descriptor, buffer, offset);
6129
6246
  }
6130
6247
  } catch (error) {
6131
- fs24.closeSync(descriptor);
6132
- fs24.rmSync(partial, { force: true });
6248
+ fs25.closeSync(descriptor);
6249
+ fs25.rmSync(partial, { force: true });
6133
6250
  throw error;
6134
6251
  }
6135
- fs24.closeSync(descriptor);
6136
- fs24.renameSync(partial, destination);
6252
+ fs25.closeSync(descriptor);
6253
+ fs25.renameSync(partial, destination);
6137
6254
  return hashPiTarball(destination);
6138
6255
  }
6139
6256
  function runProcess(invocation) {
@@ -6183,7 +6300,7 @@ async function runPiRuntimeSystem(input) {
6183
6300
  remedy: "Instala Engram o configura un ENGRAM_BIN absoluto antes de reintentar."
6184
6301
  };
6185
6302
  }
6186
- const destination = input.targetDir === void 0 ? path32.join(dataDir(), "packages", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`) : path32.join(path32.resolve(input.targetDir), "downloads", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`);
6303
+ const destination = input.targetDir === void 0 ? path33.join(dataDir(), "packages", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`) : path33.join(path33.resolve(input.targetDir), "downloads", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`);
6187
6304
  let artifact;
6188
6305
  try {
6189
6306
  artifact = await acquirePiTarball(destination);
@@ -6203,18 +6320,18 @@ async function runPiRuntimeSystem(input) {
6203
6320
  }, {
6204
6321
  download: () => artifact,
6205
6322
  backupSettings: () => createBackup(
6206
- [path32.join(paths.codingAgentDir, "settings.json")],
6323
+ [path33.join(paths.codingAgentDir, "settings.json")],
6207
6324
  "pi-package-install",
6208
- input.targetDir === void 0 ? void 0 : path32.join(path32.resolve(input.targetDir), "backups")
6325
+ input.targetDir === void 0 ? void 0 : path33.join(path33.resolve(input.targetDir), "backups")
6209
6326
  ),
6210
6327
  run: runProcess,
6211
- readSettings: () => readOptional(path32.join(paths.codingAgentDir, "settings.json"), '{"packages":[]}'),
6212
- rewriteSettings: (content) => writeText(path32.join(paths.codingAgentDir, "settings.json"), `${content}
6328
+ readSettings: () => readOptional(path33.join(paths.codingAgentDir, "settings.json"), '{"packages":[]}'),
6329
+ rewriteSettings: (content) => writeText(path33.join(paths.codingAgentDir, "settings.json"), `${content}
6213
6330
  `),
6214
6331
  writeReceiptAtomic: (content) => writeText(paths.receiptPath, content)
6215
6332
  });
6216
6333
  }
6217
- const packageRoot = path32.dirname(path32.dirname(paths.packageRunner));
6334
+ const packageRoot = path33.dirname(path33.dirname(paths.packageRunner));
6218
6335
  const writeReceipt = (receipt) => {
6219
6336
  writeText(paths.receiptPath, `${JSON.stringify(receipt, null, 2)}
6220
6337
  `);
@@ -6232,13 +6349,13 @@ async function runPiRuntimeSystem(input) {
6232
6349
  value,
6233
6350
  {
6234
6351
  backupSettings: () => createBackup(
6235
- [path32.join(paths.codingAgentDir, "settings.json")],
6352
+ [path33.join(paths.codingAgentDir, "settings.json")],
6236
6353
  "pi-package-uninstall",
6237
- input.targetDir === void 0 ? void 0 : path32.join(path32.resolve(input.targetDir), "backups")
6354
+ input.targetDir === void 0 ? void 0 : path33.join(path33.resolve(input.targetDir), "backups")
6238
6355
  ),
6239
6356
  run: runProcess,
6240
- isPackageAbsent: () => !fs24.existsSync(packageRoot),
6241
- deleteReceipt: () => fs24.rmSync(paths.receiptPath, { force: true })
6357
+ isPackageAbsent: () => !fs25.existsSync(packageRoot),
6358
+ deleteReceipt: () => fs25.rmSync(paths.receiptPath, { force: true })
6242
6359
  }
6243
6360
  )
6244
6361
  };
@@ -6318,6 +6435,13 @@ async function runManagedPiSystem(input) {
6318
6435
  ...runtimeInput
6319
6436
  } = input;
6320
6437
  const readsStyle = input.operation !== "uninstall" && input.operation !== "models";
6438
+ if (input.operation !== "models") {
6439
+ try {
6440
+ assertSystemPromptFile(piSystemPromptFile(input.targetDir), input.targetDir);
6441
+ } catch (error) {
6442
+ return { kind: "blocked", reason: "projection-prompt-markers", remedy: error instanceof Error ? error.message : String(error) };
6443
+ }
6444
+ }
6321
6445
  const preparedStyle = readsStyle && suppliedStyle === void 0 ? prepareWritingStyle(resolveWritingStyleFile({ targetDir: input.targetDir }), { rootDir: input.targetDir }) : void 0;
6322
6446
  const style = readsStyle ? suppliedStyle ?? preparedStyle : void 0;
6323
6447
  const mode = readsStyle ? writingStyleMode ?? (input.targetDir === void 0 ? loadInstallModePreference().mode : "human") : "human";
@@ -6371,7 +6495,7 @@ async function runManagedPiSystem(input) {
6371
6495
  }
6372
6496
 
6373
6497
  // src/lib/quality-runner.ts
6374
- import path33 from "path";
6498
+ import path34 from "path";
6375
6499
  import { spawn, spawnSync as spawnSync2 } from "child_process";
6376
6500
  function normalizeLimit(value, name) {
6377
6501
  if (value === void 0) return void 0;
@@ -6503,7 +6627,7 @@ function killProcessTree(child) {
6503
6627
  if (pid === void 0) return;
6504
6628
  if (process.platform === "win32") {
6505
6629
  const systemRoot = process.env.SystemRoot ?? process.env.WINDIR ?? "C:\\Windows";
6506
- const taskkill = path33.join(systemRoot, "System32", "taskkill.exe");
6630
+ const taskkill = path34.join(systemRoot, "System32", "taskkill.exe");
6507
6631
  const result = spawnSync2(taskkill, ["/pid", String(pid), "/t", "/f"], {
6508
6632
  shell: false,
6509
6633
  stdio: "ignore",
@@ -6749,9 +6873,9 @@ async function runQualityPlan(input) {
6749
6873
  }
6750
6874
 
6751
6875
  // src/lib/engram-install.ts
6752
- import fs25 from "fs";
6876
+ import fs26 from "fs";
6753
6877
  import os5 from "os";
6754
- import path34 from "path";
6878
+ import path35 from "path";
6755
6879
  import { execFileSync as execFileSync7 } from "child_process";
6756
6880
  import { createHash as createHash3 } from "crypto";
6757
6881
 
@@ -6796,15 +6920,15 @@ var engram_release_default = {
6796
6920
  async function installMissingEngram(options = {}) {
6797
6921
  const platform = options.platform ?? process.platform;
6798
6922
  const homeDir = options.homeDir ?? os5.homedir();
6799
- if (!path34.isAbsolute(homeDir)) return { ok: false, reason: "El directorio personal debe ser absoluto." };
6923
+ if (!path35.isAbsolute(homeDir)) return { ok: false, reason: "El directorio personal debe ser absoluto." };
6800
6924
  const binaryName = platform === "win32" ? "engram.exe" : "engram";
6801
- const bin = path34.join(homeDir, ".local", "bin", binaryName);
6925
+ const bin = path35.join(homeDir, ".local", "bin", binaryName);
6802
6926
  let staging;
6803
6927
  try {
6804
- const existing = fs25.lstatSync(bin, { throwIfNoEntry: false });
6928
+ const existing = fs26.lstatSync(bin, { throwIfNoEntry: false });
6805
6929
  if (existing) {
6806
- if (!fs25.statSync(bin).isFile()) throw new Error(`El destino Engram no es un archivo: ${bin}`);
6807
- fs25.accessSync(bin, platform === "win32" ? fs25.constants.F_OK : fs25.constants.X_OK);
6930
+ if (!fs26.statSync(bin).isFile()) throw new Error(`El destino Engram no es un archivo: ${bin}`);
6931
+ fs26.accessSync(bin, platform === "win32" ? fs26.constants.F_OK : fs26.constants.X_OK);
6808
6932
  return { ok: true, bin };
6809
6933
  }
6810
6934
  const key = `${platform === "win32" ? "windows" : platform}_${options.arch ?? process.arch}`;
@@ -6833,24 +6957,24 @@ async function installMissingEngram(options = {}) {
6833
6957
  if (size !== asset.size || createHash3("sha256").update(archive).digest("hex") !== asset.sha256) {
6834
6958
  throw new Error("Engram: tama\xF1o o hash SHA-256 no coincide con el artefacto aprobado.");
6835
6959
  }
6836
- fs25.mkdirSync(path34.dirname(bin), { recursive: true });
6837
- staging = fs25.mkdtempSync(path34.join(path34.dirname(bin), ".engram-install-"));
6838
- const archivePath = path34.join(staging, asset.name);
6839
- fs25.writeFileSync(archivePath, archive, { flag: "wx", mode: 384 });
6960
+ fs26.mkdirSync(path35.dirname(bin), { recursive: true });
6961
+ staging = fs26.mkdtempSync(path35.join(path35.dirname(bin), ".engram-install-"));
6962
+ const archivePath = path35.join(staging, asset.name);
6963
+ fs26.writeFileSync(archivePath, archive, { flag: "wx", mode: 384 });
6840
6964
  const binary = execFileSync7(resolveTarBin(), ["-xOf", archivePath, binaryName], {
6841
6965
  maxBuffer: 64 * 1024 * 1024,
6842
6966
  timeout: 3e4,
6843
6967
  stdio: ["ignore", "pipe", "pipe"]
6844
6968
  });
6845
6969
  if (binary.length === 0) throw new Error("El artefacto Engram no contiene un binario v\xE1lido.");
6846
- const stagedBin = path34.join(staging, binaryName);
6847
- fs25.writeFileSync(stagedBin, binary, { flag: "wx", mode: 493 });
6848
- fs25.linkSync(stagedBin, bin);
6970
+ const stagedBin = path35.join(staging, binaryName);
6971
+ fs26.writeFileSync(stagedBin, binary, { flag: "wx", mode: 493 });
6972
+ fs26.linkSync(stagedBin, bin);
6849
6973
  return { ok: true, bin };
6850
6974
  } catch (error) {
6851
6975
  return { ok: false, reason: error instanceof Error ? error.message : String(error) };
6852
6976
  } finally {
6853
- if (staging) fs25.rmSync(staging, { recursive: true, force: true });
6977
+ if (staging) fs26.rmSync(staging, { recursive: true, force: true });
6854
6978
  }
6855
6979
  }
6856
6980
 
@@ -7109,6 +7233,18 @@ async function resolveDevtoolsMcpSelection(command, flags, runtimes) {
7109
7233
  const enabled = new Set(selected);
7110
7234
  return Object.fromEntries(runtimes.map((runtime) => [runtime, enabled.has(runtime)]));
7111
7235
  }
7236
+ function assertSelectedPromptFiles(runtimes, targetDir) {
7237
+ for (const runtime of runtimes) {
7238
+ if (runtime === "pi") assertSystemPromptFile(piSystemPromptFile(targetDir), targetDir);
7239
+ else {
7240
+ const adapter = ADAPTERS[runtime];
7241
+ if (!adapter) continue;
7242
+ const detection = adapter.detect();
7243
+ if (targetDir === void 0 && !detection.installed) continue;
7244
+ assertSystemPromptFile(adapter.paths(targetDir ?? detection.configDir).systemPromptFile, targetDir);
7245
+ }
7246
+ }
7247
+ }
7112
7248
  function parseCliArgs(argv) {
7113
7249
  const [first, ...rest] = argv;
7114
7250
  const isCommand = COMMANDS.includes(first ?? "install");
@@ -7335,7 +7471,7 @@ Flags disponibles: jorgex-stack --help`
7335
7471
  return;
7336
7472
  }
7337
7473
  try {
7338
- const plan = JSON.parse(fs26.readFileSync(flags.positional[0], "utf8"));
7474
+ const plan = JSON.parse(fs27.readFileSync(flags.positional[0], "utf8"));
7339
7475
  const result = await runQualityPlan(plan);
7340
7476
  const serialized = serializeQualityReceipt(result.receipt);
7341
7477
  if (flags.receipt === void 0) process.stdout.write(`${serialized}
@@ -7363,6 +7499,7 @@ Flags disponibles: jorgex-stack --help`
7363
7499
  let completed = false;
7364
7500
  p6.intro(`jorgex-stack ${command}${flags.dryRun ? " (dry-run)" : ""}`);
7365
7501
  try {
7502
+ assertSelectedPromptFiles(runtimes, flags.targetDir);
7366
7503
  const writingStyle = prepareWritingStyle(resolveWritingStyleFile({ targetDir: flags.targetDir }), { rootDir: flags.targetDir });
7367
7504
  if (flags.targetDir === void 0) {
7368
7505
  const errors = browserPreferenceErrors();
@@ -7468,6 +7605,13 @@ Flags disponibles: jorgex-stack --help`
7468
7605
  process.exitCode = 1;
7469
7606
  return;
7470
7607
  }
7608
+ try {
7609
+ assertSelectedPromptFiles(runtimes, flags.targetDir);
7610
+ } catch (error) {
7611
+ p6.log.error(error instanceof Error ? error.message : String(error));
7612
+ process.exitCode = 1;
7613
+ return;
7614
+ }
7471
7615
  const fileRuntimes = runtimes.filter(isFileManagedRuntime);
7472
7616
  let exitCode = fileRuntimes.length > 0 || flags.removePlaywright ? await runUninstall({
7473
7617
  runtimes: fileRuntimes,
@@ -7508,6 +7652,13 @@ Flags disponibles: jorgex-stack --help`
7508
7652
  }
7509
7653
  const runtimes = await resolveRuntimes(flags);
7510
7654
  if (runtimes === null) return;
7655
+ try {
7656
+ assertSelectedPromptFiles(runtimes, flags.targetDir);
7657
+ } catch (error) {
7658
+ p6.log.error(error instanceof Error ? error.message : String(error));
7659
+ process.exitCode = 1;
7660
+ return;
7661
+ }
7511
7662
  const fileRuntimes = runtimes.filter(isFileManagedRuntime);
7512
7663
  const preferenceFile = installModePreferenceFile();
7513
7664
  const explicitMode = flags.mode !== void 0 || flags.subagentConcurrency !== void 0;