Skip to content
Back to skills

Run Minmux

ASecurity

Launch and drive the real minmux Electron app (Playwright) to verify a change end-to-end — terminals, panes/surfaces, drag & drop, themes, sidebar, Claude hook/transcript features. Use when asked to run/screenshot the app or confirm a change works beyond Vitest.

  • 6 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 2, 2026
developmentgoshellbashnode

Works with

  • claude code
  • terminal
  • cli

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 2 files and shows the line behind each finding

Scanned October 4, 2026

npx -y skills add vcmf/minmux --skill run-minmux --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Run Minmux?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Run Minmux
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/vcmf-run-minmux-minmux/badge)](https://www.skillsdirectory.com/skills/vcmf-run-minmux-minmux)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: run-minmux
description: Launch and drive the real minmux Electron app (Playwright) to verify a change end-to-end — terminals, panes/surfaces, drag & drop, themes, sidebar, Claude hook/transcript features. Use when asked to run/screenshot the app or confirm a change works beyond Vitest.
---

# Drive the real minmux app

Vitest can't load `node-pty` or WebGL, so anything touching `terminal-manager.ts`, real PTYs,
rendering, focus, or Claude integrations needs a check in the **built** app. `driver.mjs` wraps
Playwright's Electron support with the traps below already handled.

```bash
# playwright-core is a devDependency — `npm install` if it's missing (NOT in a worktree whose
# node_modules is a symlink: see Traps)
npx electron-vite build          # the driver runs out/ — rebuild after every change
node .claude/skills/run-minmux/driver.mjs smoke   # → "smoke: PASS — screenshot …"
```

Write a small script for your scenario (keep it in your scratch dir, not the repo):

```js
import { launch } from "<repo>/.claude/skills/run-minmux/driver.mjs"
const s = await launch({ settings: { renderer: "dom" } }) // dom → terminal text is readable
try {
  await s.sleep(1200) // shell startup
  await s.type("echo hi\n") // → the focused terminal; s.type(text, 1) targets pane #2
  await s.page.locator('[title="Split right"]').first().click()
  console.log(await s.terminalText())
  console.log(await s.shot("after-split")) // then LOOK at the PNG
  const [first] = await s.panes() // tab 1's panes: { session, surfaces }
  s.dropHook(first.session, {
    hook_event_name: "Stop",
    session_id: "c1",
    last_assistant_message: "…",
  })
} finally {
  await s.quit()
}
```

## Traps (why the driver looks the way it does)

- **Never touch the user's real config.** `launch()` points `HOME` (and `APPDATA` on Windows —
  where the config dir lives there) at a throwaway dir, so settings.json, workspace.json,
  claude-hooks.json and window-bg are all scratch; plus a throwaway `--user-data-dir`
  (localStorage + the single-instance lock, so it runs beside a real minmux). A real-HOME run
  overwrites the user's saved layout and hook settings. It also strips `ELECTRON_RENDERER_URL`,
  which a dev minmux leaks into its shells and would load dev-server code instead of `out/`.
- **Don't `app.close()`** — the quit guard opens a native "PTYs are running" dialog and the call
  hangs. `s.quit()` kills the process.
- **WebGL text isn't in the DOM.** Assert text with `settings: { renderer: "dom" }`; judge
  visuals (themes, borders, WebGL garble) from screenshots.
- **`[title="Close"]` also matches the window's close button** (it comes first in the DOM) —
  scope selectors, e.g. `.settings-panel [title="Close"]`.
- **`gh` loses its login under a fake HOME** (macOS Keychain lookup goes through `$HOME`) —
  pass `launch({ gh: true })` for PR/sidebar checks (token stays in the process env).
- **Simulate Claude Code** with `s.dropHook(paneId, payload)` — the same file drop the injected
  hook command performs (into `hook-events/<nonce>/claude/`; a third argument picks another
  agent's folder). Payload = Claude's raw hook JSON (`hook_event_name`, `session_id`,
  `transcript_path`, `last_assistant_message`, …). For `/color` / `/rename`, point
  `transcript_path` at a scratch JSONL and append `{"type":"agent-color","agentColor":"orange"}`
  / `{"type":"custom-title","customTitle":"…"}` lines (slash commands fire no hook — the
  transcript watch picks them up).
- **Real mouse vs dispatched events:** `locator.click()` / `page.mouse` exercise real focus
  behaviour (the close-dialog Enter bug only showed with a real click); `dispatchEvent` doesn't.
  HTML5 drag & drop works with `page.mouse.down/move/up`.
- **Worktrees:** a symlinked `node_modules` is fine for building/running, but never run
  `npm install` inside such a worktree — npm replaces the symlink with a fresh, incomplete tree.

Files in this skill

  • SKILL.md3.8 KB
  • driver.mjs7.8 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…