jorgex-stack 1.9.54 → 1.9.56

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.d.ts CHANGED
@@ -27,6 +27,7 @@ interface Flags {
27
27
  removePlaywright: boolean;
28
28
  devtools: boolean;
29
29
  noDevtools: boolean;
30
+ upgradePermissions: boolean;
30
31
  receipt?: string;
31
32
  positional: string[];
32
33
  unknownFlags: string[];
package/dist/cli.js CHANGED
@@ -359,6 +359,7 @@ function findTomlSection(lines, section) {
359
359
  }
360
360
  function upsertTomlSection(existing, section, body) {
361
361
  const header = `[${section}]`;
362
+ const target = headerName(header) ?? section;
362
363
  const block = `${header}
363
364
  ${body.trim()}
364
365
  `;
@@ -372,7 +373,7 @@ ${body.trim()}
372
373
  offset += rawLines[index].length;
373
374
  if (index < rawLines.length - 1) offset++;
374
375
  }
375
- const found = findTomlSection(lines, section);
376
+ const found = findTomlSection(lines, target);
376
377
  if (found === null) {
377
378
  const sep = existing.endsWith("\n") ? "\n" : "\n\n";
378
379
  return existing + sep + block;
@@ -1203,11 +1204,22 @@ ${gitReadCommands.map((command) => `- \`${command}\``).join("\n")}
1203
1204
  }
1204
1205
  }
1205
1206
  const defaults = loadCanonicalDefaults(ctx.stackDir)["opencode"];
