Skip to content
Back to skills

Run Matt Skills Ui

ASecurity

Run, test, screenshot or smoke-test the matt-skills-ui plugin (the web board for mattpocock/skills). Use to start the board server, check a change works in the real UI, or take screenshots of its four themes.

  • 3 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 6, 2026
ai-agentsgoshellbashnodegitapi

Works with

  • claude code
  • cursor
  • terminal
  • cli
  • api
  • mcp

Security analysis

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

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

Scanned October 6, 2026

npx -y skills add hsyam/matt-skills-ui --skill run-matt-skills-ui --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Run Matt Skills Ui?

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

Security grade badge for Run Matt Skills Ui
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/hsyam-run-matt-skills-ui/badge)](https://www.skillsdirectory.com/skills/hsyam-run-matt-skills-ui)

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-matt-skills-ui
description: Run, test, screenshot or smoke-test the matt-skills-ui plugin (the web board for mattpocock/skills). Use to start the board server, check a change works in the real UI, or take screenshots of its four themes.
metadata:
  internal: true
---

matt-skills-ui is a Claude Code plugin and a portable skill: a zero-dependency Node server (`skills/matt-skills-ui/server/`) plus a static web app (`skills/matt-skills-ui/web/`), both inside the skill folder so `npx skills add` copies them. Drive it with `driver.mjs` in this folder: it copies `test/fixtures/acme-local` (a repo in the exact formats mattpocock/skills writes) to a temp dir, starts the real server there, and talks to it over HTTP and the CLI. Paths below are relative to the plugin root (`matt-skills-ui/`).

## Prerequisites

Node ≥ 20 (tested on 24). No `npm install`. Screenshots use Playwright's `chrome-headless-shell` from `~/Library/Caches/ms-playwright` (or `~/.cache/ms-playwright`), falling back to Chrome; set `MSU_CHROME=<binary>` to override. `spawn` needs the `claude` CLI logged in.

## Run (agent path)

```bash
node .claude/skills/run-matt-skills-ui/driver.mjs smoke            # 16 API + CLI checks, ~10 s, free
node .claude/skills/run-matt-skills-ui/driver.mjs smoke --shots    # + one PNG per theme in $TMPDIR/matt-skills-ui-shots/
node .claude/skills/run-matt-skills-ui/driver.mjs spawn            # + a real headless claude agent (haiku, plan mode, ~$0.10)
node .claude/skills/run-matt-skills-ui/driver.mjs spawn --agent opencode   # same through opencode | codex | cursor | gemini | pi, read-only mode
node .claude/skills/run-matt-skills-ui/driver.mjs up               # start a board on a fresh fixture copy; prints the URL
node .claude/skills/run-matt-skills-ui/driver.mjs shot "<url>" out.png --theme transit --size 1440,900
node .claude/skills/run-matt-skills-ui/driver.mjs down             # stop every board the driver started
```

Each check prints `PASS`/`FAIL`; exit code is non-zero on any failure. Read the PNGs with the Read tool to look at them. For clicking through the UI, open the `up` URL in the in-app browser (Claude_Browser tools) or Playwright MCP; `?theme=swiss|terminal|transit|toybox` forces a theme without saving it.

Driver state lives in `$TMPDIR/matt-skills-ui-driver` (`MATT_SKILLS_UI_HOME`), never in `~/.matt-skills-ui`.

## Direct invocation

Most changes touch the readers or the next-move engine; test those without a server:

```bash
npm test                                                           # node --test test/*.test.mjs
node -e "import('./skills/matt-skills-ui/server/lib/model.mjs').then(async m => console.log(JSON.stringify(await m.buildModel('test/fixtures/acme-local'), null, 1)))"
```

`skills/matt-skills-ui/web/engine.mjs` is plain ESM shared by the browser and node: import it, `bind(state)`, call `nextFor` / `effortNext` / `stateOf`.

## Run (human path)

Load the plugin into a session and run its command in the repo you want to see:

```bash
claude --plugin-dir /path/to/matt-skills-ui
```

Then `/matt-skills-ui` (opens the browser), `/matt-skills-ui theme toybox`, `/matt-skills-ui stop`. Check manifests with `npm run validate`.

## Gotchas

- **Full Chrome `--headless` hangs** inside Claude Code's sandboxed Bash, even on `data:` URLs. `chrome-headless-shell` works. The driver prefers it.
- **`--virtual-time-budget` hangs** because the page holds an SSE connection open. Instead the server inlines the first state into `index.html` (`window.__STATE__`) when the URL carries the token, so the board paints before `load`. The driver uses `--timeout` plus `--force-prefers-reduced-motion`, which stops the load animation from showing up as a blank screenshot.
- **There's no `timeout` command on macOS.** To bound a process use `perl -e 'alarm 30; exec @ARGV' …`.
- `node --test test/` fails on Node 24 because the directory is treated as a module. Use the glob (`npm test`).
- **Skill bodies substitute** `${CLAUDE_SKILL_DIR}`, `${CLAUDE_PROJECT_DIR}`, `${CLAUDE_SESSION_ID}` and `$ARGUMENTS`. They do not substitute `${CLAUDE_PLUGIN_ROOT}`, `${CLAUDE_PLUGIN_DATA}` or `${user_config.*}`. So the theme picked at install is read from `pluginConfigs` in `~/.claude/settings.json` (`server/lib/config.mjs`), and data lives in `~/.matt-skills-ui`.
- **Spawned agents** go through the adapters in `skills/matt-skills-ui/server/lib/agents.mjs`. Claude runs `claude -p --input-format stream-json --output-format stream-json --verbose`. `--verbose` is required with stream-json. Each `result` event ends a turn, and the process stays alive waiting on stdin, which is how board replies continue the conversation. Every other agent runs one process per turn, and the next turn resumes the session id from the first turn's output. Their parser tests replay real CLI output from `test/fixtures/agents/`.
- **Spawned agents get `PWD` set to their working directory, and the preamble names the realpath.** The server's own `PWD` leaks otherwise, and OpenCode takes its project root from `PWD`. It then auto-rejects reads of the real repo as `external_directory`, and the turn ends silently (the rejection only shows on stderr). macOS's `/var` → `/private/var` symlink causes the same rejection.
- **`codex exec` reads stdin when it's piped**, so the adapter sends the prompt there (`-`). `opencode run` takes it after `--`, so a prompt starting with `-` isn't read as a flag.
- **Headless agents can't ask for permission.** Anything the settings don't allow is denied, so pick `acceptEdits`/`auto` for implementers.
- **Worktree spawns** land in `<data>/repos/<hash>/worktrees/<id>` on a branch `msu/<id>`. They are not cleaned up automatically.
- **The main session's context gauge** reads the last `usage` in `~/.claude/projects/<slug>/<session>.jsonl`. That's an internal format, so it's best-effort and shows "—" if the format changes.
- **`fs.watch` recursive** fires for `.git/` churn too. The server filters to the paths the skills write.

## Troubleshooting

- `FAIL listen: …` means a listener from an older run is consuming the outbox. Run `driver.mjs down` and retry; delivery is tracked server-side, so only one listener gets each message.
- `SKIP screenshot` or `screenshot failed`: no headless shell was found. Install one with `npx playwright install chromium-headless-shell`, or set `MSU_CHROME`.
- Board shows "Can’t reach the board server": the server died. Read `<MATT_SKILLS_UI_HOME>/repos/<hash>/server.log`.

Files in this skill

  • SKILL.md6.4 KB
  • driver.mjs10.6 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…