jorgex-stack 1.9.58 → 1.9.60

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
@@ -12,32 +12,27 @@ import {
12
12
  inspectOpencodePluginFile,
13
13
  opencodeAdapter,
14
14
  shouldRetireLegacyEngram
15
- } from "./chunk-M6QX2F3A.js";
15
+ } from "./chunk-BSHTDTJL.js";
16
16
  import {
17
17
  claudeCodeAdapter
18
- } from "./chunk-NNPFC6QV.js";
18
+ } from "./chunk-4USZMJ3I.js";
19
19
  import {
20
20
  codexAdapter
21
- } from "./chunk-GOLJ5PA3.js";
21
+ } from "./chunk-KKN4OYHD.js";
22
22
  import {
23
23
  DEFAULT_MODEL_MAP,
24
24
  DEVTOOLS_MCP_SERVER,
25
+ LEGACY_SYSTEM_PROMPT_SECTIONS,
25
26
  SYSTEM_PROMPT_SECTIONS,
26
27
  assertSystemPromptFile,
27
28
  assertSystemPromptMarkers,
28
- copyFile,
29
- createBackup,
30
29
  detectClaudeCode,
31
30
  detectCodex,
32
31
  detectEngram,
33
32
  detectOpenCode,
34
33
  engramVersion,
35
- ensureDir,
36
34
  ensureModelMapFile,
37
35
  hasHealthyManagedMarkdownMarkers,
38
- isContainedIn,
39
- listBackups,
40
- listFilesRecursive,
41
36
  loadCanonicalAgents,
42
37
  loadCanonicalHooks,
43
38
  loadCanonicalMcp,
@@ -46,26 +41,37 @@ import {
46
41
  lookPath,
47
42
  modelMapFile,
48
43
  planDetectedBinCommand,
49
- pruneEmptyDirs,
50
44
  readSystemPromptFile,
51
- readTextIfExists,
52
45
  removeMarkdownSection,
53
46
  removeSystemPromptSections,
54
47
  resolveAgentModel,
55
- restoreBackup,
56
48
  runDetectedBin,
49
+ stripLeadingHtmlComments,
50
+ upsertMarkdownSection
51
+ } from "./chunk-66WVKOU4.js";
52
+ import {
53
+ piAdapter,
54
+ piSystemPromptFile
55
+ } from "./chunk-2CBCBOP7.js";
56
+ import {
57
+ copyFile,
58
+ createBackup,
59
+ ensureDir,
60
+ isContainedIn,
61
+ listBackups,
62
+ listFilesRecursive,
63
+ pruneEmptyDirs,
64
+ readTextIfExists,
65
+ restoreBackup,
57
66
  runOfficialSetupIfNeeded,
58
67
  sameFileContent,
59
68
  shouldRunOfficialSetup,
60
- stripLeadingHtmlComments,
61
- upsertMarkdownSection,
62
69
  validateOfficialSetupDestination,
63
70
  writeText
64
- } from "./chunk-KB5IQEKL.js";
71
+ } from "./chunk-B76ZJXXD.js";
65
72
  import {
66
73
  HOME,
67
74
  dataDir,
68
- samePath,
69
75
  stackRoot
70
76
  } from "./chunk-NQZVJFV2.js";
71
77
 
@@ -355,7 +361,6 @@ function planSystemPrompt(adapter, ctx) {
355
361
  );
356
362
  const modules = {
357
363
  "system-prompt": composeProgrammaticSystemPrompt(ctx.stackDir, readModule("AGENTS.md"), ctx.mode),
358
- "engram-protocol": adapter.injectEngramProtocol(ctx) ? readModule("engram-protocol.md") : void 0,
359
364
  context7: readModule("context7.md"),
360
365
  playwright: ctx.playwrightCliEnabled ? readModule("browser-playwright.md") : void 0,
361
366
  "chrome-devtools": ctx.enabledMcpServers?.has(DEVTOOLS_MCP_SERVER) ? readModule("browser-chrome-devtools.md") : void 0,
@@ -367,6 +372,9 @@ function planSystemPrompt(adapter, ctx) {
367
372
  const body = sections[section];
368
373
  content = body ? upsertMarkdownSection(content, section, body) : removeMarkdownSection(content, section);
369
374
  }
375
+ for (const section of LEGACY_SYSTEM_PROMPT_SECTIONS) {
376
+ content = removeMarkdownSection(content, section);
377
+ }
370
378
  return [{ kind: "write", target, content }];
371
379
  }
372
380
 