1206
- if (isFreshConfig && defaults?.["permission"] !== void 0) {
1207
- root["permission"] = defaults["permission"];
1208
- ctx.warnings.push(
1209
- "OpenCode: fresh config allows ordinary reads, edits, web access and Bash; sensitive operations ask, while protected paths and obvious destruction are denied. Native matching is not a universal filesystem sandbox."
1210
- );
1207
+ const canonicalPermission = defaults?.["permission"];
1208
+ if (isFreshConfig) {
1209
+ if (canonicalPermission !== void 0) {
1210
+ root["permission"] = canonicalPermission;
1211
+ ctx.warnings.push(
1212
+ "OpenCode: fresh config allows ordinary reads, edits, web access and Bash; sensitive operations ask, while protected paths and obvious destruction are denied. Native matching is not a universal filesystem sandbox."
1213
+ );
1214
+ }
1215
+ } else if (canonicalPermission !== void 0 && !isDeepStrictEqual(root["permission"], canonicalPermission)) {
1216
+ if (ctx.upgradePermissions === true) {
1217
+ root["permission"] = canonicalPermission;
1218
+ } else {
1219
+ ctx.warnings.push(
1220
+ "OpenCode: permission block differs from the stack default and was left untouched; re-run with --upgrade-permissions to replace it (a backup is created first), or edit it by hand. Overwriting discards your own permission changes, including any extra hardenings."
1221
+ );
1222
+ }
1211
1223
  }
1212
1224
  const mcp = root["mcp"] ??= {};
1213
1225
  for (const [name, server] of Object.entries(canonical.servers)) {
@@ -1568,12 +1580,26 @@ ${agent.body}`,
1568
1580
  const settingsFile = path10.join(ctx.configDir, "settings.json");
1569
1581
  let content = upsertNativeHooks(contentSource, canonical, scriptsDir);
1570
1582
  const defaults = loadCanonicalDefaults(ctx.stackDir)["claude-code"];
1571
- if (contentSource === null && defaults?.["permissions"] !== void 0) {
1583
+ const canonicalPermissions = defaults?.["permissions"];
1584
+ if (contentSource === null) {
1585
+ if (canonicalPermissions !== void 0) {
1586
+ content = upsertJson(content, (root) => {
1587
+ if (root["permissions"] === void 0) {
1588
+ root["permissions"] = canonicalPermissions;
1589
+ ctx.warnings.push(
1590
+ "Claude Code: fresh config enables read-anywhere via Read/Grep/Glob allow rules; shell, writes and web egress remain approval-gated, but broad local reads can expose secrets not covered by deny rules."
1591
+ );
1592
+ }
1593
+ });
1594
+ }
1595
+ } else if (canonicalPermissions !== void 0) {
1572
1596
  content = upsertJson(content, (root) => {
1573
- if (root["permissions"] === void 0) {
1574
- root["permissions"] = defaults["permissions"];
1597
+ if (isDeepStrictEqual2(root["permissions"], canonicalPermissions)) return;
1598
+ if (ctx.upgradePermissions === true) {
1599
+ root["permissions"] = canonicalPermissions;
1600
+ } else {
1575
1601
  ctx.warnings.push(
1576
- "Claude Code: fresh config enables read-anywhere via Read/Grep/Glob allow rules; shell, writes and web egress remain approval-gated, but broad local reads can expose secrets not covered by deny rules."
1602
+ "Claude Code: permissions block differs from the stack default and was left untouched; re-run with --upgrade-permissions to replace it (a backup is created first), or edit it by hand. Overwriting discards your own permission changes, including any extra hardenings."
1577
1603
  );
1578
1604
  }
1579
1605
  });
@@ -1867,6 +1893,61 @@ var CODEX_PERMISSION_SECTIONS = [
1867
1893
  function renderCodexPermissionEntries(entries, quoteKeys) {
1868
1894
  return entries.map(([key, value]) => `${quoteKeys ? JSON.stringify(key) : key} = ${JSON.stringify(value)}`).join("\n");
1869
1895
  }
1896
+ var CODEX_STALE_PERMISSIONS_WARNING = "Codex: permission profile differs from the stack default and was left untouched; re-run with --upgrade-permissions to replace it (a backup is created first), or edit it by hand. Overwriting discards your own permission changes, including any extra hardenings.";
1897
+ function codexNormalizedHeader(header) {
1898
+ return headerName(`[${header}]`) ?? header;
1899
+ }
1900
+ function readCodexRootValue(config, key) {
1901
+ const lines = config.replace(/\r\n/g, "\n").split("\n");
1902
+ const mask = multilineStringMask(lines);
1903
+ const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1904
+ const keyPattern = new RegExp(`^\\s*(?:${escaped}|"${escaped}"|'${escaped}')\\s*=`);
1905
+ for (const [index, line] of lines.entries()) {
1906
+ if (mask[index]) continue;
1907
+ if (line.trim().startsWith("[")) break;
1908
+ if (!keyPattern.test(line)) continue;
1909
+ const match = CODEX_ASSIGNMENT.exec(line.trim());
1910
+ return match === null ? void 0 : parseTomlString(match[2]);
1911
+ }
1912
+ return void 0;
1913
+ }
1914
+ function setCodexRootKey(config, key, value) {
1915
+ const eol = config.includes("\r\n") ? "\r\n" : "\n";
1916
+ const lines = config.replace(/\r\n/g, "\n").split("\n");
1917
+ const mask = multilineStringMask(lines);
1918
+ const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1919
+ const keyPattern = new RegExp(`^\\s*(?:${escaped}|"${escaped}"|'${escaped}')\\s*=`);
1920
+ const assignment = new RegExp(
1921
+ `^(\\s*(?:${escaped}|"${escaped}"|'${escaped}')\\s*=\\s*)(${CODEX_JSON_STRING_ARRAY}|${CODEX_JSON_VALUE})(\\s*(?:#.*)?)$`
1922
+ );
1923
+ for (const [index, line] of lines.entries()) {
1924
+ if (mask[index]) continue;
1925
+ if (line.trim().startsWith("[")) break;
1926
+ if (!keyPattern.test(line)) continue;
1927
+ const match = assignment.exec(line);
1928
+ lines[index] = match === null ? `${key} = ${value}` : `${match[1]}${value}${match[3]}`;
1929
+ return lines.join(eol);
1930
+ }
1931
+ return upsertTomlRootKeyIfMissing(config, key, value);
1932
+ }
1933
+ function isCodexPermissionBlockCurrent(config, defaults) {
1934
+ const expectedApproval = defaults["approval_policy"];
1935
+ const expectedDefault = defaults["default_permissions"];
1936
+ if (typeof expectedApproval !== "string" || typeof expectedDefault !== "string") return true;
1937
+ if (readCodexRootValue(config, "approval_policy") !== expectedApproval) return false;
1938
+ if (readCodexRootValue(config, "default_permissions") !== expectedDefault) return false;
1939
+ return CODEX_PERMISSION_SECTIONS.every(
1940
+ ({ header, entries, quoteKeys }) => (readTomlSection(config, codexNormalizedHeader(header)) ?? "").trim() === renderCodexPermissionEntries(entries, quoteKeys).trim()
1941
+ );
1942
+ }
1943
+ function reseedCodexPermissionBlock(config, defaults) {
1944
+ let out = setCodexRootKey(config, "approval_policy", tomlString(String(defaults["approval_policy"])));
1945
+ out = setCodexRootKey(out, "default_permissions", tomlString(String(defaults["default_permissions"])));
1946
+ for (const { header, entries, quoteKeys } of CODEX_PERMISSION_SECTIONS) {
1947
+ out = upsertTomlSection(out, header, renderCodexPermissionEntries(entries, quoteKeys));
1948
+ }
1949
+ return out;
1950
+ }
1870
1951
  function parseCodexKey(raw) {
1871
1952
  if (!raw.startsWith('"')) return raw;
1872
1953
  try {
@@ -2100,6 +2181,16 @@ ${body}`
2100
2181
  ""
2101
2182
  ].join("\n");
2102
2183
  }
2184
+ if (contentSource !== null) {
2185
+ const codexDefaults = loadCanonicalDefaults(ctx.stackDir)["codex"];
2186
+ if (codexDefaults !== void 0 && !isCodexPermissionBlockCurrent(contentSource, codexDefaults)) {
2187
+ if (ctx.upgradePermissions === true) {
2188
+ content = reseedCodexPermissionBlock(content, codexDefaults);
2189
+ } else {
2190
+ ctx.warnings.push(CODEX_STALE_PERMISSIONS_WARNING);
2191
+ }
2192
+ }
2193
+ }
2103
2194
  if (!hasTomlRootKey(content, PRIMARY_MODEL_FIELD2)) {
2104
2195
  content = upsertTomlRootKeyIfMissing(content, PRIMARY_MODEL_FIELD2, PRIMARY_MODEL2);
2105
2196
  if (ctx.ownedPrimaryModelFields?.has(PRIMARY_MODEL_FIELD2) !== true) {
@@ -3229,6 +3320,7 @@ async function runInstall(opts) {
3229
3320
  engramBin,
3230
3321
  models,
3231
3322
  warnings: [],
3323
+ upgradePermissions: opts.upgradePermissions === true,
3232
3324
  enabledMcpServers: enabledMcpServers(id, opts.devtoolsMcpSelection?.[id], useManifest),
3233
3325
  playwrightCliEnabled: projectPlaywrightPrompt ? opts.playwrightToolConsent?.runtimeSelection?.[id] ?? true : useManifest && loadPlaywrightCliPreference(playwrightCliPreferenceFile(), id) === true && (plannedPlaywright ?? true),
3234
3326
  ownedMcpServers: ownedMcpServers(id, useManifest),
@@ -3483,6 +3575,10 @@ var REQUIRED_CAPABILITIES = /* @__PURE__ */ new Set([
3483
3575
  "runner-json-v1",
3484
3576
  "managed-primary-model-v1"
3485
3577
  ]);
3578
+ var PERMISSIONS_UPGRADE_CAPABILITY = "permissions-upgrade-v1";
3579
+ function supportsPermissionsUpgrade(capabilities) {
3580
+ return Array.isArray(capabilities) && capabilities.includes(PERMISSIONS_UPGRADE_CAPABILITY);
3581
+ }
3486
3582
  var ALLOWED_EXTERNAL_WRITES = /* @__PURE__ */ new Set([
3487
3583
  "settings.json",
3488
3584
  "models.json",
@@ -3506,6 +3602,7 @@ function managedExternalWritesAreSafe(writes, capabilities) {
3506
3602
  const permissions = capabilities.includes("permissions-policy-v1");
3507
3603
  const experience = capabilities.includes("experience-defaults-v1");
3508
3604
  if (experience && !permissions) return false;
3605
+ if (supportsPermissionsUpgrade(capabilities) && !permissions) return false;
3509
3606
  const allowed = experience ? EXPERIENCE_EXTERNAL_WRITES : permissions ? PERMISSIONS_EXTERNAL_WRITES : ALLOWED_EXTERNAL_WRITES;
3510
3607
  if (writes.length !== allowed.size) return false;
3511
3608
  const seen = /* @__PURE__ */ new Set();
@@ -3728,6 +3825,43 @@ function runPackageCommand(input, deps, command) {
3728
3825
  if (result.exitCode !== 0) return { kind: "blocked", reason: "runner-unhealthy" };
3729
3826
  return parseRunnerRecord(result.stdout, result.stderr, command, input.candidate, input.packageRunner) ?? { kind: "blocked", reason: "runner-output" };
3730
3827
  }
3828
+ var CONFIG_LOCKED_SIGNAL = "CONFIG_LOCKED";
3829
+ var UPGRADE_LOCK_RETRIES = 2;
3830
+ function stdoutHasConfigLockedSignal(stdout) {
3831
+ const body = stdout.endsWith("\n") ? stdout.slice(0, -1) : stdout;
3832
+ if (body === "" || body.includes("\n") || body.includes("\r")) return false;
3833
+ try {
3834
+ const parsed = JSON.parse(body);
3835
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return false;
3836
+ const error = Reflect.get(parsed, "error");
3837
+ if (error === null || typeof error !== "object" || Array.isArray(error)) return false;
3838
+ return Reflect.get(error, "code") === CONFIG_LOCKED_SIGNAL;
3839
+ } catch {
3840
+ return false;
3841
+ }
3842
+ }
3843
+ function isConfigLockedFailure(result) {
3844
+ if (result.exitCode !== 1) return false;
3845
+ return stdoutHasConfigLockedSignal(result.stdout) || result.stderr.includes(CONFIG_LOCKED_SIGNAL);
3846
+ }
3847
+ function runUpgradeWithLockRetry(input, deps) {
3848
+ let retries = 0;
3849
+ for (; ; ) {
3850
+ const raw = deps.run({
3851
+ executable: input.packageRunner,
3852
+ args: ["upgrade", "--json"],
3853
+ environment: input.environment
3854
+ });
3855
+ if (raw.exitCode !== 0) {
3856
+ if (isConfigLockedFailure(raw) && retries < UPGRADE_LOCK_RETRIES) {
3857
+ retries += 1;
3858
+ continue;
3859
+ }
3860
+ return { kind: "blocked", reason: "runner-unhealthy" };
3861
+ }
3862
+ return parseRunnerRecord(raw.stdout, raw.stderr, "upgrade", input.candidate, input.packageRunner) ?? { kind: "blocked", reason: "runner-output" };
3863
+ }
3864
+ }
3731
3865
  function isBlockedResult(value) {
3732
3866
  return "kind" in value;
3733
3867
  }
@@ -3760,7 +3894,32 @@ function executePiPackageLifecycle(input, deps) {
3760
3894
  if (result === null || typeof result !== "object" || Reflect.get(result, "changed") !== true && Reflect.get(result, "changed") !== false || !Array.isArray(Reflect.get(result, "actions"))) {
3761
3895
  return { kind: "blocked", reason: "runner-unhealthy" };
3762
3896
  }
3763
- return { kind: "synced", actions: Reflect.get(result, "actions") };
3897
+ const syncActions = Reflect.get(result, "actions");
3898
+ if (input.upgradePermissions !== true || !supportsPermissionsUpgrade(input.candidate.contract.capabilities)) {
3899
+ return { kind: "synced", actions: syncActions };
3900
+ }
3901
+ const upgrade = runUpgradeWithLockRetry(input, deps);
3902
+ if (isBlockedResult(upgrade)) return upgrade;
3903
+ const upgraded = upgrade.result;
3904
+ if (upgraded === null || typeof upgraded !== "object" || Array.isArray(upgraded)) {
3905
+ return { kind: "blocked", reason: "runner-output" };
3906
+ }
3907
+ const changed = Reflect.get(upgraded, "changed");
3908
+ const actions = Reflect.get(upgraded, "actions");
3909
+ if (changed !== true && changed !== false || !Array.isArray(actions)) {
3910
+ return { kind: "blocked", reason: "runner-unhealthy" };
3911
+ }
3912
+ if (typeof Reflect.get(upgraded, "policy") === "string" || typeof Reflect.get(upgraded, "config") === "string") {
3913
+ return { kind: "blocked", reason: "runner-output" };
3914
+ }
3915
+ if (changed === true) {
3916
+ const policySha256 = Reflect.get(upgraded, "policySha256");
3917
+ if (typeof policySha256 !== "string" || !/^[a-f0-9]{64}$/.test(policySha256)) {
3918
+ return { kind: "blocked", reason: "runner-output" };
3919
+ }
3920
+ return { kind: "synced", actions: [...syncActions, ...actions], upgraded: true, policySha256 };
3921
+ }
3922
+ return { kind: "synced", actions: [...syncActions, ...actions], upgraded: false };
3764
3923
  }
3765
3924
  const models = command.result;
3766
3925
  if (models === null || typeof models !== "object" || Reflect.get(models, "mode") !== "inherit-session" || !sameRecord(Reflect.get(models, "tiers"), ["strong", "standard", "cheap"])) {
@@ -4695,6 +4854,37 @@ function readDoctorTextIfExists(file) {
4695
4854
  throw error;
4696
4855
  }
4697
4856
  }
4857
+ var STALE_PERMISSIONS_MARKER = "differs from the stack default and was left untouched";
4858
+ var UPGRADE_PERMISSIONS_REMEDY = "jorgex-stack sync --upgrade-permissions --dry-run";
4859
+ function piAgentDir(targetDir) {
4860
+ return targetDir === void 0 ? process.env.PI_CODING_AGENT_DIR ?? path27.join(HOME, ".pi", "agent") : path27.join(targetDir, "pi-agent");
4861
+ }
4862
+ function reportPiPermissions(targetDir) {
4863
+ const agentDir = piAgentDir(targetDir);
4864
+ const configFile = path27.join(agentDir, "extensions", "pi-permission-system", "config.json");
4865
+ const receiptFile = path27.join(agentDir, "jorgex-pi", "permissions-lifecycle.v1.json");
4866
+ let config;
4867
+ let receipt;
4868
+ try {
4869
+ config = readDoctorTextIfExists(configFile);
4870
+ receipt = readDoctorTextIfExists(receiptFile);
4871
+ } catch {
4872
+ p3.log.error(`Pi: cannot read permission state in ${agentDir}; check files and permissions.`);
4873
+ return 1;
4874
+ }
4875
+ if (config === null) return 0;
4876
+ try {
4877
+ JSON.parse(config);
4878
+ } catch {
4879
+ p3.log.error(`Pi: permission policy at ${configFile} is not valid JSON and was left untouched; the Pi package doctor reports it as an error.`);
4880
+ return 1;
4881
+ }
4882
+ if (receipt !== null) return 0;
4883
+ p3.log.warn(
4884
+ `Pi: permission policy present without package ownership (${configFile}) and left untouched; Stack never rewrites Pi state \u2014 align it by hand or remove it so a later 'jorgex-stack sync --agents pi' can seed the package default.`
4885
+ );
4886
+ return 1;
4887
+ }
4698
4888
  function engramVersion(bin) {
4699
4889
  const out = runDetectedBin(bin, ["--version"], 5e3);
4700
4890
  if (out === null) return null;
@@ -4775,6 +4965,9 @@ async function runDoctor(options = {}) {
4775
4965
  return 1;
4776
4966
  }
4777
4967
  let problems = reportWritingStyle(options, writingStyle, modePreference);
4968
+ if (options.runtimes === void 0 || options.runtimes.includes("pi")) {
4969
+ problems += reportPiPermissions(options.targetDir);
4970
+ }
4778
4971
  if (options.targetDir !== void 0 || options.runtimes !== void 0 && options.runtimes.length > 0 && options.runtimes.every((id) => id === "pi")) {
4779
4972
  p3.outro(problems > 0 ? `Doctor (alcance: estilo): ${problems} problema(s) \u2014 revisa arriba.` : "Doctor (alcance: estilo): todo sano.");
4780
4973
  return problems > 0 ? 1 : 0;
@@ -4867,8 +5060,11 @@ async function runDoctor(options = {}) {
4867
5060
  if (!ctx) continue;
4868
5061
  ctx.writingStyle = writingStyle;
4869
5062
  let pending;
5063
+ let stalePermissions = false;
4870
5064
  try {
4871
- pending = diffPlan(buildPlan(adapter, ctx)).filter((d) => d.status !== "unchanged").length;
5065
+ const plan = buildPlan(adapter, ctx);
5066
+ pending = diffPlan(plan).filter((d) => d.status !== "unchanged").length;
5067
+ stalePermissions = ctx.warnings.some((warning) => warning.includes(STALE_PERMISSIONS_MARKER));
4872
5068
  } catch (err) {
4873
5069
  p3.log.error(
4874
5070
  `${adapter.name}: configuraci\xF3n incompatible o ilegible en ${detection.configDir} \u2014 ${err instanceof Error ? err.message : err}`
@@ -4876,10 +5072,16 @@ async function runDoctor(options = {}) {
4876
5072
  problems++;
4877
5073
  continue;
4878
5074
  }
5075
+ if (stalePermissions) {
5076
+ p3.log.warn(
5077
+ `${adapter.name}: permission block differs from the stack default and was left untouched; preview the upgrade with '${UPGRADE_PERMISSIONS_REMEDY}'. Overwriting discards your own permission changes, including any extra hardenings.`
5078
+ );
5079
+ problems++;
5080
+ }
4879
5081
  if (pending > 0) {
4880
5082
  p3.log.warn(`${adapter.name}: ${pending} archivos gestionados desactualizados o ausentes \u2192 ejecuta 'sync'.`);
4881
5083
  problems++;
4882
- } else {
5084
+ } else if (!stalePermissions) {
4883
5085
  p3.log.success(`${adapter.name}: config del stack al d\xEDa (${detection.configDir}).`);
4884
5086
  }
4885
5087
  const prev = manifest.runtimes[adapter.id];
@@ -6212,16 +6414,16 @@ import { createHash as createHash2 } from "crypto";
6212
6414
  var pi_runtime_pin_default = {
6213
6415
  package: {
6214
6416
  name: "jorgex-pi",
6215
- version: "0.8.25",
6216
- source: "npm:jorgex-pi@0.8.25"
6417
+ version: "0.8.26",
6418
+ source: "npm:jorgex-pi@0.8.26"
6217
6419
  },
6218
6420
  provenance: {
6219
- commit: "f8ae7ab58456d542e0cfe98232f20072d59b4966"
6421
+ commit: "7d976c949e7a68d591ac01fb0f92ba83332ba291"
6220
6422
  },
6221
6423
  tarball: {
6222
- bytes: 89140483,
6223
- sha256: "5477605d129100f0b793d9055959439af5c4a15497ff5dc44223fe8150e3ac19",
6224
- sha512: "5d86567237f295bd383e8e52c72d4fa96f20b5f8ed38487e2b72ee83c22acab8f0fe65041ed27be80fb4ffa6f170efd20dbb937b51aab162073b0e958c9ddd2f"
6424
+ bytes: 89140420,
6425
+ sha256: "fadac8c16ae63585db2a3bd7109ab413e24f8df076f2f79fe8e3e2c404bc8324",
6426
+ sha512: "a508b39b6327eef6a371c8835ab957b838779a50e102c54e24c3a205342215a302bf1056b08571d9ea6c4fb79aa93978477c68be39aea4cd3da0e7995c78a6a6"
6225
6427
  }
6226
6428
  };
6227
6429
 
@@ -6246,6 +6448,7 @@ var PI_RUNTIME_CANDIDATE = {
6246
6448
  "engram-runtime-tools-v1",
6247
6449
  "context7-http-v1",
6248
6450
  "permissions-policy-v1",
6451
+ "permissions-upgrade-v1",
6249
6452
  "experience-defaults-v1",
6250
6453
  "chrome-devtools-handoff-v1",
6251
6454
  "playwright-handoff-v1",
@@ -6258,7 +6461,7 @@ var PI_RUNTIME_CANDIDATE = {
6258
6461
  ],
6259
6462
  runner: {
6260
6463
  bin: "jorgex-pi",
6261
- commands: ["status", "doctor", "models", "sync", "cleanup"],
6464
+ commands: ["status", "doctor", "models", "sync", "upgrade", "cleanup"],
6262
6465
  schemaVersion: 1,
6263
6466
  maxStdoutBytes: 65536
6264
6467
  },
@@ -6589,7 +6792,8 @@ function runPiRuntime(input, deps) {
6589
6792
  plan,
6590
6793
  candidate: PI_RUNTIME_CANDIDATE,
6591
6794
  packageRunner: paths.packageRunner,
6592
- environment: paths.environment
6795
+ environment: paths.environment,
6796
+ ...input.upgradePermissions === true ? { upgradePermissions: true } : {}
6593
6797
  });
6594
6798
  persistReturnedReceipt(result2, paths, deps);
6595
6799
  return result2;
@@ -6930,8 +7134,11 @@ async function runManagedPiSystem(input) {
6930
7134
  packageOnly,
6931
7135
  writingStyle: suppliedStyle,
6932
7136
  writingStyleMode,
7137
+ upgradePermissions: requestedUpgrade,
6933
7138
  ...runtimeInput
6934
7139
  } = input;
7140
+ const supportsPermissionsUpgrade2 = PI_RUNTIME_CANDIDATE.contract.capabilities.includes("permissions-upgrade-v1");
7141
+ const upgradePermissions = requestedUpgrade === true && supportsPermissionsUpgrade2;
6935
7142
  if (input.operation === "doctor" && packageOnly) {
6936
7143
  const result2 = managedPackageResult(await runPiRuntimeSystem(runtimeInput));
6937
7144
  return result2.kind === "manual-existing" ? manualExistingResult(result2) : result2;
@@ -6970,7 +7177,11 @@ async function runManagedPiSystem(input) {
6970
7177
  const result = await runManagedPiOperation(input.operation, {
6971
7178
  installInitRemedy: input.targetDir === void 0 ? void 0 : INSTALL_INIT_TARGET_REMEDY,
6972
7179
  async runPackage(operation) {
6973
- return managedPackageResult(await runPiRuntimeSystem({ ...runtimeInput, operation }));
7180
+ return managedPackageResult(await runPiRuntimeSystem({
7181
+ ...runtimeInput,
7182
+ operation,
7183
+ ...upgradePermissions ? { upgradePermissions: true } : {}
7184
+ }));
6974
7185
  },
6975
7186
  runProjection(operation) {
6976
7187
  const result2 = runPiProjectionLifecycleSystem({
@@ -7527,6 +7738,7 @@ function parseFlags(args, allowReceipt = false) {
7527
7738
  removePlaywright: false,
7528
7739
  devtools: false,
7529
7740
  noDevtools: false,
7741
+ upgradePermissions: false,
7530
7742
  receipt: void 0,
7531
7743
  positional: [],
7532
7744
  unknownFlags: []
@@ -7596,6 +7808,7 @@ function parseFlags(args, allowReceipt = false) {
7596
7808
  else if (arg === "--remove-playwright") flags.removePlaywright = true;
7597
7809
  else if (arg === "--devtools") flags.devtools = true;
7598
7810
  else if (arg === "--no-devtools") flags.noDevtools = true;
7811
+ else if (arg === "--upgrade-permissions") flags.upgradePermissions = true;
7599
7812
  else if (arg.startsWith("-")) flags.unknownFlags.push(arg);
7600
7813
  else flags.positional.push(arg);
7601
7814
  }
@@ -7886,7 +8099,16 @@ async function runSelectedPi(options) {
7886
8099
  engramBin,
7887
8100
  ...devtoolsMcpEnabled === void 0 ? {} : { devtoolsMcpEnabled },
7888
8101
  ...playwrightCliEnabled === void 0 ? {} : { playwrightCliEnabled },
7889
- ...playwrightCapability === void 0 ? {} : { playwrightCapability }
8102
+ ...playwrightCapability === void 0 ? {} : { playwrightCapability },
8103
+ ...(() => {
8104
+ if (options.upgradePermissions !== true) return {};
8105
+ const supports = PI_RUNTIME_CANDIDATE.contract.capabilities.includes("permissions-upgrade-v1");
8106
+ if (!supports) {
8107
+ p6.log.info("Pi: --upgrade-permissions requiere un paquete con permissions-upgrade-v1; se contin\xFAa en modo seed-only.");
8108
+ return {};
8109
+ }
8110
+ return { upgradePermissions: true };
8111
+ })()
7890
8112
  });
7891
8113
  if (result.kind === "blocked") {
7892
8114
  const paths = "paths" in result ? `: ${result.paths.join(", ")}` : "";
@@ -7929,6 +8151,7 @@ Opciones:
7929
8151
  --engram (install) autoriza instalar el binario Engram si falta
7930
8152
  --devtools (install/sync) activa Chrome DevTools MCP para los runtimes destino (opt-in)
7931
8153
  --no-devtools (install/sync) desactiva Chrome DevTools MCP (incompatible con --devtools)
8154
+ --upgrade-permissions (install/sync) re-aplica permisos gestionados sobre config existente (opt-in)
7932
8155
  --remove-engram (uninstall) desregistra Engram de los runtimes;
7933
8156
  memorias y binario quedan intactos igualmente
7934
8157
  --playwright-runtimes <csv> Activa su gu\xEDa s\xF3lo en estos runtimes de --agents (con --playwright)
@@ -7978,7 +8201,7 @@ Flags disponibles: jorgex-stack --help`
7978
8201
  }
7979
8202
  switch (command) {
7980
8203
  case "quality": {
7981
- if (flags.targetDir !== void 0 || flags.agents.length > 0 || flags.dryRun || flags.yes || flags.mode !== void 0 || flags.subagentConcurrency !== void 0 || flags.list || flags.check || flags.removeEngram || flags.engram || flags.playwright || flags.removePlaywright || flags.devtools || flags.noDevtools) {
8204
+ if (flags.targetDir !== void 0 || flags.agents.length > 0 || flags.dryRun || flags.yes || flags.mode !== void 0 || flags.subagentConcurrency !== void 0 || flags.list || flags.check || flags.removeEngram || flags.engram || flags.playwright || flags.removePlaywright || flags.devtools || flags.noDevtools || flags.upgradePermissions) {
7982
8205
  console.error("quality solo admite <plan.json> y, opcionalmente, --receipt <path>.");
7983
8206
  process.exitCode = 1;
7984
8207
  return;
@@ -8081,6 +8304,7 @@ Flags disponibles: jorgex-stack --help`
8081
8304
  playwrightToolConsent,
8082
8305
  devtoolsMcpSelection,
8083
8306
  engramBin,
8307
+ upgradePermissions: flags.upgradePermissions,
8084
8308
  ...playwrightCapability === void 0 ? {} : { playwrightCapability },
8085
8309
  ...playwrightToolPlan.actions.length === 0 ? {} : { onPlaywrightCapability: capturePlaywrightCapability },
8086
8310
  showSummary: false
@@ -8119,7 +8343,8 @@ Flags disponibles: jorgex-stack --help`
8119
8343
  writingStyle,
8120
8344
  modePreference: mode,
8121
8345
  playwrightCliEnabled: flags.targetDir === void 0 && exitCode === 0 && playwrightToolPlan.actions.length > 0 ? playwrightToolConsent.runtimeSelection?.pi : void 0,
8122
- playwrightCapability
8346
+ playwrightCapability,
8347
+ ...flags.upgradePermissions ? { upgradePermissions: true } : {}
8123
8348
  });
8124
8349
  exitCode = Math.max(exitCode, piExitCode);
8125
8350
  piStatus = piExitCode === 0 ? "ok" : "failed";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jorgex-stack",
3
- "version": "1.9.54",
3
+ "version": "1.9.56",
4
4
  "description": "Harness multi-agente portable: instala la config JorgeX (agentes, skills, hooks, Engram, MCPs) en Claude Code, Codex CLI, OpenCode y Pi",
5
5
  "type": "module",
6
6
  "license": "MIT",