Skip to the cargo

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:

sh
hk agent instructions --target codex
hk agent hooks --target claude-code
hk agent mcp --target vscode

Mind 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 ​

  1. Look the ship over and ask for a passage plan.
  2. Keep the work to the cargo that changed. Use --files0-from when exact filenames matter, and --cd to choose the project root.
  3. Inspect each command's effects on the cargo, and prefer safe execution. --safe refuses a run before any step starts if a runnable command is unknown or destructive. That includes the hook's report command, so declare its effect with a CommandSpec (report = new CommandSpec { command = "node scripts/report-timings.js"; effect = "read" }). --safe checks declared effects only. It is no sandbox, and hk does not verify that a command behaves as declared.
  4. Read the JSON or JSONL diagnostics, and keep the raw output when ye investigate parser warnings. A hand with no diagnostic_format has no diagnostics, so read its raw output field instead, when present (see diagnostics). A step's output in 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 failed run_result with the reason in failure. A step that Ctrl-C or a fail-fast abort stopped has the status cancelled, not failed. A run its crew interrupted with Ctrl-C, when no real failure occurred, has the status cancelled and no failure, and the MCP server reports that run as cancelled too. A step that failed before the Ctrl-C keeps the status failed, and so does the run.
  5. Review the resulting diff before ye accept a fix.

Without MCP, here's a portable way to hail hk:

sh
{
  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 jsonl

Hailing 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:

sh
hk agent mcp --target codex
hk agent mcp --target claude-desktop
hk agent mcp --target claude-code
hk agent mcp --target vscode

start_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.

scopeThe cargohk flag
allThe whole of the cargo: every tracked file, plus untracked files unless stowing is enabled (see below)--all
changedCargo loaded aboard and cargo left on the dock, plus untracked files (see below): all that differs from HEAD--stash none
unstagedCargo left on the dock, plus untracked files (see below), without the cargo loaded aboard--unstaged
stagedOnly 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.

hk MCP dashboard showing a completed project check

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_active is true. Otherwise it runs hk run check --safe.
  • It always exits 0.
  • If the check fails or --safe refuses to run, it prints only {"decision":"block","reason":"..."}, so the sailor keeps working with a short diagnosis.
  • Both agents document this top-level decision/reason JSON for Stop hooks, and Codex rejects any other stdout. So hk's own run_result JSON 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 codex and prints {}, which serves under either reading.

On the clock:

  • A check that runs longer than --timeout seconds 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 570 beside a hook timeout of 600 seconds, which is Claude Code's default for command hooks.
MIT LicenseCopyright © 2026jdx.dev