ocak 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +246 -70
- data/lib/ocak/agent_generator.rb +2 -0
- data/lib/ocak/claude_runner.rb +7 -6
- data/lib/ocak/commands/clean.rb +67 -6
- data/lib/ocak/commands/design.rb +1 -9
- data/lib/ocak/commands/hiz.rb +181 -56
- data/lib/ocak/commands/init.rb +12 -2
- data/lib/ocak/commands/resume.rb +31 -3
- data/lib/ocak/commands/run.rb +28 -6
- data/lib/ocak/commands/status.rb +120 -6
- data/lib/ocak/config.rb +11 -3
- data/lib/ocak/git_utils.rb +42 -0
- data/lib/ocak/issue_fetcher.rb +84 -2
- data/lib/ocak/logger.rb +10 -2
- data/lib/ocak/merge_manager.rb +63 -13
- data/lib/ocak/merge_orchestration.rb +102 -0
- data/lib/ocak/pipeline_executor.rb +177 -37
- data/lib/ocak/pipeline_runner.rb +113 -34
- data/lib/ocak/planner.rb +5 -3
- data/lib/ocak/process_registry.rb +44 -0
- data/lib/ocak/process_runner.rb +10 -4
- data/lib/ocak/reready_processor.rb +174 -0
- data/lib/ocak/run_report.rb +82 -0
- data/lib/ocak/step_comments.rb +23 -0
- data/lib/ocak/stream_parser.rb +10 -1
- data/lib/ocak/templates/agents/documenter.md.erb +39 -9
- data/lib/ocak/templates/agents/implementer.md.erb +42 -0
- data/lib/ocak/templates/agents/planner.md.erb +9 -6
- data/lib/ocak/templates/gitignore_additions.txt +2 -0
- data/lib/ocak/templates/ocak.yml.erb +6 -2
- data/lib/ocak/worktree_manager.rb +6 -2
- data/lib/ocak.rb +1 -1
- metadata +7 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f3ade72c57a02f3647218543b060295454aec639d90ba261b4fe9b88fa05f1bf
|
|
4
|
+
data.tar.gz: 86decb5ce7099d43214859b82f3b12fc7d55b5cdf3d1d57a30ed38dc7c74a957
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6ba522815876568e13b9453cdbd943a873c2ad28cee59a6c92d99b6dc62ccfeeadac57a2a2815b95f834b7c7173ffb2baf40107dcdfabc2cc74cf3d04446ad2d
|
|
7
|
+
data.tar.gz: 9b4d7d602aa8c2c213d64bbf75a5d9c5dfecb01952d327140c268f32474aaa02e3664f6b29cd60c9eee45fa1ac97e6f5012b9bbac51d54b1de68d40c34291a64
|
data/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
*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.*
|
|
4
4
|
|
|
5
|
-
Multi-agent pipeline that processes GitHub issues autonomously with Claude Code. You write an issue, label it, and ocak runs it through implement
|
|
5
|
+
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.
|
|
6
6
|
|
|
7
7
|
## Quick Start
|
|
8
8
|
|
|
@@ -19,61 +19,168 @@ ocak init
|
|
|
19
19
|
ocak run --once --watch
|
|
20
20
|
|
|
21
21
|
# Or run a single issue:
|
|
22
|
-
ocak run
|
|
22
|
+
ocak run 42 --watch
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
## How It Works
|
|
26
26
|
|
|
27
|
-
###
|
|
27
|
+
### Pipeline Flow
|
|
28
28
|
|
|
29
|
+
```mermaid
|
|
30
|
+
flowchart LR
|
|
31
|
+
A["/design<br>Create issue"] --> B["Label<br>auto-ready"]
|
|
32
|
+
B --> C["Planner<br>Batch & classify"]
|
|
33
|
+
C --> D["Worktree<br>per issue"]
|
|
34
|
+
D --> E["Pipeline<br>Steps"]
|
|
35
|
+
E --> F["Rebase<br>& merge"]
|
|
29
36
|
```
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
|
|
38
|
+
### Pipeline Steps
|
|
39
|
+
|
|
40
|
+
```mermaid
|
|
41
|
+
flowchart TD
|
|
42
|
+
IMP["1. Implementer<br>(opus)"] --> REV["2. Reviewer<br>(sonnet)"]
|
|
43
|
+
REV -->|"🔴 findings"| FIX1["3. Fix<br>(opus)"]
|
|
44
|
+
REV -->|"no findings"| SEC
|
|
45
|
+
FIX1 --> VER["4. Verify<br>(sonnet)"]
|
|
46
|
+
VER --> SEC["5. Security Review<br>(sonnet)"]
|
|
47
|
+
SEC -->|"🔴 findings"| FIX2["6. Security Fix<br>(opus)"]
|
|
48
|
+
SEC -->|"no findings"| DOC
|
|
49
|
+
FIX2 --> DOC["7. Documenter<br>(sonnet)"]
|
|
50
|
+
DOC --> AUD["8. Auditor<br>(sonnet)"]
|
|
51
|
+
AUD --> MRG["9. Merger<br>(sonnet)"]
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
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.
|
|
55
|
+
|
|
56
|
+
Conditional steps only run when needed:
|
|
57
|
+
- **Fix** only runs if the reviewer flagged 🔴 blocking issues
|
|
58
|
+
- **Verify** only runs if fixes were actually applied
|
|
59
|
+
- **Merge** gets skipped in `--manual-review` mode or when the auditor BLOCKs
|
|
60
|
+
|
|
61
|
+
### Label State Machine
|
|
62
|
+
|
|
63
|
+
```mermaid
|
|
64
|
+
stateDiagram-v2
|
|
65
|
+
[*] --> auto_ready
|
|
66
|
+
auto_ready --> auto_doing: Pipeline picks up
|
|
67
|
+
auto_doing --> completed: Success
|
|
68
|
+
auto_doing --> pipeline_failed: Failure
|
|
69
|
+
auto_doing --> auto_pending_human: --manual-review / audit BLOCK
|
|
70
|
+
auto_doing --> auto_ready: Ctrl+C interrupt
|
|
71
|
+
pipeline_failed --> auto_doing: ocak resume
|
|
72
|
+
auto_pending_human --> auto_reready: Human labels for re-review
|
|
73
|
+
auto_reready --> auto_pending_human: Feedback addressed
|
|
74
|
+
|
|
75
|
+
state "auto-ready" as auto_ready
|
|
76
|
+
state "auto-doing" as auto_doing
|
|
77
|
+
state "completed" as completed
|
|
78
|
+
state "pipeline-failed" as pipeline_failed
|
|
79
|
+
state "auto-pending-human" as auto_pending_human
|
|
80
|
+
state "auto-reready" as auto_reready
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Complexity Classification
|
|
84
|
+
|
|
85
|
+
The planner classifes each issue as `simple` or `full`:
|
|
86
|
+
- **Simple** — skips security review, second fix pass, documenter, and auditor
|
|
87
|
+
- **Full** — runs the whole thing
|
|
88
|
+
|
|
89
|
+
`--fast` forces all issues to `simple`, giving you: implement → review → fix (if needed) → verify (if fixed) → merge.
|
|
90
|
+
|
|
91
|
+
### Merge Flow
|
|
92
|
+
|
|
93
|
+
After all pipeline steps complete:
|
|
94
|
+
|
|
95
|
+
```mermaid
|
|
96
|
+
flowchart LR
|
|
97
|
+
A["Commit<br>changes"] --> B["Rebase<br>onto main"]
|
|
98
|
+
B -->|conflict| C["Implementer<br>resolves"]
|
|
99
|
+
C --> D
|
|
100
|
+
B -->|clean| D["Run tests"]
|
|
101
|
+
D --> E["Push branch"]
|
|
102
|
+
E --> F["Merger agent<br>create PR + merge"]
|
|
39
103
|
```
|
|
40
104
|
|
|
41
|
-
|
|
42
|
-
2. **Label** — slap the `auto-ready` label on it
|
|
43
|
-
3. **Plan** — planner agent figures out which issues can safely run in parallel
|
|
44
|
-
4. **Execute** — each issue gets a worktree, runs through the pipeline steps
|
|
45
|
-
5. **Merge** — completed work gets rebased, tested, and merged sequentially
|
|
105
|
+
Merging is sequential — one at a time — so you dont get conflicts between parallel worktrees.
|
|
46
106
|
|
|
47
|
-
|
|
107
|
+
## Agents
|
|
48
108
|
|
|
49
109
|
8 agents, each with scoped tool permisions:
|
|
50
110
|
|
|
51
111
|
| Agent | Role | Tools | Model |
|
|
52
112
|
|-------|------|-------|-------|
|
|
53
|
-
| **implementer** | Write code and tests | Read, Write, Edit, Bash | opus |
|
|
54
|
-
| **reviewer** | Check patterns, tests, quality | Read, Grep, Glob (read-only) | sonnet |
|
|
55
|
-
| **security-reviewer** | OWASP Top 10, auth, injection | Read, Grep, Glob, Bash | sonnet |
|
|
56
|
-
| **auditor** | Pre-merge gate
|
|
57
|
-
| **documenter** | Add missing docs | Read, Write, Edit | sonnet |
|
|
58
|
-
| **merger** | Create PR, merge, close issue | Read, Grep, Bash | sonnet |
|
|
59
|
-
| **planner** |
|
|
60
|
-
| **pipeline** | Self-contained
|
|
113
|
+
| **implementer** | Write code and tests | Read, Write, Edit, Bash, Task | opus |
|
|
114
|
+
| **reviewer** | Check patterns, tests, quality | Read, Grep, Glob, Bash (read-only) | sonnet |
|
|
115
|
+
| **security-reviewer** | OWASP Top 10, auth, injection | Read, Grep, Glob, Bash (read-only) | sonnet |
|
|
116
|
+
| **auditor** | Pre-merge security gate | Read, Grep, Glob, Bash (read-only) | sonnet |
|
|
117
|
+
| **documenter** | Add missing docs (skips if not needed) | Read, Write, Edit, Bash | sonnet |
|
|
118
|
+
| **merger** | Create PR, merge, close issue | Read, Glob, Grep, Bash | sonnet |
|
|
119
|
+
| **planner** | Batch issues, classify complexity | Read, Glob, Grep, Bash (read-only) | haiku |
|
|
120
|
+
| **pipeline** | Self-contained single-agent mode | All tools | opus |
|
|
61
121
|
|
|
62
|
-
|
|
122
|
+
Review agents (reviewer, security-reviewer, auditor) have no Write/Edit access — they can only read and report stuff back.
|
|
63
123
|
|
|
64
|
-
|
|
124
|
+
## Skills
|
|
65
125
|
|
|
66
|
-
|
|
67
|
-
- `/audit [scope]` — codebase sweep for security, patterns, tests, data, dependencies
|
|
68
|
-
- `/scan-file <path>` — deep single-file analysis with test coverage check
|
|
69
|
-
- `/debt` — tech debt tracker with risk scoring
|
|
126
|
+
Interactive skills for use inside Claude Code:
|
|
70
127
|
|
|
71
|
-
|
|
128
|
+
| Skill | Description |
|
|
129
|
+
|-------|-------------|
|
|
130
|
+
| `/design [description]` | Researches your codebase, asks clarifying questions, produces a detailed implementation-ready issue |
|
|
131
|
+
| `/audit [scope]` | Codebase sweep — scopes: `security`, `errors`, `patterns`, `tests`, `data`, `dependencies`, or `all` |
|
|
132
|
+
| `/scan-file <path>` | Deep single-file analysis with test coverage check, scored 1-10 per method |
|
|
133
|
+
| `/debt` | Tech debt tracker with risk scoring (churn, coverage, suppressions, age, blast radius) |
|
|
134
|
+
|
|
135
|
+
## Modes
|
|
72
136
|
|
|
137
|
+
### Full Pipeline — `ocak run`
|
|
138
|
+
|
|
139
|
+
The default. Polls for `auto-ready` issues, plans batches, runs the full step sequence in parallel worktrees, merges sequentally.
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
ocak run --once --watch # Process current batch and exit
|
|
143
|
+
ocak run 42 --watch # Single issue, full pipeline
|
|
144
|
+
ocak run --manual-review --watch # Create PRs without auto-merge
|
|
145
|
+
ocak run --audit --watch # Auditor as merge gate
|
|
146
|
+
ocak run --fast --watch # Skip security/docs/audit steps
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Fast Mode — `ocak hiz`
|
|
150
|
+
|
|
151
|
+
Lightweight alternative for quick PRs you'll review youself:
|
|
152
|
+
|
|
153
|
+
```mermaid
|
|
154
|
+
flowchart LR
|
|
155
|
+
A["Implementer<br>(sonnet)"] --> B["Reviewer (haiku)<br>+<br>Security (sonnet)<br>in parallel"]
|
|
156
|
+
B --> C["Verify<br>tests + lint"]
|
|
157
|
+
C --> D["Create PR<br>(no merge)"]
|
|
73
158
|
```
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
159
|
+
|
|
160
|
+
Runs in your current checkout (no worktree), uses cheaper models, creates a PR without merging. Rougly 5-10x cheaper than the full pipeline.
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
ocak hiz 42 --watch
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Re-review Flow
|
|
167
|
+
|
|
168
|
+
When `--manual-review` is enabled, PRs sit open for human review. After leaving feedback, slap the `auto-reready` label on the PR and ocak will:
|
|
169
|
+
|
|
170
|
+
1. Check out the PR branch
|
|
171
|
+
2. Run the implementer against the review comments
|
|
172
|
+
3. Push with `--force-with-lease`
|
|
173
|
+
4. Remove the `auto-reready` label
|
|
174
|
+
5. Comment "Feedback addressed. Please re-review."
|
|
175
|
+
|
|
176
|
+
### Graceful Shutdown
|
|
177
|
+
|
|
178
|
+
`Ctrl+C` once — current agent step finishes, then the pipeline stops. WIP gets committed, labels reset to `auto-ready`, and resume commands are printed.
|
|
179
|
+
|
|
180
|
+
`Ctrl+C` twice — kills active subprocesses immediatley (SIGTERM → wait → SIGKILL), then same cleanup runs.
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
ocak resume 42 --watch # Pick up from where it stopped
|
|
77
184
|
```
|
|
78
185
|
|
|
79
186
|
## Configuration
|
|
@@ -87,23 +194,35 @@ stack:
|
|
|
87
194
|
framework: rails
|
|
88
195
|
test_command: "bundle exec rspec"
|
|
89
196
|
lint_command: "bundle exec rubocop -A"
|
|
197
|
+
setup_command: "bundle install"
|
|
90
198
|
security_commands:
|
|
91
199
|
- "bundle exec brakeman -q"
|
|
92
200
|
- "bundle exec bundler-audit check"
|
|
93
201
|
|
|
94
202
|
# Pipeline settings
|
|
95
203
|
pipeline:
|
|
96
|
-
max_parallel:
|
|
97
|
-
poll_interval: 60
|
|
204
|
+
max_parallel: 5
|
|
205
|
+
poll_interval: 60
|
|
98
206
|
worktree_dir: ".claude/worktrees"
|
|
99
207
|
log_dir: "logs/pipeline"
|
|
208
|
+
cost_budget: 5.0 # Max USD per issue (kills pipeline if exceeded)
|
|
209
|
+
manual_review: false # Create PRs without auto-merge
|
|
210
|
+
audit_mode: false # Run auditor as merge gate
|
|
211
|
+
|
|
212
|
+
# Safety controls
|
|
213
|
+
safety:
|
|
214
|
+
allowed_authors: [] # Restrict to GitHub usernames (empty = current gh user)
|
|
215
|
+
require_comment: false # Require confirmation comment before processing
|
|
216
|
+
max_issues_per_run: 5 # Cap issues per polling cycle
|
|
100
217
|
|
|
101
218
|
# GitHub labels
|
|
102
219
|
labels:
|
|
103
220
|
ready: "auto-ready"
|
|
104
|
-
in_progress: "
|
|
221
|
+
in_progress: "auto-doing"
|
|
105
222
|
completed: "completed"
|
|
106
223
|
failed: "pipeline-failed"
|
|
224
|
+
reready: "auto-reready"
|
|
225
|
+
awaiting_review: "auto-pending-human"
|
|
107
226
|
|
|
108
227
|
# Pipeline steps — add, remove, reorder as you like
|
|
109
228
|
steps:
|
|
@@ -113,19 +232,23 @@ steps:
|
|
|
113
232
|
role: review
|
|
114
233
|
- agent: implementer
|
|
115
234
|
role: fix
|
|
116
|
-
condition: has_findings
|
|
235
|
+
condition: has_findings
|
|
117
236
|
- agent: reviewer
|
|
118
237
|
role: verify
|
|
119
|
-
condition: had_fixes
|
|
120
|
-
- agent:
|
|
238
|
+
condition: had_fixes
|
|
239
|
+
- agent: security-reviewer
|
|
121
240
|
role: security
|
|
241
|
+
complexity: full
|
|
122
242
|
- agent: implementer
|
|
123
243
|
role: fix
|
|
124
244
|
condition: has_findings
|
|
245
|
+
complexity: full
|
|
125
246
|
- agent: documenter
|
|
126
247
|
role: document
|
|
248
|
+
complexity: full
|
|
127
249
|
- agent: auditor
|
|
128
250
|
role: audit
|
|
251
|
+
complexity: full
|
|
129
252
|
- agent: merger
|
|
130
253
|
role: merge
|
|
131
254
|
|
|
@@ -189,6 +312,43 @@ steps:
|
|
|
189
312
|
role: custom_step
|
|
190
313
|
```
|
|
191
314
|
|
|
315
|
+
### Per-Step Model Override
|
|
316
|
+
|
|
317
|
+
Override the default model for any step:
|
|
318
|
+
|
|
319
|
+
```yaml
|
|
320
|
+
steps:
|
|
321
|
+
- agent: implementer
|
|
322
|
+
role: implement
|
|
323
|
+
model: sonnet # Use sonnet instead of opus for cheaper runs
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## Stack Detection
|
|
327
|
+
|
|
328
|
+
`ocak init` auto-detects your project stack and generates tailored agent templates. For anything else you get generic agents that you can cusomize.
|
|
329
|
+
|
|
330
|
+
| Language | Frameworks | Test | Lint | Security |
|
|
331
|
+
|----------|-----------|------|------|----------|
|
|
332
|
+
| Ruby | Rails, Sinatra, Hanami | rspec | rubocop | brakeman, bundler-audit |
|
|
333
|
+
| TypeScript/JS | Next, Remix, Nuxt, Svelte, React, Vue, Express | vitest, jest | biome, eslint | npm audit |
|
|
334
|
+
| Python | Django, Flask, FastAPI | pytest | ruff, flake8 | bandit, safety |
|
|
335
|
+
| Rust | Actix, Axum, Rocket | cargo test | cargo clippy | cargo audit |
|
|
336
|
+
| Go | Gin, Echo, Fiber, Chi | go test | golangci-lint | gosec |
|
|
337
|
+
| Java | — | gradle test | — | — |
|
|
338
|
+
| Elixir | Phoenix | mix test | mix credo | — |
|
|
339
|
+
|
|
340
|
+
Monorepo detection: npm/pnpm workspaces, Cargo workspaces, Go workspaces, Lerna, and convention-based (`packages/`, `apps/`, `services/`).
|
|
341
|
+
|
|
342
|
+
## Run Reports
|
|
343
|
+
|
|
344
|
+
Pipeline runs generate JSON reports in `.ocak/reports/`:
|
|
345
|
+
|
|
346
|
+
```bash
|
|
347
|
+
ocak status --report
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
Shows 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.
|
|
351
|
+
|
|
192
352
|
## Writing Good Issues
|
|
193
353
|
|
|
194
354
|
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:
|
|
@@ -204,50 +364,66 @@ The `/design` skill produces issues formatted for zero-context agents. Think of
|
|
|
204
364
|
## CLI Reference
|
|
205
365
|
|
|
206
366
|
```
|
|
207
|
-
ocak init [--force] [--no-ai]
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
367
|
+
ocak init [--force] [--no-ai] Set up pipeline in current project
|
|
368
|
+
[--config-only] Only generate config, hooks, settings
|
|
369
|
+
[--skip-agents] Skip agent generation
|
|
370
|
+
[--skip-skills] Skip skill generation
|
|
371
|
+
|
|
372
|
+
ocak run [ISSUE] [options] Run the pipeline
|
|
373
|
+
--watch Stream agent activity with color
|
|
374
|
+
--dry-run Show plan without executing
|
|
375
|
+
--once Process current batch and exit
|
|
376
|
+
--fast Skip security/docs/audit (simple complexity)
|
|
377
|
+
--max-parallel N Limit concurrency (default: 5)
|
|
378
|
+
--poll-interval N Seconds between polls (default: 60)
|
|
379
|
+
--manual-review Create PRs without auto-merge
|
|
380
|
+
--audit Run auditor as post-pipeline gate
|
|
381
|
+
--verbose / --quiet Control output verbosity
|
|
382
|
+
|
|
383
|
+
ocak hiz ISSUE [options] Fast mode: implement + parallel review
|
|
384
|
+
--watch Stream agent activity
|
|
385
|
+
--dry-run Show plan without executing
|
|
386
|
+
--verbose / --quiet Control output verbosity
|
|
387
|
+
|
|
388
|
+
ocak resume ISSUE [options] Resume from last successful step
|
|
389
|
+
--watch Stream agent activity
|
|
390
|
+
--dry-run Show which steps would re-run
|
|
391
|
+
--verbose / --quiet Control output verbosity
|
|
392
|
+
|
|
393
|
+
ocak status Show pipeline state
|
|
394
|
+
--report Show run reports (cost, duration, stats)
|
|
395
|
+
|
|
396
|
+
ocak clean Remove stale worktrees
|
|
397
|
+
--logs Clean log files, state, and reports
|
|
398
|
+
--all Clean worktrees and logs
|
|
399
|
+
--keep N Only remove artifacts older than N days
|
|
400
|
+
|
|
401
|
+
ocak design [DESCRIPTION] Launch interactive issue design session
|
|
402
|
+
ocak audit [SCOPE] Print instructions for /audit skill
|
|
403
|
+
ocak debt Print instructions for /debt skill
|
|
220
404
|
```
|
|
221
405
|
|
|
222
406
|
## FAQ
|
|
223
407
|
|
|
224
408
|
**How much does it cost?**
|
|
225
409
|
|
|
226
|
-
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.
|
|
410
|
+
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.
|
|
227
411
|
|
|
228
412
|
**Is it safe?**
|
|
229
413
|
|
|
230
|
-
Reasonably. Review agents are read-only (no Write/Edit tools), merging is sequential so you
|
|
414
|
+
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.
|
|
231
415
|
|
|
232
416
|
**What if it breaks?**
|
|
233
417
|
|
|
234
|
-
Issues get labeled `pipeline-failed` with a comment explaining what went wrong. Worktrees get cleaned up
|
|
235
|
-
|
|
236
|
-
**Can I run one issue manually?**
|
|
237
|
-
|
|
238
|
-
```bash
|
|
239
|
-
ocak run --single 42 --watch
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
Runs the full pipeline for issue #42 in your current checkout (no worktree).
|
|
418
|
+
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.
|
|
243
419
|
|
|
244
420
|
**How do I pause it?**
|
|
245
421
|
|
|
246
|
-
|
|
422
|
+
`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.
|
|
247
423
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
424
|
+
```bash
|
|
425
|
+
ocak resume 42 --watch # pick up from where it stopped
|
|
426
|
+
```
|
|
251
427
|
|
|
252
428
|
## Development
|
|
253
429
|
|
data/lib/ocak/agent_generator.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'erb'
|
|
4
4
|
require 'fileutils'
|
|
5
|
+
require 'open3'
|
|
5
6
|
|
|
6
7
|
module Ocak
|
|
7
8
|
class AgentGenerator
|
|
@@ -89,6 +90,7 @@ module Ocak
|
|
|
89
90
|
monorepo = @stack.respond_to?(:monorepo) ? @stack.monorepo : false
|
|
90
91
|
packages = @stack.respond_to?(:packages) ? (@stack.packages || []) : []
|
|
91
92
|
project_dir = @project_dir
|
|
93
|
+
max_parallel = 5
|
|
92
94
|
|
|
93
95
|
binding
|
|
94
96
|
end
|
data/lib/ocak/claude_runner.rb
CHANGED
|
@@ -14,9 +14,8 @@ module Ocak
|
|
|
14
14
|
def warnings? = output.to_s.include?("\u{1F7E1}")
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
FailedStatus
|
|
18
|
-
|
|
19
|
-
end
|
|
17
|
+
# Backwards-compat alias — FailedStatus now lives in ProcessRunner.
|
|
18
|
+
FailedStatus = ProcessRunner::FailedStatus
|
|
20
19
|
|
|
21
20
|
AGENT_TOOLS = {
|
|
22
21
|
'implementer' => 'Read,Write,Edit,Glob,Grep,Bash',
|
|
@@ -53,10 +52,11 @@ module Ocak
|
|
|
53
52
|
/overloaded/i
|
|
54
53
|
].freeze
|
|
55
54
|
|
|
56
|
-
def initialize(config:, logger:, watch: nil)
|
|
55
|
+
def initialize(config:, logger:, watch: nil, registry: nil)
|
|
57
56
|
@config = config
|
|
58
57
|
@logger = logger
|
|
59
58
|
@watch = watch
|
|
59
|
+
@registry = registry
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
def run_agent(agent_name, prompt, chdir: nil, model: nil, retries: MAX_RETRIES)
|
|
@@ -119,7 +119,8 @@ module Ocak
|
|
|
119
119
|
end
|
|
120
120
|
|
|
121
121
|
def build_agent_result(parser, status, stderr, agent_name)
|
|
122
|
-
|
|
122
|
+
full = parser.full_output
|
|
123
|
+
output = full.empty? ? (parser.result_text || '') : full
|
|
123
124
|
exit_ok = status.respond_to?(:success?) && status.success?
|
|
124
125
|
success = parser.success? && exit_ok
|
|
125
126
|
|
|
@@ -153,7 +154,7 @@ module Ocak
|
|
|
153
154
|
cmd.push('--model', model) if model
|
|
154
155
|
cmd.push('--', prompt)
|
|
155
156
|
|
|
156
|
-
ProcessRunner.run(cmd, chdir: chdir, timeout: TIMEOUT, on_line: on_line)
|
|
157
|
+
ProcessRunner.run(cmd, chdir: chdir, timeout: TIMEOUT, on_line: on_line, registry: @registry)
|
|
157
158
|
end
|
|
158
159
|
|
|
159
160
|
def extract_result_from_stream(raw)
|
data/lib/ocak/commands/clean.rb
CHANGED
|
@@ -1,17 +1,34 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'fileutils'
|
|
3
4
|
require_relative '../config'
|
|
4
5
|
require_relative '../worktree_manager'
|
|
5
6
|
|
|
6
7
|
module Ocak
|
|
7
8
|
module Commands
|
|
8
9
|
class Clean < Dry::CLI::Command
|
|
9
|
-
desc 'Remove stale worktrees and
|
|
10
|
+
desc 'Remove stale worktrees, logs, and reports'
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
option :logs, type: :boolean, default: false, desc: 'Clean log files, state files, and reports'
|
|
13
|
+
option :all, type: :boolean, default: false, desc: 'Clean worktrees and logs'
|
|
14
|
+
option :keep, type: :integer, desc: 'Only remove artifacts older than N days'
|
|
15
|
+
|
|
16
|
+
def call(**options)
|
|
12
17
|
config = Config.load
|
|
13
|
-
|
|
18
|
+
do_worktrees = !options[:logs] || options[:all]
|
|
19
|
+
do_logs = options[:logs] || options[:all]
|
|
20
|
+
|
|
21
|
+
clean_worktrees(config) if do_worktrees
|
|
22
|
+
clean_logs(config, keep_days: options[:keep]) if do_logs
|
|
23
|
+
rescue Config::ConfigNotFound => e
|
|
24
|
+
warn "Error: #{e.message}"
|
|
25
|
+
exit 1
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
14
29
|
|
|
30
|
+
def clean_worktrees(config)
|
|
31
|
+
manager = WorktreeManager.new(config: config)
|
|
15
32
|
puts 'Cleaning stale worktrees...'
|
|
16
33
|
removed = manager.clean_stale
|
|
17
34
|
|
|
@@ -21,9 +38,53 @@ module Ocak
|
|
|
21
38
|
removed.each { |path| puts " Removed: #{path}" }
|
|
22
39
|
puts "Cleaned #{removed.size} worktree(s)."
|
|
23
40
|
end
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def clean_logs(config, keep_days:)
|
|
44
|
+
cutoff = keep_days ? Time.now - (keep_days * 86_400) : nil
|
|
45
|
+
puts keep_days ? "Cleaning logs older than #{keep_days} days..." : 'Cleaning logs...'
|
|
46
|
+
|
|
47
|
+
log_dir = File.join(config.project_dir, config.log_dir)
|
|
48
|
+
reports_dir = File.join(config.project_dir, '.ocak', 'reports')
|
|
49
|
+
|
|
50
|
+
artifacts = collect_artifacts(log_dir, reports_dir)
|
|
51
|
+
removed = remove_artifacts(artifacts, cutoff)
|
|
52
|
+
|
|
53
|
+
if removed.empty?
|
|
54
|
+
puts 'No artifacts to clean.'
|
|
55
|
+
else
|
|
56
|
+
puts "Cleaned #{removed.size} artifact(s)."
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def collect_artifacts(log_dir, reports_dir)
|
|
61
|
+
artifacts = []
|
|
62
|
+
|
|
63
|
+
if Dir.exist?(log_dir)
|
|
64
|
+
artifacts += Dir.glob(File.join(log_dir, '*.log'))
|
|
65
|
+
artifacts += Dir.glob(File.join(log_dir, 'issue-*-state.json'))
|
|
66
|
+
artifacts += Dir.glob(File.join(log_dir, 'issue-*/')).select { |f| File.directory?(f) }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
artifacts += Dir.glob(File.join(reports_dir, '*.json')) if Dir.exist?(reports_dir)
|
|
70
|
+
|
|
71
|
+
artifacts
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def remove_artifacts(artifacts, cutoff)
|
|
75
|
+
removed = []
|
|
76
|
+
artifacts.each do |path|
|
|
77
|
+
next if cutoff && File.mtime(path) >= cutoff
|
|
78
|
+
|
|
79
|
+
if File.directory?(path)
|
|
80
|
+
FileUtils.rm_rf(path)
|
|
81
|
+
else
|
|
82
|
+
FileUtils.rm_f(path)
|
|
83
|
+
end
|
|
84
|
+
puts " Removed: #{path}"
|
|
85
|
+
removed << path
|
|
86
|
+
end
|
|
87
|
+
removed
|
|
27
88
|
end
|
|
28
89
|
end
|
|
29
90
|
end
|
data/lib/ocak/commands/design.rb
CHANGED
|
@@ -15,18 +15,10 @@ module Ocak
|
|
|
15
15
|
exit 1
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
puts 'Starting interactive design session...'
|
|
19
|
-
puts 'This will open Claude Code with the /design skill.'
|
|
20
|
-
puts ''
|
|
21
|
-
|
|
22
18
|
if description
|
|
23
19
|
exec('claude', '--skill', skill_path, '--', description)
|
|
24
20
|
else
|
|
25
|
-
|
|
26
|
-
puts ' /design <description of what you want to build>'
|
|
27
|
-
puts ''
|
|
28
|
-
puts 'Or provide a description directly:'
|
|
29
|
-
puts ' ocak design "add user authentication with OAuth"'
|
|
21
|
+
exec('claude', '--skill', skill_path)
|
|
30
22
|
end
|
|
31
23
|
end
|
|
32
24
|
end
|