jorgex-stack 1.9.54 → 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 +1 -0
- package/dist/cli.js +152 -13
- package/package.json +1 -1
package/dist/cli.d.ts
CHANGED
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,
|
|
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
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
"
|
|
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
|
-
|
|
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"]
|
|
1574
|
-
|
|
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:
|
|
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
|
-
|
|
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];
|
|
@@ -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.
|
|
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",
|