jorgex-stack 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,4 +1,17 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ HOME,
4
+ QUALITY_PROFILES,
5
+ canonicalJson,
6
+ createQualityReceipt,
7
+ dataDir,
8
+ evaluateQualityPolicy,
9
+ samePath,
10
+ serializeQualityReceipt,
11
+ sha256,
12
+ stackRoot,
13
+ validateQualityReceipt
14
+ } from "./chunk-IO2FFZOH.js";
2
15
 
3
16
  // src/cli.ts
4
17
  import * as p6 from "@clack/prompts";
@@ -7,11 +20,11 @@ import { pathToFileURL as pathToFileURL2 } from "url";
7
20
 
8
21
  // src/install.ts
9
22
  import fs15 from "fs";
10
- import path21 from "path";
23
+ import path20 from "path";
11
24
  import * as p from "@clack/prompts";
12
25
 
13
26
  // src/adapters/opencode.ts
14
- import path7 from "path";
27
+ import path6 from "path";
15
28
  import fs3 from "fs";
16
29
  import { pathToFileURL } from "url";
17
30
 
@@ -68,37 +81,12 @@ function loadCanonicalDefaults(stackDir) {
68
81
  }
69
82
 
70
83
  // src/lib/model-map.ts
71
- import path4 from "path";
72
- import { existsSync as existsSync2 } from "fs";
73
-
74
- // src/lib/paths.ts
75
- import os from "os";
76
- import path2 from "path";
84
+ import path3 from "path";
77
85
  import { existsSync } from "fs";
78
- import { fileURLToPath } from "url";
79
- var HOME = os.homedir();
80
- function stackRoot() {
81
- let dir = path2.dirname(fileURLToPath(import.meta.url));
82
- for (let i = 0; i < 6; i++) {
83
- const candidate = path2.join(dir, "stack");
84
- if (existsSync(path2.join(candidate, "system-prompt", "AGENTS.md"))) return candidate;
85
- dir = path2.dirname(dir);
86
- }
87
- throw new Error("No se encontr\xF3 stack/ relativa al CLI \u2014 instalaci\xF3n rota.");
88
- }
89
- function dataDir() {
90
- return path2.join(HOME, ".jorgex-stack");
91
- }
92
- function samePath(a, b) {
93
- const resolvedA = path2.resolve(a);
94
- const resolvedB = path2.resolve(b);
95
- if (process.platform === "win32") return resolvedA.toLowerCase() === resolvedB.toLowerCase();
96
- return resolvedA === resolvedB;
97
- }
98
86
 
99
87
  // src/lib/fsx.ts
100
88
  import fs2 from "fs";