@@ -457,12 +465,6 @@ function planPlugins(adapter, ctx) {
457
465
  if (content.includes('"{{ENGRAM_BIN}}"')) {
458
466
  content = content.replace(/"\{\{ENGRAM_BIN\}\}"/g, JSON.stringify(ctx.engramBin ?? "engram"));
459
467
  }
460
- if (content.includes('"{{ENGRAM_PROTOCOL}}"')) {
461
- const protocol = stripLeadingHtmlComments(
462
- fs7.readFileSync(path9.join(ctx.stackDir, "system-prompt", "engram-protocol.md"), "utf8").replace(/\r\n/g, "\n")
463
- );
464
- content = content.replace(/"\{\{ENGRAM_PROTOCOL\}\}"/g, JSON.stringify(protocol));
465
- }
466
468
  content = content.replace(/(from\s+["'])(\.{1,2}\/[^"']+?)\.js(["'])/g, "$1$2.ts$3");
467
469
  if (content === raw) return { kind: "copy", source: sourceFile, target };
468
470
  return { kind: "write", target, content };
@@ -1491,52 +1493,16 @@ async function runInstall(opts) {
1491
1493
 
1492
1494
  // src/uninstall.ts
1493
1495
  import fs12 from "fs";
1494
- import path16 from "path";
1496
+ import path15 from "path";
1495
1497
  import * as p2 from "@clack/prompts";
1496
1498
 
1497
1499
  // src/lib/pi-projection-lifecycle.ts
1498
1500
  import fs11 from "fs";
1499
- import path15 from "path";
1501
+ import path14 from "path";
1500
1502
  import { createHash } from "crypto";
1501
1503
 
1502
- // src/adapters/pi.ts
1503
- import path13 from "path";
1504
- function piSystemPromptFile(targetDir) {
1505
- const configDir = targetDir === void 0 ? process.env.PI_CODING_AGENT_DIR ?? path13.join(HOME, ".pi", "agent") : path13.join(path13.resolve(targetDir), "pi-agent");
1506
- return path13.join(configDir, "AGENTS.md");
1507
- }
1508
- var piAdapter = {
1509
- id: "pi",
1510
- paths(configDir) {
1511
- const piConfigDir = path13.dirname(piSystemPromptFile());
1512
- const agentsHome = samePath(configDir, piConfigDir) ? HOME : path13.join(path13.dirname(configDir), "home");
1513
- return {
1514
- systemPromptFile: path13.join(configDir, "AGENTS.md"),
1515
- agentsDir: path13.join(configDir, "agents"),
1516
- skillsDir: path13.join(agentsHome, ".agents", "skills"),
1517
- commandsDir: path13.join(configDir, "prompts"),
1518
- pluginsDir: null,
1519
- scriptsDir: path13.join(configDir, "scripts"),
1520
- outputStylesDir: null,
1521
- profilesDir: null
1522
- };
1523
- },
1524
- renderCommand(file, content) {
1525
- return { file, content: content.replace(/\{\{input\}\}/g, "$ARGUMENTS") };
1526
- },
1527
- injectEngramProtocol() {
1528
- return true;
1529
- },
1530
- // La guía Context7 se habilita al adoptar su registro HTTP en Pi.
1531
- adaptSystemPromptSections(sections) {
1532
- const modular = { ...sections };
1533
- delete modular.context7;
1534
- return modular;
1535
- }
1536
- };
1537
-
1538
1504
  // src/lib/pi-package-lifecycle.ts
1539
- import path14 from "path";
1505
+ import path13 from "path";
1540
1506
  var REQUIRED_CAPABILITIES = /* @__PURE__ */ new Set([
1541
1507
  "foundation-contract-v1",
1542
1508
  "runner-json-v1",
@@ -1688,7 +1654,7 @@ function parseReceiptShape(receiptJson) {
1688
1654
  return null;
1689
1655
  }
1690
1656
  if (engram === void 0) return "upgrade-required";
1691
- if (engram === null || typeof engram !== "object" || Array.isArray(engram) || typeof Reflect.get(engram, "binary") !== "string" || !path14.isAbsolute(Reflect.get(engram, "binary"))) {
1657
+ if (engram === null || typeof engram !== "object" || Array.isArray(engram) || typeof Reflect.get(engram, "binary") !== "string" || !path13.isAbsolute(Reflect.get(engram, "binary"))) {
1692
1658
  return null;
1693
1659
  }
1694
1660
  return parsed;
@@ -1725,7 +1691,7 @@ function planPiPackageLifecycle(input) {
1725
1691
  if (input.receiptJson !== null) {
1726
1692
  const parsedReceipt = parseReceipt(input.receiptJson, input.candidate, {
1727
1693
  kind: input.scope.kind,
1728
- codingAgentDir: path14.resolve(input.scope.codingAgentDir)
1694
+ codingAgentDir: path13.resolve(input.scope.codingAgentDir)
1729
1695
  }, input.engramBin);
1730
1696
  if (parsedReceipt === "upgrade-required") return blocked(input, "receipt-upgrade-required");
1731
1697
  if (parsedReceipt === null) return blocked(input, "receipt-corrupt");
@@ -1760,7 +1726,7 @@ function planPiPackageLifecycle(input) {
1760
1726
  },
1761
1727
  receipt: expectedReceipt(input.candidate, "installing", {
1762
1728
  kind: input.scope.kind,
1763
- codingAgentDir: path14.resolve(input.scope.codingAgentDir)
1729
+ codingAgentDir: path13.resolve(input.scope.codingAgentDir)
1764
1730
  }, input.engramBin),
1765
1731
  ownership: ownership(true)
1766
1732
  };
@@ -1775,7 +1741,7 @@ function parseRunnerRecord(stdout, stderr, command, candidate, packageRunner) {
1775
1741
  const parsed = JSON.parse(body);
1776
1742
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return null;
1777
1743
  const record = parsed;
1778
- 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" || !path14.isAbsolute(record.package.root) || path14.resolve(packageRunner) !== path14.resolve(record.package.root, "bin", "jorgex-pi.mjs")) {
1744
+ 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" || !path13.isAbsolute(record.package.root) || path13.resolve(packageRunner) !== path13.resolve(record.package.root, "bin", "jorgex-pi.mjs")) {
1779
1745
  return null;
1780
1746
  }
1781
1747
  return record;
@@ -1923,10 +1889,10 @@ function validateOwnedOperationState(input) {
1923
1889
  }))) {
1924
1890
  return { kind: "blocked", reason: "receipt-untrusted" };
1925
1891
  }
1926
- if (receipt.scope.kind !== (input.paths.targetDir ? "target-dir" : "real") || path14.resolve(receipt.scope.codingAgentDir) !== path14.resolve(input.paths.codingAgentDir)) {
1892
+ if (receipt.scope.kind !== (input.paths.targetDir ? "target-dir" : "real") || path13.resolve(receipt.scope.codingAgentDir) !== path13.resolve(input.paths.codingAgentDir)) {
1927
1893
  return { kind: "blocked", reason: "source-divergent" };
1928
1894
  }
1929
- if (input.engramBin !== null && path14.resolve(receipt.engram.binary) !== path14.resolve(input.engramBin)) {
1895
+ if (input.engramBin !== null && path13.resolve(receipt.engram.binary) !== path13.resolve(input.engramBin)) {
1930
1896
  return { kind: "blocked", reason: "receipt-corrupt" };
1931
1897
  }
1932
1898
  const source = receipt.candidate.package.source;
@@ -2033,7 +1999,7 @@ var RETIRED_PLAYWRIGHT_FILES = [
2033
1999
  "references/video-recording.md"
2034
2000
  ];
2035
2001
  function retiredPlaywrightFiles(scope) {
2036
- return RETIRED_PLAYWRIGHT_FILES.map((file) => path15.resolve(scope.home, ".agents", "skills", "playwright-cli", file));
2002
+ return RETIRED_PLAYWRIGHT_FILES.map((file) => path14.resolve(scope.home, ".agents", "skills", "playwright-cli", file));
2037
2003
  }
2038
2004
  function handoffEnabled(input, kind) {
2039
2005
  return (kind === "devtools" ? input.devtoolsMcpEnabled : input.playwrightHandoffEnabled) === true;
@@ -2042,18 +2008,18 @@ var preparedPiProjectionUninstalls = /* @__PURE__ */ new WeakMap();
2042
2008
  function projectionScope(scope) {
2043
2009
  return {
2044
2010
  ...scope,
2045
- settingsFile: path15.join(scope.codingAgentDir, "settings.json")
2011
+ settingsFile: path14.join(scope.codingAgentDir, "settings.json")
2046
2012
  };
2047
2013
  }
2048
2014
  function isInside(root, file) {
2049
- const relative = path15.relative(path15.resolve(root), path15.resolve(file));
2050
- return relative === "" || relative !== ".." && !relative.startsWith(`..${path15.sep}`) && !path15.isAbsolute(relative);
2015
+ const relative = path14.relative(path14.resolve(root), path14.resolve(file));
2016
+ return relative === "" || relative !== ".." && !relative.startsWith(`..${path14.sep}`) && !path14.isAbsolute(relative);
2051
2017
  }
2052
2018
  function isManagedPath(scope, file) {
2053
2019
  return isInside(scope.home, file) || isInside(scope.codingAgentDir, file);
2054
2020
  }
2055
2021
  function isAllowedPath(scope, file) {
2056
- return isManagedPath(scope, file) || path15.resolve(file) === path15.resolve(scope.receiptFile);
2022
+ return isManagedPath(scope, file) || path14.resolve(file) === path14.resolve(scope.receiptFile);
2057
2023
  }
2058
2024
  function projectedPiAdapter(scope) {
2059
2025
  const paths = piAdapter.paths(scope.codingAgentDir);
@@ -2061,7 +2027,7 @@ function projectedPiAdapter(scope) {
2061
2027
  ...piAdapter,
2062
2028
  paths: () => ({
2063
2029
  ...paths,
2064
- skillsDir: path15.join(scope.home, ".agents", "skills")
2030
+ skillsDir: path14.join(scope.home, ".agents", "skills")
2065
2031
  })
2066
2032
  };
2067
2033
  }
@@ -2105,7 +2071,7 @@ function projectionPlan(input, scope, includePrompt = true) {
2105
2071
  return actions;
2106
2072
  }
2107
2073
  function handoffPath(scope, kind) {
2108
- return path15.resolve(scope.codingAgentDir, "jorgex-pi", `${kind}.v1.json`);
2074
+ return path14.resolve(scope.codingAgentDir, "jorgex-pi", `${kind}.v1.json`);
2109
2075
  }
2110
2076
  function contentHash(content) {
2111
2077
  return createHash("sha256").update(content).digest("hex");
@@ -2118,7 +2084,7 @@ function hasActionDrift(action, deps) {
2118
2084
  }
2119
2085
  function applyActions(actions, deps, handoffs = []) {
2120
2086
  for (const action of actions) {
2121
- const handoff = handoffs.find((entry) => path15.resolve(action.target) === entry.file);
2087
+ const handoff = handoffs.find((entry) => path14.resolve(action.target) === entry.file);
2122
2088
  if (handoff) {
2123
2089
  try {
2124
2090
  if (deps.readText(handoff.file) !== handoff.previous) return handoffConflict(handoff.kind, handoff.file);
@@ -2138,9 +2104,9 @@ function assertPlanContained(plan, scope) {
2138
2104
  }
2139
2105
  }
2140
2106
  function receiptFor(plan, scope) {
2141
- const systemPrompt = path15.join(scope.codingAgentDir, "AGENTS.md");
2107
+ const systemPrompt = path14.join(scope.codingAgentDir, "AGENTS.md");
2142
2108
  const owned = [...new Set(
2143
- plan.map((action) => path15.resolve(action.target)).filter((target) => target !== systemPrompt)
2109
+ plan.map((action) => path14.resolve(action.target)).filter((target) => target !== systemPrompt)
2144
2110
  )];
2145
2111
  const receipt = {
2146
2112
  schemaVersion: 1,
@@ -2153,7 +2119,7 @@ function receiptFor(plan, scope) {
2153
2119
  owned
2154
2120
  };
2155
2121
  for (const kind of HANDOFF_KINDS) {
2156
- const action = plan.find((entry) => path15.resolve(entry.target) === handoffPath(scope, kind));
2122
+ const action = plan.find((entry) => path14.resolve(entry.target) === handoffPath(scope, kind));
2157
2123
  if (action) receipt[kind] = { sha256: contentHash(canonicalActionContent(action)) };
2158
2124
  }
2159
2125
  return receipt;
@@ -2194,7 +2160,7 @@ function parseReceipt2(raw, expected) {
2194
2160
  if (matches(expectedOwned)) return { schemaVersion: 1, scope: expected.scope, owned: expectedOwned, ...digests };
2195
2161
  const retired = retiredPlaywrightFiles(expected.scope);
2196
2162
  if (retired.some((file) => expectedOwned.includes(file))) return null;
2197
- const skillsRoot = `${path15.resolve(expected.scope.home, ".agents", "skills")}${path15.sep}`;
2163
+ const skillsRoot = `${path14.resolve(expected.scope.home, ".agents", "skills")}${path14.sep}`;
2198
2164
  const insertion = expectedOwned.findIndex((file) => !file.startsWith(skillsRoot) || file > retired[0]);
2199
2165
  const legacyOwned = [...expectedOwned];
2200
2166
  legacyOwned.splice(insertion === -1 ? legacyOwned.length : insertion, 0, ...retired);
@@ -2213,8 +2179,8 @@ function realPiProjectionScope() {
2213
2179
  return {
2214
2180
  kind: "real",
2215
2181
  home: HOME,
2216
- codingAgentDir: path15.dirname(piSystemPromptFile()),
2217
- receiptFile: path15.join(dataDir(), "pi-projection-receipt.json")
2182
+ codingAgentDir: path14.dirname(piSystemPromptFile()),
2183
+ receiptFile: path14.join(dataDir(), "pi-projection-receipt.json")
2218
2184
  };
2219
2185
  }
2220
2186
  function readTextOnlyIfMissing(file) {
@@ -2252,13 +2218,13 @@ function manifestOwned(manifest) {
2252
2218
  return new Set([
2253
2219
  ...manifest.runtimes.codex?.owned ?? [],
2254
2220
  ...manifest.runtimes.opencode?.owned ?? []
2255
- ].map((file) => path15.resolve(file)));
2221
+ ].map((file) => path14.resolve(file)));
2256
2222
  }
2257
2223
  function withoutManagedPromptSections(content) {
2258
2224
  return removeSystemPromptSections(content);
2259
2225
  }
2260
2226
  function uniquePaths(paths) {
2261
- return [...new Set(paths.map((file) => path15.resolve(file)))];
2227
+ return [...new Set(paths.map((file) => path14.resolve(file)))];
2262
2228
  }
2263
2229
  function blocked2(reason, paths, remedy) {
2264
2230
  return { kind: "blocked", reason, paths: uniquePaths(paths), remedy };
@@ -2365,7 +2331,7 @@ function preparePiProjectionUninstall(input, deps) {
2365
2331
  }
2366
2332
  const scope = projectionScope(input.scope);
2367
2333
  const expectedReceipt2 = expectedProjectionReceipt(input, scope);
2368
- const prompt = path15.join(scope.codingAgentDir, "AGENTS.md");
2334
+ const prompt = path14.join(scope.codingAgentDir, "AGENTS.md");
2369
2335
  let existingPrompt;
2370
2336
  try {
2371
2337
  existingPrompt = deps.readText(prompt);
@@ -2378,7 +2344,7 @@ function preparePiProjectionUninstall(input, deps) {
2378
2344
  return blocked2("projection-prompt-markers", [prompt], error instanceof Error ? error.message : String(error));
2379
2345
  }
2380
2346
  const promptContent = existingPrompt === null ? null : withoutManagedPromptSections(existingPrompt);
2381
- const promptUpdate = promptContent === null || promptContent === existingPrompt ? null : { file: path15.resolve(prompt), content: promptContent };
2347
+ const promptUpdate = promptContent === null || promptContent === existingPrompt ? null : { file: path14.resolve(prompt), content: promptContent };
2382
2348
  const receiptRead = readProjectionReceipt(scope.receiptFile, deps);
2383
2349
  if (receiptRead.kind === "unreadable") return receiptUnreadable(scope.receiptFile);
2384
2350
  let ownedToRemove = [];
@@ -2396,7 +2362,7 @@ function preparePiProjectionUninstall(input, deps) {
2396
2362
  return cleanupFailure([scope.receiptFile]);
2397
2363
  }
2398
2364
  ownedToRemove = receipt.owned.filter((owned) => !retained.has(owned));
2399
- receiptFile = path15.resolve(scope.receiptFile);
2365
+ receiptFile = path14.resolve(scope.receiptFile);
2400
2366
  backupTargets = [...backupTargets, ...receipt.owned, receiptFile];
2401
2367
  }
2402
2368
  const handoffs = [];
@@ -2474,16 +2440,16 @@ function runPiProjectionLifecycle(input, deps) {
2474
2440
  const prepared = preparePiProjectionUninstall(input, deps);
2475
2441
  return prepared.kind === "blocked" ? prepared : completePiProjectionUninstall(prepared.plan, deps);
2476
2442
  }
2477
- const prompt = path15.join(scope.codingAgentDir, "AGENTS.md");
2443
+ const prompt = path14.join(scope.codingAgentDir, "AGENTS.md");
2478
2444
  try {
2479
2445
  assertSystemPromptMarkers(deps.readText(prompt), prompt);
2480
2446
  } catch (error) {
2481
2447
  return blocked2("projection-prompt-markers", [prompt], error instanceof Error ? error.message : String(error));
2482
2448
  }
2483
- if (input.devtoolsMcpEnabled && (!input.pnpmBin || !path15.isAbsolute(input.pnpmBin))) {
2449
+ if (input.devtoolsMcpEnabled && (!input.pnpmBin || !path14.isAbsolute(input.pnpmBin))) {
2484
2450
  return blocked2("projection-devtools-command", [handoffPath(scope, "devtools")], "DevTools requiere un ejecutable pnpm absoluto disponible en PATH.");
2485
2451
  }
2486
- if (input.playwrightHandoffEnabled && (!input.playwrightCliCommand || !path15.isAbsolute(input.playwrightCliCommand) || /[\u0000-\u001f\u007f]/.test(input.playwrightCliCommand))) {
2452
+ if (input.playwrightHandoffEnabled && (!input.playwrightCliCommand || !path14.isAbsolute(input.playwrightCliCommand) || /[\u0000-\u001f\u007f]/.test(input.playwrightCliCommand))) {
2487
2453
  return blocked2("projection-playwright-command", [handoffPath(scope, "playwright")], "Playwright requiere un ejecutable absoluto verificado. Abre una terminal nueva tras pnpm setup y reintenta install --playwright.");
2488
2454
  }
2489
2455
  const plan = projectionPlan(input, scope);
@@ -2496,12 +2462,12 @@ function runPiProjectionLifecycle(input, deps) {
2496
2462
  const expectedReceipt2 = receiptContent(receipt);
2497
2463
  const drifted = plan.filter((action) => hasActionDrift(action, deps));
2498
2464
  for (const { kind, file } of checked.handoffs) {
2499
- if (handoffEnabled(input, kind) && checked.previous?.[kind]?.sha256 !== receipt[kind]?.sha256 && !drifted.some((action) => path15.resolve(action.target) === file)) {
2465
+ if (handoffEnabled(input, kind) && checked.previous?.[kind]?.sha256 !== receipt[kind]?.sha256 && !drifted.some((action) => path14.resolve(action.target) === file)) {
2500
2466
  return input.operation === "doctor" ? { kind: "drift", paths: [file] } : handoffConflict(kind, file);
2501
2467
  }
2502
2468
  }
2503
2469
  if (input.operation === "doctor") {
2504
- const paths = drifted.map((action) => path15.resolve(action.target));
2470
+ const paths = drifted.map((action) => path14.resolve(action.target));
2505
2471
  paths.push(...removedHandoffs.map(({ file }) => file), ...retiredSkills);
2506
2472
  const currentSettings2 = deps.readText(scope.settingsFile);
2507
2473
  if (currentSettings2 === null || filterProjectedPiPackage(currentSettings2, input.packageSource) !== currentSettings2) {
@@ -2560,12 +2526,12 @@ function runPiProjectionLifecycle(input, deps) {
2560
2526
  return { kind: "synced", changed: drifted.length > 0 || packageWillChange || receiptChanged || removedHandoffs.length > 0 };
2561
2527
  }
2562
2528
  function systemProjectionLifecycle(input) {
2563
- const targetRoot = input.targetDir === void 0 ? null : path15.resolve(input.targetDir);
2529
+ const targetRoot = input.targetDir === void 0 ? null : path14.resolve(input.targetDir);
2564
2530
  const scope = targetRoot === null ? realPiProjectionScope() : {
2565
2531
  kind: "target-dir",
2566
- home: path15.join(targetRoot, "home"),
2567
- codingAgentDir: path15.dirname(piSystemPromptFile(targetRoot)),
2568
- receiptFile: path15.join(targetRoot, "state", "pi-projection-receipt.json")
2532
+ home: path14.join(targetRoot, "home"),
2533
+ codingAgentDir: path14.dirname(piSystemPromptFile(targetRoot)),
2534
+ receiptFile: path14.join(targetRoot, "state", "pi-projection-receipt.json")
2569
2535
  };
2570
2536
  return {
2571
2537
  input: {
@@ -2586,14 +2552,14 @@ function systemProjectionLifecycle(input) {
2586
2552
  backup: (paths) => createBackup(
2587
2553
  paths,
2588
2554
  `pi-projection-${input.operation}`,
2589
- targetRoot === null ? void 0 : path15.join(targetRoot, "backups")
2555
+ targetRoot === null ? void 0 : path14.join(targetRoot, "backups")
2590
2556
  ),
2591
2557
  writeText,
2592
2558
  copyFile,
2593
2559
  removeFile: (file) => {
2594
2560
  fs11.rmSync(file, { force: true });
2595
- if (retiredPlaywrightFiles(scope).includes(path15.resolve(file))) {
2596
- pruneEmptyDirs(file, path15.join(scope.home, ".agents", "skills"));
2561
+ if (retiredPlaywrightFiles(scope).includes(path14.resolve(file))) {
2562
+ pruneEmptyDirs(file, path14.join(scope.home, ".agents", "skills"));
2597
2563
  }
2598
2564
  },
2599
2565
  readManifest: targetRoot === null ? readManifest : () => ({ runtimes: {} })
@@ -2696,7 +2662,7 @@ async function runUninstall(opts) {
2696
2662
  if (!detection.installed) continue;
2697
2663
  const keepCtx = makeContext(keep, detection.configDir);
2698
2664
  if (!keepCtx) continue;
2699
- for (const action of buildContentPlan(keep, keepCtx)) retained.add(path16.resolve(action.target));
2665
+ for (const action of buildContentPlan(keep, keepCtx)) retained.add(path15.resolve(action.target));
2700
2666
  }
2701
2667
  }
2702
2668
  for (const id of opts.runtimes) {
@@ -2722,16 +2688,16 @@ async function runUninstall(opts) {
2722
2688
  exitCode = 1;
2723
2689
  continue;
2724
2690
  }
2725
- const mergedTargets = new Set(unmerge.map((a) => path16.resolve(a.target)));
2691
+ const mergedTargets = new Set(unmerge.map((a) => path15.resolve(a.target)));
2726
2692
  const usingRealConfig = opts.targetDir === void 0;
2727
2693
  const prevOwned = usingRealConfig ? readManifest().runtimes[id]?.owned ?? [] : [];
2728
- const pruneRoot = usingRealConfig ? HOME : path16.dirname(configDir);
2694
+ const pruneRoot = usingRealConfig ? HOME : path15.dirname(configDir);
2729
2695
  const planTargets = [
2730
- .../* @__PURE__ */ new Set([...buildContentPlan(adapter, ctx).map((a) => path16.resolve(a.target)), ...prevOwned.map((t) => path16.resolve(t))])
2696
+ .../* @__PURE__ */ new Set([...buildContentPlan(adapter, ctx).map((a) => path15.resolve(a.target)), ...prevOwned.map((t) => path15.resolve(t))])
2731
2697
  ].filter((t) => !mergedTargets.has(t) && fs12.existsSync(t));
2732
2698
  const deleteTargets = planTargets.filter((t) => {
2733
2699
  if (retained.has(t) || !isContainedIn(t, pruneRoot)) return false;
2734
- if (path16.basename(t) !== "engram.ts") return true;
2700
+ if (path15.basename(t) !== "engram.ts") return true;
2735
2701
  const state = inspectOpencodePluginFile(t);
2736
2702
  if (state === "official" || state === "unknown") return false;
2737
2703
  return !ctx.preserveEngram;
@@ -2814,7 +2780,7 @@ async function runUninstall(opts) {
2814
2780
  }
2815
2781
 
2816
2782
  // src/doctor.ts
2817
- import path17 from "path";
2783
+ import path16 from "path";
2818
2784
  import fs13 from "fs";
2819
2785
  import * as p3 from "@clack/prompts";
2820
2786
  function readDoctorTextIfExists(file) {
@@ -2828,12 +2794,12 @@ function readDoctorTextIfExists(file) {
2828
2794
  var STALE_PERMISSIONS_MARKER = "differs from the stack default and was left untouched";
2829
2795
  var UPGRADE_PERMISSIONS_REMEDY = "jorgex-stack sync --upgrade-permissions --dry-run";
2830
2796
  function piAgentDir(targetDir) {
2831
- return targetDir === void 0 ? process.env.PI_CODING_AGENT_DIR ?? path17.join(HOME, ".pi", "agent") : path17.join(targetDir, "pi-agent");
2797
+ return targetDir === void 0 ? process.env.PI_CODING_AGENT_DIR ?? path16.join(HOME, ".pi", "agent") : path16.join(targetDir, "pi-agent");
2832
2798
  }
2833
2799
  function reportPiPermissions(targetDir) {
2834
2800
  const agentDir = piAgentDir(targetDir);
2835
- const configFile = path17.join(agentDir, "extensions", "pi-permission-system", "config.json");
2836
- const receiptFile = path17.join(agentDir, "jorgex-pi", "permissions-lifecycle.v1.json");
2801
+ const configFile = path16.join(agentDir, "extensions", "pi-permission-system", "config.json");
2802
+ const receiptFile = path16.join(agentDir, "jorgex-pi", "permissions-lifecycle.v1.json");
2837
2803
  let config;
2838
2804
  let receipt;
2839
2805
  try {
@@ -2870,7 +2836,7 @@ function resolvePlaywrightDoctorState(input) {
2870
2836
  return { status: "healthy" };
2871
2837
  }
2872
2838
  function context7KeyConfigured(id, configDir) {
2873
- const file = id === "codex" ? path17.join(configDir, "config.toml") : id === "claude-code" ? path17.join(path17.dirname(configDir), `${path17.basename(configDir)}.json`) : path17.join(configDir, "opencode.json");
2839
+ const file = id === "codex" ? path16.join(configDir, "config.toml") : id === "claude-code" ? path16.join(path16.dirname(configDir), `${path16.basename(configDir)}.json`) : path16.join(configDir, "opencode.json");
2874
2840
  const content = readTextIfExists(file);
2875
2841
  if (content === null) return null;
2876
2842
  const match = /CONTEXT7_API_KEY"?\s*[:=]\s*"([^"]*)"/.exec(content);
@@ -2890,7 +2856,7 @@ function reportWritingStyle(options, style, mode) {
2890
2856
  for (const id of runtimes) {
2891
2857
  const adapter = id === "pi" ? piAdapter : ADAPTERS[id];
2892
2858
  if (adapter === void 0) continue;
2893
- const configDir = id === "pi" ? options.targetDir === void 0 ? process.env.PI_CODING_AGENT_DIR ?? path17.join(HOME, ".pi", "agent") : path17.join(options.targetDir, "pi-agent") : options.targetDir ?? ADAPTERS[id].detect().configDir;
2859
+ const configDir = id === "pi" ? options.targetDir === void 0 ? process.env.PI_CODING_AGENT_DIR ?? path16.join(HOME, ".pi", "agent") : path16.join(options.targetDir, "pi-agent") : options.targetDir ?? ADAPTERS[id].detect().configDir;
2894
2860
  const file = adapter.paths(configDir).systemPromptFile;
2895
2861
  try {
2896
2862
  const content = readDoctorTextIfExists(file) ?? "";
@@ -2905,7 +2871,7 @@ function reportWritingStyle(options, style, mode) {
2905
2871
  problems++;
2906
2872
  }
2907
2873
  if (id === "codex") {
2908
- const override = path17.join(configDir, "AGENTS.override.md");
2874
+ const override = path16.join(configDir, "AGENTS.override.md");
2909
2875
  if ((readDoctorTextIfExists(override) ?? "").trim() !== "") {
2910
2876
  p3.log.warn(`Codex: ${override} no vac\xEDo puede ocultar el AGENTS.md gestionado; rev\xEDsalo sin modificarlo autom\xE1ticamente.`);
2911
2877
  problems++;
@@ -2920,7 +2886,7 @@ function reportWritingStyle(options, style, mode) {
2920
2886
  }
2921
2887
  async function verifyOfficialForRuntime(runtime, configDir, engramBin, homeDir, isExplicitClaudeConfigDir) {
2922
2888
  if (runtime === "claude-code") {
2923
- const { verifyOfficialSetup: verifyOfficialSetup2 } = await import("./claude-code-WY4JJAXP.js");
2889
+ const { verifyOfficialSetup: verifyOfficialSetup2 } = await import("./claude-code-BBHLN5TD.js");
2924
2890
  const explicit = isExplicitClaudeConfigDir ?? process.env.CLAUDE_CONFIG_DIR !== void 0;
2925
2891
  const report2 = await verifyOfficialSetup2({ configDir, engramBin, homeDir, isExplicitClaudeConfigDir: explicit });
2926
2892
  return {
@@ -2930,7 +2896,7 @@ async function verifyOfficialForRuntime(runtime, configDir, engramBin, homeDir,
2930
2896
  };
2931
2897
  }
2932
2898
  if (runtime === "codex") {
2933
- const { verifyOfficialSetup: verifyOfficialSetup2 } = await import("./codex-WSWHXYJ3.js");
2899
+ const { verifyOfficialSetup: verifyOfficialSetup2 } = await import("./codex-ALGP2YBC.js");
2934
2900
  const report2 = await verifyOfficialSetup2({ configDir, engramBin });
2935
2901
  return {
2936
2902
  ok: report2.ok,
@@ -2938,7 +2904,16 @@ async function verifyOfficialForRuntime(runtime, configDir, engramBin, homeDir,
2938
2904
  ...report2.reason === void 0 ? {} : { reason: report2.reason }
2939
2905
  };
2940
2906
  }
2941
- const { verifyOfficialSetup } = await import("./opencode-NUDGK6J7.js");
2907
+ if (runtime === "pi") {
2908
+ const { verifyOfficialSetup: verifyOfficialSetup2 } = await import("./pi-CKFSPGJU.js");
2909
+ const report2 = await verifyOfficialSetup2({ configDir, engramBin, homeDir });
2910
+ return {
2911
+ ok: report2.ok,
2912
+ layers: report2.layers,
2913
+ ...report2.reason === void 0 ? {} : { reason: report2.reason }
2914
+ };
2915
+ }
2916
+ const { verifyOfficialSetup } = await import("./opencode-VG2IZRRF.js");
2942
2917
  const report = await verifyOfficialSetup({ configDir, engramBin });
2943
2918
  return {
2944
2919
  ok: report.ok,
@@ -2981,8 +2956,8 @@ async function runDoctor(options = {}) {
2981
2956
  engramBinAvailable = true;
2982
2957
  }
2983
2958
  }
2984
- const engramDataDir = process.env.ENGRAM_DATA_DIR ?? path17.join(HOME, ".engram");
2985
- const engramDb = path17.join(engramDataDir, "engram.db");
2959
+ const engramDataDir = process.env.ENGRAM_DATA_DIR ?? path16.join(HOME, ".engram");
2960
+ const engramDb = path16.join(engramDataDir, "engram.db");
2986
2961
  if (fs13.existsSync(engramDb)) {
2987
2962
  const sizeMb = (fs13.statSync(engramDb).size / 1024 / 1024).toFixed(1);
2988
2963
  p3.log.info(`Engram DB: ${engramDb} (${sizeMb} MB de memorias \u2014 el stack no la toca JAM\xC1S).`);
@@ -3087,7 +3062,7 @@ async function runDoctor(options = {}) {
3087
3062
  p3.log.warn(`${adapter.name}: ${orphans.length} archivos hu\xE9rfanos de versiones previas \u2192 ejecuta 'sync'.`);
3088
3063
  problems++;
3089
3064
  }
3090
- if (adapter.id === "codex" && fs13.existsSync(path17.join(detection.configDir, "hooks.json"))) {
3065
+ if (adapter.id === "codex" && fs13.existsSync(path16.join(detection.configDir, "hooks.json"))) {
3091
3066
  p3.log.info("Codex: recuerda que los hooks requieren aprobaci\xF3n manual \u2014 verifica con /hooks dentro de codex.");
3092
3067
  }
3093
3068
  const key = context7KeyConfigured(adapter.id, detection.configDir);
@@ -3136,14 +3111,14 @@ async function runDoctor(options = {}) {
3136
3111
 
3137
3112
  // src/update.ts
3138
3113
  import fs16 from "fs";
3139
- import path20 from "path";
3114
+ import path19 from "path";
3140
3115
  import os3 from "os";
3141
3116
  import { execFileSync as execFileSync4 } from "child_process";
3142
3117
  import * as p4 from "@clack/prompts";
3143
3118
 
3144
3119
  // src/lib/github.ts
3145
3120
  import fs14 from "fs";
3146
- import path18 from "path";
3121
+ import path17 from "path";
3147
3122
  import { execFileSync as execFileSync2 } from "child_process";
3148
3123
  import os2 from "os";
3149
3124
  import { Readable } from "stream";
@@ -3180,16 +3155,28 @@ function githubHeaders() {
3180
3155
  ...authHeaders()
3181
3156
  };
3182
3157
  }
3158
+ var ENGRAM_REPO = "Gentleman-Programming/engram";
3159
+ var ENGRAM_RELEASES_LATEST_URL = `https://api.github.com/repos/${ENGRAM_REPO}/releases/latest`;
3160
+ var ENGRAM_DOWNLOAD_PREFIX = `https://github.com/${ENGRAM_REPO}/releases/download/`;
3161
+ function expectedEngramAssetName(version, platform, arch) {
3162
+ const osPart = platform === "win32" ? "windows" : platform === "linux" ? "linux" : platform === "darwin" ? "darwin" : null;
3163
+ const archPart = arch === "x64" ? "amd64" : arch === "arm64" ? "arm64" : null;
3164
+ if (osPart === null || archPart === null) return null;
3165
+ const ext = osPart === "windows" ? "zip" : "tar.gz";
3166
+ return `engram_${version}_${osPart}_${archPart}.${ext}`;
3167
+ }
3168
+ async function fetchLatestGithubRelease(repo, fetchFn = globalThis.fetch) {
3169
+ const res = await fetchFn(`https://api.github.com/repos/${repo}/releases/latest`, {
3170
+ headers: githubHeaders(),
3171
+ signal: AbortSignal.timeout(1e4)
3172
+ });
3173
+ if (!res.ok && (res.status === 403 || res.status === 429)) rateLimitHit = true;
3174
+ return res;
3175
+ }
3183
3176
  async function latestGithubRelease(repo) {
3184
3177
  try {
3185
- const res = await fetch(`https://api.github.com/repos/${repo}/releases/latest`, {
3186
- headers: githubHeaders(),
3187
- signal: AbortSignal.timeout(1e4)
3188
- });
3189
- if (!res.ok) {
3190
- if (res.status === 403 || res.status === 429) rateLimitHit = true;
3191
- return null;
3192
- }
3178
+ const res = await fetchLatestGithubRelease(repo);
3179
+ if (!res.ok) return null;
3193
3180
  const data = await res.json();
3194
3181
  return data.tag_name?.replace(/^v/, "") ?? null;
3195
3182
  } catch {
@@ -3213,7 +3200,7 @@ async function latestGithubCommit(repo) {
3213
3200
  }
3214
3201
  }
3215
3202
  function validateExtractedTree(destDir) {
3216
- const resolved = path18.resolve(destDir);
3203
+ const resolved = path17.resolve(destDir);
3217
3204
  const walk = (dir) => {
3218
3205
  let entries;
3219
3206
  try {
@@ -3222,7 +3209,7 @@ function validateExtractedTree(destDir) {
3222
3209
  return false;
3223
3210
  }
3224
3211
  for (const entry of entries) {
3225
- const full = path18.join(dir, entry.name);
3212
+ const full = path17.join(dir, entry.name);
3226
3213
  let stat;
3227
3214
  try {
3228
3215
  stat = fs14.lstatSync(full);
@@ -3241,12 +3228,12 @@ function validateExtractedTree(destDir) {
3241
3228
  }
3242
3229
  function resolveTarBin() {
3243
3230
  if (process.platform !== "win32") return "tar";
3244
- const winTar = path18.join(process.env["SystemRoot"] ?? "C:\\Windows", "System32", "tar.exe");
3231
+ const winTar = path17.join(process.env["SystemRoot"] ?? "C:\\Windows", "System32", "tar.exe");
3245
3232
  return fs14.existsSync(winTar) ? winTar : "tar";
3246
3233
  }
3247
3234
  async function downloadRepoTarball(repo, sha, destDir, validateSubdir) {
3248
3235
  const url = `https://codeload.github.com/${repo}/tar.gz/${sha}`;
3249
- const tmp = path18.join(os2.tmpdir(), `jorgex-tarball-${Date.now()}.tar.gz`);
3236
+ const tmp = path17.join(os2.tmpdir(), `jorgex-tarball-${Date.now()}.tar.gz`);
3250
3237
  const fail = (reason) => {
3251
3238
  try {
3252
3239
  fs14.rmSync(destDir, { recursive: true, force: true });
@@ -3279,8 +3266,8 @@ async function downloadRepoTarball(repo, sha, destDir, validateSubdir) {
3279
3266
  const detail = (e.stderr?.toString().trim() || e.message || "").split("\n")[0];
3280
3267
  return fail(detail ? `tar fall\xF3: ${detail}` : "tar no disponible o fall\xF3 la extracci\xF3n");
3281
3268
  }
3282
- const resolvedDest = path18.resolve(destDir);
3283
- const validateRoot = validateSubdir ? path18.resolve(resolvedDest, validateSubdir) : resolvedDest;
3269
+ const resolvedDest = path17.resolve(destDir);
3270
+ const validateRoot = validateSubdir ? path17.resolve(resolvedDest, validateSubdir) : resolvedDest;
3284
3271
  if (validateRoot !== resolvedDest && !isContainedIn(validateRoot, resolvedDest)) {
3285
3272
  return fail(`la ruta de validaci\xF3n "${validateSubdir}" escapa del destino`);
3286
3273
  }
@@ -3302,7 +3289,7 @@ async function downloadRepoTarball(repo, sha, destDir, validateSubdir) {
3302
3289
 
3303
3290
  // src/lib/skill-update.ts
3304
3291
  import fs15 from "fs";
3305
- import path19 from "path";
3292
+ import path18 from "path";
3306
3293
  import { execFileSync as execFileSync3 } from "child_process";
3307
3294
  var PROTECTED_SKILLS = /* @__PURE__ */ new Set([
3308
3295
  "agent-delegation",
@@ -3322,10 +3309,10 @@ function sameTextContentNormalized(a, b) {
3322
3309
  }
3323
3310
  function diffSkillDirs(upstreamDir, localDir) {
3324
3311
  const upstreamFiles = new Set(
3325
- listFilesRecursive(upstreamDir).map((f) => path19.relative(upstreamDir, f))
3312
+ listFilesRecursive(upstreamDir).map((f) => path18.relative(upstreamDir, f))
3326
3313
  );
3327
3314
  const localFiles = new Set(
3328
- listFilesRecursive(localDir).map((f) => path19.relative(localDir, f))
3315
+ listFilesRecursive(localDir).map((f) => path18.relative(localDir, f))
3329
3316
  );
3330
3317
  const added = [];
3331
3318
  const modified = [];
@@ -3333,7 +3320,7 @@ function diffSkillDirs(upstreamDir, localDir) {
3333
3320
  for (const rel of upstreamFiles) {
3334
3321
  if (!localFiles.has(rel)) {
3335
3322
  added.push(rel);
3336
- } else if (!sameTextContentNormalized(path19.join(upstreamDir, rel), path19.join(localDir, rel))) {
3323
+ } else if (!sameTextContentNormalized(path18.join(upstreamDir, rel), path18.join(localDir, rel))) {
3337
3324
  modified.push(rel);
3338
3325
  }
3339
3326
  }
@@ -3395,7 +3382,7 @@ function replaceSkill(name, upstreamSkillDir, newCommit, opts) {
3395
3382
  if (PROTECTED_SKILLS.has(name)) {
3396
3383
  throw new Error(`La skill "${name}" es propia del stack y no se actualiza desde upstream.`);
3397
3384
  }
3398
- const upstreamsFile = upstreamsFilePath ?? path19.join(path19.dirname(stackRoot()), "upstreams.json");
3385
+ const upstreamsFile = upstreamsFilePath ?? path18.join(path18.dirname(stackRoot()), "upstreams.json");
3399
3386
  const raw = fs15.readFileSync(upstreamsFile, "utf8");
3400
3387
  const data = JSON.parse(raw);
3401
3388
  const skillEntry = data?.skills?.[name];
@@ -3405,8 +3392,8 @@ function replaceSkill(name, upstreamSkillDir, newCommit, opts) {
3405
3392
  if (skillEntry.kind === "release") {
3406
3393
  throw new Error(`La skill "${name}" es de tipo release y no se actualiza con replaceSkill.`);
3407
3394
  }
3408
- const skillsRoot = localSkillsRoot ?? path19.join(stackRoot(), "skills");
3409
- const localSkillDir = path19.join(skillsRoot, name);
3395
+ const skillsRoot = localSkillsRoot ?? path18.join(stackRoot(), "skills");
3396
+ const localSkillDir = path18.join(skillsRoot, name);
3410
3397
  const localFiles = listFilesRecursive(localSkillDir);
3411
3398
  if (localFiles.length > 0) {
3412
3399
  createBackup(localFiles, `skill-update-${name}`, backupsRoot);
@@ -3419,9 +3406,9 @@ function replaceSkill(name, upstreamSkillDir, newCommit, opts) {
3419
3406
  if (st.isSymbolicLink()) {
3420
3407
  throw new Error(`Symlink rechazado en upstream de skill "${name}": ${src}`);
3421
3408
  }
3422
- const rel = path19.relative(upstreamSkillDir, src);
3423
- const dest = path19.join(stagingDir, rel);
3424
- ensureDir(path19.dirname(dest));
3409
+ const rel = path18.relative(upstreamSkillDir, src);
3410
+ const dest = path18.join(stagingDir, rel);
3411
+ ensureDir(path18.dirname(dest));
3425
3412
  copyFile(src, dest);
3426
3413
  }
3427
3414
  const oldDir = `${localSkillDir}.old-${process.pid}`;
@@ -3446,7 +3433,7 @@ function rateLimitHint(prefix) {
3446
3433
  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.`;
3447
3434
  }
3448
3435
  function loadUpstreams() {
3449
- const file = path20.join(path20.dirname(stackRoot()), "upstreams.json");
3436
+ const file = path19.join(path19.dirname(stackRoot()), "upstreams.json");
3450
3437
  return JSON.parse(fs16.readFileSync(file, "utf8"));
3451
3438
  }
3452
3439
  function skillsToScan(maintainer, upstreams) {
@@ -3678,8 +3665,8 @@ function isEngramRunning() {
3678
3665
  return null;
3679
3666
  }
3680
3667
  }
3681
- function isGitClone(projectRoot = path20.dirname(stackRoot())) {
3682
- return fs16.existsSync(path20.join(projectRoot, ".git"));
3668
+ function isGitClone(projectRoot = path19.dirname(stackRoot())) {
3669
+ return fs16.existsSync(path19.join(projectRoot, ".git"));
3683
3670
  }
3684
3671
  var STACK_METHOD_CLONE = "git pull + pnpm install + pnpm build";
3685
3672
  function resolvePnpm() {
@@ -3694,7 +3681,7 @@ function cleanupTmp(dir) {
3694
3681
  }
3695
3682
  }
3696
3683
  function updateStackGitClone() {
3697
- const projectRoot = path20.dirname(stackRoot());
3684
+ const projectRoot = path19.dirname(stackRoot());
3698
3685
  const git = lookPath("git");
3699
3686
  if (!git) throw new Error("git no encontrado en PATH.");
3700
3687
  const pnpm = resolvePnpm();
@@ -3711,7 +3698,7 @@ function updateStackGlobal() {
3711
3698
  execFileSync4(pnpm, ["add", "-g", "jorgex-stack@latest"], { stdio: "inherit" });
3712
3699
  }
3713
3700
  async function downloadSkillToTemp(repo, sha, skillPath) {
3714
- const root = fs16.mkdtempSync(path20.join(os3.tmpdir(), "jorgex-skill-"));
3701
+ const root = fs16.mkdtempSync(path19.join(os3.tmpdir(), "jorgex-skill-"));
3715
3702
  try {
3716
3703
  const result = await downloadRepoTarball(repo, sha, root, skillPath);
3717
3704
  if (!result.ok) {
@@ -3719,14 +3706,14 @@ async function downloadSkillToTemp(repo, sha, skillPath) {
3719
3706
  return { error: result.reason };
3720
3707
  }
3721
3708
  if (skillPath) {
3722
- const sub = path20.resolve(path20.join(root, skillPath));
3709
+ const sub = path19.resolve(path19.join(root, skillPath));
3723
3710
  if (!isContainedIn(sub, root)) {
3724
3711
  cleanupTmp(root);
3725
3712
  return { error: `la ruta "${skillPath}" escapa del directorio temporal` };
3726
3713
  }
3727
3714
  if (fs16.existsSync(sub)) return { dir: sub, root };
3728
3715
  const lastSeg = skillPath.split("/").pop();
3729
- const sub2 = path20.resolve(path20.join(root, lastSeg));
3716
+ const sub2 = path19.resolve(path19.join(root, lastSeg));
3730
3717
  if (isContainedIn(sub2, root) && fs16.existsSync(sub2)) {
3731
3718
  if (!validateExtractedTree(sub2)) {
3732
3719
  cleanupTmp(root);
@@ -3747,10 +3734,10 @@ function pruneEngramDbBackups() {
3747
3734
  try {
3748
3735
  const dir = dataDir();
3749
3736
  if (!fs16.existsSync(dir)) return;
3750
- const backups = fs16.readdirSync(dir).filter((f) => f.startsWith("engram-db-backup-") && f.endsWith(".db")).map((f) => ({ name: f, mtime: fs16.statSync(path20.join(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
3737
+ const backups = fs16.readdirSync(dir).filter((f) => f.startsWith("engram-db-backup-") && f.endsWith(".db")).map((f) => ({ name: f, mtime: fs16.statSync(path19.join(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
3751
3738
  for (const old of backups.slice(3)) {
3752
3739
  try {
3753
- fs16.rmSync(path20.join(dir, old.name));
3740
+ fs16.rmSync(path19.join(dir, old.name));
3754
3741
  } catch {
3755
3742
  }
3756
3743
  }
@@ -3759,17 +3746,17 @@ function pruneEngramDbBackups() {
3759
3746
  }
3760
3747
  function rotateLockedBinary(binPath, sweepRoot = HOME) {
3761
3748
  if (!fs16.existsSync(binPath)) return null;
3762
- const dir = path20.dirname(binPath);
3763
- const base = path20.basename(binPath);
3749
+ const dir = path19.dirname(binPath);
3750
+ const base = path19.basename(binPath);
3764
3751
  const escapedBase = base.replace(/[.*+?^$()|[\]{}\\]/g, "\\$&");
3765
3752
  const oldPattern = new RegExp("^" + escapedBase + "\\.old-\\d+$");
3766
- const resolvedDir = path20.resolve(dir);
3767
- if (resolvedDir === path20.resolve(sweepRoot) || isContainedIn(resolvedDir, sweepRoot)) {
3753
+ const resolvedDir = path19.resolve(dir);
3754
+ if (resolvedDir === path19.resolve(sweepRoot) || isContainedIn(resolvedDir, sweepRoot)) {
3768
3755
  try {
3769
3756
  for (const entry of fs16.readdirSync(dir)) {
3770
3757
  if (oldPattern.test(entry)) {
3771
3758
  try {
3772
- fs16.rmSync(path20.join(dir, entry), { force: true });
3759
+ fs16.rmSync(path19.join(dir, entry), { force: true });
3773
3760
  } catch {
3774
3761
  }
3775
3762
  }
@@ -3777,7 +3764,7 @@ function rotateLockedBinary(binPath, sweepRoot = HOME) {
3777
3764
  } catch {
3778
3765
  }
3779
3766
  }
3780
- const rotated = path20.join(dir, `${base}.old-${Date.now()}`);
3767
+ const rotated = path19.join(dir, `${base}.old-${Date.now()}`);
3781
3768
  fs16.renameSync(binPath, rotated);
3782
3769
  return rotated;
3783
3770
  }
@@ -3787,8 +3774,8 @@ async function updateEngram(engramRepo, latestVersion) {
3787
3774
  "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)."
3788
3775
  );
3789
3776
  }
3790
- const engramDataDir = process.env.ENGRAM_DATA_DIR ?? path20.join(HOME, ".engram");
3791
- const engramDb = path20.join(engramDataDir, "engram.db");
3777
+ const engramDataDir = process.env.ENGRAM_DATA_DIR ?? path19.join(HOME, ".engram");
3778
+ const engramDb = path19.join(engramDataDir, "engram.db");
3792
3779
  if (fs16.existsSync(engramDb)) {
3793
3780
  const doBackup = await p4.confirm({
3794
3781
  message: `\xBFHacer backup de la DB de Engram antes de actualizar? (${engramDb})`,
@@ -3796,7 +3783,7 @@ async function updateEngram(engramRepo, latestVersion) {
3796
3783
  });
3797
3784
  if (!p4.isCancel(doBackup) && doBackup) {
3798
3785
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3799
- const dest = path20.join(dataDir(), `engram-db-backup-${ts}.db`);
3786
+ const dest = path19.join(dataDir(), `engram-db-backup-${ts}.db`);
3800
3787
  try {
3801
3788
  if (!fs16.existsSync(dataDir())) fs16.mkdirSync(dataDir(), { recursive: true });
3802
3789
  fs16.copyFileSync(engramDb, dest);
@@ -4134,7 +4121,7 @@ async function runInteractiveUpdate(localVersion, yes, dryRun = false, includeBr
4134
4121
  continue;
4135
4122
  }
4136
4123
  const { dir: tmpDir, root: tmpRoot } = tmpResult;
4137
- const localSkillDir = path20.join(stackRoot(), "skills", skillInfo.name);
4124
+ const localSkillDir = path19.join(stackRoot(), "skills", skillInfo.name);
4138
4125
  const diff = renderSkillDiff(tmpDir, localSkillDir);
4139
4126
  if (diff) {
4140
4127
  p4.log.info(`Diff de ${skillInfo.name}:
@@ -4226,7 +4213,7 @@ ${diff}`);
4226
4213
  }
4227
4214
 
4228
4215
  // src/models-picker.ts
4229
- import path21 from "path";
4216
+ import path20 from "path";
4230
4217
  import * as p5 from "@clack/prompts";
4231
4218
  var TIERS = ["strong", "standard", "cheap"];
4232
4219
  var EFFORTS = ["low", "medium", "high", "xhigh"];
@@ -4252,7 +4239,7 @@ function opencodeLiveModels(binPath) {
4252
4239
  }
4253
4240
  function agentsByTier() {
4254
4241
  const grouped = { strong: [], standard: [], cheap: [] };
4255
- for (const agent of loadCanonicalAgents(path21.join(stackRoot(), "agents"))) {
4242
+ for (const agent of loadCanonicalAgents(path20.join(stackRoot(), "agents"))) {
4256
4243
  if (agent.mode === "subagent") grouped[agent.tier].push(agent.name);
4257
4244
  }
4258
4245
  return grouped;
@@ -4438,15 +4425,15 @@ function cancelled() {
4438
4425
 
4439
4426
  // src/lib/release.ts
4440
4427
  import fs17 from "fs";
4441
- import path22 from "path";
4428
+ import path21 from "path";
4442
4429
  import { execFileSync as execFileSync5 } from "child_process";
4443
4430
  import { fileURLToPath } from "url";
4444
4431
  function findPackageJson() {
4445
- let dir = path22.dirname(fileURLToPath(import.meta.url));
4432
+ let dir = path21.dirname(fileURLToPath(import.meta.url));
4446
4433
  for (let i = 0; i < 6; i++) {
4447
- const candidate = path22.join(dir, "package.json");
4434
+ const candidate = path21.join(dir, "package.json");
4448
4435
  if (fs17.existsSync(candidate)) return candidate;
4449
- dir = path22.dirname(dir);
4436
+ dir = path21.dirname(dir);
4450
4437
  }
4451
4438
  throw new Error("No se encontr\xF3 package.json cerca del CLI.");
4452
4439
  }
@@ -4467,7 +4454,7 @@ function readPackageMetadata() {
4467
4454
 
4468
4455
  // src/lib/pi-runtime.ts
4469
4456
  import os4 from "os";
4470
- import path23 from "path";
4457
+ import path22 from "path";
4471
4458
  import fs18 from "fs";
4472
4459
  import { spawnSync } from "child_process";
4473
4460
  import { createHash as createHash2 } from "crypto";
@@ -4476,16 +4463,16 @@ import { createHash as createHash2 } from "crypto";
4476
4463
  var pi_runtime_pin_default = {
4477
4464
  package: {
4478
4465
  name: "jorgex-pi",
4479
- version: "0.8.27",
4480
- source: "npm:jorgex-pi@0.8.27"
4466
+ version: "0.8.28",
4467
+ source: "npm:jorgex-pi@0.8.28"
4481
4468
  },
4482
4469
  provenance: {
4483
- commit: "97fb35c88c484f264cbd9dc1f0a3c65da2f9dba1"
4470
+ commit: "056fbc7eeea07466c3ce3a6f85723e94bcf38577"
4484
4471
  },
4485
4472
  tarball: {
4486
- bytes: 89140773,
4487
- sha256: "2ac1ed88c419de87d7a519aa269cac7015e477f6c5250fe7d7090a4c40e3fa17",
4488
- sha512: "6adbdb8dcf35bd7b6eba63ac6b1c7fc2ccf2063f5768a16bdb462104143a09039be629c8d67ab0e058533d882a27fbbc3d753c769297ed9ac1c04775a2f0b614"
4473
+ bytes: 17993987,
4474
+ sha256: "3490845ad6d67c82cb0828847d0c9510a58958f068c4e8408161aadfe6f28ed9",
4475
+ sha512: "7ec1c46ccbafe8052effca13ef5e2bdff2ad09a2624bbe4ad7884622e231bbe25119b7124b6ba8f96916be34d99e511bfd83a9c16708d6f828ccf9f9d705bd7c"
4489
4476
  }
4490
4477
  };
4491
4478
 
@@ -4506,7 +4493,7 @@ var PI_RUNTIME_CANDIDATE = {
4506
4493
  "structured-questions-v1",
4507
4494
  "web-access-v1",
4508
4495
  "goal-continuation-v1",
4509
- "mcp-adapter-v1",
4496
+ "engram-official-bridge-v1",
4510
4497
  "engram-runtime-tools-v1",
4511
4498
  "context7-http-v1",
4512
4499
  "permissions-policy-v1",
@@ -4603,16 +4590,16 @@ async function resolvePiEngramRequirement(input, deps) {
4603
4590
  };
4604
4591
  }
4605
4592
  const accepted = await deps.confirm({
4606
- message: "Engram es obligatorio para JorgeX Pi. \xBFInstalar ahora el binario mediante el canal nativo?",
4593
+ message: "Engram es obligatorio para JorgeX Pi. \xBFInstalar ahora el binario oficial verificado?",
4607
4594
  initialValue: false
4608
4595
  });
4609
4596
  if (!accepted) return { kind: "offer", accepted: false };
4610
- const installed = await deps.installNative({ version: "2.0.0", channels: ["brew", "go", "url"] });
4611
- if (!installed) {
4597
+ const installed = await deps.installShared();
4598
+ if (!installed.ok) {
4612
4599
  return {
4613
4600
  kind: "blocked",
4614
4601
  reason: "engram-install-failed",
4615
- remedy: "Instala Engram manualmente o configura ENGRAM_BIN antes de reintentar."
4602
+ remedy: `La instalaci\xF3n de Engram fall\xF3: ${installed.reason} Instala Engram manualmente o configura ENGRAM_BIN antes de reintentar.`
4616
4603
  };
4617
4604
  }
4618
4605
  const detected = deps.detectHost();
@@ -4665,7 +4652,7 @@ function healthyDoctor(stdout, stderr, packageRunner, candidate) {
4665
4652
  if (record === null || typeof record !== "object" || Array.isArray(record)) return false;
4666
4653
  const packageValue = Reflect.get(record, "package");
4667
4654
  const result = Reflect.get(record, "result");
4668
- 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]) && path23.resolve(packageRunner) === path23.resolve(String(Reflect.get(packageValue, "root")), "bin", "jorgex-pi.mjs") && result !== null && typeof result === "object" && Reflect.get(result, "healthy") === true;
4655
+ 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]) && path22.resolve(packageRunner) === path22.resolve(String(Reflect.get(packageValue, "root")), "bin", "jorgex-pi.mjs") && result !== null && typeof result === "object" && Reflect.get(result, "healthy") === true;
4669
4656
  } catch {
4670
4657
  return false;
4671
4658
  }
@@ -4686,7 +4673,7 @@ function provisionalPendingDoctor(exitCode, stdout, stderr, packageRunner, candi
4686
4673
  if (JSON.stringify(Object.keys(packageValue).sort()) !== JSON.stringify(["name", "root", "version"])) return false;
4687
4674
  if (Reflect.get(packageValue, "name") !== "jorgex-pi") return false;
4688
4675
  if (Reflect.get(packageValue, "version") !== (candidate.package?.version ?? /^npm:jorgex-pi@([^\s]+)$/.exec(candidate.source)?.[1])) return false;
4689
- if (path23.resolve(packageRunner) !== path23.resolve(String(Reflect.get(packageValue, "root")), "bin", "jorgex-pi.mjs")) return false;
4676
+ if (path22.resolve(packageRunner) !== path22.resolve(String(Reflect.get(packageValue, "root")), "bin", "jorgex-pi.mjs")) return false;
4690
4677
  const result = Reflect.get(record, "result");
4691
4678
  if (result === null || typeof result !== "object" || Array.isArray(result)) return false;
4692
4679
  if (JSON.stringify(Object.keys(result).sort()) !== JSON.stringify(["checks", "healthy"])) return false;
@@ -4715,7 +4702,7 @@ function provisionalPendingDoctor(exitCode, stdout, stderr, packageRunner, candi
4715
4702
  }
4716
4703
  function installPiFromVerifiedTarball(input, deps) {
4717
4704
  const paths = input.targetDir === void 0 ? userPaths(input.engramBin, input.piExecutable) : targetPaths(input.targetDir, input.engramBin, input.piExecutable);
4718
- const destination = input.targetDir === void 0 ? path23.join(dataDir(), "packages", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`) : path23.join(path23.resolve(input.targetDir), "downloads", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`);
4705
+ const destination = input.targetDir === void 0 ? path22.join(dataDir(), "packages", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`) : path22.join(path22.resolve(input.targetDir), "downloads", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`);
4719
4706
  const artifact = deps.download(destination);
4720
4707
  if (artifact.bytes !== input.candidate.bytes || artifact.sha256 !== input.candidate.sha256 || artifact.sha512 !== input.candidate.sha512) {
4721
4708
  return { kind: "blocked", reason: "tarball-integrity" };
@@ -4723,7 +4710,7 @@ function installPiFromVerifiedTarball(input, deps) {
4723
4710
  deps.backupSettings();
4724
4711
  const scope = {
4725
4712
  kind: input.targetDir === void 0 ? "real" : "target-dir",
4726
- codingAgentDir: path23.resolve(paths.codingAgentDir)
4713
+ codingAgentDir: path22.resolve(paths.codingAgentDir)
4727
4714
  };
4728
4715
  const installing = flatCandidateReceipt(input.candidate, scope, "installing", input.engramBin);
4729
4716
  deps.writeReceiptAtomic(`${JSON.stringify(installing)}
@@ -4754,30 +4741,30 @@ function installPiFromVerifiedTarball(input, deps) {
4754
4741
  return { kind: "installed", receipt };
4755
4742
  }
4756
4743
  function runtimePath(piExecutable) {
4757
- const entries = process.platform === "win32" ? [piExecutable === void 0 ? null : path23.dirname(piExecutable), path23.dirname(process.execPath), process.env.SystemRoot ? path23.join(process.env.SystemRoot, "System32") : null] : [piExecutable === void 0 ? null : path23.dirname(piExecutable), path23.dirname(process.execPath), "/usr/local/bin", "/usr/bin", "/bin"];
4758
- return [...new Set(entries.filter((entry) => entry !== null))].join(path23.delimiter);
4744
+ const entries = process.platform === "win32" ? [piExecutable === void 0 ? null : path22.dirname(piExecutable), path22.dirname(process.execPath), process.env.SystemRoot ? path22.join(process.env.SystemRoot, "System32") : null] : [piExecutable === void 0 ? null : path22.dirname(piExecutable), path22.dirname(process.execPath), "/usr/local/bin", "/usr/bin", "/bin"];
4745
+ return [...new Set(entries.filter((entry) => entry !== null))].join(path22.delimiter);
4759
4746
  }
4760
4747
  function targetPaths(targetDir, engramBin, piExecutable) {
4761
- const root = path23.resolve(targetDir);
4762
- const codingAgentDir = path23.join(root, "pi-agent");
4763
- const home = path23.join(root, "home");
4764
- const temporary = path23.join(root, "tmp");
4748
+ const root = path22.resolve(targetDir);
4749
+ const codingAgentDir = path22.join(root, "pi-agent");
4750
+ const home = path22.join(root, "home");
4751
+ const temporary = path22.join(root, "tmp");
4765
4752
  return {
4766
4753
  codingAgentDir,
4767
- receiptPath: path23.join(root, "state", "pi-receipt.json"),
4768
- packageRunner: path23.join(codingAgentDir, "npm", "node_modules", "jorgex-pi", "bin", "jorgex-pi.mjs"),
4754
+ receiptPath: path22.join(root, "state", "pi-receipt.json"),
4755
+ packageRunner: path22.join(codingAgentDir, "npm", "node_modules", "jorgex-pi", "bin", "jorgex-pi.mjs"),
4769
4756
  environment: {
4770
4757
  HOME: home,
4771
4758
  USERPROFILE: home,
4772
- APPDATA: path23.join(root, "appdata"),
4773
- LOCALAPPDATA: path23.join(root, "localappdata"),
4774
- XDG_CONFIG_HOME: path23.join(root, "xdg-config"),
4775
- XDG_DATA_HOME: path23.join(root, "xdg-data"),
4776
- XDG_CACHE_HOME: path23.join(root, "xdg-cache"),
4759
+ APPDATA: path22.join(root, "appdata"),
4760
+ LOCALAPPDATA: path22.join(root, "localappdata"),
4761
+ XDG_CONFIG_HOME: path22.join(root, "xdg-config"),
4762
+ XDG_DATA_HOME: path22.join(root, "xdg-data"),
4763
+ XDG_CACHE_HOME: path22.join(root, "xdg-cache"),
4777
4764
  TEMP: temporary,
4778
4765
  TMP: temporary,
4779
4766
  TMPDIR: temporary,
4780
- npm_config_cache: path23.join(root, "npm-cache"),
4767
+ npm_config_cache: path22.join(root, "npm-cache"),
4781
4768
  NPM_CONFIG_IGNORE_SCRIPTS: "true",
4782
4769
  NPM_CONFIG_UPDATE_NOTIFIER: "false",
4783
4770
  PI_CODING_AGENT_DIR: codingAgentDir,
@@ -4788,15 +4775,15 @@ function targetPaths(targetDir, engramBin, piExecutable) {
4788
4775
  }
4789
4776
  function userPaths(engramBin, piExecutable) {
4790
4777
  const home = os4.homedir();
4791
- const codingAgentDir = process.env.PI_CODING_AGENT_DIR ?? path23.join(home, ".pi", "agent");
4778
+ const codingAgentDir = process.env.PI_CODING_AGENT_DIR ?? path22.join(home, ".pi", "agent");
4792
4779
  return {
4793
4780
  codingAgentDir,
4794
- receiptPath: path23.join(dataDir(), "pi-receipt.json"),
4795
- packageRunner: path23.join(codingAgentDir, "npm", "node_modules", "jorgex-pi", "bin", "jorgex-pi.mjs"),
4781
+ receiptPath: path22.join(dataDir(), "pi-receipt.json"),
4782
+ packageRunner: path22.join(codingAgentDir, "npm", "node_modules", "jorgex-pi", "bin", "jorgex-pi.mjs"),
4796
4783
  environment: {
4797
4784
  HOME: home,
4798
- XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME ?? path23.join(home, ".config"),
4799
- XDG_CACHE_HOME: process.env.XDG_CACHE_HOME ?? path23.join(home, ".cache"),
4785
+ XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME ?? path22.join(home, ".config"),
4786
+ XDG_CACHE_HOME: process.env.XDG_CACHE_HOME ?? path22.join(home, ".cache"),
4800
4787
  TMPDIR: os4.tmpdir(),
4801
4788
  NPM_CONFIG_IGNORE_SCRIPTS: "true",
4802
4789
  NPM_CONFIG_UPDATE_NOTIFIER: "false",
@@ -4824,7 +4811,7 @@ function runPiRuntime(input, deps) {
4824
4811
  return { kind: "blocked", reason: "tarball-integrity" };
4825
4812
  }
4826
4813
  const paths = input.targetDir === void 0 ? userPaths(input.engramBin, input.detected.executable) : targetPaths(input.targetDir, input.engramBin, input.detected.executable);
4827
- const settingsJson = deps.readSettings(path23.join(paths.codingAgentDir, "settings.json"));
4814
+ const settingsJson = deps.readSettings(path22.join(paths.codingAgentDir, "settings.json"));
4828
4815
  const receiptJson = deps.readReceipt(paths.receiptPath);
4829
4816
  const lifecycleInput = {
4830
4817
  candidate: PI_RUNTIME_CANDIDATE,
@@ -4888,14 +4875,14 @@ function readJsonFile(file) {
4888
4875
  function packageVersionFromExecutable(executable) {
4889
4876
  let current;
4890
4877
  try {
4891
- current = path23.dirname(fs18.realpathSync(executable));
4878
+ current = path22.dirname(fs18.realpathSync(executable));
4892
4879
  } catch {
4893
4880
  return null;
4894
4881
  }
4895
4882
  for (let depth = 0; depth < 8; depth++) {
4896
4883
  const manifests = [
4897
- path23.join(current, "package.json"),
4898
- path23.join(current, "node_modules", "@earendil-works", "pi-coding-agent", "package.json")
4884
+ path22.join(current, "package.json"),
4885
+ path22.join(current, "node_modules", "@earendil-works", "pi-coding-agent", "package.json")
4899
4886
  ];
4900
4887
  for (const manifest of manifests) {
4901
4888
  try {
@@ -4906,7 +4893,7 @@ function packageVersionFromExecutable(executable) {
4906
4893
  } catch {
4907
4894
  }
4908
4895
  }
4909
- const parent = path23.dirname(current);
4896
+ const parent = path22.dirname(current);
4910
4897
  if (parent === current) break;
4911
4898
  current = parent;
4912
4899
  }
@@ -4921,16 +4908,16 @@ function detectPiRuntime() {
4921
4908
  installed: executable !== null,
4922
4909
  executable,
4923
4910
  version: executable === null ? null : packageVersionFromExecutable(executable),
4924
- codingAgentDir: process.env.PI_CODING_AGENT_DIR ?? path23.join(home, ".pi", "agent")
4911
+ codingAgentDir: process.env.PI_CODING_AGENT_DIR ?? path22.join(home, ".pi", "agent")
4925
4912
  };
4926
4913
  }
4927
4914
  function hasManagedPiRuntime(targetDir) {
4928
- const receipt = targetDir === void 0 ? path23.join(dataDir(), "pi-receipt.json") : path23.join(path23.resolve(targetDir), "state", "pi-receipt.json");
4915
+ const receipt = targetDir === void 0 ? path22.join(dataDir(), "pi-receipt.json") : path22.join(path22.resolve(targetDir), "state", "pi-receipt.json");
4929
4916
  return fs18.statSync(receipt, { throwIfNoEntry: false })?.isFile() === true;
4930
4917
  }
4931
4918
  function resolvePiEngramBin(targetDir) {
4932
4919
  if (targetDir === void 0) return detectEngram();
4933
- const candidate = path23.join(path23.resolve(targetDir), "bin", process.platform === "win32" ? "engram.exe" : "engram");
4920
+ const candidate = path22.join(path22.resolve(targetDir), "bin", process.platform === "win32" ? "engram.exe" : "engram");
4934
4921
  return fs18.statSync(candidate, { throwIfNoEntry: false })?.isFile() ? candidate : null;
4935
4922
  }
4936
4923
  function readOptional(file, fallback) {
@@ -4970,7 +4957,7 @@ async function acquirePiTarball(destination) {
4970
4957
  }
4971
4958
  fs18.rmSync(destination, { force: true });
4972
4959
  }
4973
- fs18.mkdirSync(path23.dirname(destination), { recursive: true });
4960
+ fs18.mkdirSync(path22.dirname(destination), { recursive: true });
4974
4961
  const partial = `${destination}.partial-${process.pid}`;
4975
4962
  const response = await fetch(`https://registry.npmjs.org/jorgex-pi/-/jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`, {
4976
4963
  redirect: "error",
@@ -5013,6 +5000,21 @@ function runProcess(invocation) {
5013
5000
  stderr: result.stderr ?? (result.error?.message ?? "")
5014
5001
  };
5015
5002
  }
5003
+ function setupPiFailedRemedy(setup) {
5004
+ const detail = setup.reason ?? setup.stderr ?? "setup oficial Pi fall\xF3";
5005
+ const recovery = setup.recovery ?? "none";
5006
+ const backupId = setup.backupId ?? null;
5007
+ if (recovery === "complete") {
5008
+ return backupId !== null ? `${detail}. Se restaur\xF3 el backup ${backupId}; Pi no qued\xF3 activado.` : `${detail}. Se restaur\xF3 el backup previo; Pi no qued\xF3 activado.`;
5009
+ }
5010
+ if (recovery === "incomplete") {
5011
+ const id2 = backupId !== null ? ` (backup ${backupId})` : " (sin backup v\xE1lido)";
5012
+ const cause = setup.restoreError !== void 0 ? ` ${setup.restoreError}.` : "";
5013
+ return `${detail}. Recuperaci\xF3n incompleta${id2};${cause} revisa manualmente el estado y corrige la causa antes de reintentar; Pi no qued\xF3 activado.`;
5014
+ }
5015
+ const id = backupId !== null ? ` (backup ${backupId})` : " (sin backup v\xE1lido)";
5016
+ return `${detail}. Sin recuperaci\xF3n autom\xE1tica${id}; revisa manualmente el estado y corrige la causa antes de reintentar; Pi no qued\xF3 activado.`;
5017
+ }
5016
5018
  async function runPiRuntimeSystem(input) {
5017
5019
  if (input.engramBin === null && input.operation !== "uninstall") return runPiRuntime(input, {
5018
5020
  readSettings: () => {
@@ -5043,7 +5045,32 @@ async function runPiRuntimeSystem(input) {
5043
5045
  remedy: "Instala Engram o configura un ENGRAM_BIN absoluto antes de reintentar."
5044
5046
  };
5045
5047
  }
5046
- const destination = input.targetDir === void 0 ? path23.join(dataDir(), "packages", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`) : path23.join(path23.resolve(input.targetDir), "downloads", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`);
5048
+ if (input.targetDir === void 0) {
5049
+ const { runOfficialSetupIfNeeded: runOfficialSetupIfNeeded2 } = await import("./official-engram-setup-4WIXM4EF.js");
5050
+ const setup = await runOfficialSetupIfNeeded2("pi", {
5051
+ command: "install",
5052
+ dryRun: false,
5053
+ targetDir: void 0,
5054
+ engramBin: input.engramBin,
5055
+ configDir: paths.codingAgentDir,
5056
+ homeDir: os4.homedir()
5057
+ });
5058
+ if (!setup.ran || !setup.ok) {
5059
+ if (!setup.ran) {
5060
+ return {
5061
+ kind: "blocked",
5062
+ reason: "setup-pi-failed",
5063
+ remedy: "setup oficial Pi omitido en install real. Sin recuperaci\xF3n autom\xE1tica (sin backup v\xE1lido); revisa manualmente el estado y corrige la causa antes de reintentar; Pi no qued\xF3 activado."
5064
+ };
5065
+ }
5066
+ return {
5067
+ kind: "blocked",
5068
+ reason: "setup-pi-failed",
5069
+ remedy: setupPiFailedRemedy(setup)
5070
+ };
5071
+ }
5072
+ }
5073
+ const destination = input.targetDir === void 0 ? path22.join(dataDir(), "packages", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`) : path22.join(path22.resolve(input.targetDir), "downloads", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`);
5047
5074
  let artifact;
5048
5075
  try {
5049
5076
  artifact = await acquirePiTarball(destination);
@@ -5064,18 +5091,18 @@ async function runPiRuntimeSystem(input) {
5064
5091
  }, {
5065
5092
  download: () => artifact,
5066
5093
  backupSettings: () => createBackup(
5067
- [path23.join(paths.codingAgentDir, "settings.json")],
5094
+ [path22.join(paths.codingAgentDir, "settings.json")],
5068
5095
  "pi-package-install",
5069
- input.targetDir === void 0 ? void 0 : path23.join(path23.resolve(input.targetDir), "backups")
5096
+ input.targetDir === void 0 ? void 0 : path22.join(path22.resolve(input.targetDir), "backups")
5070
5097
  ),
5071
5098
  run: runProcess,
5072
- readSettings: () => readOptional(path23.join(paths.codingAgentDir, "settings.json"), '{"packages":[]}'),
5073
- rewriteSettings: (content) => writeText(path23.join(paths.codingAgentDir, "settings.json"), `${content}
5099
+ readSettings: () => readOptional(path22.join(paths.codingAgentDir, "settings.json"), '{"packages":[]}'),
5100
+ rewriteSettings: (content) => writeText(path22.join(paths.codingAgentDir, "settings.json"), `${content}
5074
5101
  `),
5075
5102
  writeReceiptAtomic: (content) => writeText(paths.receiptPath, content)
5076
5103
  });
5077
5104
  }
5078
- const packageRoot = path23.dirname(path23.dirname(paths.packageRunner));
5105
+ const packageRoot = path22.dirname(path22.dirname(paths.packageRunner));
5079
5106
  const writeReceipt = (receipt) => {
5080
5107
  writeText(paths.receiptPath, `${JSON.stringify(receipt, null, 2)}
5081
5108
  `);
@@ -5093,9 +5120,9 @@ async function runPiRuntimeSystem(input) {
5093
5120
  value,
5094
5121
  {
5095
5122
  backupSettings: () => createBackup(
5096
- [path23.join(paths.codingAgentDir, "settings.json")],
5123
+ [path22.join(paths.codingAgentDir, "settings.json")],
5097
5124
  "pi-package-uninstall",
5098
- input.targetDir === void 0 ? void 0 : path23.join(path23.resolve(input.targetDir), "backups")
5125
+ input.targetDir === void 0 ? void 0 : path22.join(path22.resolve(input.targetDir), "backups")
5099
5126
  ),
5100
5127
  run: runProcess,
5101
5128
  isPackageAbsent: () => !fs18.existsSync(packageRoot),
@@ -5274,7 +5301,7 @@ async function runManagedPiSystem(input) {
5274
5301
  }
5275
5302
 
5276
5303
  // src/lib/quality-runner.ts
5277
- import path24 from "path";
5304
+ import path23 from "path";
5278
5305
  import { spawn, spawnSync as spawnSync2 } from "child_process";
5279
5306
  function normalizeLimit(value, name) {
5280
5307
  if (value === void 0) return void 0;
@@ -5406,7 +5433,7 @@ function killProcessTree(child) {
5406
5433
  if (pid === void 0) return;
5407
5434
  if (process.platform === "win32") {
5408
5435
  const systemRoot = process.env.SystemRoot ?? process.env.WINDIR ?? "C:\\Windows";
5409
- const taskkill = path24.join(systemRoot, "System32", "taskkill.exe");
5436
+ const taskkill = path23.join(systemRoot, "System32", "taskkill.exe");
5410
5437
  const result = spawnSync2(taskkill, ["/pid", String(pid), "/t", "/f"], {
5411
5438
  shell: false,
5412
5439
  stdio: "ignore",
@@ -5654,107 +5681,172 @@ async function runQualityPlan(input) {
5654
5681
  // src/lib/engram-install.ts
5655
5682
  import fs19 from "fs";
5656
5683
  import os5 from "os";
5657
- import path25 from "path";
5684
+ import path24 from "path";
5658
5685
  import { execFileSync as execFileSync6 } from "child_process";
5659
5686
  import { createHash as createHash3 } from "crypto";
5660
-
5661
- // src/lib/engram-release.json
5662
- var engram_release_default = {
5663
- assets: {
5664
- darwin_arm64: {
5665
- name: "engram_2.0.0_darwin_arm64.tar.gz",
5666
- sha256: "793c695153e0e76144dc53b006a7039771bd24115385856578bc4d6506d46d7f",
5667
- size: 7210321
5668
- },
5669
- darwin_x64: {
5670
- name: "engram_2.0.0_darwin_amd64.tar.gz",
5671
- sha256: "f1dcc343ac241c5001617113279b49dec698005550b538250ccf21655fc728ac",
5672
- size: 7678428
5673
- },
5674
- linux_arm64: {
5675
- name: "engram_2.0.0_linux_arm64.tar.gz",
5676
- sha256: "a942e73ab424faaa6e2785d1563e0d9d7f20739944dae0c50071223301d44333",
5677
- size: 6926960
5678
- },
5679
- linux_x64: {
5680
- name: "engram_2.0.0_linux_amd64.tar.gz",
5681
- sha256: "23be1c2ce9739c455097ff864736213717b925b3e8821a988dfc619685a5abd5",
5682
- size: 7544949
5683
- },
5684
- windows_arm64: {
5685
- name: "engram_2.0.0_windows_arm64.zip",
5686
- sha256: "e4d9074ebf8a839573f62d7bd2719421fc428ab25a9affe82ca4b2d5555183d1",
5687
- size: 6980876
5688
- },
5689
- windows_x64: {
5690
- name: "engram_2.0.0_windows_amd64.zip",
5691
- sha256: "fba31e7221702f213954c2081aa83b38833e0e9b824a981f976f8cee2c74548c",
5692
- size: 7738896
5693
- }
5694
- },
5695
- version: "2.0.0"
5696
- };
5697
-
5698
- // src/lib/engram-install.ts
5687
+ var MAX_ENGRAM_ARCHIVE_BYTES = 64 * 1024 * 1024;
5688
+ var STABLE_TAG = /^v\d+\.\d+\.\d+$/;
5689
+ var DIGEST_RE = /^sha256:[0-9a-f]{64}$/;
5699
5690
  async function installMissingEngram(options = {}) {
5700
5691
  const platform = options.platform ?? process.platform;
5701
5692
  const homeDir = options.homeDir ?? os5.homedir();
5702
- if (!path25.isAbsolute(homeDir)) return { ok: false, reason: "El directorio personal debe ser absoluto." };
5693
+ if (!path24.isAbsolute(homeDir)) return { ok: false, reason: "El directorio personal debe ser absoluto." };
5703
5694
  const binaryName = platform === "win32" ? "engram.exe" : "engram";
5704
- const bin = path25.join(homeDir, ".local", "bin", binaryName);
5695
+ const bin = path24.join(homeDir, ".local", "bin", binaryName);
5705
5696
  let staging;
5697
+ let outcome;
5706
5698
  try {
5707
5699
  const existing = fs19.lstatSync(bin, { throwIfNoEntry: false });
5708
5700
  if (existing) {
5709
5701
  if (!fs19.statSync(bin).isFile()) throw new Error(`El destino Engram no es un archivo: ${bin}`);
5710
5702
  fs19.accessSync(bin, platform === "win32" ? fs19.constants.F_OK : fs19.constants.X_OK);
5711
- return { ok: true, bin };
5712
- }
5713
- const key = `${platform === "win32" ? "windows" : platform}_${options.arch ?? process.arch}`;
5714
- const asset = engram_release_default.assets[key];
5715
- if (!asset) throw new Error(`Engram no dispone de un binario aprobado para ${key}.`);
5716
- const response = await (options.fetch ?? globalThis.fetch)(
5717
- `https://github.com/Gentleman-Programming/engram/releases/download/v${engram_release_default.version}/${asset.name}`,
5718
- { signal: AbortSignal.timeout(12e4) }
5719
- );
5720
- if (!response.ok || !response.body) throw new Error(`Descarga Engram fallida: HTTP ${response.status}.`);
5721
- const reader = response.body.getReader();
5722
- const chunks = [];
5723
- let size = 0;
5724
- try {
5725
- while (true) {
5726
- const chunk = await reader.read();
5727
- if (chunk.done) break;
5728
- size += chunk.value.byteLength;
5729
- if (size > asset.size) throw new Error("El tama\xF1o descargado de Engram supera el aprobado.");
5730
- chunks.push(chunk.value);
5703
+ outcome = { ok: true, bin };
5704
+ } else {
5705
+ const fetchFn = options.fetch ?? globalThis.fetch;
5706
+ let releaseRes;
5707
+ try {
5708
+ releaseRes = await fetchLatestGithubRelease(ENGRAM_REPO, fetchFn);
5709
+ } catch (error) {
5710
+ const cause = error instanceof Error ? error.message : String(error);
5711
+ throw new Error(
5712
+ `Engram: error de red al consultar los metadatos del release (latest): ${cause}. Reintenta la instalaci\xF3n.`
5713
+ );
5731
5714
  }
5732
- } finally {
5733
- await reader.cancel();
5734
- }
5735
- const archive = Buffer.concat(chunks);
5736
- if (size !== asset.size || createHash3("sha256").update(archive).digest("hex") !== asset.sha256) {
5737
- throw new Error("Engram: tama\xF1o o hash SHA-256 no coincide con el artefacto aprobado.");
5738
- }
5739
- fs19.mkdirSync(path25.dirname(bin), { recursive: true });
5740
- staging = fs19.mkdtempSync(path25.join(path25.dirname(bin), ".engram-install-"));
5741
- const archivePath = path25.join(staging, asset.name);
5742
- fs19.writeFileSync(archivePath, archive, { flag: "wx", mode: 384 });
5743
- const binary = execFileSync6(resolveTarBin(), ["-xOf", archivePath, binaryName], {
5744
- maxBuffer: 64 * 1024 * 1024,
5745
- timeout: 3e4,
5746
- stdio: ["ignore", "pipe", "pipe"]
5747
- });
5748
- if (binary.length === 0) throw new Error("El artefacto Engram no contiene un binario v\xE1lido.");
5749
- const stagedBin = path25.join(staging, binaryName);
5750
- fs19.writeFileSync(stagedBin, binary, { flag: "wx", mode: 493 });
5751
- fs19.linkSync(stagedBin, bin);
5752
- return { ok: true, bin };
5715
+ if (!releaseRes.ok) {
5716
+ if (releaseRes.status === 429) {
5717
+ throw new Error(
5718
+ "Engram: metadatos del release no disponibles (HTTP 429 rate limit en la consulta latest/metadatos). Espera y reintenta; define GH_TOKEN o inicia sesi\xF3n con gh auth login para elevar el l\xEDmite."
5719
+ );
5720
+ }
5721
+ if (releaseRes.status === 403) {
5722
+ throw new Error(
5723
+ "Engram: metadatos del release no disponibles (HTTP 403 en la consulta latest/metadatos). Posible rate limit o falta de autenticaci\xF3n; define GH_TOKEN o usa gh auth login y reintenta."
5724
+ );
5725
+ }
5726
+ throw new Error(
5727
+ `Engram: metadatos del release no disponibles (HTTP ${releaseRes.status} en la consulta latest).`
5728
+ );
5729
+ }
5730
+ let raw;
5731
+ try {
5732
+ raw = await releaseRes.json();
5733
+ } catch (error) {
5734
+ if (error instanceof SyntaxError) {
5735
+ throw new Error("Engram: metadatos del release inv\xE1lidos: JSON no parseable.");
5736
+ }
5737
+ const cause = error instanceof Error ? error.message : String(error);
5738
+ const hint = error instanceof Error && error.name === "AbortError" ? "interrumpida (red/cancelada)" : "de red";
5739
+ throw new Error(
5740
+ `Engram: lectura de metadatos del release ${hint}: ${cause}. Reintenta la instalaci\xF3n.`
5741
+ );
5742
+ }
5743
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
5744
+ throw new Error("Engram: metadatos del release inv\xE1lidos: se esperaba un objeto JSON.");
5745
+ }
5746
+ const releaseJson = raw;
5747
+ const tag = releaseJson.tag_name;
5748
+ if (typeof tag !== "string" || !STABLE_TAG.test(tag)) {
5749
+ throw new Error(`Engram: tag del release inv\xE1lido: ${String(tag)}.`);
5750
+ }
5751
+ const version = tag.slice(1);
5752
+ const expectedName = expectedEngramAssetName(version, platform, options.arch ?? process.arch);
5753
+ if (expectedName === null) {
5754
+ throw new Error(`Engram no dispone de un binario aprobado para ${platform}_${options.arch ?? process.arch}.`);
5755
+ }
5756
+ if (!Array.isArray(releaseJson.assets)) throw new Error("Engram: metadatos del release sin assets.");
5757
+ const matches = releaseJson.assets.filter((asset2) => asset2?.name === expectedName);
5758
+ if (matches.length !== 1) throw new Error("Engram: asset exacto ausente o ambiguo en el release.");
5759
+ const asset = matches[0];
5760
+ if (asset.state !== "uploaded") throw new Error("Engram: asset no publicado.");
5761
+ if (!Number.isInteger(asset.size) || asset.size <= 0) {
5762
+ throw new Error("Engram: tama\xF1o del asset inv\xE1lido.");
5763
+ }
5764
+ const expectedUrl = `${ENGRAM_DOWNLOAD_PREFIX}${tag}/${expectedName}`;
5765
+ if (typeof asset.browser_download_url !== "string" || asset.browser_download_url !== expectedUrl) {
5766
+ throw new Error("Engram: URL del asset no oficial.");
5767
+ }
5768
+ if (typeof asset.digest !== "string" || !DIGEST_RE.test(asset.digest)) {
5769
+ throw new Error("Engram: digest del asset inv\xE1lido.");
5770
+ }
5771
+ const approvedSize = asset.size;
5772
+ if (approvedSize > MAX_ENGRAM_ARCHIVE_BYTES) {
5773
+ throw new Error(
5774
+ `Engram: tama\xF1o del asset aprobado (${approvedSize} bytes) excede el l\xEDmite de 64 MiB (${MAX_ENGRAM_ARCHIVE_BYTES} bytes).`
5775
+ );
5776
+ }
5777
+ const approvedSha = asset.digest.slice("sha256:".length);
5778
+ const response = await fetchFn(asset.browser_download_url, {
5779
+ signal: AbortSignal.timeout(12e4)
5780
+ });
5781
+ if (!response.ok || !response.body) throw new Error(`Descarga Engram fallida: HTTP ${response.status}.`);
5782
+ const reader = response.body.getReader();
5783
+ const chunks = [];
5784
+ let size = 0;
5785
+ let readError;
5786
+ try {
5787
+ try {
5788
+ while (true) {
5789
+ const chunk = await reader.read();
5790
+ if (chunk.done) break;
5791
+ size += chunk.value.byteLength;
5792
+ if (size > approvedSize) throw new Error("El tama\xF1o descargado de Engram supera el aprobado.");
5793
+ chunks.push(chunk.value);
5794
+ }
5795
+ } catch (error) {
5796
+ readError = error;
5797
+ throw error;
5798
+ }
5799
+ } finally {
5800
+ try {
5801
+ await reader.cancel();
5802
+ } catch (cancelError) {
5803
+ const cancelMsg = cancelError instanceof Error ? cancelError.message : String(cancelError);
5804
+ if (readError !== void 0) {
5805
+ const primaryMsg = readError instanceof Error ? readError.message : String(readError);
5806
+ throw new Error(`${primaryMsg} (adem\xE1s fall\xF3 la cancelaci\xF3n del stream: ${cancelMsg})`);
5807
+ }
5808
+ throw cancelError;
5809
+ }
5810
+ }
5811
+ const archive = Buffer.concat(chunks);
5812
+ if (size !== approvedSize || createHash3("sha256").update(archive).digest("hex") !== approvedSha) {
5813
+ throw new Error("Engram: tama\xF1o o hash SHA-256 no coincide con el artefacto aprobado.");
5814
+ }
5815
+ fs19.mkdirSync(path24.dirname(bin), { recursive: true });
5816
+ staging = fs19.mkdtempSync(path24.join(path24.dirname(bin), ".engram-install-"));
5817
+ const archivePath = path24.join(staging, expectedName);
5818
+ fs19.writeFileSync(archivePath, archive, { flag: "wx", mode: 384 });
5819
+ const binary = execFileSync6(resolveTarBin(), ["-xOf", archivePath, binaryName], {
5820
+ maxBuffer: 64 * 1024 * 1024,
5821
+ timeout: 3e4,
5822
+ stdio: ["ignore", "pipe", "pipe"]
5823
+ });
5824
+ if (binary.length === 0) throw new Error("El artefacto Engram no contiene un binario v\xE1lido.");
5825
+ const stagedBin = path24.join(staging, binaryName);
5826
+ fs19.writeFileSync(stagedBin, binary, { flag: "wx", mode: 493 });
5827
+ fs19.linkSync(stagedBin, bin);
5828
+ outcome = { ok: true, bin };
5829
+ }
5753
5830
  } catch (error) {
5754
- return { ok: false, reason: error instanceof Error ? error.message : String(error) };
5755
- } finally {
5756
- if (staging) fs19.rmSync(staging, { recursive: true, force: true });
5831
+ outcome = { ok: false, reason: error instanceof Error ? error.message : String(error) };
5757
5832
  }
5833
+ if (staging !== void 0) {
5834
+ try {
5835
+ fs19.rmSync(staging, { recursive: true, force: true });
5836
+ } catch (cleanupError) {
5837
+ const cleanupMsg = cleanupError instanceof Error ? cleanupError.message : String(cleanupError);
5838
+ if (outcome !== void 0 && outcome.ok) {
5839
+ outcome = {
5840
+ ok: true,
5841
+ bin: outcome.bin,
5842
+ warning: `Limpieza del directorio temporal ${staging} fallida tras instalar Engram: ${cleanupMsg}. El binario qued\xF3 instalado en ${outcome.bin}.`
5843
+ };
5844
+ } else if (outcome !== void 0 && !outcome.ok) {
5845
+ outcome = { ok: false, reason: `${outcome.reason} (adem\xE1s fall\xF3 la limpieza temporal: ${cleanupMsg})` };
5846
+ }
5847
+ }
5848
+ }
5849
+ return outcome ?? { ok: false, reason: "Engram: instalaci\xF3n finaliz\xF3 sin resultado verificable." };
5758
5850
  }
5759
5851
 
5760
5852
  // src/cli.ts
@@ -6088,7 +6180,10 @@ async function resolveHostEngramForInstall(flags) {
6088
6180
  }
6089
6181
  try {
6090
6182
  const result = await installMissingEngram();
6091
- if (result.ok) return { ok: true, bin: result.bin };
6183
+ if (result.ok) {
6184
+ if (result.warning) p6.log.warn(result.warning);
6185
+ return { ok: true, bin: result.bin };
6186
+ }
6092
6187
  return { ok: false, message: `Engram: ${result.reason}` };
6093
6188
  } catch (error) {
6094
6189
  return { ok: false, message: `Engram: ${error instanceof Error ? error.message : String(error)}` };
@@ -6143,7 +6238,11 @@ async function runSelectedPi(options) {
6143
6238
  const answer = await p6.confirm({ message, initialValue });
6144
6239
  return !p6.isCancel(answer) && answer;
6145
6240
  },
6146
- installNative: async ({ version }) => updateEngram("Gentleman-Programming/engram", version)
6241
+ installShared: async () => {
6242
+ const result2 = await installMissingEngram();
6243
+ if (result2.ok && result2.warning) p6.log.warn(result2.warning);
6244
+ return result2;
6245
+ }
6147
6246
  });
6148
6247
  if (requirement.kind !== "existing") {
6149
6248
  console.error(requirement.kind === "offer" ? "Pi: instalaci\xF3n cancelada; Engram sigue siendo obligatorio." : `Pi: ${requirement.reason}. ${requirement.remedy}`);