jorgex-stack 1.9.30 → 1.9.32

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 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. 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.
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 global Playwright package and Chromium cache are shared by the machine; `--playwright-runtimes` controls which runtime receives the guide. Pi is selectable only when the adopted package declares `playwright-handoff-v1`; otherwise Stack reports that it can install the global capability but cannot project it into Pi yet. 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
 
@@ -156,6 +156,9 @@ pnpm dlx jorgex-stack install
156
156
  # Non-interactive / agent: --playwright authorizes the global install.
157
157
  pnpm dlx jorgex-stack install --yes --playwright
158
158
 
159
+ # Instala el paquete compartido y activa la guía solo en estos runtimes.
160
+ pnpm dlx jorgex-stack install --playwright --playwright-runtimes=opencode,claude-code
161
+
159
162
  # Enable Chrome DevTools MCP explicitly per runtime.
160
163
  pnpm dlx jorgex-stack install --devtools
161
164
  pnpm dlx jorgex-stack install --no-devtools
@@ -163,7 +166,7 @@ pnpm dlx jorgex-stack install --agents pi --devtools
163
166
  pnpm dlx jorgex-stack sync --agents pi --no-devtools
164
167
  ```
165
168
 
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.
169
+ 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 chromium` (the Chromium cache). It then verifies that the pinned package launches Chromium headless against `about:blank`. Removal is `pnpm remove --global @playwright/cli` (no version suffix). If installation fails, the error identifies the failed phase — global package, browser download, browser launch, or preference persistence — and recommends `jorgex-stack install --playwright`; the preference is not marked enabled unless the complete plan succeeds.
167
170
 
168
171
  Daily operation:
169
172
 
@@ -179,7 +182,7 @@ Daily operation:
179
182
 
180
183
  1. **Stack** (jorgex-stack): detects whether it is a git clone or a global install, then offers an update with confirmation.
181
184
  2. **Engram** (binary): detects the installed version and offers an update through the **native channel** (brew -> `go install` -> release URL). Nothing needs to be stopped: as in upstream macOS/Linux, live processes keep using the old version until clients restart; on Windows, the in-use `.exe` is rotated by rename before installation. **Automatic DB backup before updating**. The database and memories are never touched.
182
- 3. **Playwright CLI** (only when explicitly enabled): compares the detected binary with the approved bundle pin and offers to realign it with explicit confirmation. The realignment re-applies **both** plans — `pnpm add --global @playwright/cli@0.1.18` (package) and `pnpm dlx @playwright/cli@0.1.18 install-browser` (browser cache) — and fails closed if either step returns non-zero. The error identifies whether the package-update or browser-download phase failed and recommends `jorgex-stack install --playwright` to retry both; a Playwright update does not require `sync`.
185
+ 3. **Playwright CLI** (only when explicitly enabled): compares the detected binary with the approved bundle pin and offers to realign it with explicit confirmation. The realignment re-applies **both** plans — `pnpm add --global @playwright/cli@0.1.18` (package) and `pnpm dlx @playwright/cli@0.1.18 install-browser chromium` (Chromium cache) — and fails closed if either step returns non-zero. The error identifies whether the package-update or browser-download phase failed and recommends `jorgex-stack install --playwright` to retry both; a Playwright update does not require `sync`.
183
186
  4. **Vendored skills** (maintainer only): third-party skills ship **pinned** with the stack version, so the installed package never reaches out to their upstreams. Only when running from a git clone (`pnpm cli update`) does `update` scan the upstreams in `upstreams.json`, download to a temp directory, **show a mandatory diff**, and ask for confirmation. A moved upstream is only a candidate until that review is accepted and a deliberate re-pin is made for a future release; it is never treated as an accepted official update automatically. Skills with local changes (`modified: true`) warn and require double confirmation.
184
187
 
185
188
  Usage:
package/dist/cli.d.ts CHANGED
@@ -23,6 +23,7 @@ interface Flags {
23
23
  removeEngram: boolean;
24
24
  engram: boolean;
25
25
  playwright: boolean;
26
+ playwrightRuntimes?: SelectableRuntimeId[];
26
27
  removePlaywright: boolean;
27
28
  devtools: boolean;
28
29
  noDevtools: boolean;
package/dist/cli.js CHANGED
@@ -2165,6 +2165,8 @@ function resolvePnpmFailureRemedy(reason) {
2165
2165
  return PNPM_GLOBAL_BIN_REMEDY;
2166
2166
  case "action-failed":
2167
2167
  return null;
2168
+ case "browser-launch":
2169
+ return "Chromium se descarg\xF3, pero no ha podido arrancar. Revisa el error de lanzamiento y las dependencias de tu sistema antes de reintentar.";
2168
2170
  }
2169
2171
  }
2170
2172
  function hasNonzeroProcessStatus(error) {
@@ -2186,6 +2188,18 @@ function resolvePlaywrightCliState({ binPath, versionOutput }) {
2186
2188
  }
2187
2189
  function detectPlaywrightCli() {
2188
2190
  const binPath = lookPath(PLAYWRIGHT_CLI.bin);
2191
+ if (binPath === null) {
2192
+ const pnpmHome = resolvePnpmHome();
2193
+ const name = process.platform === "win32" ? `${PLAYWRIGHT_CLI.bin}.cmd` : PLAYWRIGHT_CLI.bin;
2194
+ const known = [path18.join(pnpmHome, name), path18.join(pnpmHome, "bin", name)].find((file) => {
2195
+ try {
2196
+ return fs13.statSync(file).isFile();
2197
+ } catch {
2198
+ return false;
2199
+ }
2200
+ });
2201
+ if (known) return { status: "not-in-path", binPath: known, detectedVersion: null };
2202
+ }
2189
2203
  return resolvePlaywrightCliState({
2190
2204
  binPath,
2191
2205
  versionOutput: binPath ? runDetectedBin(binPath, ["--version"], 5e3, { NO_UPDATE_NOTIFIER: "1" }) : null
@@ -2248,7 +2262,44 @@ function planPlaywrightCliCommand(action, pnpmBin) {
2248
2262
  case "remove":
2249
2263
  return { command: pnpmBin, args: ["remove", "--global", PLAYWRIGHT_CLI.packageName] };
2250
2264
  case "install-browser":
2251
- return { command: pnpmBin, args: ["dlx", pinnedPackage, PLAYWRIGHT_CLI.browserInstallAction] };
2265
+ return { command: pnpmBin, args: ["dlx", pinnedPackage, PLAYWRIGHT_CLI.browserInstallAction, "chromium"] };
2266
+ }
2267
+ }
2268
+ function verifyPlaywrightBrowser(pnpmBin, env = process.env, cwd) {
2269
+ const rootCommand = planDetectedBinCommand(pnpmBin, ["root", "--global"]);
2270
+ if (rootCommand === null) return false;
2271
+ try {
2272
+ const root = execFileSync2(rootCommand.command, rootCommand.args, {
2273
+ encoding: "utf8",
2274
+ timeout: 5e3,
2275
+ env,
2276
+ cwd,
2277
+ stdio: ["ignore", "pipe", "inherit"]
2278
+ }).trim();
2279
+ if (!path18.isAbsolute(root)) return false;
2280
+ const packageFile = fs13.realpathSync(path18.join(root, "@playwright", "cli", "package.json"));
2281
+ const installed = JSON.parse(fs13.readFileSync(packageFile, "utf8"));
2282
+ if (installed.name !== PLAYWRIGHT_CLI.packageName || installed.version !== PLAYWRIGHT_CLI.version) return false;
2283
+ const probe = `
2284
+ const { createRequire } = require('node:module');
2285
+ const { chromium } = createRequire(process.argv[1])('playwright');
2286
+ (async () => {
2287
+ const browser = await chromium.launch({ headless: true, timeout: 15000 });
2288
+ try {
2289
+ const page = await browser.newPage();
2290
+ await page.goto('about:blank', { timeout: 5000 });
2291
+ } finally { await browser.close(); }
2292
+ })().catch(error => { console.error(error.message); process.exitCode = 1; });
2293
+ `;
2294
+ execFileSync2(process.execPath, ["-e", probe, packageFile], {
2295
+ timeout: 25e3,
2296
+ stdio: "inherit",
2297
+ cwd,
2298
+ env: { ...env, NO_UPDATE_NOTIFIER: "1" }
2299
+ });
2300
+ return true;
2301
+ } catch {
2302
+ return false;
2252
2303
  }
2253
2304
  }
2254
2305
  function executePlaywrightToolAction(action, pnpmBin = resolvePnpmBin(), env) {
@@ -2257,10 +2308,12 @@ function executePlaywrightToolAction(action, pnpmBin = resolvePnpmBin(), env) {
2257
2308
  const preflight = planDetectedBinCommand(pnpmBin, ["bin", "--global"]);
2258
2309
  if (preflight === null) return { ok: false, reason: "pnpm-command" };
2259
2310
  try {
2260
- execFileSync2(preflight.command, preflight.args, {
2261
- stdio: "inherit",
2311
+ const globalBin = execFileSync2(preflight.command, preflight.args, {
2312
+ encoding: "utf8",
2313
+ stdio: ["ignore", "pipe", "inherit"],
2262
2314
  ...env === void 0 ? {} : { env }
2263
2315
  });
2316
+ if (globalBin.trim() === "") return { ok: false, reason: "pnpm-global-bin" };
2264
2317
  } catch (error) {
2265
2318
  return { ok: false, reason: hasNonzeroProcessStatus(error) ? "pnpm-global-bin" : "pnpm-command" };
2266
2319
  }
@@ -2268,14 +2321,22 @@ function executePlaywrightToolAction(action, pnpmBin = resolvePnpmBin(), env) {
2268
2321
  const command = planPlaywrightCliCommand(action, pnpmBin);
2269
2322
  const invocation = planDetectedBinCommand(command.command, command.args);
2270
2323
  if (invocation === null) return { ok: false, reason: "pnpm-command" };
2324
+ const cwd = action === "install-browser" ? fs13.mkdtempSync(path18.join(os.tmpdir(), "jorgex-playwright-")) : void 0;
2325
+ const childEnv = action === "install-browser" ? { ...env ?? process.env, NO_UPDATE_NOTIFIER: "1" } : env ?? process.env;
2271
2326
  try {
2272
2327
  execFileSync2(invocation.command, invocation.args, {
2273
2328
  stdio: "inherit",
2274
- ...env === void 0 ? {} : { env }
2329
+ env: childEnv,
2330
+ ...cwd === void 0 ? {} : { cwd }
2275
2331
  });
2332
+ if (action === "install-browser" && !verifyPlaywrightBrowser(pnpmBin, childEnv, cwd)) {
2333
+ return { ok: false, reason: "browser-launch" };
2334
+ }
2276
2335
  return { ok: true };
2277
2336
  } catch {
2278
2337
  return { ok: false, reason: "action-failed" };
2338
+ } finally {
2339
+ if (cwd !== void 0) fs13.rmSync(cwd, { recursive: true, force: true });
2279
2340
  }
2280
2341
  }
2281
2342
 
@@ -2285,6 +2346,7 @@ import path19 from "path";
2285
2346
  var PLAYWRIGHT_CLI_PREFERENCE_VERSION = 1;
2286
2347
  var DEVTOOLS_MCP_PREFERENCE_VERSION = 1;
2287
2348
  var PRIMARY_MODEL_OWNERSHIP_VERSION = 1;
2349
+ var PLAYWRIGHT_RUNTIMES = ["opencode", "claude-code", "codex", "pi"];
2288
2350
  function readPreference(file) {
2289
2351
  try {
2290
2352
  return { raw: fs14.readFileSync(file, "utf8"), errorCode: null };
@@ -2299,8 +2361,11 @@ function playwrightCliPreferenceFile(stateDir = dataDir()) {
2299
2361
  function parsePlaywrightCliPreference(raw) {
2300
2362
  try {
2301
2363
  const value = JSON.parse(raw);
2302
- if (value.version !== PLAYWRIGHT_CLI_PREFERENCE_VERSION || typeof value.enabled !== "boolean") return void 0;
2303
- return value.enabled;
2364
+ if (!isRecord2(value)) return void 0;
2365
+ if (value.version === 1 && typeof value.enabled === "boolean") return { version: 1, enabled: value.enabled };
2366
+ if (value.version !== 2 || !isRecord2(value.enabled)) return void 0;
2367
+ if (Object.entries(value.enabled).some(([runtime, enabled]) => !PLAYWRIGHT_RUNTIMES.includes(runtime) || typeof enabled !== "boolean")) return void 0;
2368
+ return { version: 2, enabled: value.enabled };
2304
2369
  } catch {
2305
2370
  return void 0;
2306
2371
  }
@@ -2313,15 +2378,28 @@ function playwrightCliPreferenceError(file = playwrightCliPreferenceFile()) {
2313
2378
  if (raw === null || parsePlaywrightCliPreference(raw) !== void 0) return null;
2314
2379
  return `Playwright CLI: preferencia inv\xE1lida en ${file}. Corrige o borra ese archivo antes de reintentar.`;
2315
2380
  }
2316
- function loadPlaywrightCliPreference(file = playwrightCliPreferenceFile()) {
2381
+ function loadPlaywrightCliPreference(file = playwrightCliPreferenceFile(), runtime) {
2317
2382
  const { raw } = readPreference(file);
2318
- if (raw === null) return void 0;
2319
- return parsePlaywrightCliPreference(raw);
2383
+ const state = raw === null ? void 0 : parsePlaywrightCliPreference(raw);
2384
+ if (state === void 0) return void 0;
2385
+ if (state.version === 1) return state.enabled;
2386
+ return runtime === void 0 ? Object.values(state.enabled).some(Boolean) : state.enabled[runtime] === true;
2320
2387
  }
2321
- function savePlaywrightCliPreference(file, enabled) {
2388
+ function savePlaywrightCliPreference(file, enabled, selection) {
2322
2389
  const error = playwrightCliPreferenceError(file);
2323
2390
  if (error !== null) throw new Error(error);
2324
- writeText(file, JSON.stringify({ version: PLAYWRIGHT_CLI_PREFERENCE_VERSION, enabled }) + "\n");
2391
+ const { raw } = readPreference(file);
2392
+ const previous = raw === null ? void 0 : parsePlaywrightCliPreference(raw);
2393
+ if (selection === void 0 && previous?.version !== 2) {
2394
+ writeText(file, JSON.stringify({ version: PLAYWRIGHT_CLI_PREFERENCE_VERSION, enabled }) + "\n");
2395
+ return;
2396
+ }
2397
+ const inherited = previous?.version === 2 ? previous.enabled : Object.fromEntries(PLAYWRIGHT_RUNTIMES.map((runtime) => [runtime, previous?.enabled === true]));
2398
+ const choices = { ...inherited, ...selection };
2399
+ const state = { version: 2, enabled: enabled ? choices : Object.fromEntries(Object.keys(choices).map((runtime) => [runtime, false])) };
2400
+ const content = JSON.stringify(state) + "\n";
2401
+ if (parsePlaywrightCliPreference(content) === void 0) throw new Error("Playwright CLI: selecci\xF3n de runtimes inv\xE1lida.");
2402
+ writeText(file, content);
2325
2403
  }
2326
2404
  function devtoolsMcpPreferenceFile(stateDir = dataDir()) {
2327
2405
  return path19.join(stateDir, "devtools-mcp.json");
@@ -2534,7 +2612,7 @@ function makeContext(adapter, configDir, mode = DEFAULT_INSTALL_MODE_PREFERENCE,
2534
2612
  models,
2535
2613
  warnings: [],
2536
2614
  enabledMcpServers: enabledMcpServers(adapter.id, void 0, useBrowserPreferences),
2537
- playwrightCliEnabled: useBrowserPreferences && loadPlaywrightCliPreference() === true,
2615
+ playwrightCliEnabled: useBrowserPreferences && loadPlaywrightCliPreference(playwrightCliPreferenceFile(), adapter.id) === true,
2538
2616
  ownedMcpServers: ownedMcpServers(adapter.id, useBrowserPreferences),
2539
2617
  ownedPrimaryModelFields: useBrowserPreferences ? loadPrimaryModelOwnership(primaryModelOwnershipFile(), adapter.id, configDir) : /* @__PURE__ */ new Set()
2540
2618
  };
@@ -2653,7 +2731,7 @@ async function runInstall(opts) {
2653
2731
  models,
2654
2732
  warnings: [],
2655
2733
  enabledMcpServers: enabledMcpServers(id, opts.devtoolsMcpSelection?.[id], useManifest),
2656
- playwrightCliEnabled: projectPlaywrightPrompt || useManifest && loadPlaywrightCliPreference() === true,
2734
+ playwrightCliEnabled: projectPlaywrightPrompt ? opts.playwrightToolConsent?.runtimeSelection?.[id] ?? true : useManifest && loadPlaywrightCliPreference(playwrightCliPreferenceFile(), id) === true,
2657
2735
  ownedMcpServers: ownedMcpServers(id, useManifest),
2658
2736
  ownedPrimaryModelFields: useManifest ? loadPrimaryModelOwnership(primaryModelOwnershipFile(), id, configDir) : /* @__PURE__ */ new Set()
2659
2737
  };
@@ -2748,7 +2826,7 @@ async function runInstall(opts) {
2748
2826
  const pnpmBin = resolvePnpmBin();
2749
2827
  return executePlaywrightToolAction2(action, pnpmBin, env);
2750
2828
  },
2751
- persistEnabled: (enabled) => savePlaywrightCliPreference(playwrightCliPreferenceFile(), enabled),
2829
+ persistEnabled: (enabled) => savePlaywrightCliPreference(playwrightCliPreferenceFile(), enabled, opts.playwrightToolConsent?.runtimeSelection),
2752
2830
  setupPnpm: (pnpmBin) => setupPnpmGlobal(pnpmBin)
2753
2831
  };
2754
2832
  let setupAttempted = false;
@@ -2776,6 +2854,7 @@ async function runInstall(opts) {
2776
2854
  return first;
2777
2855
  }
2778
2856
  preparedEnv = setup.env;
2857
+ p.log.info("pnpm preparado para esta instalaci\xF3n. Abre una terminal nueva al terminar para que otras herramientas y doctor reciban el PATH actualizado.");
2779
2858
  return baseDeps.run(action, preparedEnv);
2780
2859
  }
2781
2860
  });
@@ -2787,7 +2866,11 @@ async function runInstall(opts) {
2787
2866
  } else {
2788
2867
  let promptReconciliationFailed = false;
2789
2868
  for (const { adapter, ctx } of successfulContexts) {
2790
- const browserCtx = { ...ctx, playwrightCliEnabled: true, warnings: [] };
2869
+ const browserCtx = {
2870
+ ...ctx,
2871
+ playwrightCliEnabled: opts.playwrightToolConsent?.runtimeSelection?.[adapter.id] ?? true,
2872
+ warnings: []
2873
+ };
2791
2874
  try {
2792
2875
  const browserChanges = diffPlan(planSystemPrompt(adapter, browserCtx)).filter((change) => change.status !== "unchanged");
2793
2876
  if (browserChanges.length === 0) continue;
@@ -2810,7 +2893,7 @@ async function runInstall(opts) {
2810
2893
  exitCode = 1;
2811
2894
  p.log.error("Playwright CLI y navegador se han instalado y la preferencia est\xE1 activada, pero la gu\xEDa de navegador qued\xF3 en estado parcial. Ejecuta 'jorgex-stack sync' para repararla.");
2812
2895
  } else {
2813
- p.log.success("Playwright CLI y navegador instalados.");
2896
+ p.log.success("Playwright CLI instalado y arranque de Chromium verificado.");
2814
2897
  }
2815
2898
  }
2816
2899
  }
@@ -3952,6 +4035,7 @@ function engramVersion(bin) {
3952
4035
  }
3953
4036
  function resolvePlaywrightDoctorState(input) {
3954
4037
  if (input.enabled !== true) return { status: "disabled" };
4038
+ if (input.cli.status === "not-in-path") return { status: "not-in-path" };
3955
4039
  if (input.cli.status === "absent") return { status: "missing", missing: "package" };
3956
4040
  if (input.cli.status === "broken") return { status: "broken" };
3957
4041
  if (input.cli.status === "outdated") return { status: "outdated" };
@@ -4012,7 +4096,10 @@ async function runDoctor() {
4012
4096
  if (playwright.status === "disabled") {
4013
4097
  p3.log.info("Playwright CLI: deshabilitado (opcional). Usa 'install --playwright' para instalarlo de forma expl\xEDcita.");
4014
4098
  } else if (playwright.status === "healthy") {
4015
- p3.log.success("Playwright CLI: paquete y navegador listos.");
4099
+ p3.log.success("Playwright CLI: paquete y cach\xE9 de Chromium detectados (doctor no prueba el arranque).");
4100
+ } else if (playwright.status === "not-in-path") {
4101
+ p3.log.warn("Playwright CLI: instalado en el directorio de pnpm, pero fuera del PATH de esta terminal. Abre una terminal nueva tras pnpm setup; no hace falta reinstalarlo.");
4102
+ problems++;
4016
4103
  } else if (playwright.status === "missing") {
4017
4104
  const target = playwright.missing === "package" ? "el paquete global" : "el navegador de Playwright";
4018
4105
  p3.log.warn(`Playwright CLI: habilitado, pero falta ${target} \u2192 ejecuta 'jorgex-stack install --playwright'.`);
@@ -5478,9 +5565,12 @@ function normalizeInstalledSource(settingsJson, alias, canonical) {
5478
5565
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return null;
5479
5566
  const packages = Reflect.get(parsed, "packages");
5480
5567
  if (!Array.isArray(packages)) return null;
5481
- const hasCanonical = packages.some((entry) => entry === canonical || entry !== null && typeof entry === "object" && !Array.isArray(entry) && Reflect.get(entry, "source") === canonical);
5482
- if (packages.filter((entry) => entry === alias).length !== 1 || hasCanonical) return null;
5483
- Reflect.set(parsed, "packages", packages.map((entry) => entry === alias ? canonical : entry));
5568
+ const sourceOf = (entry) => entry !== null && typeof entry === "object" && !Array.isArray(entry) ? Reflect.get(entry, "source") : entry;
5569
+ if (packages.filter((entry) => sourceOf(entry) === alias).length !== 1 || packages.some((entry) => sourceOf(entry) === canonical)) return null;
5570
+ Reflect.set(parsed, "packages", packages.map((entry) => {
5571
+ if (sourceOf(entry) !== alias) return entry;
5572
+ return typeof entry === "string" ? canonical : { ...entry, source: canonical };
5573
+ }));
5484
5574
  return JSON.stringify(parsed);
5485
5575
  } catch {
5486
5576
  return null;
@@ -5943,7 +6033,7 @@ function managedPackageResult(result) {
5943
6033
  async function runManagedPiSystem(input) {
5944
6034
  const { devtoolsMcpEnabled: explicitDevtools, ...runtimeInput } = input;
5945
6035
  const devtoolsMcpEnabled = explicitDevtools ?? (input.targetDir === void 0 && loadDevtoolsMcpPreference(devtoolsMcpPreferenceFile(), "pi"));
5946
- const playwrightCliEnabled = input.targetDir === void 0 && loadPlaywrightCliPreference() === true;
6036
+ const playwrightCliEnabled = input.targetDir === void 0 && loadPlaywrightCliPreference(void 0, "pi") === true;
5947
6037
  const projectionInput = {
5948
6038
  targetDir: input.targetDir,
5949
6039
  packageSource: PI_RUNTIME_CANDIDATE.package.source,
@@ -6470,6 +6560,7 @@ var VERSION = readPackageVersion();
6470
6560
  var COMMANDS = ["install", "sync", "models", "update", "doctor", "restore", "uninstall", "quality"];
6471
6561
  var QUALITY_REJECTED_VALUE_FLAGS = /* @__PURE__ */ new Set([
6472
6562
  "--agents",
6563
+ "--playwright-runtimes",
6473
6564
  "-a",
6474
6565
  "--target-dir",
6475
6566
  "--mode",
@@ -6555,7 +6646,13 @@ function parseFlags(args, allowReceipt = false) {
6555
6646
  flags.subagentConcurrency = value ?? "";
6556
6647
  i = nextIndex;
6557
6648
  } else if (arg.startsWith("--subagent-concurrency=")) flags.subagentConcurrency = arg.slice(23);
6558
- else if (arg === "--dry-run") flags.dryRun = true;
6649
+ else if (arg === "--playwright-runtimes") {
6650
+ const [value, nextIndex] = readValue(i);
6651
+ flags.playwrightRuntimes = (value ?? "").split(",").filter(Boolean);
6652
+ i = nextIndex;
6653
+ } else if (arg.startsWith("--playwright-runtimes=")) {
6654
+ flags.playwrightRuntimes = arg.slice("--playwright-runtimes=".length).split(",").filter(Boolean);
6655
+ } else if (arg === "--dry-run") flags.dryRun = true;
6559
6656
  else if (arg === "--yes" || arg === "-y") flags.yes = true;
6560
6657
  else if (arg === "--help" || arg === "-h") flags.help = true;
6561
6658
  else if (arg === "--version" || arg === "-v") flags.version = true;
@@ -6633,8 +6730,25 @@ Corrige o borra ${preferenceFile}, o vuelve a ejecutar con --mode human|programm
6633
6730
  subagentConcurrency: concurrency
6634
6731
  };
6635
6732
  }
6636
- async function resolvePlaywrightToolConsent(command, flags) {
6733
+ async function resolvePlaywrightToolConsent(command, flags, runtimes) {
6637
6734
  const interactive = Boolean(process.stdout.isTTY);
6735
+ const supported = runtimes.filter((runtime) => runtime !== "pi" || PI_RUNTIME_CANDIDATE.contract.capabilities.includes("playwright-handoff-v1"));
6736
+ if (flags.playwrightRuntimes !== void 0) {
6737
+ const requested = flags.playwrightRuntimes;
6738
+ let error;
6739
+ if (requested.length === 0) error = "--playwright-runtimes requiere al menos un runtime.";
6740
+ for (const runtime of requested) {
6741
+ if (!["opencode", "claude-code", "codex", "pi"].includes(runtime)) error = `Runtime Playwright desconocido: ${runtime}.`;
6742
+ else if (!runtimes.includes(runtime)) error = `El runtime ${runtime} no est\xE1 en --agents/destinos de esta instalaci\xF3n.`;
6743
+ else if (!supported.includes(runtime)) error = "Pi requiere un candidato con playwright-handoff-v1 para activar Playwright.";
6744
+ if (error) break;
6745
+ }
6746
+ if (error) {
6747
+ console.error(error);
6748
+ process.exitCode = 1;
6749
+ return null;
6750
+ }
6751
+ }
6638
6752
  let confirmed = false;
6639
6753
  if (command === "install" && interactive && !flags.yes && !flags.dryRun && flags.targetDir === void 0) {
6640
6754
  const answer = await p6.confirm({
@@ -6644,13 +6758,33 @@ async function resolvePlaywrightToolConsent(command, flags) {
6644
6758
  if (p6.isCancel(answer)) return null;
6645
6759
  confirmed = answer === true;
6646
6760
  }
6761
+ let runtimeSelection;
6762
+ const approved = interactive && !flags.yes ? confirmed : flags.yes && flags.playwright;
6763
+ if (command === "install" && approved && supported.length > 0) {
6764
+ let selected = flags.playwrightRuntimes ?? supported;
6765
+ if (interactive && !flags.yes && !flags.dryRun && flags.targetDir === void 0 && flags.playwrightRuntimes === void 0) {
6766
+ const answer = await p6.multiselect({
6767
+ message: "\xBFEn qu\xE9 runtimes activar la gu\xEDa de Playwright? (instalaci\xF3n global compartida)",
6768
+ required: false,
6769
+ options: supported.map((runtime) => ({ value: runtime, label: runtime === "pi" ? "Pi" : ADAPTERS[runtime]?.name ?? runtime })),
6770
+ initialValues: supported.filter((runtime) => loadPlaywrightCliPreference(void 0, runtime) === true)
6771
+ });
6772
+ if (p6.isCancel(answer)) return null;
6773
+ selected = answer;
6774
+ }
6775
+ runtimeSelection = Object.fromEntries(supported.map((runtime) => [runtime, selected.includes(runtime)]));
6776
+ }
6777
+ if (command === "install" && approved && runtimes.includes("pi") && !supported.includes("pi")) {
6778
+ p6.log.info("La activaci\xF3n de Playwright en Pi requiere la pr\xF3xima adopci\xF3n del paquete Pi; el binario global s\xED puede instalarse.");
6779
+ }
6647
6780
  return {
6648
6781
  command,
6649
6782
  interactive,
6650
6783
  yes: flags.yes,
6651
6784
  targetDir: flags.targetDir !== void 0,
6652
6785
  explicitToolSelection: flags.playwright,
6653
- confirmed
6786
+ confirmed,
6787
+ ...runtimeSelection === void 0 ? {} : { runtimeSelection }
6654
6788
  };
6655
6789
  }
6656
6790
  async function resolveDevtoolsMcpSelection(command, flags, runtimes) {
@@ -6825,6 +6959,7 @@ Opciones:
6825
6959
  --no-devtools (install/sync) desactiva Chrome DevTools MCP (incompatible con --devtools)
6826
6960
  --remove-engram (uninstall) desregistra Engram de los runtimes;
6827
6961
  memorias y binario quedan intactos igualmente
6962
+ --playwright-runtimes <csv> Activa su gu\xEDa s\xF3lo en estos runtimes de --agents (con --playwright)
6828
6963
  --remove-playwright (uninstall) retira solo el paquete global de Playwright;
6829
6964
  nunca perfiles, cach\xE9 ni navegadores
6830
6965
  --receipt <path> (quality) escribe el receipt en ese path de forma at\xF3mica
@@ -6859,6 +6994,11 @@ Flags disponibles: jorgex-stack --help`
6859
6994
  process.exitCode = 1;
6860
6995
  return;
6861
6996
  }
6997
+ if (flags.playwrightRuntimes !== void 0 && (command !== "install" || !flags.playwright)) {
6998
+ console.error("--playwright-runtimes requiere install --playwright.");
6999
+ process.exitCode = 1;
7000
+ return;
7001
+ }
6862
7002
  if (command !== "quality" && flags.targetDir !== void 0 && flags.agents.length !== 1) {
6863
7003
  console.error("--target-dir requiere exactamente un runtime en --agents.");
6864
7004
  process.exitCode = 1;
@@ -6925,8 +7065,11 @@ Flags disponibles: jorgex-stack --help`
6925
7065
  exitCode = process.exitCode === 1 ? 1 : 0;
6926
7066
  return;
6927
7067
  }
6928
- const playwrightToolConsent = await resolvePlaywrightToolConsent(command, flags);
6929
- if (playwrightToolConsent === null) return;
7068
+ const playwrightToolConsent = await resolvePlaywrightToolConsent(command, flags, runtimes);
7069
+ if (playwrightToolConsent === null) {
7070
+ exitCode = process.exitCode === 1 ? 1 : 0;
7071
+ return;
7072
+ }
6930
7073
  if (!await ensureOpenCodeModelsForInstall(command, flags, fileRuntimes)) {
6931
7074
  exitCode = 1;
6932
7075
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jorgex-stack",
3
- "version": "1.9.30",
3
+ "version": "1.9.32",
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",