Playwright interactive
A skill library for AI coding agents with a CI quality gate: 38 skills, four-target sync, and an 18-check eval harness that fails the build on malformed skills.
npx -y skills add VJDiPaola/skill-forge --skill playwright-interactiveAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
3 things to look at
- 12 days oldThe repository was created 12 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Interactively debug a web or Electron UI via Playwright in a persistent js_repl session. Use when debugging a web or Electron UI interactively and browser state must persist between commands.
SKILL.md
11.6 KB, ~2.5k tokens by cl100k_base, as published. Nobody here has run it
Playwright Interactive Skill
Use a persistent js_repl Playwright session to debug local web or Electron apps, keep the same handles alive across iterations, and run functional plus visual QA without restarting the whole toolchain unless the process ownership changed.
Preconditions
js_replmust be enabled for this skill.- If
js_replis missing, enable it in~/.codex/config.toml:
[features]
js_repl = true
- You can also start a new session with
--enable js_repl(equivalent to-c features.js_repl=true). - After enabling
js_repl, start a new Codex session so the tool list refreshes. - For now, run this workflow with sandboxing disabled: start Codex with
--sandbox danger-full-access(or the equivalent config forsandbox_mode=danger-full-access). This is a temporary requirement whilejs_repl+ Playwright support inside the sandbox is still being completed. - Run setup from the same project directory you need to debug.
- Treat
js_repl_resetas a recovery tool, not routine cleanup. Resetting the kernel destroys your Playwright handles.
One-time setup
test -f package.json || npm init -y
npm install playwright
# Web-only, for headed Chromium or mobile emulation:
# npx playwright install chromium
# Electron-only, and only if the target workspace is the app itself:
# npm install --save-dev electron
node -e "import('playwright').then(() => console.log('playwright import ok')).catch((error) => { console.error(error); process.exit(1); })"
If you switch to a different workspace later, repeat setup there.
Core Workflow
- Write a brief QA inventory before testing:
- Build the inventory from three sources: the user's requested requirements, the user-visible features or behaviors you actually implemented, and the claims you expect to make in the final response.
- Anything that appears in any of those three sources must map to at least one QA check before signoff.
- List the user-visible claims you intend to sign off on.
- List every meaningful user-facing control, mode switch, or implemented interactive behavior.
- List the state changes or view changes each control or implemented behavior can cause.
- Use this as the shared coverage list for both functional QA and visual QA.
- For each claim or control-state pair, note the intended functional check, the specific state where the visual check must happen, and the evidence you expect to capture.
- If a requirement is visually central but subjective, convert it into an observable QA check instead of leaving it implicit.
- Add at least 2 exploratory or off-happy-path scenarios that could expose fragile behavior.
- Run the bootstrap cell once.
- Start or confirm any required dev server in a persistent TTY session.
- Launch the correct runtime and keep reusing the same Playwright handles.
- After each code change, reload for renderer-only changes or relaunch for main-process/startup changes.
- Run functional QA with normal user input.
- Run a separate visual QA pass.
- Verify viewport fit and capture the screenshots needed to support your claims.
- Clean up the Playwright session only when the task is actually finished.
Bootstrap (Run Once)
var chromium;
var electronLauncher;
var browser;
var context;
var page;
var mobileContext;
var mobilePage;
var electronApp;
var appWindow;
try {
({ chromium, _electron: electronLauncher } = await import("playwright"));
console.log("Playwright loaded");
} catch (error) {
throw new Error(
`Could not load playwright from the current js_repl cwd. Run the setup commands from this workspace first. Original error: ${error}`
);
}
Binding rules:
- Use
varfor the shared top-level Playwright handles because laterjs_replcells reuse them. - The setup cells below are intentionally short happy paths. If a handle looks stale, set that binding to
undefinedand rerun the cell instead of adding recovery logic everywhere. - Prefer one named handle per surface you care about (
page,mobilePage,appWindow) over repeatedly rediscovering pages from the context.
Shared web helpers:
var resetWebHandles = function () {
context = undefined;
page = undefined;
mobileContext = undefined;
mobilePage = undefined;
};
var ensureWebBrowser = async function () {
if (browser && !browser.isConnected()) {
browser = undefined;
resetWebHandles();
}
browser ??= await chromium.launch({ headless: false });
return browser;
};
var reloadWebContexts = async function () {
for (const currentContext of [context, mobileContext]) {
if (!currentContext) continue;
for (const p of currentContext.pages()) {
await p.reload({ waitUntil: "domcontentloaded" });
}
}
console.log("Reloaded existing web tabs");
};
Choose Session Mode
For web apps, use an explicit viewport by default and treat native-window mode as a separate validation pass.
- Use an explicit viewport for routine iteration, breakpoint checks, reproducible screenshots, snapshot diffs, and model-assisted localization. This is the default because it is stable across machines and avoids host window-manager variability.
- When you need deterministic high-DPI behavior, keep the explicit viewport and add
deviceScaleFactorrather than switching straight to native-window mode. - Use native-window mode (
viewport: null) for a separate headed pass when you need to validate launched window size, OS-level DPI behavior, browser chrome interactions, or bugs that may depend on the host display configuration. - For Electron, assume native-window behavior all the time. Electron launches through Playwright with
noDefaultViewport, so treat it like a real desktop window and check the as-launched size and layout before resizing anything. - When signoff depends on both layout breakpoints and real desktop behavior, do both passes: explicit viewport first for deterministic QA, then native-window validation for final environment-specific checks.
- Treat switching modes as a context reset. Do not reuse a viewport-emulated
contextfor a native-window pass or vice versa; close the oldpageandcontext, then create a new one for the new mode.
Start or Reuse a Session
Copy-paste cells for every session type live in references/session-recipes.md:
- Desktop web context (explicit 1600x900 viewport)
- Mobile web context (390x844, touch)
- Native-window web pass (
viewport: null, headed) - Electron session (launch, reuse, staleness resets)
Read that file when starting a session; the sections below assume one is running.
Reuse Sessions During Iteration
Keep the same session alive whenever you can.
Web renderer reload:
await reloadWebContexts();
Electron renderer-only reload:
await appWindow.reload({ waitUntil: "domcontentloaded" });
console.log("Reloaded Electron window");
Electron restart after main-process, preload, or startup changes:
await electronApp.close().catch(() => {});
electronApp = undefined;
appWindow = undefined;
electronApp = await electronLauncher.launch({
args: [ELECTRON_ENTRY],
});
appWindow = await electronApp.firstWindow();
console.log("Relaunched Electron window:", await appWindow.title());
If your launch requires an explicit cwd, include the same cwd here.
Default posture:
- Keep each
js_replcell short and focused on one interaction burst. - Reuse the existing top-level bindings (
browser,context,page,electronApp,appWindow) instead of redeclaring them. - If you need isolation, create a new page or a new context inside the same browser.
- For Electron, use
electronApp.evaluate(...)only for main-process inspection or purpose-built diagnostics. - Fix helper mistakes in place; do not reset the REPL unless the kernel is actually broken.
Checklists, Screenshots, and Viewport Fit
Deep reference material for this skill lives in references/. Load the relevant file when you reach that phase; do not inline all three at once.
references/qa-checklists.md— Session Loop, Reload Decision, Functional QA, Visual QA, and Signoff checklists. Work through these in order during each iteration.references/screenshots.md— Canonical patterns for emitting screenshots withcodex.emitImage(...), including CSS-pixel normalization for web and Electron, native-window fallback, and raw-exception cases.references/viewport-fit.md— Required viewport-fit checks before signoff, with web and Electron numeric-check recipes.
Dev Server
For local web debugging, keep the app running in a persistent TTY session. Do not rely on one-shot background commands from a short-lived shell.
Use the project's normal start command, for example:
npm start
Before page.goto(...), verify the chosen port is listening and the app responds.
For Electron debugging, launch the app from js_repl through _electron.launch(...) so the same session owns the process. If the Electron renderer depends on a separate dev server (for example Vite or Next), keep that server running in a persistent TTY session and then relaunch or reload the Electron app from js_repl.
Cleanup
Only run cleanup when the task is actually finished:
- This cleanup is manual. Exiting Codex, closing the terminal, or losing the
js_replsession does not implicitly runelectronApp.close(),context.close(), orbrowser.close(). - For Electron specifically, assume the app may keep running if you leave the session without executing the cleanup cell first.
if (electronApp) {
await electronApp.close().catch(() => {});
}
if (mobileContext) {
await mobileContext.close().catch(() => {});
}
if (context) {
await context.close().catch(() => {});
}
if (browser) {
await browser.close().catch(() => {});
}
browser = undefined;
context = undefined;
page = undefined;
mobileContext = undefined;
mobilePage = undefined;
electronApp = undefined;
appWindow = undefined;
console.log("Playwright session closed");
If you plan to exit Codex immediately after debugging, run the cleanup cell first and wait for the "Playwright session closed" log before quitting.
Common Failure Modes
Cannot find module 'playwright': run the one-time setup in the current workspace and verify the import before usingjs_repl.- Playwright package is installed but the browser executable is missing: run
npx playwright install chromium. page.goto: net::ERR_CONNECTION_REFUSED: make sure the dev server is still running in a persistent TTY session, recheck the port, and preferhttp://127.0.0.1:<port>.electron.launchhangs, times out, or exits immediately: verify the localelectrondependency, confirm theargstarget, and make sure any renderer dev server is already running before launch.Identifier has already been declared: reuse the existing top-level bindings, choose a new name, or wrap the code in{ ... }. Usejs_repl_resetonly when the kernel is genuinely stuck.browserContext.newPage: Protocol error (Target.createTarget): Not supportedwhile working with Electron: do not useappWindow.context().newPage()orelectronApp.context().newPage()as a scratch page; use the Electron-specific screenshot normalization flow in the model-bound screenshots section.js_repltimed out or reset: rerun the bootstrap cell and recreate the session with shorter, more focused cells.- Browser launch or network operations fail immediately: confirm the session was started with
--sandbox danger-full-accessand restart that way if needed.