Your code evolves while you sleep. 🦉 Spec-driven autonomous coding loop.
Project description
owloop is a spec-driven autonomous coding loop for Claude Code. You write specs, start the loop, and wake up to clean commits.
Each iteration spawns a fresh agent with zero accumulated context, verifies every acceptance criterion with real shell commands, and only commits when they pass.
owloop run → pick spec → fresh agent → verify with shell → commit → next spec → 🌅
Quick Start
uv tool install owloop # or: pip install owloop
owloop init # scaffolds .owloop/ directory
owloop spec "refactor error handling" # agent scans code, drafts spec, asks for approval
owloop run # start the loop — Ollie takes it from here
All commands
| Command | Description |
|---|---|
owloop init |
Initialize project (creates .owloop/, .gitignore entries) |
owloop spec "goal" |
Turn a goal into a concrete spec via agent clarification |
owloop check |
Validate all specs before running (pre-flight linter) |
owloop run |
Start the autonomous loop with TUI |
owloop run -n 20 |
Limit to 20 iterations |
owloop run --max-tokens 200000 |
Stop after token budget reached |
owloop run --idle-timeout 1800 |
Kill hung agent after 30 min silence |
owloop status |
Show specs and completion progress |
owloop report |
Generate AI-powered HTML summary report |
owloop report --no-ai |
Generate fast, offline report |
owloop spec-from-issue 42 |
Generate a spec from a GitHub issue |
owloop version |
Show installed version |
How It Works
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#d4a025', 'primaryTextColor': '#0b1026', 'primaryBorderColor': '#3a4270', 'lineColor': '#3a4270', 'secondaryColor': '#121a2e', 'tertiaryColor': '#121a2e', 'fontFamily': 'Inter, system-ui, sans-serif' }}}%%
graph LR
A["🦉 Pick spec"]:::owl --> B["fresh agent context"]:::neutral
B --> C{"DONE?"}:::neutral
C -- yes --> D["✅ Commit"]:::neutral --> E{"More specs?"}:::neutral
C -- no --> R["🔄 Retry ≤3"]:::retry --> A
E -- yes --> A
E -- no --> F["🌅 Complete"]:::done
classDef owl fill:#d4a025,stroke:#d4a025,color:#0b1026
classDef done fill:#8fd19e33,stroke:#8fd19e,color:#f2ecd8
classDef retry fill:#e0777d33,stroke:#e0777d,color:#f2ecd8
classDef neutral fill:#121a2e,stroke:#3a4270,color:#f2ecd8
| Property | How |
|---|---|
| Fresh context | Each iteration is a brand-new claude -p process. No context rot. |
| Deterministic completion | grep for <promise>DONE</promise> — no AI judgment needed. |
| Worktree isolation | Runs in a separate git worktree. Your main checkout stays untouched. |
| Auto Mode | --permission-mode auto: asks before risky moves, never YOLO. |
| Token budget cap | --max-tokens stops the run before costs spiral. |
| AI reports | owloop report produces a reviewable HTML artifact after each run. |
More features
- Cross-iteration notes —
run-notes.mdcarries learnings between iterations. - Fix-loop detection — same files modified 3+ rounds triggers a death-spiral warning.
- Sleep prevention — keeps your machine awake during overnight runs (macOS / Linux / Windows).
- Pre-flight linting —
owloop checkvalidates specs before the loop starts.
Specs
Specs are constraint-oriented: define what to do, what's off-limits, and make every acceptance criterion a shell command.
# Spec: Extract ValidationError Handling
## Priority: 1
## Requirements
- Extract repeated `except ValidationError` blocks into a single `@app.errorhandler`
## Acceptance Criteria
- [ ] grep -c "except ValidationError" backend/app/api/*.py → ≤ 5
- [ ] uv run ruff check backend/ → 0 errors
## Exclusions
- Do NOT change API response formats
- Do NOT touch models/, schemas/, services/
If you can write a shell command that verifies "done", it's a good owloop task. If "done" requires a human to look and decide, it's not.
Compared To
| owloop | Claude Code /goal |
|
|---|---|---|
| Completion signal | grep (deterministic) |
Haiku model (probabilistic) |
| Context | Fresh per iteration | Same session |
| Specs | Constraint-oriented | Free-form |
| Best for | Backlog of verifiable tasks | One focused task |
FAQ
When should I use owloop instead of /goal?
/goal is great for one task in one sitting. owloop is for backlogs — a queue of specs, each run in a fresh context, unattended overnight. If you're clearing twenty lint categories or migrating a whole module, owloop scales further than one long session.
Is this safe to run on production code?
owloop runs in a separate git worktree with --permission-mode auto. Your main branch stays clean. Treat every overnight run as a PR to review in the morning, not a deploy.
Development
uv sync --group dev # install dev dependencies
uv run pytest -q # run tests with coverage
uv run ruff check src/owloop tests
uv run mypy src/owloop tests
Brand
owloop's identity — Ollie the owl, the night/amber palette, and the "your code evolves while you sleep" story — is documented in .github/BRAND.md.
Credits
Inspired by and built upon:
- Geoffrey Huntley's Ralph Wiggum methodology — the original "unattended coding loop" concept
- Florian Standhartinger's ralph-wiggum — the implementation owloop was forked from
- gnhf — multi-agent overnight runner, influenced owloop's fresh-context-per-iteration design
License
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file owloop-0.3.0.tar.gz.
File metadata
- Download URL: owloop-0.3.0.tar.gz
- Upload date:
- Size: 427.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e4f5f7b75ee87961a0fbe081ec9d829319e5b42f25b33accfc305aced1624566
|
|
| MD5 |
000059be1e8d31dd69a513e9b37ef224
|
|
| BLAKE2b-256 |
d36d2825e98f0463eebc760160c053146e41510031af54342b99c491614b2b5c
|
Provenance
The following attestation bundles were made for owloop-0.3.0.tar.gz:
Publisher:
release.yml on caoergou/owloop
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
owloop-0.3.0.tar.gz -
Subject digest:
e4f5f7b75ee87961a0fbe081ec9d829319e5b42f25b33accfc305aced1624566 - Sigstore transparency entry: 2061096516
- Sigstore integration time:
-
Permalink:
caoergou/owloop@1f9c283e199c3a8677f9f989a3a9dc343d69fa2d -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/caoergou
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1f9c283e199c3a8677f9f989a3a9dc343d69fa2d -
Trigger Event:
push
-
Statement type:
File details
Details for the file owloop-0.3.0-py3-none-any.whl.
File metadata
- Download URL: owloop-0.3.0-py3-none-any.whl
- Upload date:
- Size: 63.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
13a0afd6d9f00f6d7a45e4daa9076b57ed03dce8b1be495b185244ffc66560c8
|
|
| MD5 |
e5e4215e278b86b84e4e737daebc7be0
|
|
| BLAKE2b-256 |
ce6256d6cb85c671783cb5cee7b3142d39a0e99b86a560cc867e4eda4a10b3c4
|
Provenance
The following attestation bundles were made for owloop-0.3.0-py3-none-any.whl:
Publisher:
release.yml on caoergou/owloop
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
owloop-0.3.0-py3-none-any.whl -
Subject digest:
13a0afd6d9f00f6d7a45e4daa9076b57ed03dce8b1be495b185244ffc66560c8 - Sigstore transparency entry: 2061097230
- Sigstore integration time:
-
Permalink:
caoergou/owloop@1f9c283e199c3a8677f9f989a3a9dc343d69fa2d -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/caoergou
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1f9c283e199c3a8677f9f989a3a9dc343d69fa2d -
Trigger Event:
push
-
Statement type: