Under the hood

Hooks

How voodoo knows what Claude Code is doing without reading its screen, and without writing anything into your project.

Working out what an agent is doing by reading English text off its screen misreads. Claude Code runs a hook at each turn of its work, so voodoo asks it instead.

How it is wired

The daemon starts Claude Code with --settings, pointing at one settings file it keeps in its own home. In that file every hook is a curl to a receiver on the loopback interface. Those reports drive a small state machine, and nothing polls.

  • Nothing is written into your project. No .claude/settings.local.json to merge into, nothing to clean up, nothing for git status to notice.
  • A hook runs inside the agent’s turn, so it gives the daemon three seconds and then gives up. It never fails, and it is inert outside a session voodoo started.
  • The receiver’s replies are always empty, because Claude Code reads whatever a prompt hook prints into the model’s context.

What the hooks don’t say

  • Granting a permission fires no hook. The tool it was for finishing is the first sign you said yes.
  • Cancelling a turn fires no stop. The agent reporting itself idle is the only sign the turn is over.
  • A stop is held while subagents are still reported running, with ninety seconds’ grace so one that never reports back can’t hold a turn open forever.

Inside a session

VariableWhat it is
LU_SESSION_IDWhich session the process is. Set inside every session.
LU_HOOK_URLWhere an agent’s hooks report to. A port the system picks, new on every start of the daemon.
LU_HOOK_TOKENWhat the hooks prove themselves with. New on every start of the daemon.

More on the receiver under security.