giro
Control never crosses an LLM. Content always does.
Most agent tooling tells a coding agent what to do and hopes it complies. giro inverts that. A deterministic loop owns every decision: it hands your coding agent one slice of a spec, runs your tests and blind reviewers on the result, feeds their findings back, and ends one of two ways: proof on a branch for you to merge, or a question for you. Never a silent "done". A budget enforced by a while loop cannot be talked out of; an instruction can.
Install
You do not type giro's commands. Paste this into your coding agent, in the git repository giro should work on:
Install giro here: run `uv tool install giro`, then `giro install`, then read .claude/skills/giro-setup/SKILL.md and follow it.
giro install places five skills in .claude/skills/ (.agents/skills/ links there for Codex and Cursor). giro-setup finds the commands your tests really run and the agent CLIs you have, asks only what it cannot look up, and writes giro.toml once you confirm. You need Python 3.11+, uv, git, and one supported coding agent logged in. Read running safely first: the worker gets its CLI's permission-bypass flag.
What you ask, in plain words
| You say | Skill | What happens |
|---|---|---|
| "Grill me on rate limiting for the public API." | grill |
Interviews you; writes terms to CONTEXT.md, lasting decisions to docs/adr/. |
| "Write that up as a spec." | spec |
Writes docs/specs/rate-limit/SPEC.md, plain markdown. |
| "Plan it into issues." | plan |
Optional: slices the Spec into Issues. Otherwise the engine plans. |
| "Implement rate-limit with giro." | giro |
Dispatches a detached Run on branch giro/rate-limit. You keep working on yours. |
| "How is it going?" | giro |
Reads the Run's log: wave 2, issue 03, attempt 1, which gate failed. |
| "What does it need from me?" | giro |
Shows the question the Run stopped on, writes your answer into the Issue, dispatches again. |
| "Walk me through what it changed." | giro |
Shows the proof's diff against your base branch. Merging it is always your hand. |
Grill, spec and plan
These three skills are the documentation phase, and they need no giro at all: copy them into any coding agent's skills folder. They start from Matt Pocock's grill-with-docs, to-spec and to-tickets and keep his judgment: the frontier-round interview, the spec template, tracer-bullet slicing. What changes is where the result lives. Everything is a file in your repository, beside the code it describes, and the loop treats those files as rules a worker cannot rewrite.
| Skill | Writes | Adapted from | What is different |
|---|---|---|---|
grill |
CONTEXT.md, docs/adr/NNNN-*.md |
grill-with-docs |
One skill instead of two. The glossary and the decisions it writes become binding: every worker is given CONTEXT.md, and any worker edit to either is reverted. |
spec |
docs/specs/<slug>/SPEC.md |
to-spec |
A folder in the repo, not a tracker issue. Adds a diagram of the moving parts (in any format, kept in the folder), an example for every contract, and a Docs impact section naming the docs and figures the change makes stale. |
plan |
docs/specs/<slug>/issues/NN-*.md |
to-tickets |
Issues live in the Spec's folder, with blocked_by in frontmatter that the engine schedules from. Each one ends with "Not in this Issue", the scope fence the reviewers check. |
They are plain markdown, so adjust them to how you work: giro install keeps an edited copy as yours and replaces it only with --force. The side-by-side, section by section, is in skills/README.
The idea
The agent does the work. Structure it can't argue with decides what counts. A human holds the exits.
A Spec is markdown describing one feature. The engine splits it into Issues (small slices) and for each starts a fresh agent, the worker, to edit files. giro commits the change and runs your gates: command gates (the exit code decides) and judge gates, where a blind LLM, given the Spec, the Issue (with its log from earlier Runs), the diff and a criterion, gives a verdict. A while loop counts budgets; no prompt talks it round.
A Run that completes ends one of two ways. Proof: all gates green, the work waits on branch giro/<spec> for your merge. Or needs-human: budget spent or a worker question. The reason is written into the Issue in .giro/worktrees/<spec>/, where the giro skill reads it to you. After an error, ask again; the Run resumes from the markdown. Runs see only committed code (the Spec is copied as-is): commit what gates need.
What is enforced by code, and what only by instruction
| Rule | Enforced by | Where |
|---|---|---|
| Attempts per Issue and validate gap cycles per Spec are capped | code | src/giro/loops.py _attempt_cycle, run_spec_loop |
Worker edits to docs/specs/, docs/adr/, prompts/, gates/, CONTEXT.md, giro.toml are reverted on every outcome, including ones the worker committed or hid with an ignore rule (not via git index flags; see the .git row) |
code | src/giro/loops.py _guardrail_hits, _attempt_cycle |
| Only the engine commits: worker commits are unwound and re-judged (not its branches, tags or pushes) | code | src/giro/loops.py _attempt_cycle |
| Judges are fresh and never see the current attempt's reply; the Issue's log from earlier Runs reaches them. No valid verdict after one retry: fail | code | src/giro/gates.py run_judged_gate, src/giro/loops.py _material |
| Your branch, index and uncommitted edits are never touched | code | src/giro/workspace.py ensure_spec_worktree |
| One Run per Spec; a per-checkout worker cap | code | src/giro/runs.py spec_lock, worker_slot |
| giro never merges into your base branch | code: its only merge targets giro/<spec> |
src/giro/loops.py _integrate_branch |
| The worker stays in the Issue's scope and never weakens a test | instruction, LLM-judged | src/giro/loops.py WORKER_PREAMBLE, prompts/review.md |
Changes follow CONTEXT.md and ADRs (conformance gate, off in the starter config) |
instruction, LLM-judged | prompts/conformance.md |
| Judges and planners don't edit files | config only: no bypass flag in the starter roster or the role fallback | src/giro/config.py Config.roster_for, INIT_TEMPLATE |
The worker doesn't touch .git (index flags included) or files outside the repo |
instruction only; no sandbox | src/giro/loops.py WORKER_PREAMBLE |
Supported coding agents
giro starts a coding agent through a driver: prompt in, edits in the working tree, one JSON reply out. It ships drivers for Claude Code (claude, the default), Codex (codex), Gemini CLI (gemini) and Antigravity (agy). giro.toml picks one per role, so the worker, the reviewers and the planner can each use a different agent.
Any other agent that runs headless from the command line can be added. A driver is a short class in src/giro/drivers.py: it builds the command line and, if needed, unwraps the reply; register it in DRIVER_REGISTRY and name it in giro.toml. Drivers defined in giro.toml alone, with no code, are specified in open-drivers and not built yet. Cursor and other chat hosts need no driver: they read the same skills.
Reference
In CI, call the command the skills call: giro implement <spec> exits 0 on proof, 2 on needs-human, 1 on error; giro init writes a starter giro.toml with no interview. Every command, the loops, gates, state and drivers: the guide. The contract: design.
Where it is used
giro is built with giro. Four of the Specs under docs/specs/ (detached-runs, github-projection, setup-skill, strip-skill-frontmatter) were run to done by the engine, and each Issue file keeps its attempts and verdicts; the others were not. Two live runs elsewhere (first light, parallel waves) are on toy repos. This repository's public history begins at 0.1.0.
Status and limits
- Pre-alpha 0.1.0, POSIX, MIT. The GitHub projection is experimental.
- The engine bounds iteration, not blast radius: use a container, VM or throwaway clone.
- Judge gates are opinions; put real tests in command gates.
Credits
The grill, spec and plan skills, and the review gate's prompt, adapt Matt Pocock's Skills for Real Engineers (MIT): the interviewing, spec-writing, slicing and review judgment is his; the loop around them is giro's. The notice is in LICENSE.
Metadata
Release files for giro 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| giro-0.1.0.tar.gz | 173.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| giro-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 298.7 kB
Release files / giro-0.1.0.tar.gz
| Download URL | giro-0.1.0.tar.gz |
|---|---|
| Size | 173.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d79870f4aef9fc9617a12da8ffcf20b8c2a8471a3d0184959578d908a1c240b6
|
|
BLAKE2b-256 checksum How to use checksums |
a1a797e6e3b53be70d9f3f72fcea1643ffdd0d69810da8fafe2ad67a3d875ccf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / giro-0.1.0-py3-none-any.whl
| Download URL | giro-0.1.0-py3-none-any.whl |
|---|---|
| Size | 125.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d93d7c9b2b945ff8c6722bb66e229acde74b15da3e876df516d62efd00dd8148
|
|
BLAKE2b-256 checksum How to use checksums |
85bc779d22b5a118d96dbf04827387142013330609a16f104f85901a8572d13f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|