CLI Reference
The paintless command — the local dev host that the widget and extension connect to.
The paintless CLI runs the local dev host: a WebSocket server the widget or
extension connects to, which applies agent edits to your working tree and reports
progress back to the browser. It is dev-mode only — production uses the
server.
npx paintless devpaintless dev
Starts the dev host. Without a config file it runs the Claude Code agent against
your project; with a paintless.config.* it uses whatever agents and
destinations you declare (see Configuration).
paintless dev [--port <port>] [--host <addr>] [--root <dir>] [--config <file>] [--allow-origin <url>]| Flag | Default | Purpose |
|---|---|---|
--port <port> | 7327 | WebSocket port the widget connects to. Must match the widget's host if you change it. |
--host <addr> | 127.0.0.1 | Interface to bind. The socket is unauthenticated, so binding anything else exposes your repo to that network — the host warns when you do. |
--root <dir> | current directory | Project root the agent edits and commits in. Everything the agent can touch lives under this path. |
--config <file> | auto-detected | Path to a paintless.config.{ts,js,mjs}. TypeScript is loaded through jiti, so no build step. |
--allow-origin <url> | — | Extra browser origin allowed to connect. Repeatable. See below. |
--help, -h | — | Print usage. |
The default port 7327 is also the widget's default host — change one and you
must change the other.
--root is the agent's boundary
Whatever you pass as --root is the entire surface the agent can read and edit,
and where commits land. Point it at the project you are working on. Pointing it
at a monorepo root means the agent may touch any package in it.
Windows 8.3 short paths (KYECHA~1) break the embedded agent's path checks, so
the CLI expands the root through realpathSync.native. Pass long-form paths when
you script it.
Origin allow-list (--allow-origin)
The dev host only accepts WebSocket connections from localhost / loopback
origins. This blocks cross-site WebSocket hijacking — a malicious page you visit
while paintless dev is running must not be able to push code changes into your
tree. Non-browser clients (tests, tooling) send no Origin header and are
allowed.
To connect from another origin — a dev server on a LAN address, say — add it explicitly, repeating the flag per origin:
paintless dev --allow-origin http://192.168.1.20:5173 --allow-origin https://staging.localSee Security for the full posture.
What a session looks like
- The widget connects and handshakes (
hello). - You pick an element and submit — the host receives a
ChangeRequest, status goesrunning. - The agent edits the working tree in place; HMR shows it. Status goes
pending— the change is applied but uncommitted. - You Commit (a clean git commit) or Discard (revert) from the browser.
Only one change may await review at a time, and changes across all connected tabs are serialized, so two agents never edit the same tree at once.
Programmatic use
The package also exports startDevServer for embedding the host in your own
tooling:
import { startDevServer } from 'paintless'
import { loadConfig } from 'paintless'
const config = await loadConfig(root)
startDevServer({ root, config, port: 7327, allowedOrigins: [] })