jorgex-stack 1.5.0 → 1.7.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,12 +20,13 @@ 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";
29
+ import { isDeepStrictEqual } from "util";
16
30
  import { pathToFileURL } from "url";
17
31
 
18
32
  // src/lib/canonical.ts
@@ -68,37 +82,12 @@ function loadCanonicalDefaults(stackDir) {
68
82
  }
69
83
 
70
84
  // 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";
85
+ import path3 from "path";
77
86
  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
87
 
99
88
  // src/lib/fsx.ts
100
89
  import fs2 from "fs";
101
- import path3 from "path";
90
+ import path2 from "path";
102
91
  function readTextIfExists(file) {
103
92
  try {
104
93
  return fs2.readFileSync(file, "utf8");
@@ -111,7 +100,7 @@ function ensureDir(dir) {
111
100
  }
112
101
  var tmpCounter = 0;
113
102
  function tmpPath(target) {
114
- return path3.join(path3.dirname(target), `.jorgex-${process.pid}-${tmpCounter++}.tmp`);
103
+ return path2.join(path2.dirname(target), `.jorgex-${process.pid}-${tmpCounter++}.tmp`);
115
104
  }
116
105
  function renameInto(tmp, target) {
117
106
  try {
@@ -122,23 +111,23 @@ function renameInto(tmp, target) {
122
111
  }
123
112
  }
124
113
  function writeText(file, content) {
125
- ensureDir(path3.dirname(file));
114
+ ensureDir(path2.dirname(file));
126
115
  const tmp = tmpPath(file);
127
116
  fs2.writeFileSync(tmp, content, "utf8");
128
117
  renameInto(tmp, file);
129
118
  }
130
119
  function copyFile(source, target) {
131
- ensureDir(path3.dirname(target));
120
+ ensureDir(path2.dirname(target));
132
121
  const tmp = tmpPath(target);
133
122
  fs2.copyFileSync(source, tmp);
134
123
  renameInto(tmp, target);
135
124
  }
136
125
  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);
126
+ const rel = path2.relative(path2.resolve(root), path2.resolve(child));
127
+ return rel !== "" && rel !== ".." && !rel.startsWith(`..${path2.sep}`) && !path2.isAbsolute(rel);
139
128
  }
140
129
  function pruneEmptyDirs(file, root) {
141
- let dir = path3.dirname(file);
130
+ let dir = path2.dirname(file);
142
131
  while (dir.startsWith(root) && dir !== root) {
143
132
  try {
144
133
  if (fs2.readdirSync(dir).length > 0) return;
@@ -146,7 +135,7 @@ function pruneEmptyDirs(file, root) {
146
135
  } catch {
147
136
  return;
148
137
  }
149
- dir = path3.dirname(dir);
138
+ dir = path2.dirname(dir);
150
139
  }
151
140
  }
152
141
  function sameFileContent(a, b) {
@@ -163,7 +152,7 @@ function listFilesRecursive(dir) {
163
152
  if (!fs2.existsSync(dir)) return out;
164
153
  const walk = (d) => {
165
154
  for (const entry of fs2.readdirSync(d, { withFileTypes: true })) {
166
- const p7 = path3.join(d, entry.name);
155
+ const p7 = path2.join(d, entry.name);
167
156
  if (entry.isDirectory()) walk(p7);
168
157
  else out.push(p7);
169
158
  }
@@ -198,7 +187,7 @@ var DEFAULT_MODEL_MAP = {
198
187
  }
199
188
  };
200
189
  function modelMapFile() {
201
- return path4.join(dataDir(), "model-map.json");
190
+ return path3.join(dataDir(), "model-map.json");
202
191
  }
203
192
  function loadModelMap() {
204
193
  const raw = readTextIfExists(modelMapFile());
@@ -217,22 +206,22 @@ function loadModelMap() {
217
206
  }
218
207
  function ensureModelMapFile() {
219
208
  const file = modelMapFile();
220
- if (!existsSync2(file)) {
209
+ if (!existsSync(file)) {
221
210
  writeText(file, JSON.stringify(DEFAULT_MODEL_MAP, null, 2) + "\n");
222
211
  }
223
212
  return file;
224
213
  }
225
214
 
226
215
  // src/lib/detect.ts
227
- import path5 from "path";
228
- import { existsSync as existsSync3, statSync } from "fs";
216
+ import path4 from "path";
217
+ import { existsSync as existsSync2, statSync } from "fs";
229
218
  import { execFileSync } from "child_process";
230
219
  function lookPath(cmd) {
231
220
  const exts = process.platform === "win32" ? [".exe", ".cmd", ".bat", ".ps1", ""] : [""];
232
- for (const dir of (process.env.PATH ?? "").split(path5.delimiter)) {
221
+ for (const dir of (process.env.PATH ?? "").split(path4.delimiter)) {
233
222
  if (!dir) continue;
234
223
  for (const ext of exts) {
235
- const candidate = path5.join(dir, cmd + ext);
224
+ const candidate = path4.join(dir, cmd + ext);
236
225
  if (statSync(candidate, { throwIfNoEntry: false })?.isFile()) return candidate;
237
226
  }
238
227
  }
@@ -261,50 +250,50 @@ function runDetectedBin(bin, args, timeoutMs) {
261
250
  }
262
251
  }
263
252
  function detectOpenCode() {
264
- const configDir = process.env.OPENCODE_CONFIG_DIR ?? path5.join(HOME, ".config", "opencode");
253
+ const configDir = process.env.OPENCODE_CONFIG_DIR ?? path4.join(HOME, ".config", "opencode");
265
254
  const binPath = lookPath("opencode");
266
255
  return {
267
256
  id: "opencode",
268
257
  name: "OpenCode",
269
- installed: binPath !== null || existsSync3(configDir),
258
+ installed: binPath !== null || existsSync2(configDir),
270
259
  binPath,
271
260
  configDir
272
261
  };
273
262
  }
274
263
  function detectClaudeCode() {
275
- const configDir = path5.join(HOME, ".claude");
264
+ const configDir = path4.join(HOME, ".claude");
276
265
  const binPath = lookPath("claude");
277
266
  return {
278
267
  id: "claude-code",
279
268
  name: "Claude Code",
280
- installed: binPath !== null || existsSync3(configDir),
269
+ installed: binPath !== null || existsSync2(configDir),
281
270
  binPath,
282
271
  configDir
283
272
  };
284
273
  }
285
274
  function detectCodex() {
286
- const configDir = process.env.CODEX_HOME ?? path5.join(HOME, ".codex");
275
+ const configDir = process.env.CODEX_HOME ?? path4.join(HOME, ".codex");
287
276
  const binPath = lookPath("codex");
288
277
  return {
289
278
  id: "codex",
290
279
  name: "Codex CLI",
291
- installed: binPath !== null || existsSync3(configDir),
280
+ installed: binPath !== null || existsSync2(configDir),
292
281
  binPath,
293
282
  configDir
294
283
  };
295
284
  }
296
285
  function detectEngram() {
297
286
  const fromEnv = process.env.ENGRAM_BIN;
298
- if (fromEnv && existsSync3(fromEnv)) return fromEnv;
287
+ if (fromEnv && existsSync2(fromEnv)) return fromEnv;
299
288
  const onPath = lookPath("engram");
300
289
  if (onPath) return onPath;
301
290
  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")
291
+ path4.join(HOME, "go", "bin", "engram.exe"),
292
+ path4.join(HOME, "go", "bin", "engram"),
293
+ path4.join(HOME, ".local", "bin", "engram.exe"),
294
+ path4.join(HOME, ".local", "bin", "engram")
306
295
  ];
307
- for (const c of candidates) if (existsSync3(c)) return c;
296
+ for (const c of candidates) if (existsSync2(c)) return c;
308
297
  return null;
309
298
  }
310
299
 
@@ -312,15 +301,19 @@ function detectEngram() {
312
301
  function markers(name) {
313
302
  return { open: `<!-- jorgex:${name} -->`, close: `<!-- /jorgex:${name} -->` };
314
303
  }
304
+ function hasHealthyManagedMarkdownMarkers(existing, name) {
305
+ const { open, close } = markers(name);
306
+ const count = (marker) => existing.split(marker).length - 1;
307
+ const onOwnLine = (marker) => existing.split("\n").some((line) => line.trim() === marker);
308
+ return count(open) === 1 && count(close) === 1 && existing.indexOf(close) > existing.indexOf(open) && onOwnLine(open) && onOwnLine(close);
309
+ }
315
310
  function repairOrphanMarkers(existing, name) {
316
311
  const { open, close } = markers(name);
317
312
  const count = (marker) => existing.split(marker).length - 1;
318
313
  const opens = count(open);
319
314
  const closes = count(close);
320
315
  if (opens === 0 && closes === 0) return existing;
321
- const onOwnLine = (marker) => existing.split("\n").some((line) => line.trim() === marker);
322
- const healthy = opens === 1 && closes === 1 && existing.indexOf(close) > existing.indexOf(open) && onOwnLine(open) && onOwnLine(close);
323
- if (healthy) return existing;
316
+ if (hasHealthyManagedMarkdownMarkers(existing, name)) return existing;
324
317
  return existing.split("\n").map((line) => line.trim() === open || line.trim() === close ? null : line.split(open).join("").split(close).join("")).filter((line) => line !== null).join("\n");
325
318
  }
326
319
  function upsertMarkdownSection(existing, name, content) {
@@ -473,9 +466,9 @@ function readTomlSection(existing, section) {
473
466
  }
474
467
 
475
468
  // src/lib/hooks-format.ts
476
- import path6 from "path";
469
+ import path5 from "path";
477
470
  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));
471
+ 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
472
  }
480
473
  function plainHookCommands(canonical) {
481
474
  return new Set(
@@ -522,7 +515,7 @@ function upsertNativeHooks(existing, canonical, scriptsDir, mapMatcher = (m) =>
522
515
  ...h.timeout !== void 0 ? { timeout: h.timeout } : {}
523
516
  }))
524
517
  };
525
- const scriptNames = entry.hooks.map((h) => /\{\{SCRIPTS_DIR\}\}[/\\]([\w./-]+)/.exec(h.command)?.[1]).filter((s) => s !== void 0).map((s) => path6.basename(s));
518
+ const scriptNames = entry.hooks.map((h) => /\{\{SCRIPTS_DIR\}\}[/\\]([\w./-]+)/.exec(h.command)?.[1]).filter((s) => s !== void 0).map((s) => path5.basename(s));
526
519
  const plainCommands = entry.hooks.map((h) => h.command).filter((c) => !c.includes("{{SCRIPTS_DIR}}"));
527
520
  const index = list.findIndex((existingEntry) => {
528
521
  const e = existingEntry;
@@ -552,6 +545,79 @@ var DESTRUCTIVE_GIT_DENY = [
552
545
  ];
553
546
  var GIT_GUARD_SCRIPT = "block-destructive-git.cjs";
554
547
 
548
+ // src/lib/quality-capabilities.ts
549
+ var QUALITY_CAPABILITY_NAMESPACE = "jorgex.quality.capabilities";
550
+ var QUALITY_CAPABILITY_VERSION = 1;
551
+ var QUALITY_CAPABILITY_IDS = [
552
+ "policy-guidance",
553
+ "tool-approval",
554
+ "external-verification"
555
+ ];
556
+ var UNAVAILABLE_REASON = "No reviewed local declaration is available";
557
+ var EXTERNAL_VERIFICATION_REASON = "External verification is available only through the external verifier";
558
+ function hasText(value) {
559
+ return typeof value === "string" && value.trim() !== "";
560
+ }
561
+ function isRuntime(value) {
562
+ return value === "claude-code" || value === "codex" || value === "opencode" || value === "pi";
563
+ }
564
+ function hasValidEvidence(value) {
565
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
566
+ const evidence = value;
567
+ return hasText(evidence.source) && hasText(evidence.version);
568
+ }
569
+ function unavailableCapability(id, reason = UNAVAILABLE_REASON) {
570
+ return { id, state: "unavailable", reason };
571
+ }
572
+ function declarationFor(id, declarations, runtime) {
573
+ if (runtime === "unknown") {
574
+ return unavailableCapability(id, "Runtime is unknown; local capability cannot be established");
575
+ }
576
+ if (id === "external-verification") {
577
+ return unavailableCapability(id, EXTERNAL_VERIFICATION_REASON);
578
+ }
579
+ const matches = declarations.filter((declaration2) => declaration2?.id === id);
580
+ if (matches.length === 0) return unavailableCapability(id);
581
+ if (matches.length !== 1) return unavailableCapability(id, "Duplicate capability declarations are ambiguous");
582
+ const declaration = matches[0];
583
+ const state = declaration.state;
584
+ const reason = hasText(declaration.reason) ? declaration.reason.trim() : void 0;
585
+ if (!reason) return unavailableCapability(id, "Capability declaration has no reason");
586
+ if (state !== "prompt-only" && state !== "manual" && state !== "unavailable") {
587
+ return unavailableCapability(id, "Capability state is not a supported local state");
588
+ }
589
+ if (state === "unavailable") return { id, state, reason };
590
+ if (!hasValidEvidence(declaration.evidence)) {
591
+ return unavailableCapability(id, "Capability declaration lacks reviewed source/version evidence");
592
+ }
593
+ const evidence = declaration.evidence;
594
+ return {
595
+ id,
596
+ state,
597
+ reason,
598
+ evidence: {
599
+ source: evidence.source.trim(),
600
+ version: evidence.version.trim()
601
+ }
602
+ };
603
+ }
604
+ function hasManagedMarkdownSection(content, name) {
605
+ return content !== null && hasHealthyManagedMarkdownMarkers(content, name);
606
+ }
607
+ function createLocalCapabilityReport(runtime, declarations) {
608
+ const normalizedRuntime = isRuntime(runtime) ? runtime : "unknown";
609
+ return {
610
+ namespace: QUALITY_CAPABILITY_NAMESPACE,
611
+ version: QUALITY_CAPABILITY_VERSION,
612
+ runtime: normalizedRuntime,
613
+ capabilities: QUALITY_CAPABILITY_IDS.map((id) => declarationFor(id, declarations, normalizedRuntime))
614
+ };
615
+ }
616
+ function localQualityStatus(profile, status) {
617
+ if ((profile === "high" || profile === "release") && status === "pass") return "incomplete";
618
+ return status;
619
+ }
620
+
555
621
  // src/adapters/opencode.ts
556
622
  function yamlString(value) {
557
623
  return JSON.stringify(value);
@@ -592,26 +658,55 @@ function pruneEmpty(parent, key) {
592
658
  const value = objectValue(parent[key]);
593
659
  if (value !== null && Object.keys(value).length === 0) delete parent[key];
594
660
  }
661
+ function hasOpenCodeManualApproval(configDir) {
662
+ const content = readTextIfExists(path6.join(configDir, "opencode.json"));
663
+ if (content === null) return false;
664
+ try {
665
+ const root = JSON.parse(content);
666
+ const permission = objectValue(objectValue(root)?.permission);
667
+ const expected = loadCanonicalDefaults(stackRoot())["opencode"]?.["permission"];
668
+ return permission !== null && expected !== void 0 && isDeepStrictEqual(permission, expected);
669
+ } catch {
670
+ return false;
671
+ }
672
+ }
595
673
  var opencodeAdapter = {
596
674
  id: "opencode",
597
675
  name: "OpenCode",
598
676
  detect: detectOpenCode,
677
+ reportCapabilities(configDir) {
678
+ const prompt = readTextIfExists(path6.join(configDir, "AGENTS.md"));
679
+ return createLocalCapabilityReport("opencode", [
680
+ ...hasManagedMarkdownSection(prompt, "system-prompt") ? [{
681
+ id: "policy-guidance",
682
+ state: "prompt-only",
683
+ reason: "The managed policy prompt is advisory and cannot enforce the policy",
684
+ evidence: { source: "jorgex-stack-system-prompt", version: "1" }
685
+ }] : [],
686
+ ...hasOpenCodeManualApproval(configDir) ? [{
687
+ id: "tool-approval",
688
+ state: "manual",
689
+ reason: "Canonical approval declarations require a human decision; runtime activation is not certified",
690
+ evidence: { source: "jorgex-stack-opencode-approval-policy", version: "1" }
691
+ }] : []
692
+ ]);
693
+ },
599
694
  injectEngramProtocol() {
600
695
  return false;
601
696
  },
602
697
  paths(configDir) {
603
698
  const isRealConfigDir = samePath(
604
699
  configDir,
605
- process.env.OPENCODE_CONFIG_DIR ?? path7.join(HOME, ".config", "opencode")
700
+ process.env.OPENCODE_CONFIG_DIR ?? path6.join(HOME, ".config", "opencode")
606
701
  );
607
- const agentsHome = isRealConfigDir ? HOME : path7.dirname(configDir);
702
+ const agentsHome = isRealConfigDir ? HOME : path6.dirname(configDir);
608
703
  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"),
704
+ systemPromptFile: path6.join(configDir, "AGENTS.md"),
705
+ agentsDir: path6.join(configDir, "agents"),
706
+ skillsDir: path6.join(agentsHome, ".agents", "skills"),
707
+ commandsDir: path6.join(configDir, "commands"),
708
+ pluginsDir: path6.join(configDir, "plugins"),
709
+ scriptsDir: path6.join(configDir, "scripts"),
615
710
  outputStylesDir: null,
616
711
  profilesDir: null
617
712
  };
@@ -669,12 +764,12 @@ ${agent.body}`,
669
764
  ctx.warnings.push(`opencode: hook sin {{SCRIPTS_DIR}} no traducible: ${hook.command}`);
670
765
  continue;
671
766
  }
672
- const script = `scripts/${path7.basename(match[1])}`;
767
+ const script = `scripts/${path6.basename(match[1])}`;
673
768
  (bashEntries[includes] ??= []).push(script);
674
769
  }
675
770
  }
676
771
  }
677
- const hooksFile = path7.join(ctx.configDir, "hooks.json");
772
+ const hooksFile = path6.join(ctx.configDir, "hooks.json");
678
773
  const content = upsertJson(readTextIfExists(hooksFile), (root) => {
679
774
  const afterValue = root["tool.execute.after"] ??= {};
680
775
  if (afterValue === null || typeof afterValue !== "object" || Array.isArray(afterValue)) {
@@ -709,16 +804,16 @@ ${agent.body}`,
709
804
  }
710
805
  });
711
806
  actions.push({ kind: "write", target: hooksFile, content });
712
- const scriptsSource = path7.join(ctx.stackDir, "scripts");
807
+ const scriptsSource = path6.join(ctx.stackDir, "scripts");
713
808
  if (fs3.existsSync(scriptsSource)) {
714
809
  for (const f of fs3.readdirSync(scriptsSource)) {
715
- actions.push({ kind: "copy", source: path7.join(scriptsSource, f), target: path7.join(scriptsDir, f) });
810
+ actions.push({ kind: "copy", source: path6.join(scriptsSource, f), target: path6.join(scriptsDir, f) });
716
811
  }
717
812
  }
718
813
  return actions;
719
814
  },
720
815
  planMainConfig(canonical, ctx) {
721
- const file = path7.join(ctx.configDir, "opencode.json");
816
+ const file = path6.join(ctx.configDir, "opencode.json");
722
817
  const { pluginsDir } = this.paths(ctx.configDir);
723
818
  const original = readTextIfExists(file);
724
819
  const contentSource = original === null || original.trim() === "" ? null : original;
@@ -830,7 +925,7 @@ ${agent.body}`,
830
925
  content = removeMarkdownSection(content, "browser");
831
926
  actions.push({ kind: "write", target: systemPromptFile, content });
832
927
  }
833
- const configFile = path7.join(ctx.configDir, "opencode.json");
928
+ const configFile = path6.join(ctx.configDir, "opencode.json");
834
929
  const config = readTextIfExists(configFile);
835
930
  if (config !== null) {
836
931
  const mcpOwnership = [];
@@ -898,7 +993,7 @@ ${agent.body}`,
898
993
  ...primaryModelOwnership.length > 0 ? { primaryModelOwnership } : {}
899
994
  });
900
995
  }
901
- const hooksFile = path7.join(ctx.configDir, "hooks.json");
996
+ const hooksFile = path6.join(ctx.configDir, "hooks.json");
902
997
  const hooksJson = readTextIfExists(hooksFile);
903
998
  if (hooksJson !== null) {
904
999
  const ourScripts = hookScriptNames(hooks);
@@ -924,8 +1019,9 @@ ${agent.body}`,
924
1019
  };
925
1020
 
926
1021
  // src/adapters/claude-code.ts
927
- import path8 from "path";
1022
+ import path7 from "path";
928
1023
  import fs4 from "fs";
1024
+ import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
929
1025
  function yamlString2(value) {
930
1026
  return JSON.stringify(value);
931
1027
  }
@@ -936,6 +1032,21 @@ var ENGRAM_AGENT_TOOLS = [
936
1032
  "Skill",
937
1033
  ...memoryTools(["mem_context", "mem_search", "mem_get_observation", "mem_timeline", "mem_current_project"])
938
1034
  ];
1035
+ function isRecord(value) {
1036
+ return value !== null && typeof value === "object" && !Array.isArray(value);
1037
+ }
1038
+ function hasClaudeManualApproval(configDir) {
1039
+ const content = readTextIfExists(path7.join(configDir, "settings.json"));
1040
+ if (content === null) return false;
1041
+ try {
1042
+ const root = JSON.parse(content);
1043
+ const permissions = isRecord(root) ? root.permissions : void 0;
1044
+ const expected = loadCanonicalDefaults(stackRoot())["claude-code"]?.["permissions"];
1045
+ return isRecord(permissions) && expected !== void 0 && isDeepStrictEqual2(permissions, expected);
1046
+ } catch {
1047
+ return false;
1048
+ }
1049
+ }
939
1050
  function toolsFor(agent) {
940
1051
  if (agent.name === "engram") return ENGRAM_AGENT_TOOLS.join(", ");
941
1052
  if (!agent.readonly) return null;
@@ -945,8 +1056,8 @@ function toolsFor(agent) {
945
1056
  return tools.join(", ");
946
1057
  }
947
1058
  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"));
1059
+ if (fs4.existsSync(path7.join(configDir, "plugins", "marketplaces", "engram"))) return true;
1060
+ const registry = readTextIfExists(path7.join(configDir, "plugins", "installed_plugins.json"));
950
1061
  if (registry === null) return false;
951
1062
  try {
952
1063
  const parsed = JSON.parse(registry);
@@ -968,18 +1079,35 @@ var claudeCodeAdapter = {
968
1079
  id: "claude-code",
969
1080
  name: "Claude Code",
970
1081
  detect: detectClaudeCode,
1082
+ reportCapabilities(configDir) {
1083
+ const prompt = readTextIfExists(path7.join(configDir, "CLAUDE.md"));
1084
+ return createLocalCapabilityReport("claude-code", [
1085
+ ...hasManagedMarkdownSection(prompt, "system-prompt") ? [{
1086
+ id: "policy-guidance",
1087
+ state: "prompt-only",
1088
+ reason: "The managed policy prompt is advisory and cannot enforce the policy",
1089
+ evidence: { source: "jorgex-stack-system-prompt", version: "1" }
1090
+ }] : [],
1091
+ ...hasClaudeManualApproval(configDir) ? [{
1092
+ id: "tool-approval",
1093
+ state: "manual",
1094
+ reason: "Canonical approval declarations require a human decision; runtime activation is not certified",
1095
+ evidence: { source: "jorgex-stack-claude-approval-policy", version: "1" }
1096
+ }] : []
1097
+ ]);
1098
+ },
971
1099
  injectEngramProtocol(ctx) {
972
1100
  return !hasEngramPlugin(ctx.configDir);
973
1101
  },
974
1102
  paths(configDir) {
975
1103
  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"),
1104
+ systemPromptFile: path7.join(configDir, "CLAUDE.md"),
1105
+ agentsDir: path7.join(configDir, "agents"),
1106
+ skillsDir: path7.join(configDir, "skills"),
1107
+ commandsDir: path7.join(configDir, "commands"),
980
1108
  pluginsDir: null,
981
- scriptsDir: path8.join(configDir, "scripts"),
982
- outputStylesDir: path8.join(configDir, "output-styles"),
1109
+ scriptsDir: path7.join(configDir, "scripts"),
1110
+ outputStylesDir: path7.join(configDir, "output-styles"),
983
1111
  profilesDir: null
984
1112
  };
985
1113
  },
@@ -1025,9 +1153,9 @@ ${agent.body}`,
1025
1153
  planHooks(canonical, ctx) {
1026
1154
  const actions = [];
1027
1155
  const { scriptsDir } = this.paths(ctx.configDir);
1028
- const original = readTextIfExists(path8.join(ctx.configDir, "settings.json"));
1156
+ const original = readTextIfExists(path7.join(ctx.configDir, "settings.json"));
1029
1157
  const contentSource = original === null || original.trim() === "" ? null : original;
1030
- const settingsFile = path8.join(ctx.configDir, "settings.json");
1158
+ const settingsFile = path7.join(ctx.configDir, "settings.json");
1031
1159
  let content = upsertNativeHooks(contentSource, canonical, scriptsDir);
1032
1160
  const defaults = loadCanonicalDefaults(ctx.stackDir)["claude-code"];
1033
1161
  if (contentSource === null && defaults?.["permissions"] !== void 0) {
@@ -1041,16 +1169,16 @@ ${agent.body}`,
1041
1169
  });
1042
1170
  }
1043
1171
  actions.push({ kind: "write", target: settingsFile, content });
1044
- const scriptsSource = path8.join(ctx.stackDir, "scripts");
1172
+ const scriptsSource = path7.join(ctx.stackDir, "scripts");
1045
1173
  if (fs4.existsSync(scriptsSource)) {
1046
1174
  for (const f of fs4.readdirSync(scriptsSource)) {
1047
- actions.push({ kind: "copy", source: path8.join(scriptsSource, f), target: path8.join(scriptsDir, f) });
1175
+ actions.push({ kind: "copy", source: path7.join(scriptsSource, f), target: path7.join(scriptsDir, f) });
1048
1176
  }
1049
1177
  }
1050
1178
  return actions;
1051
1179
  },
1052
1180
  planMainConfig(canonical, ctx) {
1053
- const file = path8.join(path8.dirname(ctx.configDir), `${path8.basename(ctx.configDir)}.json`);
1181
+ const file = path7.join(path7.dirname(ctx.configDir), `${path7.basename(ctx.configDir)}.json`);
1054
1182
  const mcpOwnership = [];
1055
1183
  const content = upsertJson(readTextIfExists(file), (root) => {
1056
1184
  const servers = root["mcpServers"] ??= {};
@@ -1115,7 +1243,7 @@ ${agent.body}`,
1115
1243
  content = removeMarkdownSection(content, "browser");
1116
1244
  actions.push({ kind: "write", target: systemPromptFile, content });
1117
1245
  }
1118
- const settingsFile = path8.join(ctx.configDir, "settings.json");
1246
+ const settingsFile = path7.join(ctx.configDir, "settings.json");
1119
1247
  const settings = readTextIfExists(settingsFile);
1120
1248
  if (settings !== null) {
1121
1249
  const content = removeNativeHooks(settings, hooks);
@@ -1123,7 +1251,7 @@ ${agent.body}`,
1123
1251
  actions.push({ kind: "write", target: settingsFile, content: content.trim() === "{}" ? "" : content });
1124
1252
  }
1125
1253
  }
1126
- const mainFile = path8.join(path8.dirname(ctx.configDir), `${path8.basename(ctx.configDir)}.json`);
1254
+ const mainFile = path7.join(path7.dirname(ctx.configDir), `${path7.basename(ctx.configDir)}.json`);
1127
1255
  const main2 = readTextIfExists(mainFile);
1128
1256
  if (main2 !== null) {
1129
1257
  const mcpOwnership = [];
@@ -1149,7 +1277,7 @@ ${agent.body}`,
1149
1277
  };
1150
1278
 
1151
1279
  // src/adapters/codex.ts
1152
- import path9 from "path";
1280
+ import path8 from "path";
1153
1281
  import fs5 from "fs";
1154
1282
  function tomlString(value) {
1155
1283
  return JSON.stringify(value);
@@ -1173,37 +1301,194 @@ function isManagedOptionalStdioServer3(server, section) {
1173
1301
  return server.optional === true && server.transport === "stdio" && section?.trim() === stdioMcpSection(server);
1174
1302
  }
1175
1303
  function hasActiveEngramPlugin(configDir) {
1176
- const config = readTextIfExists(path9.join(configDir, "config.toml"));
1304
+ const config = readTextIfExists(path8.join(configDir, "config.toml"));
1177
1305
  if (config === null) return false;
1178
1306
  const match = /\[plugins\."engram@[^"]*"\]([^[]*)/.exec(config);
1179
1307
  return match !== null && !/enabled\s*=\s*false/.test(match[1]);
1180
1308
  }
1181
1309
  function hasEngramProtocol(configDir) {
1182
1310
  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"));
1311
+ if (fs5.existsSync(path8.join(configDir, "engram-instructions.md"))) return true;
1312
+ const config = readTextIfExists(path8.join(configDir, "config.toml"));
1185
1313
  return config !== null && /engram-instructions\.md/.test(config);
1186
1314
  }
1315
+ var CODEX_JSON_STRING = String.raw`"(?:\\.|[^"\\\r\n])*"`;
1316
+ var CODEX_JSON_VALUE = String.raw`(?:${CODEX_JSON_STRING}|-?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?|true|false)`;
1317
+ var CODEX_JSON_STRING_ARRAY = String.raw`\[(?:\s*${CODEX_JSON_STRING}(?:\s*,\s*${CODEX_JSON_STRING})*\s*)?\]`;
1318
+ var CODEX_KEY = String.raw`(?:[A-Za-z0-9_-]+|${CODEX_JSON_STRING})`;
1319
+ var CODEX_ASSIGNMENT = new RegExp(String.raw`^\s*(${CODEX_KEY})\s*=\s*(${CODEX_JSON_STRING_ARRAY}|${CODEX_JSON_VALUE})\s*(?:#.*)?$`);
1320
+ var CODEX_HEADER = new RegExp(String.raw`^\[\s*(${CODEX_KEY}(?:\s*\.\s*${CODEX_KEY})*)\s*\]\s*(?:#.*)?$`);
1321
+ var CODEX_PERMISSION_HEADERS = {
1322
+ base: "permissions.jorgex-read-anywhere",
1323
+ filesystem: "permissions.jorgex-read-anywhere.filesystem",
1324
+ workspaceRoots: 'permissions.jorgex-read-anywhere.filesystem.":workspace_roots"'
1325
+ };
1326
+ var CODEX_PERMISSION_PROFILE = {
1327
+ base: [["extends", ":workspace"]],
1328
+ filesystem: [
1329
+ [":root", "read"],
1330
+ ["*.env", "deny"],
1331
+ ["*.env.*", "deny"],
1332
+ ["~/.ssh/**", "deny"],
1333
+ ["~/.aws/credentials", "deny"],
1334
+ ["~/.npmrc", "deny"],
1335
+ ["~/.git-credentials", "deny"],
1336
+ ["**/id_rsa", "deny"],
1337
+ ["**/id_ed25519", "deny"],
1338
+ ["**/*.pem", "deny"],
1339
+ ["**/*.key", "deny"]
1340
+ ],
1341
+ workspaceRoots: [
1342
+ [".", "write"],
1343
+ ["*.env", "deny"],
1344
+ ["*.env.*", "deny"],
1345
+ [".ssh/**", "deny"],
1346
+ [".aws/credentials", "deny"],
1347
+ [".npmrc", "deny"],
1348
+ [".git-credentials", "deny"],
1349
+ ["**/id_rsa", "deny"],
1350
+ ["**/id_ed25519", "deny"],
1351
+ ["**/*.pem", "deny"],
1352
+ ["**/*.key", "deny"]
1353
+ ]
1354
+ };
1355
+ var CODEX_PERMISSION_SECTIONS = [
1356
+ { header: CODEX_PERMISSION_HEADERS.base, entries: CODEX_PERMISSION_PROFILE.base, quoteKeys: false },
1357
+ { header: CODEX_PERMISSION_HEADERS.filesystem, entries: CODEX_PERMISSION_PROFILE.filesystem, quoteKeys: true },
1358
+ { header: CODEX_PERMISSION_HEADERS.workspaceRoots, entries: CODEX_PERMISSION_PROFILE.workspaceRoots, quoteKeys: true }
1359
+ ];
1360
+ function renderCodexPermissionEntries(entries, quoteKeys) {
1361
+ return entries.map(([key, value]) => `${quoteKeys ? JSON.stringify(key) : key} = ${JSON.stringify(value)}`).join("\n");
1362
+ }
1363
+ function parseCodexKey(raw) {
1364
+ if (!raw.startsWith('"')) return raw;
1365
+ try {
1366
+ const value = JSON.parse(raw);
1367
+ return typeof value === "string" ? value : null;
1368
+ } catch {
1369
+ return null;
1370
+ }
1371
+ }
1372
+ function parseCodexValue(raw) {
1373
+ try {
1374
+ const value = JSON.parse(raw);
1375
+ return Array.isArray(value) && value.every((item) => typeof item === "string") || !Array.isArray(value) && (value === null || typeof value !== "object") ? value : void 0;
1376
+ } catch {
1377
+ return void 0;
1378
+ }
1379
+ }
1380
+ function parseCodexHeader(line) {
1381
+ const match = CODEX_HEADER.exec(line.trim());
1382
+ if (match === null) return null;
1383
+ for (const quoted of match[1].match(/"(?:\\.|[^"\\\r\n])*"/g) ?? []) {
1384
+ if (parseCodexKey(quoted) === null) return null;
1385
+ }
1386
+ return match[1].replace(/\s*\.\s*/g, ".");
1387
+ }
1388
+ function scanCodexToml(config) {
1389
+ if (config.includes("'''") || config.includes('"""')) return null;
1390
+ const root = /* @__PURE__ */ new Map();
1391
+ const sections = /* @__PURE__ */ new Map();
1392
+ const headers = /* @__PURE__ */ new Set();
1393
+ const keys = /* @__PURE__ */ new Set();
1394
+ let section = null;
1395
+ for (const line of config.replace(/\r\n/g, "\n").split("\n")) {
1396
+ const trimmed = line.trim();
1397
+ if (trimmed === "" || trimmed.startsWith("#")) continue;
1398
+ if (trimmed.startsWith("[")) {
1399
+ const header = parseCodexHeader(trimmed);
1400
+ if (header === null || headers.has(header)) return null;
1401
+ headers.add(header);
1402
+ if (header.startsWith(`${CODEX_PERMISSION_HEADERS.base}.`) && !CODEX_PERMISSION_SECTIONS.some((entry) => entry.header === header)) {
1403
+ return null;
1404
+ }
1405
+ section = header;
1406
+ if (CODEX_PERMISSION_SECTIONS.some((entry) => entry.header === header)) sections.set(header, /* @__PURE__ */ new Map());
1407
+ continue;
1408
+ }
1409
+ const relevant = section === null || CODEX_PERMISSION_SECTIONS.some((entry) => entry.header === section);
1410
+ const match = CODEX_ASSIGNMENT.exec(trimmed);
1411
+ if (!relevant && match === null) continue;
1412
+ if (match === null) return null;
1413
+ const key = parseCodexKey(match[1]);
1414
+ const value = parseCodexValue(match[2]);
1415
+ if (key === null || value === void 0) return null;
1416
+ if (section === null && (key === "profile" || key === "sandbox_mode")) return null;
1417
+ const scope = section ?? "<root>";
1418
+ const declaration = `${scope}\0${key}`;
1419
+ if (keys.has(declaration)) return null;
1420
+ keys.add(declaration);
1421
+ if (section === null) root.set(key, value);
1422
+ else if (sections.has(section)) sections.get(section).set(key, value);
1423
+ }
1424
+ return { root, sections };
1425
+ }
1426
+ function hasCanonicalCodexPermissionProfile(scan) {
1427
+ for (const { header, entries } of CODEX_PERMISSION_SECTIONS) {
1428
+ const actual = scan.sections.get(header);
1429
+ if (actual === void 0 || actual.size !== entries.length) return false;
1430
+ for (const [key, expected] of entries) {
1431
+ if (actual.get(key) !== expected) return false;
1432
+ }
1433
+ }
1434
+ return true;
1435
+ }
1436
+ function hasCodexManualApproval(configDir) {
1437
+ const config = readTextIfExists(path8.join(configDir, "config.toml"));
1438
+ if (config === null) return false;
1439
+ const scan = scanCodexToml(config);
1440
+ if (scan === null) return false;
1441
+ try {
1442
+ const defaults = loadCanonicalDefaults(stackRoot())["codex"];
1443
+ const expectedApproval = defaults?.["approval_policy"];
1444
+ const expectedPermissions = defaults?.["default_permissions"];
1445
+ return typeof expectedApproval === "string" && typeof expectedPermissions === "string" && scan.root.get("approval_policy") === expectedApproval && scan.root.get("default_permissions") === expectedPermissions && hasCanonicalCodexPermissionProfile(scan);
1446
+ } catch {
1447
+ return false;
1448
+ }
1449
+ }
1187
1450
  var codexAdapter = {
1188
1451
  id: "codex",
1189
1452
  name: "Codex CLI",
1190
1453
  detect: detectCodex,
1454
+ reportCapabilities(configDir) {
1455
+ const prompt = readTextIfExists(path8.join(configDir, "AGENTS.md"));
1456
+ return createLocalCapabilityReport("codex", [
1457
+ ...hasManagedMarkdownSection(prompt, "system-prompt") ? [{
1458
+ id: "policy-guidance",
1459
+ state: "prompt-only",
1460
+ reason: "The managed policy prompt is advisory and cannot enforce the policy",
1461
+ evidence: { source: "jorgex-stack-system-prompt", version: "1" }
1462
+ }] : [],
1463
+ ...hasCodexManualApproval(configDir) ? [{
1464
+ id: "tool-approval",
1465
+ state: "manual",
1466
+ reason: "Canonical approval declarations require a human decision; runtime activation is not certified",
1467
+ evidence: { source: "jorgex-stack-codex-approval-policy", version: "1" }
1468
+ }] : [],
1469
+ {
1470
+ id: "external-verification",
1471
+ state: "unavailable",
1472
+ reason: "External verification is available only through the external verifier"
1473
+ }
1474
+ ]);
1475
+ },
1191
1476
  injectEngramProtocol(ctx) {
1192
1477
  return !hasEngramProtocol(ctx.configDir);
1193
1478
  },
1194
1479
  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");
1480
+ const isRealConfigDir = samePath(configDir, process.env.CODEX_HOME ?? path8.join(HOME, ".codex"));
1481
+ const agentsHome = isRealConfigDir ? HOME : path8.dirname(configDir);
1482
+ const skillsDir = path8.join(agentsHome, ".agents", "skills");
1198
1483
  return {
1199
- systemPromptFile: path9.join(configDir, "AGENTS.md"),
1200
- agentsDir: path9.join(configDir, "agents"),
1484
+ systemPromptFile: path8.join(configDir, "AGENTS.md"),
1485
+ agentsDir: path8.join(configDir, "agents"),
1201
1486
  skillsDir,
1202
1487
  // Los custom prompts de Codex están deprecados: los commands se
1203
1488
  // instalan como skills (renderCommand produce <nombre>/SKILL.md).
1204
1489
  commandsDir: skillsDir,
1205
1490
  pluginsDir: null,
1206
- scriptsDir: path9.join(configDir, "scripts"),
1491
+ scriptsDir: path8.join(configDir, "scripts"),
1207
1492
  outputStylesDir: null,
1208
1493
  profilesDir: configDir
1209
1494
  };
@@ -1251,7 +1536,7 @@ ${body}`
1251
1536
  planHooks(canonical, ctx) {
1252
1537
  const actions = [];
1253
1538
  const { scriptsDir } = this.paths(ctx.configDir);
1254
- const hooksFile = path9.join(ctx.configDir, "hooks.json");
1539
+ const hooksFile = path8.join(ctx.configDir, "hooks.json");
1255
1540
  const content = upsertNativeHooks(
1256
1541
  readTextIfExists(hooksFile),
1257
1542
  canonical,
@@ -1262,16 +1547,16 @@ ${body}`
1262
1547
  ctx.warnings.push(
1263
1548
  "Codex: los hooks no-managed requieren aprobaci\xF3n manual \u2014 ejecuta /hooks dentro de codex para activarlos."
1264
1549
  );
1265
- const scriptsSource = path9.join(ctx.stackDir, "scripts");
1550
+ const scriptsSource = path8.join(ctx.stackDir, "scripts");
1266
1551
  if (fs5.existsSync(scriptsSource)) {
1267
1552
  for (const f of fs5.readdirSync(scriptsSource)) {
1268
- actions.push({ kind: "copy", source: path9.join(scriptsSource, f), target: path9.join(scriptsDir, f) });
1553
+ actions.push({ kind: "copy", source: path8.join(scriptsSource, f), target: path8.join(scriptsDir, f) });
1269
1554
  }
1270
1555
  }
1271
1556
  return actions;
1272
1557
  },
1273
1558
  planMainConfig(canonical, ctx) {
1274
- const file = path9.join(ctx.configDir, "config.toml");
1559
+ const file = path8.join(ctx.configDir, "config.toml");
1275
1560
  const original = readTextIfExists(file);
1276
1561
  const contentSource = original === null || original.trim() === "" ? null : original;
1277
1562
  let content = contentSource;
@@ -1287,37 +1572,20 @@ ${body}`
1287
1572
  ctx.warnings.push(
1288
1573
  "Codex: fresh config enables read-anywhere via the jorgex-read-anywhere permission profile; broad local reads can expose secrets not covered by deny rules."
1289
1574
  );
1290
- content = upsertTomlSection(content, "permissions.jorgex-read-anywhere", 'extends = ":workspace"');
1291
1575
  content = upsertTomlSection(
1292
1576
  content,
1293
- "permissions.jorgex-read-anywhere.filesystem",
1294
- [
1295
- '":root" = "read"',
1296
- '"*.env" = "deny"',
1297
- '"*.env.*" = "deny"',
1298
- '"~/.ssh/**" = "deny"',
1299
- '"~/.aws/credentials" = "deny"',
1300
- '"~/.npmrc" = "deny"',
1301
- '"~/.git-credentials" = "deny"',
1302
- '"**/id_rsa" = "deny"',
1303
- '"**/id_ed25519" = "deny"',
1304
- '"**/*.pem" = "deny"',
1305
- '"**/*.key" = "deny"'
1306
- ].join("\n")
1577
+ CODEX_PERMISSION_HEADERS.base,
1578
+ renderCodexPermissionEntries(CODEX_PERMISSION_PROFILE.base, false)
1579
+ );
1580
+ content = upsertTomlSection(
1581
+ content,
1582
+ CODEX_PERMISSION_HEADERS.filesystem,
1583
+ renderCodexPermissionEntries(CODEX_PERMISSION_PROFILE.filesystem, true)
1307
1584
  );
1308
1585
  content += [
1309
- '\n[permissions.jorgex-read-anywhere.filesystem.":workspace_roots"]',
1310
- '"." = "write"',
1311
- '"*.env" = "deny"',
1312
- '"*.env.*" = "deny"',
1313
- '".ssh/**" = "deny"',
1314
- '".aws/credentials" = "deny"',
1315
- '".npmrc" = "deny"',
1316
- '".git-credentials" = "deny"',
1317
- '"**/id_rsa" = "deny"',
1318
- '"**/id_ed25519" = "deny"',
1319
- '"**/*.pem" = "deny"',
1320
- '"**/*.key" = "deny"',
1586
+ `
1587
+ [${CODEX_PERMISSION_HEADERS.workspaceRoots}]`,
1588
+ renderCodexPermissionEntries(CODEX_PERMISSION_PROFILE.workspaceRoots, true),
1321
1589
  ""
1322
1590
  ].join("\n");
1323
1591
  }
@@ -1407,7 +1675,7 @@ args = [${(server.args ?? []).map(tomlString).join(", ")}]`);
1407
1675
  content = removeMarkdownSection(content, "browser");
1408
1676
  actions.push({ kind: "write", target: systemPromptFile, content });
1409
1677
  }
1410
- const configFile = path9.join(ctx.configDir, "config.toml");
1678
+ const configFile = path8.join(ctx.configDir, "config.toml");
1411
1679
  const config = readTextIfExists(configFile);
1412
1680
  if (config !== null) {
1413
1681
  let content = config;
@@ -1442,7 +1710,7 @@ args = [${(server.args ?? []).map(tomlString).join(", ")}]`);
1442
1710
  ...primaryModelOwnership.length > 0 ? { primaryModelOwnership } : {}
1443
1711
  });
1444
1712
  }
1445
- const hooksFile = path9.join(ctx.configDir, "hooks.json");
1713
+ const hooksFile = path8.join(ctx.configDir, "hooks.json");
1446
1714
  const hooksJson = readTextIfExists(hooksFile);
1447
1715
  if (hooksJson !== null) {
1448
1716
  const content = removeNativeHooks(hooksJson, hooks);
@@ -1456,7 +1724,7 @@ args = [${(server.args ?? []).map(tomlString).join(", ")}]`);
1456
1724
 
1457
1725
  // src/lib/install-mode.ts
1458
1726
  import fs6 from "fs";
1459
- import path10 from "path";
1727
+ import path9 from "path";
1460
1728
  var INSTALL_MODES = /* @__PURE__ */ new Set(["human", "programmatic"]);
1461
1729
  var SUBAGENT_CONCURRENCIES = /* @__PURE__ */ new Set(["serial", "parallel"]);
1462
1730
  var DEFAULT_INSTALL_MODE_PREFERENCE = {
@@ -1464,7 +1732,7 @@ var DEFAULT_INSTALL_MODE_PREFERENCE = {
1464
1732
  subagentConcurrency: "serial"
1465
1733
  };
1466
1734
  function installModePreferenceFile() {
1467
- return path10.join(dataDir(), "install-mode.json");
1735
+ return path9.join(dataDir(), "install-mode.json");
1468
1736
  }
1469
1737
  function hasInstallModePreference(file = installModePreferenceFile()) {
1470
1738
  return fs6.existsSync(file);
@@ -1539,11 +1807,11 @@ function parseInstallModePreferenceFlags(mode, subagentConcurrency) {
1539
1807
 
1540
1808
  // src/lib/backup.ts
1541
1809
  import fs7 from "fs";
1542
- import path11 from "path";
1810
+ import path10 from "path";
1543
1811
  import crypto from "crypto";
1544
1812
  var KEEP_BACKUPS = 10;
1545
1813
  function backupsRoot() {
1546
- return path11.join(dataDir(), "backups");
1814
+ return path10.join(dataDir(), "backups");
1547
1815
  }
1548
1816
  function compositeChecksum(files) {
1549
1817
  const hash = crypto.createHash("sha256");
@@ -1562,19 +1830,19 @@ function createBackup(files, label, root = backupsRoot()) {
1562
1830
  if (latest?.checksum === checksum) return latest;
1563
1831
  const base = `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}-${label}`;
1564
1832
  let id = base;
1565
- let dir = path11.join(root, id);
1833
+ let dir = path10.join(root, id);
1566
1834
  for (let n = 1; fs7.existsSync(dir); n++) {
1567
1835
  id = `${base}-${n}`;
1568
- dir = path11.join(root, id);
1836
+ dir = path10.join(root, id);
1569
1837
  }
1570
- ensureDir(path11.join(dir, "files"));
1838
+ ensureDir(path10.join(dir, "files"));
1571
1839
  const entries = existing.map((original, i) => {
1572
- const stored = path11.join(dir, "files", `${String(i).padStart(4, "0")}-${path11.basename(original)}`);
1840
+ const stored = path10.join(dir, "files", `${String(i).padStart(4, "0")}-${path10.basename(original)}`);
1573
1841
  fs7.copyFileSync(original, stored);
1574
1842
  return { original, stored };
1575
1843
  });
1576
1844
  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");
1845
+ writeText(path10.join(dir, "manifest.json"), JSON.stringify(info, null, 2) + "\n");
1578
1846
  pruneBackups(root);
1579
1847
  return info;
1580
1848
  }
@@ -1583,7 +1851,7 @@ function listBackups(root = backupsRoot()) {
1583
1851
  const infos = [];
1584
1852
  for (const entry of fs7.readdirSync(root, { withFileTypes: true })) {
1585
1853
  if (!entry.isDirectory()) continue;
1586
- const manifest = readTextIfExists(path11.join(root, entry.name, "manifest.json"));
1854
+ const manifest = readTextIfExists(path10.join(root, entry.name, "manifest.json"));
1587
1855
  if (manifest === null) continue;
1588
1856
  try {
1589
1857
  infos.push(JSON.parse(manifest));
@@ -1600,7 +1868,7 @@ function restoreBackup(id, root = backupsRoot(), boundary = HOME) {
1600
1868
  for (const { original, stored } of info.files) {
1601
1869
  if (!fs7.existsSync(stored)) continue;
1602
1870
  if (!isContainedIn(original, boundary)) continue;
1603
- ensureDir(path11.dirname(original));
1871
+ ensureDir(path10.dirname(original));
1604
1872
  fs7.copyFileSync(stored, original);
1605
1873
  restored++;
1606
1874
  }
@@ -1609,15 +1877,15 @@ function restoreBackup(id, root = backupsRoot(), boundary = HOME) {
1609
1877
  function pruneBackups(root) {
1610
1878
  const all = listBackups(root);
1611
1879
  for (const old of all.slice(KEEP_BACKUPS)) {
1612
- fs7.rmSync(path11.join(root, old.id), { recursive: true, force: true });
1880
+ fs7.rmSync(path10.join(root, old.id), { recursive: true, force: true });
1613
1881
  }
1614
1882
  }
1615
1883
 
1616
1884
  // src/lib/manifest.ts
1617
1885
  import fs8 from "fs";
1618
- import path12 from "path";
1886
+ import path11 from "path";
1619
1887
  function manifestFile() {
1620
- return path12.join(dataDir(), "manifest.json");
1888
+ return path11.join(dataDir(), "manifest.json");
1621
1889
  }
1622
1890
  function readManifest(file = manifestFile()) {
1623
1891
  const raw = readTextIfExists(file);
@@ -1641,24 +1909,24 @@ function removeRuntimeManifest(id, file = manifestFile()) {
1641
1909
  writeText(file, JSON.stringify(manifest, null, 2) + "\n");
1642
1910
  }
1643
1911
  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)
1912
+ return prevOwned.map((f) => path11.resolve(f)).filter(
1913
+ (f) => !currentTargets.has(f) && path11.basename(f) !== "engram.ts" && isContainedIn(f, root) && fs8.existsSync(f)
1646
1914
  );
1647
1915
  }
1648
1916
 
1649
1917
  // src/components/system-prompt.ts
1650
- import path14 from "path";
1918
+ import path13 from "path";
1651
1919
  import fs10 from "fs";
1652
1920
 
1653
1921
  // src/lib/mode-composition.ts
1654
1922
  import fs9 from "fs";
1655
- import path13 from "path";
1923
+ import path12 from "path";
1656
1924
  var PROGRAMMATIC_ROOT = ["modes", "programmatic"];
1657
1925
  var PROGRAMMATIC_MARKER = "<!-- jorgex:programmatic-mode -->";
1658
1926
  var LEGACY_RESULT_CONTRACT_SECTION = /\n?##\s+Result contract[\s\S]*$/;
1659
1927
  var normalize = (value) => value.replace(/\r\n/g, "\n");
1660
1928
  function loadProgrammaticAddendum(stackDir, fileName) {
1661
- return normalize(fs9.readFileSync(path13.join(stackDir, ...PROGRAMMATIC_ROOT, fileName), "utf8")).trim();
1929
+ return normalize(fs9.readFileSync(path12.join(stackDir, ...PROGRAMMATIC_ROOT, fileName), "utf8")).trim();
1662
1930
  }
1663
1931
  function appendAddendum(base, addendum) {
1664
1932
  const normalizedBase = normalize(base);
@@ -1701,15 +1969,15 @@ function composeProgrammaticAgentBody(stackDir, agent, mode, concurrency) {
1701
1969
  var normalize2 = (s) => s.replace(/\r\n/g, "\n");
1702
1970
  function planSystemPrompt(adapter, ctx) {
1703
1971
  const target = adapter.paths(ctx.configDir).systemPromptFile;
1704
- const agentsMd = normalize2(fs10.readFileSync(path14.join(ctx.stackDir, "system-prompt", "AGENTS.md"), "utf8"));
1972
+ const agentsMd = normalize2(fs10.readFileSync(path13.join(ctx.stackDir, "system-prompt", "AGENTS.md"), "utf8"));
1705
1973
  const protocol = stripLeadingHtmlComments(
1706
- normalize2(fs10.readFileSync(path14.join(ctx.stackDir, "system-prompt", "engram-protocol.md"), "utf8"))
1974
+ normalize2(fs10.readFileSync(path13.join(ctx.stackDir, "system-prompt", "engram-protocol.md"), "utf8"))
1707
1975
  );
1708
1976
  const composedAgentsMd = composeProgrammaticSystemPrompt(ctx.stackDir, agentsMd, ctx.mode);
1709
1977
  const browser = [
1710
1978
  ctx.playwrightCliEnabled ? "browser-playwright.md" : null,
1711
1979
  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");
1980
+ ].filter((file) => file !== null).map((file) => normalize2(fs10.readFileSync(path13.join(ctx.stackDir, "system-prompt", file), "utf8"))).join("\n\n");
1713
1981
  let content = readTextIfExists(target);
1714
1982
  content = upsertMarkdownSection(content, "system-prompt", composedAgentsMd);
1715
1983
  if (adapter.injectEngramProtocol(ctx)) {
@@ -1722,7 +1990,7 @@ function planSystemPrompt(adapter, ctx) {
1722
1990
  }
1723
1991
 
1724
1992
  // src/components/agents.ts
1725
- import path15 from "path";
1993
+ import path14 from "path";
1726
1994
  function planAgents(adapter, ctx) {
1727
1995
  const { agentsDir, commandsDir, outputStylesDir, profilesDir, scriptsDir } = adapter.paths(ctx.configDir);
1728
1996
  const dirFor = {
@@ -1732,7 +2000,7 @@ function planAgents(adapter, ctx) {
1732
2000
  profile: profilesDir
1733
2001
  };
1734
2002
  const scriptsBase = scriptsDir.replace(/\\/g, "/");
1735
- return loadCanonicalAgents(path15.join(ctx.stackDir, "agents")).flatMap((agent) => {
2003
+ return loadCanonicalAgents(path14.join(ctx.stackDir, "agents")).flatMap((agent) => {
1736
2004
  const composedAgent = {
1737
2005
  ...agent,
1738
2006
  body: composeProgrammaticAgentBody(ctx.stackDir, agent, ctx.mode, ctx.subagentConcurrency)
@@ -1744,42 +2012,42 @@ function planAgents(adapter, ctx) {
1744
2012
  return [];
1745
2013
  }
1746
2014
  const content = rendered.content.replace(/\{\{SCRIPTS_DIR\}\}/g, scriptsBase);
1747
- return [{ kind: "write", target: path15.join(dir, rendered.file), content }];
2015
+ return [{ kind: "write", target: path14.join(dir, rendered.file), content }];
1748
2016
  });
1749
2017
  });
1750
2018
  }
1751
2019
 
1752
2020
  // src/components/skills.ts
1753
- import path16 from "path";
2021
+ import path15 from "path";
1754
2022
  function planSkills(adapter, ctx) {
1755
2023
  const { skillsDir } = adapter.paths(ctx.configDir);
1756
- const source = path16.join(ctx.stackDir, "skills");
2024
+ const source = path15.join(ctx.stackDir, "skills");
1757
2025
  return listFilesRecursive(source).map((file) => {
1758
- const relative = path16.relative(source, file);
1759
- return { kind: "copy", source: file, target: path16.join(skillsDir, relative) };
2026
+ const relative = path15.relative(source, file);
2027
+ return { kind: "copy", source: file, target: path15.join(skillsDir, relative) };
1760
2028
  });
1761
2029
  }
1762
2030
 
1763
2031
  // src/components/commands.ts
1764
- import path17 from "path";
2032
+ import path16 from "path";
1765
2033
  import fs11 from "fs";
1766
2034
  function planCommands(adapter, ctx) {
1767
2035
  const { commandsDir } = adapter.paths(ctx.configDir);
1768
- const source = path17.join(ctx.stackDir, "commands");
2036
+ const source = path16.join(ctx.stackDir, "commands");
1769
2037
  if (!fs11.existsSync(source)) return [];
1770
2038
  const commandFiles = [
1771
2039
  ...listMarkdownFiles(source),
1772
- ...listMarkdownFiles(path17.join(source, adapter.id))
2040
+ ...listMarkdownFiles(path16.join(source, adapter.id))
1773
2041
  ];
1774
2042
  return commandFiles.map(({ file, fullPath }) => {
1775
2043
  const raw = fs11.readFileSync(fullPath, "utf8").replace(/\r\n/g, "\n");
1776
2044
  const rendered = adapter.renderCommand(file, raw);
1777
- return { kind: "write", target: path17.join(commandsDir, rendered.file), content: rendered.content };
2045
+ return { kind: "write", target: path16.join(commandsDir, rendered.file), content: rendered.content };
1778
2046
  });
1779
2047
  }
1780
2048
  function listMarkdownFiles(dir) {
1781
2049
  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) }));
2050
+ 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
2051
  }
1784
2052
 
1785
2053
  // src/components/hooks.ts
@@ -1793,15 +2061,15 @@ function planMcp(adapter, ctx) {
1793
2061
  }
1794
2062
 
1795
2063
  // src/components/plugins.ts
1796
- import path18 from "path";
2064
+ import path17 from "path";
1797
2065
  import fs12 from "fs";
1798
2066
  function planPlugins(adapter, ctx) {
1799
2067
  const { pluginsDir } = adapter.paths(ctx.configDir);
1800
2068
  if (pluginsDir === null) return [];
1801
- const source = path18.join(ctx.stackDir, "plugins", adapter.id);
2069
+ const source = path17.join(ctx.stackDir, "plugins", adapter.id);
1802
2070
  if (!fs12.existsSync(source)) return [];
1803
2071
  return listFilesRecursive(source).filter((f) => f.endsWith(".ts")).map((sourceFile) => {
1804
- const target = path18.join(pluginsDir, path18.relative(source, sourceFile));
2072
+ const target = path17.join(pluginsDir, path17.relative(source, sourceFile));
1805
2073
  const raw = fs12.readFileSync(sourceFile, "utf8");
1806
2074
  let content = raw;
1807
2075
  if (content.includes('"{{ENGRAM_BIN}}"')) {
@@ -1809,7 +2077,7 @@ function planPlugins(adapter, ctx) {
1809
2077
  }
1810
2078
  if (content.includes('"{{ENGRAM_PROTOCOL}}"')) {
1811
2079
  const protocol = stripLeadingHtmlComments(
1812
- fs12.readFileSync(path18.join(ctx.stackDir, "system-prompt", "engram-protocol.md"), "utf8").replace(/\r\n/g, "\n")
2080
+ fs12.readFileSync(path17.join(ctx.stackDir, "system-prompt", "engram-protocol.md"), "utf8").replace(/\r\n/g, "\n")
1813
2081
  );
1814
2082
  content = content.replace(/"\{\{ENGRAM_PROTOCOL\}\}"/g, JSON.stringify(protocol));
1815
2083
  }
@@ -1821,8 +2089,8 @@ function planPlugins(adapter, ctx) {
1821
2089
 
1822
2090
  // src/lib/external-tools.ts
1823
2091
  import fs13 from "fs";
1824
- import os2 from "os";
1825
- import path19 from "path";
2092
+ import os from "os";
2093
+ import path18 from "path";
1826
2094
  import { execFileSync as execFileSync2 } from "child_process";
1827
2095
  var PLAYWRIGHT_CLI = {
1828
2096
  packageName: "@playwright/cli",
@@ -1873,9 +2141,9 @@ function resolvePnpmBin() {
1873
2141
  const pnpmCmd = lookPath("pnpm.cmd");
1874
2142
  return pnpmCmd !== null && !pnpmCmd.toLowerCase().endsWith(".ps1") ? pnpmCmd : null;
1875
2143
  }
1876
- function isPlaywrightBrowserReady(env = process.env, platform = process.platform, homeDir = os2.homedir()) {
2144
+ function isPlaywrightBrowserReady(env = process.env, platform = process.platform, homeDir = os.homedir()) {
1877
2145
  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"));
2146
+ 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
2147
  if (configuredPath === "0") return { status: "missing", path: cacheDir, errorCode: "DISABLED" };
1880
2148
  try {
1881
2149
  const ready = fs13.readdirSync(cacheDir, { withFileTypes: true }).some(
@@ -1923,7 +2191,7 @@ function executePlaywrightToolAction(action, pnpmBin = resolvePnpmBin()) {
1923
2191
 
1924
2192
  // src/lib/tool-preferences.ts
1925
2193
  import fs14 from "fs";
1926
- import path20 from "path";
2194
+ import path19 from "path";
1927
2195
  var PLAYWRIGHT_CLI_PREFERENCE_VERSION = 1;
1928
2196
  var DEVTOOLS_MCP_PREFERENCE_VERSION = 1;
1929
2197
  var PRIMARY_MODEL_OWNERSHIP_VERSION = 1;
@@ -1936,7 +2204,7 @@ function readPreference(file) {
1936
2204
  }
1937
2205
  }
1938
2206
  function playwrightCliPreferenceFile(stateDir = dataDir()) {
1939
- return path20.join(stateDir, "playwright-cli.json");
2207
+ return path19.join(stateDir, "playwright-cli.json");
1940
2208
  }
1941
2209
  function parsePlaywrightCliPreference(raw) {
1942
2210
  try {
@@ -1966,12 +2234,12 @@ function savePlaywrightCliPreference(file, enabled) {
1966
2234
  writeText(file, JSON.stringify({ version: PLAYWRIGHT_CLI_PREFERENCE_VERSION, enabled }) + "\n");
1967
2235
  }
1968
2236
  function devtoolsMcpPreferenceFile(stateDir = dataDir()) {
1969
- return path20.join(stateDir, "devtools-mcp.json");
2237
+ return path19.join(stateDir, "devtools-mcp.json");
1970
2238
  }
1971
2239
  function primaryModelOwnershipFile(stateDir = dataDir()) {
1972
- return path20.join(stateDir, "primary-model.json");
2240
+ return path19.join(stateDir, "primary-model.json");
1973
2241
  }
1974
- function isRecord(value) {
2242
+ function isRecord2(value) {
1975
2243
  return value !== null && typeof value === "object" && !Array.isArray(value);
1976
2244
  }
1977
2245
  function isRuntimeId(value) {
@@ -1980,7 +2248,7 @@ function isRuntimeId(value) {
1980
2248
  function parseDevtoolsMcpState(raw) {
1981
2249
  try {
1982
2250
  const value = JSON.parse(raw);
1983
- if (!isRecord(value) || value.version !== DEVTOOLS_MCP_PREFERENCE_VERSION || !isRecord(value.enabled) || !isRecord(value.owned)) return null;
2251
+ if (!isRecord2(value) || value.version !== DEVTOOLS_MCP_PREFERENCE_VERSION || !isRecord2(value.enabled) || !isRecord2(value.owned)) return null;
1984
2252
  const enabled = {};
1985
2253
  for (const [runtime, selected] of Object.entries(value.enabled)) {
1986
2254
  if (!isRuntimeId(runtime) || typeof selected !== "boolean") return null;
@@ -1988,7 +2256,7 @@ function parseDevtoolsMcpState(raw) {
1988
2256
  }
1989
2257
  const owned = {};
1990
2258
  for (const [runtime, servers] of Object.entries(value.owned)) {
1991
- if (!isRuntimeId(runtime) || !isRecord(servers)) return null;
2259
+ if (!isRuntimeId(runtime) || !isRecord2(servers)) return null;
1992
2260
  const managed = {};
1993
2261
  for (const [server, marked] of Object.entries(servers)) {
1994
2262
  if (marked !== true) return null;
@@ -2044,13 +2312,13 @@ function saveDevtoolsMcpOwnership(file, runtime, server, owned) {
2044
2312
  function parsePrimaryModelOwnership(raw) {
2045
2313
  try {
2046
2314
  const value = JSON.parse(raw);
2047
- if (!isRecord(value) || value.version !== PRIMARY_MODEL_OWNERSHIP_VERSION || !isRecord(value.owned)) return null;
2315
+ if (!isRecord2(value) || value.version !== PRIMARY_MODEL_OWNERSHIP_VERSION || !isRecord2(value.owned)) return null;
2048
2316
  const owned = {};
2049
2317
  for (const [runtime, configs] of Object.entries(value.owned)) {
2050
- if (!isRuntimeId(runtime) || !isRecord(configs)) return null;
2318
+ if (!isRuntimeId(runtime) || !isRecord2(configs)) return null;
2051
2319
  const markedConfigs = {};
2052
2320
  for (const [configDir, fields] of Object.entries(configs)) {
2053
- if (configDir === "" || !isRecord(fields)) return null;
2321
+ if (configDir === "" || !isRecord2(fields)) return null;
2054
2322
  const markedFields = {};
2055
2323
  for (const [field, state] of Object.entries(fields)) {
2056
2324
  if (field === "" || state !== true) return null;
@@ -2079,7 +2347,7 @@ function primaryModelOwnershipError(file = primaryModelOwnershipFile()) {
2079
2347
  return `Primary model: ownership inv\xE1lido en ${file}. Corrige o borra ese archivo antes de reintentar.`;
2080
2348
  }
2081
2349
  function primaryModelConfigKey(configDir) {
2082
- const resolved = path20.resolve(configDir);
2350
+ const resolved = path19.resolve(configDir);
2083
2351
  return process.platform === "win32" ? resolved.toLowerCase() : resolved;
2084
2352
  }
2085
2353
  function loadPrimaryModelOwnership(file, runtime, configDir) {
@@ -2225,7 +2493,7 @@ function collectAllCurrentTargets(mode = DEFAULT_INSTALL_MODE_PREFERENCE) {
2225
2493
  continue;
2226
2494
  }
2227
2495
  try {
2228
- for (const action of buildPlan(adapter, ctx)) targets.add(path21.resolve(action.target));
2496
+ for (const action of buildPlan(adapter, ctx)) targets.add(path20.resolve(action.target));
2229
2497
  } catch (error) {
2230
2498
  complete = false;
2231
2499
  warnings.push(
@@ -2327,10 +2595,10 @@ async function runInstall(opts) {
2327
2595
  }
2328
2596
  const writeManifest = () => {
2329
2597
  if (!useManifest) return;
2330
- const unmergeTargets = new Set(adapter.planUnmerge(canonicalMcp, canonicalHooks, ctx).map((a) => path21.resolve(a.target)));
2598
+ const unmergeTargets = new Set(adapter.planUnmerge(canonicalMcp, canonicalHooks, ctx).map((a) => path20.resolve(a.target)));
2331
2599
  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);
2600
+ const liveOwned = plan.map((a) => path20.resolve(a.target)).filter(keepTarget);
2601
+ const previousOwned = (prevManifest?.owned ?? []).map((target) => path20.resolve(target)).filter(keepTarget);
2334
2602
  const owned = canOrphan ? liveOwned : [.../* @__PURE__ */ new Set([...previousOwned, ...liveOwned])];
2335
2603
  writeRuntimeManifest(id, { configDir, owned, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
2336
2604
  };
@@ -2359,7 +2627,7 @@ async function runInstall(opts) {
2359
2627
  const backup = useManifest ? createBackup([...updates.map((c) => c.action.target), ...orphans], `install-${id}`) : null;
2360
2628
  if (backup) p.log.info(`Backup: ${backup.id} (${backup.files.length} archivos)`);
2361
2629
  applyChanges(changes, useManifest ? (action) => persistConfigurationOwnershipChanges(id, configDir, [action]) : void 0);
2362
- const pruneRoot = useManifest ? HOME : path21.dirname(configDir);
2630
+ const pruneRoot = useManifest ? HOME : path20.dirname(configDir);
2363
2631
  for (const orphan of orphans) {
2364
2632
  fs15.rmSync(orphan, { force: true });
2365
2633
  pruneEmptyDirs(orphan, pruneRoot);
@@ -2444,27 +2712,27 @@ async function runInstall(opts) {
2444
2712
 
2445
2713
  // src/uninstall.ts
2446
2714
  import fs17 from "fs";
2447
- import path25 from "path";
2715
+ import path24 from "path";
2448
2716
  import * as p2 from "@clack/prompts";
2449
2717
 
2450
2718
  // src/lib/pi-projection-lifecycle.ts
2451
2719
  import fs16 from "fs";
2452
- import path24 from "path";
2720
+ import path23 from "path";
2453
2721
 
2454
2722
  // src/adapters/pi.ts
2455
- import path22 from "path";
2723
+ import path21 from "path";
2456
2724
  var piAdapter = {
2457
2725
  id: "pi",
2458
2726
  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");
2727
+ const piConfigDir = process.env.PI_CODING_AGENT_DIR ?? path21.join(HOME, ".pi", "agent");
2728
+ const agentsHome = samePath(configDir, piConfigDir) ? HOME : path21.join(path21.dirname(configDir), "home");
2461
2729
  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"),
2730
+ systemPromptFile: path21.join(configDir, "AGENTS.md"),
2731
+ agentsDir: path21.join(configDir, "agents"),
2732
+ skillsDir: path21.join(agentsHome, ".agents", "skills"),
2733
+ commandsDir: path21.join(configDir, "prompts"),
2466
2734
  pluginsDir: null,
2467
- scriptsDir: path22.join(configDir, "scripts"),
2735
+ scriptsDir: path21.join(configDir, "scripts"),
2468
2736
  outputStylesDir: null,
2469
2737
  profilesDir: null
2470
2738
  };
@@ -2478,7 +2746,7 @@ var piAdapter = {
2478
2746
  };
2479
2747
 
2480
2748
  // src/lib/pi-package-lifecycle.ts
2481
- import path23 from "path";
2749
+ import path22 from "path";
2482
2750
  var REQUIRED_CAPABILITIES = /* @__PURE__ */ new Set([
2483
2751
  "foundation-contract-v1",
2484
2752
  "runner-json-v1",
@@ -2610,7 +2878,7 @@ function parseReceiptShape(receiptJson) {
2610
2878
  return null;
2611
2879
  }
2612
2880
  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"))) {
2881
+ if (engram === null || typeof engram !== "object" || Array.isArray(engram) || typeof Reflect.get(engram, "binary") !== "string" || !path22.isAbsolute(Reflect.get(engram, "binary"))) {
2614
2882
  return null;
2615
2883
  }
2616
2884
  return parsed;
@@ -2647,7 +2915,7 @@ function planPiPackageLifecycle(input) {
2647
2915
  if (input.receiptJson !== null) {
2648
2916
  const parsedReceipt = parseReceipt(input.receiptJson, input.candidate, {
2649
2917
  kind: input.scope.kind,
2650
- codingAgentDir: path23.resolve(input.scope.codingAgentDir)
2918
+ codingAgentDir: path22.resolve(input.scope.codingAgentDir)
2651
2919
  }, input.engramBin);
2652
2920
  if (parsedReceipt === "upgrade-required") return blocked(input, "receipt-upgrade-required");
2653
2921
  if (parsedReceipt === null) return blocked(input, "receipt-corrupt");
@@ -2682,7 +2950,7 @@ function planPiPackageLifecycle(input) {
2682
2950
  },
2683
2951
  receipt: expectedReceipt(input.candidate, "installing", {
2684
2952
  kind: input.scope.kind,
2685
- codingAgentDir: path23.resolve(input.scope.codingAgentDir)
2953
+ codingAgentDir: path22.resolve(input.scope.codingAgentDir)
2686
2954
  }, input.engramBin),
2687
2955
  ownership: ownership(true)
2688
2956
  };
@@ -2697,7 +2965,7 @@ function parseRunnerRecord(stdout, stderr, command, candidate, packageRunner) {
2697
2965
  const parsed = JSON.parse(body);
2698
2966
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return null;
2699
2967
  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")) {
2968
+ 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
2969
  return null;
2702
2970
  }
2703
2971
  return record;
@@ -2783,10 +3051,10 @@ function validateOwnedOperationState(input) {
2783
3051
  }))) {
2784
3052
  return { kind: "blocked", reason: "receipt-untrusted" };
2785
3053
  }
2786
- if (receipt.scope.kind !== (input.paths.targetDir ? "target-dir" : "real") || path23.resolve(receipt.scope.codingAgentDir) !== path23.resolve(input.paths.codingAgentDir)) {
3054
+ if (receipt.scope.kind !== (input.paths.targetDir ? "target-dir" : "real") || path22.resolve(receipt.scope.codingAgentDir) !== path22.resolve(input.paths.codingAgentDir)) {
2787
3055
  return { kind: "blocked", reason: "source-divergent" };
2788
3056
  }
2789
- if (input.engramBin !== null && path23.resolve(receipt.engram.binary) !== path23.resolve(input.engramBin)) {
3057
+ if (input.engramBin !== null && path22.resolve(receipt.engram.binary) !== path22.resolve(input.engramBin)) {
2790
3058
  return { kind: "blocked", reason: "receipt-corrupt" };
2791
3059
  }
2792
3060
  const source = receipt.candidate.package.source;
@@ -2883,18 +3151,18 @@ var preparedPiProjectionUninstalls = /* @__PURE__ */ new WeakMap();
2883
3151
  function projectionScope(scope) {
2884
3152
  return {
2885
3153
  ...scope,
2886
- settingsFile: path24.join(scope.codingAgentDir, "settings.json")
3154
+ settingsFile: path23.join(scope.codingAgentDir, "settings.json")
2887
3155
  };
2888
3156
  }
2889
3157
  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);
3158
+ const relative = path23.relative(path23.resolve(root), path23.resolve(file));
3159
+ return relative === "" || relative !== ".." && !relative.startsWith(`..${path23.sep}`) && !path23.isAbsolute(relative);
2892
3160
  }
2893
3161
  function isManagedPath(scope, file) {
2894
3162
  return isInside(scope.home, file) || isInside(scope.codingAgentDir, file);
2895
3163
  }
2896
3164
  function isAllowedPath(scope, file) {
2897
- return isManagedPath(scope, file) || path24.resolve(file) === path24.resolve(scope.receiptFile);
3165
+ return isManagedPath(scope, file) || path23.resolve(file) === path23.resolve(scope.receiptFile);
2898
3166
  }
2899
3167
  function projectedPiAdapter(scope) {
2900
3168
  const paths = piAdapter.paths(scope.codingAgentDir);
@@ -2902,7 +3170,7 @@ function projectedPiAdapter(scope) {
2902
3170
  ...piAdapter,
2903
3171
  paths: () => ({
2904
3172
  ...paths,
2905
- skillsDir: path24.join(scope.home, ".agents", "skills")
3173
+ skillsDir: path23.join(scope.home, ".agents", "skills")
2906
3174
  })
2907
3175
  };
2908
3176
  }
@@ -2942,9 +3210,9 @@ function assertPlanContained(plan, scope) {
2942
3210
  }
2943
3211
  }
2944
3212
  function receiptFor(plan, scope) {
2945
- const systemPrompt = path24.join(scope.codingAgentDir, "AGENTS.md");
3213
+ const systemPrompt = path23.join(scope.codingAgentDir, "AGENTS.md");
2946
3214
  const owned = [...new Set(
2947
- plan.map((action) => path24.resolve(action.target)).filter((target) => target !== systemPrompt)
3215
+ plan.map((action) => path23.resolve(action.target)).filter((target) => target !== systemPrompt)
2948
3216
  )];
2949
3217
  return {
2950
3218
  schemaVersion: 1,
@@ -2996,8 +3264,8 @@ function realPiProjectionScope() {
2996
3264
  return {
2997
3265
  kind: "real",
2998
3266
  home: HOME,
2999
- codingAgentDir: process.env.PI_CODING_AGENT_DIR ?? path24.join(HOME, ".pi", "agent"),
3000
- receiptFile: path24.join(dataDir(), "pi-projection-receipt.json")
3267
+ codingAgentDir: process.env.PI_CODING_AGENT_DIR ?? path23.join(HOME, ".pi", "agent"),
3268
+ receiptFile: path23.join(dataDir(), "pi-projection-receipt.json")
3001
3269
  };
3002
3270
  }
3003
3271
  function readTextOnlyIfMissing(file) {
@@ -3035,13 +3303,13 @@ function manifestOwned(manifest) {
3035
3303
  return new Set([
3036
3304
  ...manifest.runtimes.codex?.owned ?? [],
3037
3305
  ...manifest.runtimes.opencode?.owned ?? []
3038
- ].map((file) => path24.resolve(file)));
3306
+ ].map((file) => path23.resolve(file)));
3039
3307
  }
3040
3308
  function withoutManagedPromptSections(content) {
3041
3309
  return ["system-prompt", "engram-protocol", "browser"].reduce((current, section) => removeMarkdownSection(current, section), content);
3042
3310
  }
3043
3311
  function uniquePaths(paths) {
3044
- return [...new Set(paths.map((file) => path24.resolve(file)))];
3312
+ return [...new Set(paths.map((file) => path23.resolve(file)))];
3045
3313
  }
3046
3314
  function blocked2(reason, paths, remedy) {
3047
3315
  return { kind: "blocked", reason, paths: uniquePaths(paths), remedy };
@@ -3114,7 +3382,7 @@ function preparePiProjectionUninstall(input, deps) {
3114
3382
  }
3115
3383
  const scope = projectionScope(input.scope);
3116
3384
  const expectedReceipt2 = expectedProjectionReceipt(input, scope);
3117
- const prompt = path24.join(scope.codingAgentDir, "AGENTS.md");
3385
+ const prompt = path23.join(scope.codingAgentDir, "AGENTS.md");
3118
3386
  let existingPrompt;
3119
3387
  try {
3120
3388
  existingPrompt = deps.readText(prompt);
@@ -3122,7 +3390,7 @@ function preparePiProjectionUninstall(input, deps) {
3122
3390
  return cleanupFailure([prompt]);
3123
3391
  }
3124
3392
  const promptContent = existingPrompt === null ? null : withoutManagedPromptSections(existingPrompt);
3125
- const promptUpdate = promptContent === null || promptContent === existingPrompt ? null : { file: path24.resolve(prompt), content: promptContent };
3393
+ const promptUpdate = promptContent === null || promptContent === existingPrompt ? null : { file: path23.resolve(prompt), content: promptContent };
3126
3394
  const receiptRead = readProjectionReceipt(scope.receiptFile, deps);
3127
3395
  if (receiptRead.kind === "unreadable") return receiptUnreadable(scope.receiptFile);
3128
3396
  let ownedToRemove = [];
@@ -3138,7 +3406,7 @@ function preparePiProjectionUninstall(input, deps) {
3138
3406
  return cleanupFailure([scope.receiptFile]);
3139
3407
  }
3140
3408
  ownedToRemove = receipt.owned.filter((owned) => !retained.has(owned));
3141
- receiptFile = path24.resolve(scope.receiptFile);
3409
+ receiptFile = path23.resolve(scope.receiptFile);
3142
3410
  backupTargets = [...backupTargets, ...receipt.owned, receiptFile];
3143
3411
  }
3144
3412
  const backup = backupExisting(backupTargets, deps);
@@ -3194,7 +3462,7 @@ function runPiProjectionLifecycle(input, deps) {
3194
3462
  const expectedReceipt2 = receiptContent(receipt);
3195
3463
  const drifted = plan.filter((action) => hasActionDrift(action, deps));
3196
3464
  if (input.operation === "doctor") {
3197
- const paths = drifted.map((action) => path24.resolve(action.target));
3465
+ const paths = drifted.map((action) => path23.resolve(action.target));
3198
3466
  const currentSettings2 = deps.readText(scope.settingsFile);
3199
3467
  if (currentSettings2 === null || filterProjectedPiPackage(currentSettings2, input.packageSource) !== currentSettings2) {
3200
3468
  paths.push(scope.settingsFile);
@@ -3225,12 +3493,12 @@ function runPiProjectionLifecycle(input, deps) {
3225
3493
  return { kind: "synced", changed: drifted.length > 0 || packageWillChange || receiptChanged };
3226
3494
  }
3227
3495
  function systemProjectionLifecycle(input) {
3228
- const targetRoot = input.targetDir === void 0 ? null : path24.resolve(input.targetDir);
3496
+ const targetRoot = input.targetDir === void 0 ? null : path23.resolve(input.targetDir);
3229
3497
  const scope = targetRoot === null ? realPiProjectionScope() : {
3230
3498
  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")
3499
+ home: path23.join(targetRoot, "home"),
3500
+ codingAgentDir: path23.join(targetRoot, "pi-agent"),
3501
+ receiptFile: path23.join(targetRoot, "state", "pi-projection-receipt.json")
3234
3502
  };
3235
3503
  return {
3236
3504
  input: {
@@ -3246,7 +3514,7 @@ function systemProjectionLifecycle(input) {
3246
3514
  backup: (paths) => createBackup(
3247
3515
  paths,
3248
3516
  `pi-projection-${input.operation}`,
3249
- targetRoot === null ? void 0 : path24.join(targetRoot, "backups")
3517
+ targetRoot === null ? void 0 : path23.join(targetRoot, "backups")
3250
3518
  ),
3251
3519
  writeText,
3252
3520
  copyFile,
@@ -3327,7 +3595,7 @@ async function runUninstall(opts) {
3327
3595
  if (!detection.installed) continue;
3328
3596
  const keepCtx = makeContext(keep, detection.configDir);
3329
3597
  if (!keepCtx) continue;
3330
- for (const action of buildPlan(keep, keepCtx)) retained.add(path25.resolve(action.target));
3598
+ for (const action of buildPlan(keep, keepCtx)) retained.add(path24.resolve(action.target));
3331
3599
  }
3332
3600
  }
3333
3601
  for (const id of opts.runtimes) {
@@ -3346,15 +3614,15 @@ async function runUninstall(opts) {
3346
3614
  if (!ctx) continue;
3347
3615
  ctx.preserveEngram = !removeEngram;
3348
3616
  const unmerge = adapter.planUnmerge(mcpForUnmerge, hooks, ctx);
3349
- const mergedTargets = new Set(unmerge.map((a) => path25.resolve(a.target)));
3617
+ const mergedTargets = new Set(unmerge.map((a) => path24.resolve(a.target)));
3350
3618
  const usingRealConfig = opts.targetDir === void 0;
3351
3619
  const prevOwned = usingRealConfig ? readManifest().runtimes[id]?.owned ?? [] : [];
3352
- const pruneRoot = usingRealConfig ? HOME : path25.dirname(configDir);
3620
+ const pruneRoot = usingRealConfig ? HOME : path24.dirname(configDir);
3353
3621
  const planTargets = [
3354
- .../* @__PURE__ */ new Set([...buildPlan(adapter, ctx).map((a) => path25.resolve(a.target)), ...prevOwned.map((t) => path25.resolve(t))])
3622
+ .../* @__PURE__ */ new Set([...buildPlan(adapter, ctx).map((a) => path24.resolve(a.target)), ...prevOwned.map((t) => path24.resolve(t))])
3355
3623
  ].filter((t) => !mergedTargets.has(t) && fs17.existsSync(t));
3356
3624
  const deleteTargets = planTargets.filter(
3357
- (t) => !retained.has(t) && !(ctx.preserveEngram && path25.basename(t) === "engram.ts") && isContainedIn(t, pruneRoot)
3625
+ (t) => !retained.has(t) && !(ctx.preserveEngram && path24.basename(t) === "engram.ts") && isContainedIn(t, pruneRoot)
3358
3626
  );
3359
3627
  const sharedKept = planTargets.length - deleteTargets.length;
3360
3628
  p2.log.step(`${adapter.name} \u2192 ${configDir}`);
@@ -3434,7 +3702,7 @@ async function runUninstall(opts) {
3434
3702
  }
3435
3703
 
3436
3704
  // src/doctor.ts
3437
- import path26 from "path";
3705
+ import path25 from "path";
3438
3706
  import fs18 from "fs";
3439
3707
  import * as p3 from "@clack/prompts";
3440
3708
  function engramVersion(bin) {
@@ -3454,7 +3722,7 @@ function resolvePlaywrightDoctorState(input) {
3454
3722
  return { status: "healthy" };
3455
3723
  }
3456
3724
  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");
3725
+ 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
3726
  const content = readTextIfExists(file);
3459
3727
  if (content === null) return null;
3460
3728
  const match = /CONTEXT7_API_KEY"?\s*[:=]\s*"([^"]*)"/.exec(content);
@@ -3477,8 +3745,8 @@ async function runDoctor() {
3477
3745
  p3.log.success(`Engram: ${version} (${engramBin})`);
3478
3746
  }
3479
3747
  }
3480
- const engramDataDir = process.env.ENGRAM_DATA_DIR ?? path26.join(HOME, ".engram");
3481
- const engramDb = path26.join(engramDataDir, "engram.db");
3748
+ const engramDataDir = process.env.ENGRAM_DATA_DIR ?? path25.join(HOME, ".engram");
3749
+ const engramDb = path25.join(engramDataDir, "engram.db");
3482
3750
  if (fs18.existsSync(engramDb)) {
3483
3751
  const sizeMb = (fs18.statSync(engramDb).size / 1024 / 1024).toFixed(1);
3484
3752
  p3.log.info(`Engram DB: ${engramDb} (${sizeMb} MB de memorias \u2014 el stack no la toca JAM\xC1S).`);
@@ -3534,6 +3802,9 @@ async function runDoctor() {
3534
3802
  p3.log.warn(`${adapter.name}: no instalado en esta m\xE1quina.`);
3535
3803
  continue;
3536
3804
  }
3805
+ const capabilityReport = adapter.reportCapabilities(detection.configDir);
3806
+ const capabilitySummary = capabilityReport.capabilities.map((capability) => `${capability.id}=${capability.state}`).join(", ");
3807
+ p3.log.info(`${adapter.name}: capabilities diagnostic (${capabilitySummary}); no certifica enforcement local.`);
3537
3808
  const ctx = makeContext(adapter, detection.configDir, modePreference);
3538
3809
  if (!ctx) continue;
3539
3810
  let pending;
@@ -3558,10 +3829,10 @@ async function runDoctor() {
3558
3829
  p3.log.warn(`${adapter.name}: ${orphans.length} archivos hu\xE9rfanos de versiones previas \u2192 ejecuta 'sync'.`);
3559
3830
  problems++;
3560
3831
  }
3561
- if (adapter.id === "codex" && fs18.existsSync(path26.join(detection.configDir, "hooks.json"))) {
3832
+ if (adapter.id === "codex" && fs18.existsSync(path25.join(detection.configDir, "hooks.json"))) {
3562
3833
  p3.log.info("Codex: recuerda que los hooks requieren aprobaci\xF3n manual \u2014 verifica con /hooks dentro de codex.");
3563
3834
  }
3564
- if (adapter.id === "codex" && fs18.existsSync(path26.join(detection.configDir, "AGENTS.override.md"))) {
3835
+ if (adapter.id === "codex" && fs18.existsSync(path25.join(detection.configDir, "AGENTS.override.md"))) {
3565
3836
  p3.log.warn(
3566
3837
  "Codex: existe ~/.codex/AGENTS.override.md \u2014 tiene prioridad ABSOLUTA y tapa el AGENTS.md gestionado por el stack."
3567
3838
  );
@@ -3576,16 +3847,16 @@ async function runDoctor() {
3576
3847
 
3577
3848
  // src/update.ts
3578
3849
  import fs21 from "fs";
3579
- import path29 from "path";
3580
- import os4 from "os";
3850
+ import path28 from "path";
3851
+ import os3 from "os";
3581
3852
  import { execFileSync as execFileSync5 } from "child_process";
3582
3853
  import * as p4 from "@clack/prompts";
3583
3854
 
3584
3855
  // src/lib/github.ts
3585
3856
  import fs19 from "fs";
3586
- import path27 from "path";
3857
+ import path26 from "path";
3587
3858
  import { execFileSync as execFileSync3 } from "child_process";
3588
- import os3 from "os";
3859
+ import os2 from "os";
3589
3860
  import { Readable } from "stream";
3590
3861
  import { pipeline } from "stream/promises";
3591
3862
  var cachedToken;
@@ -3653,7 +3924,7 @@ async function latestGithubCommit(repo) {
3653
3924
  }
3654
3925
  }
3655
3926
  function validateExtractedTree(destDir) {
3656
- const resolved = path27.resolve(destDir);
3927
+ const resolved = path26.resolve(destDir);
3657
3928
  const walk = (dir) => {
3658
3929
  let entries;
3659
3930
  try {
@@ -3662,7 +3933,7 @@ function validateExtractedTree(destDir) {
3662
3933
  return false;
3663
3934
  }
3664
3935
  for (const entry of entries) {
3665
- const full = path27.join(dir, entry.name);
3936
+ const full = path26.join(dir, entry.name);
3666
3937
  let stat;
3667
3938
  try {
3668
3939
  stat = fs19.lstatSync(full);
@@ -3681,12 +3952,12 @@ function validateExtractedTree(destDir) {
3681
3952
  }
3682
3953
  function resolveTarBin() {
3683
3954
  if (process.platform !== "win32") return "tar";
3684
- const winTar = path27.join(process.env["SystemRoot"] ?? "C:\\Windows", "System32", "tar.exe");
3955
+ const winTar = path26.join(process.env["SystemRoot"] ?? "C:\\Windows", "System32", "tar.exe");
3685
3956
  return fs19.existsSync(winTar) ? winTar : "tar";
3686
3957
  }
3687
3958
  async function downloadRepoTarball(repo, sha, destDir, validateSubdir) {
3688
3959
  const url = `https://codeload.github.com/${repo}/tar.gz/${sha}`;
3689
- const tmp = path27.join(os3.tmpdir(), `jorgex-tarball-${Date.now()}.tar.gz`);
3960
+ const tmp = path26.join(os2.tmpdir(), `jorgex-tarball-${Date.now()}.tar.gz`);
3690
3961
  const fail = (reason) => {
3691
3962
  try {
3692
3963
  fs19.rmSync(destDir, { recursive: true, force: true });
@@ -3719,8 +3990,8 @@ async function downloadRepoTarball(repo, sha, destDir, validateSubdir) {
3719
3990
  const detail = (e.stderr?.toString().trim() || e.message || "").split("\n")[0];
3720
3991
  return fail(detail ? `tar fall\xF3: ${detail}` : "tar no disponible o fall\xF3 la extracci\xF3n");
3721
3992
  }
3722
- const resolvedDest = path27.resolve(destDir);
3723
- const validateRoot = validateSubdir ? path27.resolve(resolvedDest, validateSubdir) : resolvedDest;
3993
+ const resolvedDest = path26.resolve(destDir);
3994
+ const validateRoot = validateSubdir ? path26.resolve(resolvedDest, validateSubdir) : resolvedDest;
3724
3995
  if (validateRoot !== resolvedDest && !isContainedIn(validateRoot, resolvedDest)) {
3725
3996
  return fail(`la ruta de validaci\xF3n "${validateSubdir}" escapa del destino`);
3726
3997
  }
@@ -3742,7 +4013,7 @@ async function downloadRepoTarball(repo, sha, destDir, validateSubdir) {
3742
4013
 
3743
4014
  // src/lib/skill-update.ts
3744
4015
  import fs20 from "fs";
3745
- import path28 from "path";
4016
+ import path27 from "path";
3746
4017
  import { execFileSync as execFileSync4 } from "child_process";
3747
4018
  var PROTECTED_SKILLS = /* @__PURE__ */ new Set([
3748
4019
  "agent-delegation",
@@ -3761,10 +4032,10 @@ function sameTextContentNormalized(a, b) {
3761
4032
  }
3762
4033
  function diffSkillDirs(upstreamDir, localDir) {
3763
4034
  const upstreamFiles = new Set(
3764
- listFilesRecursive(upstreamDir).map((f) => path28.relative(upstreamDir, f))
4035
+ listFilesRecursive(upstreamDir).map((f) => path27.relative(upstreamDir, f))
3765
4036
  );
3766
4037
  const localFiles = new Set(
3767
- listFilesRecursive(localDir).map((f) => path28.relative(localDir, f))
4038
+ listFilesRecursive(localDir).map((f) => path27.relative(localDir, f))
3768
4039
  );
3769
4040
  const added = [];
3770
4041
  const modified = [];
@@ -3772,7 +4043,7 @@ function diffSkillDirs(upstreamDir, localDir) {
3772
4043
  for (const rel of upstreamFiles) {
3773
4044
  if (!localFiles.has(rel)) {
3774
4045
  added.push(rel);
3775
- } else if (!sameTextContentNormalized(path28.join(upstreamDir, rel), path28.join(localDir, rel))) {
4046
+ } else if (!sameTextContentNormalized(path27.join(upstreamDir, rel), path27.join(localDir, rel))) {
3776
4047
  modified.push(rel);
3777
4048
  }
3778
4049
  }
@@ -3834,7 +4105,7 @@ function replaceSkill(name, upstreamSkillDir, newCommit, opts) {
3834
4105
  if (PROTECTED_SKILLS.has(name)) {
3835
4106
  throw new Error(`La skill "${name}" es propia del stack y no se actualiza desde upstream.`);
3836
4107
  }
3837
- const upstreamsFile = upstreamsFilePath ?? path28.join(path28.dirname(stackRoot()), "upstreams.json");
4108
+ const upstreamsFile = upstreamsFilePath ?? path27.join(path27.dirname(stackRoot()), "upstreams.json");
3838
4109
  const raw = fs20.readFileSync(upstreamsFile, "utf8");
3839
4110
  const data = JSON.parse(raw);
3840
4111
  const skillEntry = data?.skills?.[name];
@@ -3844,8 +4115,8 @@ function replaceSkill(name, upstreamSkillDir, newCommit, opts) {
3844
4115
  if (skillEntry.kind === "release") {
3845
4116
  throw new Error(`La skill "${name}" es de tipo release y no se actualiza con replaceSkill.`);
3846
4117
  }
3847
- const skillsRoot = localSkillsRoot ?? path28.join(stackRoot(), "skills");
3848
- const localSkillDir = path28.join(skillsRoot, name);
4118
+ const skillsRoot = localSkillsRoot ?? path27.join(stackRoot(), "skills");
4119
+ const localSkillDir = path27.join(skillsRoot, name);
3849
4120
  const localFiles = listFilesRecursive(localSkillDir);
3850
4121
  if (localFiles.length > 0) {
3851
4122
  createBackup(localFiles, `skill-update-${name}`, backupsRoot2);
@@ -3858,9 +4129,9 @@ function replaceSkill(name, upstreamSkillDir, newCommit, opts) {
3858
4129
  if (st.isSymbolicLink()) {
3859
4130
  throw new Error(`Symlink rechazado en upstream de skill "${name}": ${src}`);
3860
4131
  }
3861
- const rel = path28.relative(upstreamSkillDir, src);
3862
- const dest = path28.join(stagingDir, rel);
3863
- ensureDir(path28.dirname(dest));
4132
+ const rel = path27.relative(upstreamSkillDir, src);
4133
+ const dest = path27.join(stagingDir, rel);
4134
+ ensureDir(path27.dirname(dest));
3864
4135
  copyFile(src, dest);
3865
4136
  }
3866
4137
  const oldDir = `${localSkillDir}.old-${process.pid}`;
@@ -3885,7 +4156,7 @@ function rateLimitHint(prefix) {
3885
4156
  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
4157
  }
3887
4158
  function loadUpstreams() {
3888
- const file = path29.join(path29.dirname(stackRoot()), "upstreams.json");
4159
+ const file = path28.join(path28.dirname(stackRoot()), "upstreams.json");
3889
4160
  return JSON.parse(fs21.readFileSync(file, "utf8"));
3890
4161
  }
3891
4162
  function skillsToScan(maintainer, upstreams) {
@@ -4035,8 +4306,8 @@ function isEngramRunning() {
4035
4306
  return null;
4036
4307
  }
4037
4308
  }
4038
- function isGitClone(projectRoot = path29.dirname(stackRoot())) {
4039
- return fs21.existsSync(path29.join(projectRoot, ".git"));
4309
+ function isGitClone(projectRoot = path28.dirname(stackRoot())) {
4310
+ return fs21.existsSync(path28.join(projectRoot, ".git"));
4040
4311
  }
4041
4312
  var STACK_METHOD_CLONE = "git pull + pnpm install + pnpm build";
4042
4313
  function resolvePnpm() {
@@ -4051,7 +4322,7 @@ function cleanupTmp(dir) {
4051
4322
  }
4052
4323
  }
4053
4324
  function updateStackGitClone() {
4054
- const projectRoot = path29.dirname(stackRoot());
4325
+ const projectRoot = path28.dirname(stackRoot());
4055
4326
  const git = lookPath("git");
4056
4327
  if (!git) throw new Error("git no encontrado en PATH.");
4057
4328
  const pnpm = resolvePnpm();
@@ -4068,7 +4339,7 @@ function updateStackGlobal() {
4068
4339
  execFileSync5(pnpm, ["add", "-g", "jorgex-stack@latest"], { stdio: "inherit" });
4069
4340
  }
4070
4341
  async function downloadSkillToTemp(repo, sha, skillPath) {
4071
- const root = fs21.mkdtempSync(path29.join(os4.tmpdir(), "jorgex-skill-"));
4342
+ const root = fs21.mkdtempSync(path28.join(os3.tmpdir(), "jorgex-skill-"));
4072
4343
  try {
4073
4344
  const result = await downloadRepoTarball(repo, sha, root, skillPath);
4074
4345
  if (!result.ok) {
@@ -4076,14 +4347,14 @@ async function downloadSkillToTemp(repo, sha, skillPath) {
4076
4347
  return { error: result.reason };
4077
4348
  }
4078
4349
  if (skillPath) {
4079
- const sub = path29.resolve(path29.join(root, skillPath));
4350
+ const sub = path28.resolve(path28.join(root, skillPath));
4080
4351
  if (!isContainedIn(sub, root)) {
4081
4352
  cleanupTmp(root);
4082
4353
  return { error: `la ruta "${skillPath}" escapa del directorio temporal` };
4083
4354
  }
4084
4355
  if (fs21.existsSync(sub)) return { dir: sub, root };
4085
4356
  const lastSeg = skillPath.split("/").pop();
4086
- const sub2 = path29.resolve(path29.join(root, lastSeg));
4357
+ const sub2 = path28.resolve(path28.join(root, lastSeg));
4087
4358
  if (isContainedIn(sub2, root) && fs21.existsSync(sub2)) {
4088
4359
  if (!validateExtractedTree(sub2)) {
4089
4360
  cleanupTmp(root);
@@ -4104,10 +4375,10 @@ function pruneEngramDbBackups() {
4104
4375
  try {
4105
4376
  const dir = dataDir();
4106
4377
  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);
4378
+ 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
4379
  for (const old of backups.slice(3)) {
4109
4380
  try {
4110
- fs21.rmSync(path29.join(dir, old.name));
4381
+ fs21.rmSync(path28.join(dir, old.name));
4111
4382
  } catch {
4112
4383
  }
4113
4384
  }
@@ -4116,17 +4387,17 @@ function pruneEngramDbBackups() {
4116
4387
  }
4117
4388
  function rotateLockedBinary(binPath, sweepRoot = HOME) {
4118
4389
  if (!fs21.existsSync(binPath)) return null;
4119
- const dir = path29.dirname(binPath);
4120
- const base = path29.basename(binPath);
4390
+ const dir = path28.dirname(binPath);
4391
+ const base = path28.basename(binPath);
4121
4392
  const escapedBase = base.replace(/[.*+?^$()|[\]{}\\]/g, "\\$&");
4122
4393
  const oldPattern = new RegExp("^" + escapedBase + "\\.old-\\d+$");
4123
- const resolvedDir = path29.resolve(dir);
4124
- if (resolvedDir === path29.resolve(sweepRoot) || isContainedIn(resolvedDir, sweepRoot)) {
4394
+ const resolvedDir = path28.resolve(dir);
4395
+ if (resolvedDir === path28.resolve(sweepRoot) || isContainedIn(resolvedDir, sweepRoot)) {
4125
4396
  try {
4126
4397
  for (const entry of fs21.readdirSync(dir)) {
4127
4398
  if (oldPattern.test(entry)) {
4128
4399
  try {
4129
- fs21.rmSync(path29.join(dir, entry), { force: true });
4400
+ fs21.rmSync(path28.join(dir, entry), { force: true });
4130
4401
  } catch {
4131
4402
  }
4132
4403
  }
@@ -4134,7 +4405,7 @@ function rotateLockedBinary(binPath, sweepRoot = HOME) {
4134
4405
  } catch {
4135
4406
  }
4136
4407
  }
4137
- const rotated = path29.join(dir, `${base}.old-${Date.now()}`);
4408
+ const rotated = path28.join(dir, `${base}.old-${Date.now()}`);
4138
4409
  fs21.renameSync(binPath, rotated);
4139
4410
  return rotated;
4140
4411
  }
@@ -4144,8 +4415,8 @@ async function updateEngram(engramRepo, latestVersion) {
4144
4415
  "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
4416
  );
4146
4417
  }
4147
- const engramDataDir = process.env.ENGRAM_DATA_DIR ?? path29.join(HOME, ".engram");
4148
- const engramDb = path29.join(engramDataDir, "engram.db");
4418
+ const engramDataDir = process.env.ENGRAM_DATA_DIR ?? path28.join(HOME, ".engram");
4419
+ const engramDb = path28.join(engramDataDir, "engram.db");
4149
4420
  if (fs21.existsSync(engramDb)) {
4150
4421
  const doBackup = await p4.confirm({
4151
4422
  message: `\xBFHacer backup de la DB de Engram antes de actualizar? (${engramDb})`,
@@ -4153,7 +4424,7 @@ async function updateEngram(engramRepo, latestVersion) {
4153
4424
  });
4154
4425
  if (!p4.isCancel(doBackup) && doBackup) {
4155
4426
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4156
- const dest = path29.join(dataDir(), `engram-db-backup-${ts}.db`);
4427
+ const dest = path28.join(dataDir(), `engram-db-backup-${ts}.db`);
4157
4428
  try {
4158
4429
  if (!fs21.existsSync(dataDir())) fs21.mkdirSync(dataDir(), { recursive: true });
4159
4430
  fs21.copyFileSync(engramDb, dest);
@@ -4490,7 +4761,7 @@ async function runInteractiveUpdate(localVersion, yes, dryRun = false, includeBr
4490
4761
  continue;
4491
4762
  }
4492
4763
  const { dir: tmpDir, root: tmpRoot } = tmpResult;
4493
- const localSkillDir = path29.join(stackRoot(), "skills", skillInfo.name);
4764
+ const localSkillDir = path28.join(stackRoot(), "skills", skillInfo.name);
4494
4765
  const diff = renderSkillDiff(tmpDir, localSkillDir);
4495
4766
  if (diff) {
4496
4767
  p4.log.info(`Diff de ${skillInfo.name}:
@@ -4576,7 +4847,7 @@ ${diff}`);
4576
4847
  }
4577
4848
 
4578
4849
  // src/models-picker.ts
4579
- import path30 from "path";
4850
+ import path29 from "path";
4580
4851
  import * as p5 from "@clack/prompts";
4581
4852
  var TIERS = ["strong", "standard", "cheap"];
4582
4853
  var EFFORTS = ["low", "medium", "high", "xhigh"];
@@ -4600,7 +4871,7 @@ function opencodeLiveModels(binPath) {
4600
4871
  }
4601
4872
  function agentsByTier() {
4602
4873
  const grouped = { strong: [], standard: [], cheap: [] };
4603
- for (const agent of loadCanonicalAgents(path30.join(stackRoot(), "agents"))) {
4874
+ for (const agent of loadCanonicalAgents(path29.join(stackRoot(), "agents"))) {
4604
4875
  if (agent.mode === "subagent") grouped[agent.tier].push(agent.name);
4605
4876
  }
4606
4877
  return grouped;
@@ -4781,15 +5052,15 @@ function cancelled() {
4781
5052
 
4782
5053
  // src/lib/release.ts
4783
5054
  import fs22 from "fs";
4784
- import path31 from "path";
5055
+ import path30 from "path";
4785
5056
  import { execFileSync as execFileSync6 } from "child_process";
4786
- import { fileURLToPath as fileURLToPath2 } from "url";
5057
+ import { fileURLToPath } from "url";
4787
5058
  function findPackageJson() {
4788
- let dir = path31.dirname(fileURLToPath2(import.meta.url));
5059
+ let dir = path30.dirname(fileURLToPath(import.meta.url));
4789
5060
  for (let i = 0; i < 6; i++) {
4790
- const candidate = path31.join(dir, "package.json");
5061
+ const candidate = path30.join(dir, "package.json");
4791
5062
  if (fs22.existsSync(candidate)) return candidate;
4792
- dir = path31.dirname(dir);
5063
+ dir = path30.dirname(dir);
4793
5064
  }
4794
5065
  throw new Error("No se encontr\xF3 package.json cerca del CLI.");
4795
5066
  }
@@ -4809,8 +5080,8 @@ function readPackageMetadata() {
4809
5080
  }
4810
5081
 
4811
5082
  // src/lib/pi-runtime.ts
4812
- import os5 from "os";
4813
- import path32 from "path";
5083
+ import os4 from "os";
5084
+ import path31 from "path";
4814
5085
  import fs23 from "fs";
4815
5086
  import { spawnSync } from "child_process";
4816
5087
  import { createHash } from "crypto";
@@ -4964,14 +5235,14 @@ function healthyDoctor(stdout, stderr, packageRunner, candidate) {
4964
5235
  if (record === null || typeof record !== "object" || Array.isArray(record)) return false;
4965
5236
  const packageValue = Reflect.get(record, "package");
4966
5237
  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;
5238
+ 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
5239
  } catch {
4969
5240
  return false;
4970
5241
  }
4971
5242
  }
4972
5243
  function installPiFromVerifiedTarball(input, deps) {
4973
5244
  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`);
5245
+ 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
5246
  const artifact = deps.download(destination);
4976
5247
  if (artifact.bytes !== input.candidate.bytes || artifact.sha256 !== input.candidate.sha256 || artifact.sha512 !== input.candidate.sha512) {
4977
5248
  return { kind: "blocked", reason: "tarball-integrity" };
@@ -4979,7 +5250,7 @@ function installPiFromVerifiedTarball(input, deps) {
4979
5250
  deps.backupSettings();
4980
5251
  const scope = {
4981
5252
  kind: input.targetDir === void 0 ? "real" : "target-dir",
4982
- codingAgentDir: path32.resolve(paths.codingAgentDir)
5253
+ codingAgentDir: path31.resolve(paths.codingAgentDir)
4983
5254
  };
4984
5255
  const installing = flatCandidateReceipt(input.candidate, scope, "installing", input.engramBin);
4985
5256
  deps.writeReceiptAtomic(`${JSON.stringify(installing)}
@@ -5008,30 +5279,30 @@ function installPiFromVerifiedTarball(input, deps) {
5008
5279
  return { kind: "installed", receipt };
5009
5280
  }
5010
5281
  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);
5282
+ 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"];
5283
+ return [...new Set(entries.filter((entry) => entry !== null))].join(path31.delimiter);
5013
5284
  }
5014
5285
  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");
5286
+ const root = path31.resolve(targetDir);
5287
+ const codingAgentDir = path31.join(root, "pi-agent");
5288
+ const home = path31.join(root, "home");
5289
+ const temporary = path31.join(root, "tmp");
5019
5290
  return {
5020
5291
  codingAgentDir,
5021
- receiptPath: path32.join(root, "state", "pi-receipt.json"),
5022
- packageRunner: path32.join(codingAgentDir, "npm", "node_modules", "jorgex-pi", "bin", "jorgex-pi.mjs"),
5292
+ receiptPath: path31.join(root, "state", "pi-receipt.json"),
5293
+ packageRunner: path31.join(codingAgentDir, "npm", "node_modules", "jorgex-pi", "bin", "jorgex-pi.mjs"),
5023
5294
  environment: {
5024
5295
  HOME: home,
5025
5296
  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"),
5297
+ APPDATA: path31.join(root, "appdata"),
5298
+ LOCALAPPDATA: path31.join(root, "localappdata"),
5299
+ XDG_CONFIG_HOME: path31.join(root, "xdg-config"),
5300
+ XDG_DATA_HOME: path31.join(root, "xdg-data"),
5301
+ XDG_CACHE_HOME: path31.join(root, "xdg-cache"),
5031
5302
  TEMP: temporary,
5032
5303
  TMP: temporary,
5033
5304
  TMPDIR: temporary,
5034
- npm_config_cache: path32.join(root, "npm-cache"),
5305
+ npm_config_cache: path31.join(root, "npm-cache"),
5035
5306
  NPM_CONFIG_IGNORE_SCRIPTS: "true",
5036
5307
  NPM_CONFIG_UPDATE_NOTIFIER: "false",
5037
5308
  PI_CODING_AGENT_DIR: codingAgentDir,
@@ -5041,17 +5312,17 @@ function targetPaths(targetDir, engramBin, piExecutable) {
5041
5312
  };
5042
5313
  }
5043
5314
  function userPaths(engramBin, piExecutable) {
5044
- const home = os5.homedir();
5045
- const codingAgentDir = process.env.PI_CODING_AGENT_DIR ?? path32.join(home, ".pi", "agent");
5315
+ const home = os4.homedir();
5316
+ const codingAgentDir = process.env.PI_CODING_AGENT_DIR ?? path31.join(home, ".pi", "agent");
5046
5317
  return {
5047
5318
  codingAgentDir,
5048
- receiptPath: path32.join(dataDir(), "pi-receipt.json"),
5049
- packageRunner: path32.join(codingAgentDir, "npm", "node_modules", "jorgex-pi", "bin", "jorgex-pi.mjs"),
5319
+ receiptPath: path31.join(dataDir(), "pi-receipt.json"),
5320
+ packageRunner: path31.join(codingAgentDir, "npm", "node_modules", "jorgex-pi", "bin", "jorgex-pi.mjs"),
5050
5321
  environment: {
5051
5322
  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(),
5323
+ XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME ?? path31.join(home, ".config"),
5324
+ XDG_CACHE_HOME: process.env.XDG_CACHE_HOME ?? path31.join(home, ".cache"),
5325
+ TMPDIR: os4.tmpdir(),
5055
5326
  NPM_CONFIG_IGNORE_SCRIPTS: "true",
5056
5327
  NPM_CONFIG_UPDATE_NOTIFIER: "false",
5057
5328
  PI_CODING_AGENT_DIR: codingAgentDir,
@@ -5078,7 +5349,7 @@ function runPiRuntime(input, deps) {
5078
5349
  return { kind: "blocked", reason: "tarball-integrity" };
5079
5350
  }
5080
5351
  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"));
5352
+ const settingsJson = deps.readSettings(path31.join(paths.codingAgentDir, "settings.json"));
5082
5353
  const receiptJson = deps.readReceipt(paths.receiptPath);
5083
5354
  const lifecycleInput = {
5084
5355
  candidate: PI_RUNTIME_CANDIDATE,
@@ -5138,14 +5409,14 @@ function readJsonFile(file) {
5138
5409
  function packageVersionFromExecutable(executable) {
5139
5410
  let current;
5140
5411
  try {
5141
- current = path32.dirname(fs23.realpathSync(executable));
5412
+ current = path31.dirname(fs23.realpathSync(executable));
5142
5413
  } catch {
5143
5414
  return null;
5144
5415
  }
5145
5416
  for (let depth = 0; depth < 8; depth++) {
5146
5417
  const manifests = [
5147
- path32.join(current, "package.json"),
5148
- path32.join(current, "node_modules", "@earendil-works", "pi-coding-agent", "package.json")
5418
+ path31.join(current, "package.json"),
5419
+ path31.join(current, "node_modules", "@earendil-works", "pi-coding-agent", "package.json")
5149
5420
  ];
5150
5421
  for (const manifest of manifests) {
5151
5422
  try {
@@ -5156,7 +5427,7 @@ function packageVersionFromExecutable(executable) {
5156
5427
  } catch {
5157
5428
  }
5158
5429
  }
5159
- const parent = path32.dirname(current);
5430
+ const parent = path31.dirname(current);
5160
5431
  if (parent === current) break;
5161
5432
  current = parent;
5162
5433
  }
@@ -5164,23 +5435,23 @@ function packageVersionFromExecutable(executable) {
5164
5435
  }
5165
5436
  function detectPiRuntime() {
5166
5437
  const executable = lookPath("pi");
5167
- const home = os5.homedir();
5438
+ const home = os4.homedir();
5168
5439
  return {
5169
5440
  id: "pi",
5170
5441
  name: "Pi",
5171
5442
  installed: executable !== null,
5172
5443
  executable,
5173
5444
  version: executable === null ? null : packageVersionFromExecutable(executable),
5174
- codingAgentDir: process.env.PI_CODING_AGENT_DIR ?? path32.join(home, ".pi", "agent")
5445
+ codingAgentDir: process.env.PI_CODING_AGENT_DIR ?? path31.join(home, ".pi", "agent")
5175
5446
  };
5176
5447
  }
5177
5448
  function hasManagedPiRuntime(targetDir) {
5178
- const receipt = targetDir === void 0 ? path32.join(dataDir(), "pi-receipt.json") : path32.join(path32.resolve(targetDir), "state", "pi-receipt.json");
5449
+ const receipt = targetDir === void 0 ? path31.join(dataDir(), "pi-receipt.json") : path31.join(path31.resolve(targetDir), "state", "pi-receipt.json");
5179
5450
  return fs23.statSync(receipt, { throwIfNoEntry: false })?.isFile() === true;
5180
5451
  }
5181
5452
  function resolvePiEngramBin(targetDir) {
5182
5453
  if (targetDir === void 0) return detectEngram();
5183
- const candidate = path32.join(path32.resolve(targetDir), "bin", process.platform === "win32" ? "engram.exe" : "engram");
5454
+ const candidate = path31.join(path31.resolve(targetDir), "bin", process.platform === "win32" ? "engram.exe" : "engram");
5184
5455
  return fs23.statSync(candidate, { throwIfNoEntry: false })?.isFile() ? candidate : null;
5185
5456
  }
5186
5457
  function readOptional(file, fallback) {
@@ -5220,7 +5491,7 @@ async function acquirePiTarball(destination) {
5220
5491
  }
5221
5492
  fs23.rmSync(destination, { force: true });
5222
5493
  }
5223
- fs23.mkdirSync(path32.dirname(destination), { recursive: true });
5494
+ fs23.mkdirSync(path31.dirname(destination), { recursive: true });
5224
5495
  const partial = `${destination}.partial-${process.pid}`;
5225
5496
  const response = await fetch(`https://registry.npmjs.org/jorgex-pi/-/jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`, {
5226
5497
  redirect: "error",
@@ -5293,7 +5564,7 @@ async function runPiRuntimeSystem(input) {
5293
5564
  remedy: "Instala Engram o configura un ENGRAM_BIN absoluto antes de reintentar."
5294
5565
  };
5295
5566
  }
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`);
5567
+ 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
5568
  let artifact;
5298
5569
  try {
5299
5570
  artifact = await acquirePiTarball(destination);
@@ -5313,18 +5584,18 @@ async function runPiRuntimeSystem(input) {
5313
5584
  }, {
5314
5585
  download: () => artifact,
5315
5586
  backupSettings: () => createBackup(
5316
- [path32.join(paths.codingAgentDir, "settings.json")],
5587
+ [path31.join(paths.codingAgentDir, "settings.json")],
5317
5588
  "pi-package-install",
5318
- input.targetDir === void 0 ? void 0 : path32.join(path32.resolve(input.targetDir), "backups")
5589
+ input.targetDir === void 0 ? void 0 : path31.join(path31.resolve(input.targetDir), "backups")
5319
5590
  ),
5320
5591
  run: runProcess,
5321
- readSettings: () => readOptional(path32.join(paths.codingAgentDir, "settings.json"), '{"packages":[]}'),
5322
- rewriteSettings: (content) => writeText(path32.join(paths.codingAgentDir, "settings.json"), `${content}
5592
+ readSettings: () => readOptional(path31.join(paths.codingAgentDir, "settings.json"), '{"packages":[]}'),
5593
+ rewriteSettings: (content) => writeText(path31.join(paths.codingAgentDir, "settings.json"), `${content}
5323
5594
  `),
5324
5595
  writeReceiptAtomic: (content) => writeText(paths.receiptPath, content)
5325
5596
  });
5326
5597
  }
5327
- const packageRoot = path32.dirname(path32.dirname(paths.packageRunner));
5598
+ const packageRoot = path31.dirname(path31.dirname(paths.packageRunner));
5328
5599
  const writeReceipt = (receipt) => {
5329
5600
  writeText(paths.receiptPath, `${JSON.stringify(receipt, null, 2)}
5330
5601
  `);
@@ -5342,9 +5613,9 @@ async function runPiRuntimeSystem(input) {
5342
5613
  value,
5343
5614
  {
5344
5615
  backupSettings: () => createBackup(
5345
- [path32.join(paths.codingAgentDir, "settings.json")],
5616
+ [path31.join(paths.codingAgentDir, "settings.json")],
5346
5617
  "pi-package-uninstall",
5347
- input.targetDir === void 0 ? void 0 : path32.join(path32.resolve(input.targetDir), "backups")
5618
+ input.targetDir === void 0 ? void 0 : path31.join(path31.resolve(input.targetDir), "backups")
5348
5619
  ),
5349
5620
  run: runProcess,
5350
5621
  isPackageAbsent: () => !fs23.existsSync(packageRoot),
@@ -5445,466 +5716,8 @@ async function runManagedPiSystem(input) {
5445
5716
  }
5446
5717
 
5447
5718
  // src/lib/quality-runner.ts
5448
- import path33 from "path";
5719
+ import path32 from "path";
5449
5720
  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
5721
  function normalizeLimit(value, name) {
5909
5722
  if (value === void 0) return void 0;
5910
5723
  if (!Number.isSafeInteger(value) || value < 0) {
@@ -5922,14 +5735,14 @@ function normalizeTimeout(value) {
5922
5735
  }
5923
5736
  return timeoutMs;
5924
5737
  }
5925
- var COMMIT_SHA_PATTERN2 = /^[0-9a-f]{40}$/i;
5738
+ var COMMIT_SHA_PATTERN = /^[0-9a-f]{40}$/i;
5926
5739
  function isRecord3(value) {
5927
5740
  return value !== null && typeof value === "object" && !Array.isArray(value);
5928
5741
  }
5929
5742
  function hasText2(value) {
5930
5743
  return typeof value === "string" && value.trim() !== "";
5931
5744
  }
5932
- function isDenseStringArray2(value) {
5745
+ function isDenseStringArray(value) {
5933
5746
  if (!Array.isArray(value)) return false;
5934
5747
  for (let index = 0; index < value.length; index += 1) {
5935
5748
  if (!Object.prototype.hasOwnProperty.call(value, String(index)) || typeof value[index] !== "string") return false;
@@ -5939,7 +5752,7 @@ function isDenseStringArray2(value) {
5939
5752
  function isNonNegativeSafeInteger(value) {
5940
5753
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
5941
5754
  }
5942
- function isQualityProfile3(value) {
5755
+ function isQualityProfile(value) {
5943
5756
  return typeof value === "string" && QUALITY_PROFILES.includes(value);
5944
5757
  }
5945
5758
  function assertValidEnvironment(value, label) {
@@ -5951,13 +5764,13 @@ function assertValidEnvironment(value, label) {
5951
5764
  function assertQualityPlanInput(value) {
5952
5765
  if (!isRecord3(value)) throw new Error("Invalid quality plan");
5953
5766
  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)) {
5767
+ if (typeof value.identity.baseSha !== "string" || !COMMIT_SHA_PATTERN.test(value.identity.baseSha)) {
5955
5768
  throw new Error("Invalid quality plan identity.baseSha");
5956
5769
  }
5957
- if (typeof value.identity.headSha !== "string" || !COMMIT_SHA_PATTERN2.test(value.identity.headSha)) {
5770
+ if (typeof value.identity.headSha !== "string" || !COMMIT_SHA_PATTERN.test(value.identity.headSha)) {
5958
5771
  throw new Error("Invalid quality plan identity.headSha");
5959
5772
  }
5960
- if (!isQualityProfile3(value.profile)) throw new Error("Invalid quality plan profile");
5773
+ if (!isQualityProfile(value.profile)) throw new Error("Invalid quality plan profile");
5961
5774
  if (!Array.isArray(value.controls)) throw new Error("Invalid quality plan controls");
5962
5775
  const controlIds = /* @__PURE__ */ new Set();
5963
5776
  for (let index = 0; index < value.controls.length; index += 1) {
@@ -5980,7 +5793,7 @@ function assertQualityPlanInput(value) {
5980
5793
  const command = value.commands[index];
5981
5794
  if (!isRecord3(command)) throw new Error(`Invalid quality plan commands[${index}]`);
5982
5795
  const timeoutMs = command.timeoutMs;
5983
- if (!hasText2(command.controlId) || !hasText2(command.commandId) || !hasText2(command.executable) || !isDenseStringArray2(command.argv) || !isNonNegativeSafeInteger(timeoutMs)) {
5796
+ if (!hasText2(command.controlId) || !hasText2(command.commandId) || !hasText2(command.executable) || !isDenseStringArray(command.argv) || !isNonNegativeSafeInteger(timeoutMs)) {
5984
5797
  throw new Error(`Invalid quality plan commands[${index}]`);
5985
5798
  }
5986
5799
  if (timeoutMs > MAX_NODE_TIMEOUT_MS) {
@@ -6035,7 +5848,7 @@ function killProcessTree(child) {
6035
5848
  if (pid === void 0) return;
6036
5849
  if (process.platform === "win32") {
6037
5850
  const systemRoot = process.env.SystemRoot ?? process.env.WINDIR ?? "C:\\Windows";
6038
- const taskkill = path33.join(systemRoot, "System32", "taskkill.exe");
5851
+ const taskkill = path32.join(systemRoot, "System32", "taskkill.exe");
6039
5852
  const result = spawnSync2(taskkill, ["/pid", String(pid), "/t", "/f"], {
6040
5853
  shell: false,
6041
5854
  stdio: "ignore",
@@ -6261,11 +6074,15 @@ async function runQualityPlan(input) {
6261
6074
  results.push(receiptResultFor(command.controlId, result));
6262
6075
  }
6263
6076
  results.push(...missingRequiredResults(input.controls, input.commands));
6264
- const evaluation = evaluateQualityPolicy({
6077
+ const policyEvaluation = evaluateQualityPolicy({
6265
6078
  profile: input.profile,
6266
6079
  controls: input.controls,
6267
6080
  results
6268
6081
  });
6082
+ const evaluation = {
6083
+ ...policyEvaluation,
6084
+ status: localQualityStatus(input.profile, policyEvaluation.status)
6085
+ };
6269
6086
  const receipt = createQualityReceipt({
6270
6087
  authority: "local",
6271
6088
  identity,