Skip to content
Hallway is out (npm i -g hallway): v0, v1 and v2 have shipped, apart from checking it all on a real Mac. See the roadmap.

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.

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.

CommandDecision
npm run devBlocked: hallway run my-app -- npm run dev
cd web && pnpm devBlocked: hallway run my-app -- pnpm dev
astro dev --port 4321Blocked: hallway run my-app -- astro dev --port '$PORT'
PORT=3000 npx next devBlocked: 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 installAllowed
vite --helpAllowed
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.

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.

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.

Delete the entry from the settings file. Nothing else was installed, so there is nothing else to undo.

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.