Goals for Claude Code that survive the session, enforce a budget, and refuse to close without evidence.
A port of the goal engine from openai/codex (codex-rs/ext/goal,
codex-rs/prompts/templates/goals), rebuilt on Claude Code's hook surface — plus the parts codex does
not have: durable state, budgets beyond tokens, mechanical stall detection, a progress ledger, and a
completion call that will not fire without evidence.
You give it an objective. It keeps driving turns toward that objective, on its own, until the work is verifiably done — or until it runs out of budget, hits a real impasse, or you stop it.
/plugin marketplace add redstone-md/goalkeeper
/plugin install goalkeeper@goalkeeper
Restart Claude Code so the hooks load. Requires Node ≥ 18 on PATH; no dependencies.
Install from a local clone instead
git clone https://github.com/redstone-md/goalkeeper.git/plugin marketplace add /absolute/path/to/goalkeeper
/plugin install goalkeeper@goalkeeper
Update later with /plugin marketplace update goalkeeper. Remove with /plugin uninstall goalkeeper.
/goalkeeper:goal Port the auth service to OAuth2 and get the integration suite green
That is the whole workflow. The goal is now active and the loop runs itself.
| Command | What it does |
|---|---|
/goalkeeper:goal <objective> |
set the goal and start driving turns |
/goalkeeper:goal |
status, usage, ledger |
/goalkeeper:goal edit <objective> |
replace the objective in place |
/goalkeeper:goal pause · resume |
stop / restart automatic continuation |
/goalkeeper:goal budget --tokens 800000 --turns 40 --minutes 120 |
set or raise budgets |
/goalkeeper:goal queue <objective> |
run it after the current goal completes |
/goalkeeper:goal log · report |
progress ledger · full summary |
/goalkeeper:goal clear |
drop the goal |
/goalkeeper:goal config --maxContinuations 50 --stallLimit 4 |
tune the loop |
Only the model closes a goal out, and only through the CLI — the exact commands, with the absolute plugin path, are printed when the goal is set and repeated in every continuation prompt:
node <plugin>/lib/cli.js complete --evidence "<one line of proof per requirement>"
node <plugin>/lib/cli.js block --reason "<specific unmet external condition>"| codex | goalkeeper |
|---|---|
ThreadGoal row in SQLite, per thread |
state.json per project root, under ~/.claude/goalkeeper/ |
on_thread_idle → continue_if_idle → try_start_turn_if_idle |
Stop hook → {"decision":"block","reason":<prompt>} |
continuation.md steering item |
same prompt, re-rendered each turn with live budget + ledger |
TokenUsageContributor |
usage parsed out of the session transcript JSONL |
budget_limited status + wrap-up prompt |
same, plus turn and wall-clock budgets |
update_goal(status) tool |
cli.js complete / block, evidence required |
on_thread_resume |
SessionStart hook (startup, resume, and compact) |
| continuation deferral row | interrupt detection + UserPromptSubmit clearing the deferral |
- Survives everything. codex ties a goal to one thread. Here the goal lives on the project, so
/clear, a crash, a new terminal or an auto-compaction do not lose it —SessionStartreinjects it andUserPromptSubmitkeeps the objective in context as the window rolls. - Three budgets, all enforced. codex enforces tokens only (time is tracked but never trips).
goalkeeper enforces tokens, turns and wall clock, and hard-caps automatic continuations
(
maxContinuations, default 30) so an unbudgeted goal cannot spin forever. - Mechanical stall detection. codex learns about an impasse only if the model self-reports it.
goalkeeper fingerprints the workspace (
HEAD+git status+diff --stat) every turn. One no-change turn injects a change-approach prompt;stallLimitof them (default 3) marks the goal blocked instead of burning budget on a loop. - A progress ledger. Every continuation records tokens, tool calls, touched files and whether the workspace moved. The completion audit has something to audit against, and the last few entries ride along in the continuation prompt so the model can see it is repeating itself.
- Evidence-gated completion.
completewithout--evidenceexits non-zero. The evidence line is stored on the goal and printed inreport. Optional adversarial verification via the bundledgoal-auditorsubagent. - A queue. Chain objectives; the next one activates when the current completes.
- The objective is quoted as untrusted data in every prompt, never as instructions.
- The fidelity clause: no shrinking the objective to what fits in this turn, no substituting an easier task that happens to pass the current tests.
- The completion audit: requirement by requirement, current-state evidence, uncertain evidence counts as not achieved.
- The blocked audit: three consecutive turns of the same blocker before
blockedis allowed, and neverblockedjust because the work is hard. - Long objectives (>4000 chars) spill to a sidecar file; the goal stores a read-this-file reference.
- The
Stophook restarts turns repeatedly. That is the point, and it costs tokens. Set a budget:/goalkeeper:goal budget --tokens 500000. Even unbudgeted, the loop stops after 30 continuations. - Esc defers the loop: an interrupted turn is not re-blocked, and the goal picks up on your next message.
pausestops continuation immediately;clearremoves the goal.- Any hook error is swallowed and exits 0 — a broken goal can never wedge a session.
- Nothing leaves the machine. State is a local JSON file (
/goalkeeper:goal path).
.claude-plugin/plugin.json plugin manifest
commands/goal.md /goalkeeper:goal
agents/goal-auditor.md adversarial completion auditor (read-only)
hooks/ stop.js · prompt.js · session-start.js · hooks.json
lib/ state · usage accounting · engine · render · cli
templates/ continuation · budget-limit · objective-updated · stalled · session-card
test/smoke.js 19 end-to-end checks
Prompts live in templates/ as plain Markdown — edit them to change how the loop talks.
node test/smoke.js19 end-to-end checks over the CLI and all three hooks, in a throwaway CLAUDE_CONFIG_DIR. No test
framework, no dependencies.