@nbardy/oompa 0.1.0 → 0.2.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 CHANGED
@@ -79,25 +79,50 @@ 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", "iterations": 5, "count": 1, "prompt": "config/prompts/planner.md"},
88
- {"model": "codex:codex-5.2-mini", "iterations": 10, "count": 3, "prompt": "config/prompts/executor.md"}
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}
89
89
  ]
90
90
  }
91
91
  ```
92
92
 
93
93
  This spawns:
94
- - **1 planner** (opus) — creates tasks, doesn't write code
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
+
107
+ #### Composable prompts
108
+
109
+ `prompt` accepts a string or an array. Arrays get concatenated, so you can reuse a shared base across workers:
110
+
111
+ ```json
112
+ {
113
+ "workers": [
114
+ {"model": "claude:opus-4.5", "prompt": ["prompts/base.md", "prompts/architect.md"], "count": 1},
115
+ {"model": "codex:codex-5.2-mini", "prompt": ["prompts/base.md", "prompts/frontend.md"], "count": 2},
116
+ {"model": "codex:codex-5.2-mini", "prompt": ["prompts/base.md", "prompts/backend.md"], "count": 2}
117
+ ]
118
+ }
119
+ ```
120
+
121
+ 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.
122
+
98
123
  ### Task System
99
124
 
100
- Workers self-organize via filesystem:
125
+ Workers self-organize via the filesystem. Tasks live at the project root and are shared across all worktrees:
101
126
 
102
127
  ```
103
128
  tasks/
@@ -106,20 +131,31 @@ tasks/
106
131
  └── complete/*.edn # done
107
132
  ```
108
133
 
109
- Workers can:
110
- - **Claim tasks**: `mv pending/task.edn current/`
111
- - **Complete tasks**: `mv current/task.edn complete/`
112
- - **Create tasks**: write new `.edn` to `pending/`
134
+ From inside a worktree, workers reach tasks via `../tasks/`:
135
+ - **See tasks**: `ls ../tasks/pending/`
136
+ - **Claim**: `mv ../tasks/pending/task.edn ../tasks/current/`
137
+ - **Complete**: `mv ../tasks/current/task.edn ../tasks/complete/`
138
+ - **Create**: write new `.edn` to `../tasks/pending/`
139
+
140
+ Task file format:
141
+ ```edn
142
+ {:id "task-001"
143
+ :summary "Add user authentication"
144
+ :description "Implement JWT-based auth for the API"
145
+ :difficulty :medium
146
+ :files ["src/auth.py" "tests/test_auth.py"]
147
+ :acceptance ["Login endpoint returns token" "Tests pass"]}
148
+ ```
113
149
 
114
- ### Prompts
150
+ ### Bundled Prompts
115
151
 
116
- Three built-in worker types:
152
+ Three prompt files ship with oompa that you can use in your `prompt` arrays:
117
153
 
118
- | Prompt | Role | Creates Tasks? | Executes Tasks? |
119
- |--------|------|----------------|-----------------|
120
- | `worker.md` | Hybrid | | |
121
- | `planner.md` | Planner | | |
122
- | `executor.md` | Executor | | |
154
+ | Prompt | Creates Tasks? | Executes Tasks? | Best For |
155
+ |--------|----------------|-----------------|----------|
156
+ | `config/prompts/worker.md` (default) | yes | yes | General purpose |
157
+ | `config/prompts/planner.md` | yes | sometimes | Big models — task design |
158
+ | `config/prompts/executor.md` | no | yes | Small/fast models — heads-down work |
123
159
 
124
160
  ---
125
161
 
@@ -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\": \"codex:codex-5.2\", \"iterations\": 5, \"count\": 1, \"prompt\": \"prompts/planner.md\"}")
303
+ (println " {\"model\": \"codex:codex-5.2-mini\", \"prompt\": \"prompts/executor.md\", \"iterations\": 10, \"count\": 3},")
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,7 @@
325
327
  :harness harness
326
328
  :model model
327
329
  :iterations (or (:iterations wc) 10)
328
- :custom-prompt (:prompt wc)
330
+ :prompts (:prompt wc)
329
331
  :review-harness (:harness review-model)
330
332
  :review-model (:model review-model)})))
331
333
  expanded-workers)]
@@ -93,15 +93,29 @@
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
- [{:keys [id swarm-id harness model iterations custom-prompt review-harness review-model]}]
107
+ "Create a worker config.
108
+ :prompts is a string or vector of strings — paths to prompt files."
109
+ [{:keys [id swarm-id harness model iterations prompts review-harness review-model]}]
99
110
  {:id id
100
111
  :swarm-id swarm-id
101
112
  :harness (or harness :codex)
102
113
  :model model
103
114
  :iterations (or iterations 10)
104
- :custom-prompt custom-prompt
115
+ :prompts (cond
116
+ (vector? prompts) prompts
117
+ (string? prompts) [prompts]
118
+ :else [])
105
119
  :review-harness review-harness
106
120
  :review-model review-model
107
121
  :completed 0
@@ -128,25 +142,36 @@
128
142
 
129
143
  (defn- run-agent!
130
144
  "Run agent with prompt, return {:output string, :done? bool, :exit int}"
131
- [{:keys [id swarm-id harness model custom-prompt]} worktree-path context]
132
- (let [;; Load prompt (check config/prompts/ as default)
133
- prompt-content (or (agent/load-custom-prompt custom-prompt)
134
- (agent/load-custom-prompt "config/prompts/worker.md")
135
- "Goal: Match spec.md\nProcess: Create/claim tasks in tasks/{pending,current,complete}/*.edn\nMethod: Isolate changes to your worktree, commit and merge when complete")
136
-
137
- ;; Inject worktree and context
138
- full-prompt (str "Worktree: " worktree-path "\n"
139
- "Task Status: " (:task_status context) "\n\n"
140
- prompt-content)
145
+ [{:keys [id swarm-id harness model prompts]} worktree-path context]
146
+ (let [;; 1. Task header (always, from package)
147
+ task-header (or (load-prompt "config/prompts/_task_header.md") "")
148
+
149
+ ;; 2. Load and concatenate all user prompts (string or list)
150
+ user-prompts (if (seq prompts)
151
+ (->> prompts
152
+ (map load-prompt)
153
+ (remove nil?)
154
+ (str/join "\n\n"))
155
+ (or (load-prompt "config/prompts/worker.md")
156
+ "You are a worker. Claim tasks, execute them, complete them."))
157
+
158
+ ;; Assemble: task header + status + user prompts
159
+ full-prompt (str task-header "\n"
160
+ "Task Status: " (:task_status context) "\n"
161
+ "Pending: " (:pending_tasks context) "\n\n"
162
+ user-prompts)
141
163
  session-id (str/lower-case (str (java.util.UUID/randomUUID)))
142
164
  swarm-id* (or swarm-id "unknown")
143
165
  tagged-prompt (str "[oompa:" swarm-id* ":" id "] " full-prompt)
144
166
  abs-worktree (.getAbsolutePath (io/file worktree-path))
145
167
 
146
- ;; Build command
168
+ ;; Build command — both harnesses run with cwd=worktree, no sandbox
169
+ ;; so agents can `..` to reach project root for task management
147
170
  cmd (case harness
148
- :codex (cond-> ["codex" "exec" "--full-auto" "--skip-git-repo-check"
149
- "-C" worktree-path "--sandbox" "workspace-write"]
171
+ :codex (cond-> ["codex" "exec"
172
+ "--dangerously-bypass-approvals-and-sandbox"
173
+ "--skip-git-repo-check"
174
+ "-C" abs-worktree]
150
175
  model (into ["--model" model])
151
176
  true (conj "--" full-prompt))
152
177
  :claude (cond-> ["claude" "-p" "--dangerously-skip-permissions"
@@ -156,13 +181,11 @@
156
181
  _ (when (= harness :codex)
157
182
  (persist-message! id session-id abs-worktree "user" tagged-prompt))
158
183
 
159
- ;; Run agent
184
+ ;; Run agent — both run with cwd=worktree
160
185
  result (try
161
186
  (if (= harness :claude)
162
- ;; Claude reads from stdin
163
- (process/sh cmd {:in tagged-prompt :out :string :err :string})
164
- ;; Codex takes prompt as arg
165
- (process/sh cmd {:out :string :err :string}))
187
+ (process/sh cmd {:dir abs-worktree :in tagged-prompt :out :string :err :string})
188
+ (process/sh cmd {:dir abs-worktree :out :string :err :string}))
166
189
  (catch Exception e
167
190
  {:exit -1 :out "" :err (.getMessage e)}))]
168
191
 
@@ -194,20 +217,24 @@
194
217
  "- NEEDS_CHANGES with bullet points of issues\n"
195
218
  "- REJECTED if fundamentally wrong")
196
219
 
197
- ;; Build command
220
+ abs-wt (.getAbsolutePath (io/file worktree-path))
221
+
222
+ ;; Build command — cwd=worktree, no sandbox
198
223
  cmd (case review-harness
199
- :codex (cond-> ["codex" "exec" "--full-auto" "--skip-git-repo-check"
200
- "-C" worktree-path "--sandbox" "workspace-write"]
224
+ :codex (cond-> ["codex" "exec"
225
+ "--dangerously-bypass-approvals-and-sandbox"
226
+ "--skip-git-repo-check"
227
+ "-C" abs-wt]
201
228
  review-model (into ["--model" review-model])
202
229
  true (conj "--" review-prompt))
203
230
  :claude (cond-> ["claude" "-p" "--dangerously-skip-permissions"]
204
231
  review-model (into ["--model" review-model])))
205
232
 
206
- ;; Run reviewer
233
+ ;; Run reviewer — cwd=worktree
207
234
  result (try
208
235
  (if (= review-harness :claude)
209
- (process/sh cmd {:in review-prompt :out :string :err :string})
210
- (process/sh cmd {:out :string :err :string}))
236
+ (process/sh cmd {:dir abs-wt :in review-prompt :out :string :err :string})
237
+ (process/sh cmd {:dir abs-wt :out :string :err :string}))
211
238
  (catch Exception e
212
239
  {:exit -1 :out "" :err (.getMessage e)}))
213
240
 
@@ -229,9 +256,13 @@
229
256
  feedback "\n\n"
230
257
  "Please fix these issues in the worktree.")
231
258
 
259
+ abs-wt (.getAbsolutePath (io/file worktree-path))
260
+
232
261
  cmd (case harness
233
- :codex (cond-> ["codex" "exec" "--full-auto" "--skip-git-repo-check"
234
- "-C" worktree-path "--sandbox" "workspace-write"]
262
+ :codex (cond-> ["codex" "exec"
263
+ "--dangerously-bypass-approvals-and-sandbox"
264
+ "--skip-git-repo-check"
265
+ "-C" abs-wt]
235
266
  model (into ["--model" model])
236
267
  true (conj "--" fix-prompt))
237
268
  :claude (cond-> ["claude" "-p" "--dangerously-skip-permissions"]
@@ -239,8 +270,8 @@
239
270
 
240
271
  result (try
241
272
  (if (= harness :claude)
242
- (process/sh cmd {:in fix-prompt :out :string :err :string})
243
- (process/sh cmd {:out :string :err :string}))
273
+ (process/sh cmd {:dir abs-wt :in fix-prompt :out :string :err :string})
274
+ (process/sh cmd {:dir abs-wt :out :string :err :string}))
244
275
  (catch Exception e
245
276
  {:exit -1 :out "" :err (.getMessage e)}))]
246
277
 
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
- Goal: Execute tasks from tasks/pending/
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
- ## Your Role
6
-
7
- You are an executor. You:
8
- - Claim and execute tasks
9
- - Do NOT create new tasks
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,10 @@
1
- Goal: Break spec.md into executable tasks
2
- Process: Create tasks in tasks/pending/*.edn
3
- Method: Do NOT write code. Only create and refine tasks.
4
-
5
- ## Your Role
6
-
7
- You are a planner. You:
8
- - Read spec.md to understand the goal
9
- - Create small, focused tasks in tasks/pending/
10
- - Monitor progress by checking tasks/complete/
11
- - Refine or split tasks that are too large
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
+ - You may also execute tasks if the queue is full and you see something quick to do.
10
+ - Spend time thinking. Good task decomposition is the highest-leverage thing you can do.
@@ -1,31 +1,6 @@
1
- Goal: Match spec.md
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
- ## Task Management
6
-
7
- Check tasks/pending/ for available work:
8
- - To claim: move file from pending/ to current/
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.
@@ -4,15 +4,15 @@
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
- "count": 3,
15
- "prompt": "config/prompts/executor.md"
15
+ "count": 3
16
16
  }
17
17
  ]
18
18
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nbardy/oompa",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Git-worktree multi-agent swarm orchestrator for Codex and Claude",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",