@monoes/monomindcli 1.9.14 → 1.9.16
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.
|
@@ -108,6 +108,8 @@ Complexity threshold for manager agent: any of these is true:
|
|
|
108
108
|
- Has external dependencies (APIs, services)
|
|
109
109
|
- Is estimated to take more than one conversation turn
|
|
110
110
|
|
|
111
|
+
**DOMAIN EXCEPTION — `idea`:** The `idea` domain MUST always be handled by the master invoking `Skill("mastermind:idea")` directly — NEVER by spawning a Task agent. Spawned agents do not have Skill tool access, so delegating `idea` to a Task agent silently degrades to raw analysis with no pipeline execution. After `mastermind:idea` returns, treat its output as the `idea` domain's unified output schema and proceed to the next domain.
|
|
112
|
+
|
|
111
113
|
**Per-domain goal extraction:** For each activated domain, extract a one-sentence goal from the prompt describing what that domain must accomplish. Then **run the following Bash block**, substituting `<domain_goals_json>` with a JSON object mapping each domain name to its one-sentence goal (use the full `resolved_prompt` as the value for any domain where no specific goal is extractable):
|
|
112
114
|
|
|
113
115
|
```bash
|
|
@@ -164,95 +166,87 @@ If mode = auto: proceed immediately.
|
|
|
164
166
|
|
|
165
167
|
Follow the Monotask Space+Board Setup Procedure from `_protocol.md`. Resolve the space **once**, then create one board per active domain. Use `project_name` as the space name so all boards across repos and domains share the same space.
|
|
166
168
|
|
|
167
|
-
|
|
168
|
-
# Require bash 4.3+ for associative arrays and namerefs (local -n introduced in 4.3)
|
|
169
|
-
# macOS ships bash 3.2; install via: brew install bash
|
|
170
|
-
(( BASH_VERSINFO[0] * 100 + BASH_VERSINFO[1] < 403 )) && \
|
|
171
|
-
{ echo "ERROR: bash 4.3+ required for namerefs (current: $BASH_VERSION). Install: brew install bash"; exit 1; }
|
|
169
|
+
**Board naming convention:** Every board is named `<project_name>-<domain>` (e.g. `factory-idea`, `factory-build`). This canonical name is stable across runs — mastermind finds the existing board instead of creating a new one every time.
|
|
172
170
|
|
|
171
|
+
```bash
|
|
172
|
+
# Compatible with macOS bash 3.2 — no associative arrays, uses jq accumulation instead
|
|
173
173
|
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
|
174
174
|
SESSION_STATE="$REPO_ROOT/.monomind/sessions/current.json"
|
|
175
175
|
|
|
176
|
-
# Reload persisted context (this is a fresh shell; Step 3 wrote these)
|
|
177
176
|
SESSION_ID=$(jq -r '.sessionId // empty' "$SESSION_STATE" 2>/dev/null)
|
|
178
177
|
[ -z "$SESSION_ID" ] && { echo "ERROR: SESSION_ID missing in current.json — run Step 3 first"; exit 1; }
|
|
179
178
|
project_name=$(jq -r '.project_name // ""' "$SESSION_STATE")
|
|
180
179
|
[ -z "$project_name" ] && { echo "ERROR: project_name is empty in current.json — run Step 3 first"; exit 1; }
|
|
181
180
|
resolved_prompt=$(jq -r '.prompt // ""' "$SESSION_STATE")
|
|
182
181
|
|
|
183
|
-
# domains_needed: NOT yet in current.json at this point — must be LLM-substituted inline.
|
|
184
182
|
# LLM: replace DOMAINS_LIST_HERE with space-separated domain names, e.g.: build marketing sales
|
|
185
|
-
|
|
183
|
+
domains_needed="DOMAINS_LIST_HERE"
|
|
184
|
+
[ "$domains_needed" = "DOMAINS_LIST_HERE" ] && { echo "ERROR: LLM did not substitute DOMAINS_LIST_HERE"; exit 1; }
|
|
185
|
+
[ -z "$domains_needed" ] && { echo "ERROR: domains_needed is empty — nothing to do"; exit 1; }
|
|
186
186
|
|
|
187
|
-
# Resolve space once
|
|
188
|
-
|
|
189
|
-
space_id=$(monotask space list 2>/dev/null | awk -F'|' '{gsub(/^ +| +$/,"",$2); if($2==n) gsub(/^ +| +$/,"",$1); if($2==n) print $1}' n="$project_name" | head -1)
|
|
187
|
+
# Resolve space once — find existing by exact name or create
|
|
188
|
+
space_id=$(monotask space list 2>/dev/null | awk -F'|' '{gsub(/^ +| +$/,"",$1);gsub(/^ +| +$/,"",$2);if($2==n)print $1}' n="$project_name" | head -1)
|
|
190
189
|
[ -z "$space_id" ] && space_id=$(monotask space create "$project_name" 2>/dev/null | grep -oE '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}')
|
|
191
190
|
[ -z "$space_id" ] && { echo "ERROR: Could not find or create space '$project_name'"; exit 1; }
|
|
191
|
+
echo "Space: $space_id ($project_name)"
|
|
192
192
|
|
|
193
|
-
#
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
# Hydrate domain_goals from Step 4's current.json write — prevents Step 4 extraction being clobbered
|
|
197
|
-
# Use NUL-delimited pairs (not @tsv) to safely handle backslashes, tabs, and other special chars in goals
|
|
198
|
-
while IFS= read -r -d '' k && IFS= read -r -d '' v; do
|
|
199
|
-
[[ -n "$k" ]] && domain_goals[$k]="$v"
|
|
200
|
-
done < <(jq -j '.domain_goals // {} | to_entries[] | (.key + "\u0000" + (.value // "") + "\u0000")' "$SESSION_STATE" 2>/dev/null)
|
|
193
|
+
# jq accumulation state (replaces bash 4.3+ associative arrays)
|
|
194
|
+
state_patch='{}'
|
|
201
195
|
|
|
202
|
-
# Loop over every active domain — LLM: replace DOMAINS_LIST_HERE with the resolved domain list
|
|
203
|
-
domains_needed="DOMAINS_LIST_HERE"
|
|
204
|
-
[ "$domains_needed" = "DOMAINS_LIST_HERE" ] && { echo "ERROR: LLM did not substitute DOMAINS_LIST_HERE with domain names"; exit 1; }
|
|
205
|
-
[ -z "$domains_needed" ] && { echo "ERROR: domains_needed is empty — nothing to do"; exit 1; }
|
|
206
196
|
for domain in $domains_needed; do
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
[ -
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
197
|
+
canonical="${project_name}-${domain}"
|
|
198
|
+
|
|
199
|
+
# Find existing board by canonical name — reuse across runs
|
|
200
|
+
board_id=$(monotask board list 2>/dev/null | awk -F'|' '{gsub(/^ +| +$/,"",$1);gsub(/^ +| +$/,"",$2);if($2==n)print $1}' n="$canonical" | head -1)
|
|
201
|
+
|
|
202
|
+
if [ -n "$board_id" ]; then
|
|
203
|
+
echo "Reusing board: $board_id ($canonical)"
|
|
204
|
+
# Fetch existing column IDs
|
|
205
|
+
cols_json=$(monotask column list "$board_id" --json 2>/dev/null || echo '[]')
|
|
206
|
+
todo_col=$(echo "$cols_json" | jq -r '[.[] | select(.title=="Todo" or .title=="Backlog")] | .[0].id // empty')
|
|
207
|
+
doing_col=$(echo "$cols_json" | jq -r '[.[] | select(.title=="Doing" or .title=="In Progress")] | .[0].id // empty')
|
|
208
|
+
done_col=$(echo "$cols_json" | jq -r '[.[] | select(.title=="Done")] | .[0].id // empty')
|
|
209
|
+
else
|
|
210
|
+
echo "Creating board: $canonical"
|
|
211
|
+
board_id=$(monotask board create --space "$space_id" "$canonical" --json 2>/dev/null | jq -r '.id // empty')
|
|
212
|
+
[ -z "$board_id" ] && { echo "ERROR: Failed to create board '$canonical'"; exit 1; }
|
|
213
|
+
monotask space boards add "$space_id" "$board_id" >/dev/null 2>&1 || true
|
|
214
|
+
todo_col=$(monotask column create "$board_id" "Todo" --json | jq -r '.id // empty')
|
|
215
|
+
[ -z "$todo_col" ] && { echo "ERROR: Failed to create Todo column for $domain"; exit 1; }
|
|
216
|
+
doing_col=$(monotask column create "$board_id" "Doing" --json | jq -r '.id // empty')
|
|
217
|
+
[ -z "$doing_col" ] && { echo "ERROR: Failed to create Doing column for $domain"; exit 1; }
|
|
218
|
+
done_col=$(monotask column create "$board_id" "Done" --json | jq -r '.id // empty')
|
|
219
|
+
[ -z "$done_col" ] && { echo "ERROR: Failed to create Done column for $domain"; exit 1; }
|
|
220
|
+
fi
|
|
224
221
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
_to_json_map() {
|
|
228
|
-
local -n _arr=$1
|
|
229
|
-
for k in "${!_arr[@]}"; do
|
|
230
|
-
jq -n --arg k "$k" --arg v "${_arr[$k]}" '{key:$k,value:$v}'
|
|
231
|
-
done | jq -s 'from_entries // {}'
|
|
232
|
-
}
|
|
233
|
-
domains_goals_json=$(_to_json_map domain_goals)
|
|
234
|
-
board_ids_json=$(_to_json_map board_ids)
|
|
235
|
-
todo_cols_json=$(_to_json_map todo_cols)
|
|
236
|
-
doing_cols_json=$(_to_json_map doing_cols)
|
|
237
|
-
done_cols_json=$(_to_json_map done_cols)
|
|
222
|
+
domain_goal=$(jq -r --arg d "$domain" '.domain_goals[$d] // empty' "$SESSION_STATE")
|
|
223
|
+
[ -z "$domain_goal" ] && domain_goal="$resolved_prompt"
|
|
238
224
|
|
|
225
|
+
state_patch=$(echo "$state_patch" | jq \
|
|
226
|
+
--arg d "$domain" --arg b "$board_id" \
|
|
227
|
+
--arg t "$todo_col" --arg g "$doing_col" --arg e "$done_col" \
|
|
228
|
+
--arg goal "$domain_goal" \
|
|
229
|
+
'.board_ids[$d]=$b | .todo_cols[$d]=$t | .doing_cols[$d]=$g | .done_cols[$d]=$e | .domain_goals[$d]=$goal')
|
|
230
|
+
|
|
231
|
+
echo "DOMAIN=$domain BOARD=$board_id TODO=$todo_col DOING=$doing_col DONE=$done_col"
|
|
232
|
+
done
|
|
233
|
+
|
|
234
|
+
# Persist to current.json — one atomic merge
|
|
239
235
|
jq --arg domains "$domains_needed" \
|
|
240
|
-
--argjson
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
--argjson dones "$done_cols_json" \
|
|
245
|
-
'. + {domains_needed:($domains | split(" ") | map(select(length>0))),
|
|
246
|
-
domain_goals:$goals, board_ids:$boards,
|
|
247
|
-
todo_cols:$todos, doing_cols:$doings, done_cols:$dones}' \
|
|
248
|
-
"$REPO_ROOT/.monomind/sessions/current.json" > "$REPO_ROOT/.monomind/sessions/current.json.tmp" \
|
|
249
|
-
&& mv "$REPO_ROOT/.monomind/sessions/current.json.tmp" "$REPO_ROOT/.monomind/sessions/current.json"
|
|
236
|
+
--argjson patch "$state_patch" \
|
|
237
|
+
'. + $patch + {domains_needed:($domains | split(" ") | map(select(length>0)))}' \
|
|
238
|
+
"$SESSION_STATE" > "$SESSION_STATE.tmp" && mv "$SESSION_STATE.tmp" "$SESSION_STATE"
|
|
239
|
+
echo "Session state saved to current.json"
|
|
250
240
|
```
|
|
251
241
|
|
|
252
242
|
### Step 7 — Spawn Domain Managers
|
|
253
243
|
|
|
244
|
+
**BEFORE THIS STEP:** If `idea` is in `domains_needed`, invoke `Skill("mastermind:idea")` directly now (master context has Skill tool access). Pass the resolved prompt, project path, and mode. Write the result to `.monomind/sessions/<SESSION_ID>/idea.json` and mark the `idea` domain as handled. Do NOT include `idea` in the Task spawning below.
|
|
245
|
+
|
|
254
246
|
**Before spawning**, select the best domain manager agent type from the registry for each active domain. Do not hardcode `coordinator` — pick the agent whose expertise best fits the domain goal.
|
|
255
247
|
|
|
248
|
+
**BASH TOOL REQUIREMENT:** Domain managers must run `monotask` CLI commands. Only use subagent_types that include Bash in their tool list. If the registry returns an agent without Bash (e.g. `Product Manager`, `Backend Architect`), override it with `general-purpose` (which has all tools). Agents without Bash cannot create cards, emit curl events, or write session files — they will silently produce degraded output.
|
|
249
|
+
|
|
256
250
|
**Phase A — Registry selection** (run as one Bash call; must complete before Phase C):
|
|
257
251
|
|
|
258
252
|
```bash
|
|
@@ -15,18 +15,18 @@ This file is a reference loaded by mastermind domain skills and master. It is NE
|
|
|
15
15
|
Execute at the START of every mastermind run (master or standalone domain command). Load in this order:
|
|
16
16
|
|
|
17
17
|
**Step A — Tier 3 core principles (all domains):**
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
Try `mcp__monobrain__agentdb_hierarchical-recall` with query `"mastermind principles"`, topK 20.
|
|
19
|
+
If it returns `"AgentDB bridge not available"` or any error, fall back to:
|
|
20
|
+
`mcp__monobrain__memory_search` with query `"mastermind principles"`, namespace `"mastermind:principles"`, limit 20.
|
|
21
21
|
|
|
22
22
|
**Step B — Tier 2 weekly summary for this domain:**
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
Try `mcp__monobrain__agentdb_context-synthesize` with query `[current prompt keywords]`, maxEntries 10.
|
|
24
|
+
If it fails, fall back to:
|
|
25
|
+
`mcp__monobrain__memory_search` with query `[current prompt keywords]`, namespace `"mastermind:<domain>:weekly"`, limit 10.
|
|
26
26
|
|
|
27
27
|
**Step C — Relevant graph nodes:**
|
|
28
|
-
Call `
|
|
29
|
-
|
|
28
|
+
Call `mcp__monobrain__graphify_query` with question `[3-5 keywords extracted from current prompt]`, depth 2.
|
|
29
|
+
If the graph is not built yet (error: "No graph found"), skip this tier — continue without graph context.
|
|
30
30
|
|
|
31
31
|
Combine all results into a **BRAIN CONTEXT** block. Insert this block before any planning, decomposition, or agent spawning step. Format:
|
|
32
32
|
|
|
@@ -55,13 +55,20 @@ score = confidence × (1 / (days_since_run + 1)) × log(uses + 1)
|
|
|
55
55
|
- `uses`: 1 (first write)
|
|
56
56
|
|
|
57
57
|
**Step 2 — Append to Tier 1 raw log:**
|
|
58
|
-
|
|
58
|
+
Try `mcp__monobrain__agentdb_hierarchical-store` with:
|
|
59
59
|
- namespace: `mastermind:<domain>:raw`
|
|
60
60
|
- content: [full unified output schema YAML from this run, as a string]
|
|
61
61
|
- metadata: `{ score, project, run_id, date: ISO8601, domain }`
|
|
62
62
|
|
|
63
|
+
If AgentDB is unavailable, fall back to `mcp__monobrain__memory_store`:
|
|
64
|
+
- key: `mastermind:<domain>:run:<run_id>`
|
|
65
|
+
- value: [JSON-encoded unified output schema]
|
|
66
|
+
- namespace: `mastermind:<domain>:raw`
|
|
67
|
+
- tags: `["mastermind", "<domain>", "run"]`
|
|
68
|
+
|
|
63
69
|
**Step 3 — Check weekly compaction trigger:**
|
|
64
|
-
|
|
70
|
+
Try `mcp__monobrain__agentdb_health` on namespace `mastermind:<domain>:raw`.
|
|
71
|
+
If unavailable, call `mcp__monobrain__memory_stats` and check entry count manually.
|
|
65
72
|
If `entry_count >= 20` OR `days_since_last_compaction >= 7`:
|
|
66
73
|
1. Retrieve all Tier 1 entries since last compaction
|
|
67
74
|
2. Produce a per-domain weekly summary (use LLM synthesis: "Summarize the key decisions, patterns, and lessons from these run logs in under 300 words")
|
|
@@ -201,25 +208,25 @@ Every mastermind run MUST emit structured events to the live dashboard via WebFe
|
|
|
201
208
|
}
|
|
202
209
|
```
|
|
203
210
|
|
|
204
|
-
### How to Emit (WebFetch
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
session:
|
|
214
|
-
prompt
|
|
215
|
-
mode
|
|
216
|
-
|
|
217
|
-
ts:
|
|
218
|
-
})
|
|
219
|
-
})
|
|
211
|
+
### How to Emit (curl-first — WebFetch is blocked for localhost in Claude Code runtimes)
|
|
212
|
+
|
|
213
|
+
**Always use curl via Bash.** WebFetch is restricted for `localhost` URLs in Claude Code agent runtimes and will return ECONNREFUSED even when the server is running. Use this pattern:
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
curl -s -o /dev/null -X POST "http://localhost:4242/api/mastermind/event" \
|
|
217
|
+
-H "Content-Type: application/json" \
|
|
218
|
+
-d "$(jq -cn \
|
|
219
|
+
--arg sid "$SESSION_ID" \
|
|
220
|
+
--arg type "session:start" \
|
|
221
|
+
--arg prompt "$resolved_prompt" \
|
|
222
|
+
--arg mode "$mode" \
|
|
223
|
+
--arg proj "$(pwd)" \
|
|
224
|
+
'{type:$type,session:$sid,prompt:$prompt,mode:$mode,project:$proj,ts:(now*1000|floor)}')" || true
|
|
220
225
|
```
|
|
221
226
|
|
|
222
|
-
**
|
|
227
|
+
**Always append `|| true`** — event emission is non-blocking and MUST NOT abort the run.
|
|
228
|
+
|
|
229
|
+
**If Bash is unavailable** (e.g. the agent type has no Bash tool): skip dashboard events entirely. They are observability-only and do not affect pipeline correctness. The master context always has Bash and emits session:start, domain:dispatch, and session:complete on behalf of the run.
|
|
223
230
|
|
|
224
231
|
**Session ID:** Generate once at session:start and reuse across all subsequent events for this run. A simple ID format: `mm-<ISO8601-compact>` (e.g. `mm-20260505T142300`).
|
|
225
232
|
|
|
@@ -240,53 +247,78 @@ WebFetch({
|
|
|
240
247
|
|
|
241
248
|
**Always follow this exact order — never create a board without first ensuring a space exists.**
|
|
242
249
|
|
|
250
|
+
**Board naming convention:** Boards are named `<project_name>-<domain>` (e.g. `factory-idea`, `factory-build`). This canonical name is stable across runs — find the existing board first, create only if it does not exist.
|
|
251
|
+
|
|
243
252
|
Every mastermind run that needs a task board MUST:
|
|
244
253
|
1. Resolve the space (find existing or create new) — space name = `project_name`
|
|
245
|
-
2.
|
|
246
|
-
3.
|
|
247
|
-
4. Create
|
|
248
|
-
5. Create cards within those columns
|
|
254
|
+
2. Find existing board by canonical name `<project_name>-<domain>` or create it with `--space`
|
|
255
|
+
3. Fetch column IDs from existing board OR create columns for new board
|
|
256
|
+
4. Create cards within those columns
|
|
249
257
|
|
|
250
258
|
If the user is running across multiple repos for the same project, they MUST use the same `project_name` so all boards land in one space.
|
|
251
259
|
|
|
252
260
|
### Canonical bash block (substitute `<domain>` with: build, marketing, ops, content, etc.)
|
|
253
261
|
|
|
254
262
|
```bash
|
|
255
|
-
#
|
|
263
|
+
# Compatible with macOS bash 3.2
|
|
256
264
|
project_name="${project_name:-$(basename "$PWD")}"
|
|
257
|
-
|
|
258
|
-
[ -z "$space_id" ] && space_id=$(monotask space create "$project_name" 2>&1 | grep -oE '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}')
|
|
259
|
-
[ -z "$space_id" ] && { echo "ERROR: Could not find or create space '$project_name' — verify monotask is installed (monotask --version)"; exit 1; }
|
|
265
|
+
canonical="${project_name}-<domain>"
|
|
260
266
|
|
|
261
|
-
# Step
|
|
262
|
-
|
|
263
|
-
[ -z "$
|
|
264
|
-
|
|
265
|
-
# Step 3 — Link board to space immediately
|
|
266
|
-
monotask space boards add "$space_id" "$board_id" >/dev/null 2>&1 || true
|
|
267
|
+
# Step 1 — Resolve space
|
|
268
|
+
space_id=$(monotask space list 2>/dev/null | awk -F'|' '{gsub(/^ +| +$/,"",$1);gsub(/^ +| +$/,"",$2);if($2==n)print $1}' n="$project_name" | head -1)
|
|
269
|
+
[ -z "$space_id" ] && space_id=$(monotask space create "$project_name" 2>/dev/null | grep -oE '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}')
|
|
270
|
+
[ -z "$space_id" ] && { echo "ERROR: Could not find or create space '$project_name' — verify monotask is installed (monotask --version)"; exit 1; }
|
|
267
271
|
|
|
268
|
-
# Step
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
+
# Step 2 — Find existing board by canonical name or create
|
|
273
|
+
board_id=$(monotask board list 2>/dev/null | awk -F'|' '{gsub(/^ +| +$/,"",$1);gsub(/^ +| +$/,"",$2);if($2==n)print $1}' n="$canonical" | head -1)
|
|
274
|
+
if [ -n "$board_id" ]; then
|
|
275
|
+
# Step 3a — Fetch column IDs from existing board
|
|
276
|
+
cols_json=$(monotask column list "$board_id" --json 2>/dev/null || echo '[]')
|
|
277
|
+
todo_col=$(echo "$cols_json" | jq -r '[.[] | select(.title=="Todo" or .title=="Backlog")] | .[0].id // empty')
|
|
278
|
+
doing_col=$(echo "$cols_json" | jq -r '[.[] | select(.title=="Doing" or .title=="In Progress")] | .[0].id // empty')
|
|
279
|
+
done_col=$(echo "$cols_json" | jq -r '[.[] | select(.title=="Done")] | .[0].id // empty')
|
|
280
|
+
else
|
|
281
|
+
# Step 3b — Create board and columns
|
|
282
|
+
board_id=$(monotask board create --space "$space_id" "$canonical" --json | jq -r '.id // empty')
|
|
283
|
+
[ -z "$board_id" ] && { echo "ERROR: Failed to create board '$canonical'"; exit 1; }
|
|
284
|
+
monotask space boards add "$space_id" "$board_id" >/dev/null 2>&1 || true
|
|
285
|
+
todo_col=$(monotask column create "$board_id" "Todo" --json | jq -r '.id')
|
|
286
|
+
doing_col=$(monotask column create "$board_id" "Doing" --json | jq -r '.id')
|
|
287
|
+
done_col=$(monotask column create "$board_id" "Done" --json | jq -r '.id')
|
|
288
|
+
fi
|
|
272
289
|
```
|
|
273
290
|
|
|
274
|
-
When master.md runs multiple domains, resolve the space **once** before the loop, then repeat steps 2–
|
|
291
|
+
When master.md runs multiple domains, resolve the space **once** before the loop, then repeat steps 2–3 per domain using jq accumulation (no bash 4.3+ needed):
|
|
275
292
|
|
|
276
293
|
```bash
|
|
277
|
-
#
|
|
294
|
+
# Compatible with macOS bash 3.2 — jq accumulation instead of declare -A
|
|
278
295
|
project_name="<resolved project_name>"
|
|
279
|
-
space_id=$(monotask space list 2>/dev/null | awk -F'
|
|
280
|
-
[ -z "$space_id" ] && space_id=$(monotask space create "$project_name" 2
|
|
296
|
+
space_id=$(monotask space list 2>/dev/null | awk -F'|' '{gsub(/^ +| +$/,"",$1);gsub(/^ +| +$/,"",$2);if($2==n)print $1}' n="$project_name" | head -1)
|
|
297
|
+
[ -z "$space_id" ] && space_id=$(monotask space create "$project_name" 2>/dev/null | grep -oE '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}')
|
|
281
298
|
[ -z "$space_id" ] && { echo "ERROR: Could not find or create space '$project_name'"; exit 1; }
|
|
282
299
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
monotask
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
300
|
+
state_patch='{}'
|
|
301
|
+
for domain in build marketing ops; do # substitute actual domain list
|
|
302
|
+
canonical="${project_name}-${domain}"
|
|
303
|
+
board_id=$(monotask board list 2>/dev/null | awk -F'|' '{gsub(/^ +| +$/,"",$1);gsub(/^ +| +$/,"",$2);if($2==n)print $1}' n="$canonical" | head -1)
|
|
304
|
+
if [ -n "$board_id" ]; then
|
|
305
|
+
cols_json=$(monotask column list "$board_id" --json 2>/dev/null || echo '[]')
|
|
306
|
+
todo_col=$(echo "$cols_json" | jq -r '[.[] | select(.title=="Todo" or .title=="Backlog")] | .[0].id // empty')
|
|
307
|
+
doing_col=$(echo "$cols_json" | jq -r '[.[] | select(.title=="Doing" or .title=="In Progress")] | .[0].id // empty')
|
|
308
|
+
done_col=$(echo "$cols_json" | jq -r '[.[] | select(.title=="Done")] | .[0].id // empty')
|
|
309
|
+
else
|
|
310
|
+
board_id=$(monotask board create --space "$space_id" "$canonical" --json | jq -r '.id // empty')
|
|
311
|
+
[ -z "$board_id" ] && { echo "ERROR: Failed to create $canonical board"; exit 1; }
|
|
312
|
+
monotask space boards add "$space_id" "$board_id" >/dev/null 2>&1 || true
|
|
313
|
+
todo_col=$(monotask column create "$board_id" "Todo" --json | jq -r '.id')
|
|
314
|
+
doing_col=$(monotask column create "$board_id" "Doing" --json | jq -r '.id')
|
|
315
|
+
done_col=$(monotask column create "$board_id" "Done" --json | jq -r '.id')
|
|
316
|
+
fi
|
|
317
|
+
state_patch=$(echo "$state_patch" | jq \
|
|
318
|
+
--arg d "$domain" --arg b "$board_id" \
|
|
319
|
+
--arg t "$todo_col" --arg g "$doing_col" --arg e "$done_col" \
|
|
320
|
+
'.board_ids[$d]=$b | .todo_cols[$d]=$t | .doing_cols[$d]=$g | .done_cols[$d]=$e')
|
|
321
|
+
done
|
|
290
322
|
```
|
|
291
323
|
|
|
292
324
|
---
|
|
@@ -105,6 +105,12 @@ COL_ELABORATED=$(echo "$columns"| jq -r '.[] | select(.title == "Elaborated") |
|
|
|
105
105
|
COL_TASKED=$(echo "$columns" | jq -r '.[] | select(.title == "Tasked") | .id' | head -1)
|
|
106
106
|
COL_ICED=$(echo "$columns" | jq -r '.[] | select(.title == "Iced") | .id' | head -1)
|
|
107
107
|
COL_REJECTED=$(echo "$columns" | jq -r '.[] | select(.title == "Rejected") | .id' | head -1)
|
|
108
|
+
[ -z "$COL_NEW" ] && { echo "ERROR: Could not find 'New' column on BOARD_ID=$BOARD_ID"; exit 1; }
|
|
109
|
+
[ -z "$COL_EVALUATED" ] && { echo "ERROR: Could not find 'Evaluated' column on BOARD_ID=$BOARD_ID"; exit 1; }
|
|
110
|
+
[ -z "$COL_ELABORATED" ] && { echo "ERROR: Could not find 'Elaborated' column on BOARD_ID=$BOARD_ID"; exit 1; }
|
|
111
|
+
[ -z "$COL_TASKED" ] && { echo "ERROR: Could not find 'Tasked' column on BOARD_ID=$BOARD_ID"; exit 1; }
|
|
112
|
+
[ -z "$COL_ICED" ] && { echo "ERROR: Could not find 'Iced' column on BOARD_ID=$BOARD_ID"; exit 1; }
|
|
113
|
+
[ -z "$COL_REJECTED" ] && { echo "ERROR: Could not find 'Rejected' column on BOARD_ID=$BOARD_ID"; exit 1; }
|
|
108
114
|
```
|
|
109
115
|
|
|
110
116
|
**After either branch above, validate and echo all values.** This is the canonical source for Step 4 Task construction.
|
|
@@ -173,7 +179,7 @@ echo "Selected specialists: $specialist_list"
|
|
|
173
179
|
```
|
|
174
180
|
|
|
175
181
|
**CRITICAL — Variable substitution required before constructing the Task call:**
|
|
176
|
-
The Task agent runs in an isolated context and cannot inherit shell variables. Before writing the Task prompt below, read the literal UUID values from the `=== IDEA BOARD LITERAL VALUES ===` echo block above and embed them as **hard-coded strings** in the prompt. Do NOT write `${BOARD_ID}`, `${COL_NEW}`, etc. in the prompt — the agent will receive those as literal dollar-sign strings and its `monotask card create` calls will fail, causing it to improvise with `monotask board create` and corrupt board names. Replace every `${BOARD_ID}`, `${COL_NEW}`, `${project_name}`, `${brain_context}`, `${prompt}`, `${date}`, and `${specialist_list}` with the actual value before calling Task.
|
|
182
|
+
The Task agent runs in an isolated context and cannot inherit shell variables. Before writing the Task prompt below, read the literal UUID values from the `=== IDEA BOARD LITERAL VALUES ===` echo block above and embed them as **hard-coded strings** in the prompt. Do NOT write `${BOARD_ID}`, `${COL_NEW}`, etc. in the prompt — the agent will receive those as literal dollar-sign strings and its `monotask card create` calls will fail, causing it to improvise with `monotask board create` and corrupt board names. Replace every `${BOARD_ID}`, `${COL_NEW}`, `${project_name}`, `${brain_context}`, `${prompt}`, `${date}`, and `${specialist_list}` with the actual value before calling Task. Leave `$result`, `$CARD_ID`, and other loop-internal variables as-is — they are bash variables the agent itself will set at runtime, not substitution targets.
|
|
177
183
|
|
|
178
184
|
Spawn the Idea Manager with `run_in_background: false` so its output is available for Step 5.
|
|
179
185
|
|
|
@@ -242,8 +248,8 @@ For each unique idea, create one card in the New column (COL_NEW = ${COL_NEW}):
|
|
|
242
248
|
|
|
243
249
|
result=$(monotask card create "${BOARD_ID}" "${COL_NEW}" "<idea title ≤80 chars>" --json)
|
|
244
250
|
CARD_ID=$(echo "$result" | jq -r '.id // empty')
|
|
245
|
-
monotask card
|
|
246
|
-
CATEGORY: <feature | technical-baseline | business-operation>
|
|
251
|
+
monotask card set-description "${BOARD_ID}" "$CARD_ID" "<2-3 sentence description>"
|
|
252
|
+
monotask card comment add "${BOARD_ID}" "$CARD_ID" "CATEGORY: <feature | technical-baseline | business-operation>
|
|
247
253
|
SOURCE: <which specialist angle produced this>"
|
|
248
254
|
monotask card label add "${BOARD_ID}" "$CARD_ID" "mastermind-idea"
|
|
249
255
|
monotask card label add "${BOARD_ID}" "$CARD_ID" "category:<category>"
|
|
@@ -264,20 +270,29 @@ END_IDEAS_OUTPUT`
|
|
|
264
270
|
})
|
|
265
271
|
```
|
|
266
272
|
|
|
267
|
-
Parse the `IDEAS_OUTPUT` JSON block from the agent's response
|
|
273
|
+
Parse the `IDEAS_OUTPUT` JSON block from the agent's response and assign it:
|
|
274
|
+
```bash
|
|
275
|
+
ideas_output_json='<paste the JSON array from IDEAS_OUTPUT here>'
|
|
276
|
+
```
|
|
277
|
+
If zero ideas were returned, report "Idea Manager produced no ideas." and STOP.
|
|
268
278
|
|
|
269
279
|
---
|
|
270
280
|
|
|
271
281
|
### Step 5 — Validation (Product Manager Evaluation)
|
|
272
282
|
|
|
283
|
+
**Build `ideas_list` before constructing the Step 5 Task prompt:**
|
|
284
|
+
```bash
|
|
285
|
+
# Format the full IDEAS_OUTPUT array (from Step 4) as the literal string to embed in the Task prompt.
|
|
286
|
+
# Each line: card_id | title | description | category | source_angle
|
|
287
|
+
ideas_list=$(echo "$ideas_output_json" | jq -r \
|
|
288
|
+
'.[] | "- card_id: \(.card_id)\n title: \(.title)\n description: \(.description)\n category: \(.category)\n source: \(.source_angle)\n"')
|
|
289
|
+
```
|
|
290
|
+
|
|
273
291
|
**CRITICAL — Variable substitution required for Step 5 Task call:**
|
|
274
|
-
|
|
292
|
+
Before constructing the Task prompt below, read the literal UUID values from the `=== IDEA BOARD LITERAL VALUES ===` echo block (Step 3) and embed them as hard-coded strings. Also embed the full `brain_context`, `prompt`, and `ideas_list` (built above) as literal text. Replace every `${BOARD_ID}`, `${COL_EVALUATED}`, `${COL_ICED}`, `${COL_REJECTED}`, `${brain_context}`, `${prompt}`, `${project_name}`, `${date}`, and `${ideas_list}` with its actual value before calling Task — the agent receives unsubstituted `${...}` strings verbatim and silently skips every board update.
|
|
275
293
|
|
|
276
294
|
Spawn a single `Product Manager` agent via the Task tool. The PM agent has Bash tool access and is responsible for both producing verdicts and executing all board updates directly.
|
|
277
295
|
|
|
278
|
-
**CRITICAL — Variable substitution required for Step 5 Task call (same rule as Step 4):**
|
|
279
|
-
Before constructing the Task prompt below, read the literal UUID values from the `=== IDEA BOARD LITERAL VALUES ===` echo block (Step 3) and embed them as hard-coded strings. Also embed the full `brain_context`, `prompt`, and the entire ideas list (with card IDs) as literal text. Do NOT write `${BOARD_ID}`, `${COL_EVALUATED}`, `${brain_context}`, etc. — the agent receives them as literal dollar-sign strings and silently skips every board update.
|
|
280
|
-
|
|
281
296
|
```javascript
|
|
282
297
|
Task({
|
|
283
298
|
subagent_type: "Product Manager",
|
|
@@ -347,7 +362,11 @@ END_VERDICTS_OUTPUT`
|
|
|
347
362
|
})
|
|
348
363
|
```
|
|
349
364
|
|
|
350
|
-
After the PM agent completes, parse the `VERDICTS_OUTPUT` block from the agent's response
|
|
365
|
+
After the PM agent completes, parse the `VERDICTS_OUTPUT` block from the agent's response and assign it:
|
|
366
|
+
```bash
|
|
367
|
+
verdicts_output_json='<paste the JSON array from VERDICTS_OUTPUT here>'
|
|
368
|
+
```
|
|
369
|
+
This variable is used throughout Steps 6a and 6c to build idea lists, registry keywords, and inherit impact/effort scores — it must be set before proceeding. If **all** ideas are iced or rejected, output a summary table and STOP — skip Steps 6–7.
|
|
351
370
|
|
|
352
371
|
---
|
|
353
372
|
|
|
@@ -355,16 +374,29 @@ After the PM agent completes, parse the `VERDICTS_OUTPUT` block from the agent's
|
|
|
355
374
|
|
|
356
375
|
#### 6a. Elaboration (conditional)
|
|
357
376
|
|
|
358
|
-
For any evaluated idea with `skipElaboration: true`, move it directly to `Elaborated
|
|
377
|
+
For any evaluated idea with `skipElaboration: true`, move it directly to `Elaborated`, set a description, and write a rationale comment so future readers understand why no deep elaboration was needed:
|
|
359
378
|
```bash
|
|
379
|
+
monotask card set-description "$BOARD_ID" "$CARD_ID" "Elaboration skipped — PM assessed this as straightforward.\n\nRationale: <rationale from VERDICTS_OUTPUT for this card_id>\n\nImpact: <impact>/10 | Effort: <effort>/10"
|
|
360
380
|
monotask card move "$BOARD_ID" "$CARD_ID" "$COL_ELABORATED" --json
|
|
361
381
|
monotask card comment add "$BOARD_ID" "$CARD_ID" "Elaboration skipped: PM assessed this idea as straightforward with no significant unknowns. Rationale: <rationale from VERDICTS_OUTPUT for this card_id>"
|
|
362
382
|
```
|
|
363
383
|
|
|
364
384
|
For ideas with `skipElaboration: false`, **split by category** before spawning agents:
|
|
365
385
|
|
|
386
|
+
**Build `dev_ideas_list` and `ops_ideas_list` before constructing the Step 6a Task prompts:**
|
|
387
|
+
```bash
|
|
388
|
+
# Filter VERDICTS_OUTPUT (parsed in Step 5) by category and format as literal text.
|
|
389
|
+
dev_ideas_list=$(echo "$verdicts_output_json" | jq -r \
|
|
390
|
+
'.[] | select(.verdict == "evaluated") | select(.category == "feature" or .category == "technical-baseline") |
|
|
391
|
+
"- card_id: \(.card_id)\n title: \(.title)\n category: \(.category)\n rationale: \(.rationale)\n"')
|
|
392
|
+
|
|
393
|
+
ops_ideas_list=$(echo "$verdicts_output_json" | jq -r \
|
|
394
|
+
'.[] | select(.verdict == "evaluated") | select(.category == "business-operation") |
|
|
395
|
+
"- card_id: \(.card_id)\n title: \(.title)\n category: \(.category)\n rationale: \(.rationale)\n"')
|
|
396
|
+
```
|
|
397
|
+
|
|
366
398
|
**CRITICAL — Variable substitution required for Step 6a Task calls:**
|
|
367
|
-
Elaboration agents run in isolated Task contexts. Before constructing each Task prompt, replace every `${brain_context}`, `${dev_ideas_list}`, and `${ops_ideas_list}` with the actual literal text — `brain_context` from the brain load, `dev_ideas_list` and `ops_ideas_list`
|
|
399
|
+
Elaboration agents run in isolated Task contexts. Before constructing each Task prompt, replace every `${brain_context}`, `${dev_ideas_list}`, and `${ops_ideas_list}` with the actual literal text — `brain_context` from the brain load, `dev_ideas_list` and `ops_ideas_list` built above. Do NOT leave any `${...}` placeholders in the prompt — the agent receives them as literal dollar-sign strings and produces empty ELABORATION_OUTPUT blocks.
|
|
368
400
|
|
|
369
401
|
**Dev ideas** (`feature` or `technical-baseline`):
|
|
370
402
|
Spawn two agents in parallel via Task tool:
|
|
@@ -484,42 +516,81 @@ END_ELABORATION_OUTPUT`
|
|
|
484
516
|
})
|
|
485
517
|
```
|
|
486
518
|
|
|
487
|
-
|
|
519
|
+
Wait for all background agents to complete (do not proceed until all — or two, if only one category — return ELABORATION_OUTPUT blocks). Then build `merged_elaboration_json` by merging agent outputs per `card_id` and injecting `category` from `verdicts_output_json`:
|
|
488
520
|
|
|
489
521
|
```bash
|
|
490
|
-
#
|
|
491
|
-
|
|
522
|
+
# Assign each agent's ELABORATION_OUTPUT JSON array from its output block:
|
|
523
|
+
dev_researcher_json='<ELABORATION_OUTPUT array from the dev researcher agent>'
|
|
524
|
+
dev_codebase_json='<ELABORATION_OUTPUT array from the dev code-explorer agent>'
|
|
525
|
+
ops_researcher_json='<ELABORATION_OUTPUT array from the ops researcher agent>'
|
|
526
|
+
ops_pm_json='<ELABORATION_OUTPUT array from the ops PM agent>'
|
|
527
|
+
# Use '[]' for any track that had no ideas (e.g. if all ideas were dev, set ops_* to '[]')
|
|
528
|
+
|
|
529
|
+
# Merge dev track: join researcher + codebase findings by card_id, inject category from verdicts
|
|
530
|
+
dev_merged=$(jq -n \
|
|
531
|
+
--argjson r "$dev_researcher_json" \
|
|
532
|
+
--argjson c "$dev_codebase_json" \
|
|
533
|
+
--argjson v "$verdicts_output_json" \
|
|
534
|
+
'[$r[] | {card_id: .card_id, blocking_issue: .blocking_issue,
|
|
535
|
+
researcher_findings: .findings,
|
|
536
|
+
codebase_findings: ([$c[] | select(.card_id == .card_id)] | first | .findings // ""),
|
|
537
|
+
category: ([$v[] | select(.card_id == .card_id)] | first | .category // "feature")}]')
|
|
538
|
+
|
|
539
|
+
# Merge ops track: join researcher + PM findings by card_id, inject category
|
|
540
|
+
ops_merged=$(jq -n \
|
|
541
|
+
--argjson r "$ops_researcher_json" \
|
|
542
|
+
--argjson p "$ops_pm_json" \
|
|
543
|
+
--argjson v "$verdicts_output_json" \
|
|
544
|
+
'[$r[] | {card_id: .card_id, blocking_issue: .blocking_issue,
|
|
545
|
+
researcher_findings: .findings,
|
|
546
|
+
pm_findings: ([$p[] | select(.card_id == .card_id)] | first | .findings // ""),
|
|
547
|
+
category: ([$v[] | select(.card_id == .card_id)] | first | .category // "business-operation")}]')
|
|
548
|
+
|
|
549
|
+
# Combine both tracks
|
|
550
|
+
merged_elaboration_json=$(jq -s 'add' <(echo "$dev_merged") <(echo "$ops_merged"))
|
|
551
|
+
```
|
|
492
552
|
|
|
493
|
-
|
|
494
|
-
<researcher findings>
|
|
553
|
+
Use process substitution (not a pipeline) so the while loop runs in the main shell and variables remain in scope after `done`:
|
|
495
554
|
|
|
496
|
-
|
|
497
|
-
|
|
555
|
+
```bash
|
|
556
|
+
while IFS= read -r idea; do
|
|
557
|
+
CARD_ID=$(echo "$idea" | jq -r '.card_id')
|
|
558
|
+
category=$(echo "$idea" | jq -r '.category')
|
|
559
|
+
blocking_issue=$(echo "$idea" | jq -r '.blocking_issue // empty')
|
|
498
560
|
|
|
499
|
-
|
|
500
|
-
|
|
561
|
+
if [ "$category" = "business-operation" ]; then
|
|
562
|
+
researcher_findings=$(echo "$idea" | jq -r '.researcher_findings')
|
|
563
|
+
pm_findings=$(echo "$idea" | jq -r '.pm_findings')
|
|
564
|
+
monotask card set-description "$BOARD_ID" "$CARD_ID" "## Elaboration Findings
|
|
501
565
|
|
|
502
566
|
### Industry context & benchmarks:
|
|
503
|
-
|
|
567
|
+
$researcher_findings
|
|
504
568
|
|
|
505
569
|
### Feasibility & stakeholder impact:
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
570
|
+
$pm_findings"
|
|
571
|
+
monotask card comment add "$BOARD_ID" "$CARD_ID" "Industry context & benchmarks: $researcher_findings"
|
|
572
|
+
monotask card comment add "$BOARD_ID" "$CARD_ID" "Feasibility & stakeholder impact: $pm_findings"
|
|
573
|
+
else
|
|
574
|
+
researcher_findings=$(echo "$idea" | jq -r '.researcher_findings')
|
|
575
|
+
codebase_findings=$(echo "$idea" | jq -r '.codebase_findings')
|
|
576
|
+
monotask card set-description "$BOARD_ID" "$CARD_ID" "## Elaboration Findings
|
|
577
|
+
|
|
578
|
+
### Edge cases & prior art:
|
|
579
|
+
$researcher_findings
|
|
580
|
+
|
|
581
|
+
### Codebase constraints:
|
|
582
|
+
$codebase_findings"
|
|
583
|
+
monotask card comment add "$BOARD_ID" "$CARD_ID" "Edge cases & prior art: $researcher_findings"
|
|
584
|
+
monotask card comment add "$BOARD_ID" "$CARD_ID" "Codebase constraints: $codebase_findings"
|
|
585
|
+
fi
|
|
586
|
+
|
|
587
|
+
if [ -n "$blocking_issue" ]; then
|
|
588
|
+
monotask card move "$BOARD_ID" "$CARD_ID" "$COL_ICED" --json
|
|
589
|
+
monotask card comment add "$BOARD_ID" "$CARD_ID" "Blocked during elaboration: $blocking_issue"
|
|
590
|
+
else
|
|
591
|
+
monotask card move "$BOARD_ID" "$CARD_ID" "$COL_ELABORATED" --json
|
|
592
|
+
fi
|
|
593
|
+
done < <(echo "$merged_elaboration_json" | jq -c '.[]')
|
|
523
594
|
```
|
|
524
595
|
|
|
525
596
|
#### 6b. User Confirmation Gate
|
|
@@ -528,6 +599,10 @@ monotask card comment add "$BOARD_ID" "$CARD_ID" "Blocked during elaboration: <i
|
|
|
528
599
|
|
|
529
600
|
**Confirm mode only:** If `mode` is `confirm` (default), present a review table of all elaborated ideas to the user.
|
|
530
601
|
|
|
602
|
+
Build the table from `verdicts_output_json` (filtered to `verdict == "evaluated"`). For each row:
|
|
603
|
+
- **Impact** and **Effort** come from `.impact` and `.effort` fields in `verdicts_output_json`
|
|
604
|
+
- **Track**: `feature` or `technical-baseline` → `dev`; `business-operation` → `ops`
|
|
605
|
+
|
|
531
606
|
Print this exact format:
|
|
532
607
|
|
|
533
608
|
```
|
|
@@ -580,8 +655,9 @@ After applying all user instructions, proceed to Step 6c with the remaining idea
|
|
|
580
655
|
REGISTRY=".monomind/registry.json"
|
|
581
656
|
|
|
582
657
|
# Dev decomposition agent — pick the most relevant engineering/architecture specialist
|
|
583
|
-
#
|
|
584
|
-
DEV_IDEA_CONTEXT
|
|
658
|
+
# Build keyword signal from elaborated dev idea titles (from VERDICTS_OUTPUT filtered above)
|
|
659
|
+
DEV_IDEA_CONTEXT=$(echo "$verdicts_output_json" | jq -r \
|
|
660
|
+
'[.[] | select(.verdict=="evaluated") | select(.category=="feature" or .category=="technical-baseline") | .title] | join(" ")')
|
|
585
661
|
dev_decomp_agent=$(jq -r \
|
|
586
662
|
--arg kw "$(echo "$DEV_IDEA_CONTEXT" | tr '[:upper:]' '[:lower:]' | grep -oE '[a-z]{5,}' | sort -u | tr '\n' ' ')" \
|
|
587
663
|
'[ .agents[] | select(.deprecated != true)
|
|
@@ -601,7 +677,8 @@ dev_decomp_agent=$(jq -r \
|
|
|
601
677
|
dev_decomp_agent="${dev_decomp_agent:-Software Architect}"
|
|
602
678
|
|
|
603
679
|
# Ops decomposition agent — pick the most relevant strategy/sales/product specialist
|
|
604
|
-
OPS_IDEA_CONTEXT
|
|
680
|
+
OPS_IDEA_CONTEXT=$(echo "$verdicts_output_json" | jq -r \
|
|
681
|
+
'[.[] | select(.verdict=="evaluated") | select(.category=="business-operation") | .title] | join(" ")')
|
|
605
682
|
ops_decomp_agent=$(jq -r \
|
|
606
683
|
--arg kw "$(echo "$OPS_IDEA_CONTEXT" | tr '[:upper:]' '[:lower:]' | grep -oE '[a-z]{5,}' | sort -u | tr '\n' ' ')" \
|
|
607
684
|
'[ .agents[] | select(.deprecated != true)
|
|
@@ -624,30 +701,88 @@ echo "Dev decomp: $dev_decomp_agent | Ops decomp: $ops_decomp_agent"
|
|
|
624
701
|
```
|
|
625
702
|
|
|
626
703
|
**CRITICAL — Variable substitution required for Step 6c Task calls:**
|
|
627
|
-
|
|
704
|
+
Before constructing each Task prompt, replace `${brain_context}`, `${prompt}`, `${project_name}`, `${dev_ideas_elaborated}`, and `${ops_ideas_elaborated}` with actual literal text. Also substitute the `subagent_type` values: replace `dev_decomp_agent` and `ops_decomp_agent` with the string values echoed by the registry selection above (e.g. `"Software Architect"`). Build those lists from the VERDICTS_OUTPUT and ELABORATION_OUTPUT results:
|
|
628
705
|
|
|
629
|
-
|
|
630
|
-
|
|
706
|
+
```bash
|
|
707
|
+
# Build elaborated idea lists including card comments (full context for decomposition agents)
|
|
708
|
+
dev_ideas_elaborated=$(echo "$verdicts_output_json" | jq -r \
|
|
709
|
+
'[.[] | select(.verdict=="evaluated") | select(.category=="feature" or .category=="technical-baseline") |
|
|
710
|
+
"card_id: \(.card_id)\ntitle: \(.title)\ncategory: \(.category)\nrationale: \(.rationale)\nimpact: \(.impact) effort: \(.effort)"] | join("\n\n")')
|
|
711
|
+
|
|
712
|
+
ops_ideas_elaborated=$(echo "$verdicts_output_json" | jq -r \
|
|
713
|
+
'[.[] | select(.verdict=="evaluated") | select(.category=="business-operation") |
|
|
714
|
+
"card_id: \(.card_id)\ntitle: \(.title)\ncategory: \(.category)\nrationale: \(.rationale)\nimpact: \(.impact) effort: \(.effort)"] | join("\n\n")')
|
|
715
|
+
```
|
|
631
716
|
|
|
632
717
|
**Spawn decomposition agents by track** — run both in parallel if both tracks have elaborated ideas:
|
|
633
718
|
|
|
634
|
-
|
|
635
|
-
|
|
719
|
+
```javascript
|
|
720
|
+
// Dev decomposition agent (only if dev_ideas_elaborated is non-empty)
|
|
721
|
+
Task({
|
|
722
|
+
subagent_type: dev_decomp_agent, // value from registry selection above
|
|
723
|
+
description: "Task decomposition: dev ideas for " + project_name,
|
|
724
|
+
run_in_background: true,
|
|
725
|
+
prompt: `SAFETY CHECK: You are a decomposition agent. You must NOT call monotask board create, space create, or column create. Your only job is producing a TASKS_OUTPUT block — the outer skill creates the cards. If you are unsure of any card ID, list it as "UNKNOWN" rather than inventing a value.
|
|
636
726
|
|
|
637
|
-
|
|
638
|
-
- Their subset of elaborated ideas (titles, descriptions, all card comments, literal card IDs, and category)
|
|
639
|
-
- The literal `brain_context` and `prompt` values
|
|
727
|
+
Decompose the following dev ideas into concrete subtasks (2–6 per idea). Each subtask should be independently implementable.
|
|
640
728
|
|
|
641
|
-
|
|
729
|
+
PROJECT: ${project_name}
|
|
730
|
+
GOAL: ${prompt}
|
|
731
|
+
|
|
732
|
+
BRAIN CONTEXT:
|
|
733
|
+
${brain_context}
|
|
734
|
+
|
|
735
|
+
DEV IDEAS TO DECOMPOSE:
|
|
736
|
+
${dev_ideas_elaborated}
|
|
737
|
+
|
|
738
|
+
For each idea, produce 2–6 subtasks. If an idea's scope is unclear, flag it in FLAGGED instead of decomposing.
|
|
642
739
|
|
|
643
|
-
```
|
|
644
740
|
TASKS_OUTPUT
|
|
645
741
|
[
|
|
646
742
|
{
|
|
647
|
-
"parent_card_id": "<ideation board card ID>",
|
|
743
|
+
"parent_card_id": "<ideation board card ID from the list above>",
|
|
648
744
|
"title": "<subtask title ≤80 chars>",
|
|
649
|
-
"description": "<what to build/do>",
|
|
650
|
-
"category": "feature | technical-baseline
|
|
745
|
+
"description": "<what to build/do — specific and actionable>",
|
|
746
|
+
"category": "feature | technical-baseline",
|
|
747
|
+
"agent": "<recommended subagent_type>",
|
|
748
|
+
"effort": <1-10>,
|
|
749
|
+
"has_prerequisites": <true | false>
|
|
750
|
+
}
|
|
751
|
+
]
|
|
752
|
+
FLAGGED
|
|
753
|
+
[
|
|
754
|
+
{ "card_id": "<ideation card ID>", "question": "<what needs clarifying>" }
|
|
755
|
+
]
|
|
756
|
+
END_TASKS_OUTPUT`
|
|
757
|
+
})
|
|
758
|
+
|
|
759
|
+
// Ops decomposition agent (only if ops_ideas_elaborated is non-empty)
|
|
760
|
+
Task({
|
|
761
|
+
subagent_type: ops_decomp_agent, // value from registry selection above
|
|
762
|
+
description: "Task decomposition: ops ideas for " + project_name,
|
|
763
|
+
run_in_background: true,
|
|
764
|
+
prompt: `SAFETY CHECK: You are a decomposition agent. You must NOT call monotask board create, space create, or column create. Your only job is producing a TASKS_OUTPUT block — the outer skill creates the cards. If you are unsure of any card ID, list it as "UNKNOWN" rather than inventing a value.
|
|
765
|
+
|
|
766
|
+
Decompose the following business-operation ideas into concrete subtasks (2–6 per idea). Each subtask should be independently actionable.
|
|
767
|
+
|
|
768
|
+
PROJECT: ${project_name}
|
|
769
|
+
GOAL: ${prompt}
|
|
770
|
+
|
|
771
|
+
BRAIN CONTEXT:
|
|
772
|
+
${brain_context}
|
|
773
|
+
|
|
774
|
+
OPS IDEAS TO DECOMPOSE:
|
|
775
|
+
${ops_ideas_elaborated}
|
|
776
|
+
|
|
777
|
+
For each idea, produce 2–6 subtasks. If an idea's scope is unclear, flag it in FLAGGED instead of decomposing.
|
|
778
|
+
|
|
779
|
+
TASKS_OUTPUT
|
|
780
|
+
[
|
|
781
|
+
{
|
|
782
|
+
"parent_card_id": "<ideation board card ID from the list above>",
|
|
783
|
+
"title": "<subtask title ≤80 chars>",
|
|
784
|
+
"description": "<what to build/do — specific and actionable>",
|
|
785
|
+
"category": "business-operation",
|
|
651
786
|
"agent": "<recommended subagent_type>",
|
|
652
787
|
"effort": <1-10>,
|
|
653
788
|
"has_prerequisites": <true | false>
|
|
@@ -657,7 +792,8 @@ FLAGGED
|
|
|
657
792
|
[
|
|
658
793
|
{ "card_id": "<ideation card ID>", "question": "<what needs clarifying>" }
|
|
659
794
|
]
|
|
660
|
-
END_TASKS_OUTPUT
|
|
795
|
+
END_TASKS_OUTPUT`
|
|
796
|
+
})
|
|
661
797
|
```
|
|
662
798
|
|
|
663
799
|
**After both decomposition agents return**, the outer skill creates task cards on the appropriate board for each task's category. Each task card inherits the parent idea's `impact` and `effort` scores from the VERDICTS_OUTPUT parsed in Step 5 — look up by `parent_card_id`.
|
|
@@ -680,11 +816,14 @@ monotask column create "$TASK_BOARD_ID" "Human in Loop" --json >/dev/null
|
|
|
680
816
|
monotask column create "$TASK_BOARD_ID" "Done" --json >/dev/null
|
|
681
817
|
```
|
|
682
818
|
|
|
683
|
-
Look up column IDs:
|
|
819
|
+
Look up and validate column IDs:
|
|
684
820
|
```bash
|
|
821
|
+
[ -z "$TASK_BOARD_ID" ] && { echo "ERROR: TASK_BOARD_ID is empty — aborting to prevent card creation on null board"; exit 1; }
|
|
685
822
|
task_columns=$(monotask column list "$TASK_BOARD_ID" --json)
|
|
686
823
|
TASK_COL_TODO=$(echo "$task_columns" | jq -r '.[] | select(.title == "Todo") | .id' | head -1)
|
|
687
824
|
TASK_COL_BACKLOG=$(echo "$task_columns" | jq -r '.[] | select(.title == "Backlog") | .id' | head -1)
|
|
825
|
+
[ -z "$TASK_COL_TODO" ] && { echo "ERROR: Could not find 'Todo' column on TASK_BOARD_ID=$TASK_BOARD_ID"; exit 1; }
|
|
826
|
+
[ -z "$TASK_COL_BACKLOG" ] && { echo "ERROR: Could not find 'Backlog' column on TASK_BOARD_ID=$TASK_BOARD_ID"; exit 1; }
|
|
688
827
|
```
|
|
689
828
|
|
|
690
829
|
---
|
|
@@ -705,19 +844,43 @@ monotask column create "$OPS_BOARD_ID" "Human in Loop" --json >/dev/null
|
|
|
705
844
|
monotask column create "$OPS_BOARD_ID" "Done" --json >/dev/null
|
|
706
845
|
```
|
|
707
846
|
|
|
708
|
-
Look up column IDs:
|
|
847
|
+
Look up and validate column IDs:
|
|
709
848
|
```bash
|
|
849
|
+
[ -z "$OPS_BOARD_ID" ] && { echo "ERROR: OPS_BOARD_ID is empty — aborting to prevent card creation on null board"; exit 1; }
|
|
710
850
|
ops_columns=$(monotask column list "$OPS_BOARD_ID" --json)
|
|
711
851
|
OPS_COL_TODO=$(echo "$ops_columns" | jq -r '.[] | select(.title == "Todo") | .id' | head -1)
|
|
712
852
|
OPS_COL_BACKLOG=$(echo "$ops_columns" | jq -r '.[] | select(.title == "Backlog") | .id' | head -1)
|
|
853
|
+
[ -z "$OPS_COL_TODO" ] && { echo "ERROR: Could not find 'Todo' column on OPS_BOARD_ID=$OPS_BOARD_ID"; exit 1; }
|
|
854
|
+
[ -z "$OPS_COL_BACKLOG" ] && { echo "ERROR: Could not find 'Backlog' column on OPS_BOARD_ID=$OPS_BOARD_ID"; exit 1; }
|
|
713
855
|
```
|
|
714
856
|
|
|
715
857
|
---
|
|
716
858
|
|
|
717
|
-
**
|
|
859
|
+
**Assign TASKS_OUTPUT from each decomposition agent, then merge and iterate:**
|
|
718
860
|
|
|
719
861
|
```bash
|
|
720
|
-
|
|
862
|
+
# Parse the TASKS_OUTPUT block from the dev decomp agent's response and assign:
|
|
863
|
+
dev_tasks_json='<TASKS_OUTPUT JSON array from dev decomp agent, or [] if no dev ideas>'
|
|
864
|
+
# Parse the TASKS_OUTPUT block from the ops decomp agent's response and assign:
|
|
865
|
+
ops_tasks_json='<TASKS_OUTPUT JSON array from ops decomp agent, or [] if no ops ideas>'
|
|
866
|
+
|
|
867
|
+
merged_tasks_json=$(jq -s 'add' <(echo "$dev_tasks_json") <(echo "$ops_tasks_json"))
|
|
868
|
+
|
|
869
|
+
# Use process substitution (not a pipeline) so variables set inside the loop
|
|
870
|
+
# remain in scope after done — needed for the parent-annotation step below.
|
|
871
|
+
# Also initialise the subtask-accumulator map before the loop.
|
|
872
|
+
declare -A parent_subtask_summaries # parent_card_id -> newline-separated task summary lines
|
|
873
|
+
|
|
874
|
+
while IFS= read -r task; do
|
|
875
|
+
parent_card_id=$(echo "$task" | jq -r '.parent_card_id')
|
|
876
|
+
title=$(echo "$task" | jq -r '.title')
|
|
877
|
+
description=$(echo "$task" | jq -r '.description')
|
|
878
|
+
category=$(echo "$task" | jq -r '.category')
|
|
879
|
+
agent=$(echo "$task" | jq -r '.agent')
|
|
880
|
+
task_effort=$(echo "$task" | jq -r '.effort')
|
|
881
|
+
has_prerequisites=$(echo "$task" | jq -r '.has_prerequisites')
|
|
882
|
+
|
|
883
|
+
if [ "$category" = "business-operation" ]; then
|
|
721
884
|
TARGET_BOARD="$OPS_BOARD_ID"
|
|
722
885
|
COL_TARGET=$([ "$has_prerequisites" = "true" ] && echo "$OPS_COL_BACKLOG" || echo "$OPS_COL_TODO")
|
|
723
886
|
BOARD_LABEL="Operations Tasks"
|
|
@@ -727,14 +890,20 @@ else
|
|
|
727
890
|
BOARD_LABEL="Implementation Tasks"
|
|
728
891
|
fi
|
|
729
892
|
|
|
893
|
+
# Inherit impact and effort from parent idea — look up from verdicts_output_json by parent_card_id
|
|
894
|
+
parent_impact=$(echo "$verdicts_output_json" | jq -r --arg id "$parent_card_id" '.[] | select(.card_id == $id) | .impact // 5')
|
|
895
|
+
parent_effort=$(echo "$verdicts_output_json" | jq -r --arg id "$parent_card_id" '.[] | select(.card_id == $id) | .effort // 5')
|
|
896
|
+
# Default to 5 if lookup returned empty (e.g. parent_card_id was "UNKNOWN")
|
|
897
|
+
[ -z "$parent_impact" ] && parent_impact=5
|
|
898
|
+
[ -z "$parent_effort" ] && parent_effort=5
|
|
899
|
+
|
|
730
900
|
# Create task card as a proper subtask of the parent idea card (cross-board link)
|
|
731
901
|
# Signature: subtask add <PARENT_BOARD_ID> <PARENT_CARD_ID> <CHILD_BOARD_ID> <COL_ID> <TITLE>
|
|
732
902
|
TASK_CARD_ID=$(monotask card subtask add "$BOARD_ID" "$parent_card_id" "$TARGET_BOARD" "$COL_TARGET" "<task title>" --json | jq -r '.id')
|
|
733
903
|
# Set the task description as the primary content field
|
|
734
904
|
monotask card set-description "$TARGET_BOARD" "$TASK_CARD_ID" "<what to build/do — from TASKS_OUTPUT description>"
|
|
735
|
-
|
|
736
|
-
monotask card set-
|
|
737
|
-
monotask card set-effort "$TARGET_BOARD" "$TASK_CARD_ID" <parent_effort>
|
|
905
|
+
monotask card set-impact "$TARGET_BOARD" "$TASK_CARD_ID" "$parent_impact"
|
|
906
|
+
monotask card set-effort "$TARGET_BOARD" "$TASK_CARD_ID" "$parent_effort"
|
|
738
907
|
monotask card comment add "$TARGET_BOARD" "$TASK_CARD_ID" \
|
|
739
908
|
"SOURCE: mastermind:idea | <first 100 chars of prompt>
|
|
740
909
|
AGENT: <agent>
|
|
@@ -743,14 +912,22 @@ PARENT IDEA IMPACT: <parent_impact>/10 PARENT IDEA EFFORT: <parent_effort>/10
|
|
|
743
912
|
CATEGORY: <category>
|
|
744
913
|
PARENT IDEA: <idea title> (card: <parent_card_id> on ideation board)"
|
|
745
914
|
monotask card label add "$TARGET_BOARD" "$TASK_CARD_ID" "mastermind:idea"
|
|
746
|
-
monotask card label add "$TARGET_BOARD" "$TASK_CARD_ID" "category
|
|
915
|
+
monotask card label add "$TARGET_BOARD" "$TASK_CARD_ID" "category:$category"
|
|
916
|
+
|
|
917
|
+
# Accumulate subtask summary for this parent (used in the post-loop annotation step)
|
|
918
|
+
parent_subtask_summaries[$parent_card_id]+=" - $title (agent: $agent, effort: $task_effort/10, board: $BOARD_LABEL)\n"
|
|
919
|
+
|
|
920
|
+
done < <(echo "$merged_tasks_json" | jq -c '.[]')
|
|
747
921
|
```
|
|
748
922
|
|
|
749
|
-
|
|
923
|
+
After the loop, annotate each parent idea card and move it to `Tasked`:
|
|
750
924
|
```bash
|
|
751
|
-
|
|
752
|
-
"
|
|
753
|
-
monotask card
|
|
925
|
+
for parent_card_id in "${!parent_subtask_summaries[@]}"; do
|
|
926
|
+
subtask_list="${parent_subtask_summaries[$parent_card_id]}"
|
|
927
|
+
monotask card comment add "$BOARD_ID" "$parent_card_id" \
|
|
928
|
+
"Subtasks created:\n${subtask_list}"
|
|
929
|
+
monotask card move "$BOARD_ID" "$parent_card_id" "$COL_TASKED" --json
|
|
930
|
+
done
|
|
754
931
|
```
|
|
755
932
|
|
|
756
933
|
For each entry in FLAGGED, move the idea to `Iced`:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monoes/monomindcli",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.16",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Monomind CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
|
|
6
6
|
"main": "dist/src/index.js",
|