Ocak (pronounced "oh-JAHK") is Turkish for "forge" or "hearth" - the place where raw material meets fire and becomes something useful. Also: let 'em cook.
Multi-agent pipeline that processes GitHub issues autonomously with Claude Code. You write an issue, label it, and ocak runs it through implement → review → fix → security review → document → audit → merge. Each issue gets its own worktree so they can run in parrallel.
gem install ocak
# In your project directory:
ocak init
# Create an issue (interactive):
# Inside Claude Code, run /design
# Process all ready issues:
ocak run --once --watch
# Or run a single issue:
ocak run 42 --watchflowchart LR
A["/design<br>Create issue"] --> B["Label<br>auto-ready"]
B --> C["Planner<br>Batch & classify"]
C --> D["Worktree<br>per issue"]
D --> E["Pipeline<br>Steps"]
E --> F["Rebase<br>& merge"]
flowchart TD
IMP["1. Implementer<br>(opus)"] --> REV["2. Reviewer<br>(sonnet)"]
REV -->|"🔴 findings"| FIX1["3. Fix<br>(opus)"]
REV -->|"no findings"| SEC
FIX1 --> VER["4. Verify<br>(sonnet)"]
VER --> SEC["5. Security Review<br>(sonnet)"]
SEC -->|"🔴 findings"| FIX2["6. Security Fix<br>(opus)"]
SEC -->|"no findings"| DOC
FIX2 --> DOC["7. Documenter<br>(sonnet)"]
DOC --> AUD["8. Auditor<br>(sonnet)"]
AUD --> MRG["9. Merger<br>(sonnet)"]
Steps 5-8 are tagged complexity: full and get skipped for simple issues. --fast forces everything to simple complexity so a typo fix doesnt burn through the whole pipeline.
Conditional steps only run when needed:
- Fix only runs if the reviewer flagged 🔴 blocking issues
- Verify only runs if fixes were actually applied
- Merge gets skipped in
--manual-reviewmode or when the auditor BLOCKs
stateDiagram-v2
[*] --> auto_ready
auto_ready --> auto_doing: Pipeline picks up
auto_doing --> completed: Success
auto_doing --> pipeline_failed: Failure
auto_doing --> auto_pending_human: --manual-review / audit BLOCK
auto_doing --> auto_ready: Ctrl+C interrupt
pipeline_failed --> auto_doing: ocak resume
auto_pending_human --> auto_reready: Human labels for re-review
auto_reready --> auto_pending_human: Feedback addressed
state "auto-ready" as auto_ready
state "auto-doing" as auto_doing
state "completed" as completed
state "pipeline-failed" as pipeline_failed
state "auto-pending-human" as auto_pending_human
state "auto-reready" as auto_reready
The planner classifes each issue as simple or full:
- Simple - skips security review, second fix pass, documenter, and auditor
- Full - runs the whole thing
--fast forces all issues to simple, giving you: implement → review → fix (if needed) → verify (if fixed) → merge.
After all pipeline steps complete:
flowchart LR
A["Commit<br>changes"] --> B["Rebase<br>onto main"]
B -->|conflict| C["Implementer<br>resolves"]
C --> D
B -->|clean| D["Run tests"]
D --> E["Push branch"]
E --> F["Merger agent<br>create PR + merge"]
Merging is sequential - one at a time - so you dont get conflicts between parallel worktrees.
8 agents, each with scoped tool permisions:
| Agent | Role | Tools | Model |
|---|---|---|---|
| implementer | Write code and tests | Read, Write, Edit, Bash, Task | opus |
| reviewer | Check patterns, tests, quality | Read, Grep, Glob, Bash (read-only) | sonnet |
| security-reviewer | OWASP Top 10, auth, injection | Read, Grep, Glob, Bash (read-only) | sonnet |
| auditor | Pre-merge security gate | Read, Grep, Glob, Bash (read-only) | sonnet |
| documenter | Add missing docs (skips if not needed) | Read, Write, Edit, Bash | sonnet |
| merger | Create PR, merge, close issue | Read, Glob, Grep, Bash | sonnet |
| planner | Batch issues, classify complexity | Read, Glob, Grep, Bash (read-only) | haiku |
| pipeline | Self-contained single-agent mode | All tools | opus |
Review agents (reviewer, security-reviewer, auditor) have no Write/Edit access - they can only read and report stuff back.
Interactive skills for use inside Claude Code:
| Skill | Description |
|---|---|
/design [description] |
Researches your codebase, asks clarifying questions, produces a detailed implementation-ready issue |
/audit [scope] |
Codebase sweep - scopes: security, errors, patterns, tests, data, dependencies, or all |
/scan-file <path> |
Deep single-file analysis with test coverage check, scored 1-10 per method |
/debt |
Tech debt tracker with risk scoring (churn, coverage, suppressions, age, blast radius) |
The default. Polls for auto-ready issues, plans batches, runs the full step sequence in parallel worktrees, merges sequentally.
ocak run --once --watch # Process current batch and exit
ocak run 42 --watch # Single issue, full pipeline
ocak run --manual-review --watch # Create PRs without auto-merge
ocak run --audit --watch # Auditor as merge gate
ocak run --fast --watch # Skip security/docs/audit stepsLightweight alternative for quick PRs you'll review youself:
flowchart LR
A["Implementer<br>(sonnet)"] --> B["Reviewer (haiku)<br>+<br>Security (sonnet)<br>in parallel"]
B --> C["Verify<br>tests + lint"]
C --> D["Create PR<br>(no merge)"]
Runs in your current checkout (no worktree), uses cheaper models, creates a PR without merging. Rougly 5-10x cheaper than the full pipeline.
ocak hiz 42 --watchWhen --manual-review is enabled, PRs sit open for human review. After leaving feedback, slap the auto-reready label on the PR and ocak will:
- Check out the PR branch
- Run the implementer against the review comments
- Push with
--force-with-lease - Remove the
auto-rereadylabel - Comment "Feedback addressed. Please re-review."
Ocak can run without GitHub. Set issues.backend: local in ocak.yml (or just create .ocak/issues/ and it auto-detects). Issues are stored as numbered markdown files with YAML frontmatter.
ocak issue create "Add retry logic to API client" --label auto-ready
ocak issue list
ocak issue view 1
ocak run 1 --watchIssues live in .ocak/issues/0001.md, .ocak/issues/0002.md, etc. Labels, complexity, and pipeline comments are all tracked in the file. Merging goes straight to main (no PRs) via LocalMergeManager.
Run issues across multiple repos from a single issue tracker. One "god repo" holds all the issues, agents run in worktrees of whatever repo the issue targets.
Each issue specifies its target with YAML front-matter at the top of the body:
---
target_repo: my-service
---
Actual issue description here...
Enable it in ocak.yml:
multi_repo: trueMap repo names to local paths in ~/.config/ocak/config.yml:
repos:
my-service: ~/dev/my-service
other-thing: ~/dev/other-thingLabels and comments stay on the god repo's issues. PRs get created in the target repo. Worktree isolation, parallel batches, sequential merging all work the same.
Ctrl+C once - current agent step finishes, then the pipeline stops. WIP gets committed, labels reset to auto-ready, and resume commands are printed.
Ctrl+C twice - kills active subprocesses immediatley (SIGTERM → wait → SIGKILL), then same cleanup runs.
ocak resume 42 --watch # Pick up from where it stoppedocak init generates ocak.yml at your project root:
# Auto-detected project stack
stack:
language: ruby
framework: rails
test_command: "bundle exec rspec"
lint_command: "bundle exec rubocop -A"
setup_command: "bundle install"
security_commands:
- "bundle exec brakeman -q"
- "bundle exec bundler-audit check"
# Issue backend (omit for GitHub, or set to "local" for offline mode)
issues:
backend: github # or "local" - auto-detected if .ocak/issues/ exists
# Pipeline settings
pipeline:
max_parallel: 5
poll_interval: 60
worktree_dir: ".claude/worktrees"
log_dir: "logs/pipeline"
cost_budget: 5.0 # Max USD per issue (kills pipeline if exceeded)
manual_review: false # Create PRs without auto-merge
audit_mode: false # Run auditor as merge gate
# Safety controls
safety:
allowed_authors: [] # Restrict to GitHub usernames (empty = current gh user)
require_comment: false # Require confirmation comment before processing
max_issues_per_run: 5 # Cap issues per polling cycle
# GitHub labels
labels:
ready: "auto-ready"
in_progress: "auto-doing"
completed: "completed"
failed: "pipeline-failed"
reready: "auto-reready"
awaiting_review: "auto-pending-human"
# Pipeline steps - add, remove, reorder as you like
steps:
- agent: implementer
role: implement
- agent: reviewer
role: review
- agent: implementer
role: fix
condition: has_findings
- agent: reviewer
role: verify
condition: had_fixes
- agent: security-reviewer
role: security
complexity: full
- agent: implementer
role: fix
condition: has_findings
complexity: full
- agent: documenter
role: document
complexity: full
- agent: auditor
role: audit
complexity: full
- agent: merger
role: merge
# Override agent files
agents:
implementer: .claude/agents/implementer.md
reviewer: .claude/agents/reviewer.md
# ...ocak init scaffolds ~/.config/ocak/config.yml (or $XDG_CONFIG_HOME/ocak/config.yml) for machine-specific settings. When both files exist, project ocak.yml wins on conflicts.
# ~/.config/ocak/config.yml
# Repo path mappings for multi-repo mode
repos:
my-service: ~/dev/my-service
# Machine-level overrides
pipeline:
max_parallel: 3
cost_budget: 10.00The repos: key only comes from user config so repo paths dont leak into project config.
Point any agent at a custom file:
agents:
reviewer: .claude/agents/my-custom-reviewer.mdRemove steps you don't need, add your own, reorder them:
steps:
- agent: implementer
role: implement
- agent: reviewer
role: review
- agent: merger
role: mergeCreate a markdown file with YAML frontmatter:
---
name: my-agent
description: Does something specific
tools: Read, Glob, Grep, Bash
model: sonnet
---
# My Custom Agent
[Instructions for the agent...]Then reference it in ocak.yml:
agents:
my_agent: .claude/agents/my-agent.md
steps:
- agent: my_agent
role: custom_stepOverride the default model for any step:
steps:
- agent: implementer
role: implement
model: sonnet # Use sonnet instead of opus for cheaper runsocak init auto-detects your project stack and generates tailored agent templates. For anything else you get generic agents that you can cusomize.
| Language | Frameworks | Test | Lint | Security |
|---|---|---|---|---|
| Ruby | Rails, Sinatra, Hanami | rspec | rubocop | brakeman, bundler-audit |
| TypeScript/JS | Next, Remix, Nuxt, Svelte, React, Vue, Express | vitest, jest | biome, eslint | npm audit |
| Python | Django, Flask, FastAPI | pytest | ruff, flake8 | bandit, safety |
| Rust | Actix, Axum, Rocket | cargo test | cargo clippy | cargo audit |
| Go | Gin, Echo, Fiber, Chi | go test | golangci-lint | gosec |
| Java | - | gradle test | - | - |
| Elixir | Phoenix | mix test | mix credo | - |
Monorepo detection: npm/pnpm workspaces, Cargo workspaces, Go workspaces, Lerna, and convention-based (packages/, apps/, services/).
Pipeline runs generate JSON reports in .ocak/reports/:
ocak status --reportShows per-run stats (cost, duration, steps completed, failures) and aggregates across recent runs (avg cost, avg duration, success rate, slowest step, most-skipped step). Handy for figuring out where all your money went.
The /design skill produces issues formatted for zero-context agents. Think of it as writing a ticket for a contractor who's never seen your codebase - everthing they need should be in the issue body. The key sections:
- Context - what part of the system, with specific file paths
- Acceptance Criteria - "when X, then Y" format, each independantly testable
- Implementation Guide - exact files to create/modify
- Patterns to Follow - references to actual files in the codebase
- Security Considerations - auth, validation, data exposure
- Test Requirements - specific test cases with file paths
- Out of Scope - explicit boundaries so it doesnt scope creep
ocak init [--force] [--no-ai] Set up pipeline in current project
[--config-only] Only generate config, hooks, settings
[--skip-agents] Skip agent generation
[--skip-skills] Skip skill generation
ocak run [ISSUE] [options] Run the pipeline
--watch Stream agent activity with color
--dry-run Show plan without executing
--once Process current batch and exit
--fast Skip security/docs/audit (simple complexity)
--max-parallel N Limit concurrency (default: 5)
--poll-interval N Seconds between polls (default: 60)
--manual-review Create PRs without auto-merge
--audit Run auditor as post-pipeline gate
--verbose / --quiet Control output verbosity
ocak hiz ISSUE [options] Fast mode: implement + parallel review
--watch Stream agent activity
--dry-run Show plan without executing
--verbose / --quiet Control output verbosity
ocak resume ISSUE [options] Resume from last successful step
--watch Stream agent activity
--dry-run Show which steps would re-run
--verbose / --quiet Control output verbosity
ocak status Show pipeline state
--report Show run reports (cost, duration, stats)
ocak clean Remove stale worktrees
--logs Clean log files, state, and reports
--all Clean worktrees and logs
--keep N Only remove artifacts older than N days
ocak issue create TITLE [options] Create a local issue
--body TEXT Issue body (opens $EDITOR if omitted)
--label LABEL Add label (repeatable)
--complexity full|simple Set complexity (default: full)
ocak issue list [--label LABEL] List local issues
ocak issue view ISSUE View a local issue
ocak issue edit ISSUE Open issue in $EDITOR
ocak issue close ISSUE Mark issue as completed
ocak design [DESCRIPTION] Launch interactive issue design session
ocak audit [SCOPE] Print instructions for /audit skill
ocak debt Print instructions for /debt skill
How much does it cost?
Depends on the issue. Simple stuff is $2-5, complex issues can be $10-15. The implementer runs on opus which is the expensive part, reviews on sonnet are pretty cheap. Use ocak hiz for ~5-10x cheaper runs. Set cost_budget in config to cap spend per issue.
Is it safe?
Reasonably. Review agents are read-only (no Write/Edit tools), merging is sequential so you dont get conflicts, and failed piplines get labeled and logged. You can always --dry-run first to see what it would do.
Can I trust ocak.yml from someone else's repo?
Treat it like a Makefile. Commands in test_command, lint_command, security_commands, etc. run with your user privileges. First time you run ocak in a new repo it'll print a warning. Review the commands before running ocak run on repos you didnt write.
What if it breaks?
Issues get labeled pipeline-failed with a comment explaining what went wrong. Worktrees get cleaned up automaticaly. Run ocak clean to remove any stragglers, and check logs/pipeline/ for detailed logs.
How do I pause it?
Ctrl+C once lets the current step finish, commits WIP changes, resets labels, and prints resume commands. Ctrl+C twice kills subprocesses immediately. Exit code 130 either way.
ocak resume 42 --watch # pick up from where it stoppedgit clone https://github.com/clayharmon/ocak
cd ocak
bundle install
bundle exec rspec
bundle exec rubocopBug reports and pull requests welcome on GitHub.
MIT. See LICENSE.txt.