jorgex-stack 1.9.41 → 1.9.43
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 +5 -3
- package/dist/cli.js +333 -38
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -114,7 +114,7 @@ Programmatic mode does **not** provide:
|
|
|
114
114
|
|
|
115
115
|
### Pi runtime
|
|
116
116
|
|
|
117
|
-
El canon de Stack y el paquete Pi adoptado mantienen una snapshot de 17 skills
|
|
117
|
+
El canon de Stack y el paquete Pi adoptado mantienen una snapshot de 17 árboles de skills con 89 archivos de skill. La identidad, procedencia e integridad del paquete adoptado son autoritativas en `src/lib/pi-runtime-pin.json`.
|
|
118
118
|
|
|
119
119
|
Pi combines the frozen **snapshot v2** package with a Stack-owned shared projection. The version references that follow describe historical Stack/Pi transitions, not the current pin. The current pin, package integrity and lifecycle are maintained in [docs/references/pi-runtime.md](docs/references/pi-runtime.md) and `src/lib/pi-runtime-pin.json`; this README does not imply a future release.
|
|
120
120
|
|
|
@@ -128,7 +128,9 @@ pnpm dlx jorgex-stack@1.9.7 sync --agents pi
|
|
|
128
128
|
pnpm dlx jorgex-stack@1.9.7 uninstall --agents pi
|
|
129
129
|
```
|
|
130
130
|
|
|
131
|
-
Stack downloads the frozen registry tarball, verifies its exact size plus SHA-256/SHA-512, backs up Pi's `settings.json`, and only then asks Pi to install that local file. The exact artifact and integrity values are authoritative in `src/lib/pi-runtime-pin.json`, while the lifecycle remains authoritative in `src/lib/pi-runtime.ts`. Pi's own package-manager invocation is the narrow runtime exception to the repository's pnpm-only rule; the Stack lifecycle never launches npm directly. After the package is healthy, Stack projects the shared resources into Pi: marked `jorgex:system-prompt` and `jorgex:engram-protocol` sections in `~/.pi/agent/AGENTS.md`, canonical skills under `~/.agents/skills`, and `~/.pi/agent/prompts/lean-audit.md`. Pi 0.8.
|
|
131
|
+
Stack downloads the frozen registry tarball, verifies its exact size plus SHA-256/SHA-512, backs up Pi's `settings.json`, and only then asks Pi to install that local file. The exact artifact and integrity values are authoritative in `src/lib/pi-runtime-pin.json`, while the lifecycle remains authoritative in `src/lib/pi-runtime.ts`. Pi's own package-manager invocation is the narrow runtime exception to the repository's pnpm-only rule; the Stack lifecycle never launches npm directly. After the package is healthy, Stack projects the shared resources into Pi: marked `jorgex:system-prompt` and `jorgex:engram-protocol` sections in `~/.pi/agent/AGENTS.md`, canonical skills under `~/.agents/skills`, and `~/.pi/agent/prompts/lean-audit.md`. Pi 0.8.21 registers Context7 through an isolated in-memory HTTP bridge during bootstrap; `available` means configuration permits registration and does not imply an HTTP handshake. A conflict preserves the existing MCP file and blocks managed activation. No MCP configuration or credentials are written. For the file runtimes, Context7, Playwright and DevTools use independent managed sections. The global Playwright package and Chromium cache are shared by the machine; `--playwright-runtimes` controls which runtime receives the guide. The adopted Pi package implements and tests `playwright-handoff-v1` through `PI_CODING_AGENT_DIR/jorgex-pi/playwright.v1.json`. Historical package entries do not describe the current pin. Filters are applied only after this projection exists, so the package does not duplicate shared resources. Package ownership is recorded separately in `~/.jorgex-stack/pi-receipt.json`; projection ownership is recorded in `~/.jorgex-stack/pi-projection-receipt.json`. Package receipts reject manual, duplicate, divergent, partial, corrupt, copied-to-another-scope, or unknown-history state. Projection cleanup requires an exact scope-bound ownership receipt; DevTools conflicts preserve the handoff for review.
|
|
132
|
+
|
|
133
|
+
The static `AGENTS.md` projected by Stack does not include Context7; Pi's native bootstrap adds that section after registering the isolated bridge. Pi never writes, owns or removes MCP files. In Claude Code, Codex and OpenCode, a compatible existing Context7 entry is preserved and removed only with explicit canonical Stack ownership.
|
|
132
134
|
|
|
133
135
|
Historically, the published Pi 0.8.0 direct-package snapshot added `work-audit`: the snapshot grew from **17 to 18 skill trees** (96 to 97 files), and the active runtime allowlist grew from **16 to 17 skills**. The historical Pi 0.8.5 snapshot reference records 18 skill trees and 98 files; it does not describe the adopted pin. Reference F2-A is included while the private F1 skills remain preserved. `playwright-cli` remains in the snapshot but inactive because browser automation is a separate opt-in integration.
|
|
134
136
|
|
|
@@ -190,7 +192,7 @@ Daily operation:
|
|
|
190
192
|
- `doctor` reports the Playwright CLI state (`disabled`, `healthy`, `missing:package`, `missing:browser`, `unreadable`, `broken`, `outdated`) and, when the opt-in package and browser cache are present, verifies one local headless Chromium launch against `about:blank`; it does not open external sites or repair state. An `unreadable` browser cache includes its exact path and filesystem error code so permissions or another local cause can be investigated. If either preference file is corrupt, doctor prints the exact path and the remedy (`Corrige o borra ese archivo antes de reintentar`) before any other browser check; in that case `install`/`uninstall`/`update`/`update --check` will abort with exit 1 until the file is fixed, so the corruption cannot be reconciled destructively.
|
|
191
193
|
- `update --check` only inspects Playwright CLI when its preference is `enabled` (a binary appearing in `PATH` is not consent). It compares the installed version against the approved pin `0.1.18` — it does not consult npm latest, and a Playwright CLI binary-only update does not require `sync` afterwards.
|
|
192
194
|
- `uninstall` preserves the global `@playwright/cli` package and all browser data by default; `--remove-playwright` removes the package only (never the browser cache, profiles, cookies, storage state, traces, screenshots or videos). If `pnpm remove --global @playwright/cli` exits non-zero, `uninstall` reports the failure instead of a success outro. For DevTools MCP, ownership is released only after the corresponding unmerge is applied; if no unmerge action is written, the ownership marker is preserved for a later retry.
|
|
193
|
-
- `install`/`sync` also inject (and `disable`/`uninstall` remove) managed capability sections in `AGENTS.md` (OpenCode, Codex) or `CLAUDE.md` (Claude Code). File runtimes keep `jorgex:playwright`, `jorgex:chrome-devtools` and `jorgex:context7` independent; Pi 0.8.
|
|
195
|
+
- `install`/`sync` also inject (and `disable`/`uninstall` remove) managed capability sections in `AGENTS.md` (OpenCode, Codex) or `CLAUDE.md` (Claude Code). File runtimes keep `jorgex:playwright`, `jorgex:chrome-devtools` and `jorgex:context7` independent; Pi 0.8.21 projects the Context7 section after registering its isolated HTTP bridge and keeps the legacy `jorgex:browser` format for migration. User content outside managed markers is preserved. `--target-dir` never reads the real preferences, so by default no section is emitted in target-dir runs — but explicit flags like `--devtools` simulate the MCP entry (and its DevTools block) inside the temp target without touching the real global state, and `install --dry-run --playwright` projects the Playwright section into the plan preview without installing anything. If reconciliation detects a partial or ambiguous system-prompt state, the CLI exits non-zero and recommends repairing it with `sync` or a repeated install; no write is attempted before marker validation. See [docs/references/browser-automation.md](docs/references/browser-automation.md) §2.7 for the full lifecycle.
|
|
194
196
|
|
|
195
197
|
### Update: Interactive Flow
|
|
196
198
|
|
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";
|
|
@@ -916,6 +922,24 @@ var gitReadCommands = [
|
|
|
916
922
|
function yamlString(value) {
|
|
917
923
|
return JSON.stringify(value);
|
|
918
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
|
+
}
|
|
919
943
|
function isManagedOptionalStdioServer(server, value) {
|
|
920
944
|
if (!server.optional || server.transport !== "stdio" || value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
921
945
|
return false;
|
|
@@ -936,6 +960,31 @@ var PRIMARY_LIMIT_PREFIX = `provider.openai.models.${PRIMARY_MODEL_ID}.limit`;
|
|
|
936
960
|
function objectValue(value) {
|
|
937
961
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
938
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
|
+
}
|
|
939
988
|
function ensureObject(parent, key, fieldPath) {
|
|
940
989
|
if (parent[key] === void 0) parent[key] = {};
|
|
941
990
|
const value = objectValue(parent[key]);
|
|
@@ -1118,12 +1167,19 @@ ${gitReadCommands.map((command) => `- \`${command}\``).join("\n")}
|
|
|
1118
1167
|
planMainConfig(canonical, ctx) {
|
|
1119
1168
|
const file = path9.join(ctx.configDir, "opencode.json");
|
|
1120
1169
|
const { pluginsDir } = this.paths(ctx.configDir);
|
|
1121
|
-
const original =
|
|
1170
|
+
const original = readMcpConfig(file);
|
|
1122
1171
|
const contentSource = original === null || original.trim() === "" ? null : original;
|
|
1123
1172
|
const isFreshConfig = contentSource === null;
|
|
1124
1173
|
const mcpOwnership = [];
|
|
1125
1174
|
const primaryModelOwnership = [];
|
|
1126
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"]);
|
|
1127
1183
|
root["$schema"] ??= "https://opencode.ai/config.json";
|
|
1128
1184
|
if (root[PRIMARY_MODEL_FIELD] === void 0) {
|
|
1129
1185
|
root[PRIMARY_MODEL_FIELD] = PRIMARY_MODEL;
|
|
@@ -1157,6 +1213,12 @@ ${gitReadCommands.map((command) => `- \`${command}\``).join("\n")}
|
|
|
1157
1213
|
for (const [name, server] of Object.entries(canonical.servers)) {
|
|
1158
1214
|
const existing = mcp[name];
|
|
1159
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
|
+
}
|
|
1160
1222
|
if (!isCanonicalMcpServerEnabled(name, server, ctx.enabledMcpServers)) {
|
|
1161
1223
|
if (owned) {
|
|
1162
1224
|
if (isManagedOptionalStdioServer(server, existing)) delete mcp[name];
|
|
@@ -1193,6 +1255,9 @@ ${gitReadCommands.map((command) => `- \`${command}\``).join("\n")}
|
|
|
1193
1255
|
url: server.url,
|
|
1194
1256
|
...Object.keys(headers).length > 0 ? { headers } : {}
|
|
1195
1257
|
};
|
|
1258
|
+
if (name === "context7" && existing === void 0 && !owned) {
|
|
1259
|
+
mcpOwnership.push({ server: name, owned: true });
|
|
1260
|
+
}
|
|
1196
1261
|
}
|
|
1197
1262
|
}
|
|
1198
1263
|
if (pluginsDir !== null) {
|
|
@@ -1227,7 +1292,7 @@ ${gitReadCommands.map((command) => `- \`${command}\``).join("\n")}
|
|
|
1227
1292
|
actions.push({ kind: "write", target: systemPromptFile, content });
|
|
1228
1293
|
}
|
|
1229
1294
|
const configFile = path9.join(ctx.configDir, "opencode.json");
|
|
1230
|
-
const config =
|
|
1295
|
+
const config = readMcpConfig(configFile);
|
|
1231
1296
|
if (config !== null) {
|
|
1232
1297
|
const mcpOwnership = [];
|
|
1233
1298
|
const primaryModelOwnership = [];
|
|
@@ -1264,9 +1329,22 @@ ${gitReadCommands.map((command) => `- \`${command}\``).join("\n")}
|
|
|
1264
1329
|
for (const field of managedFields) {
|
|
1265
1330
|
if (ctx.ownedPrimaryModelFields?.has(field) === true) primaryModelOwnership.push({ field, owned: false });
|
|
1266
1331
|
}
|
|
1267
|
-
const
|
|
1268
|
-
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) {
|
|
1269
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
|
+
}
|
|
1270
1348
|
if (!server.optional) {
|
|
1271
1349
|
delete mcpBlock[name];
|
|
1272
1350
|
continue;
|
|
@@ -1341,6 +1419,47 @@ var ENGRAM_AGENT_TOOLS = [
|
|
|
1341
1419
|
function isRecord(value) {
|
|
1342
1420
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1343
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
|
+
}
|
|
1344
1463
|
function hasClaudeManualApproval(configDir) {
|
|
1345
1464
|
const content = readTextIfExists(path10.join(configDir, "settings.json"));
|
|
1346
1465
|
if (content === null) return false;
|
|
@@ -1486,11 +1605,24 @@ ${agent.body}`,
|
|
|
1486
1605
|
planMainConfig(canonical, ctx) {
|
|
1487
1606
|
const file = path10.join(path10.dirname(ctx.configDir), `${path10.basename(ctx.configDir)}.json`);
|
|
1488
1607
|
const mcpOwnership = [];
|
|
1489
|
-
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"]);
|
|
1490
1616
|
const servers = root["mcpServers"] ??= {};
|
|
1491
1617
|
for (const [name, server] of Object.entries(canonical.servers)) {
|
|
1492
1618
|
const existing = servers[name];
|
|
1493
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
|
+
}
|
|
1494
1626
|
if (!isCanonicalMcpServerEnabled(name, server, ctx.enabledMcpServers)) {
|
|
1495
1627
|
if (owned) {
|
|
1496
1628
|
if (isManagedOptionalStdioServer2(server, existing)) delete servers[name];
|
|
@@ -1534,6 +1666,9 @@ ${agent.body}`,
|
|
|
1534
1666
|
url: server.url,
|
|
1535
1667
|
...Object.keys(headers).length > 0 ? { headers } : {}
|
|
1536
1668
|
};
|
|
1669
|
+
if (name === "context7" && existing === void 0 && !owned) {
|
|
1670
|
+
mcpOwnership.push({ server: name, owned: true });
|
|
1671
|
+
}
|
|
1537
1672
|
}
|
|
1538
1673
|
}
|
|
1539
1674
|
});
|
|
@@ -1556,13 +1691,26 @@ ${agent.body}`,
|
|
|
1556
1691
|
}
|
|
1557
1692
|
}
|
|
1558
1693
|
const mainFile = path10.join(path10.dirname(ctx.configDir), `${path10.basename(ctx.configDir)}.json`);
|
|
1559
|
-
const main2 =
|
|
1694
|
+
const main2 = readMcpConfig2(mainFile);
|
|
1560
1695
|
if (main2 !== null) {
|
|
1561
1696
|
const mcpOwnership = [];
|
|
1562
1697
|
const content = upsertJson(main2, (root) => {
|
|
1563
|
-
const
|
|
1564
|
-
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;
|
|
1565
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
|
+
}
|
|
1566
1714
|
if (!server.optional) {
|
|
1567
1715
|
delete servers[name];
|
|
1568
1716
|
continue;
|
|
@@ -1586,6 +1734,15 @@ import fs8 from "fs";
|
|
|
1586
1734
|
function tomlString(value) {
|
|
1587
1735
|
return JSON.stringify(value);
|
|
1588
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
|
+
}
|
|
1589
1746
|
var PRIMARY_MODEL2 = '"gpt-5.6-sol"';
|
|
1590
1747
|
var PRIMARY_CONTEXT_WINDOW = "872000";
|
|
1591
1748
|
var PRIMARY_MODEL_FIELD2 = "model";
|
|
@@ -1622,6 +1779,67 @@ var CODEX_JSON_STRING_ARRAY = String.raw`\[(?:\s*${CODEX_JSON_STRING}(?:\s*,\s*$
|
|
|
1622
1779
|
var CODEX_KEY = String.raw`(?:[A-Za-z0-9_-]+|${CODEX_JSON_STRING})`;
|
|
1623
1780
|
var CODEX_ASSIGNMENT = new RegExp(String.raw`^\s*(${CODEX_KEY})\s*=\s*(${CODEX_JSON_STRING_ARRAY}|${CODEX_JSON_VALUE})\s*(?:#.*)?$`);
|
|
1624
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
|
+
}
|
|
1625
1843
|
var CODEX_PERMISSION_HEADERS = {
|
|
1626
1844
|
base: "permissions.jorgex-read-anywhere",
|
|
1627
1845
|
filesystem: "permissions.jorgex-read-anywhere.filesystem",
|
|
@@ -1861,11 +2079,15 @@ ${body}`
|
|
|
1861
2079
|
},
|
|
1862
2080
|
planMainConfig(canonical, ctx) {
|
|
1863
2081
|
const file = path11.join(ctx.configDir, "config.toml");
|
|
1864
|
-
const original =
|
|
2082
|
+
const original = readMcpConfig3(file);
|
|
1865
2083
|
const contentSource = original === null || original.trim() === "" ? null : original;
|
|
1866
2084
|
let content = contentSource;
|
|
1867
2085
|
const mcpOwnership = [];
|
|
1868
2086
|
const primaryModelOwnership = [];
|
|
2087
|
+
const context7 = canonical.servers.context7;
|
|
2088
|
+
if (context7 !== void 0) {
|
|
2089
|
+
assertCompatibleContext73(context7, readTomlSection(contentSource, "mcp_servers.context7"), contentSource);
|
|
2090
|
+
}
|
|
1869
2091
|
if (contentSource === null) {
|
|
1870
2092
|
const defaults = loadCanonicalDefaults(ctx.stackDir)["codex"] ?? {};
|
|
1871
2093
|
for (const [key, value] of Object.entries(defaults)) {
|
|
@@ -1909,6 +2131,12 @@ ${body}`
|
|
|
1909
2131
|
const section = `mcp_servers.${name}`;
|
|
1910
2132
|
const existing = readTomlSection(content, section);
|
|
1911
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
|
+
}
|
|
1912
2140
|
if (!isCanonicalMcpServerEnabled(name, server, ctx.enabledMcpServers)) {
|
|
1913
2141
|
if (owned) {
|
|
1914
2142
|
if (isManagedOptionalStdioServer3(server, existing)) content = removeTomlSection(content, section);
|
|
@@ -1957,6 +2185,9 @@ args = [${(server.args ?? []).map(tomlString).join(", ")}]`);
|
|
|
1957
2185
|
if (literalPairs.length > 0) body.push(`http_headers = { ${literalPairs.join(", ")} }`);
|
|
1958
2186
|
if (refPairs.length > 0) body.push(`env_http_headers = { ${refPairs.join(", ")} }`);
|
|
1959
2187
|
content = upsertTomlSection(content, section, body.join("\n"));
|
|
2188
|
+
if (name === "context7" && existing === null && !owned) {
|
|
2189
|
+
mcpOwnership.push({ server: name, owned: true });
|
|
2190
|
+
}
|
|
1960
2191
|
}
|
|
1961
2192
|
}
|
|
1962
2193
|
if (content === null) return [];
|
|
@@ -1978,7 +2209,7 @@ args = [${(server.args ?? []).map(tomlString).join(", ")}]`);
|
|
|
1978
2209
|
actions.push({ kind: "write", target: systemPromptFile, content });
|
|
1979
2210
|
}
|
|
1980
2211
|
const configFile = path11.join(ctx.configDir, "config.toml");
|
|
1981
|
-
const config =
|
|
2212
|
+
const config = readMcpConfig3(configFile);
|
|
1982
2213
|
if (config !== null) {
|
|
1983
2214
|
let content = config;
|
|
1984
2215
|
const primaryModelOwnership = [];
|
|
@@ -1993,6 +2224,18 @@ args = [${(server.args ?? []).map(tomlString).join(", ")}]`);
|
|
|
1993
2224
|
const mcpOwnership = [];
|
|
1994
2225
|
for (const [name, server] of Object.entries(mcp.servers)) {
|
|
1995
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
|
+
}
|
|
1996
2239
|
if (!server.optional) {
|
|
1997
2240
|
content = removeTomlSection(content, section);
|
|
1998
2241
|
continue;
|
|
@@ -2782,7 +3025,7 @@ function enabledMcpServers(runtime, explicitDevtoolsEnabled, useBrowserPreferenc
|
|
|
2782
3025
|
function ownedMcpServers(runtime, useBrowserPreferences = true) {
|
|
2783
3026
|
if (!useBrowserPreferences) return /* @__PURE__ */ new Set();
|
|
2784
3027
|
const file = devtoolsMcpPreferenceFile();
|
|
2785
|
-
return
|
|
3028
|
+
return new Set([DEVTOOLS_MCP_SERVER, "context7"].filter((server) => loadDevtoolsMcpOwnership(file, runtime, server)));
|
|
2786
3029
|
}
|
|
2787
3030
|
function persistConfigurationOwnershipChanges(runtime, configDir, plan) {
|
|
2788
3031
|
const latest = /* @__PURE__ */ new Map();
|
|
@@ -2814,17 +3057,29 @@ function makeContext(adapter, configDir, mode = DEFAULT_INSTALL_MODE_PREFERENCE,
|
|
|
2814
3057
|
ownedPrimaryModelFields: useBrowserPreferences ? loadPrimaryModelOwnership(primaryModelOwnershipFile(), adapter.id, configDir) : /* @__PURE__ */ new Set()
|
|
2815
3058
|
};
|
|
2816
3059
|
}
|
|
2817
|
-
function
|
|
3060
|
+
function buildContentPlan(adapter, ctx) {
|
|
2818
3061
|
return [
|
|
2819
3062
|
...planSystemPrompt(adapter, ctx),
|
|
2820
3063
|
...planAgents(adapter, ctx),
|
|
2821
3064
|
...planSkills(adapter, ctx),
|
|
2822
3065
|
...planCommands(adapter, ctx),
|
|
2823
3066
|
...planHooks(adapter, ctx),
|
|
2824
|
-
...planMcp(adapter, ctx),
|
|
2825
3067
|
...planPlugins(adapter, ctx)
|
|
2826
3068
|
];
|
|
2827
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
|
+
}
|
|
2828
3083
|
function diffPlan(plan) {
|
|
2829
3084
|
return plan.map((action) => {
|
|
2830
3085
|
if (action.kind === "write") {
|
|
@@ -2883,6 +3138,13 @@ async function runInstall(opts) {
|
|
|
2883
3138
|
p.log.error(error instanceof Error ? error.message : String(error));
|
|
2884
3139
|
return 1;
|
|
2885
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
|
+
}
|
|
2886
3148
|
let writingStyle;
|
|
2887
3149
|
let preparedStyle;
|
|
2888
3150
|
try {
|
|
@@ -2912,7 +3174,6 @@ async function runInstall(opts) {
|
|
|
2912
3174
|
const playwrightCapability = opts.dryRun || !useManifest ? void 0 : opts.playwrightCapability ?? (shouldInspectPlaywright ? inspectPlaywrightCapability() : void 0);
|
|
2913
3175
|
const effectivePlaywright = playwrightCapability?.effective;
|
|
2914
3176
|
const plannedPlaywright = effectivePlaywright ?? (toolPlan !== null && toolPlan.actions.length > 0 ? false : void 0);
|
|
2915
|
-
const modelMap = hasFileRuntimes ? loadModelMap() : {};
|
|
2916
3177
|
if (preparedStyle !== void 0) {
|
|
2917
3178
|
try {
|
|
2918
3179
|
p.log.info(`Estilo de escritura: ${preparedStyle.sourcePath}${opts.dryRun ? " (instalaci\xF3n prevista; sin escrituras)" : ""}.`);
|
|
@@ -2977,7 +3238,7 @@ async function runInstall(opts) {
|
|
|
2977
3238
|
let diff = diffPlan(plan);
|
|
2978
3239
|
let creates = diff.filter((d) => d.status === "create");
|
|
2979
3240
|
let updates = diff.filter((d) => d.status === "update");
|
|
2980
|
-
let changes =
|
|
3241
|
+
let changes = diff.filter((change) => change.status !== "unchanged");
|
|
2981
3242
|
const prevManifest = useManifest ? readManifest().runtimes[id] : void 0;
|
|
2982
3243
|
const orphans = canOrphan && prevManifest ? findOrphans(prevManifest.owned, current.targets) : [];
|
|
2983
3244
|
p.log.step(`${adapter.name} \u2192 ${configDir}`);
|
|
@@ -3024,7 +3285,7 @@ async function runInstall(opts) {
|
|
|
3024
3285
|
diff = diffPlan(plan);
|
|
3025
3286
|
creates = diff.filter((d) => d.status === "create");
|
|
3026
3287
|
updates = diff.filter((d) => d.status === "update");
|
|
3027
|
-
changes =
|
|
3288
|
+
changes = diff.filter((change) => change.status !== "unchanged");
|
|
3028
3289
|
}
|
|
3029
3290
|
const backup = useManifest ? createBackup([...updates.map((c) => c.action.target), ...orphans], `install-${id}`) : null;
|
|
3030
3291
|
if (backup) p.log.info(`Backup: ${backup.id} (${backup.files.length} archivos)`);
|
|
@@ -3195,14 +3456,11 @@ var piAdapter = {
|
|
|
3195
3456
|
injectEngramProtocol() {
|
|
3196
3457
|
return true;
|
|
3197
3458
|
},
|
|
3198
|
-
//
|
|
3459
|
+
// La guía Context7 se habilita al adoptar su registro HTTP en Pi.
|
|
3199
3460
|
adaptSystemPromptSections(sections) {
|
|
3200
|
-
const
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
"system-prompt": [legacy["system-prompt"], context7].filter(Boolean).join("\n\n"),
|
|
3204
|
-
browser: [playwright, devtools].filter(Boolean).join("\n\n")
|
|
3205
|
-
};
|
|
3461
|
+
const modular = { ...sections };
|
|
3462
|
+
delete modular.context7;
|
|
3463
|
+
return modular;
|
|
3206
3464
|
}
|
|
3207
3465
|
};
|
|
3208
3466
|
|
|
@@ -3218,22 +3476,30 @@ var ALLOWED_EXTERNAL_WRITES = /* @__PURE__ */ new Set([
|
|
|
3218
3476
|
"models.json",
|
|
3219
3477
|
"jorgex-pi/sol-lifecycle.v1.json"
|
|
3220
3478
|
]);
|
|
3479
|
+
var PERMISSIONS_EXTERNAL_WRITES = /* @__PURE__ */ new Set([
|
|
3480
|
+
...ALLOWED_EXTERNAL_WRITES,
|
|
3481
|
+
"extensions/pi-permission-system/config.json",
|
|
3482
|
+
"jorgex-pi/permissions-lifecycle.v1.json",
|
|
3483
|
+
"jorgex-pi/permissions-backups"
|
|
3484
|
+
]);
|
|
3221
3485
|
function sameRecord(left, right) {
|
|
3222
3486
|
return JSON.stringify(left) === JSON.stringify(right);
|
|
3223
3487
|
}
|
|
3224
|
-
function managedExternalWritesAreSafe(writes) {
|
|
3225
|
-
if (
|
|
3488
|
+
function managedExternalWritesAreSafe(writes, capabilities) {
|
|
3489
|
+
if (!Array.isArray(capabilities)) return false;
|
|
3490
|
+
const allowed = capabilities.includes("permissions-policy-v1") ? PERMISSIONS_EXTERNAL_WRITES : ALLOWED_EXTERNAL_WRITES;
|
|
3491
|
+
if (writes.length !== allowed.size) return false;
|
|
3226
3492
|
const seen = /* @__PURE__ */ new Set();
|
|
3227
3493
|
for (const write of writes) {
|
|
3228
3494
|
if (write === null || typeof write !== "object" || Array.isArray(write)) return false;
|
|
3229
3495
|
if (Object.keys(write).sort().join(",") !== "owner,relativePath,root,semantics") return false;
|
|
3230
3496
|
if (write.owner !== "jorgex-pi" || write.root !== "PI_CODING_AGENT_DIR") return false;
|
|
3231
|
-
if (typeof write.relativePath !== "string" || !
|
|
3497
|
+
if (typeof write.relativePath !== "string" || !allowed.has(write.relativePath)) return false;
|
|
3232
3498
|
if (/^(?:[A-Za-z]:|[\\/])/.test(write.relativePath) || write.relativePath.split(/[\\/]/).includes("..")) return false;
|
|
3233
3499
|
if (typeof write.semantics !== "string" || write.semantics.trim() === "" || seen.has(write.relativePath)) return false;
|
|
3234
3500
|
seen.add(write.relativePath);
|
|
3235
3501
|
}
|
|
3236
|
-
return seen.size ===
|
|
3502
|
+
return seen.size === allowed.size;
|
|
3237
3503
|
}
|
|
3238
3504
|
function ownership(receipt) {
|
|
3239
3505
|
return { receipt, adapters: false, manifest: false, modelMap: false };
|
|
@@ -3354,7 +3620,7 @@ function parseReceipt(receiptJson, candidate, scope, engramBin) {
|
|
|
3354
3620
|
return sameRecord(parsed, expected) ? expected : null;
|
|
3355
3621
|
}
|
|
3356
3622
|
function candidateIsValid(candidate, observed) {
|
|
3357
|
-
return candidate.package.name === "jorgex-pi" && candidate.package.source === `npm:${candidate.package.name}@${candidate.package.version}` && candidate.contract.schemaVersion === 1 && candidate.contract.runner.schemaVersion === 1 && candidate.contract.runner.bin === "jorgex-pi" && candidate.contract.runner.maxStdoutBytes === 65536 && managedExternalWritesAreSafe(candidate.contract.managedExternalWrites) && [...REQUIRED_CAPABILITIES].every((capability) => candidate.contract.capabilities.includes(capability)) && sameRecord(candidate.tarball, observed);
|
|
3623
|
+
return candidate.package.name === "jorgex-pi" && candidate.package.source === `npm:${candidate.package.name}@${candidate.package.version}` && candidate.contract.schemaVersion === 1 && candidate.contract.runner.schemaVersion === 1 && candidate.contract.runner.bin === "jorgex-pi" && candidate.contract.runner.maxStdoutBytes === 65536 && managedExternalWritesAreSafe(candidate.contract.managedExternalWrites, candidate.contract.capabilities) && [...REQUIRED_CAPABILITIES].every((capability) => candidate.contract.capabilities.includes(capability)) && sameRecord(candidate.tarball, observed);
|
|
3358
3624
|
}
|
|
3359
3625
|
function planPiPackageLifecycle(input) {
|
|
3360
3626
|
if (!candidateIsValid(input.candidate, input.observedTarball)) {
|
|
@@ -4285,7 +4551,7 @@ async function runUninstall(opts) {
|
|
|
4285
4551
|
if (!detection.installed) continue;
|
|
4286
4552
|
const keepCtx = makeContext(keep, detection.configDir);
|
|
4287
4553
|
if (!keepCtx) continue;
|
|
4288
|
-
for (const action of
|
|
4554
|
+
for (const action of buildContentPlan(keep, keepCtx)) retained.add(path26.resolve(action.target));
|
|
4289
4555
|
}
|
|
4290
4556
|
}
|
|
4291
4557
|
for (const id of opts.runtimes) {
|
|
@@ -4303,13 +4569,20 @@ async function runUninstall(opts) {
|
|
|
4303
4569
|
const ctx = makeContext(adapter, configDir, void 0, useBrowserPreferences);
|
|
4304
4570
|
if (!ctx) continue;
|
|
4305
4571
|
ctx.preserveEngram = !removeEngram;
|
|
4306
|
-
|
|
4572
|
+
let unmerge;
|
|
4573
|
+
try {
|
|
4574
|
+
unmerge = adapter.planUnmerge(mcpForUnmerge, hooks, ctx);
|
|
4575
|
+
} catch (error) {
|
|
4576
|
+
p2.log.error(`${adapter.name}: no se pudo planificar la limpieza en ${configDir} \u2014 ${error instanceof Error ? error.message : String(error)}.`);
|
|
4577
|
+
exitCode = 1;
|
|
4578
|
+
continue;
|
|
4579
|
+
}
|
|
4307
4580
|
const mergedTargets = new Set(unmerge.map((a) => path26.resolve(a.target)));
|
|
4308
4581
|
const usingRealConfig = opts.targetDir === void 0;
|
|
4309
4582
|
const prevOwned = usingRealConfig ? readManifest().runtimes[id]?.owned ?? [] : [];
|
|
4310
4583
|
const pruneRoot = usingRealConfig ? HOME : path26.dirname(configDir);
|
|
4311
4584
|
const planTargets = [
|
|
4312
|
-
.../* @__PURE__ */ new Set([...
|
|
4585
|
+
.../* @__PURE__ */ new Set([...buildContentPlan(adapter, ctx).map((a) => path26.resolve(a.target)), ...prevOwned.map((t) => path26.resolve(t))])
|
|
4313
4586
|
].filter((t) => !mergedTargets.has(t) && fs19.existsSync(t));
|
|
4314
4587
|
const deleteTargets = planTargets.filter(
|
|
4315
4588
|
(t) => !retained.has(t) && !(ctx.preserveEngram && path26.basename(t) === "engram.ts") && isContainedIn(t, pruneRoot)
|
|
@@ -4579,7 +4852,7 @@ async function runDoctor(options = {}) {
|
|
|
4579
4852
|
pending = diffPlan(buildPlan(adapter, ctx)).filter((d) => d.status !== "unchanged").length;
|
|
4580
4853
|
} catch (err) {
|
|
4581
4854
|
p3.log.error(
|
|
4582
|
-
`${adapter.name}:
|
|
4855
|
+
`${adapter.name}: configuraci\xF3n incompatible o ilegible en ${detection.configDir} \u2014 ${err instanceof Error ? err.message : err}`
|
|
4583
4856
|
);
|
|
4584
4857
|
problems++;
|
|
4585
4858
|
continue;
|
|
@@ -5866,16 +6139,16 @@ import { createHash as createHash2 } from "crypto";
|
|
|
5866
6139
|
var pi_runtime_pin_default = {
|
|
5867
6140
|
package: {
|
|
5868
6141
|
name: "jorgex-pi",
|
|
5869
|
-
version: "0.8.
|
|
5870
|
-
source: "npm:jorgex-pi@0.8.
|
|
6142
|
+
version: "0.8.21",
|
|
6143
|
+
source: "npm:jorgex-pi@0.8.21"
|
|
5871
6144
|
},
|
|
5872
6145
|
provenance: {
|
|
5873
|
-
commit: "
|
|
6146
|
+
commit: "425356a083708aa873a14a094d06bd7abe302bc0"
|
|
5874
6147
|
},
|
|
5875
6148
|
tarball: {
|
|
5876
|
-
bytes:
|
|
5877
|
-
sha256: "
|
|
5878
|
-
sha512: "
|
|
6149
|
+
bytes: 89139189,
|
|
6150
|
+
sha256: "bf8846caf25eddebe31edc396ed9acc0b5636f7a2a4edd5df4f166beadec40e7",
|
|
6151
|
+
sha512: "74184929ee136671dd3160f76f96a57a4d9299a2dcae2608ca476220a3ea8a7e9cf32fc6a9bbd09334d16c21b7e52dc46c1b5dd76107ad936e4b4c651f1fa684"
|
|
5879
6152
|
}
|
|
5880
6153
|
};
|
|
5881
6154
|
|
|
@@ -5890,6 +6163,7 @@ var PI_RUNTIME_CANDIDATE = {
|
|
|
5890
6163
|
capabilities: [
|
|
5891
6164
|
"foundation-contract-v1",
|
|
5892
6165
|
"stack-snapshot-v2",
|
|
6166
|
+
"modular-system-prompts-v1",
|
|
5893
6167
|
"runtime-agents-v1",
|
|
5894
6168
|
"permission-gated-tools-v1",
|
|
5895
6169
|
"structured-questions-v1",
|
|
@@ -5897,6 +6171,8 @@ var PI_RUNTIME_CANDIDATE = {
|
|
|
5897
6171
|
"goal-continuation-v1",
|
|
5898
6172
|
"mcp-adapter-v1",
|
|
5899
6173
|
"engram-runtime-tools-v1",
|
|
6174
|
+
"context7-http-v1",
|
|
6175
|
+
"permissions-policy-v1",
|
|
5900
6176
|
"chrome-devtools-handoff-v1",
|
|
5901
6177
|
"playwright-handoff-v1",
|
|
5902
6178
|
"runner-json-v1",
|
|
@@ -5929,6 +6205,24 @@ var PI_RUNTIME_CANDIDATE = {
|
|
|
5929
6205
|
root: "PI_CODING_AGENT_DIR",
|
|
5930
6206
|
relativePath: "jorgex-pi/sol-lifecycle.v1.json",
|
|
5931
6207
|
semantics: "record field, container, and file ownership; remove the receipt when empty"
|
|
6208
|
+
},
|
|
6209
|
+
{
|
|
6210
|
+
owner: "jorgex-pi",
|
|
6211
|
+
root: "PI_CODING_AGENT_DIR",
|
|
6212
|
+
relativePath: "extensions/pi-permission-system/config.json",
|
|
6213
|
+
semantics: "seed the generated permission policy only when absent; publish exclusively, preserve preexisting or invalid user state, and remove only an exact owned copy during cleanup"
|
|
6214
|
+
},
|
|
6215
|
+
{
|
|
6216
|
+
owner: "jorgex-pi",
|
|
6217
|
+
root: "PI_CODING_AGENT_DIR",
|
|
6218
|
+
relativePath: "jorgex-pi/permissions-lifecycle.v1.json",
|
|
6219
|
+
semantics: "record initialization and exact permission-config ownership without storing user configuration or credentials"
|
|
6220
|
+
},
|
|
6221
|
+
{
|
|
6222
|
+
owner: "jorgex-pi",
|
|
6223
|
+
root: "PI_CODING_AGENT_DIR",
|
|
6224
|
+
relativePath: "jorgex-pi/permissions-backups",
|
|
6225
|
+
semantics: "retain cleanup backups of exact owned permission policy bytes"
|
|
5932
6226
|
}
|
|
5933
6227
|
]
|
|
5934
6228
|
}
|
|
@@ -7583,6 +7877,7 @@ Flags disponibles: jorgex-stack --help`
|
|
|
7583
7877
|
p6.intro(`jorgex-stack ${command}${flags.dryRun ? " (dry-run)" : ""}`);
|
|
7584
7878
|
try {
|
|
7585
7879
|
assertSelectedPromptFiles(runtimes, flags.targetDir);
|
|
7880
|
+
preflightSelectedMcpConfigs(fileRuntimes, flags.targetDir);
|
|
7586
7881
|
const writingStyle = prepareWritingStyle(resolveWritingStyleFile({ targetDir: flags.targetDir }), { rootDir: flags.targetDir });
|
|
7587
7882
|
if (flags.targetDir === void 0) {
|
|
7588
7883
|
const errors = browserPreferenceErrors();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jorgex-stack",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.43",
|
|
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",
|