Clockwork hands: coding agents
hk gives coding agents, the clockwork hands, a narrow gangway to the ship's checks and fixes, and one ye can inspect. They can read structured CLI output wherever they sail, or hail the MCP server for persistent runs, cancellation, paged logs, diagnostics, diffs, and the interactive dashboard.
Draw up a starting point without touching any host configuration:
hk agent instructions --target codex
hk agent hooks --target claude-code
hk agent mcp --target vscodeMind Claude Desktop: it uses global host configuration and does not reliably launch servers from the project directory. Replace /absolute/path/to/project in its generated snippet before ye install it.
Every generator writes only to stdout. Look the result over, then place or merge it into the right host configuration yerself.
Sailing a clockwork hand safely
- Look the ship over and ask for a passage plan.
- Keep the work to the cargo that changed. Use
--files0-fromwhen exact filenames matter, and--cdto choose the project root. - Inspect each command's effects on the cargo, and prefer safe execution.
--saferefuses a run before any step starts if a runnable command is unknown or destructive. That includes the hook'sreportcommand, so declare its effect with aCommandSpec(report = new CommandSpec { command = "node scripts/report-timings.js"; effect = "read" }).--safechecks declared effects only. It is no sandbox, and hk does not verify that a command behaves as declared. - Read the JSON or JSONL diagnostics, and keep the raw output when ye investigate parser warnings. A hand with no
diagnostic_formathas no diagnostics, so read its rawoutputfield instead, when present (see diagnostics). A step'soutputin a structured result is capped at 64 KiB and ends with a marker stating the cap; the MCP server fails a run whose structured stream outgrows 16 MiB. A run that founders before any step starts, such as a configuration error, still produces a failedrun_resultwith the reason infailure. A step that Ctrl-C or a fail-fast abort stopped has the statuscancelled, notfailed. A run its crew interrupted with Ctrl-C, when no real failure occurred, has the statuscancelledand nofailure, and the MCP server reports that run ascancelledtoo. A step that failed before the Ctrl-C keeps the statusfailed, and so does the run. - Review the resulting diff before ye accept a fix.
Without MCP, here's a portable way to hail hk:
{
git diff --name-only -z
git diff --cached --name-only -z
git ls-files --others --exclude-standard -z
} | hk run check --files0-from - --safe --format jsonlHailing over MCP
hk mcp --root <project> starts a STDIO MCP server. It does not listen on a network port, so nobody hails it from across the water. The root is fixed when it starts (plus any roots the host supplies), and its tools cannot bring aboard arbitrary filesystem paths or unrestricted shell commands.
Draw up the configuration for a supported host:
hk agent mcp --target codex
hk agent mcp --target claude-desktop
hk agent mcp --target claude-code
hk agent mcp --target vscodestart_check, start_safe_check, and start_safe_fix take an optional scope argument that picks the cargo a run covers. It defaults to all, and any other value is turned away at the gangway.
scope | The cargo | hk flag |
|---|---|---|
all | The whole of the cargo: every tracked file, plus untracked files unless stowing is enabled (see below) | --all |
changed | Cargo loaded aboard and cargo left on the dock, plus untracked files (see below): all that differs from HEAD | --stash none |
unstaged | Cargo left on the dock, plus untracked files (see below), without the cargo loaded aboard | --unstaged |
staged | Only cargo loaded aboard, which is what a pre-commit hook inspects | --staged |
changed stands the stowing down for the run, so it always covers cargo loaded aboard and cargo left on the dock even when the project or HK_STASH enables stashing. --stash none does not overrule HK_STASH_UNTRACKED=0: with that set, hk never goes looking for untracked cargo, so changed and unstaged leave it out. staged is untouched by it, because untracked files are never loaded aboard. all keeps the project's stash setting, and hk leaves untracked files out of --all while stashing is enabled. Use changed or unstaged to inspect only what a clockwork hand just touched, instead of the whole ship, or staged to inspect just the cargo the next voyage would carry. Like changed, staged runs without stowing, so cargo loaded aboard that also has changes left on the dock is inspected as it lies on the deck.
Codex, Claude Code, Claude Desktop, and VS Code can all work the structured MCP tools. Hosts that implement MCP Apps also get the hk dashboard; other hosts get the same structured content and a useful text fallback.
A rich dashboard for the quarterdeck
The dashboard shows the current run, each step's status and timing, normalized diagnostics grouped by file, searchable logs, and the resulting diff. The only actions that lay hands on the cargo are confirmed safe fixes, and the server stays the source of truth.

A completed check of the whole ship in an MCP Apps host, with each step's timing and each command's effects.
See the dashboard demo runbook for a deterministic project fixture, one that sails the same course every time.
Rigging ChatGPT Desktop
ChatGPT Desktop can reach the local STDIO server through OpenAI's Secure MCP Tunnel. Build hk, create the demo fixture, and initialize a local-STDIO tunnel whose MCP command is the absolute hk mcp --root <fixture> command. Run tunnel-client doctor before ye start the tunnel, then create a development-mode app in ChatGPT and select the tunnel.
The tunnel is a development bridge, a gangplank for development work. hk itself ships no HTTP listener, hosted service, or authentication system.
Sailing instructions and hooks
Use hk agent instructions for a short block of sailing instructions fit for AGENTS.md, CLAUDE.md, or a generic agent prompt.
Use hk agent hooks for an optional Codex or Claude Code stop hook, or a VS Code task. Hook output is deliberately a snippet, not an automatic installation: inspect what it does to yer workflow before ye enable it.
The Codex and Claude Code snippets run hk agent stop-hook:
- It reads the Stop hook input from stdin (the first JSON value, waiting at most 5 seconds, so a pipe the sailor leaves open cannot stall it).
- It sits idle when
stop_hook_activeis true. Otherwise it runshk run check --safe. - It always exits 0.
- If the check fails or
--saferefuses to run, it prints only{"decision":"block","reason":"..."}, so the sailor keeps working with a short diagnosis. - Both agents document this top-level
decision/reasonJSON for Stop hooks, and Codex rejects any other stdout. So hk's ownrun_resultJSON is never printed from these hooks.
When the check passes:
- Claude Code's documented pass is exit 0 and silence, so the Claude Code snippet says nothing.
- Codex's scrolls argue with themselves about whether silence is accepted. So the Codex snippet passes
--target codexand prints{}, which serves under either reading.
On the clock:
- A check that runs longer than
--timeoutseconds is stopped, steps included, and reported as a block. - The default is 100 seconds, under Codex's 120-second hook timeout.
- The Claude Code snippet passes
--timeout 570beside a hooktimeoutof 600 seconds, which is Claude Code's default for command hooks.
