jorgex-stack 1.9.29 → 1.9.30
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 +3 -1
- package/dist/cli.js +168 -42
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -120,7 +120,7 @@ pnpm dlx jorgex-stack@1.9.7 sync --agents pi
|
|
|
120
120
|
pnpm dlx jorgex-stack@1.9.7 uninstall --agents pi
|
|
121
121
|
```
|
|
122
122
|
|
|
123
|
-
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 historical `0.8.0` tarball was `89128340` bytes; the exact adopted 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`. When the managed Playwright preference is active, the projection also adds or removes the marked `jorgex:browser` section dynamically. The Pi-only `install --agents pi --playwright` flow installs and persists that Playwright capability just like the other harnesses. Chrome DevTools
|
|
123
|
+
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 historical `0.8.0` tarball was `89128340` bytes; the exact adopted 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`. When the managed Playwright preference is active, the projection also adds or removes the marked `jorgex:browser` section dynamically. The Pi-only `install --agents pi --playwright` flow installs and persists that Playwright capability just like the other harnesses. The adopted Pi `0.8.13` candidate also supports the optional Chrome DevTools handoff; Stack persists the selected handoff as part of the Pi projection and records its SHA-256 in the optional `devtools.sha256` field of `~/.jorgex-stack/pi-projection-receipt.json` after a successful operation. Context7 remains outside the Pi scope. The published `1.9.7` entry `{ "source": "npm:jorgex-pi@0.8.4", "skills": [], "prompts": [] }` and the `0.8.5` snapshot reference are historical; they do not describe the adopted 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.
|
|
124
124
|
|
|
125
125
|
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.
|
|
126
126
|
|
|
@@ -159,6 +159,8 @@ pnpm dlx jorgex-stack install --yes --playwright
|
|
|
159
159
|
# Enable Chrome DevTools MCP explicitly per runtime.
|
|
160
160
|
pnpm dlx jorgex-stack install --devtools
|
|
161
161
|
pnpm dlx jorgex-stack install --no-devtools
|
|
162
|
+
pnpm dlx jorgex-stack install --agents pi --devtools
|
|
163
|
+
pnpm dlx jorgex-stack sync --agents pi --no-devtools
|
|
162
164
|
```
|
|
163
165
|
|
|
164
166
|
Under the hood, `--playwright` runs two `pnpm` argv-only plans back to back: `pnpm add --global @playwright/cli@0.1.18` (the package) and then `pnpm dlx @playwright/cli@0.1.18 install-browser` (the browser binary cache). Removal is `pnpm remove --global @playwright/cli` (no version suffix). If installation fails, the error identifies the failed phase — global package, browser download, or preference persistence — and recommends `jorgex-stack install --playwright`; the preference is not marked enabled unless the complete plan succeeds.
|
package/dist/cli.js
CHANGED
|
@@ -2341,7 +2341,7 @@ function parseDevtoolsMcpState(raw) {
|
|
|
2341
2341
|
if (!isRecord2(value) || value.version !== DEVTOOLS_MCP_PREFERENCE_VERSION || !isRecord2(value.enabled) || !isRecord2(value.owned)) return null;
|
|
2342
2342
|
const enabled = {};
|
|
2343
2343
|
for (const [runtime, selected] of Object.entries(value.enabled)) {
|
|
2344
|
-
if (!isRuntimeId(runtime) || typeof selected !== "boolean") return null;
|
|
2344
|
+
if (!isRuntimeId(runtime) && runtime !== "pi" || typeof selected !== "boolean") return null;
|
|
2345
2345
|
enabled[runtime] = selected;
|
|
2346
2346
|
}
|
|
2347
2347
|
const owned = {};
|
|
@@ -2844,6 +2844,7 @@ import * as p2 from "@clack/prompts";
|
|
|
2844
2844
|
// src/lib/pi-projection-lifecycle.ts
|
|
2845
2845
|
import fs16 from "fs";
|
|
2846
2846
|
import path23 from "path";
|
|
2847
|
+
import { createHash } from "crypto";
|
|
2847
2848
|
|
|
2848
2849
|
// src/adapters/pi.ts
|
|
2849
2850
|
import path21 from "path";
|
|
@@ -3307,14 +3308,33 @@ function projectionPlan(input, scope) {
|
|
|
3307
3308
|
engramBin: input.engramBin,
|
|
3308
3309
|
models: DEFAULT_MODEL_MAP.codex,
|
|
3309
3310
|
warnings: [],
|
|
3310
|
-
playwrightCliEnabled: input.playwrightCliEnabled
|
|
3311
|
+
playwrightCliEnabled: input.playwrightCliEnabled,
|
|
3312
|
+
enabledMcpServers: input.devtoolsMcpEnabled ? /* @__PURE__ */ new Set([DEVTOOLS_MCP_SERVER]) : void 0
|
|
3311
3313
|
};
|
|
3312
3314
|
const adapter = projectedPiAdapter(scope);
|
|
3313
|
-
|
|
3315
|
+
const actions = [
|
|
3314
3316
|
...planSystemPrompt(adapter, ctx),
|
|
3315
3317
|
...planSkills(adapter, ctx),
|
|
3316
3318
|
...planCommands(adapter, ctx)
|
|
3317
3319
|
];
|
|
3320
|
+
if (input.devtoolsMcpEnabled && input.pnpmBin) {
|
|
3321
|
+
const server = loadCanonicalMcp(input.stackDir).servers[DEVTOOLS_MCP_SERVER];
|
|
3322
|
+
if (server?.transport !== "stdio" || !Array.isArray(server.args)) throw new Error("Falta la configuraci\xF3n can\xF3nica de DevTools.");
|
|
3323
|
+
actions.push({ kind: "write", target: devtoolsPath(scope), content: `${JSON.stringify({
|
|
3324
|
+
schemaVersion: 1,
|
|
3325
|
+
enabled: true,
|
|
3326
|
+
command: input.pnpmBin,
|
|
3327
|
+
args: server.args
|
|
3328
|
+
}, null, 2)}
|
|
3329
|
+
` });
|
|
3330
|
+
}
|
|
3331
|
+
return actions;
|
|
3332
|
+
}
|
|
3333
|
+
function devtoolsPath(scope) {
|
|
3334
|
+
return path23.resolve(scope.codingAgentDir, "jorgex-pi", "devtools.v1.json");
|
|
3335
|
+
}
|
|
3336
|
+
function contentHash(content) {
|
|
3337
|
+
return createHash("sha256").update(content).digest("hex");
|
|
3318
3338
|
}
|
|
3319
3339
|
function canonicalActionContent(action) {
|
|
3320
3340
|
return action.kind === "write" ? action.content : fs16.readFileSync(action.source, "utf8");
|
|
@@ -3322,8 +3342,15 @@ function canonicalActionContent(action) {
|
|
|
3322
3342
|
function hasActionDrift(action, deps) {
|
|
3323
3343
|
return deps.readText(action.target) !== canonicalActionContent(action);
|
|
3324
3344
|
}
|
|
3325
|
-
function applyActions(actions, deps) {
|
|
3345
|
+
function applyActions(actions, deps, handoff) {
|
|
3326
3346
|
for (const action of actions) {
|
|
3347
|
+
if (handoff && path23.resolve(action.target) === handoff.file) {
|
|
3348
|
+
try {
|
|
3349
|
+
if (deps.readText(handoff.file) !== handoff.previous) return devtoolsConflict(handoff.file);
|
|
3350
|
+
} catch {
|
|
3351
|
+
return devtoolsConflict(handoff.file);
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3327
3354
|
if (action.kind === "write") deps.writeText(action.target, action.content);
|
|
3328
3355
|
else deps.copyFile(action.source, action.target);
|
|
3329
3356
|
}
|
|
@@ -3340,6 +3367,7 @@ function receiptFor(plan, scope) {
|
|
|
3340
3367
|
const owned = [...new Set(
|
|
3341
3368
|
plan.map((action) => path23.resolve(action.target)).filter((target) => target !== systemPrompt)
|
|
3342
3369
|
)];
|
|
3370
|
+
const handoff = plan.find((action) => path23.resolve(action.target) === devtoolsPath(scope));
|
|
3343
3371
|
return {
|
|
3344
3372
|
schemaVersion: 1,
|
|
3345
3373
|
scope: {
|
|
@@ -3348,7 +3376,8 @@ function receiptFor(plan, scope) {
|
|
|
3348
3376
|
codingAgentDir: scope.codingAgentDir,
|
|
3349
3377
|
receiptFile: scope.receiptFile
|
|
3350
3378
|
},
|
|
3351
|
-
owned
|
|
3379
|
+
owned,
|
|
3380
|
+
...handoff ? { devtools: { sha256: contentHash(canonicalActionContent(handoff)) } } : {}
|
|
3352
3381
|
};
|
|
3353
3382
|
}
|
|
3354
3383
|
function receiptContent(receipt) {
|
|
@@ -3367,16 +3396,25 @@ function parseReceipt2(raw, expected) {
|
|
|
3367
3396
|
const version = Reflect.get(receipt, "schemaVersion");
|
|
3368
3397
|
const receivedScope = Reflect.get(receipt, "scope");
|
|
3369
3398
|
const owned = Reflect.get(receipt, "owned");
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
if (Reflect.get(receivedScope, "kind") !== expected.scope.kind || Reflect.get(receivedScope, "home") !== expected.scope.home || Reflect.get(receivedScope, "codingAgentDir") !== expected.scope.codingAgentDir || Reflect.get(receivedScope, "receiptFile") !== expected.scope.receiptFile || !Array.isArray(owned) || owned.length !== expected.owned.length) {
|
|
3399
|
+
const devtools = Reflect.get(receipt, "devtools");
|
|
3400
|
+
const hasDevtools = Object.hasOwn(receipt, "devtools");
|
|
3401
|
+
if (version !== expected.schemaVersion || !hasExactKeys(receipt, ["schemaVersion", "scope", "owned", ...hasDevtools ? ["devtools"] : []]) || receivedScope === null || typeof receivedScope !== "object" || Array.isArray(receivedScope) || !hasExactKeys(receivedScope, ["kind", "home", "codingAgentDir", "receiptFile"])) {
|
|
3374
3402
|
return null;
|
|
3375
3403
|
}
|
|
3376
|
-
if (
|
|
3404
|
+
if (Reflect.get(receivedScope, "kind") !== expected.scope.kind || Reflect.get(receivedScope, "home") !== expected.scope.home || Reflect.get(receivedScope, "codingAgentDir") !== expected.scope.codingAgentDir || Reflect.get(receivedScope, "receiptFile") !== expected.scope.receiptFile || !Array.isArray(owned)) {
|
|
3377
3405
|
return null;
|
|
3378
3406
|
}
|
|
3379
|
-
return
|
|
3407
|
+
if (hasDevtools && (devtools === null || typeof devtools !== "object" || Array.isArray(devtools) || !hasExactKeys(devtools, ["sha256"]) || typeof Reflect.get(devtools, "sha256") !== "string" || !/^[a-f0-9]{64}$/.test(Reflect.get(devtools, "sha256")))) return null;
|
|
3408
|
+
const handoff = devtoolsPath(expected.scope);
|
|
3409
|
+
const expectedOwned = expected.owned.filter((file) => file !== handoff);
|
|
3410
|
+
if (hasDevtools) expectedOwned.push(handoff);
|
|
3411
|
+
if (owned.length !== expectedOwned.length || !owned.every((file, index) => typeof file === "string" && file === expectedOwned[index])) return null;
|
|
3412
|
+
return {
|
|
3413
|
+
schemaVersion: 1,
|
|
3414
|
+
scope: expected.scope,
|
|
3415
|
+
owned: expectedOwned,
|
|
3416
|
+
...hasDevtools ? { devtools: { sha256: Reflect.get(devtools, "sha256") } } : {}
|
|
3417
|
+
};
|
|
3380
3418
|
} catch {
|
|
3381
3419
|
return null;
|
|
3382
3420
|
}
|
|
@@ -3502,6 +3540,35 @@ function readProjectionReceipt(receiptFile, deps) {
|
|
|
3502
3540
|
return { kind: "unreadable" };
|
|
3503
3541
|
}
|
|
3504
3542
|
}
|
|
3543
|
+
function devtoolsConflict(file) {
|
|
3544
|
+
return blocked2(
|
|
3545
|
+
"projection-devtools-conflict",
|
|
3546
|
+
[file],
|
|
3547
|
+
"El handoff de DevTools es ajeno o fue modificado. Cons\xE9rvalo y revisa su propiedad antes de reintentar; Stack no lo sobrescribe ni elimina."
|
|
3548
|
+
);
|
|
3549
|
+
}
|
|
3550
|
+
function inspectDevtools(input, scope, expected, deps) {
|
|
3551
|
+
const file = devtoolsPath(scope);
|
|
3552
|
+
const read = readProjectionReceipt(scope.receiptFile, deps);
|
|
3553
|
+
if (read.kind === "unreadable") return receiptUnreadable(scope.receiptFile);
|
|
3554
|
+
const previous = read.kind === "present" ? parseReceipt2(read.content, expected) : null;
|
|
3555
|
+
let content;
|
|
3556
|
+
try {
|
|
3557
|
+
content = deps.readText(file);
|
|
3558
|
+
} catch {
|
|
3559
|
+
return devtoolsConflict(file);
|
|
3560
|
+
}
|
|
3561
|
+
let recordedDevtools = false;
|
|
3562
|
+
if (read.kind === "present") {
|
|
3563
|
+
try {
|
|
3564
|
+
recordedDevtools = Object.hasOwn(JSON.parse(read.content), "devtools");
|
|
3565
|
+
} catch {
|
|
3566
|
+
}
|
|
3567
|
+
if (!previous && (input.devtoolsMcpEnabled || content !== null || recordedDevtools)) return receiptInvalid(scope.receiptFile);
|
|
3568
|
+
}
|
|
3569
|
+
if (content !== null && (!previous?.devtools || contentHash(content) !== previous.devtools.sha256)) return devtoolsConflict(file);
|
|
3570
|
+
return { kind: "checked", previous, content };
|
|
3571
|
+
}
|
|
3505
3572
|
function preparePiProjectionUninstall(input, deps) {
|
|
3506
3573
|
if (input.operation !== "uninstall") {
|
|
3507
3574
|
return cleanupFailure([]);
|
|
@@ -3520,11 +3587,13 @@ function preparePiProjectionUninstall(input, deps) {
|
|
|
3520
3587
|
const receiptRead = readProjectionReceipt(scope.receiptFile, deps);
|
|
3521
3588
|
if (receiptRead.kind === "unreadable") return receiptUnreadable(scope.receiptFile);
|
|
3522
3589
|
let ownedToRemove = [];
|
|
3590
|
+
let ownedReceipt = null;
|
|
3523
3591
|
let receiptFile = null;
|
|
3524
3592
|
let backupTargets = promptUpdate === null ? [] : [promptUpdate.file];
|
|
3525
3593
|
if (receiptRead.kind === "present") {
|
|
3526
3594
|
const receipt = parseReceipt2(receiptRead.content, expectedReceipt2);
|
|
3527
3595
|
if (receipt === null) return receiptInvalid(scope.receiptFile);
|
|
3596
|
+
ownedReceipt = receipt;
|
|
3528
3597
|
let retained;
|
|
3529
3598
|
try {
|
|
3530
3599
|
retained = manifestOwned(deps.readManifest());
|
|
@@ -3535,13 +3604,22 @@ function preparePiProjectionUninstall(input, deps) {
|
|
|
3535
3604
|
receiptFile = path23.resolve(scope.receiptFile);
|
|
3536
3605
|
backupTargets = [...backupTargets, ...receipt.owned, receiptFile];
|
|
3537
3606
|
}
|
|
3607
|
+
const handoff = devtoolsPath(scope);
|
|
3608
|
+
let handoffContent;
|
|
3609
|
+
try {
|
|
3610
|
+
handoffContent = deps.readText(handoff);
|
|
3611
|
+
} catch {
|
|
3612
|
+
return devtoolsConflict(handoff);
|
|
3613
|
+
}
|
|
3614
|
+
if (handoffContent !== null && (!ownedReceipt?.devtools || contentHash(handoffContent) !== ownedReceipt.devtools.sha256)) return devtoolsConflict(handoff);
|
|
3538
3615
|
const backup = backupExisting(backupTargets, deps);
|
|
3539
3616
|
if (backup.kind === "backup-failed") return backupFailure(backup.paths);
|
|
3540
3617
|
const token = {};
|
|
3541
3618
|
preparedPiProjectionUninstalls.set(token, {
|
|
3542
3619
|
prompt: promptUpdate,
|
|
3543
3620
|
ownedToRemove,
|
|
3544
|
-
receiptFile
|
|
3621
|
+
receiptFile,
|
|
3622
|
+
...ownedReceipt?.devtools ? { devtools: { file: handoff, sha256: ownedReceipt.devtools.sha256 } } : {}
|
|
3545
3623
|
});
|
|
3546
3624
|
return { kind: "prepared", plan: token };
|
|
3547
3625
|
}
|
|
@@ -3553,6 +3631,15 @@ function completePiProjectionUninstall(plan, deps) {
|
|
|
3553
3631
|
if (prepared === void 0) {
|
|
3554
3632
|
return cleanupFailure([]);
|
|
3555
3633
|
}
|
|
3634
|
+
if (prepared.devtools) {
|
|
3635
|
+
let current;
|
|
3636
|
+
try {
|
|
3637
|
+
current = deps.readText(prepared.devtools.file);
|
|
3638
|
+
} catch {
|
|
3639
|
+
return devtoolsConflict(prepared.devtools.file);
|
|
3640
|
+
}
|
|
3641
|
+
if (current !== null && contentHash(current) !== prepared.devtools.sha256) return devtoolsConflict(prepared.devtools.file);
|
|
3642
|
+
}
|
|
3556
3643
|
if (prepared.prompt !== null) {
|
|
3557
3644
|
try {
|
|
3558
3645
|
deps.writeText(prepared.prompt.file, prepared.prompt.content);
|
|
@@ -3562,6 +3649,10 @@ function completePiProjectionUninstall(plan, deps) {
|
|
|
3562
3649
|
}
|
|
3563
3650
|
for (const owned of prepared.ownedToRemove) {
|
|
3564
3651
|
try {
|
|
3652
|
+
if (prepared.devtools?.file === owned) {
|
|
3653
|
+
const current = deps.readText(owned);
|
|
3654
|
+
if (current !== null && contentHash(current) !== prepared.devtools.sha256) return devtoolsConflict(owned);
|
|
3655
|
+
}
|
|
3565
3656
|
deps.removeFile(owned);
|
|
3566
3657
|
} catch {
|
|
3567
3658
|
return cleanupFailure([owned]);
|
|
@@ -3582,13 +3673,23 @@ function runPiProjectionLifecycle(input, deps) {
|
|
|
3582
3673
|
const prepared = preparePiProjectionUninstall(input, deps);
|
|
3583
3674
|
return prepared.kind === "blocked" ? prepared : completePiProjectionUninstall(prepared.plan, deps);
|
|
3584
3675
|
}
|
|
3676
|
+
if (input.devtoolsMcpEnabled && (!input.pnpmBin || !path23.isAbsolute(input.pnpmBin))) {
|
|
3677
|
+
return blocked2("projection-devtools-command", [devtoolsPath(scope)], "DevTools requiere un ejecutable pnpm absoluto disponible en PATH.");
|
|
3678
|
+
}
|
|
3585
3679
|
const plan = projectionPlan(input, scope);
|
|
3586
3680
|
assertPlanContained(plan, scope);
|
|
3587
3681
|
const receipt = receiptFor(plan, scope);
|
|
3682
|
+
const devtools = inspectDevtools(input, scope, receipt, deps);
|
|
3683
|
+
if (devtools.kind === "blocked") return input.operation === "doctor" ? { kind: "drift", paths: devtools.paths } : devtools;
|
|
3684
|
+
const removeHandoff = !input.devtoolsMcpEnabled && devtools.previous?.devtools !== void 0 && devtools.content !== null;
|
|
3588
3685
|
const expectedReceipt2 = receiptContent(receipt);
|
|
3589
3686
|
const drifted = plan.filter((action) => hasActionDrift(action, deps));
|
|
3687
|
+
if (input.devtoolsMcpEnabled && devtools.previous?.devtools?.sha256 !== receipt.devtools?.sha256 && !drifted.some((action) => path23.resolve(action.target) === devtoolsPath(scope))) {
|
|
3688
|
+
return input.operation === "doctor" ? { kind: "drift", paths: [devtoolsPath(scope)] } : devtoolsConflict(devtoolsPath(scope));
|
|
3689
|
+
}
|
|
3590
3690
|
if (input.operation === "doctor") {
|
|
3591
3691
|
const paths = drifted.map((action) => path23.resolve(action.target));
|
|
3692
|
+
if (removeHandoff) paths.push(devtoolsPath(scope));
|
|
3592
3693
|
const currentSettings2 = deps.readText(scope.settingsFile);
|
|
3593
3694
|
if (currentSettings2 === null || filterProjectedPiPackage(currentSettings2, input.packageSource) !== currentSettings2) {
|
|
3594
3695
|
paths.push(scope.settingsFile);
|
|
@@ -3604,19 +3705,30 @@ function runPiProjectionLifecycle(input, deps) {
|
|
|
3604
3705
|
}
|
|
3605
3706
|
const packageWillChange = filteredSettings !== null && filteredSettings !== currentSettings;
|
|
3606
3707
|
const receiptChanged = deps.readText(scope.receiptFile) !== expectedReceipt2;
|
|
3607
|
-
if (drifted.length > 0 || packageWillChange || receiptChanged) {
|
|
3708
|
+
if (drifted.length > 0 || packageWillChange || receiptChanged || removeHandoff) {
|
|
3608
3709
|
const backup = backupExisting([
|
|
3609
3710
|
...drifted.map((action) => action.target),
|
|
3711
|
+
...removeHandoff ? [devtoolsPath(scope)] : [],
|
|
3610
3712
|
...packageWillChange ? [scope.settingsFile] : [],
|
|
3611
3713
|
...receiptChanged ? [scope.receiptFile] : []
|
|
3612
3714
|
], deps);
|
|
3613
3715
|
if (backup.kind === "backup-failed") return backupFailure(backup.paths);
|
|
3614
3716
|
}
|
|
3615
|
-
|
|
3717
|
+
if (removeHandoff) {
|
|
3718
|
+
try {
|
|
3719
|
+
const current = deps.readText(devtoolsPath(scope));
|
|
3720
|
+
if (current !== null && contentHash(current) !== devtools.previous?.devtools?.sha256) return devtoolsConflict(devtoolsPath(scope));
|
|
3721
|
+
deps.removeFile(devtoolsPath(scope));
|
|
3722
|
+
} catch {
|
|
3723
|
+
return cleanupFailure([devtoolsPath(scope)]);
|
|
3724
|
+
}
|
|
3725
|
+
}
|
|
3726
|
+
const writeResult = applyActions(drifted, deps, input.devtoolsMcpEnabled ? { file: devtoolsPath(scope), previous: devtools.content } : void 0);
|
|
3727
|
+
if (writeResult) return writeResult;
|
|
3616
3728
|
if (packageWillChange && filteredSettings !== null) deps.writeText(scope.settingsFile, filteredSettings);
|
|
3617
3729
|
if (receiptChanged) deps.writeText(scope.receiptFile, expectedReceipt2);
|
|
3618
3730
|
if (input.operation === "install") return { kind: "installed", receipt };
|
|
3619
|
-
return { kind: "synced", changed: drifted.length > 0 || packageWillChange || receiptChanged };
|
|
3731
|
+
return { kind: "synced", changed: drifted.length > 0 || packageWillChange || receiptChanged || removeHandoff };
|
|
3620
3732
|
}
|
|
3621
3733
|
function systemProjectionLifecycle(input) {
|
|
3622
3734
|
const targetRoot = input.targetDir === void 0 ? null : path23.resolve(input.targetDir);
|
|
@@ -3633,7 +3745,9 @@ function systemProjectionLifecycle(input) {
|
|
|
3633
3745
|
packageSource: input.packageSource,
|
|
3634
3746
|
stackDir: stackRoot(),
|
|
3635
3747
|
engramBin: input.engramBin ?? "",
|
|
3636
|
-
playwrightCliEnabled: input.playwrightCliEnabled
|
|
3748
|
+
playwrightCliEnabled: input.playwrightCliEnabled,
|
|
3749
|
+
devtoolsMcpEnabled: input.devtoolsMcpEnabled,
|
|
3750
|
+
pnpmBin: input.pnpmBin
|
|
3637
3751
|
},
|
|
3638
3752
|
deps: {
|
|
3639
3753
|
readText: readTextOnlyIfMissing,
|
|
@@ -5218,22 +5332,22 @@ import os4 from "os";
|
|
|
5218
5332
|
import path31 from "path";
|
|
5219
5333
|
import fs23 from "fs";
|
|
5220
5334
|
import { spawnSync } from "child_process";
|
|
5221
|
-
import { createHash } from "crypto";
|
|
5335
|
+
import { createHash as createHash2 } from "crypto";
|
|
5222
5336
|
|
|
5223
5337
|
// src/lib/pi-runtime-pin.json
|
|
5224
5338
|
var pi_runtime_pin_default = {
|
|
5225
5339
|
package: {
|
|
5226
5340
|
name: "jorgex-pi",
|
|
5227
|
-
version: "0.8.
|
|
5228
|
-
source: "npm:jorgex-pi@0.8.
|
|
5341
|
+
version: "0.8.13",
|
|
5342
|
+
source: "npm:jorgex-pi@0.8.13"
|
|
5229
5343
|
},
|
|
5230
5344
|
provenance: {
|
|
5231
|
-
commit: "
|
|
5345
|
+
commit: "e28a677da0a112925251cf11269b70cb5c8aa15e"
|
|
5232
5346
|
},
|
|
5233
5347
|
tarball: {
|
|
5234
|
-
bytes:
|
|
5235
|
-
sha256: "
|
|
5236
|
-
sha512: "
|
|
5348
|
+
bytes: 89143404,
|
|
5349
|
+
sha256: "6bbe877249b900af582c8881e0dd7993951ea1cef6e7252522c626c078de7478",
|
|
5350
|
+
sha512: "bb527d1f3812406f2184f778b22cca00ead33692fecac0012cf8cdcdc469b7cccf3c789c35b4327dd599d06542162bd07eefeeaaa40de19662ec5fc422b14af8"
|
|
5237
5351
|
}
|
|
5238
5352
|
};
|
|
5239
5353
|
|
|
@@ -5255,6 +5369,7 @@ var PI_RUNTIME_CANDIDATE = {
|
|
|
5255
5369
|
"goal-continuation-v1",
|
|
5256
5370
|
"mcp-adapter-v1",
|
|
5257
5371
|
"engram-runtime-tools-v1",
|
|
5372
|
+
"chrome-devtools-handoff-v1",
|
|
5258
5373
|
"runner-json-v1",
|
|
5259
5374
|
"tui-branding-v1",
|
|
5260
5375
|
"managed-primary-model-v1",
|
|
@@ -5607,8 +5722,8 @@ function readOptional(file, fallback) {
|
|
|
5607
5722
|
}
|
|
5608
5723
|
function hashPiTarball(file) {
|
|
5609
5724
|
const descriptor = fs23.openSync(file, "r");
|
|
5610
|
-
const sha2562 =
|
|
5611
|
-
const sha512 =
|
|
5725
|
+
const sha2562 = createHash2("sha256");
|
|
5726
|
+
const sha512 = createHash2("sha512");
|
|
5612
5727
|
const buffer = Buffer.allocUnsafe(1024 * 1024);
|
|
5613
5728
|
let bytes = 0;
|
|
5614
5729
|
try {
|
|
@@ -5826,36 +5941,44 @@ function managedPackageResult(result) {
|
|
|
5826
5941
|
return result;
|
|
5827
5942
|
}
|
|
5828
5943
|
async function runManagedPiSystem(input) {
|
|
5944
|
+
const { devtoolsMcpEnabled: explicitDevtools, ...runtimeInput } = input;
|
|
5945
|
+
const devtoolsMcpEnabled = explicitDevtools ?? (input.targetDir === void 0 && loadDevtoolsMcpPreference(devtoolsMcpPreferenceFile(), "pi"));
|
|
5829
5946
|
const playwrightCliEnabled = input.targetDir === void 0 && loadPlaywrightCliPreference() === true;
|
|
5830
5947
|
const projectionInput = {
|
|
5831
5948
|
targetDir: input.targetDir,
|
|
5832
5949
|
packageSource: PI_RUNTIME_CANDIDATE.package.source,
|
|
5833
5950
|
engramBin: input.engramBin,
|
|
5834
|
-
playwrightCliEnabled
|
|
5951
|
+
playwrightCliEnabled,
|
|
5952
|
+
devtoolsMcpEnabled,
|
|
5953
|
+
pnpmBin: devtoolsMcpEnabled && input.operation !== "uninstall" ? resolvePnpmBin() : null
|
|
5835
5954
|
};
|
|
5836
|
-
|
|
5955
|
+
const result = await runManagedPiOperation(input.operation, {
|
|
5837
5956
|
async runPackage(operation) {
|
|
5838
|
-
return managedPackageResult(await runPiRuntimeSystem({ ...
|
|
5957
|
+
return managedPackageResult(await runPiRuntimeSystem({ ...runtimeInput, operation }));
|
|
5839
5958
|
},
|
|
5840
5959
|
runProjection(operation) {
|
|
5841
|
-
const
|
|
5960
|
+
const result2 = runPiProjectionLifecycleSystem({
|
|
5842
5961
|
operation,
|
|
5843
5962
|
...projectionInput
|
|
5844
5963
|
});
|
|
5845
|
-
return Promise.resolve(
|
|
5964
|
+
return Promise.resolve(result2.kind === "drift" ? {
|
|
5846
5965
|
kind: "drift",
|
|
5847
|
-
paths:
|
|
5966
|
+
paths: result2.paths,
|
|
5848
5967
|
remedy: "Ejecuta sync --agents pi para reparar la proyecci\xF3n de Pi."
|
|
5849
|
-
} :
|
|
5968
|
+
} : result2);
|
|
5850
5969
|
},
|
|
5851
5970
|
prepareProjectionUninstall() {
|
|
5852
|
-
const
|
|
5853
|
-
return Promise.resolve(
|
|
5971
|
+
const result2 = preparePiProjectionUninstallSystem({ operation: "uninstall", ...projectionInput });
|
|
5972
|
+
return Promise.resolve(result2.kind === "prepared" ? { kind: "prepared", token: result2.plan } : result2);
|
|
5854
5973
|
},
|
|
5855
5974
|
completeProjectionUninstall(token) {
|
|
5856
5975
|
return Promise.resolve(completePiProjectionUninstallSystem(token, { operation: "uninstall", ...projectionInput }));
|
|
5857
5976
|
}
|
|
5858
5977
|
});
|
|
5978
|
+
if (input.targetDir === void 0 && explicitDevtools !== void 0 && (input.operation === "install" || input.operation === "sync") && result.kind !== "blocked") {
|
|
5979
|
+
saveDevtoolsMcpPreference(devtoolsMcpPreferenceFile(), "pi", explicitDevtools);
|
|
5980
|
+
}
|
|
5981
|
+
return result;
|
|
5859
5982
|
}
|
|
5860
5983
|
|
|
5861
5984
|
// src/lib/quality-runner.ts
|
|
@@ -6241,7 +6364,7 @@ import fs24 from "fs";
|
|
|
6241
6364
|
import os5 from "os";
|
|
6242
6365
|
import path33 from "path";
|
|
6243
6366
|
import { execFileSync as execFileSync7 } from "child_process";
|
|
6244
|
-
import { createHash as
|
|
6367
|
+
import { createHash as createHash3 } from "crypto";
|
|
6245
6368
|
|
|
6246
6369
|
// src/lib/engram-release.json
|
|
6247
6370
|
var engram_release_default = {
|
|
@@ -6318,7 +6441,7 @@ async function installMissingEngram(options = {}) {
|
|
|
6318
6441
|
await reader.cancel();
|
|
6319
6442
|
}
|
|
6320
6443
|
const archive = Buffer.concat(chunks);
|
|
6321
|
-
if (size !== asset.size ||
|
|
6444
|
+
if (size !== asset.size || createHash3("sha256").update(archive).digest("hex") !== asset.sha256) {
|
|
6322
6445
|
throw new Error("Engram: tama\xF1o o hash SHA-256 no coincide con el artefacto aprobado.");
|
|
6323
6446
|
}
|
|
6324
6447
|
fs24.mkdirSync(path33.dirname(bin), { recursive: true });
|
|
@@ -6544,8 +6667,9 @@ async function resolveDevtoolsMcpSelection(command, flags, runtimes) {
|
|
|
6544
6667
|
}
|
|
6545
6668
|
const file = devtoolsMcpPreferenceFile();
|
|
6546
6669
|
const selected = await p6.multiselect({
|
|
6547
|
-
message: "Chrome DevTools MCP avanzado (
|
|
6548
|
-
|
|
6670
|
+
message: "Chrome DevTools MCP avanzado (opcional). \xBFEn qu\xE9 runtimes activarlo?",
|
|
6671
|
+
required: false,
|
|
6672
|
+
options: runtimes.map((runtime) => ({ value: runtime, label: runtime === "pi" ? "Pi" : ADAPTERS[runtime]?.name ?? runtime })),
|
|
6549
6673
|
initialValues: runtimes.filter((runtime) => loadDevtoolsMcpPreference(file, runtime))
|
|
6550
6674
|
});
|
|
6551
6675
|
if (p6.isCancel(selected)) return null;
|
|
@@ -6617,7 +6741,7 @@ async function resolveHostEngramForInstall(flags) {
|
|
|
6617
6741
|
return { ok: false, message: `Engram: ${error instanceof Error ? error.message : String(error)}` };
|
|
6618
6742
|
}
|
|
6619
6743
|
}
|
|
6620
|
-
async function runSelectedPi(operation, targetDir, yes = false, resolvedEngramBin) {
|
|
6744
|
+
async function runSelectedPi(operation, targetDir, yes = false, resolvedEngramBin, devtoolsMcpEnabled) {
|
|
6621
6745
|
if (targetDir === void 0 && operation !== "models") {
|
|
6622
6746
|
const preferenceErrors = browserPreferenceErrors();
|
|
6623
6747
|
if (preferenceErrors.length > 0) {
|
|
@@ -6659,7 +6783,8 @@ async function runSelectedPi(operation, targetDir, yes = false, resolvedEngramBi
|
|
|
6659
6783
|
operation,
|
|
6660
6784
|
targetDir,
|
|
6661
6785
|
detected: { executable: detected.executable, version: detected.version },
|
|
6662
|
-
engramBin
|
|
6786
|
+
engramBin,
|
|
6787
|
+
...devtoolsMcpEnabled === void 0 ? {} : { devtoolsMcpEnabled }
|
|
6663
6788
|
});
|
|
6664
6789
|
if (result.kind === "blocked") {
|
|
6665
6790
|
const paths = "paths" in result ? `: ${result.paths.join(", ")}` : "";
|
|
@@ -6795,7 +6920,7 @@ Flags disponibles: jorgex-stack --help`
|
|
|
6795
6920
|
}
|
|
6796
6921
|
const mode = fileRuntimes.length > 0 ? await resolveInstallMode(flags) : void 0;
|
|
6797
6922
|
if (mode === null) return;
|
|
6798
|
-
const devtoolsMcpSelection = await resolveDevtoolsMcpSelection(command, flags,
|
|
6923
|
+
const devtoolsMcpSelection = await resolveDevtoolsMcpSelection(command, flags, runtimes);
|
|
6799
6924
|
if (devtoolsMcpSelection === null) {
|
|
6800
6925
|
exitCode = process.exitCode === 1 ? 1 : 0;
|
|
6801
6926
|
return;
|
|
@@ -6852,7 +6977,8 @@ Flags disponibles: jorgex-stack --help`
|
|
|
6852
6977
|
command,
|
|
6853
6978
|
flags.targetDir,
|
|
6854
6979
|
flags.yes,
|
|
6855
|
-
flags.targetDir === void 0 ? engramBin : void 0
|
|
6980
|
+
flags.targetDir === void 0 ? engramBin : void 0,
|
|
6981
|
+
devtoolsMcpSelection.pi
|
|
6856
6982
|
));
|
|
6857
6983
|
}
|
|
6858
6984
|
completed = true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jorgex-stack",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.30",
|
|
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",
|