A local AI-assisted development environment: task board, a Fastify daemon that drives Claude Code over stream-json, and a spec-driven flow that blocks implementation until the investigation is approved.
Fastify 5 bound to 127.0.0.1 spawns the Claude Code CLI with stream-json on both ends, no shell, arguments as an array. The prompt goes in over stdin, never argv, and the stream parser turns events, tool-use blocks and control requests into a live log on the WebSocket.
The permission level becomes CLI arguments. The default is acceptEdits plus an allowlist of 19 commands, with git push deliberately left out. The question channel uses permission-prompt-tool and the answer goes back through the process stdin, which turns every agent request into a toast I approve or deny in the UI.
Each task or project runs in vibe or spec mode. In spec mode the first run is a read-only investigation that returns JSON with refutations, hypothesis, evidence, files, acceptance criteria, tests and out-of-scope. With no approved spec, implementation cannot start. An approved spec can be versioned in the repository as specs/id-slug.md.
VibeDesk is the tool I use every day to run development with Claude Code. It puts the task board, a project's repositories, the rules the agent must respect and the execution itself in one place, with the log streaming while a task runs. It is not published: it is an internal tool, and this page exists because it holds engineering decisions worth more than the product.
The repository is an npm-workspaces monorepo with three applications:
127.0.0.1. It is what talks to the Claude Code CLI, to git and to gh.Roughly 16k lines of code and 2k lines of Vitest tests across 20 files in web and daemon.
Browsers do not spawn processes, read the disk or run git. Instead of pushing everything into Electron, the system work went into a local service with its own API. The frontend talks to it over HTTP and WebSocket, and behaves the same in a browser tab or inside the desktop app.
That made the security surface explicit, and it is deliberately small:
localhost, 127.0.0.1, [::1] and Electron's internal protocol.~/.vibedesk/daemon.json with 0600 permissions, and the API never returns the value, only a hint that one exists.Spec mode exists because an agent that starts writing too early produces plausible, wrong code. The cycle is:
Asking for refutations before the hypothesis is intentional: it forces a list of what the code contradicts, not only what confirms the first idea.
~/.claude/projects.Daily use, no public release. Known rough edges: the README is stale in two places, the repository is not under version control yet, and there are manual test files sitting in the root that need to go before anything is published.