jorgex-stack 1.9.53 → 1.9.55

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),
@@ -4695,6 +4787,37 @@ function readDoctorTextIfExists(file) {
4695
4787
  throw error;
4696
4788
  }
4697
4789
  }
4790
+ var STALE_PERMISSIONS_MARKER = "differs from the stack default and was left untouched";
4791
+ var UPGRADE_PERMISSIONS_REMEDY = "jorgex-stack sync --upgrade-permissions --dry-run";
4792
+ function piAgentDir(targetDir) {
4793
+ return targetDir === void 0 ? process.env.PI_CODING_AGENT_DIR ?? path27.join(HOME, ".pi", "agent") : path27.join(targetDir, "pi-agent");
4794
+ }
4795
+ function reportPiPermissions(targetDir) {
4796
+ const agentDir = piAgentDir(targetDir);
4797
+ const configFile = path27.join(agentDir, "extensions", "pi-permission-system", "config.json");
4798
+ const receiptFile = path27.join(agentDir, "jorgex-pi", "permissions-lifecycle.v1.json");
4799
+ let config;
4800
+ let receipt;
4801
+ try {
4802
+ config = readDoctorTextIfExists(configFile);
4803
+ receipt = readDoctorTextIfExists(receiptFile);
4804
+ } catch {
4805
+ p3.log.error(`Pi: cannot read permission state in ${agentDir}; check files and permissions.`);
4806
+ return 1;
4807
+ }
4808
+ if (config === null) return 0;
4809
+ try {
4810
+ JSON.parse(config);
4811
+ } catch {
4812
+ 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.`);
4813
+ return 1;
4814
+ }
4815
+ if (receipt !== null) return 0;
4816
+ p3.log.warn(
4817
+ `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.`
4818
+ );
4819
+ return 1;
4820
+ }
4698
4821
  function engramVersion(bin) {
4699
4822
  const out = runDetectedBin(bin, ["--version"], 5e3);
4700
4823
  if (out === null) return null;
@@ -4775,6 +4898,9 @@ async function runDoctor(options = {}) {
4775
4898
  return 1;
4776
4899
  }
4777
4900
  let problems = reportWritingStyle(options, writingStyle, modePreference);
4901
+ if (options.runtimes === void 0 || options.runtimes.includes("pi")) {
4902
+ problems += reportPiPermissions(options.targetDir);
4903
+ }
4778
4904
  if (options.targetDir !== void 0 || options.runtimes !== void 0 && options.runtimes.length > 0 && options.runtimes.every((id) => id === "pi")) {
4779
4905
  p3.outro(problems > 0 ? `Doctor (alcance: estilo): ${problems} problema(s) \u2014 revisa arriba.` : "Doctor (alcance: estilo): todo sano.");
4780
4906
  return problems > 0 ? 1 : 0;
@@ -4867,8 +4993,11 @@ async function runDoctor(options = {}) {
4867
4993
  if (!ctx) continue;
4868
4994
  ctx.writingStyle = writingStyle;
4869
4995
  let pending;
4996
+ let stalePermissions = false;
4870
4997
  try {
4871
- pending = diffPlan(buildPlan(adapter, ctx)).filter((d) => d.status !== "unchanged").length;
4998
+ const plan = buildPlan(adapter, ctx);
4999
+ pending = diffPlan(plan).filter((d) => d.status !== "unchanged").length;
5000
+ stalePermissions = ctx.warnings.some((warning) => warning.includes(STALE_PERMISSIONS_MARKER));
4872
5001
  } catch (err) {
4873
5002
  p3.log.error(
4874
5003
  `${adapter.name}: configuraci\xF3n incompatible o ilegible en ${detection.configDir} \u2014 ${err instanceof Error ? err.message : err}`
@@ -4876,10 +5005,16 @@ async function runDoctor(options = {}) {
4876
5005
  problems++;
4877
5006
  continue;
4878
5007
  }
5008
+ if (stalePermissions) {
5009
+ p3.log.warn(
5010
+ `${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.`
5011
+ );
5012
+ problems++;
5013
+ }
4879
5014
  if (pending > 0) {
4880
5015
  p3.log.warn(`${adapter.name}: ${pending} archivos gestionados desactualizados o ausentes \u2192 ejecuta 'sync'.`);
4881
5016
  problems++;
4882
- } else {
5017
+ } else if (!stalePermissions) {
4883
5018
  p3.log.success(`${adapter.name}: config del stack al d\xEDa (${detection.configDir}).`);
4884
5019
  }
4885
5020
  const prev = manifest.runtimes[adapter.id];
@@ -6212,16 +6347,16 @@ import { createHash as createHash2 } from "crypto";
6212
6347
  var pi_runtime_pin_default = {
6213
6348
  package: {
6214
6349
  name: "jorgex-pi",
6215
- version: "0.8.24",
6216
- source: "npm:jorgex-pi@0.8.24"
6350
+ version: "0.8.25",
6351
+ source: "npm:jorgex-pi@0.8.25"
6217
6352
  },
6218
6353
  provenance: {
6219
- commit: "652d7e445e6f184c4543593c115026aa2f71e761"
6354
+ commit: "f8ae7ab58456d542e0cfe98232f20072d59b4966"
6220
6355
  },
6221
6356
  tarball: {
6222
- bytes: 89140631,
6223
- sha256: "6f67e546c86f21f9b5ff139f429551e696be4a8389ce4a6262e137dcce923a5f",
6224
- sha512: "1ce4bfc316f1635c5e498af7134ae16f430a4f6a3d2c99c7aa6fc31b76ce18684759c6480f506b5de45aa5b5b682e03f68d10fc77c83ae1679006dab679d9aa4"
6357
+ bytes: 89140483,
6358
+ sha256: "5477605d129100f0b793d9055959439af5c4a15497ff5dc44223fe8150e3ac19",
6359
+ sha512: "5d86567237f295bd383e8e52c72d4fa96f20b5f8ed38487e2b72ee83c22acab8f0fe65041ed27be80fb4ffa6f170efd20dbb937b51aab162073b0e958c9ddd2f"
6225
6360
  }
6226
6361
  };
6227
6362
 
@@ -7527,6 +7662,7 @@ function parseFlags(args, allowReceipt = false) {
7527
7662
  removePlaywright: false,
7528
7663
  devtools: false,
7529
7664
  noDevtools: false,
7665
+ upgradePermissions: false,
7530
7666
  receipt: void 0,
7531
7667
  positional: [],
7532
7668
  unknownFlags: []
@@ -7596,6 +7732,7 @@ function parseFlags(args, allowReceipt = false) {
7596
7732
  else if (arg === "--remove-playwright") flags.removePlaywright = true;
7597
7733
  else if (arg === "--devtools") flags.devtools = true;
7598
7734
  else if (arg === "--no-devtools") flags.noDevtools = true;
7735
+ else if (arg === "--upgrade-permissions") flags.upgradePermissions = true;
7599
7736
  else if (arg.startsWith("-")) flags.unknownFlags.push(arg);
7600
7737
  else flags.positional.push(arg);
7601
7738
  }
@@ -7929,6 +8066,7 @@ Opciones:
7929
8066
  --engram (install) autoriza instalar el binario Engram si falta
7930
8067
  --devtools (install/sync) activa Chrome DevTools MCP para los runtimes destino (opt-in)
7931
8068
  --no-devtools (install/sync) desactiva Chrome DevTools MCP (incompatible con --devtools)
8069
+ --upgrade-permissions (install/sync) re-aplica permisos gestionados sobre config existente (opt-in)
7932
8070
  --remove-engram (uninstall) desregistra Engram de los runtimes;
7933
8071
  memorias y binario quedan intactos igualmente
7934
8072
  --playwright-runtimes <csv> Activa su gu\xEDa s\xF3lo en estos runtimes de --agents (con --playwright)
@@ -7978,7 +8116,7 @@ Flags disponibles: jorgex-stack --help`
7978
8116
  }
7979
8117
  switch (command) {
7980
8118
  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) {
8119
+ 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
8120
  console.error("quality solo admite <plan.json> y, opcionalmente, --receipt <path>.");
7983
8121
  process.exitCode = 1;
7984
8122
  return;
@@ -8081,6 +8219,7 @@ Flags disponibles: jorgex-stack --help`
8081
8219
  playwrightToolConsent,
8082
8220
  devtoolsMcpSelection,
8083
8221
  engramBin,
8222
+ upgradePermissions: flags.upgradePermissions,
8084
8223
  ...playwrightCapability === void 0 ? {} : { playwrightCapability },
8085
8224
  ...playwrightToolPlan.actions.length === 0 ? {} : { onPlaywrightCapability: capturePlaywrightCapability },
8086
8225
  showSummary: false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jorgex-stack",
3
- "version": "1.9.53",
3
+ "version": "1.9.55",
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",