
fh-tools: fourteen deterministic tools for coding agents
fh-tools is where the deterministic doctrine becomes concrete: fourteen small tools that coding agents call instead of guessing. Each answers one question, each answers it the same way every time, and each says clearly whether it gave a verdict or hit a technical problem.
What you get. Facts on demand for your agents, in any language you like to write tools in, with no server and no shared runtime, and a conformance checker that refuses any tool whose output varies.
1. The fourteen tools
| Tool | Answers |
|---|---|
| fh-toolgate | does this repository conform to the tool contract? |
| fh-envlint | do .env and .env.example agree? |
| fh-testgap | which added lines does no test exercise? |
| fh-edit-apply | apply this patch to this content |
| fh-edit-diagnose | why did that patch not apply? |
| fh-bench-score | is this change an improvement? |
| fh-repomap | what does this repository look like? |
| fh-secret-scan | does this text carry a credential? |
| fh-changelog | what changed over this range? |
| fh-deps | is this dependency set reproducible? |
| fh-trace-query | have we seen this failure before? |
| fh-pr | what does this pull request say? |
| fh-story-lint | does this proposed story split conform? |
| fh-spec-lint | does this spec state a contract? |
2. The contract
A tool is an executable that reads a JSON object on stdin and writes a JSON object on stdout. Not a library, not a server, not a plugin. Any language qualifies; there is no port, no daemon; and determinism can be tested from outside: replay the input, compare the bytes.
Each tool carries a tool.json: name, version, a description of 120 characters at most (it sits in the model's context every turn it is loaded), entry point, input and output schemas, exit codes, required system programs, deterministic: true, and an estimated context cost.
| Exit code | Meaning |
|---|---|
| 0 | success |
| 1 | a valid answer with a negative verdict |
| 2 | refusal: the operation is not permitted |
| 3 and above | technical error, never a verdict |
The difference between 1 and 3 is load-bearing. A linter missing from the PATH once produced a red that looked like a code failure, and cost a day chasing the wrong thing.
3. Conformance: three replays, identical bytes
echo '{"repo": "fh-tools/fh-envlint"}' | python3 fh-tools/fh-toolgate/src/fh_tool_verify.py
The checker reads the manifest, runs the entry point, and replays each declared case three times, demanding byte-identical output. A tool whose output varies does not ship. A small launcher, fh-tool <name>, runs any tool from its manifest so callers never deal with paths, and a tool server exposes them to any agent.
4. The editing pair, split on purpose
fh-edit-apply applies a patch; fh-edit-diagnose explains why one did not. The second is where the value is: what comes back after a failed edit decides whether the model repairs its work or starts over from scratch.
Traps
A runner that calls a model is not a tool, even if it lives in the same repository. Ours says so; it produces raw runs, and a real tool scores them, so anyone can re-score published runs without owning the hardware.
Found this useful? Tell us what to measure next.