jorgex-stack 1.9.40 → 1.9.42
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/README.md +4 -2
- package/dist/cli.js +334 -56
- package/package.json +1 -1
- package/stack/agents/README.md +3 -3
- package/stack/config/defaults.json +618 -12
package/dist/cli.js
CHANGED
|
@@ -401,6 +401,12 @@ function readTomlSection(existing, section) {
|
|
|
401
401
|
if (found === null) return null;
|
|
402
402
|
return lines.slice(found.start + 1, found.end).join("\n");
|
|
403
403
|
}
|
|
404
|
+
function hasTomlChildSection(existing, section) {
|
|
405
|
+
if (existing === null) return false;
|
|
406
|
+
const lines = existing.split(/\r?\n/);
|
|
407
|
+
const mask = multilineStringMask(lines);
|
|
408
|
+
return lines.some((line, index) => !mask[index] && headerName(line)?.startsWith(`${section}.`) === true);
|
|
409
|
+
}
|
|
404
410
|
|
|
405
411
|
// src/lib/writing-style.ts
|
|
406
412
|
var DEFAULT_SECTION = "writing-style-default";
|
|
@@ -830,19 +836,6 @@ function upsertNativeHooks(existing, canonical, scriptsDir, mapMatcher = (m) =>
|
|
|
830
836
|
});
|
|
831
837
|
}
|
|
832
838
|
|
|
833
|
-
// src/lib/git-guard.ts
|
|
834
|
-
var DESTRUCTIVE_GIT_DENY = [
|
|
835
|
-
"git reset*",
|
|
836
|
-
"git clean*",
|
|
837
|
-
"git checkout -- *",
|
|
838
|
-
"git restore*",
|
|
839
|
-
"git push*--force*",
|
|
840
|
-
// posición libre: caza "git push origin main --force[-with-lease]"
|
|
841
|
-
"git push -f*",
|
|
842
|
-
"git switch*--discard-changes*"
|
|
843
|
-
];
|
|
844
|
-
var GIT_GUARD_SCRIPT = "block-destructive-git.cjs";
|
|
845
|
-
|
|
846
839
|
// src/lib/quality-capabilities.ts
|
|
847
840
|
var QUALITY_CAPABILITY_NAMESPACE = "jorgex.quality.capabilities";
|
|
848
841
|
var QUALITY_CAPABILITY_VERSION = 1;
|
|
@@ -917,9 +910,36 @@ function localQualityStatus(profile, status) {
|
|
|
917
910
|
}
|
|
918
911
|
|
|
919
912
|
// src/adapters/opencode.ts
|
|
913
|
+
var gitReadPrefix = "git --no-pager -c core.fsmonitor=false -c log.showSignature=false";
|
|
914
|
+
var gitReadCommands = [
|
|
915
|
+
"diff",
|
|
916
|
+
"diff --stat",
|
|
917
|
+
"diff --name-only",
|
|
918
|
+
"diff --cached",
|
|
919
|
+
"log",
|
|
920
|
+
"log --oneline -10"
|
|
921
|
+
].map((action) => `${gitReadPrefix} ${action} --no-ext-diff --no-textconv --end-of-options`);
|
|
920
922
|
function yamlString(value) {
|
|
921
923
|
return JSON.stringify(value);
|
|
922
924
|
}
|
|
925
|
+
function readMcpConfig(file) {
|
|
926
|
+
let content;
|
|
927
|
+
try {
|
|
928
|
+
content = fs6.readFileSync(file, "utf8");
|
|
929
|
+
} catch (error) {
|
|
930
|
+
const code = error instanceof Error && "code" in error && typeof error.code === "string" ? error.code : "UNKNOWN";
|
|
931
|
+
if (code === "ENOENT") return null;
|
|
932
|
+
throw new Error(`OpenCode: no se pudo leer la configuraci\xF3n MCP en ${file} (${code}).`);
|
|
933
|
+
}
|
|
934
|
+
if (content.trim() !== "") {
|
|
935
|
+
try {
|
|
936
|
+
if (objectValue(JSON.parse(content)) === null) throw new Error();
|
|
937
|
+
} catch {
|
|
938
|
+
throw new Error(`OpenCode: la configuraci\xF3n MCP en ${file} debe contener un objeto JSON v\xE1lido.`);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
return content;
|
|
942
|
+
}
|
|
923
943
|
function isManagedOptionalStdioServer(server, value) {
|
|
924
944
|
if (!server.optional || server.transport !== "stdio" || value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
925
945
|
return false;
|
|
@@ -940,6 +960,31 @@ var PRIMARY_LIMIT_PREFIX = `provider.openai.models.${PRIMARY_MODEL_ID}.limit`;
|
|
|
940
960
|
function objectValue(value) {
|
|
941
961
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
942
962
|
}
|
|
963
|
+
function isCompatibleContext7Server(server, value) {
|
|
964
|
+
const current = objectValue(value);
|
|
965
|
+
if (server.transport !== "http" || typeof server.url !== "string" || current === null) return false;
|
|
966
|
+
return current.type === "remote" && current.url === server.url && (current.enabled === void 0 || current.enabled === true);
|
|
967
|
+
}
|
|
968
|
+
function canonicalContext7Server(server) {
|
|
969
|
+
const headers = {};
|
|
970
|
+
for (const [key, raw] of Object.entries(server.headers ?? {})) {
|
|
971
|
+
const envRef = /^\$\{(\w+)\}$/.exec(raw);
|
|
972
|
+
headers[key] = envRef ? `{env:${envRef[1]}}` : raw;
|
|
973
|
+
}
|
|
974
|
+
return {
|
|
975
|
+
type: "remote",
|
|
976
|
+
url: server.url,
|
|
977
|
+
...Object.keys(headers).length > 0 ? { headers } : {}
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
function isCanonicalContext7Server(server, value) {
|
|
981
|
+
return isCompatibleContext7Server(server, value) && isDeepStrictEqual(value, canonicalContext7Server(server));
|
|
982
|
+
}
|
|
983
|
+
function assertCompatibleContext7(server, value) {
|
|
984
|
+
if (value !== void 0 && !isCompatibleContext7Server(server, value)) {
|
|
985
|
+
throw new Error("OpenCode: MCP 'context7' entra en conflicto con una definici\xF3n existente (endpoint, tipo o estado nativo incompatible). Conserva la configuraci\xF3n y corrige el conflicto antes de reintentar.");
|
|
986
|
+
}
|
|
987
|
+
}
|
|
943
988
|
function ensureObject(parent, key, fieldPath) {
|
|
944
989
|
if (parent[key] === void 0) parent[key] = {};
|
|
945
990
|
const value = objectValue(parent[key]);
|
|
@@ -1015,15 +1060,20 @@ var opencodeAdapter = {
|
|
|
1015
1060
|
const tierModel = resolveAgentModel(models, agent.name, agent.tier);
|
|
1016
1061
|
lines.push(`model: ${tierModel.model}`);
|
|
1017
1062
|
if (tierModel.variant) lines.push(`variant: ${tierModel.variant}`);
|
|
1018
|
-
lines.push("permission:");
|
|
1019
|
-
lines.push(
|
|
1063
|
+
if (agent.readonly || agent.bash !== "full" || !agent.spawn) lines.push("permission:");
|
|
1064
|
+
if (agent.readonly) lines.push(" edit: deny");
|
|
1020
1065
|
if (agent.bash === "none") lines.push(" bash: deny");
|
|
1021
|
-
else if (agent.bash === "git-read")
|
|
1022
|
-
|
|
1023
|
-
const
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1066
|
+
else if (agent.bash === "git-read") {
|
|
1067
|
+
lines.push(' bash:\n "*": deny');
|
|
1068
|
+
for (const command of gitReadCommands) {
|
|
1069
|
+
lines.push(` ${yamlString(command)}: allow`, ` ${yamlString(`${command} *`)}: allow`);
|
|
1070
|
+
}
|
|
1071
|
+
const permission = objectValue(loadCanonicalDefaults(stackRoot())["opencode"]?.permission);
|
|
1072
|
+
const bash = objectValue(permission?.bash);
|
|
1073
|
+
if (bash === null) throw new Error("OpenCode: canonical Bash policy is required for git-read agents.");
|
|
1074
|
+
for (const [pattern, decision] of Object.entries(bash)) {
|
|
1075
|
+
if (decision === "deny") lines.push(` ${yamlString(pattern)}: deny`);
|
|
1076
|
+
}
|
|
1027
1077
|
}
|
|
1028
1078
|
if (!agent.spawn) lines.push(" task: deny");
|
|
1029
1079
|
}
|
|
@@ -1033,7 +1083,11 @@ ${deny}`);
|
|
|
1033
1083
|
content: `---
|
|
1034
1084
|
${lines.join("\n")}
|
|
1035
1085
|
---
|
|
1036
|
-
${agent.body}
|
|
1086
|
+
${agent.body}${agent.bash === "git-read" ? `
|
|
1087
|
+
|
|
1088
|
+
Use only these read-only Git command prefixes; put refs and paths after --end-of-options:
|
|
1089
|
+
${gitReadCommands.map((command) => `- \`${command}\``).join("\n")}
|
|
1090
|
+
` : ""}`,
|
|
1037
1091
|
kind: "agent"
|
|
1038
1092
|
}
|
|
1039
1093
|
];
|
|
@@ -1113,12 +1167,19 @@ ${agent.body}`,
|
|
|
1113
1167
|
planMainConfig(canonical, ctx) {
|
|
1114
1168
|
const file = path9.join(ctx.configDir, "opencode.json");
|
|
1115
1169
|
const { pluginsDir } = this.paths(ctx.configDir);
|
|
1116
|
-
const original =
|
|
1170
|
+
const original = readMcpConfig(file);
|
|
1117
1171
|
const contentSource = original === null || original.trim() === "" ? null : original;
|
|
1118
1172
|
const isFreshConfig = contentSource === null;
|
|
1119
1173
|
const mcpOwnership = [];
|
|
1120
1174
|
const primaryModelOwnership = [];
|
|
1121
1175
|
const content = upsertJson(contentSource, (root) => {
|
|
1176
|
+
const rawMcp = root["mcp"];
|
|
1177
|
+
if (rawMcp !== void 0 && objectValue(rawMcp) === null) {
|
|
1178
|
+
throw new Error("OpenCode: la clave 'mcp' debe ser un objeto; corr\xEDgela antes de reintentar sync.");
|
|
1179
|
+
}
|
|
1180
|
+
const existingMcp = rawMcp;
|
|
1181
|
+
const context7 = canonical.servers.context7;
|
|
1182
|
+
if (context7 !== void 0) assertCompatibleContext7(context7, existingMcp?.["context7"]);
|
|
1122
1183
|
root["$schema"] ??= "https://opencode.ai/config.json";
|
|
1123
1184
|
if (root[PRIMARY_MODEL_FIELD] === void 0) {
|
|
1124
1185
|
root[PRIMARY_MODEL_FIELD] = PRIMARY_MODEL;
|
|
@@ -1145,13 +1206,19 @@ ${agent.body}`,
|
|
|
1145
1206
|
if (isFreshConfig && defaults?.["permission"] !== void 0) {
|
|
1146
1207
|
root["permission"] = defaults["permission"];
|
|
1147
1208
|
ctx.warnings.push(
|
|
1148
|
-
"OpenCode: fresh config
|
|
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."
|
|
1149
1210
|
);
|
|
1150
1211
|
}
|
|
1151
1212
|
const mcp = root["mcp"] ??= {};
|
|
1152
1213
|
for (const [name, server] of Object.entries(canonical.servers)) {
|
|
1153
1214
|
const existing = mcp[name];
|
|
1154
1215
|
const owned = ctx.ownedMcpServers?.has(name) === true;
|
|
1216
|
+
if (name === "context7" && existing !== void 0) {
|
|
1217
|
+
if (owned && !isCanonicalContext7Server(server, existing)) {
|
|
1218
|
+
mcpOwnership.push({ server: name, owned: false });
|
|
1219
|
+
}
|
|
1220
|
+
continue;
|
|
1221
|
+
}
|
|
1155
1222
|
if (!isCanonicalMcpServerEnabled(name, server, ctx.enabledMcpServers)) {
|
|
1156
1223
|
if (owned) {
|
|
1157
1224
|
if (isManagedOptionalStdioServer(server, existing)) delete mcp[name];
|
|
@@ -1188,6 +1255,9 @@ ${agent.body}`,
|
|
|
1188
1255
|
url: server.url,
|
|
1189
1256
|
...Object.keys(headers).length > 0 ? { headers } : {}
|
|
1190
1257
|
};
|
|
1258
|
+
if (name === "context7" && existing === void 0 && !owned) {
|
|
1259
|
+
mcpOwnership.push({ server: name, owned: true });
|
|
1260
|
+
}
|
|
1191
1261
|
}
|
|
1192
1262
|
}
|
|
1193
1263
|
if (pluginsDir !== null) {
|
|
@@ -1222,7 +1292,7 @@ ${agent.body}`,
|
|
|
1222
1292
|
actions.push({ kind: "write", target: systemPromptFile, content });
|
|
1223
1293
|
}
|
|
1224
1294
|
const configFile = path9.join(ctx.configDir, "opencode.json");
|
|
1225
|
-
const config =
|
|
1295
|
+
const config = readMcpConfig(configFile);
|
|
1226
1296
|
if (config !== null) {
|
|
1227
1297
|
const mcpOwnership = [];
|
|
1228
1298
|
const primaryModelOwnership = [];
|
|
@@ -1259,9 +1329,22 @@ ${agent.body}`,
|
|
|
1259
1329
|
for (const field of managedFields) {
|
|
1260
1330
|
if (ctx.ownedPrimaryModelFields?.has(field) === true) primaryModelOwnership.push({ field, owned: false });
|
|
1261
1331
|
}
|
|
1262
|
-
const
|
|
1263
|
-
if (
|
|
1332
|
+
const rawMcpBlock = root["mcp"];
|
|
1333
|
+
if (rawMcpBlock !== void 0 && objectValue(rawMcpBlock) === null) {
|
|
1334
|
+
throw new Error("OpenCode: la clave 'mcp' debe ser un objeto; corr\xEDgela antes de reintentar uninstall.");
|
|
1335
|
+
}
|
|
1336
|
+
const mcpBlock = rawMcpBlock;
|
|
1337
|
+
if (mcpBlock !== void 0) {
|
|
1264
1338
|
for (const [name, server] of Object.entries(mcp.servers)) {
|
|
1339
|
+
if (name === "context7") {
|
|
1340
|
+
const canonical = isCanonicalContext7Server(server, mcpBlock[name]);
|
|
1341
|
+
const owned = ctx.ownedMcpServers?.has(name) === true;
|
|
1342
|
+
if (owned) {
|
|
1343
|
+
if (canonical) delete mcpBlock[name];
|
|
1344
|
+
mcpOwnership.push({ server: name, owned: false });
|
|
1345
|
+
}
|
|
1346
|
+
continue;
|
|
1347
|
+
}
|
|
1265
1348
|
if (!server.optional) {
|
|
1266
1349
|
delete mcpBlock[name];
|
|
1267
1350
|
continue;
|
|
@@ -1318,6 +1401,11 @@ ${agent.body}`,
|
|
|
1318
1401
|
import path10 from "path";
|
|
1319
1402
|
import fs7 from "fs";
|
|
1320
1403
|
import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
|
|
1404
|
+
|
|
1405
|
+
// src/lib/git-guard.ts
|
|
1406
|
+
var GIT_GUARD_SCRIPT = "block-destructive-git.cjs";
|
|
1407
|
+
|
|
1408
|
+
// src/adapters/claude-code.ts
|
|
1321
1409
|
function yamlString2(value) {
|
|
1322
1410
|
return JSON.stringify(value);
|
|
1323
1411
|
}
|
|
@@ -1331,6 +1419,47 @@ var ENGRAM_AGENT_TOOLS = [
|
|
|
1331
1419
|
function isRecord(value) {
|
|
1332
1420
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1333
1421
|
}
|
|
1422
|
+
function readMcpConfig2(file) {
|
|
1423
|
+
let content;
|
|
1424
|
+
try {
|
|
1425
|
+
content = fs7.readFileSync(file, "utf8");
|
|
1426
|
+
} catch (error) {
|
|
1427
|
+
const code = error instanceof Error && "code" in error && typeof error.code === "string" ? error.code : "UNKNOWN";
|
|
1428
|
+
if (code === "ENOENT") return null;
|
|
1429
|
+
throw new Error(`Claude Code: no se pudo leer la configuraci\xF3n MCP en ${file} (${code}).`);
|
|
1430
|
+
}
|
|
1431
|
+
if (content.trim() !== "") {
|
|
1432
|
+
try {
|
|
1433
|
+
if (!isRecord(JSON.parse(content))) throw new Error();
|
|
1434
|
+
} catch {
|
|
1435
|
+
throw new Error(`Claude Code: la configuraci\xF3n MCP en ${file} debe contener un objeto JSON v\xE1lido.`);
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
return content;
|
|
1439
|
+
}
|
|
1440
|
+
function isCompatibleContext7Server2(server, value) {
|
|
1441
|
+
if (server.transport !== "http" || typeof server.url !== "string" || !isRecord(value)) return false;
|
|
1442
|
+
return value.type === "http" && value.url === server.url;
|
|
1443
|
+
}
|
|
1444
|
+
function canonicalContext7Server2(server) {
|
|
1445
|
+
const headers = {};
|
|
1446
|
+
for (const [key, raw] of Object.entries(server.headers ?? {})) {
|
|
1447
|
+
headers[key] = /^\$\{(\w+)\}$/.test(raw) ? "" : raw;
|
|
1448
|
+
}
|
|
1449
|
+
return {
|
|
1450
|
+
type: "http",
|
|
1451
|
+
url: server.url,
|
|
1452
|
+
...Object.keys(headers).length > 0 ? { headers } : {}
|
|
1453
|
+
};
|
|
1454
|
+
}
|
|
1455
|
+
function isCanonicalContext7Server2(server, value) {
|
|
1456
|
+
return isCompatibleContext7Server2(server, value) && isDeepStrictEqual2(value, canonicalContext7Server2(server));
|
|
1457
|
+
}
|
|
1458
|
+
function assertCompatibleContext72(server, value) {
|
|
1459
|
+
if (value !== void 0 && !isCompatibleContext7Server2(server, value)) {
|
|
1460
|
+
throw new Error("Claude Code: MCP 'context7' entra en conflicto con una definici\xF3n existente (endpoint o tipo incompatible). Conserva la configuraci\xF3n y corrige el conflicto antes de reintentar.");
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1334
1463
|
function hasClaudeManualApproval(configDir) {
|
|
1335
1464
|
const content = readTextIfExists(path10.join(configDir, "settings.json"));
|
|
1336
1465
|
if (content === null) return false;
|
|
@@ -1476,11 +1605,24 @@ ${agent.body}`,
|
|
|
1476
1605
|
planMainConfig(canonical, ctx) {
|
|
1477
1606
|
const file = path10.join(path10.dirname(ctx.configDir), `${path10.basename(ctx.configDir)}.json`);
|
|
1478
1607
|
const mcpOwnership = [];
|
|
1479
|
-
const content = upsertJson(
|
|
1608
|
+
const content = upsertJson(readMcpConfig2(file), (root) => {
|
|
1609
|
+
const rawServers = root["mcpServers"];
|
|
1610
|
+
if (rawServers !== void 0 && !isRecord(rawServers)) {
|
|
1611
|
+
throw new Error("Claude Code: la clave 'mcpServers' debe ser un objeto; corr\xEDgela antes de reintentar sync.");
|
|
1612
|
+
}
|
|
1613
|
+
const existingServers = rawServers;
|
|
1614
|
+
const context7 = canonical.servers.context7;
|
|
1615
|
+
if (context7 !== void 0) assertCompatibleContext72(context7, existingServers?.["context7"]);
|
|
1480
1616
|
const servers = root["mcpServers"] ??= {};
|
|
1481
1617
|
for (const [name, server] of Object.entries(canonical.servers)) {
|
|
1482
1618
|
const existing = servers[name];
|
|
1483
1619
|
const owned = ctx.ownedMcpServers?.has(name) === true;
|
|
1620
|
+
if (name === "context7" && existing !== void 0) {
|
|
1621
|
+
if (owned && !isCanonicalContext7Server2(server, existing)) {
|
|
1622
|
+
mcpOwnership.push({ server: name, owned: false });
|
|
1623
|
+
}
|
|
1624
|
+
continue;
|
|
1625
|
+
}
|
|
1484
1626
|
if (!isCanonicalMcpServerEnabled(name, server, ctx.enabledMcpServers)) {
|
|
1485
1627
|
if (owned) {
|
|
1486
1628
|
if (isManagedOptionalStdioServer2(server, existing)) delete servers[name];
|
|
@@ -1524,6 +1666,9 @@ ${agent.body}`,
|
|
|
1524
1666
|
url: server.url,
|
|
1525
1667
|
...Object.keys(headers).length > 0 ? { headers } : {}
|
|
1526
1668
|
};
|
|
1669
|
+
if (name === "context7" && existing === void 0 && !owned) {
|
|
1670
|
+
mcpOwnership.push({ server: name, owned: true });
|
|
1671
|
+
}
|
|
1527
1672
|
}
|
|
1528
1673
|
}
|
|
1529
1674
|
});
|
|
@@ -1546,13 +1691,26 @@ ${agent.body}`,
|
|
|
1546
1691
|
}
|
|
1547
1692
|
}
|
|
1548
1693
|
const mainFile = path10.join(path10.dirname(ctx.configDir), `${path10.basename(ctx.configDir)}.json`);
|
|
1549
|
-
const main2 =
|
|
1694
|
+
const main2 = readMcpConfig2(mainFile);
|
|
1550
1695
|
if (main2 !== null) {
|
|
1551
1696
|
const mcpOwnership = [];
|
|
1552
1697
|
const content = upsertJson(main2, (root) => {
|
|
1553
|
-
const
|
|
1554
|
-
if (
|
|
1698
|
+
const rawServers = root["mcpServers"];
|
|
1699
|
+
if (rawServers === void 0) return;
|
|
1700
|
+
if (!isRecord(rawServers)) {
|
|
1701
|
+
throw new Error("Claude Code: la clave 'mcpServers' debe ser un objeto; corr\xEDgela antes de reintentar uninstall.");
|
|
1702
|
+
}
|
|
1703
|
+
const servers = rawServers;
|
|
1555
1704
|
for (const [name, server] of Object.entries(mcp.servers)) {
|
|
1705
|
+
if (name === "context7") {
|
|
1706
|
+
const canonical = isCanonicalContext7Server2(server, servers[name]);
|
|
1707
|
+
const owned = ctx.ownedMcpServers?.has(name) === true;
|
|
1708
|
+
if (owned) {
|
|
1709
|
+
if (canonical) delete servers[name];
|
|
1710
|
+
mcpOwnership.push({ server: name, owned: false });
|
|
1711
|
+
}
|
|
1712
|
+
continue;
|
|
1713
|
+
}
|
|
1556
1714
|
if (!server.optional) {
|
|
1557
1715
|
delete servers[name];
|
|
1558
1716
|
continue;
|
|
@@ -1576,6 +1734,15 @@ import fs8 from "fs";
|
|
|
1576
1734
|
function tomlString(value) {
|
|
1577
1735
|
return JSON.stringify(value);
|
|
1578
1736
|
}
|
|
1737
|
+
function readMcpConfig3(file) {
|
|
1738
|
+
try {
|
|
1739
|
+
return fs8.readFileSync(file, "utf8");
|
|
1740
|
+
} catch (error) {
|
|
1741
|
+
const code = error instanceof Error && "code" in error && typeof error.code === "string" ? error.code : "UNKNOWN";
|
|
1742
|
+
if (code === "ENOENT") return null;
|
|
1743
|
+
throw new Error(`Codex: no se pudo leer la configuraci\xF3n MCP en ${file} (${code}).`);
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1579
1746
|
var PRIMARY_MODEL2 = '"gpt-5.6-sol"';
|
|
1580
1747
|
var PRIMARY_CONTEXT_WINDOW = "872000";
|
|
1581
1748
|
var PRIMARY_MODEL_FIELD2 = "model";
|
|
@@ -1612,6 +1779,67 @@ var CODEX_JSON_STRING_ARRAY = String.raw`\[(?:\s*${CODEX_JSON_STRING}(?:\s*,\s*$
|
|
|
1612
1779
|
var CODEX_KEY = String.raw`(?:[A-Za-z0-9_-]+|${CODEX_JSON_STRING})`;
|
|
1613
1780
|
var CODEX_ASSIGNMENT = new RegExp(String.raw`^\s*(${CODEX_KEY})\s*=\s*(${CODEX_JSON_STRING_ARRAY}|${CODEX_JSON_VALUE})\s*(?:#.*)?$`);
|
|
1614
1781
|
var CODEX_HEADER = new RegExp(String.raw`^\[\s*(${CODEX_KEY}(?:\s*\.\s*${CODEX_KEY})*)\s*\]\s*(?:#.*)?$`);
|
|
1782
|
+
function tomlAssignment(section, key) {
|
|
1783
|
+
const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1784
|
+
const keyPattern = new RegExp(`^\\s*(?:${escaped}|"${escaped}"|'${escaped}')\\s*=`);
|
|
1785
|
+
const lines = section.split(/\r?\n/);
|
|
1786
|
+
const mask = multilineStringMask(lines);
|
|
1787
|
+
for (const [index, line] of lines.entries()) {
|
|
1788
|
+
if (mask[index] || !keyPattern.test(line)) continue;
|
|
1789
|
+
const match = CODEX_ASSIGNMENT.exec(line);
|
|
1790
|
+
if (match) return { present: true, raw: match[2] };
|
|
1791
|
+
const equals = line.indexOf("=");
|
|
1792
|
+
const raw = equals === -1 ? void 0 : line.slice(equals + 1).trim().replace(/\s+#.*$/, "");
|
|
1793
|
+
return { present: true, ...raw?.startsWith("'") && raw.endsWith("'") ? { raw } : {} };
|
|
1794
|
+
}
|
|
1795
|
+
return { present: false };
|
|
1796
|
+
}
|
|
1797
|
+
function parseTomlString(raw) {
|
|
1798
|
+
if (raw === void 0) return void 0;
|
|
1799
|
+
const value = raw.trim();
|
|
1800
|
+
if (value.startsWith('"')) {
|
|
1801
|
+
try {
|
|
1802
|
+
const parsed = JSON.parse(value);
|
|
1803
|
+
return typeof parsed === "string" ? parsed : void 0;
|
|
1804
|
+
} catch {
|
|
1805
|
+
return void 0;
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
if (value.length >= 2 && value.startsWith("'") && value.endsWith("'")) {
|
|
1809
|
+
return value.slice(1, -1);
|
|
1810
|
+
}
|
|
1811
|
+
return void 0;
|
|
1812
|
+
}
|
|
1813
|
+
function context7HttpSection(server) {
|
|
1814
|
+
const body = [`url = ${tomlString(server.url)}`];
|
|
1815
|
+
const envHeaders = [];
|
|
1816
|
+
const literalHeaders = [];
|
|
1817
|
+
for (const [key, raw] of Object.entries(server.headers ?? {})) {
|
|
1818
|
+
const envRef = /^\$\{(\w+)\}$/.exec(raw);
|
|
1819
|
+
if (envRef) envHeaders.push(`${tomlString(key)} = ${tomlString(envRef[1])}`);
|
|
1820
|
+
else literalHeaders.push(`${tomlString(key)} = ${tomlString(raw)}`);
|
|
1821
|
+
}
|
|
1822
|
+
if (literalHeaders.length > 0) body.push(`http_headers = { ${literalHeaders.join(", ")} }`);
|
|
1823
|
+
if (envHeaders.length > 0) body.push(`env_http_headers = { ${envHeaders.join(", ")} }`);
|
|
1824
|
+
return body.join("\n");
|
|
1825
|
+
}
|
|
1826
|
+
function isCompatibleContext7Server3(server, section) {
|
|
1827
|
+
if (server.transport !== "http" || typeof server.url !== "string" || section === null) return false;
|
|
1828
|
+
const url = tomlAssignment(section, "url");
|
|
1829
|
+
const command = tomlAssignment(section, "command");
|
|
1830
|
+
const type = tomlAssignment(section, "type");
|
|
1831
|
+
const enabled = tomlAssignment(section, "enabled");
|
|
1832
|
+
const declaredType = type.present ? parseTomlString(type.raw) : "http";
|
|
1833
|
+
return url.present && parseTomlString(url.raw) === server.url && !command.present && declaredType === "http" && (!enabled.present || enabled.raw?.trim() === "true");
|
|
1834
|
+
}
|
|
1835
|
+
function isCanonicalContext7Server3(server, section, config) {
|
|
1836
|
+
return isCompatibleContext7Server3(server, section) && section.trim() === context7HttpSection(server) && !hasTomlChildSection(config, "mcp_servers.context7");
|
|
1837
|
+
}
|
|
1838
|
+
function assertCompatibleContext73(server, section, config) {
|
|
1839
|
+
if (section === null && hasTomlChildSection(config, "mcp_servers.context7") || section !== null && !isCompatibleContext7Server3(server, section)) {
|
|
1840
|
+
throw new Error("Codex: MCP 'context7' entra en conflicto con una definici\xF3n existente (endpoint, tipo o estado nativo incompatible). Conserva la configuraci\xF3n y corrige el conflicto antes de reintentar.");
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1615
1843
|
var CODEX_PERMISSION_HEADERS = {
|
|
1616
1844
|
base: "permissions.jorgex-read-anywhere",
|
|
1617
1845
|
filesystem: "permissions.jorgex-read-anywhere.filesystem",
|
|
@@ -1851,11 +2079,15 @@ ${body}`
|
|
|
1851
2079
|
},
|
|
1852
2080
|
planMainConfig(canonical, ctx) {
|
|
1853
2081
|
const file = path11.join(ctx.configDir, "config.toml");
|
|
1854
|
-
const original =
|
|
2082
|
+
const original = readMcpConfig3(file);
|
|
1855
2083
|
const contentSource = original === null || original.trim() === "" ? null : original;
|
|
1856
2084
|
let content = contentSource;
|
|
1857
2085
|
const mcpOwnership = [];
|
|
1858
2086
|
const primaryModelOwnership = [];
|
|
2087
|
+
const context7 = canonical.servers.context7;
|
|
2088
|
+
if (context7 !== void 0) {
|
|
2089
|
+
assertCompatibleContext73(context7, readTomlSection(contentSource, "mcp_servers.context7"), contentSource);
|
|
2090
|
+
}
|
|
1859
2091
|
if (contentSource === null) {
|
|
1860
2092
|
const defaults = loadCanonicalDefaults(ctx.stackDir)["codex"] ?? {};
|
|
1861
2093
|
for (const [key, value] of Object.entries(defaults)) {
|
|
@@ -1899,6 +2131,12 @@ ${body}`
|
|
|
1899
2131
|
const section = `mcp_servers.${name}`;
|
|
1900
2132
|
const existing = readTomlSection(content, section);
|
|
1901
2133
|
const owned = ctx.ownedMcpServers?.has(name) === true;
|
|
2134
|
+
if (name === "context7" && existing !== null) {
|
|
2135
|
+
if (owned && !isCanonicalContext7Server3(server, existing, content)) {
|
|
2136
|
+
mcpOwnership.push({ server: name, owned: false });
|
|
2137
|
+
}
|
|
2138
|
+
continue;
|
|
2139
|
+
}
|
|
1902
2140
|
if (!isCanonicalMcpServerEnabled(name, server, ctx.enabledMcpServers)) {
|
|
1903
2141
|
if (owned) {
|
|
1904
2142
|
if (isManagedOptionalStdioServer3(server, existing)) content = removeTomlSection(content, section);
|
|
@@ -1947,6 +2185,9 @@ args = [${(server.args ?? []).map(tomlString).join(", ")}]`);
|
|
|
1947
2185
|
if (literalPairs.length > 0) body.push(`http_headers = { ${literalPairs.join(", ")} }`);
|
|
1948
2186
|
if (refPairs.length > 0) body.push(`env_http_headers = { ${refPairs.join(", ")} }`);
|
|
1949
2187
|
content = upsertTomlSection(content, section, body.join("\n"));
|
|
2188
|
+
if (name === "context7" && existing === null && !owned) {
|
|
2189
|
+
mcpOwnership.push({ server: name, owned: true });
|
|
2190
|
+
}
|
|
1950
2191
|
}
|
|
1951
2192
|
}
|
|
1952
2193
|
if (content === null) return [];
|
|
@@ -1968,7 +2209,7 @@ args = [${(server.args ?? []).map(tomlString).join(", ")}]`);
|
|
|
1968
2209
|
actions.push({ kind: "write", target: systemPromptFile, content });
|
|
1969
2210
|
}
|
|
1970
2211
|
const configFile = path11.join(ctx.configDir, "config.toml");
|
|
1971
|
-
const config =
|
|
2212
|
+
const config = readMcpConfig3(configFile);
|
|
1972
2213
|
if (config !== null) {
|
|
1973
2214
|
let content = config;
|
|
1974
2215
|
const primaryModelOwnership = [];
|
|
@@ -1983,6 +2224,18 @@ args = [${(server.args ?? []).map(tomlString).join(", ")}]`);
|
|
|
1983
2224
|
const mcpOwnership = [];
|
|
1984
2225
|
for (const [name, server] of Object.entries(mcp.servers)) {
|
|
1985
2226
|
const section = `mcp_servers.${name}`;
|
|
2227
|
+
if (name === "context7") {
|
|
2228
|
+
const current = readTomlSection(content, section);
|
|
2229
|
+
const canonical = isCanonicalContext7Server3(server, current, content);
|
|
2230
|
+
const owned = ctx.ownedMcpServers?.has(name) === true;
|
|
2231
|
+
if (owned) {
|
|
2232
|
+
if (canonical) {
|
|
2233
|
+
content = removeTomlSection(content, section);
|
|
2234
|
+
}
|
|
2235
|
+
mcpOwnership.push({ server: name, owned: false });
|
|
2236
|
+
}
|
|
2237
|
+
continue;
|
|
2238
|
+
}
|
|
1986
2239
|
if (!server.optional) {
|
|
1987
2240
|
content = removeTomlSection(content, section);
|
|
1988
2241
|
continue;
|
|
@@ -2772,7 +3025,7 @@ function enabledMcpServers(runtime, explicitDevtoolsEnabled, useBrowserPreferenc
|
|
|
2772
3025
|
function ownedMcpServers(runtime, useBrowserPreferences = true) {
|
|
2773
3026
|
if (!useBrowserPreferences) return /* @__PURE__ */ new Set();
|
|
2774
3027
|
const file = devtoolsMcpPreferenceFile();
|
|
2775
|
-
return
|
|
3028
|
+
return new Set([DEVTOOLS_MCP_SERVER, "context7"].filter((server) => loadDevtoolsMcpOwnership(file, runtime, server)));
|
|
2776
3029
|
}
|
|
2777
3030
|
function persistConfigurationOwnershipChanges(runtime, configDir, plan) {
|
|
2778
3031
|
const latest = /* @__PURE__ */ new Map();
|
|
@@ -2804,17 +3057,29 @@ function makeContext(adapter, configDir, mode = DEFAULT_INSTALL_MODE_PREFERENCE,
|
|
|
2804
3057
|
ownedPrimaryModelFields: useBrowserPreferences ? loadPrimaryModelOwnership(primaryModelOwnershipFile(), adapter.id, configDir) : /* @__PURE__ */ new Set()
|
|
2805
3058
|
};
|
|
2806
3059
|
}
|
|
2807
|
-
function
|
|
3060
|
+
function buildContentPlan(adapter, ctx) {
|
|
2808
3061
|
return [
|
|
2809
3062
|
...planSystemPrompt(adapter, ctx),
|
|
2810
3063
|
...planAgents(adapter, ctx),
|
|
2811
3064
|
...planSkills(adapter, ctx),
|
|
2812
3065
|
...planCommands(adapter, ctx),
|
|
2813
3066
|
...planHooks(adapter, ctx),
|
|
2814
|
-
...planMcp(adapter, ctx),
|
|
2815
3067
|
...planPlugins(adapter, ctx)
|
|
2816
3068
|
];
|
|
2817
3069
|
}
|
|
3070
|
+
function buildPlan(adapter, ctx) {
|
|
3071
|
+
return [...planMcp(adapter, ctx), ...buildContentPlan(adapter, ctx)];
|
|
3072
|
+
}
|
|
3073
|
+
function preflightSelectedMcpConfigs(runtimes, targetDir) {
|
|
3074
|
+
for (const id of runtimes) {
|
|
3075
|
+
const adapter = ADAPTERS[id];
|
|
3076
|
+
if (!adapter) continue;
|
|
3077
|
+
const detection = adapter.detect();
|
|
3078
|
+
if (targetDir === void 0 && !detection.installed) continue;
|
|
3079
|
+
const ctx = makeContext(adapter, targetDir ?? detection.configDir, void 0, targetDir === void 0);
|
|
3080
|
+
if (ctx) planMcp(adapter, ctx);
|
|
3081
|
+
}
|
|
3082
|
+
}
|
|
2818
3083
|
function diffPlan(plan) {
|
|
2819
3084
|
return plan.map((action) => {
|
|
2820
3085
|
if (action.kind === "write") {
|
|
@@ -2873,6 +3138,13 @@ async function runInstall(opts) {
|
|
|
2873
3138
|
p.log.error(error instanceof Error ? error.message : String(error));
|
|
2874
3139
|
return 1;
|
|
2875
3140
|
}
|
|
3141
|
+
const modelMap = opts.runtimes.length > 0 ? loadModelMap() : {};
|
|
3142
|
+
try {
|
|
3143
|
+
preflightSelectedMcpConfigs(opts.runtimes, opts.targetDir);
|
|
3144
|
+
} catch (error) {
|
|
3145
|
+
p.log.error(error instanceof Error ? error.message : String(error));
|
|
3146
|
+
return 1;
|
|
3147
|
+
}
|
|
2876
3148
|
let writingStyle;
|
|
2877
3149
|
let preparedStyle;
|
|
2878
3150
|
try {
|
|
@@ -2902,7 +3174,6 @@ async function runInstall(opts) {
|
|
|
2902
3174
|
const playwrightCapability = opts.dryRun || !useManifest ? void 0 : opts.playwrightCapability ?? (shouldInspectPlaywright ? inspectPlaywrightCapability() : void 0);
|
|
2903
3175
|
const effectivePlaywright = playwrightCapability?.effective;
|
|
2904
3176
|
const plannedPlaywright = effectivePlaywright ?? (toolPlan !== null && toolPlan.actions.length > 0 ? false : void 0);
|
|
2905
|
-
const modelMap = hasFileRuntimes ? loadModelMap() : {};
|
|
2906
3177
|
if (preparedStyle !== void 0) {
|
|
2907
3178
|
try {
|
|
2908
3179
|
p.log.info(`Estilo de escritura: ${preparedStyle.sourcePath}${opts.dryRun ? " (instalaci\xF3n prevista; sin escrituras)" : ""}.`);
|
|
@@ -2967,7 +3238,7 @@ async function runInstall(opts) {
|
|
|
2967
3238
|
let diff = diffPlan(plan);
|
|
2968
3239
|
let creates = diff.filter((d) => d.status === "create");
|
|
2969
3240
|
let updates = diff.filter((d) => d.status === "update");
|
|
2970
|
-
let changes =
|
|
3241
|
+
let changes = diff.filter((change) => change.status !== "unchanged");
|
|
2971
3242
|
const prevManifest = useManifest ? readManifest().runtimes[id] : void 0;
|
|
2972
3243
|
const orphans = canOrphan && prevManifest ? findOrphans(prevManifest.owned, current.targets) : [];
|
|
2973
3244
|
p.log.step(`${adapter.name} \u2192 ${configDir}`);
|
|
@@ -3014,7 +3285,7 @@ async function runInstall(opts) {
|
|
|
3014
3285
|
diff = diffPlan(plan);
|
|
3015
3286
|
creates = diff.filter((d) => d.status === "create");
|
|
3016
3287
|
updates = diff.filter((d) => d.status === "update");
|
|
3017
|
-
changes =
|
|
3288
|
+
changes = diff.filter((change) => change.status !== "unchanged");
|
|
3018
3289
|
}
|
|
3019
3290
|
const backup = useManifest ? createBackup([...updates.map((c) => c.action.target), ...orphans], `install-${id}`) : null;
|
|
3020
3291
|
if (backup) p.log.info(`Backup: ${backup.id} (${backup.files.length} archivos)`);
|
|
@@ -3185,14 +3456,11 @@ var piAdapter = {
|
|
|
3185
3456
|
injectEngramProtocol() {
|
|
3186
3457
|
return true;
|
|
3187
3458
|
},
|
|
3188
|
-
//
|
|
3459
|
+
// La guía Context7 se habilita al adoptar su registro HTTP en Pi.
|
|
3189
3460
|
adaptSystemPromptSections(sections) {
|
|
3190
|
-
const
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
"system-prompt": [legacy["system-prompt"], context7].filter(Boolean).join("\n\n"),
|
|
3194
|
-
browser: [playwright, devtools].filter(Boolean).join("\n\n")
|
|
3195
|
-
};
|
|
3461
|
+
const modular = { ...sections };
|
|
3462
|
+
delete modular.context7;
|
|
3463
|
+
return modular;
|
|
3196
3464
|
}
|
|
3197
3465
|
};
|
|
3198
3466
|
|
|
@@ -4275,7 +4543,7 @@ async function runUninstall(opts) {
|
|
|
4275
4543
|
if (!detection.installed) continue;
|
|
4276
4544
|
const keepCtx = makeContext(keep, detection.configDir);
|
|
4277
4545
|
if (!keepCtx) continue;
|
|
4278
|
-
for (const action of
|
|
4546
|
+
for (const action of buildContentPlan(keep, keepCtx)) retained.add(path26.resolve(action.target));
|
|
4279
4547
|
}
|
|
4280
4548
|
}
|
|
4281
4549
|
for (const id of opts.runtimes) {
|
|
@@ -4293,13 +4561,20 @@ async function runUninstall(opts) {
|
|
|
4293
4561
|
const ctx = makeContext(adapter, configDir, void 0, useBrowserPreferences);
|
|
4294
4562
|
if (!ctx) continue;
|
|
4295
4563
|
ctx.preserveEngram = !removeEngram;
|
|
4296
|
-
|
|
4564
|
+
let unmerge;
|
|
4565
|
+
try {
|
|
4566
|
+
unmerge = adapter.planUnmerge(mcpForUnmerge, hooks, ctx);
|
|
4567
|
+
} catch (error) {
|
|
4568
|
+
p2.log.error(`${adapter.name}: no se pudo planificar la limpieza en ${configDir} \u2014 ${error instanceof Error ? error.message : String(error)}.`);
|
|
4569
|
+
exitCode = 1;
|
|
4570
|
+
continue;
|
|
4571
|
+
}
|
|
4297
4572
|
const mergedTargets = new Set(unmerge.map((a) => path26.resolve(a.target)));
|
|
4298
4573
|
const usingRealConfig = opts.targetDir === void 0;
|
|
4299
4574
|
const prevOwned = usingRealConfig ? readManifest().runtimes[id]?.owned ?? [] : [];
|
|
4300
4575
|
const pruneRoot = usingRealConfig ? HOME : path26.dirname(configDir);
|
|
4301
4576
|
const planTargets = [
|
|
4302
|
-
.../* @__PURE__ */ new Set([...
|
|
4577
|
+
.../* @__PURE__ */ new Set([...buildContentPlan(adapter, ctx).map((a) => path26.resolve(a.target)), ...prevOwned.map((t) => path26.resolve(t))])
|
|
4303
4578
|
].filter((t) => !mergedTargets.has(t) && fs19.existsSync(t));
|
|
4304
4579
|
const deleteTargets = planTargets.filter(
|
|
4305
4580
|
(t) => !retained.has(t) && !(ctx.preserveEngram && path26.basename(t) === "engram.ts") && isContainedIn(t, pruneRoot)
|
|
@@ -4569,7 +4844,7 @@ async function runDoctor(options = {}) {
|
|
|
4569
4844
|
pending = diffPlan(buildPlan(adapter, ctx)).filter((d) => d.status !== "unchanged").length;
|
|
4570
4845
|
} catch (err) {
|
|
4571
4846
|
p3.log.error(
|
|
4572
|
-
`${adapter.name}:
|
|
4847
|
+
`${adapter.name}: configuraci\xF3n incompatible o ilegible en ${detection.configDir} \u2014 ${err instanceof Error ? err.message : err}`
|
|
4573
4848
|
);
|
|
4574
4849
|
problems++;
|
|
4575
4850
|
continue;
|
|
@@ -5856,16 +6131,16 @@ import { createHash as createHash2 } from "crypto";
|
|
|
5856
6131
|
var pi_runtime_pin_default = {
|
|
5857
6132
|
package: {
|
|
5858
6133
|
name: "jorgex-pi",
|
|
5859
|
-
version: "0.8.
|
|
5860
|
-
source: "npm:jorgex-pi@0.8.
|
|
6134
|
+
version: "0.8.20",
|
|
6135
|
+
source: "npm:jorgex-pi@0.8.20"
|
|
5861
6136
|
},
|
|
5862
6137
|
provenance: {
|
|
5863
|
-
commit: "
|
|
6138
|
+
commit: "c4d021382cd3b752eaa396044753ac7533b6645d"
|
|
5864
6139
|
},
|
|
5865
6140
|
tarball: {
|
|
5866
|
-
bytes:
|
|
5867
|
-
sha256: "
|
|
5868
|
-
sha512: "
|
|
6141
|
+
bytes: 89127646,
|
|
6142
|
+
sha256: "c7bbb0dc587b10e8e04174589cc519ad7576d2cbbceb5d2d20614d313e1ee884",
|
|
6143
|
+
sha512: "189480a4d812048b65466156479d1d89f7fa7044471ff6a076b710c0014b0e50eaa0261d7a2a8381a5e71a598be61fe2ed714b6b1dacd5a5f955b59a412009c0"
|
|
5869
6144
|
}
|
|
5870
6145
|
};
|
|
5871
6146
|
|
|
@@ -5880,6 +6155,7 @@ var PI_RUNTIME_CANDIDATE = {
|
|
|
5880
6155
|
capabilities: [
|
|
5881
6156
|
"foundation-contract-v1",
|
|
5882
6157
|
"stack-snapshot-v2",
|
|
6158
|
+
"modular-system-prompts-v1",
|
|
5883
6159
|
"runtime-agents-v1",
|
|
5884
6160
|
"permission-gated-tools-v1",
|
|
5885
6161
|
"structured-questions-v1",
|
|
@@ -5887,6 +6163,7 @@ var PI_RUNTIME_CANDIDATE = {
|
|
|
5887
6163
|
"goal-continuation-v1",
|
|
5888
6164
|
"mcp-adapter-v1",
|
|
5889
6165
|
"engram-runtime-tools-v1",
|
|
6166
|
+
"context7-http-v1",
|
|
5890
6167
|
"chrome-devtools-handoff-v1",
|
|
5891
6168
|
"playwright-handoff-v1",
|
|
5892
6169
|
"runner-json-v1",
|
|
@@ -7573,6 +7850,7 @@ Flags disponibles: jorgex-stack --help`
|
|
|
7573
7850
|
p6.intro(`jorgex-stack ${command}${flags.dryRun ? " (dry-run)" : ""}`);
|
|
7574
7851
|
try {
|
|
7575
7852
|
assertSelectedPromptFiles(runtimes, flags.targetDir);
|
|
7853
|
+
preflightSelectedMcpConfigs(fileRuntimes, flags.targetDir);
|
|
7576
7854
|
const writingStyle = prepareWritingStyle(resolveWritingStyleFile({ targetDir: flags.targetDir }), { rootDir: flags.targetDir });
|
|
7577
7855
|
if (flags.targetDir === void 0) {
|
|
7578
7856
|
const errors = browserPreferenceErrors();
|