@nbardy/oompa 0.1.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.
- package/README.md +53 -16
- package/agentnet/src/agentnet/cli.clj +6 -3
- package/agentnet/src/agentnet/worker.clj +103 -43
- package/bin/oompa.js +1 -1
- package/config/prompts/_task_header.md +46 -0
- package/config/prompts/executor.md +6 -31
- package/config/prompts/planner.md +11 -35
- package/config/prompts/worker.md +5 -30
- package/oompa.example.json +4 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -79,25 +79,51 @@ This repo has a fleshed out version of the idea. The oompa loompas are organized
|
|
|
79
79
|
|
|
80
80
|
### Configuration
|
|
81
81
|
|
|
82
|
-
**oompa.json
|
|
82
|
+
**oompa.json** — the only file you need:
|
|
83
83
|
```json
|
|
84
84
|
{
|
|
85
85
|
"review_model": "codex:codex-5.2",
|
|
86
86
|
"workers": [
|
|
87
|
-
{"model": "claude:opus-4.5", "
|
|
88
|
-
{"model": "codex:codex-5.2-mini", "iterations": 10, "count": 3, "
|
|
87
|
+
{"model": "claude:opus-4.5", "prompt": ["config/prompts/planner.md"], "iterations": 5, "count": 1},
|
|
88
|
+
{"model": "codex:codex-5.2-mini", "prompt": ["config/prompts/executor.md"], "iterations": 10, "count": 3, "can_plan": false}
|
|
89
89
|
]
|
|
90
90
|
}
|
|
91
91
|
```
|
|
92
92
|
|
|
93
93
|
This spawns:
|
|
94
|
-
- **1 planner** (opus) —
|
|
94
|
+
- **1 planner** (opus) — reads spec, explores codebase, creates/refines tasks
|
|
95
95
|
- **3 executors** (mini) — claims and executes tasks fast
|
|
96
96
|
- **Reviews** done by codex-5.2 before any merge
|
|
97
97
|
|
|
98
|
+
#### Worker fields
|
|
99
|
+
|
|
100
|
+
| Field | Required | Description |
|
|
101
|
+
|-------|----------|-------------|
|
|
102
|
+
| `model` | yes | `harness:model` (e.g. `claude:opus-4.5`, `codex:codex-5.2-mini`) |
|
|
103
|
+
| `prompt` | no | String or array of paths — concatenated into one prompt |
|
|
104
|
+
| `iterations` | no | Max iterations per worker (default: 10) |
|
|
105
|
+
| `count` | no | Number of workers with this config (default: 1) |
|
|
106
|
+
| `can_plan` | no | If `false`, worker waits for tasks before starting (default: `true`) |
|
|
107
|
+
|
|
108
|
+
#### Composable prompts
|
|
109
|
+
|
|
110
|
+
`prompt` accepts a string or an array. Arrays get concatenated, so you can reuse a shared base across workers:
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"workers": [
|
|
115
|
+
{"model": "claude:opus-4.5", "prompt": ["prompts/base.md", "prompts/architect.md"], "count": 1},
|
|
116
|
+
{"model": "codex:codex-5.2-mini", "prompt": ["prompts/base.md", "prompts/frontend.md"], "count": 2},
|
|
117
|
+
{"model": "codex:codex-5.2-mini", "prompt": ["prompts/base.md", "prompts/backend.md"], "count": 2}
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Every worker automatically gets task management instructions injected above your prompts. Your prompts just say *what* the worker should do — the framework handles *how* tasks work.
|
|
123
|
+
|
|
98
124
|
### Task System
|
|
99
125
|
|
|
100
|
-
Workers self-organize via filesystem:
|
|
126
|
+
Workers self-organize via the filesystem. Tasks live at the project root and are shared across all worktrees:
|
|
101
127
|
|
|
102
128
|
```
|
|
103
129
|
tasks/
|
|
@@ -106,20 +132,31 @@ tasks/
|
|
|
106
132
|
└── complete/*.edn # done
|
|
107
133
|
```
|
|
108
134
|
|
|
109
|
-
|
|
110
|
-
- **
|
|
111
|
-
- **
|
|
112
|
-
- **
|
|
135
|
+
From inside a worktree, workers reach tasks via `../tasks/`:
|
|
136
|
+
- **See tasks**: `ls ../tasks/pending/`
|
|
137
|
+
- **Claim**: `mv ../tasks/pending/task.edn ../tasks/current/`
|
|
138
|
+
- **Complete**: `mv ../tasks/current/task.edn ../tasks/complete/`
|
|
139
|
+
- **Create**: write new `.edn` to `../tasks/pending/`
|
|
140
|
+
|
|
141
|
+
Task file format:
|
|
142
|
+
```edn
|
|
143
|
+
{:id "task-001"
|
|
144
|
+
:summary "Add user authentication"
|
|
145
|
+
:description "Implement JWT-based auth for the API"
|
|
146
|
+
:difficulty :medium
|
|
147
|
+
:files ["src/auth.py" "tests/test_auth.py"]
|
|
148
|
+
:acceptance ["Login endpoint returns token" "Tests pass"]}
|
|
149
|
+
```
|
|
113
150
|
|
|
114
|
-
### Prompts
|
|
151
|
+
### Bundled Prompts
|
|
115
152
|
|
|
116
|
-
Three
|
|
153
|
+
Three prompt files ship with oompa that you can use in your `prompt` arrays:
|
|
117
154
|
|
|
118
|
-
| Prompt |
|
|
119
|
-
|
|
120
|
-
| `worker.md` |
|
|
121
|
-
| `planner.md` |
|
|
122
|
-
| `executor.md` |
|
|
155
|
+
| Prompt | Creates Tasks? | Executes Tasks? | Best For |
|
|
156
|
+
|--------|----------------|-----------------|----------|
|
|
157
|
+
| `config/prompts/worker.md` (default) | yes | yes | General purpose |
|
|
158
|
+
| `config/prompts/planner.md` | yes | sometimes | Big models — task design |
|
|
159
|
+
| `config/prompts/executor.md` | no | yes | Small/fast models — heads-down work |
|
|
123
160
|
|
|
124
161
|
---
|
|
125
162
|
|
|
@@ -300,10 +300,12 @@
|
|
|
300
300
|
(println "{")
|
|
301
301
|
(println " \"review_model\": \"codex:codex-5.2\",")
|
|
302
302
|
(println " \"workers\": [")
|
|
303
|
-
(println " {\"model\": \"codex:codex-5.2-mini\", \"iterations\": 10, \"count\": 3},")
|
|
304
|
-
(println " {\"model\": \"
|
|
303
|
+
(println " {\"model\": \"codex:codex-5.2-mini\", \"prompt\": \"prompts/executor.md\", \"iterations\": 10, \"count\": 3, \"can_plan\": false},")
|
|
304
|
+
(println " {\"model\": \"claude:opus-4.5\", \"prompt\": [\"prompts/base.md\", \"prompts/planner.md\"], \"count\": 1}")
|
|
305
305
|
(println " ]")
|
|
306
306
|
(println "}")
|
|
307
|
+
(println)
|
|
308
|
+
(println "prompt: string or array of paths — concatenated into one prompt.")
|
|
307
309
|
(System/exit 1))
|
|
308
310
|
(let [config (json/parse-string (slurp f) true)
|
|
309
311
|
review-model (some-> (:review_model config) parse-model-string)
|
|
@@ -325,7 +327,8 @@
|
|
|
325
327
|
:harness harness
|
|
326
328
|
:model model
|
|
327
329
|
:iterations (or (:iterations wc) 10)
|
|
328
|
-
:
|
|
330
|
+
:prompts (:prompt wc)
|
|
331
|
+
:can-plan (:can_plan wc)
|
|
329
332
|
:review-harness (:harness review-model)
|
|
330
333
|
:review-model (:model review-model)})))
|
|
331
334
|
expanded-workers)]
|
|
@@ -93,15 +93,31 @@
|
|
|
93
93
|
;; Worker State
|
|
94
94
|
;; =============================================================================
|
|
95
95
|
|
|
96
|
+
(def ^:private package-root
|
|
97
|
+
"Root of the oompa package — set by bin/oompa.js, falls back to cwd."
|
|
98
|
+
(or (System/getenv "OOMPA_PACKAGE_ROOT") "."))
|
|
99
|
+
|
|
100
|
+
(defn- load-prompt
|
|
101
|
+
"Load a prompt file. Tries path as-is first, then from package root."
|
|
102
|
+
[path]
|
|
103
|
+
(or (agent/load-custom-prompt path)
|
|
104
|
+
(agent/load-custom-prompt (str package-root "/" path))))
|
|
105
|
+
|
|
96
106
|
(defn create-worker
|
|
97
|
-
"Create a worker config
|
|
98
|
-
|
|
107
|
+
"Create a worker config.
|
|
108
|
+
:prompts is a string or vector of strings — paths to prompt files.
|
|
109
|
+
:can-plan when false, worker waits for tasks before starting (backpressure)."
|
|
110
|
+
[{:keys [id swarm-id harness model iterations prompts can-plan review-harness review-model]}]
|
|
99
111
|
{:id id
|
|
100
112
|
:swarm-id swarm-id
|
|
101
113
|
:harness (or harness :codex)
|
|
102
114
|
:model model
|
|
103
115
|
:iterations (or iterations 10)
|
|
104
|
-
:
|
|
116
|
+
:prompts (cond
|
|
117
|
+
(vector? prompts) prompts
|
|
118
|
+
(string? prompts) [prompts]
|
|
119
|
+
:else [])
|
|
120
|
+
:can-plan (if (some? can-plan) can-plan true)
|
|
105
121
|
:review-harness review-harness
|
|
106
122
|
:review-model review-model
|
|
107
123
|
:completed 0
|
|
@@ -128,25 +144,36 @@
|
|
|
128
144
|
|
|
129
145
|
(defn- run-agent!
|
|
130
146
|
"Run agent with prompt, return {:output string, :done? bool, :exit int}"
|
|
131
|
-
[{:keys [id swarm-id harness model
|
|
132
|
-
(let [;;
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
147
|
+
[{:keys [id swarm-id harness model prompts]} worktree-path context]
|
|
148
|
+
(let [;; 1. Task header (always, from package)
|
|
149
|
+
task-header (or (load-prompt "config/prompts/_task_header.md") "")
|
|
150
|
+
|
|
151
|
+
;; 2. Load and concatenate all user prompts (string or list)
|
|
152
|
+
user-prompts (if (seq prompts)
|
|
153
|
+
(->> prompts
|
|
154
|
+
(map load-prompt)
|
|
155
|
+
(remove nil?)
|
|
156
|
+
(str/join "\n\n"))
|
|
157
|
+
(or (load-prompt "config/prompts/worker.md")
|
|
158
|
+
"You are a worker. Claim tasks, execute them, complete them."))
|
|
159
|
+
|
|
160
|
+
;; Assemble: task header + status + user prompts
|
|
161
|
+
full-prompt (str task-header "\n"
|
|
162
|
+
"Task Status: " (:task_status context) "\n"
|
|
163
|
+
"Pending: " (:pending_tasks context) "\n\n"
|
|
164
|
+
user-prompts)
|
|
141
165
|
session-id (str/lower-case (str (java.util.UUID/randomUUID)))
|
|
142
166
|
swarm-id* (or swarm-id "unknown")
|
|
143
167
|
tagged-prompt (str "[oompa:" swarm-id* ":" id "] " full-prompt)
|
|
144
168
|
abs-worktree (.getAbsolutePath (io/file worktree-path))
|
|
145
169
|
|
|
146
|
-
;; Build command
|
|
170
|
+
;; Build command — both harnesses run with cwd=worktree, no sandbox
|
|
171
|
+
;; so agents can `..` to reach project root for task management
|
|
147
172
|
cmd (case harness
|
|
148
|
-
:codex (cond-> ["codex" "exec"
|
|
149
|
-
"-
|
|
173
|
+
:codex (cond-> ["codex" "exec"
|
|
174
|
+
"--dangerously-bypass-approvals-and-sandbox"
|
|
175
|
+
"--skip-git-repo-check"
|
|
176
|
+
"-C" abs-worktree]
|
|
150
177
|
model (into ["--model" model])
|
|
151
178
|
true (conj "--" full-prompt))
|
|
152
179
|
:claude (cond-> ["claude" "-p" "--dangerously-skip-permissions"
|
|
@@ -156,14 +183,13 @@
|
|
|
156
183
|
_ (when (= harness :codex)
|
|
157
184
|
(persist-message! id session-id abs-worktree "user" tagged-prompt))
|
|
158
185
|
|
|
159
|
-
;; Run agent
|
|
186
|
+
;; Run agent — both run with cwd=worktree
|
|
160
187
|
result (try
|
|
161
188
|
(if (= harness :claude)
|
|
162
|
-
|
|
163
|
-
(process/sh cmd {:
|
|
164
|
-
;; Codex takes prompt as arg
|
|
165
|
-
(process/sh cmd {:out :string :err :string}))
|
|
189
|
+
(process/sh cmd {:dir abs-worktree :in tagged-prompt :out :string :err :string})
|
|
190
|
+
(process/sh cmd {:dir abs-worktree :out :string :err :string}))
|
|
166
191
|
(catch Exception e
|
|
192
|
+
(println (format "[%s] Agent exception: %s" id (.getMessage e)))
|
|
167
193
|
{:exit -1 :out "" :err (.getMessage e)}))]
|
|
168
194
|
|
|
169
195
|
(when (= harness :codex)
|
|
@@ -194,20 +220,24 @@
|
|
|
194
220
|
"- NEEDS_CHANGES with bullet points of issues\n"
|
|
195
221
|
"- REJECTED if fundamentally wrong")
|
|
196
222
|
|
|
197
|
-
|
|
223
|
+
abs-wt (.getAbsolutePath (io/file worktree-path))
|
|
224
|
+
|
|
225
|
+
;; Build command — cwd=worktree, no sandbox
|
|
198
226
|
cmd (case review-harness
|
|
199
|
-
:codex (cond-> ["codex" "exec"
|
|
200
|
-
"-
|
|
227
|
+
:codex (cond-> ["codex" "exec"
|
|
228
|
+
"--dangerously-bypass-approvals-and-sandbox"
|
|
229
|
+
"--skip-git-repo-check"
|
|
230
|
+
"-C" abs-wt]
|
|
201
231
|
review-model (into ["--model" review-model])
|
|
202
232
|
true (conj "--" review-prompt))
|
|
203
233
|
:claude (cond-> ["claude" "-p" "--dangerously-skip-permissions"]
|
|
204
234
|
review-model (into ["--model" review-model])))
|
|
205
235
|
|
|
206
|
-
;; Run reviewer
|
|
236
|
+
;; Run reviewer — cwd=worktree
|
|
207
237
|
result (try
|
|
208
238
|
(if (= review-harness :claude)
|
|
209
|
-
(process/sh cmd {:in review-prompt :out :string :err :string})
|
|
210
|
-
(process/sh cmd {:out :string :err :string}))
|
|
239
|
+
(process/sh cmd {:dir abs-wt :in review-prompt :out :string :err :string})
|
|
240
|
+
(process/sh cmd {:dir abs-wt :out :string :err :string}))
|
|
211
241
|
(catch Exception e
|
|
212
242
|
{:exit -1 :out "" :err (.getMessage e)}))
|
|
213
243
|
|
|
@@ -229,9 +259,13 @@
|
|
|
229
259
|
feedback "\n\n"
|
|
230
260
|
"Please fix these issues in the worktree.")
|
|
231
261
|
|
|
262
|
+
abs-wt (.getAbsolutePath (io/file worktree-path))
|
|
263
|
+
|
|
232
264
|
cmd (case harness
|
|
233
|
-
:codex (cond-> ["codex" "exec"
|
|
234
|
-
"-
|
|
265
|
+
:codex (cond-> ["codex" "exec"
|
|
266
|
+
"--dangerously-bypass-approvals-and-sandbox"
|
|
267
|
+
"--skip-git-repo-check"
|
|
268
|
+
"-C" abs-wt]
|
|
235
269
|
model (into ["--model" model])
|
|
236
270
|
true (conj "--" fix-prompt))
|
|
237
271
|
:claude (cond-> ["claude" "-p" "--dangerously-skip-permissions"]
|
|
@@ -239,8 +273,8 @@
|
|
|
239
273
|
|
|
240
274
|
result (try
|
|
241
275
|
(if (= harness :claude)
|
|
242
|
-
(process/sh cmd {:in fix-prompt :out :string :err :string})
|
|
243
|
-
(process/sh cmd {:out :string :err :string}))
|
|
276
|
+
(process/sh cmd {:dir abs-wt :in fix-prompt :out :string :err :string})
|
|
277
|
+
(process/sh cmd {:dir abs-wt :out :string :err :string}))
|
|
244
278
|
(catch Exception e
|
|
245
279
|
{:exit -1 :out "" :err (.getMessage e)}))]
|
|
246
280
|
|
|
@@ -249,18 +283,18 @@
|
|
|
249
283
|
|
|
250
284
|
(defn- merge-to-main!
|
|
251
285
|
"Merge worktree changes to main branch"
|
|
252
|
-
[wt-path wt-id worker-id]
|
|
286
|
+
[wt-path wt-id worker-id project-root]
|
|
253
287
|
(println (format "[%s] Merging changes to main" worker-id))
|
|
254
288
|
(let [;; Commit in worktree if needed
|
|
255
289
|
_ (process/sh ["git" "add" "-A"] {:dir wt-path})
|
|
256
290
|
_ (process/sh ["git" "commit" "-m" (str "Work from " wt-id) "--allow-empty"]
|
|
257
291
|
{:dir wt-path})
|
|
258
|
-
;; Checkout main and merge
|
|
292
|
+
;; Checkout main and merge (in project root, not worktree)
|
|
259
293
|
checkout-result (process/sh ["git" "checkout" "main"]
|
|
260
|
-
{:out :string :err :string})
|
|
294
|
+
{:dir project-root :out :string :err :string})
|
|
261
295
|
merge-result (when (zero? (:exit checkout-result))
|
|
262
296
|
(process/sh ["git" "merge" wt-id "--no-edit"]
|
|
263
|
-
{:out :string :err :string}))]
|
|
297
|
+
{:dir project-root :out :string :err :string}))]
|
|
264
298
|
(and (zero? (:exit checkout-result))
|
|
265
299
|
(zero? (:exit merge-result)))))
|
|
266
300
|
|
|
@@ -310,15 +344,17 @@
|
|
|
310
344
|
Returns {:status :done|:continue|:error, :task task-or-nil}"
|
|
311
345
|
[worker iteration total-iterations]
|
|
312
346
|
(let [worker-id (:id worker)
|
|
313
|
-
|
|
347
|
+
project-root (System/getProperty "user.dir")
|
|
348
|
+
wt-dir (format ".w%s-i%d" worker-id iteration)
|
|
349
|
+
wt-branch (format "oompa/%s-i%d" worker-id iteration)
|
|
314
350
|
|
|
315
351
|
;; Create worktree
|
|
316
352
|
_ (println (format "[%s] Starting iteration %d/%d" worker-id iteration total-iterations))
|
|
317
|
-
wt-path (str
|
|
353
|
+
wt-path (str project-root "/" wt-dir)]
|
|
318
354
|
|
|
319
355
|
(try
|
|
320
|
-
;; Setup worktree
|
|
321
|
-
(process/sh ["git" "worktree" "add" wt-
|
|
356
|
+
;; Setup worktree (in project root) — dir starts with . but branch name must be valid
|
|
357
|
+
(process/sh ["git" "worktree" "add" wt-dir "-b" wt-branch] {:dir project-root})
|
|
322
358
|
|
|
323
359
|
;; Build context
|
|
324
360
|
(let [context (build-context)
|
|
@@ -336,7 +372,7 @@
|
|
|
336
372
|
;; Agent errored
|
|
337
373
|
(not (zero? exit))
|
|
338
374
|
(do
|
|
339
|
-
(println (format "[%s] Agent error (exit %d)" worker-id exit))
|
|
375
|
+
(println (format "[%s] Agent error (exit %d): %s" worker-id exit (subs (or output "") 0 (min 200 (count (or output ""))))))
|
|
340
376
|
{:status :error :exit exit})
|
|
341
377
|
|
|
342
378
|
;; Success - run review loop before merge
|
|
@@ -344,7 +380,7 @@
|
|
|
344
380
|
(let [{:keys [approved?]} (review-loop! worker wt-path worker-id)]
|
|
345
381
|
(if approved?
|
|
346
382
|
(do
|
|
347
|
-
(merge-to-main! wt-path wt-
|
|
383
|
+
(merge-to-main! wt-path wt-branch worker-id project-root)
|
|
348
384
|
(println (format "[%s] Iteration %d/%d complete" worker-id iteration total-iterations))
|
|
349
385
|
{:status :continue})
|
|
350
386
|
(do
|
|
@@ -352,13 +388,33 @@
|
|
|
352
388
|
{:status :continue})))))
|
|
353
389
|
|
|
354
390
|
(finally
|
|
355
|
-
;; Cleanup worktree
|
|
356
|
-
(process/sh ["git" "worktree" "remove" wt-
|
|
391
|
+
;; Cleanup worktree (in project root)
|
|
392
|
+
(process/sh ["git" "worktree" "remove" wt-dir "--force"] {:dir project-root})))))
|
|
357
393
|
|
|
358
394
|
;; =============================================================================
|
|
359
395
|
;; Worker Loop
|
|
360
396
|
;; =============================================================================
|
|
361
397
|
|
|
398
|
+
(def ^:private max-wait-for-tasks 60)
|
|
399
|
+
(def ^:private wait-poll-interval 5)
|
|
400
|
+
|
|
401
|
+
(defn- wait-for-tasks!
|
|
402
|
+
"Wait up to 60s for pending/current tasks to appear. Used for backpressure
|
|
403
|
+
on workers that can't create their own tasks (can_plan: false)."
|
|
404
|
+
[worker-id]
|
|
405
|
+
(loop [waited 0]
|
|
406
|
+
(cond
|
|
407
|
+
(pos? (tasks/pending-count)) true
|
|
408
|
+
(pos? (tasks/current-count)) true
|
|
409
|
+
(>= waited max-wait-for-tasks)
|
|
410
|
+
(do (println (format "[%s] No tasks after %ds, proceeding anyway" worker-id waited))
|
|
411
|
+
false)
|
|
412
|
+
:else
|
|
413
|
+
(do (when (zero? (mod waited 15))
|
|
414
|
+
(println (format "[%s] Waiting for tasks... (%ds)" worker-id waited)))
|
|
415
|
+
(Thread/sleep (* wait-poll-interval 1000))
|
|
416
|
+
(recur (+ waited wait-poll-interval))))))
|
|
417
|
+
|
|
362
418
|
(defn run-worker!
|
|
363
419
|
"Run worker loop until done or iterations exhausted.
|
|
364
420
|
|
|
@@ -372,6 +428,10 @@
|
|
|
372
428
|
(or (:model worker) "default")
|
|
373
429
|
iterations))
|
|
374
430
|
|
|
431
|
+
;; Backpressure: workers that can't create tasks wait for tasks to exist
|
|
432
|
+
(when-not (:can-plan worker)
|
|
433
|
+
(wait-for-tasks! id))
|
|
434
|
+
|
|
375
435
|
(loop [iter 1
|
|
376
436
|
completed 0]
|
|
377
437
|
(if (> iter iterations)
|
package/bin/oompa.js
CHANGED
|
@@ -18,7 +18,7 @@ if (!fs.existsSync(swarmScript) || !fs.existsSync(classpath)) {
|
|
|
18
18
|
const result = spawnSync("bb", ["--classpath", classpath, swarmScript, ...argv], {
|
|
19
19
|
stdio: "inherit",
|
|
20
20
|
cwd: process.cwd(),
|
|
21
|
-
env: process.env
|
|
21
|
+
env: { ...process.env, OOMPA_PACKAGE_ROOT: packageRoot }
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
if (result.error) {
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
## Task Management (auto-injected by oompa)
|
|
2
|
+
|
|
3
|
+
You are working in a git worktree. Your code changes go in `.` (current directory).
|
|
4
|
+
Tasks live in the project root at `../tasks/`. You can reach them from your worktree.
|
|
5
|
+
|
|
6
|
+
### See available tasks
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
ls ../tasks/pending/
|
|
10
|
+
cat ../tasks/pending/*.edn
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Claim a task (mark as in-progress)
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
mv ../tasks/pending/<task-file>.edn ../tasks/current/<task-file>.edn
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Complete a task
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
mv ../tasks/current/<task-file>.edn ../tasks/complete/<task-file>.edn
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Create a new task
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
cat > ../tasks/pending/task-NNN.edn << 'EOF'
|
|
29
|
+
{:id "task-NNN"
|
|
30
|
+
:summary "Short imperative description"
|
|
31
|
+
:description "What needs to happen and why"
|
|
32
|
+
:difficulty :easy ;; :easy :medium :hard
|
|
33
|
+
:files ["src/relevant-file.py"]
|
|
34
|
+
:acceptance ["Specific condition that means done"]}
|
|
35
|
+
EOF
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Rules
|
|
39
|
+
|
|
40
|
+
- Before starting work: read the project spec and all tasks to understand scope.
|
|
41
|
+
- First action: claim your task by moving it to `../tasks/current/`.
|
|
42
|
+
- If the `mv` fails (another worker claimed it first), pick a different task.
|
|
43
|
+
- One task per commit (or a small, tightly-related set with overlapping files).
|
|
44
|
+
- If tasks are missing or underspecified: stop and write tasks before coding.
|
|
45
|
+
- If work emerges during execution: create new tasks in `../tasks/pending/`.
|
|
46
|
+
- When all tasks are complete and the spec is satisfied, output: __DONE__
|
|
@@ -1,32 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
Process: Claim task (mv pending/ → current/), execute, complete (mv current/ → complete/)
|
|
3
|
-
Method: Isolate changes to your worktree, commit and merge when complete
|
|
1
|
+
You are an executor. You claim tasks and complete them. Nothing else.
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
- Do NOT plan or design
|
|
11
|
-
- Just execute what's assigned
|
|
12
|
-
|
|
13
|
-
## Workflow
|
|
14
|
-
|
|
15
|
-
1. Pick a task from tasks/pending/
|
|
16
|
-
2. Move it to tasks/current/
|
|
17
|
-
3. Execute the task in your worktree
|
|
18
|
-
4. Commit your changes
|
|
19
|
-
5. Move task to tasks/complete/
|
|
20
|
-
6. Merge your worktree to main
|
|
21
|
-
|
|
22
|
-
## Guidelines
|
|
23
|
-
|
|
24
|
-
- Focus on one task at a time
|
|
25
|
-
- Follow the task description exactly
|
|
26
|
-
- If a task is unclear, skip it (leave in pending/)
|
|
27
|
-
- Keep changes minimal and focused
|
|
28
|
-
|
|
29
|
-
## Exit Condition
|
|
30
|
-
|
|
31
|
-
When tasks/pending/ is empty:
|
|
32
|
-
Output: __DONE__
|
|
3
|
+
- Pick a task from pending, claim it, execute it, complete it.
|
|
4
|
+
- Follow the task description and acceptance criteria exactly.
|
|
5
|
+
- Do not create new tasks. Do not redesign. Do not refactor beyond the task scope.
|
|
6
|
+
- If a task is unclear, skip it (leave in pending/) and pick another.
|
|
7
|
+
- Keep changes minimal.
|
|
@@ -1,35 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
- Do NOT execute tasks yourself
|
|
13
|
-
|
|
14
|
-
## Task Creation
|
|
15
|
-
|
|
16
|
-
Write .edn files to tasks/pending/:
|
|
17
|
-
```edn
|
|
18
|
-
{:id "task-001"
|
|
19
|
-
:summary "Add user authentication"
|
|
20
|
-
:description "Implement JWT-based auth for the API"
|
|
21
|
-
:files ["src/auth.py" "tests/test_auth.py"]
|
|
22
|
-
:acceptance ["Login endpoint returns token" "Tests pass"]}
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## Guidelines
|
|
26
|
-
|
|
27
|
-
- Keep tasks small (1-2 files max)
|
|
28
|
-
- Be specific about acceptance criteria
|
|
29
|
-
- Consider dependencies between tasks
|
|
30
|
-
- Check what's already complete before creating duplicates
|
|
31
|
-
|
|
32
|
-
## Exit Condition
|
|
33
|
-
|
|
34
|
-
When spec.md is fully covered by tasks and all are complete:
|
|
35
|
-
Output: __DONE__
|
|
1
|
+
You are a planner. You read the spec, explore the codebase, and create well-scoped tasks.
|
|
2
|
+
|
|
3
|
+
- Read spec.md and understand the full goal before creating any tasks.
|
|
4
|
+
- Explore the existing code to understand what exists and what's missing.
|
|
5
|
+
- Create small, focused tasks with clear acceptance criteria.
|
|
6
|
+
- Set :difficulty on each task (:easy, :medium, :hard) so the right worker picks it up.
|
|
7
|
+
- Refine or split tasks that are too large or vague.
|
|
8
|
+
- Check ../tasks/complete/ before creating duplicates.
|
|
9
|
+
- Do NOT claim or complete tasks. Your job is to create them for other workers.
|
|
10
|
+
- Do NOT write application code. Only write task .edn files.
|
|
11
|
+
- Spend time thinking. Good task decomposition is the highest-leverage thing you can do.
|
package/config/prompts/worker.md
CHANGED
|
@@ -1,31 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
Process: Create/claim tasks in tasks/{pending,current,complete}/*.edn
|
|
3
|
-
Method: Isolate changes to your worktree, commit and merge when complete
|
|
1
|
+
You are a worker. You claim tasks, execute them, and complete them.
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
-
|
|
9
|
-
- To complete: move file from current/ to complete/
|
|
10
|
-
- To create: write new .edn file to pending/
|
|
11
|
-
|
|
12
|
-
Task file format:
|
|
13
|
-
```edn
|
|
14
|
-
{:id "task-001"
|
|
15
|
-
:summary "Short description"
|
|
16
|
-
:description "Detailed description"
|
|
17
|
-
:files ["src/foo.py"]}
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## Workflow
|
|
21
|
-
|
|
22
|
-
1. Check if tasks/pending/ has tasks
|
|
23
|
-
2. If yes: claim one (mv to current/), execute it, complete it (mv to complete/)
|
|
24
|
-
3. If no: check spec.md for gaps, create new tasks if needed
|
|
25
|
-
4. Commit changes to your worktree
|
|
26
|
-
5. Merge your worktree branch to main
|
|
27
|
-
|
|
28
|
-
## Exit Condition
|
|
29
|
-
|
|
30
|
-
When spec.md is fully satisfied and no more tasks are needed:
|
|
31
|
-
Output: __DONE__
|
|
3
|
+
- Pick tasks that match your strengths.
|
|
4
|
+
- If no tasks exist, read the spec and create them.
|
|
5
|
+
- Keep changes focused. Commit when a task is done.
|
|
6
|
+
- If a task is too big, split it into smaller tasks and work on one.
|
package/oompa.example.json
CHANGED
|
@@ -4,15 +4,16 @@
|
|
|
4
4
|
"workers": [
|
|
5
5
|
{
|
|
6
6
|
"model": "claude:opus-4.5",
|
|
7
|
+
"prompt": ["config/prompts/planner.md"],
|
|
7
8
|
"iterations": 5,
|
|
8
|
-
"count": 1
|
|
9
|
-
"prompt": "config/prompts/planner.md"
|
|
9
|
+
"count": 1
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
12
|
"model": "codex:codex-5.2-mini",
|
|
13
|
+
"prompt": ["config/prompts/executor.md"],
|
|
13
14
|
"iterations": 10,
|
|
14
15
|
"count": 3,
|
|
15
|
-
"
|
|
16
|
+
"can_plan": false
|
|
16
17
|
}
|
|
17
18
|
]
|
|
18
19
|
}
|