101
- import path3 from "path";
89
+ import path2 from "path";
102
90
  function readTextIfExists(file) {
103
91
  try {
104
92
  return fs2.readFileSync(file, "utf8");
@@ -111,7 +99,7 @@ function ensureDir(dir) {
111
99
  }
112
100
  var tmpCounter = 0;
113
101
  function tmpPath(target) {
114
- return path3.join(path3.dirname(target), `.jorgex-${process.pid}-${tmpCounter++}.tmp`);
102
+ return path2.join(path2.dirname(target), `.jorgex-${process.pid}-${tmpCounter++}.tmp`);
115
103
  }
116
104
  function renameInto(tmp, target) {
117
105
  try {
@@ -122,23 +110,23 @@ function renameInto(tmp, target) {
122
110
  }
123
111
  }
124
112
  function writeText(file, content) {
125
- ensureDir(path3.dirname(file));
113
+ ensureDir(path2.dirname(file));
126
114
  const tmp = tmpPath(file);
127
115
  fs2.writeFileSync(tmp, content, "utf8");
128
116
  renameInto(tmp, file);
129
117
  }
130
118
  function copyFile(source, target) {
131
- ensureDir(path3.dirname(target));
119
+ ensureDir(path2.dirname(target));
132
120
  const tmp = tmpPath(target);
133
121
  fs2.copyFileSync(source, tmp);
134
122
  renameInto(tmp, target);
135
123
  }
136
124
  function isContainedIn(child, root) {
137
- const rel = path3.relative(path3.resolve(root), path3.resolve(child));
138
- return rel !== "" && rel !== ".." && !rel.startsWith(`..${path3.sep}`) && !path3.isAbsolute(rel);
125
+ const rel = path2.relative(path2.resolve(root), path2.resolve(child));
126
+ return rel !== "" && rel !== ".." && !rel.startsWith(`..${path2.sep}`) && !path2.isAbsolute(rel);
139
127
  }
140
128
  function pruneEmptyDirs(file, root) {
141
- let dir = path3.dirname(file);
129
+ let dir = path2.dirname(file);
142
130
  while (dir.startsWith(root) && dir !== root) {
143
131
  try {
144
132
  if (fs2.readdirSync(dir).length > 0) return;
@@ -146,7 +134,7 @@ function pruneEmptyDirs(file, root) {
146
134
  } catch {
147
135
  return;
148
136
  }
149
- dir = path3.dirname(dir);
137
+ dir = path2.dirname(dir);
150
138
  }
151
139
  }
152
140
  function sameFileContent(a, b) {
@@ -163,7 +151,7 @@ function listFilesRecursive(dir) {
163
151
  if (!fs2.existsSync(dir)) return out;
164
152
  const walk = (d) => {
165
153
  for (const entry of fs2.readdirSync(d, { withFileTypes: true })) {
166
- const p7 = path3.join(d, entry.name);
154
+ const p7 = path2.join(d, entry.name);
167
155
  if (entry.isDirectory()) walk(p7);
168
156
  else out.push(p7);
169
157
  }
@@ -198,7 +186,7 @@ var DEFAULT_MODEL_MAP = {
198
186
  }
199
187
  };
200
188
  function modelMapFile() {
201
- return path4.join(dataDir(), "model-map.json");
189
+ return path3.join(dataDir(), "model-map.json");
202
190
  }
203
191
  function loadModelMap() {
204
192
  const raw = readTextIfExists(modelMapFile());
@@ -217,22 +205,22 @@ function loadModelMap() {
217
205
  }
218
206
  function ensureModelMapFile() {
219
207
  const file = modelMapFile();
220
- if (!existsSync2(file)) {
208
+ if (!existsSync(file)) {
221
209
  writeText(file, JSON.stringify(DEFAULT_MODEL_MAP, null, 2) + "\n");
222
210
  }
223
211
  return file;
224
212
  }
225
213
 
226
214
  // src/lib/detect.ts
227
- import path5 from "path";
228
- import { existsSync as existsSync3, statSync } from "fs";
215
+ import path4 from "path";
216
+ import { existsSync as existsSync2, statSync } from "fs";
229
217
  import { execFileSync } from "child_process";
230
218
  function lookPath(cmd) {
231
219
  const exts = process.platform === "win32" ? [".exe", ".cmd", ".bat", ".ps1", ""] : [""];
232
- for (const dir of (process.env.PATH ?? "").split(path5.delimiter)) {
220
+ for (const dir of (process.env.PATH ?? "").split(path4.delimiter)) {
233
221
  if (!dir) continue;
234
222
  for (const ext of exts) {
235
- const candidate = path5.join(dir, cmd + ext);
223
+ const candidate = path4.join(dir, cmd + ext);
236
224
  if (statSync(candidate, { throwIfNoEntry: false })?.isFile()) return candidate;
237
225
  }
238
226
  }
@@ -261,50 +249,50 @@ function runDetectedBin(bin, args, timeoutMs) {
261
249
  }
262
250
  }
263
251
  function detectOpenCode() {
264
- const configDir = process.env.OPENCODE_CONFIG_DIR ?? path5.join(HOME, ".config", "opencode");
252
+ const configDir = process.env.OPENCODE_CONFIG_DIR ?? path4.join(HOME, ".config", "opencode");
265
253
  const binPath = lookPath("opencode");
266
254
  return {
267
255
  id: "opencode",
268
256
  name: "OpenCode",
269
- installed: binPath !== null || existsSync3(configDir),
257
+ installed: binPath !== null || existsSync2(configDir),
270
258
  binPath,
271
259
  configDir
272
260
  };
273
261
  }
274
262
  function detectClaudeCode() {
275
- const configDir = path5.join(HOME, ".claude");
263
+ const configDir = path4.join(HOME, ".claude");
276
264
  const binPath = lookPath("claude");
277
265
  return {
278
266
  id: "claude-code",
279
267
  name: "Claude Code",
280
- installed: binPath !== null || existsSync3(configDir),
268
+ installed: binPath !== null || existsSync2(configDir),
281
269
  binPath,
282
270
  configDir
283
271
  };
284
272
  }
285
273
  function detectCodex() {
286
- const configDir = process.env.CODEX_HOME ?? path5.join(HOME, ".codex");
274
+ const configDir = process.env.CODEX_HOME ?? path4.join(HOME, ".codex");
287
275
  const binPath = lookPath("codex");
288
276
  return {
289
277
  id: "codex",
290
278
  name: "Codex CLI",
291
- installed: binPath !== null || existsSync3(configDir),
279
+ installed: binPath !== null || existsSync2(configDir),
292
280
  binPath,
293
281
  configDir
294
282
  };
295
283
  }
296
284
  function detectEngram() {
297
285
  const fromEnv = process.env.ENGRAM_BIN;
298
- if (fromEnv && existsSync3(fromEnv)) return fromEnv;
286
+ if (fromEnv && existsSync2(fromEnv)) return fromEnv;
299
287
  const onPath = lookPath("engram");
300
288
  if (onPath) return onPath;
301
289
  const candidates = [
302
- path5.join(HOME, "go", "bin", "engram.exe"),
303
- path5.join(HOME, "go", "bin", "engram"),
304
- path5.join(HOME, ".local", "bin", "engram.exe"),
305
- path5.join(HOME, ".local", "bin", "engram")
290
+ path4.join(HOME, "go", "bin", "engram.exe"),
291
+ path4.join(HOME, "go", "bin", "engram"),
292
+ path4.join(HOME, ".local", "bin", "engram.exe"),
293
+ path4.join(HOME, ".local", "bin", "engram")
306
294
  ];
307
- for (const c of candidates) if (existsSync3(c)) return c;
295
+ for (const c of candidates) if (existsSync2(c)) return c;
308
296
  return null;
309
297
  }
310
298
 
@@ -473,9 +461,9 @@ function readTomlSection(existing, section) {
473
461
  }
474
462
 
475
463
  // src/lib/hooks-format.ts
476
- import path6 from "path";
464
+ import path5 from "path";
477
465
  function hookScriptNames(canonical) {
478
- 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) => path6.basename(s));
466
+ 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) => path5.basename(s));
479
467
  }
480
468
  function plainHookCommands(canonical) {
481
469
  return new Set(
@@ -522,7 +510,7 @@ function upsertNativeHooks(existing, canonical, scriptsDir, mapMatcher = (m) =>
522
510
  ...h.timeout !== void 0 ? { timeout: h.timeout } : {}
523
511
  }))
524
512
  };
525
- const scriptNames = entry.hooks.map((h) => /\{\{SCRIPTS_DIR\}\}[/\\]([\w./-]+)/.exec(h.command)?.[1]).filter((s) => s !== void 0).map((s) => path6.basename(s));
513
+ const scriptNames = entry.hooks.map((h) => /\{\{SCRIPTS_DIR\}\}[/\\]([\w./-]+)/.exec(h.command)?.[1]).filter((s) => s !== void 0).map((s) => path5.basename(s));
526
514
  const plainCommands = entry.hooks.map((h) => h.command).filter((c) => !c.includes("{{SCRIPTS_DIR}}"));
527
515
  const index = list.findIndex((existingEntry) => {
528
516
  const e = existingEntry;
@@ -602,16 +590,16 @@ var opencodeAdapter = {
602
590
  paths(configDir) {
603
591
  const isRealConfigDir = samePath(
604
592
  configDir,
605
- process.env.OPENCODE_CONFIG_DIR ?? path7.join(HOME, ".config", "opencode")
593
+ process.env.OPENCODE_CONFIG_DIR ?? path6.join(HOME, ".config", "opencode")
606
594
  );
607
- const agentsHome = isRealConfigDir ? HOME : path7.dirname(configDir);
595
+ const agentsHome = isRealConfigDir ? HOME : path6.dirname(configDir);
608
596
  return {
609
- systemPromptFile: path7.join(configDir, "AGENTS.md"),
610
- agentsDir: path7.join(configDir, "agents"),
611
- skillsDir: path7.join(agentsHome, ".agents", "skills"),
612
- commandsDir: path7.join(configDir, "commands"),
613
- pluginsDir: path7.join(configDir, "plugins"),
614
- scriptsDir: path7.join(configDir, "scripts"),
597
+ systemPromptFile: path6.join(configDir, "AGENTS.md"),
598
+ agentsDir: path6.join(configDir, "agents"),
599
+ skillsDir: path6.join(agentsHome, ".agents", "skills"),
600
+ commandsDir: path6.join(configDir, "commands"),
601
+ pluginsDir: path6.join(configDir, "plugins"),
602
+ scriptsDir: path6.join(configDir, "scripts"),
615
603
  outputStylesDir: null,
616
604
  profilesDir: null
617
605
  };
@@ -669,12 +657,12 @@ ${agent.body}`,
669
657
  ctx.warnings.push(`opencode: hook sin {{SCRIPTS_DIR}} no traducible: ${hook.command}`);
670
658
  continue;
671
659
  }
672
- const script = `scripts/${path7.basename(match[1])}`;
660
+ const script = `scripts/${path6.basename(match[1])}`;
673
661
  (bashEntries[includes] ??= []).push(script);
674
662
  }
675
663
  }
676
664
  }
677
- const hooksFile = path7.join(ctx.configDir, "hooks.json");
665
+ const hooksFile = path6.join(ctx.configDir, "hooks.json");
678
666
  const content = upsertJson(readTextIfExists(hooksFile), (root) => {
679
667
  const afterValue = root["tool.execute.after"] ??= {};
680
668
  if (afterValue === null || typeof afterValue !== "object" || Array.isArray(afterValue)) {
@@ -709,16 +697,16 @@ ${agent.body}`,
709
697
  }
710
698
  });
711
699
  actions.push({ kind: "write", target: hooksFile, content });
712
- const scriptsSource = path7.join(ctx.stackDir, "scripts");
700
+ const scriptsSource = path6.join(ctx.stackDir, "scripts");
713
701
  if (fs3.existsSync(scriptsSource)) {
714
702
  for (const f of fs3.readdirSync(scriptsSource)) {
715
- actions.push({ kind: "copy", source: path7.join(scriptsSource, f), target: path7.join(scriptsDir, f) });
703
+ actions.push({ kind: "copy", source: path6.join(scriptsSource, f), target: path6.join(scriptsDir, f) });
716
704
  }
717
705
  }
718
706
  return actions;
719
707
  },
720
708
  planMainConfig(canonical, ctx) {
721
- const file = path7.join(ctx.configDir, "opencode.json");
709
+ const file = path6.join(ctx.configDir, "opencode.json");
722
710
  const { pluginsDir } = this.paths(ctx.configDir);
723
711
  const original = readTextIfExists(file);
724
712
  const contentSource = original === null || original.trim() === "" ? null : original;
@@ -830,7 +818,7 @@ ${agent.body}`,
830
818
  content = removeMarkdownSection(content, "browser");
831
819
  actions.push({ kind: "write", target: systemPromptFile, content });
832
820
  }
833
- const configFile = path7.join(ctx.configDir, "opencode.json");
821
+ const configFile = path6.join(ctx.configDir, "opencode.json");
834
822
  const config = readTextIfExists(configFile);
835
823
  if (config !== null) {
836
824
  const mcpOwnership = [];
@@ -898,7 +886,7 @@ ${agent.body}`,
898
886
  ...primaryModelOwnership.length > 0 ? { primaryModelOwnership } : {}
899
887
  });
900
888
  }
901
- const hooksFile = path7.join(ctx.configDir, "hooks.json");
889
+ const hooksFile = path6.join(ctx.configDir, "hooks.json");
902
890
  const hooksJson = readTextIfExists(hooksFile);
903
891
  if (hooksJson !== null) {
904
892
  const ourScripts = hookScriptNames(hooks);
@@ -924,7 +912,7 @@ ${agent.body}`,
924
912
  };
925
913
 
926
914
  // src/adapters/claude-code.ts
927
- import path8 from "path";
915
+ import path7 from "path";
928
916
  import fs4 from "fs";
929
917
  function yamlString2(value) {
930
918
  return JSON.stringify(value);
@@ -945,8 +933,8 @@ function toolsFor(agent) {
945
933
  return tools.join(", ");
946
934
  }
947
935
  function hasEngramPlugin(configDir) {
948
- if (fs4.existsSync(path8.join(configDir, "plugins", "marketplaces", "engram"))) return true;
949
- const registry = readTextIfExists(path8.join(configDir, "plugins", "installed_plugins.json"));
936
+ if (fs4.existsSync(path7.join(configDir, "plugins", "marketplaces", "engram"))) return true;
937
+ const registry = readTextIfExists(path7.join(configDir, "plugins", "installed_plugins.json"));
950
938
  if (registry === null) return false;
951
939
  try {
952
940
  const parsed = JSON.parse(registry);
@@ -973,13 +961,13 @@ var claudeCodeAdapter = {
973
961
  },
974
962
  paths(configDir) {
975
963
  return {
976
- systemPromptFile: path8.join(configDir, "CLAUDE.md"),
977
- agentsDir: path8.join(configDir, "agents"),
978
- skillsDir: path8.join(configDir, "skills"),
979
- commandsDir: path8.join(configDir, "commands"),
964
+ systemPromptFile: path7.join(configDir, "CLAUDE.md"),
965
+ agentsDir: path7.join(configDir, "agents"),
966
+ skillsDir: path7.join(configDir, "skills"),
967
+ commandsDir: path7.join(configDir, "commands"),
980
968
  pluginsDir: null,
981
- scriptsDir: path8.join(configDir, "scripts"),
982
- outputStylesDir: path8.join(configDir, "output-styles"),
969
+ scriptsDir: path7.join(configDir, "scripts"),
970
+ outputStylesDir: path7.join(configDir, "output-styles"),
983
971
  profilesDir: null
984
972
  };
985
973
  },
@@ -1025,9 +1013,9 @@ ${agent.body}`,
1025
1013
  planHooks(canonical, ctx) {
1026
1014
  const actions = [];
1027
1015
  const { scriptsDir } = this.paths(ctx.configDir);
1028
- const original = readTextIfExists(path8.join(ctx.configDir, "settings.json"));
1016
+ const original = readTextIfExists(path7.join(ctx.configDir, "settings.json"));
1029
1017
  const contentSource = original === null || original.trim() === "" ? null : original;
1030
- const settingsFile = path8.join(ctx.configDir, "settings.json");
1018
+ const settingsFile = path7.join(ctx.configDir, "settings.json");
1031
1019
  let content = upsertNativeHooks(contentSource, canonical, scriptsDir);
1032
1020
  const defaults = loadCanonicalDefaults(ctx.stackDir)["claude-code"];
1033
1021
  if (contentSource === null && defaults?.["permissions"] !== void 0) {
@@ -1041,16 +1029,16 @@ ${agent.body}`,
1041
1029
  });
1042
1030
  }
1043
1031
  actions.push({ kind: "write", target: settingsFile, content });
1044
- const scriptsSource = path8.join(ctx.stackDir, "scripts");
1032
+ const scriptsSource = path7.join(ctx.stackDir, "scripts");
1045
1033
  if (fs4.existsSync(scriptsSource)) {
1046
1034
  for (const f of fs4.readdirSync(scriptsSource)) {
1047
- actions.push({ kind: "copy", source: path8.join(scriptsSource, f), target: path8.join(scriptsDir, f) });
1035
+ actions.push({ kind: "copy", source: path7.join(scriptsSource, f), target: path7.join(scriptsDir, f) });
1048
1036
  }
1049
1037
  }
1050
1038
  return actions;
1051
1039
  },
1052
1040
  planMainConfig(canonical, ctx) {
1053
- const file = path8.join(path8.dirname(ctx.configDir), `${path8.basename(ctx.configDir)}.json`);
1041
+ const file = path7.join(path7.dirname(ctx.configDir), `${path7.basename(ctx.configDir)}.json`);
1054
1042
  const mcpOwnership = [];
1055
1043
  const content = upsertJson(readTextIfExists(file), (root) => {
1056
1044
  const servers = root["mcpServers"] ??= {};
@@ -1115,7 +1103,7 @@ ${agent.body}`,
1115
1103
  content = removeMarkdownSection(content, "browser");
1116
1104
  actions.push({ kind: "write", target: systemPromptFile, content });
1117
1105
  }
1118
- const settingsFile = path8.join(ctx.configDir, "settings.json");
1106
+ const settingsFile = path7.join(ctx.configDir, "settings.json");
1119
1107
  const settings = readTextIfExists(settingsFile);
1120
1108
  if (settings !== null) {
1121
1109
  const content = removeNativeHooks(settings, hooks);
@@ -1123,7 +1111,7 @@ ${agent.body}`,
1123
1111
  actions.push({ kind: "write", target: settingsFile, content: content.trim() === "{}" ? "" : content });
1124
1112
  }
1125
1113
  }
1126
- const mainFile = path8.join(path8.dirname(ctx.configDir), `${path8.basename(ctx.configDir)}.json`);
1114
+ const mainFile = path7.join(path7.dirname(ctx.configDir), `${path7.basename(ctx.configDir)}.json`);
1127
1115
  const main2 = readTextIfExists(mainFile);
1128
1116
  if (main2 !== null) {
1129
1117
  const mcpOwnership = [];
@@ -1149,7 +1137,7 @@ ${agent.body}`,
1149
1137
  };
1150
1138
 
1151
1139
  // src/adapters/codex.ts
1152
- import path9 from "path";
1140
+ import path8 from "path";
1153
1141
  import fs5 from "fs";
1154
1142
  function tomlString(value) {
1155
1143
  return JSON.stringify(value);
@@ -1173,15 +1161,15 @@ function isManagedOptionalStdioServer3(server, section) {
1173
1161
  return server.optional === true && server.transport === "stdio" && section?.trim() === stdioMcpSection(server);
1174
1162
  }
1175
1163
  function hasActiveEngramPlugin(configDir) {
1176
- const config = readTextIfExists(path9.join(configDir, "config.toml"));
1164
+ const config = readTextIfExists(path8.join(configDir, "config.toml"));
1177
1165
  if (config === null) return false;
1178
1166
  const match = /\[plugins\."engram@[^"]*"\]([^[]*)/.exec(config);
1179
1167
  return match !== null && !/enabled\s*=\s*false/.test(match[1]);
1180
1168
  }
1181
1169
  function hasEngramProtocol(configDir) {
1182
1170
  if (hasActiveEngramPlugin(configDir)) return true;
1183
- if (fs5.existsSync(path9.join(configDir, "engram-instructions.md"))) return true;
1184
- const config = readTextIfExists(path9.join(configDir, "config.toml"));
1171
+ if (fs5.existsSync(path8.join(configDir, "engram-instructions.md"))) return true;
1172
+ const config = readTextIfExists(path8.join(configDir, "config.toml"));
1185
1173
  return config !== null && /engram-instructions\.md/.test(config);
1186
1174
  }
1187
1175
  var codexAdapter = {
@@ -1192,18 +1180,18 @@ var codexAdapter = {
1192
1180
  return !hasEngramProtocol(ctx.configDir);
1193
1181
  },
1194
1182
  paths(configDir) {
1195
- const isRealConfigDir = samePath(configDir, process.env.CODEX_HOME ?? path9.join(HOME, ".codex"));
1196
- const agentsHome = isRealConfigDir ? HOME : path9.dirname(configDir);
1197
- const skillsDir = path9.join(agentsHome, ".agents", "skills");
1183
+ const isRealConfigDir = samePath(configDir, process.env.CODEX_HOME ?? path8.join(HOME, ".codex"));
1184
+ const agentsHome = isRealConfigDir ? HOME : path8.dirname(configDir);
1185
+ const skillsDir = path8.join(agentsHome, ".agents", "skills");
1198
1186
  return {
1199
- systemPromptFile: path9.join(configDir, "AGENTS.md"),
1200
- agentsDir: path9.join(configDir, "agents"),
1187
+ systemPromptFile: path8.join(configDir, "AGENTS.md"),
1188
+ agentsDir: path8.join(configDir, "agents"),
1201
1189
  skillsDir,
1202
1190
  // Los custom prompts de Codex están deprecados: los commands se
1203
1191
  // instalan como skills (renderCommand produce <nombre>/SKILL.md).
1204
1192
  commandsDir: skillsDir,
1205
1193
  pluginsDir: null,
1206
- scriptsDir: path9.join(configDir, "scripts"),
1194
+ scriptsDir: path8.join(configDir, "scripts"),
1207
1195
  outputStylesDir: null,
1208
1196
  profilesDir: configDir
1209
1197
  };
@@ -1251,7 +1239,7 @@ ${body}`
1251
1239
  planHooks(canonical, ctx) {
1252
1240
  const actions = [];
1253
1241
  const { scriptsDir } = this.paths(ctx.configDir);
1254
- const hooksFile = path9.join(ctx.configDir, "hooks.json");
1242
+ const hooksFile = path8.join(ctx.configDir, "hooks.json");
1255
1243
  const content = upsertNativeHooks(
1256
1244
  readTextIfExists(hooksFile),
1257
1245
  canonical,
@@ -1262,16 +1250,16 @@ ${body}`
1262
1250
  ctx.warnings.push(
1263
1251
  "Codex: los hooks no-managed requieren aprobaci\xF3n manual \u2014 ejecuta /hooks dentro de codex para activarlos."
1264
1252
  );
1265
- const scriptsSource = path9.join(ctx.stackDir, "scripts");
1253
+ const scriptsSource = path8.join(ctx.stackDir, "scripts");
1266
1254
  if (fs5.existsSync(scriptsSource)) {
1267
1255
  for (const f of fs5.readdirSync(scriptsSource)) {
1268
- actions.push({ kind: "copy", source: path9.join(scriptsSource, f), target: path9.join(scriptsDir, f) });
1256
+ actions.push({ kind: "copy", source: path8.join(scriptsSource, f), target: path8.join(scriptsDir, f) });
1269
1257
  }
1270
1258
  }
1271
1259
  return actions;
1272
1260
  },
1273
1261
  planMainConfig(canonical, ctx) {
1274
- const file = path9.join(ctx.configDir, "config.toml");
1262
+ const file = path8.join(ctx.configDir, "config.toml");
1275
1263
  const original = readTextIfExists(file);
1276
1264
  const contentSource = original === null || original.trim() === "" ? null : original;
1277
1265
  let content = contentSource;
@@ -1407,7 +1395,7 @@ args = [${(server.args ?? []).map(tomlString).join(", ")}]`);
1407
1395
  content = removeMarkdownSection(content, "browser");
1408
1396
  actions.push({ kind: "write", target: systemPromptFile, content });
1409
1397
  }
1410
- const configFile = path9.join(ctx.configDir, "config.toml");
1398
+ const configFile = path8.join(ctx.configDir, "config.toml");
1411
1399
  const config = readTextIfExists(configFile);
1412
1400
  if (config !== null) {
1413
1401
  let content = config;
@@ -1442,7 +1430,7 @@ args = [${(server.args ?? []).map(tomlString).join(", ")}]`);
1442
1430
  ...primaryModelOwnership.length > 0 ? { primaryModelOwnership } : {}
1443
1431
  });
1444
1432
  }
1445
- const hooksFile = path9.join(ctx.configDir, "hooks.json");
1433
+ const hooksFile = path8.join(ctx.configDir, "hooks.json");
1446
1434
  const hooksJson = readTextIfExists(hooksFile);
1447
1435
  if (hooksJson !== null) {
1448
1436
  const content = removeNativeHooks(hooksJson, hooks);
@@ -1456,7 +1444,7 @@ args = [${(server.args ?? []).map(tomlString).join(", ")}]`);
1456
1444
 
1457
1445
  // src/lib/install-mode.ts
1458
1446
  import fs6 from "fs";
1459
- import path10 from "path";
1447
+ import path9 from "path";
1460
1448
  var INSTALL_MODES = /* @__PURE__ */ new Set(["human", "programmatic"]);
1461
1449
  var SUBAGENT_CONCURRENCIES = /* @__PURE__ */ new Set(["serial", "parallel"]);
1462
1450
  var DEFAULT_INSTALL_MODE_PREFERENCE = {
@@ -1464,7 +1452,7 @@ var DEFAULT_INSTALL_MODE_PREFERENCE = {
1464
1452
  subagentConcurrency: "serial"
1465
1453
  };
1466
1454
  function installModePreferenceFile() {
1467
- return path10.join(dataDir(), "install-mode.json");
1455
+ return path9.join(dataDir(), "install-mode.json");
1468
1456
  }
1469
1457
  function hasInstallModePreference(file = installModePreferenceFile()) {
1470
1458
  return fs6.existsSync(file);
@@ -1539,11 +1527,11 @@ function parseInstallModePreferenceFlags(mode, subagentConcurrency) {
1539
1527
 
1540
1528
  // src/lib/backup.ts
1541
1529
  import fs7 from "fs";
1542
- import path11 from "path";
1530
+ import path10 from "path";
1543
1531
  import crypto from "crypto";
1544
1532
  var KEEP_BACKUPS = 10;
1545
1533
  function backupsRoot() {
1546
- return path11.join(dataDir(), "backups");
1534
+ return path10.join(dataDir(), "backups");
1547
1535
  }
1548
1536
  function compositeChecksum(files) {
1549
1537
  const hash = crypto.createHash("sha256");
@@ -1562,19 +1550,19 @@ function createBackup(files, label, root = backupsRoot()) {
1562
1550
  if (latest?.checksum === checksum) return latest;
1563
1551
  const base = `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}-${label}`;
1564
1552
  let id = base;
1565
- let dir = path11.join(root, id);
1553
+ let dir = path10.join(root, id);
1566
1554
  for (let n = 1; fs7.existsSync(dir); n++) {
1567
1555
  id = `${base}-${n}`;
1568
- dir = path11.join(root, id);
1556
+ dir = path10.join(root, id);
1569
1557
  }
1570
- ensureDir(path11.join(dir, "files"));
1558
+ ensureDir(path10.join(dir, "files"));
1571
1559
  const entries = existing.map((original, i) => {
1572
- const stored = path11.join(dir, "files", `${String(i).padStart(4, "0")}-${path11.basename(original)}`);
1560
+ const stored = path10.join(dir, "files", `${String(i).padStart(4, "0")}-${path10.basename(original)}`);
1573
1561
  fs7.copyFileSync(original, stored);
1574
1562
  return { original, stored };
1575
1563
  });
1576
1564
  const info = { id, label, createdAt: (/* @__PURE__ */ new Date()).toISOString(), files: entries, checksum };
1577
- writeText(path11.join(dir, "manifest.json"), JSON.stringify(info, null, 2) + "\n");
1565
+ writeText(path10.join(dir, "manifest.json"), JSON.stringify(info, null, 2) + "\n");
1578
1566
  pruneBackups(root);
1579
1567
  return info;
1580
1568
  }
@@ -1583,7 +1571,7 @@ function listBackups(root = backupsRoot()) {
1583
1571
  const infos = [];
1584
1572
  for (const entry of fs7.readdirSync(root, { withFileTypes: true })) {
1585
1573
  if (!entry.isDirectory()) continue;
1586
- const manifest = readTextIfExists(path11.join(root, entry.name, "manifest.json"));
1574
+ const manifest = readTextIfExists(path10.join(root, entry.name, "manifest.json"));
1587
1575
  if (manifest === null) continue;
1588
1576
  try {
1589
1577
  infos.push(JSON.parse(manifest));
@@ -1600,7 +1588,7 @@ function restoreBackup(id, root = backupsRoot(), boundary = HOME) {
1600
1588
  for (const { original, stored } of info.files) {
1601
1589
  if (!fs7.existsSync(stored)) continue;
1602
1590
  if (!isContainedIn(original, boundary)) continue;
1603
- ensureDir(path11.dirname(original));
1591
+ ensureDir(path10.dirname(original));
1604
1592
  fs7.copyFileSync(stored, original);
1605
1593
  restored++;
1606
1594
  }
@@ -1609,15 +1597,15 @@ function restoreBackup(id, root = backupsRoot(), boundary = HOME) {
1609
1597
  function pruneBackups(root) {
1610
1598
  const all = listBackups(root);
1611
1599
  for (const old of all.slice(KEEP_BACKUPS)) {
1612
- fs7.rmSync(path11.join(root, old.id), { recursive: true, force: true });
1600
+ fs7.rmSync(path10.join(root, old.id), { recursive: true, force: true });
1613
1601
  }
1614
1602
  }
1615
1603
 
1616
1604
  // src/lib/manifest.ts
1617
1605
  import fs8 from "fs";
1618
- import path12 from "path";
1606
+ import path11 from "path";
1619
1607
  function manifestFile() {
1620
- return path12.join(dataDir(), "manifest.json");
1608
+ return path11.join(dataDir(), "manifest.json");
1621
1609
  }
1622
1610
  function readManifest(file = manifestFile()) {
1623
1611
  const raw = readTextIfExists(file);
@@ -1641,24 +1629,24 @@ function removeRuntimeManifest(id, file = manifestFile()) {
1641
1629
  writeText(file, JSON.stringify(manifest, null, 2) + "\n");
1642
1630
  }
1643
1631
  function findOrphans(prevOwned, currentTargets, root = HOME) {
1644
- return prevOwned.map((f) => path12.resolve(f)).filter(
1645
- (f) => !currentTargets.has(f) && path12.basename(f) !== "engram.ts" && isContainedIn(f, root) && fs8.existsSync(f)
1632
+ return prevOwned.map((f) => path11.resolve(f)).filter(
1633
+ (f) => !currentTargets.has(f) && path11.basename(f) !== "engram.ts" && isContainedIn(f, root) && fs8.existsSync(f)
1646
1634
  );
1647
1635
  }
1648
1636
 
1649
1637
  // src/components/system-prompt.ts
1650
- import path14 from "path";
1638
+ import path13 from "path";
1651
1639
  import fs10 from "fs";
1652
1640
 
1653
1641
  // src/lib/mode-composition.ts
1654
1642
  import fs9 from "fs";
1655
- import path13 from "path";
1643
+ import path12 from "path";
1656
1644
  var PROGRAMMATIC_ROOT = ["modes", "programmatic"];
1657
1645
  var PROGRAMMATIC_MARKER = "<!-- jorgex:programmatic-mode -->";
1658
1646
  var LEGACY_RESULT_CONTRACT_SECTION = /\n?##\s+Result contract[\s\S]*$/;
1659
1647
  var normalize = (value) => value.replace(/\r\n/g, "\n");
1660
1648
  function loadProgrammaticAddendum(stackDir, fileName) {
1661
- return normalize(fs9.readFileSync(path13.join(stackDir, ...PROGRAMMATIC_ROOT, fileName), "utf8")).trim();
1649
+ return normalize(fs9.readFileSync(path12.join(stackDir, ...PROGRAMMATIC_ROOT, fileName), "utf8")).trim();
1662
1650
  }
1663
1651
  function appendAddendum(base, addendum) {
1664
1652
  const normalizedBase = normalize(base);
@@ -1701,15 +1689,15 @@ function composeProgrammaticAgentBody(stackDir, agent, mode, concurrency) {
1701
1689
  var normalize2 = (s) => s.replace(/\r\n/g, "\n");
1702
1690
  function planSystemPrompt(adapter, ctx) {
1703
1691
  const target = adapter.paths(ctx.configDir).systemPromptFile;
1704
- const agentsMd = normalize2(fs10.readFileSync(path14.join(ctx.stackDir, "system-prompt", "AGENTS.md"), "utf8"));
1692
+ const agentsMd = normalize2(fs10.readFileSync(path13.join(ctx.stackDir, "system-prompt", "AGENTS.md"), "utf8"));
1705
1693
  const protocol = stripLeadingHtmlComments(
1706
- normalize2(fs10.readFileSync(path14.join(ctx.stackDir, "system-prompt", "engram-protocol.md"), "utf8"))
1694
+ normalize2(fs10.readFileSync(path13.join(ctx.stackDir, "system-prompt", "engram-protocol.md"), "utf8"))
1707
1695
  );
1708
1696
  const composedAgentsMd = composeProgrammaticSystemPrompt(ctx.stackDir, agentsMd, ctx.mode);
1709
1697
  const browser = [
1710
1698
  ctx.playwrightCliEnabled ? "browser-playwright.md" : null,
1711
1699
  ctx.enabledMcpServers?.has(DEVTOOLS_MCP_SERVER) ? "browser-chrome-devtools.md" : null
1712
- ].filter((file) => file !== null).map((file) => normalize2(fs10.readFileSync(path14.join(ctx.stackDir, "system-prompt", file), "utf8"))).join("\n\n");
1700
+ ].filter((file) => file !== null).map((file) => normalize2(fs10.readFileSync(path13.join(ctx.stackDir, "system-prompt", file), "utf8"))).join("\n\n");
1713
1701
  let content = readTextIfExists(target);
1714
1702
  content = upsertMarkdownSection(content, "system-prompt", composedAgentsMd);
1715
1703
  if (adapter.injectEngramProtocol(ctx)) {
@@ -1722,7 +1710,7 @@ function planSystemPrompt(adapter, ctx) {
1722
1710
  }
1723
1711
 
1724
1712
  // src/components/agents.ts
1725
- import path15 from "path";
1713
+ import path14 from "path";
1726
1714
  function planAgents(adapter, ctx) {
1727
1715
  const { agentsDir, commandsDir, outputStylesDir, profilesDir, scriptsDir } = adapter.paths(ctx.configDir);
1728
1716
  const dirFor = {
@@ -1732,7 +1720,7 @@ function planAgents(adapter, ctx) {
1732
1720
  profile: profilesDir
1733
1721
  };
1734
1722
  const scriptsBase = scriptsDir.replace(/\\/g, "/");
1735
- return loadCanonicalAgents(path15.join(ctx.stackDir, "agents")).flatMap((agent) => {
1723
+ return loadCanonicalAgents(path14.join(ctx.stackDir, "agents")).flatMap((agent) => {
1736
1724
  const composedAgent = {
1737
1725
  ...agent,
1738
1726
  body: composeProgrammaticAgentBody(ctx.stackDir, agent, ctx.mode, ctx.subagentConcurrency)
@@ -1744,42 +1732,42 @@ function planAgents(adapter, ctx) {
1744
1732
  return [];
1745
1733
  }
1746
1734
  const content = rendered.content.replace(/\{\{SCRIPTS_DIR\}\}/g, scriptsBase);
1747
- return [{ kind: "write", target: path15.join(dir, rendered.file), content }];
1735
+ return [{ kind: "write", target: path14.join(dir, rendered.file), content }];
1748
1736
  });
1749
1737
  });
1750
1738
  }
1751
1739
 
1752
1740
  // src/components/skills.ts
1753
- import path16 from "path";
1741
+ import path15 from "path";
1754
1742
  function planSkills(adapter, ctx) {
1755
1743
  const { skillsDir } = adapter.paths(ctx.configDir);
1756
- const source = path16.join(ctx.stackDir, "skills");
1744
+ const source = path15.join(ctx.stackDir, "skills");
1757
1745
  return listFilesRecursive(source).map((file) => {
1758
- const relative = path16.relative(source, file);
1759
- return { kind: "copy", source: file, target: path16.join(skillsDir, relative) };
1746
+ const relative = path15.relative(source, file);
1747
+ return { kind: "copy", source: file, target: path15.join(skillsDir, relative) };
1760
1748
  });
1761
1749
  }
1762
1750
 
1763
1751
  // src/components/commands.ts
1764
- import path17 from "path";
1752
+ import path16 from "path";
1765
1753
  import fs11 from "fs";
1766
1754
  function planCommands(adapter, ctx) {
1767
1755
  const { commandsDir } = adapter.paths(ctx.configDir);
1768
- const source = path17.join(ctx.stackDir, "commands");
1756
+ const source = path16.join(ctx.stackDir, "commands");
1769
1757
  if (!fs11.existsSync(source)) return [];
1770
1758
  const commandFiles = [
1771
1759
  ...listMarkdownFiles(source),
1772
- ...listMarkdownFiles(path17.join(source, adapter.id))
1760
+ ...listMarkdownFiles(path16.join(source, adapter.id))
1773
1761
  ];
1774
1762
  return commandFiles.map(({ file, fullPath }) => {
1775
1763
  const raw = fs11.readFileSync(fullPath, "utf8").replace(/\r\n/g, "\n");
1776
1764
  const rendered = adapter.renderCommand(file, raw);
1777
- return { kind: "write", target: path17.join(commandsDir, rendered.file), content: rendered.content };
1765
+ return { kind: "write", target: path16.join(commandsDir, rendered.file), content: rendered.content };
1778
1766
  });
1779
1767
  }
1780
1768
  function listMarkdownFiles(dir) {
1781
1769
  if (!fs11.existsSync(dir)) return [];
1782
- return fs11.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => ({ file: entry.name, fullPath: path17.join(dir, entry.name) }));
1770
+ return fs11.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => ({ file: entry.name, fullPath: path16.join(dir, entry.name) }));
1783
1771
  }
1784
1772
 
1785
1773
  // src/components/hooks.ts
@@ -1793,15 +1781,15 @@ function planMcp(adapter, ctx) {
1793
1781
  }
1794
1782
 
1795
1783
  // src/components/plugins.ts
1796
- import path18 from "path";
1784
+ import path17 from "path";
1797
1785
  import fs12 from "fs";
1798
1786
  function planPlugins(adapter, ctx) {
1799
1787
  const { pluginsDir } = adapter.paths(ctx.configDir);
1800
1788
  if (pluginsDir === null) return [];
1801
- const source = path18.join(ctx.stackDir, "plugins", adapter.id);
1789
+ const source = path17.join(ctx.stackDir, "plugins", adapter.id);
1802
1790
  if (!fs12.existsSync(source)) return [];
1803
1791
  return listFilesRecursive(source).filter((f) => f.endsWith(".ts")).map((sourceFile) => {
1804
- const target = path18.join(pluginsDir, path18.relative(source, sourceFile));
1792
+ const target = path17.join(pluginsDir, path17.relative(source, sourceFile));
1805
1793
  const raw = fs12.readFileSync(sourceFile, "utf8");
1806
1794
  let content = raw;
1807
1795
  if (content.includes('"{{ENGRAM_BIN}}"')) {
@@ -1809,7 +1797,7 @@ function planPlugins(adapter, ctx) {
1809
1797
  }
1810
1798
  if (content.includes('"{{ENGRAM_PROTOCOL}}"')) {
1811
1799
  const protocol = stripLeadingHtmlComments(
1812
- fs12.readFileSync(path18.join(ctx.stackDir, "system-prompt", "engram-protocol.md"), "utf8").replace(/\r\n/g, "\n")
1800
+ fs12.readFileSync(path17.join(ctx.stackDir, "system-prompt", "engram-protocol.md"), "utf8").replace(/\r\n/g, "\n")
1813
1801
  );
1814
1802
  content = content.replace(/"\{\{ENGRAM_PROTOCOL\}\}"/g, JSON.stringify(protocol));
1815
1803
  }
@@ -1821,8 +1809,8 @@ function planPlugins(adapter, ctx) {
1821
1809
 
1822
1810
  // src/lib/external-tools.ts
1823
1811
  import fs13 from "fs";
1824
- import os2 from "os";
1825
- import path19 from "path";
1812
+ import os from "os";
1813
+ import path18 from "path";
1826
1814
  import { execFileSync as execFileSync2 } from "child_process";
1827
1815
  var PLAYWRIGHT_CLI = {
1828
1816
  packageName: "@playwright/cli",
@@ -1873,9 +1861,9 @@ function resolvePnpmBin() {
1873
1861
  const pnpmCmd = lookPath("pnpm.cmd");
1874
1862
  return pnpmCmd !== null && !pnpmCmd.toLowerCase().endsWith(".ps1") ? pnpmCmd : null;
1875
1863
  }
1876
- function isPlaywrightBrowserReady(env = process.env, platform = process.platform, homeDir = os2.homedir()) {
1864
+ function isPlaywrightBrowserReady(env = process.env, platform = process.platform, homeDir = os.homedir()) {
1877
1865
  const configuredPath = env.PLAYWRIGHT_BROWSERS_PATH;
1878
- 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"));
1866
+ const cacheDir = configuredPath ?? (platform === "win32" ? path18.join(env.LOCALAPPDATA ?? path18.join(homeDir, "AppData", "Local"), "ms-playwright") : platform === "darwin" ? path18.join(homeDir, "Library", "Caches", "ms-playwright") : path18.join(env.XDG_CACHE_HOME ?? path18.join(homeDir, ".cache"), "ms-playwright"));
1879
1867
  if (configuredPath === "0") return { status: "missing", path: cacheDir, errorCode: "DISABLED" };
1880
1868
  try {
1881
1869
  const ready = fs13.readdirSync(cacheDir, { withFileTypes: true }).some(
@@ -1923,7 +1911,7 @@ function executePlaywrightToolAction(action, pnpmBin = resolvePnpmBin()) {
1923
1911
 
1924
1912
  // src/lib/tool-preferences.ts
1925
1913
  import fs14 from "fs";
1926
- import path20 from "path";
1914
+ import path19 from "path";
1927
1915
  var PLAYWRIGHT_CLI_PREFERENCE_VERSION = 1;
1928
1916
  var DEVTOOLS_MCP_PREFERENCE_VERSION = 1;
1929
1917
  var PRIMARY_MODEL_OWNERSHIP_VERSION = 1;
@@ -1936,7 +1924,7 @@ function readPreference(file) {
1936
1924
  }
1937
1925
  }
1938
1926
  function playwrightCliPreferenceFile(stateDir = dataDir()) {
1939
- return path20.join(stateDir, "playwright-cli.json");
1927
+ return path19.join(stateDir, "playwright-cli.json");
1940
1928
  }
1941
1929
  function parsePlaywrightCliPreference(raw) {
1942
1930
  try {
@@ -1966,10 +1954,10 @@ function savePlaywrightCliPreference(file, enabled) {
1966
1954
  writeText(file, JSON.stringify({ version: PLAYWRIGHT_CLI_PREFERENCE_VERSION, enabled }) + "\n");
1967
1955
  }
1968
1956
  function devtoolsMcpPreferenceFile(stateDir = dataDir()) {
1969
- return path20.join(stateDir, "devtools-mcp.json");
1957
+ return path19.join(stateDir, "devtools-mcp.json");
1970
1958
  }
1971
1959
  function primaryModelOwnershipFile(stateDir = dataDir()) {
1972
- return path20.join(stateDir, "primary-model.json");
1960
+ return path19.join(stateDir, "primary-model.json");
1973
1961
  }
1974
1962
  function isRecord(value) {
1975
1963
  return value !== null && typeof value === "object" && !Array.isArray(value);
@@ -2079,7 +2067,7 @@ function primaryModelOwnershipError(file = primaryModelOwnershipFile()) {
2079
2067
  return `Primary model: ownership inv\xE1lido en ${file}. Corrige o borra ese archivo antes de reintentar.`;
2080
2068
  }
2081
2069
  function primaryModelConfigKey(configDir) {
2082
- const resolved = path20.resolve(configDir);
2070
+ const resolved = path19.resolve(configDir);
2083
2071
  return process.platform === "win32" ? resolved.toLowerCase() : resolved;
2084
2072
  }
2085
2073
  function loadPrimaryModelOwnership(file, runtime, configDir) {
@@ -2225,7 +2213,7 @@ function collectAllCurrentTargets(mode = DEFAULT_INSTALL_MODE_PREFERENCE) {
2225
2213
  continue;
2226
2214
  }
2227
2215
  try {
2228
- for (const action of buildPlan(adapter, ctx)) targets.add(path21.resolve(action.target));
2216
+ for (const action of buildPlan(adapter, ctx)) targets.add(path20.resolve(action.target));
2229
2217
  } catch (error) {
2230
2218
  complete = false;
2231
2219
  warnings.push(
@@ -2327,10 +2315,10 @@ async function runInstall(opts) {
2327
2315
  }
2328
2316
  const writeManifest = () => {
2329
2317
  if (!useManifest) return;
2330
- const unmergeTargets = new Set(adapter.planUnmerge(canonicalMcp, canonicalHooks, ctx).map((a) => path21.resolve(a.target)));
2318
+ const unmergeTargets = new Set(adapter.planUnmerge(canonicalMcp, canonicalHooks, ctx).map((a) => path20.resolve(a.target)));
2331
2319
  const keepTarget = (target) => !unmergeTargets.has(target);
2332
- const liveOwned = plan.map((a) => path21.resolve(a.target)).filter(keepTarget);
2333
- const previousOwned = (prevManifest?.owned ?? []).map((target) => path21.resolve(target)).filter(keepTarget);
2320
+ const liveOwned = plan.map((a) => path20.resolve(a.target)).filter(keepTarget);
2321
+ const previousOwned = (prevManifest?.owned ?? []).map((target) => path20.resolve(target)).filter(keepTarget);
2334
2322
  const owned = canOrphan ? liveOwned : [.../* @__PURE__ */ new Set([...previousOwned, ...liveOwned])];
2335
2323
  writeRuntimeManifest(id, { configDir, owned, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
2336
2324
  };
@@ -2359,7 +2347,7 @@ async function runInstall(opts) {
2359
2347
  const backup = useManifest ? createBackup([...updates.map((c) => c.action.target), ...orphans], `install-${id}`) : null;
2360
2348
  if (backup) p.log.info(`Backup: ${backup.id} (${backup.files.length} archivos)`);
2361
2349
  applyChanges(changes, useManifest ? (action) => persistConfigurationOwnershipChanges(id, configDir, [action]) : void 0);
2362
- const pruneRoot = useManifest ? HOME : path21.dirname(configDir);
2350
+ const pruneRoot = useManifest ? HOME : path20.dirname(configDir);
2363
2351
  for (const orphan of orphans) {
2364
2352
  fs15.rmSync(orphan, { force: true });
2365
2353
  pruneEmptyDirs(orphan, pruneRoot);
@@ -2444,27 +2432,27 @@ async function runInstall(opts) {
2444
2432
 
2445
2433
  // src/uninstall.ts
2446
2434
  import fs17 from "fs";
2447
- import path25 from "path";
2435
+ import path24 from "path";
2448
2436
  import * as p2 from "@clack/prompts";
2449
2437
 
2450
2438
  // src/lib/pi-projection-lifecycle.ts
2451
2439
  import fs16 from "fs";
2452
- import path24 from "path";
2440
+ import path23 from "path";
2453
2441
 
2454
2442
  // src/adapters/pi.ts
2455
- import path22 from "path";
2443
+ import path21 from "path";
2456
2444
  var piAdapter = {
2457
2445
  id: "pi",
2458
2446
  paths(configDir) {
2459
- const piConfigDir = process.env.PI_CODING_AGENT_DIR ?? path22.join(HOME, ".pi", "agent");
2460
- const agentsHome = samePath(configDir, piConfigDir) ? HOME : path22.join(path22.dirname(configDir), "home");
2447
+ const piConfigDir = process.env.PI_CODING_AGENT_DIR ?? path21.join(HOME, ".pi", "agent");
2448
+ const agentsHome = samePath(configDir, piConfigDir) ? HOME : path21.join(path21.dirname(configDir), "home");
2461
2449
  return {
2462
- systemPromptFile: path22.join(configDir, "AGENTS.md"),
2463
- agentsDir: path22.join(configDir, "agents"),
2464
- skillsDir: path22.join(agentsHome, ".agents", "skills"),
2465
- commandsDir: path22.join(configDir, "prompts"),
2450
+ systemPromptFile: path21.join(configDir, "AGENTS.md"),
2451
+ agentsDir: path21.join(configDir, "agents"),
2452
+ skillsDir: path21.join(agentsHome, ".agents", "skills"),
2453
+ commandsDir: path21.join(configDir, "prompts"),
2466
2454
  pluginsDir: null,
2467
- scriptsDir: path22.join(configDir, "scripts"),
2455
+ scriptsDir: path21.join(configDir, "scripts"),
2468
2456
  outputStylesDir: null,
2469
2457
  profilesDir: null
2470
2458
  };
@@ -2478,7 +2466,7 @@ var piAdapter = {
2478
2466
  };
2479
2467
 
2480
2468
  // src/lib/pi-package-lifecycle.ts
2481
- import path23 from "path";
2469
+ import path22 from "path";
2482
2470
  var REQUIRED_CAPABILITIES = /* @__PURE__ */ new Set([
2483
2471
  "foundation-contract-v1",
2484
2472
  "runner-json-v1",
@@ -2610,7 +2598,7 @@ function parseReceiptShape(receiptJson) {
2610
2598
  return null;
2611
2599
  }
2612
2600
  if (engram === void 0) return "upgrade-required";
2613
- if (engram === null || typeof engram !== "object" || Array.isArray(engram) || typeof Reflect.get(engram, "binary") !== "string" || !path23.isAbsolute(Reflect.get(engram, "binary"))) {
2601
+ if (engram === null || typeof engram !== "object" || Array.isArray(engram) || typeof Reflect.get(engram, "binary") !== "string" || !path22.isAbsolute(Reflect.get(engram, "binary"))) {
2614
2602
  return null;
2615
2603
  }
2616
2604
  return parsed;
@@ -2647,7 +2635,7 @@ function planPiPackageLifecycle(input) {
2647
2635
  if (input.receiptJson !== null) {
2648
2636
  const parsedReceipt = parseReceipt(input.receiptJson, input.candidate, {
2649
2637
  kind: input.scope.kind,
2650
- codingAgentDir: path23.resolve(input.scope.codingAgentDir)
2638
+ codingAgentDir: path22.resolve(input.scope.codingAgentDir)
2651
2639
  }, input.engramBin);
2652
2640
  if (parsedReceipt === "upgrade-required") return blocked(input, "receipt-upgrade-required");
2653
2641
  if (parsedReceipt === null) return blocked(input, "receipt-corrupt");
@@ -2682,7 +2670,7 @@ function planPiPackageLifecycle(input) {
2682
2670
  },
2683
2671
  receipt: expectedReceipt(input.candidate, "installing", {
2684
2672
  kind: input.scope.kind,
2685
- codingAgentDir: path23.resolve(input.scope.codingAgentDir)
2673
+ codingAgentDir: path22.resolve(input.scope.codingAgentDir)
2686
2674
  }, input.engramBin),
2687
2675
  ownership: ownership(true)
2688
2676
  };
@@ -2697,7 +2685,7 @@ function parseRunnerRecord(stdout, stderr, command, candidate, packageRunner) {
2697
2685
  const parsed = JSON.parse(body);
2698
2686
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return null;
2699
2687
  const record = parsed;
2700
- 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")) {
2688
+ 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" || !path22.isAbsolute(record.package.root) || path22.resolve(packageRunner) !== path22.resolve(record.package.root, "bin", "jorgex-pi.mjs")) {
2701
2689
  return null;
2702
2690
  }
2703
2691
  return record;
@@ -2783,10 +2771,10 @@ function validateOwnedOperationState(input) {
2783
2771
  }))) {
2784
2772
  return { kind: "blocked", reason: "receipt-untrusted" };
2785
2773
  }
2786
- if (receipt.scope.kind !== (input.paths.targetDir ? "target-dir" : "real") || path23.resolve(receipt.scope.codingAgentDir) !== path23.resolve(input.paths.codingAgentDir)) {
2774
+ if (receipt.scope.kind !== (input.paths.targetDir ? "target-dir" : "real") || path22.resolve(receipt.scope.codingAgentDir) !== path22.resolve(input.paths.codingAgentDir)) {
2787
2775
  return { kind: "blocked", reason: "source-divergent" };
2788
2776
  }
2789
- if (input.engramBin !== null && path23.resolve(receipt.engram.binary) !== path23.resolve(input.engramBin)) {
2777
+ if (input.engramBin !== null && path22.resolve(receipt.engram.binary) !== path22.resolve(input.engramBin)) {
2790
2778
  return { kind: "blocked", reason: "receipt-corrupt" };
2791
2779
  }
2792
2780
  const source = receipt.candidate.package.source;
@@ -2883,18 +2871,18 @@ var preparedPiProjectionUninstalls = /* @__PURE__ */ new WeakMap();
2883
2871
  function projectionScope(scope) {
2884
2872
  return {
2885
2873
  ...scope,
2886
- settingsFile: path24.join(scope.codingAgentDir, "settings.json")
2874
+ settingsFile: path23.join(scope.codingAgentDir, "settings.json")
2887
2875
  };
2888
2876
  }
2889
2877
  function isInside(root, file) {
2890
- const relative = path24.relative(path24.resolve(root), path24.resolve(file));
2891
- return relative === "" || relative !== ".." && !relative.startsWith(`..${path24.sep}`) && !path24.isAbsolute(relative);
2878
+ const relative = path23.relative(path23.resolve(root), path23.resolve(file));
2879
+ return relative === "" || relative !== ".." && !relative.startsWith(`..${path23.sep}`) && !path23.isAbsolute(relative);
2892
2880
  }
2893
2881
  function isManagedPath(scope, file) {
2894
2882
  return isInside(scope.home, file) || isInside(scope.codingAgentDir, file);
2895
2883
  }
2896
2884
  function isAllowedPath(scope, file) {
2897
- return isManagedPath(scope, file) || path24.resolve(file) === path24.resolve(scope.receiptFile);
2885
+ return isManagedPath(scope, file) || path23.resolve(file) === path23.resolve(scope.receiptFile);
2898
2886
  }
2899
2887
  function projectedPiAdapter(scope) {
2900
2888
  const paths = piAdapter.paths(scope.codingAgentDir);
@@ -2902,7 +2890,7 @@ function projectedPiAdapter(scope) {
2902
2890
  ...piAdapter,
2903
2891
  paths: () => ({
2904
2892
  ...paths,
2905
- skillsDir: path24.join(scope.home, ".agents", "skills")
2893
+ skillsDir: path23.join(scope.home, ".agents", "skills")
2906
2894
  })
2907
2895
  };
2908
2896
  }
@@ -2942,9 +2930,9 @@ function assertPlanContained(plan, scope) {
2942
2930
  }
2943
2931
  }
2944
2932
  function receiptFor(plan, scope) {
2945
- const systemPrompt = path24.join(scope.codingAgentDir, "AGENTS.md");
2933
+ const systemPrompt = path23.join(scope.codingAgentDir, "AGENTS.md");
2946
2934
  const owned = [...new Set(
2947
- plan.map((action) => path24.resolve(action.target)).filter((target) => target !== systemPrompt)
2935
+ plan.map((action) => path23.resolve(action.target)).filter((target) => target !== systemPrompt)
2948
2936
  )];
2949
2937
  return {
2950
2938
  schemaVersion: 1,
@@ -2996,8 +2984,8 @@ function realPiProjectionScope() {
2996
2984
  return {
2997
2985
  kind: "real",
2998
2986
  home: HOME,
2999
- codingAgentDir: process.env.PI_CODING_AGENT_DIR ?? path24.join(HOME, ".pi", "agent"),
3000
- receiptFile: path24.join(dataDir(), "pi-projection-receipt.json")
2987
+ codingAgentDir: process.env.PI_CODING_AGENT_DIR ?? path23.join(HOME, ".pi", "agent"),
2988
+ receiptFile: path23.join(dataDir(), "pi-projection-receipt.json")
3001
2989
  };
3002
2990
  }
3003
2991
  function readTextOnlyIfMissing(file) {
@@ -3035,13 +3023,13 @@ function manifestOwned(manifest) {
3035
3023
  return new Set([
3036
3024
  ...manifest.runtimes.codex?.owned ?? [],
3037
3025
  ...manifest.runtimes.opencode?.owned ?? []
3038
- ].map((file) => path24.resolve(file)));
3026
+ ].map((file) => path23.resolve(file)));
3039
3027
  }
3040
3028
  function withoutManagedPromptSections(content) {
3041
3029
  return ["system-prompt", "engram-protocol", "browser"].reduce((current, section) => removeMarkdownSection(current, section), content);
3042
3030
  }
3043
3031
  function uniquePaths(paths) {
3044
- return [...new Set(paths.map((file) => path24.resolve(file)))];
3032
+ return [...new Set(paths.map((file) => path23.resolve(file)))];
3045
3033
  }
3046
3034
  function blocked2(reason, paths, remedy) {
3047
3035
  return { kind: "blocked", reason, paths: uniquePaths(paths), remedy };
@@ -3114,7 +3102,7 @@ function preparePiProjectionUninstall(input, deps) {
3114
3102
  }
3115
3103
  const scope = projectionScope(input.scope);
3116
3104
  const expectedReceipt2 = expectedProjectionReceipt(input, scope);
3117
- const prompt = path24.join(scope.codingAgentDir, "AGENTS.md");
3105
+ const prompt = path23.join(scope.codingAgentDir, "AGENTS.md");
3118
3106
  let existingPrompt;
3119
3107
  try {
3120
3108
  existingPrompt = deps.readText(prompt);
@@ -3122,7 +3110,7 @@ function preparePiProjectionUninstall(input, deps) {
3122
3110
  return cleanupFailure([prompt]);
3123
3111
  }
3124
3112
  const promptContent = existingPrompt === null ? null : withoutManagedPromptSections(existingPrompt);
3125
- const promptUpdate = promptContent === null || promptContent === existingPrompt ? null : { file: path24.resolve(prompt), content: promptContent };
3113
+ const promptUpdate = promptContent === null || promptContent === existingPrompt ? null : { file: path23.resolve(prompt), content: promptContent };
3126
3114
  const receiptRead = readProjectionReceipt(scope.receiptFile, deps);
3127
3115
  if (receiptRead.kind === "unreadable") return receiptUnreadable(scope.receiptFile);
3128
3116
  let ownedToRemove = [];
@@ -3138,7 +3126,7 @@ function preparePiProjectionUninstall(input, deps) {
3138
3126
  return cleanupFailure([scope.receiptFile]);
3139
3127
  }
3140
3128
  ownedToRemove = receipt.owned.filter((owned) => !retained.has(owned));
3141
- receiptFile = path24.resolve(scope.receiptFile);
3129
+ receiptFile = path23.resolve(scope.receiptFile);
3142
3130
  backupTargets = [...backupTargets, ...receipt.owned, receiptFile];
3143
3131
  }
3144
3132
  const backup = backupExisting(backupTargets, deps);
@@ -3194,7 +3182,7 @@ function runPiProjectionLifecycle(input, deps) {
3194
3182
  const expectedReceipt2 = receiptContent(receipt);
3195
3183
  const drifted = plan.filter((action) => hasActionDrift(action, deps));
3196
3184
  if (input.operation === "doctor") {
3197
- const paths = drifted.map((action) => path24.resolve(action.target));
3185
+ const paths = drifted.map((action) => path23.resolve(action.target));
3198
3186
  const currentSettings2 = deps.readText(scope.settingsFile);
3199
3187
  if (currentSettings2 === null || filterProjectedPiPackage(currentSettings2, input.packageSource) !== currentSettings2) {
3200
3188
  paths.push(scope.settingsFile);
@@ -3225,12 +3213,12 @@ function runPiProjectionLifecycle(input, deps) {
3225
3213
  return { kind: "synced", changed: drifted.length > 0 || packageWillChange || receiptChanged };
3226
3214
  }
3227
3215
  function systemProjectionLifecycle(input) {
3228
- const targetRoot = input.targetDir === void 0 ? null : path24.resolve(input.targetDir);
3216
+ const targetRoot = input.targetDir === void 0 ? null : path23.resolve(input.targetDir);
3229
3217
  const scope = targetRoot === null ? realPiProjectionScope() : {
3230
3218
  kind: "target-dir",
3231
- home: path24.join(targetRoot, "home"),
3232
- codingAgentDir: path24.join(targetRoot, "pi-agent"),
3233
- receiptFile: path24.join(targetRoot, "state", "pi-projection-receipt.json")
3219
+ home: path23.join(targetRoot, "home"),
3220
+ codingAgentDir: path23.join(targetRoot, "pi-agent"),
3221
+ receiptFile: path23.join(targetRoot, "state", "pi-projection-receipt.json")
3234
3222
  };
3235
3223
  return {
3236
3224
  input: {
@@ -3246,7 +3234,7 @@ function systemProjectionLifecycle(input) {
3246
3234
  backup: (paths) => createBackup(
3247
3235
  paths,
3248
3236
  `pi-projection-${input.operation}`,
3249
- targetRoot === null ? void 0 : path24.join(targetRoot, "backups")
3237
+ targetRoot === null ? void 0 : path23.join(targetRoot, "backups")
3250
3238
  ),
3251
3239
  writeText,
3252
3240
  copyFile,
@@ -3327,7 +3315,7 @@ async function runUninstall(opts) {
3327
3315
  if (!detection.installed) continue;
3328
3316
  const keepCtx = makeContext(keep, detection.configDir);
3329
3317
  if (!keepCtx) continue;
3330
- for (const action of buildPlan(keep, keepCtx)) retained.add(path25.resolve(action.target));
3318
+ for (const action of buildPlan(keep, keepCtx)) retained.add(path24.resolve(action.target));
3331
3319
  }
3332
3320
  }
3333
3321
  for (const id of opts.runtimes) {
@@ -3346,15 +3334,15 @@ async function runUninstall(opts) {
3346
3334
  if (!ctx) continue;
3347
3335
  ctx.preserveEngram = !removeEngram;
3348
3336
  const unmerge = adapter.planUnmerge(mcpForUnmerge, hooks, ctx);
3349
- const mergedTargets = new Set(unmerge.map((a) => path25.resolve(a.target)));
3337
+ const mergedTargets = new Set(unmerge.map((a) => path24.resolve(a.target)));
3350
3338
  const usingRealConfig = opts.targetDir === void 0;
3351
3339
  const prevOwned = usingRealConfig ? readManifest().runtimes[id]?.owned ?? [] : [];
3352
- const pruneRoot = usingRealConfig ? HOME : path25.dirname(configDir);
3340
+ const pruneRoot = usingRealConfig ? HOME : path24.dirname(configDir);
3353
3341
  const planTargets = [
3354
- .../* @__PURE__ */ new Set([...buildPlan(adapter, ctx).map((a) => path25.resolve(a.target)), ...prevOwned.map((t) => path25.resolve(t))])
3342
+ .../* @__PURE__ */ new Set([...buildPlan(adapter, ctx).map((a) => path24.resolve(a.target)), ...prevOwned.map((t) => path24.resolve(t))])
3355
3343
  ].filter((t) => !mergedTargets.has(t) && fs17.existsSync(t));
3356
3344
  const deleteTargets = planTargets.filter(
3357
- (t) => !retained.has(t) && !(ctx.preserveEngram && path25.basename(t) === "engram.ts") && isContainedIn(t, pruneRoot)
3345
+ (t) => !retained.has(t) && !(ctx.preserveEngram && path24.basename(t) === "engram.ts") && isContainedIn(t, pruneRoot)
3358
3346
  );
3359
3347
  const sharedKept = planTargets.length - deleteTargets.length;
3360
3348
  p2.log.step(`${adapter.name} \u2192 ${configDir}`);
@@ -3434,7 +3422,7 @@ async function runUninstall(opts) {
3434
3422
  }
3435
3423
 
3436
3424
  // src/doctor.ts
3437
- import path26 from "path";
3425
+ import path25 from "path";
3438
3426
  import fs18 from "fs";
3439
3427
  import * as p3 from "@clack/prompts";
3440
3428
  function engramVersion(bin) {
@@ -3454,7 +3442,7 @@ function resolvePlaywrightDoctorState(input) {
3454
3442
  return { status: "healthy" };
3455
3443
  }
3456
3444
  function context7KeyConfigured(id, configDir) {
3457
- 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");
3445
+ const file = id === "codex" ? path25.join(configDir, "config.toml") : id === "claude-code" ? path25.join(path25.dirname(configDir), `${path25.basename(configDir)}.json`) : path25.join(configDir, "opencode.json");
3458
3446
  const content = readTextIfExists(file);
3459
3447
  if (content === null) return null;
3460
3448
  const match = /CONTEXT7_API_KEY"?\s*[:=]\s*"([^"]*)"/.exec(content);
@@ -3477,8 +3465,8 @@ async function runDoctor() {
3477
3465
  p3.log.success(`Engram: ${version} (${engramBin})`);
3478
3466
  }
3479
3467
  }
3480
- const engramDataDir = process.env.ENGRAM_DATA_DIR ?? path26.join(HOME, ".engram");
3481
- const engramDb = path26.join(engramDataDir, "engram.db");
3468
+ const engramDataDir = process.env.ENGRAM_DATA_DIR ?? path25.join(HOME, ".engram");
3469
+ const engramDb = path25.join(engramDataDir, "engram.db");
3482
3470
  if (fs18.existsSync(engramDb)) {
3483
3471
  const sizeMb = (fs18.statSync(engramDb).size / 1024 / 1024).toFixed(1);
3484
3472
  p3.log.info(`Engram DB: ${engramDb} (${sizeMb} MB de memorias \u2014 el stack no la toca JAM\xC1S).`);
@@ -3558,10 +3546,10 @@ async function runDoctor() {
3558
3546
  p3.log.warn(`${adapter.name}: ${orphans.length} archivos hu\xE9rfanos de versiones previas \u2192 ejecuta 'sync'.`);
3559
3547
  problems++;
3560
3548
  }
3561
- if (adapter.id === "codex" && fs18.existsSync(path26.join(detection.configDir, "hooks.json"))) {
3549
+ if (adapter.id === "codex" && fs18.existsSync(path25.join(detection.configDir, "hooks.json"))) {
3562
3550
  p3.log.info("Codex: recuerda que los hooks requieren aprobaci\xF3n manual \u2014 verifica con /hooks dentro de codex.");
3563
3551
  }
3564
- if (adapter.id === "codex" && fs18.existsSync(path26.join(detection.configDir, "AGENTS.override.md"))) {
3552
+ if (adapter.id === "codex" && fs18.existsSync(path25.join(detection.configDir, "AGENTS.override.md"))) {
3565
3553
  p3.log.warn(
3566
3554
  "Codex: existe ~/.codex/AGENTS.override.md \u2014 tiene prioridad ABSOLUTA y tapa el AGENTS.md gestionado por el stack."
3567
3555
  );
@@ -3576,16 +3564,16 @@ async function runDoctor() {
3576
3564
 
3577
3565
  // src/update.ts
3578
3566
  import fs21 from "fs";
3579
- import path29 from "path";
3580
- import os4 from "os";
3567
+ import path28 from "path";
3568
+ import os3 from "os";
3581
3569
  import { execFileSync as execFileSync5 } from "child_process";
3582
3570
  import * as p4 from "@clack/prompts";
3583
3571
 
3584
3572
  // src/lib/github.ts
3585
3573
  import fs19 from "fs";
3586
- import path27 from "path";
3574
+ import path26 from "path";
3587
3575
  import { execFileSync as execFileSync3 } from "child_process";
3588
- import os3 from "os";
3576
+ import os2 from "os";
3589
3577
  import { Readable } from "stream";
3590
3578
  import { pipeline } from "stream/promises";
3591
3579
  var cachedToken;
@@ -3653,7 +3641,7 @@ async function latestGithubCommit(repo) {
3653
3641
  }
3654
3642
  }
3655
3643
  function validateExtractedTree(destDir) {
3656
- const resolved = path27.resolve(destDir);
3644
+ const resolved = path26.resolve(destDir);
3657
3645
  const walk = (dir) => {
3658
3646
  let entries;
3659
3647
  try {
@@ -3662,7 +3650,7 @@ function validateExtractedTree(destDir) {
3662
3650
  return false;
3663
3651
  }
3664
3652
  for (const entry of entries) {
3665
- const full = path27.join(dir, entry.name);
3653
+ const full = path26.join(dir, entry.name);
3666
3654
  let stat;
3667
3655
  try {
3668
3656
  stat = fs19.lstatSync(full);
@@ -3681,12 +3669,12 @@ function validateExtractedTree(destDir) {
3681
3669
  }
3682
3670
  function resolveTarBin() {
3683
3671
  if (process.platform !== "win32") return "tar";
3684
- const winTar = path27.join(process.env["SystemRoot"] ?? "C:\\Windows", "System32", "tar.exe");
3672
+ const winTar = path26.join(process.env["SystemRoot"] ?? "C:\\Windows", "System32", "tar.exe");
3685
3673
  return fs19.existsSync(winTar) ? winTar : "tar";
3686
3674
  }
3687
3675
  async function downloadRepoTarball(repo, sha, destDir, validateSubdir) {
3688
3676
  const url = `https://codeload.github.com/${repo}/tar.gz/${sha}`;
3689
- const tmp = path27.join(os3.tmpdir(), `jorgex-tarball-${Date.now()}.tar.gz`);
3677
+ const tmp = path26.join(os2.tmpdir(), `jorgex-tarball-${Date.now()}.tar.gz`);
3690
3678
  const fail = (reason) => {
3691
3679
  try {
3692
3680
  fs19.rmSync(destDir, { recursive: true, force: true });
@@ -3719,8 +3707,8 @@ async function downloadRepoTarball(repo, sha, destDir, validateSubdir) {
3719
3707
  const detail = (e.stderr?.toString().trim() || e.message || "").split("\n")[0];
3720
3708
  return fail(detail ? `tar fall\xF3: ${detail}` : "tar no disponible o fall\xF3 la extracci\xF3n");
3721
3709
  }
3722
- const resolvedDest = path27.resolve(destDir);
3723
- const validateRoot = validateSubdir ? path27.resolve(resolvedDest, validateSubdir) : resolvedDest;
3710
+ const resolvedDest = path26.resolve(destDir);
3711
+ const validateRoot = validateSubdir ? path26.resolve(resolvedDest, validateSubdir) : resolvedDest;
3724
3712
  if (validateRoot !== resolvedDest && !isContainedIn(validateRoot, resolvedDest)) {
3725
3713
  return fail(`la ruta de validaci\xF3n "${validateSubdir}" escapa del destino`);
3726
3714
  }
@@ -3742,7 +3730,7 @@ async function downloadRepoTarball(repo, sha, destDir, validateSubdir) {
3742
3730
 
3743
3731
  // src/lib/skill-update.ts
3744
3732
  import fs20 from "fs";
3745
- import path28 from "path";
3733
+ import path27 from "path";
3746
3734
  import { execFileSync as execFileSync4 } from "child_process";
3747
3735
  var PROTECTED_SKILLS = /* @__PURE__ */ new Set([
3748
3736
  "agent-delegation",
@@ -3761,10 +3749,10 @@ function sameTextContentNormalized(a, b) {
3761
3749
  }
3762
3750
  function diffSkillDirs(upstreamDir, localDir) {
3763
3751
  const upstreamFiles = new Set(
3764
- listFilesRecursive(upstreamDir).map((f) => path28.relative(upstreamDir, f))
3752
+ listFilesRecursive(upstreamDir).map((f) => path27.relative(upstreamDir, f))
3765
3753
  );
3766
3754
  const localFiles = new Set(
3767
- listFilesRecursive(localDir).map((f) => path28.relative(localDir, f))
3755
+ listFilesRecursive(localDir).map((f) => path27.relative(localDir, f))
3768
3756
  );
3769
3757
  const added = [];
3770
3758
  const modified = [];
@@ -3772,7 +3760,7 @@ function diffSkillDirs(upstreamDir, localDir) {
3772
3760
  for (const rel of upstreamFiles) {
3773
3761
  if (!localFiles.has(rel)) {
3774
3762
  added.push(rel);
3775
- } else if (!sameTextContentNormalized(path28.join(upstreamDir, rel), path28.join(localDir, rel))) {
3763
+ } else if (!sameTextContentNormalized(path27.join(upstreamDir, rel), path27.join(localDir, rel))) {
3776
3764
  modified.push(rel);
3777
3765
  }
3778
3766
  }
@@ -3834,7 +3822,7 @@ function replaceSkill(name, upstreamSkillDir, newCommit, opts) {
3834
3822
  if (PROTECTED_SKILLS.has(name)) {
3835
3823
  throw new Error(`La skill "${name}" es propia del stack y no se actualiza desde upstream.`);
3836
3824
  }
3837
- const upstreamsFile = upstreamsFilePath ?? path28.join(path28.dirname(stackRoot()), "upstreams.json");
3825
+ const upstreamsFile = upstreamsFilePath ?? path27.join(path27.dirname(stackRoot()), "upstreams.json");
3838
3826
  const raw = fs20.readFileSync(upstreamsFile, "utf8");
3839
3827
  const data = JSON.parse(raw);
3840
3828
  const skillEntry = data?.skills?.[name];
@@ -3844,8 +3832,8 @@ function replaceSkill(name, upstreamSkillDir, newCommit, opts) {
3844
3832
  if (skillEntry.kind === "release") {
3845
3833
  throw new Error(`La skill "${name}" es de tipo release y no se actualiza con replaceSkill.`);
3846
3834
  }
3847
- const skillsRoot = localSkillsRoot ?? path28.join(stackRoot(), "skills");
3848
- const localSkillDir = path28.join(skillsRoot, name);
3835
+ const skillsRoot = localSkillsRoot ?? path27.join(stackRoot(), "skills");
3836
+ const localSkillDir = path27.join(skillsRoot, name);
3849
3837
  const localFiles = listFilesRecursive(localSkillDir);
3850
3838
  if (localFiles.length > 0) {
3851
3839
  createBackup(localFiles, `skill-update-${name}`, backupsRoot2);
@@ -3858,9 +3846,9 @@ function replaceSkill(name, upstreamSkillDir, newCommit, opts) {
3858
3846
  if (st.isSymbolicLink()) {
3859
3847
  throw new Error(`Symlink rechazado en upstream de skill "${name}": ${src}`);
3860
3848
  }
3861
- const rel = path28.relative(upstreamSkillDir, src);
3862
- const dest = path28.join(stagingDir, rel);
3863
- ensureDir(path28.dirname(dest));
3849
+ const rel = path27.relative(upstreamSkillDir, src);
3850
+ const dest = path27.join(stagingDir, rel);
3851
+ ensureDir(path27.dirname(dest));
3864
3852
  copyFile(src, dest);
3865
3853
  }
3866
3854
  const oldDir = `${localSkillDir}.old-${process.pid}`;
@@ -3885,7 +3873,7 @@ function rateLimitHint(prefix) {
3885
3873
  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.`;
3886
3874
  }
3887
3875
  function loadUpstreams() {
3888
- const file = path29.join(path29.dirname(stackRoot()), "upstreams.json");
3876
+ const file = path28.join(path28.dirname(stackRoot()), "upstreams.json");
3889
3877
  return JSON.parse(fs21.readFileSync(file, "utf8"));
3890
3878
  }
3891
3879
  function skillsToScan(maintainer, upstreams) {
@@ -4035,8 +4023,8 @@ function isEngramRunning() {
4035
4023
  return null;
4036
4024
  }
4037
4025
  }
4038
- function isGitClone(projectRoot = path29.dirname(stackRoot())) {
4039
- return fs21.existsSync(path29.join(projectRoot, ".git"));
4026
+ function isGitClone(projectRoot = path28.dirname(stackRoot())) {
4027
+ return fs21.existsSync(path28.join(projectRoot, ".git"));
4040
4028
  }
4041
4029
  var STACK_METHOD_CLONE = "git pull + pnpm install + pnpm build";
4042
4030
  function resolvePnpm() {
@@ -4051,7 +4039,7 @@ function cleanupTmp(dir) {
4051
4039
  }
4052
4040
  }
4053
4041
  function updateStackGitClone() {
4054
- const projectRoot = path29.dirname(stackRoot());
4042
+ const projectRoot = path28.dirname(stackRoot());
4055
4043
  const git = lookPath("git");
4056
4044
  if (!git) throw new Error("git no encontrado en PATH.");
4057
4045
  const pnpm = resolvePnpm();
@@ -4068,7 +4056,7 @@ function updateStackGlobal() {
4068
4056
  execFileSync5(pnpm, ["add", "-g", "jorgex-stack@latest"], { stdio: "inherit" });
4069
4057
  }
4070
4058
  async function downloadSkillToTemp(repo, sha, skillPath) {
4071
- const root = fs21.mkdtempSync(path29.join(os4.tmpdir(), "jorgex-skill-"));
4059
+ const root = fs21.mkdtempSync(path28.join(os3.tmpdir(), "jorgex-skill-"));
4072
4060
  try {
4073
4061
  const result = await downloadRepoTarball(repo, sha, root, skillPath);
4074
4062
  if (!result.ok) {
@@ -4076,14 +4064,14 @@ async function downloadSkillToTemp(repo, sha, skillPath) {
4076
4064
  return { error: result.reason };
4077
4065
  }
4078
4066
  if (skillPath) {
4079
- const sub = path29.resolve(path29.join(root, skillPath));
4067
+ const sub = path28.resolve(path28.join(root, skillPath));
4080
4068
  if (!isContainedIn(sub, root)) {
4081
4069
  cleanupTmp(root);
4082
4070
  return { error: `la ruta "${skillPath}" escapa del directorio temporal` };
4083
4071
  }
4084
4072
  if (fs21.existsSync(sub)) return { dir: sub, root };
4085
4073
  const lastSeg = skillPath.split("/").pop();
4086
- const sub2 = path29.resolve(path29.join(root, lastSeg));
4074
+ const sub2 = path28.resolve(path28.join(root, lastSeg));
4087
4075
  if (isContainedIn(sub2, root) && fs21.existsSync(sub2)) {
4088
4076
  if (!validateExtractedTree(sub2)) {
4089
4077
  cleanupTmp(root);
@@ -4104,10 +4092,10 @@ function pruneEngramDbBackups() {
4104
4092
  try {
4105
4093
  const dir = dataDir();
4106
4094
  if (!fs21.existsSync(dir)) return;
4107
- const backups = fs21.readdirSync(dir).filter((f) => f.startsWith("engram-db-backup-") && f.endsWith(".db")).map((f) => ({ name: f, mtime: fs21.statSync(path29.join(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
4095
+ const backups = fs21.readdirSync(dir).filter((f) => f.startsWith("engram-db-backup-") && f.endsWith(".db")).map((f) => ({ name: f, mtime: fs21.statSync(path28.join(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
4108
4096
  for (const old of backups.slice(3)) {
4109
4097
  try {
4110
- fs21.rmSync(path29.join(dir, old.name));
4098
+ fs21.rmSync(path28.join(dir, old.name));
4111
4099
  } catch {
4112
4100
  }
4113
4101
  }
@@ -4116,17 +4104,17 @@ function pruneEngramDbBackups() {
4116
4104
  }
4117
4105
  function rotateLockedBinary(binPath, sweepRoot = HOME) {
4118
4106
  if (!fs21.existsSync(binPath)) return null;
4119
- const dir = path29.dirname(binPath);
4120
- const base = path29.basename(binPath);
4107
+ const dir = path28.dirname(binPath);
4108
+ const base = path28.basename(binPath);
4121
4109
  const escapedBase = base.replace(/[.*+?^$()|[\]{}\\]/g, "\\$&");
4122
4110
  const oldPattern = new RegExp("^" + escapedBase + "\\.old-\\d+$");
4123
- const resolvedDir = path29.resolve(dir);
4124
- if (resolvedDir === path29.resolve(sweepRoot) || isContainedIn(resolvedDir, sweepRoot)) {
4111
+ const resolvedDir = path28.resolve(dir);
4112
+ if (resolvedDir === path28.resolve(sweepRoot) || isContainedIn(resolvedDir, sweepRoot)) {
4125
4113
  try {
4126
4114
  for (const entry of fs21.readdirSync(dir)) {
4127
4115
  if (oldPattern.test(entry)) {
4128
4116
  try {
4129
- fs21.rmSync(path29.join(dir, entry), { force: true });
4117
+ fs21.rmSync(path28.join(dir, entry), { force: true });
4130
4118
  } catch {
4131
4119
  }
4132
4120
  }
@@ -4134,7 +4122,7 @@ function rotateLockedBinary(binPath, sweepRoot = HOME) {
4134
4122
  } catch {
4135
4123
  }
4136
4124
  }
4137
- const rotated = path29.join(dir, `${base}.old-${Date.now()}`);
4125
+ const rotated = path28.join(dir, `${base}.old-${Date.now()}`);
4138
4126
  fs21.renameSync(binPath, rotated);
4139
4127
  return rotated;
4140
4128
  }
@@ -4144,8 +4132,8 @@ async function updateEngram(engramRepo, latestVersion) {
4144
4132
  "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)."
4145
4133
  );
4146
4134
  }
4147
- const engramDataDir = process.env.ENGRAM_DATA_DIR ?? path29.join(HOME, ".engram");
4148
- const engramDb = path29.join(engramDataDir, "engram.db");
4135
+ const engramDataDir = process.env.ENGRAM_DATA_DIR ?? path28.join(HOME, ".engram");
4136
+ const engramDb = path28.join(engramDataDir, "engram.db");
4149
4137
  if (fs21.existsSync(engramDb)) {
4150
4138
  const doBackup = await p4.confirm({
4151
4139
  message: `\xBFHacer backup de la DB de Engram antes de actualizar? (${engramDb})`,
@@ -4153,7 +4141,7 @@ async function updateEngram(engramRepo, latestVersion) {
4153
4141
  });
4154
4142
  if (!p4.isCancel(doBackup) && doBackup) {
4155
4143
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4156
- const dest = path29.join(dataDir(), `engram-db-backup-${ts}.db`);
4144
+ const dest = path28.join(dataDir(), `engram-db-backup-${ts}.db`);
4157
4145
  try {
4158
4146
  if (!fs21.existsSync(dataDir())) fs21.mkdirSync(dataDir(), { recursive: true });
4159
4147
  fs21.copyFileSync(engramDb, dest);
@@ -4490,7 +4478,7 @@ async function runInteractiveUpdate(localVersion, yes, dryRun = false, includeBr
4490
4478
  continue;
4491
4479
  }
4492
4480
  const { dir: tmpDir, root: tmpRoot } = tmpResult;
4493
- const localSkillDir = path29.join(stackRoot(), "skills", skillInfo.name);
4481
+ const localSkillDir = path28.join(stackRoot(), "skills", skillInfo.name);
4494
4482
  const diff = renderSkillDiff(tmpDir, localSkillDir);
4495
4483
  if (diff) {
4496
4484
  p4.log.info(`Diff de ${skillInfo.name}:
@@ -4576,7 +4564,7 @@ ${diff}`);
4576
4564
  }
4577
4565
 
4578
4566
  // src/models-picker.ts
4579
- import path30 from "path";
4567
+ import path29 from "path";
4580
4568
  import * as p5 from "@clack/prompts";
4581
4569
  var TIERS = ["strong", "standard", "cheap"];
4582
4570
  var EFFORTS = ["low", "medium", "high", "xhigh"];
@@ -4600,7 +4588,7 @@ function opencodeLiveModels(binPath) {
4600
4588
  }
4601
4589
  function agentsByTier() {
4602
4590
  const grouped = { strong: [], standard: [], cheap: [] };
4603
- for (const agent of loadCanonicalAgents(path30.join(stackRoot(), "agents"))) {
4591
+ for (const agent of loadCanonicalAgents(path29.join(stackRoot(), "agents"))) {
4604
4592
  if (agent.mode === "subagent") grouped[agent.tier].push(agent.name);
4605
4593
  }
4606
4594
  return grouped;
@@ -4781,15 +4769,15 @@ function cancelled() {
4781
4769
 
4782
4770
  // src/lib/release.ts
4783
4771
  import fs22 from "fs";
4784
- import path31 from "path";
4772
+ import path30 from "path";
4785
4773
  import { execFileSync as execFileSync6 } from "child_process";
4786
- import { fileURLToPath as fileURLToPath2 } from "url";
4774
+ import { fileURLToPath } from "url";
4787
4775
  function findPackageJson() {
4788
- let dir = path31.dirname(fileURLToPath2(import.meta.url));
4776
+ let dir = path30.dirname(fileURLToPath(import.meta.url));
4789
4777
  for (let i = 0; i < 6; i++) {
4790
- const candidate = path31.join(dir, "package.json");
4778
+ const candidate = path30.join(dir, "package.json");
4791
4779
  if (fs22.existsSync(candidate)) return candidate;
4792
- dir = path31.dirname(dir);
4780
+ dir = path30.dirname(dir);
4793
4781
  }
4794
4782
  throw new Error("No se encontr\xF3 package.json cerca del CLI.");
4795
4783
  }
@@ -4809,8 +4797,8 @@ function readPackageMetadata() {
4809
4797
  }
4810
4798
 
4811
4799
  // src/lib/pi-runtime.ts
4812
- import os5 from "os";
4813
- import path32 from "path";
4800
+ import os4 from "os";
4801
+ import path31 from "path";
4814
4802
  import fs23 from "fs";
4815
4803
  import { spawnSync } from "child_process";
4816
4804
  import { createHash } from "crypto";
@@ -4964,14 +4952,14 @@ function healthyDoctor(stdout, stderr, packageRunner, candidate) {
4964
4952
  if (record === null || typeof record !== "object" || Array.isArray(record)) return false;
4965
4953
  const packageValue = Reflect.get(record, "package");
4966
4954
  const result = Reflect.get(record, "result");
4967
- 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;
4955
+ 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]) && path31.resolve(packageRunner) === path31.resolve(String(Reflect.get(packageValue, "root")), "bin", "jorgex-pi.mjs") && result !== null && typeof result === "object" && Reflect.get(result, "healthy") === true;
4968
4956
  } catch {
4969
4957
  return false;
4970
4958
  }
4971
4959
  }
4972
4960
  function installPiFromVerifiedTarball(input, deps) {
4973
4961
  const paths = input.targetDir === void 0 ? userPaths(input.engramBin, input.piExecutable) : targetPaths(input.targetDir, input.engramBin, input.piExecutable);
4974
- 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`);
4962
+ const destination = input.targetDir === void 0 ? path31.join(dataDir(), "packages", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`) : path31.join(path31.resolve(input.targetDir), "downloads", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`);
4975
4963
  const artifact = deps.download(destination);
4976
4964
  if (artifact.bytes !== input.candidate.bytes || artifact.sha256 !== input.candidate.sha256 || artifact.sha512 !== input.candidate.sha512) {
4977
4965
  return { kind: "blocked", reason: "tarball-integrity" };
@@ -4979,7 +4967,7 @@ function installPiFromVerifiedTarball(input, deps) {
4979
4967
  deps.backupSettings();
4980
4968
  const scope = {
4981
4969
  kind: input.targetDir === void 0 ? "real" : "target-dir",
4982
- codingAgentDir: path32.resolve(paths.codingAgentDir)
4970
+ codingAgentDir: path31.resolve(paths.codingAgentDir)
4983
4971
  };
4984
4972
  const installing = flatCandidateReceipt(input.candidate, scope, "installing", input.engramBin);
4985
4973
  deps.writeReceiptAtomic(`${JSON.stringify(installing)}
@@ -5008,30 +4996,30 @@ function installPiFromVerifiedTarball(input, deps) {
5008
4996
  return { kind: "installed", receipt };
5009
4997
  }
5010
4998
  function runtimePath(piExecutable) {
5011
- 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"];
5012
- return [...new Set(entries.filter((entry) => entry !== null))].join(path32.delimiter);
4999
+ const entries = process.platform === "win32" ? [piExecutable === void 0 ? null : path31.dirname(piExecutable), path31.dirname(process.execPath), process.env.SystemRoot ? path31.join(process.env.SystemRoot, "System32") : null] : [piExecutable === void 0 ? null : path31.dirname(piExecutable), path31.dirname(process.execPath), "/usr/local/bin", "/usr/bin", "/bin"];
5000
+ return [...new Set(entries.filter((entry) => entry !== null))].join(path31.delimiter);
5013
5001
  }
5014
5002
  function targetPaths(targetDir, engramBin, piExecutable) {
5015
- const root = path32.resolve(targetDir);
5016
- const codingAgentDir = path32.join(root, "pi-agent");
5017
- const home = path32.join(root, "home");
5018
- const temporary = path32.join(root, "tmp");
5003
+ const root = path31.resolve(targetDir);
5004
+ const codingAgentDir = path31.join(root, "pi-agent");
5005
+ const home = path31.join(root, "home");
5006
+ const temporary = path31.join(root, "tmp");
5019
5007
  return {
5020
5008
  codingAgentDir,
5021
- receiptPath: path32.join(root, "state", "pi-receipt.json"),
5022
- packageRunner: path32.join(codingAgentDir, "npm", "node_modules", "jorgex-pi", "bin", "jorgex-pi.mjs"),
5009
+ receiptPath: path31.join(root, "state", "pi-receipt.json"),
5010
+ packageRunner: path31.join(codingAgentDir, "npm", "node_modules", "jorgex-pi", "bin", "jorgex-pi.mjs"),
5023
5011
  environment: {
5024
5012
  HOME: home,
5025
5013
  USERPROFILE: home,
5026
- APPDATA: path32.join(root, "appdata"),
5027
- LOCALAPPDATA: path32.join(root, "localappdata"),
5028
- XDG_CONFIG_HOME: path32.join(root, "xdg-config"),
5029
- XDG_DATA_HOME: path32.join(root, "xdg-data"),
5030
- XDG_CACHE_HOME: path32.join(root, "xdg-cache"),
5014
+ APPDATA: path31.join(root, "appdata"),
5015
+ LOCALAPPDATA: path31.join(root, "localappdata"),
5016
+ XDG_CONFIG_HOME: path31.join(root, "xdg-config"),
5017
+ XDG_DATA_HOME: path31.join(root, "xdg-data"),
5018
+ XDG_CACHE_HOME: path31.join(root, "xdg-cache"),
5031
5019
  TEMP: temporary,
5032
5020
  TMP: temporary,
5033
5021
  TMPDIR: temporary,
5034
- npm_config_cache: path32.join(root, "npm-cache"),
5022
+ npm_config_cache: path31.join(root, "npm-cache"),
5035
5023
  NPM_CONFIG_IGNORE_SCRIPTS: "true",
5036
5024
  NPM_CONFIG_UPDATE_NOTIFIER: "false",
5037
5025
  PI_CODING_AGENT_DIR: codingAgentDir,
@@ -5041,17 +5029,17 @@ function targetPaths(targetDir, engramBin, piExecutable) {
5041
5029
  };
5042
5030
  }
5043
5031
  function userPaths(engramBin, piExecutable) {
5044
- const home = os5.homedir();
5045
- const codingAgentDir = process.env.PI_CODING_AGENT_DIR ?? path32.join(home, ".pi", "agent");
5032
+ const home = os4.homedir();
5033
+ const codingAgentDir = process.env.PI_CODING_AGENT_DIR ?? path31.join(home, ".pi", "agent");
5046
5034
  return {
5047
5035
  codingAgentDir,
5048
- receiptPath: path32.join(dataDir(), "pi-receipt.json"),
5049
- packageRunner: path32.join(codingAgentDir, "npm", "node_modules", "jorgex-pi", "bin", "jorgex-pi.mjs"),
5036
+ receiptPath: path31.join(dataDir(), "pi-receipt.json"),
5037
+ packageRunner: path31.join(codingAgentDir, "npm", "node_modules", "jorgex-pi", "bin", "jorgex-pi.mjs"),
5050
5038
  environment: {
5051
5039
  HOME: home,
5052
- XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME ?? path32.join(home, ".config"),
5053
- XDG_CACHE_HOME: process.env.XDG_CACHE_HOME ?? path32.join(home, ".cache"),
5054
- TMPDIR: os5.tmpdir(),
5040
+ XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME ?? path31.join(home, ".config"),
5041
+ XDG_CACHE_HOME: process.env.XDG_CACHE_HOME ?? path31.join(home, ".cache"),
5042
+ TMPDIR: os4.tmpdir(),
5055
5043
  NPM_CONFIG_IGNORE_SCRIPTS: "true",
5056
5044
  NPM_CONFIG_UPDATE_NOTIFIER: "false",
5057
5045
  PI_CODING_AGENT_DIR: codingAgentDir,
@@ -5078,7 +5066,7 @@ function runPiRuntime(input, deps) {
5078
5066
  return { kind: "blocked", reason: "tarball-integrity" };
5079
5067
  }
5080
5068
  const paths = input.targetDir === void 0 ? userPaths(input.engramBin, input.detected.executable) : targetPaths(input.targetDir, input.engramBin, input.detected.executable);
5081
- const settingsJson = deps.readSettings(path32.join(paths.codingAgentDir, "settings.json"));
5069
+ const settingsJson = deps.readSettings(path31.join(paths.codingAgentDir, "settings.json"));
5082
5070
  const receiptJson = deps.readReceipt(paths.receiptPath);
5083
5071
  const lifecycleInput = {
5084
5072
  candidate: PI_RUNTIME_CANDIDATE,
@@ -5138,14 +5126,14 @@ function readJsonFile(file) {
5138
5126
  function packageVersionFromExecutable(executable) {
5139
5127
  let current;
5140
5128
  try {
5141
- current = path32.dirname(fs23.realpathSync(executable));
5129
+ current = path31.dirname(fs23.realpathSync(executable));
5142
5130
  } catch {
5143
5131
  return null;
5144
5132
  }
5145
5133
  for (let depth = 0; depth < 8; depth++) {
5146
5134
  const manifests = [
5147
- path32.join(current, "package.json"),
5148
- path32.join(current, "node_modules", "@earendil-works", "pi-coding-agent", "package.json")
5135
+ path31.join(current, "package.json"),
5136
+ path31.join(current, "node_modules", "@earendil-works", "pi-coding-agent", "package.json")
5149
5137
  ];
5150
5138
  for (const manifest of manifests) {
5151
5139
  try {
@@ -5156,7 +5144,7 @@ function packageVersionFromExecutable(executable) {
5156
5144
  } catch {
5157
5145
  }
5158
5146
  }
5159
- const parent = path32.dirname(current);
5147
+ const parent = path31.dirname(current);
5160
5148
  if (parent === current) break;
5161
5149
  current = parent;
5162
5150
  }
@@ -5164,23 +5152,23 @@ function packageVersionFromExecutable(executable) {
5164
5152
  }
5165
5153
  function detectPiRuntime() {
5166
5154
  const executable = lookPath("pi");
5167
- const home = os5.homedir();
5155
+ const home = os4.homedir();
5168
5156
  return {
5169
5157
  id: "pi",
5170
5158
  name: "Pi",
5171
5159
  installed: executable !== null,
5172
5160
  executable,
5173
5161
  version: executable === null ? null : packageVersionFromExecutable(executable),
5174
- codingAgentDir: process.env.PI_CODING_AGENT_DIR ?? path32.join(home, ".pi", "agent")
5162
+ codingAgentDir: process.env.PI_CODING_AGENT_DIR ?? path31.join(home, ".pi", "agent")
5175
5163
  };
5176
5164
  }
5177
5165
  function hasManagedPiRuntime(targetDir) {
5178
- const receipt = targetDir === void 0 ? path32.join(dataDir(), "pi-receipt.json") : path32.join(path32.resolve(targetDir), "state", "pi-receipt.json");
5166
+ const receipt = targetDir === void 0 ? path31.join(dataDir(), "pi-receipt.json") : path31.join(path31.resolve(targetDir), "state", "pi-receipt.json");
5179
5167
  return fs23.statSync(receipt, { throwIfNoEntry: false })?.isFile() === true;
5180
5168
  }
5181
5169
  function resolvePiEngramBin(targetDir) {
5182
5170
  if (targetDir === void 0) return detectEngram();
5183
- const candidate = path32.join(path32.resolve(targetDir), "bin", process.platform === "win32" ? "engram.exe" : "engram");
5171
+ const candidate = path31.join(path31.resolve(targetDir), "bin", process.platform === "win32" ? "engram.exe" : "engram");
5184
5172
  return fs23.statSync(candidate, { throwIfNoEntry: false })?.isFile() ? candidate : null;
5185
5173
  }
5186
5174
  function readOptional(file, fallback) {
@@ -5220,7 +5208,7 @@ async function acquirePiTarball(destination) {
5220
5208
  }
5221
5209
  fs23.rmSync(destination, { force: true });
5222
5210
  }
5223
- fs23.mkdirSync(path32.dirname(destination), { recursive: true });
5211
+ fs23.mkdirSync(path31.dirname(destination), { recursive: true });
5224
5212
  const partial = `${destination}.partial-${process.pid}`;
5225
5213
  const response = await fetch(`https://registry.npmjs.org/jorgex-pi/-/jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`, {
5226
5214
  redirect: "error",
@@ -5293,7 +5281,7 @@ async function runPiRuntimeSystem(input) {
5293
5281
  remedy: "Instala Engram o configura un ENGRAM_BIN absoluto antes de reintentar."
5294
5282
  };
5295
5283
  }
5296
- 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`);
5284
+ const destination = input.targetDir === void 0 ? path31.join(dataDir(), "packages", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`) : path31.join(path31.resolve(input.targetDir), "downloads", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`);
5297
5285
  let artifact;
5298
5286
  try {
5299
5287
  artifact = await acquirePiTarball(destination);
@@ -5313,18 +5301,18 @@ async function runPiRuntimeSystem(input) {
5313
5301
  }, {
5314
5302
  download: () => artifact,
5315
5303
  backupSettings: () => createBackup(
5316
- [path32.join(paths.codingAgentDir, "settings.json")],
5304
+ [path31.join(paths.codingAgentDir, "settings.json")],
5317
5305
  "pi-package-install",
5318
- input.targetDir === void 0 ? void 0 : path32.join(path32.resolve(input.targetDir), "backups")
5306
+ input.targetDir === void 0 ? void 0 : path31.join(path31.resolve(input.targetDir), "backups")
5319
5307
  ),
5320
5308
  run: runProcess,
5321
- readSettings: () => readOptional(path32.join(paths.codingAgentDir, "settings.json"), '{"packages":[]}'),
5322
- rewriteSettings: (content) => writeText(path32.join(paths.codingAgentDir, "settings.json"), `${content}
5309
+ readSettings: () => readOptional(path31.join(paths.codingAgentDir, "settings.json"), '{"packages":[]}'),
5310
+ rewriteSettings: (content) => writeText(path31.join(paths.codingAgentDir, "settings.json"), `${content}
5323
5311
  `),
5324
5312
  writeReceiptAtomic: (content) => writeText(paths.receiptPath, content)
5325
5313
  });
5326
5314
  }
5327
- const packageRoot = path32.dirname(path32.dirname(paths.packageRunner));
5315
+ const packageRoot = path31.dirname(path31.dirname(paths.packageRunner));
5328
5316
  const writeReceipt = (receipt) => {
5329
5317
  writeText(paths.receiptPath, `${JSON.stringify(receipt, null, 2)}
5330
5318
  `);
@@ -5342,9 +5330,9 @@ async function runPiRuntimeSystem(input) {
5342
5330
  value,
5343
5331
  {
5344
5332
  backupSettings: () => createBackup(
5345
- [path32.join(paths.codingAgentDir, "settings.json")],
5333
+ [path31.join(paths.codingAgentDir, "settings.json")],
5346
5334
  "pi-package-uninstall",
5347
- input.targetDir === void 0 ? void 0 : path32.join(path32.resolve(input.targetDir), "backups")
5335
+ input.targetDir === void 0 ? void 0 : path31.join(path31.resolve(input.targetDir), "backups")
5348
5336
  ),
5349
5337
  run: runProcess,
5350
5338
  isPackageAbsent: () => !fs23.existsSync(packageRoot),
@@ -5445,466 +5433,8 @@ async function runManagedPiSystem(input) {
5445
5433
  }
5446
5434
 
5447
5435
  // src/lib/quality-runner.ts
5448
- import path33 from "path";
5436
+ import path32 from "path";
5449
5437
  import { spawn, spawnSync as spawnSync2 } from "child_process";
5450
-
5451
- // src/lib/quality-policy.ts
5452
- var QUALITY_PROFILES = ["routine", "elevated", "high", "release"];
5453
- function hasText(value) {
5454
- return typeof value === "string" && value.trim() !== "";
5455
- }
5456
- function isQualityProfile(value) {
5457
- return typeof value === "string" && QUALITY_PROFILES.includes(value);
5458
- }
5459
- function mergeStatus(current, next) {
5460
- if (current === "fail" || next === "fail") return "fail";
5461
- if (current === "incomplete" || next === "incomplete") return "incomplete";
5462
- return "pass";
5463
- }
5464
- function resultStatus(result) {
5465
- switch (result.status) {
5466
- case "fail":
5467
- return "fail";
5468
- case "incomplete":
5469
- return "incomplete";
5470
- case "not-applicable":
5471
- return "pass";
5472
- case "pass":
5473
- return hasText(result.evidence) ? "pass" : "incomplete";
5474
- default:
5475
- return "incomplete";
5476
- }
5477
- }
5478
- function evaluateQualityPolicy(input) {
5479
- const requestedProfile = input.profile === void 0 ? "routine" : input.profile;
5480
- if (!isQualityProfile(requestedProfile)) {
5481
- throw new Error(`Unknown quality profile: ${String(requestedProfile)}`);
5482
- }
5483
- const controls = /* @__PURE__ */ new Map();
5484
- let status = "pass";
5485
- let hasRequired = false;
5486
- for (const control of input.controls) {
5487
- if (!hasText(control.id) || control.requirement !== "required" && control.requirement !== "optional" || controls.has(control.id)) {
5488
- status = mergeStatus(status, "incomplete");
5489
- continue;
5490
- }
5491
- controls.set(control.id, control);
5492
- if (control.requirement === "required") hasRequired = true;
5493
- }
5494
- const results = /* @__PURE__ */ new Map();
5495
- for (const result of input.results) {
5496
- const control = controls.get(result.controlId);
5497
- if (!control || results.has(result.controlId)) {
5498
- status = mergeStatus(status, "incomplete");
5499
- }
5500
- if (control && !results.has(result.controlId)) {
5501
- results.set(result.controlId, result);
5502
- }
5503
- if (control && result.status === "not-applicable") {
5504
- const validException = control.requirement === "optional" && control.notApplicable === true && hasText(result.reason);
5505
- status = mergeStatus(status, validException ? "pass" : "incomplete");
5506
- continue;
5507
- }
5508
- if (control) status = mergeStatus(status, resultStatus(result));
5509
- }
5510
- if (!hasRequired) status = mergeStatus(status, "incomplete");
5511
- for (const control of controls.values()) {
5512
- if (control.requirement !== "required") continue;
5513
- const result = results.get(control.id);
5514
- if (!result) {
5515
- status = mergeStatus(status, "incomplete");
5516
- continue;
5517
- }
5518
- if (result.status === "not-applicable") {
5519
- status = mergeStatus(status, "incomplete");
5520
- } else {
5521
- status = mergeStatus(status, resultStatus(result));
5522
- }
5523
- }
5524
- return { profile: requestedProfile, status };
5525
- }
5526
-
5527
- // src/lib/quality-receipt.ts
5528
- import { createHash as createHash2 } from "crypto";
5529
- var QUALITY_RECEIPT_NAMESPACE = "jorgex.quality.receipt";
5530
- var QUALITY_RECEIPT_VERSION = 1;
5531
- var COMMIT_SHA_PATTERN = /^[0-9a-f]{40}$/i;
5532
- var SHA256_PATTERN = /^[0-9a-f]{64}$/i;
5533
- var MAX_EXCERPT_LENGTH = 512;
5534
- var REDACTED = "[REDACTED]";
5535
- var QUALITY_RESULT_STATUSES = [
5536
- "pass",
5537
- "fail",
5538
- "incomplete",
5539
- "not-applicable"
5540
- ];
5541
- var SENSITIVE_FLAG_PATTERN = /^(?:-{1,2})(?:(?:[a-z][a-z0-9_.-]*[-_]?)?(?:access[-_]?token|api[-_]?key|auth(?:orization)?|client[-_]?secret|credential|pass(?:word|wd)?|refresh[-_]?token|secret|token))$/i;
5542
- var SENSITIVE_ASSIGNMENT_PATTERN = /^(?:-{0,2})(?:(?:[a-z][a-z0-9_.-]*[-_]?)?(?:access[-_]?token|api[-_]?key|auth(?:orization)?|client[-_]?secret|credential|pass(?:word|wd)?|refresh[-_]?token|secret|token))\s*[=:]\s*.+$/i;
5543
- var SENSITIVE_OUTPUT_PATTERN = /((?:authorization\s*:\s*bearer\s+|bearer\s+))[^\s,;]+/gi;
5544
- var SENSITIVE_KEY_VALUE_PATTERN = /((?:^|(?<=[^\w.-]))(?:-{0,2})(?:(?:[a-z][a-z0-9_.-]*[-_]?)?(?:access[-_]?token|api[-_]?key|auth(?:orization)?|client[-_]?secret|credential|pass(?:word|wd)?|refresh[-_]?token|secret|token))\s*[=:]\s*)(?:"[^"]*"|'[^']*'|[^\r\n,;]+)/gi;
5545
- var SENSITIVE_SEPARATE_FLAG_PATTERN = /((?:^|(?<=[^\w.-]))-{1,2}(?:(?:[a-z][a-z0-9_.-]*[-_]?)?(?:access[-_]?token|api[-_]?key|auth(?:orization)?|client[-_]?secret|credential|pass(?:word|wd)?|refresh[-_]?token|secret|token))[ \t]+)(?:"[^"]*"|'[^']*'|[^\r\n,;]+)/gi;
5546
- var SENSITIVE_STRUCTURED_VALUE_PATTERN = /((?:"|')?(?:token|password|api[-_]?key|access[-_]?token|_?auth(?:orization)?(?:[-_.]?token)?|aws[-_]?secret[-_]?access[-_]?key|private[-_]?key)(?:"|')?\s*:\s*)("(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*')/gi;
5547
- function isRecord2(value) {
5548
- return value !== null && typeof value === "object" && !Array.isArray(value);
5549
- }
5550
- function hasOwn(value, key) {
5551
- return Object.prototype.hasOwnProperty.call(value, key);
5552
- }
5553
- function isDenseStringArray(value) {
5554
- if (!Array.isArray(value)) return false;
5555
- for (let index = 0; index < value.length; index += 1) {
5556
- if (!hasOwn(value, String(index)) || typeof value[index] !== "string") return false;
5557
- }
5558
- return true;
5559
- }
5560
- function assertExactKeys(value, allowed, label) {
5561
- const allowedKeys = new Set(allowed);
5562
- const unexpected = Object.keys(value).find((key) => !allowedKeys.has(key));
5563
- if (unexpected !== void 0) {
5564
- throw new Error(`Unexpected ${label} field: ${unexpected}`);
5565
- }
5566
- }
5567
- function requireRecord(value, label) {
5568
- if (!isRecord2(value)) throw new Error(`Invalid ${label}`);
5569
- return value;
5570
- }
5571
- function requireText(value, label) {
5572
- if (typeof value !== "string" || value.trim() === "") {
5573
- throw new Error(`Invalid ${label}`);
5574
- }
5575
- return value;
5576
- }
5577
- function isQualityProfile2(value) {
5578
- return QUALITY_PROFILES.includes(value);
5579
- }
5580
- function isQualityReceiptResultStatus(value) {
5581
- return QUALITY_RESULT_STATUSES.includes(value);
5582
- }
5583
- function requireSha(value, label, pattern) {
5584
- const text2 = requireText(value, label);
5585
- if (!pattern.test(text2)) throw new Error(`Invalid ${label}`);
5586
- return text2;
5587
- }
5588
- function redactText(value) {
5589
- return value.replace(SENSITIVE_STRUCTURED_VALUE_PATTERN, (_match, prefix, quotedValue) => {
5590
- const quote = quotedValue[0];
5591
- return `${prefix}${quote}${REDACTED}${quote}`;
5592
- }).replace(SENSITIVE_OUTPUT_PATTERN, (_match, prefix) => {
5593
- const normalizedPrefix = prefix.toLowerCase().startsWith("authorization") ? prefix.slice(0, prefix.toLowerCase().indexOf("bearer") + "bearer".length) : "Bearer ";
5594
- return `${normalizedPrefix}${REDACTED}`;
5595
- }).replace(SENSITIVE_KEY_VALUE_PATTERN, `$1${REDACTED}`).replace(SENSITIVE_SEPARATE_FLAG_PATTERN, `$1${REDACTED}`);
5596
- }
5597
- function redactAssignment(value) {
5598
- const separator = value.indexOf("=") >= 0 ? "=" : ":";
5599
- const name = value.slice(0, value.indexOf(separator));
5600
- return `${name}${separator}${REDACTED}`;
5601
- }
5602
- function redactArgv(argv) {
5603
- if (!isDenseStringArray(argv)) throw new Error("Invalid argv: sparse or non-string array");
5604
- let redactNext = false;
5605
- return argv.map((argument) => {
5606
- if (redactNext) {
5607
- redactNext = false;
5608
- return REDACTED;
5609
- }
5610
- if (SENSITIVE_FLAG_PATTERN.test(argument)) {
5611
- redactNext = true;
5612
- return argument;
5613
- }
5614
- if (SENSITIVE_ASSIGNMENT_PATTERN.test(argument)) {
5615
- return redactAssignment(argument);
5616
- }
5617
- return redactText(argument);
5618
- });
5619
- }
5620
- function excerptFor(output) {
5621
- const stdout = redactText(output.stdout);
5622
- const stderr = redactText(output.stderr);
5623
- const combined = [stdout, stderr].filter((part) => part !== "").join("\n");
5624
- return Array.from(combined).slice(0, MAX_EXCERPT_LENGTH).join("");
5625
- }
5626
- function outputDigestFor(output) {
5627
- const sanitized = {
5628
- stdout: redactText(output.stdout),
5629
- stderr: redactText(output.stderr)
5630
- };
5631
- return sha256(canonicalJson(sanitized));
5632
- }
5633
- function normalizeCommand(command) {
5634
- return {
5635
- commandId: command.commandId,
5636
- executable: command.executable,
5637
- argv: redactArgv(command.argv),
5638
- exitCode: command.exitCode,
5639
- durationMs: command.durationMs,
5640
- excerpt: excerptFor(command.output),
5641
- outputDigest: outputDigestFor(command.output)
5642
- };
5643
- }
5644
- function normalizeResult(result) {
5645
- if (result.status === "pass") {
5646
- return {
5647
- controlId: result.controlId,
5648
- status: "pass",
5649
- evidence: result.evidence,
5650
- ...result.reason === void 0 ? {} : { reason: result.reason }
5651
- };
5652
- }
5653
- return {
5654
- controlId: result.controlId,
5655
- status: result.status,
5656
- ...result.evidence === void 0 ? {} : { evidence: result.evidence },
5657
- ...result.reason === void 0 ? {} : { reason: result.reason }
5658
- };
5659
- }
5660
- function normalizeProvenance(provenance) {
5661
- return {
5662
- issuer: provenance.issuer,
5663
- executionId: provenance.executionId,
5664
- evidenceLocator: provenance.evidenceLocator,
5665
- evidenceDigest: provenance.evidenceDigest
5666
- };
5667
- }
5668
- function canonicalValue(value) {
5669
- if (value === null) return "null";
5670
- switch (typeof value) {
5671
- case "string": {
5672
- const result = JSON.stringify(value);
5673
- if (result === void 0) throw new Error("Unable to canonicalize string");
5674
- return result;
5675
- }
5676
- case "boolean":
5677
- return value ? "true" : "false";
5678
- case "number": {
5679
- if (!Number.isFinite(value)) throw new Error("Cannot canonicalize non-finite number");
5680
- const result = JSON.stringify(value);
5681
- if (result === void 0) throw new Error("Unable to canonicalize number");
5682
- return result;
5683
- }
5684
- case "object": {
5685
- if (Array.isArray(value)) {
5686
- for (let index = 0; index < value.length; index += 1) {
5687
- if (!hasOwn(value, String(index))) {
5688
- throw new Error("Cannot canonicalize sparse array");
5689
- }
5690
- }
5691
- return `[${value.map((item) => canonicalValue(item)).join(",")}]`;
5692
- }
5693
- const prototype = Object.getPrototypeOf(value);
5694
- if (prototype !== Object.prototype && prototype !== null) {
5695
- throw new Error("Cannot canonicalize non-plain object");
5696
- }
5697
- const object = value;
5698
- return `{${Object.keys(object).sort().map((key) => {
5699
- return `${JSON.stringify(key)}:${canonicalValue(object[key])}`;
5700
- }).join(",")}}`;
5701
- }
5702
- default:
5703
- throw new Error(`Cannot canonicalize ${typeof value}`);
5704
- }
5705
- }
5706
- function canonicalJson(value) {
5707
- return canonicalValue(value);
5708
- }
5709
- function sha256(value) {
5710
- return createHash2("sha256").update(value, "utf8").digest("hex");
5711
- }
5712
- function validateIdentity(value, expected) {
5713
- const identity = requireRecord(value, "identity");
5714
- assertExactKeys(identity, ["profile", "baseSha", "headSha", "policyDigest"], "identity");
5715
- const profile = requireText(identity.profile, "identity.profile");
5716
- if (!isQualityProfile2(profile)) throw new Error(`Invalid identity.profile: ${profile}`);
5717
- const baseSha = requireSha(identity.baseSha, "identity.baseSha", COMMIT_SHA_PATTERN);
5718
- const headSha = requireSha(identity.headSha, "identity.headSha", COMMIT_SHA_PATTERN);
5719
- const policyDigest = requireSha(identity.policyDigest, "identity.policyDigest", SHA256_PATTERN);
5720
- const actual = { profile, baseSha, headSha, policyDigest };
5721
- if (expected !== void 0) {
5722
- for (const field of ["profile", "baseSha", "headSha", "policyDigest"]) {
5723
- if (actual[field] !== expected[field]) {
5724
- throw new Error(`Quality receipt identity mismatch: ${field}`);
5725
- }
5726
- }
5727
- }
5728
- return actual;
5729
- }
5730
- function validateProvenance(value) {
5731
- const provenance = requireRecord(value, "provenance");
5732
- assertExactKeys(provenance, ["issuer", "executionId", "evidenceLocator", "evidenceDigest"], "provenance");
5733
- const issuer = requireText(provenance.issuer, "provenance.issuer");
5734
- const executionId = requireText(provenance.executionId, "provenance.executionId");
5735
- const evidenceLocator = requireText(provenance.evidenceLocator, "provenance.evidenceLocator");
5736
- if (!/^https?:\/\/\S+$/.test(evidenceLocator)) {
5737
- throw new Error("Invalid provenance.evidenceLocator");
5738
- }
5739
- try {
5740
- const parsed = new URL(evidenceLocator);
5741
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
5742
- throw new Error("unsupported locator protocol");
5743
- }
5744
- } catch {
5745
- throw new Error("Invalid provenance.evidenceLocator");
5746
- }
5747
- const evidenceDigest = requireSha(provenance.evidenceDigest, "provenance.evidenceDigest", SHA256_PATTERN);
5748
- return { issuer, executionId, evidenceLocator, evidenceDigest };
5749
- }
5750
- function validateCommand(value, index) {
5751
- const command = requireRecord(value, `commands[${index}]`);
5752
- assertExactKeys(
5753
- command,
5754
- ["commandId", "executable", "argv", "exitCode", "durationMs", "excerpt", "outputDigest"],
5755
- `commands[${index}]`
5756
- );
5757
- const commandId = requireText(command.commandId, `commands[${index}].commandId`);
5758
- const executable = requireText(command.executable, `commands[${index}].executable`);
5759
- if (!isDenseStringArray(command.argv)) {
5760
- throw new Error(`Invalid commands[${index}].argv`);
5761
- }
5762
- if (typeof command.exitCode !== "number" || !Number.isInteger(command.exitCode)) {
5763
- throw new Error(`Invalid commands[${index}].exitCode`);
5764
- }
5765
- if (typeof command.durationMs !== "number" || !Number.isFinite(command.durationMs) || command.durationMs < 0) {
5766
- throw new Error(`Invalid commands[${index}].durationMs`);
5767
- }
5768
- if (!hasOwn(command, "excerpt") || typeof command.excerpt !== "string") {
5769
- throw new Error(`Invalid commands[${index}].excerpt`);
5770
- }
5771
- const excerpt = command.excerpt;
5772
- if (Array.from(excerpt).length > MAX_EXCERPT_LENGTH) throw new Error(`Invalid commands[${index}].excerpt`);
5773
- if (redactText(excerpt) !== excerpt) {
5774
- throw new Error(`Invalid commands[${index}].excerpt: contains an unredacted secret`);
5775
- }
5776
- const argv = [...command.argv];
5777
- const sanitizedArgv = redactArgv(argv);
5778
- if (sanitizedArgv.some((argument, argumentIndex) => argument !== argv[argumentIndex])) {
5779
- throw new Error(`Invalid commands[${index}].argv: contains an unredacted secret`);
5780
- }
5781
- const outputDigest = requireSha(command.outputDigest, `commands[${index}].outputDigest`, SHA256_PATTERN);
5782
- return {
5783
- commandId,
5784
- executable,
5785
- argv,
5786
- exitCode: command.exitCode,
5787
- durationMs: command.durationMs,
5788
- excerpt,
5789
- outputDigest
5790
- };
5791
- }
5792
- function validateResult(value, index) {
5793
- const result = requireRecord(value, `results[${index}]`);
5794
- assertExactKeys(result, ["controlId", "status", "evidence", "reason"], `results[${index}]`);
5795
- const controlId = requireText(result.controlId, `results[${index}].controlId`);
5796
- const status = result.status;
5797
- if (typeof status !== "string" || !isQualityReceiptResultStatus(status)) {
5798
- throw new Error(`Invalid results[${index}].status`);
5799
- }
5800
- let evidence;
5801
- if (hasOwn(result, "evidence")) {
5802
- if (typeof result.evidence !== "string") {
5803
- throw new Error(`Invalid results[${index}].evidence`);
5804
- }
5805
- evidence = result.evidence;
5806
- }
5807
- let reason;
5808
- if (hasOwn(result, "reason")) {
5809
- if (typeof result.reason !== "string") {
5810
- throw new Error(`Invalid results[${index}].reason`);
5811
- }
5812
- reason = result.reason;
5813
- }
5814
- if (status === "pass") {
5815
- if (evidence === void 0 || evidence.trim() === "") {
5816
- throw new Error(`Invalid results[${index}].evidence: pass requires evidence`);
5817
- }
5818
- return {
5819
- controlId,
5820
- status,
5821
- evidence,
5822
- ...reason === void 0 ? {} : { reason }
5823
- };
5824
- }
5825
- return {
5826
- controlId,
5827
- status,
5828
- ...evidence === void 0 ? {} : { evidence },
5829
- ...reason === void 0 ? {} : { reason }
5830
- };
5831
- }
5832
- function validateQualityReceipt(value, expectedIdentity) {
5833
- const receipt = requireRecord(value, "quality receipt");
5834
- if (receipt.namespace !== QUALITY_RECEIPT_NAMESPACE) {
5835
- throw new Error(`Invalid quality receipt namespace: ${String(receipt.namespace)}`);
5836
- }
5837
- if (receipt.version !== QUALITY_RECEIPT_VERSION) {
5838
- throw new Error(`Unsupported quality receipt version: ${String(receipt.version)}`);
5839
- }
5840
- assertExactKeys(receipt, ["namespace", "version", "authority", "identity", "commands", "results", "provenance"], "receipt");
5841
- if (receipt.authority !== "local" && receipt.authority !== "enforced") {
5842
- throw new Error(`Invalid quality receipt authority: ${String(receipt.authority)}`);
5843
- }
5844
- const identity = validateIdentity(receipt.identity, expectedIdentity);
5845
- if (!Array.isArray(receipt.commands)) throw new Error("Invalid quality receipt commands");
5846
- if (!Array.isArray(receipt.results)) throw new Error("Invalid quality receipt results");
5847
- for (let index = 0; index < receipt.commands.length; index += 1) {
5848
- validateCommand(receipt.commands[index], index);
5849
- }
5850
- for (let index = 0; index < receipt.results.length; index += 1) {
5851
- validateResult(receipt.results[index], index);
5852
- }
5853
- const hasProvenance = hasOwn(receipt, "provenance");
5854
- if (receipt.authority === "enforced" && (!hasProvenance || receipt.provenance === void 0)) {
5855
- throw new Error("Enforced quality receipts require provenance");
5856
- }
5857
- if (hasProvenance) {
5858
- if (receipt.provenance === void 0) throw new Error("Invalid quality receipt provenance");
5859
- validateProvenance(receipt.provenance);
5860
- }
5861
- void identity;
5862
- }
5863
- function createQualityReceipt(input) {
5864
- const authority = input.authority;
5865
- if (authority !== "local" && authority !== "enforced") {
5866
- throw new Error(`Invalid quality receipt authority: ${String(authority)}`);
5867
- }
5868
- if (input.authority === "enforced" && input.provenance === void 0) {
5869
- throw new Error("Enforced quality receipts require provenance");
5870
- }
5871
- const base = {
5872
- namespace: QUALITY_RECEIPT_NAMESPACE,
5873
- version: QUALITY_RECEIPT_VERSION,
5874
- identity: {
5875
- profile: input.identity.profile,
5876
- baseSha: input.identity.baseSha,
5877
- headSha: input.identity.headSha,
5878
- policyDigest: input.identity.policyDigest
5879
- },
5880
- commands: input.commands.map(normalizeCommand),
5881
- results: input.results.map(normalizeResult)
5882
- };
5883
- let receipt;
5884
- if (input.authority === "enforced") {
5885
- const provenance = input.provenance;
5886
- if (provenance === void 0) throw new Error("Enforced quality receipts require provenance");
5887
- receipt = {
5888
- ...base,
5889
- authority: "enforced",
5890
- provenance: normalizeProvenance(provenance)
5891
- };
5892
- } else {
5893
- receipt = {
5894
- ...base,
5895
- authority: "local",
5896
- ...input.provenance === void 0 ? {} : { provenance: normalizeProvenance(input.provenance) }
5897
- };
5898
- }
5899
- validateQualityReceipt(receipt);
5900
- return receipt;
5901
- }
5902
- function serializeQualityReceipt(receipt) {
5903
- validateQualityReceipt(receipt);
5904
- return canonicalJson(receipt);
5905
- }
5906
-
5907
- // src/lib/quality-runner.ts
5908
5438
  function normalizeLimit(value, name) {
5909
5439
  if (value === void 0) return void 0;
5910
5440
  if (!Number.isSafeInteger(value) || value < 0) {
@@ -5922,14 +5452,14 @@ function normalizeTimeout(value) {
5922
5452
  }
5923
5453
  return timeoutMs;
5924
5454
  }
5925
- var COMMIT_SHA_PATTERN2 = /^[0-9a-f]{40}$/i;
5926
- function isRecord3(value) {
5455
+ var COMMIT_SHA_PATTERN = /^[0-9a-f]{40}$/i;
5456
+ function isRecord2(value) {
5927
5457
  return value !== null && typeof value === "object" && !Array.isArray(value);
5928
5458
  }
5929
- function hasText2(value) {
5459
+ function hasText(value) {
5930
5460
  return typeof value === "string" && value.trim() !== "";
5931
5461
  }
5932
- function isDenseStringArray2(value) {
5462
+ function isDenseStringArray(value) {
5933
5463
  if (!Array.isArray(value)) return false;
5934
5464
  for (let index = 0; index < value.length; index += 1) {
5935
5465
  if (!Object.prototype.hasOwnProperty.call(value, String(index)) || typeof value[index] !== "string") return false;
@@ -5939,30 +5469,30 @@ function isDenseStringArray2(value) {
5939
5469
  function isNonNegativeSafeInteger(value) {
5940
5470
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
5941
5471
  }
5942
- function isQualityProfile3(value) {
5472
+ function isQualityProfile(value) {
5943
5473
  return typeof value === "string" && QUALITY_PROFILES.includes(value);
5944
5474
  }
5945
5475
  function assertValidEnvironment(value, label) {
5946
- if (!isRecord3(value)) throw new Error(`Invalid ${label}`);
5476
+ if (!isRecord2(value)) throw new Error(`Invalid ${label}`);
5947
5477
  for (const [key, entry] of Object.entries(value)) {
5948
5478
  if (typeof entry !== "string") throw new Error(`Invalid ${label}.${key}`);
5949
5479
  }
5950
5480
  }
5951
5481
  function assertQualityPlanInput(value) {
5952
- if (!isRecord3(value)) throw new Error("Invalid quality plan");
5953
- if (!isRecord3(value.identity)) throw new Error("Invalid quality plan identity");
5954
- if (typeof value.identity.baseSha !== "string" || !COMMIT_SHA_PATTERN2.test(value.identity.baseSha)) {
5482
+ if (!isRecord2(value)) throw new Error("Invalid quality plan");
5483
+ if (!isRecord2(value.identity)) throw new Error("Invalid quality plan identity");
5484
+ if (typeof value.identity.baseSha !== "string" || !COMMIT_SHA_PATTERN.test(value.identity.baseSha)) {
5955
5485
  throw new Error("Invalid quality plan identity.baseSha");
5956
5486
  }
5957
- if (typeof value.identity.headSha !== "string" || !COMMIT_SHA_PATTERN2.test(value.identity.headSha)) {
5487
+ if (typeof value.identity.headSha !== "string" || !COMMIT_SHA_PATTERN.test(value.identity.headSha)) {
5958
5488
  throw new Error("Invalid quality plan identity.headSha");
5959
5489
  }
5960
- if (!isQualityProfile3(value.profile)) throw new Error("Invalid quality plan profile");
5490
+ if (!isQualityProfile(value.profile)) throw new Error("Invalid quality plan profile");
5961
5491
  if (!Array.isArray(value.controls)) throw new Error("Invalid quality plan controls");
5962
5492
  const controlIds = /* @__PURE__ */ new Set();
5963
5493
  for (let index = 0; index < value.controls.length; index += 1) {
5964
5494
  const control = value.controls[index];
5965
- if (!isRecord3(control) || !hasText2(control.id) || control.requirement !== "required" && control.requirement !== "optional") {
5495
+ if (!isRecord2(control) || !hasText(control.id) || control.requirement !== "required" && control.requirement !== "optional") {
5966
5496
  throw new Error(`Invalid quality plan controls[${index}]`);
5967
5497
  }
5968
5498
  if (controlIds.has(control.id)) {
@@ -5978,9 +5508,9 @@ function assertQualityPlanInput(value) {
5978
5508
  const commandControlIds = /* @__PURE__ */ new Set();
5979
5509
  for (let index = 0; index < value.commands.length; index += 1) {
5980
5510
  const command = value.commands[index];
5981
- if (!isRecord3(command)) throw new Error(`Invalid quality plan commands[${index}]`);
5511
+ if (!isRecord2(command)) throw new Error(`Invalid quality plan commands[${index}]`);
5982
5512
  const timeoutMs = command.timeoutMs;
5983
- if (!hasText2(command.controlId) || !hasText2(command.commandId) || !hasText2(command.executable) || !isDenseStringArray2(command.argv) || !isNonNegativeSafeInteger(timeoutMs)) {
5513
+ if (!hasText(command.controlId) || !hasText(command.commandId) || !hasText(command.executable) || !isDenseStringArray(command.argv) || !isNonNegativeSafeInteger(timeoutMs)) {
5984
5514
  throw new Error(`Invalid quality plan commands[${index}]`);
5985
5515
  }
5986
5516
  if (timeoutMs > MAX_NODE_TIMEOUT_MS) {
@@ -6035,7 +5565,7 @@ function killProcessTree(child) {
6035
5565
  if (pid === void 0) return;
6036
5566
  if (process.platform === "win32") {
6037
5567
  const systemRoot = process.env.SystemRoot ?? process.env.WINDIR ?? "C:\\Windows";
6038
- const taskkill = path33.join(systemRoot, "System32", "taskkill.exe");
5568
+ const taskkill = path32.join(systemRoot, "System32", "taskkill.exe");
6039
5569
  const result = spawnSync2(taskkill, ["/pid", String(pid), "/t", "/f"], {
6040
5570
  shell: false,
6041
5571
  stdio: "ignore",
@@ -6223,7 +5753,7 @@ function missingRequiredResults(controls, commands) {
6223
5753
  const commandControlIds = new Set(commands.map((command) => command.controlId));
6224
5754
  const missing = /* @__PURE__ */ new Set();
6225
5755
  for (const control of controls) {
6226
- if (control.requirement !== "required" || !hasText2(control.id) || commandControlIds.has(control.id)) continue;
5756
+ if (control.requirement !== "required" || !hasText(control.id) || commandControlIds.has(control.id)) continue;
6227
5757
  missing.add(control.id);
6228
5758
  }
6229
5759
  return [...missing].map((controlId) => ({