The dev-server hook
v2 Available since 0.2.0. Instruction files help, but agents still
reach for npm run dev out of habit, and every server started that way dies with the session. hallway hook check-command is an optional Claude Code PreToolUse hook for
the Bash tool that catches those commands before they run and tells the agent the
hallway run equivalent.
It is never installed automatically: you add it, and you can remove it.
What it does
Section titled “What it does”The hook reads Claude Code’s payload on stdin. If the command starts a dev or preview server directly, it prints
Claude Code’s JSON decision, a deny with a reason (hookSpecificOutput.permissionDecision: "deny"), and exits 0.
The agent sees the reason and can rerun the server the right way. Anything else gets no output, and the command runs
as normal.
It recognises the common dev-server commands, including npm run dev, npm start, pnpm dev, yarn dev,
bun run dev, vite, astro dev, next dev, nuxi dev, ng serve, python -m http.server,
manage.py runserver, uvicorn, flask run, php -S, hugo server, jekyll serve and npx serve. It still
finds them behind cd dir &&, nohup … &, environment assignments, and npx, pnpm exec or uv run.
| Command | Decision |
|---|---|
npm run dev | Blocked: hallway run my-app -- npm run dev |
cd web && pnpm dev | Blocked: hallway run my-app -- pnpm dev |
astro dev --port 4321 | Blocked: hallway run my-app -- astro dev --port '$PORT' |
PORT=3000 npx next dev | Blocked: hallway run my-app -- npx next dev -p '$PORT' |
nohup python -m http.server 8000 & | Blocked: hallway run my-app -- python -m http.server '$PORT' |
npm run build, npm test, npm install | Allowed |
vite --help | Allowed |
echo "npm run dev" | Allowed (a mention inside a string or comment) |
hallway run my-app -- vite --port '$PORT' | Allowed |
The suggested slug comes from the name of the working directory (here, ~/Code/my-app), and a fixed port such as
--port 3000 becomes '$PORT', so the server listens on the port Hallway assigns. Anything the hook can’t parse is
let through: it would rather miss a server than block a command it doesn’t understand.
The message the agent sees
Section titled “The message the agent sees”For vite --port 3000, run in ~/Code/my-app:
Hallway: don't start a Vite dev server directly (`vite --port 3000`): it would die with this session and land on a port nobody can find.Run it with Hallway instead, which keeps it running outside this session on a stable port and prints its URL:
hallway run my-app -- vite --port '$PORT'
`hallway run my-app` again restarts it; `hallway status` lists what's running; `hallway logs my-app` shows its output.When the command has no port flag for Hallway to rewrite, the message also says that the server must listen on
$PORT, and how to pass it.
Installing it
Section titled “Installing it”Add this to .claude/settings.json in a project, for that project only, or to ~/.claude/settings.json, for every
project:
{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [{ "type": "command", "command": "hallway hook check-command" }] } ] }}If the file already has a hooks section, add the entry to its PreToolUse list rather than replacing it. The
hallway binary must be on the PATH Claude Code runs hooks with.
Removing it
Section titled “Removing it”Delete the entry from the settings file. Nothing else was installed, so there is nothing else to undo.
Known false positives
Section titled “Known false positives”npm start (and yarn start, and the like) is blocked even in a package whose start script isn’t a server, since
the hook only sees the command, not the script. The agent can ask you to run it, or you can run it yourself.