monomind 1.17.0 → 1.17.2

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.
Files changed (94) hide show
  1. package/.claude/agents/engineering/engineering-security-engineer.md +1 -1
  2. package/.claude/commands/mastermind/_repeat.md +4 -0
  3. package/.claude/commands/mastermind/master.md +52 -1
  4. package/.claude/scheduled_tasks.lock +1 -1
  5. package/.claude/skills/mastermind/_repeat.md +2 -0
  6. package/package.json +1 -1
  7. package/packages/@monomind/cli/.claude/agents/engineering/engineering-security-engineer.md +1 -1
  8. package/packages/@monomind/cli/.claude/commands/mastermind/_repeat.md +4 -0
  9. package/packages/@monomind/cli/.claude/commands/mastermind/master.md +52 -1
  10. package/packages/@monomind/cli/.claude/skills/mastermind/_repeat.md +2 -0
  11. package/packages/@monomind/cli/dist/src/__tests__/browse-analyzer.test.js +42 -59
  12. package/packages/@monomind/cli/dist/src/agents/registry-builder.d.ts +8 -0
  13. package/packages/@monomind/cli/dist/src/agents/registry-builder.js +22 -0
  14. package/packages/@monomind/cli/dist/src/browser/dashboard/server.js +18 -0
  15. package/packages/@monomind/cli/dist/src/browser/dashboard/ui.html +37 -125
  16. package/packages/@monomind/cli/dist/src/commands/agent-lifecycle.d.ts +17 -0
  17. package/packages/@monomind/cli/dist/src/commands/agent-lifecycle.js +320 -0
  18. package/packages/@monomind/cli/dist/src/commands/agent-ops.d.ts +9 -0
  19. package/packages/@monomind/cli/dist/src/commands/agent-ops.js +329 -0
  20. package/packages/@monomind/cli/dist/src/commands/agent.js +5 -907
  21. package/packages/@monomind/cli/dist/src/commands/analyze-ast.d.ts +26 -0
  22. package/packages/@monomind/cli/dist/src/commands/analyze-ast.js +284 -0
  23. package/packages/@monomind/cli/dist/src/commands/analyze-boundaries.d.ts +14 -0
  24. package/packages/@monomind/cli/dist/src/commands/analyze-boundaries.js +295 -0
  25. package/packages/@monomind/cli/dist/src/commands/analyze-diff.d.ts +8 -0
  26. package/packages/@monomind/cli/dist/src/commands/analyze-diff.js +395 -0
  27. package/packages/@monomind/cli/dist/src/commands/analyze-graph.d.ts +14 -0
  28. package/packages/@monomind/cli/dist/src/commands/analyze-graph.js +304 -0
  29. package/packages/@monomind/cli/dist/src/commands/analyze-imports.d.ts +11 -0
  30. package/packages/@monomind/cli/dist/src/commands/analyze-imports.js +287 -0
  31. package/packages/@monomind/cli/dist/src/commands/analyze-symbols.d.ts +14 -0
  32. package/packages/@monomind/cli/dist/src/commands/analyze-symbols.js +302 -0
  33. package/packages/@monomind/cli/dist/src/commands/analyze.d.ts +38 -0
  34. package/packages/@monomind/cli/dist/src/commands/analyze.js +12 -1827
  35. package/packages/@monomind/cli/dist/src/commands/doctor-env-checks.d.ts +26 -0
  36. package/packages/@monomind/cli/dist/src/commands/doctor-env-checks.js +189 -0
  37. package/packages/@monomind/cli/dist/src/commands/doctor-project-checks.d.ts +20 -0
  38. package/packages/@monomind/cli/dist/src/commands/doctor-project-checks.js +432 -0
  39. package/packages/@monomind/cli/dist/src/commands/doctor.js +54 -943
  40. package/packages/@monomind/cli/dist/src/commands/hive-mind-comms.d.ts +11 -0
  41. package/packages/@monomind/cli/dist/src/commands/hive-mind-comms.js +242 -0
  42. package/packages/@monomind/cli/dist/src/commands/hive-mind-helpers.d.ts +35 -0
  43. package/packages/@monomind/cli/dist/src/commands/hive-mind-helpers.js +203 -0
  44. package/packages/@monomind/cli/dist/src/commands/hive-mind-ops.d.ts +8 -0
  45. package/packages/@monomind/cli/dist/src/commands/hive-mind-ops.js +233 -0
  46. package/packages/@monomind/cli/dist/src/commands/hive-mind-spawn.d.ts +12 -0
  47. package/packages/@monomind/cli/dist/src/commands/hive-mind-spawn.js +274 -0
  48. package/packages/@monomind/cli/dist/src/commands/hive-mind.js +10 -1129
  49. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-commands.d.ts +4 -4
  50. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-commands.js +19 -819
  51. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-gaps.d.ts +7 -0
  52. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-gaps.js +334 -0
  53. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-routing.d.ts +7 -0
  54. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-routing.js +399 -0
  55. package/packages/@monomind/cli/dist/src/commands/init-subcommands.d.ts +8 -0
  56. package/packages/@monomind/cli/dist/src/commands/init-subcommands.js +156 -0
  57. package/packages/@monomind/cli/dist/src/commands/init-upgrade.d.ts +6 -0
  58. package/packages/@monomind/cli/dist/src/commands/init-upgrade.js +203 -0
  59. package/packages/@monomind/cli/dist/src/commands/init-wizard.d.ts +6 -0
  60. package/packages/@monomind/cli/dist/src/commands/init-wizard.js +246 -0
  61. package/packages/@monomind/cli/dist/src/commands/init.js +6 -623
  62. package/packages/@monomind/cli/dist/src/commands/memory-admin.d.ts +10 -0
  63. package/packages/@monomind/cli/dist/src/commands/memory-admin.js +433 -0
  64. package/packages/@monomind/cli/dist/src/commands/memory-crud.d.ts +9 -0
  65. package/packages/@monomind/cli/dist/src/commands/memory-crud.js +342 -0
  66. package/packages/@monomind/cli/dist/src/commands/memory-list.d.ts +10 -0
  67. package/packages/@monomind/cli/dist/src/commands/memory-list.js +321 -0
  68. package/packages/@monomind/cli/dist/src/commands/memory-transfer.d.ts +9 -0
  69. package/packages/@monomind/cli/dist/src/commands/memory-transfer.js +372 -0
  70. package/packages/@monomind/cli/dist/src/commands/memory.d.ts +6 -0
  71. package/packages/@monomind/cli/dist/src/commands/memory.js +10 -1441
  72. package/packages/@monomind/cli/dist/src/commands/neural-core.d.ts +8 -0
  73. package/packages/@monomind/cli/dist/src/commands/neural-core.js +274 -0
  74. package/packages/@monomind/cli/dist/src/commands/neural-optimize.d.ts +7 -0
  75. package/packages/@monomind/cli/dist/src/commands/neural-optimize.js +332 -0
  76. package/packages/@monomind/cli/dist/src/commands/neural-registry.d.ts +7 -0
  77. package/packages/@monomind/cli/dist/src/commands/neural-registry.js +290 -0
  78. package/packages/@monomind/cli/dist/src/commands/neural.js +3 -974
  79. package/packages/@monomind/cli/dist/src/commands/platforms.js +327 -7
  80. package/packages/@monomind/cli/dist/src/commands/security-cve.d.ts +6 -0
  81. package/packages/@monomind/cli/dist/src/commands/security-cve.js +310 -0
  82. package/packages/@monomind/cli/dist/src/commands/security-misc.d.ts +9 -0
  83. package/packages/@monomind/cli/dist/src/commands/security-misc.js +293 -0
  84. package/packages/@monomind/cli/dist/src/commands/security-scan.d.ts +18 -0
  85. package/packages/@monomind/cli/dist/src/commands/security-scan.js +328 -0
  86. package/packages/@monomind/cli/dist/src/commands/security.js +3 -958
  87. package/packages/@monomind/cli/dist/src/commands/session.js +1 -1
  88. package/packages/@monomind/cli/dist/src/commands/swarm.js +23 -17
  89. package/packages/@monomind/cli/dist/src/index.js +8 -37
  90. package/packages/@monomind/cli/dist/src/mcp-tools/swarm-tools.js +77 -0
  91. package/packages/@monomind/cli/dist/src/parser.js +11 -6
  92. package/packages/@monomind/cli/dist/src/routing/llm-caller.js +1 -2
  93. package/packages/@monomind/cli/package.json +2 -3
  94. package/packages/@monomind/cli/scripts/understand-analyze.mjs +1 -1
@@ -24,7 +24,7 @@ capability:
24
24
  model_preference: opus
25
25
  termination: All identified vulnerabilities have remediation steps documented
26
26
  triggers:
27
- - pattern: "\\b(auth|jwt|session|oauth|saml|oidc|csrf|xss|sql.injection|ssrf|rce|lfi)\\b"
27
+ - pattern: "\\b(jwt|oauth|saml|oidc|csrf|xss|sql.injection|ssrf|rce|lfi|session\\W{0,3}(hijack|fixation|token\\W{0,3}theft)\\w*|auth\\w*\\W{0,3}(bypass|bug|vuln|flaw|exploit)\\w*)\\b"
28
28
  mode: "inject"
29
29
  - pattern: "CVE-\\d{4}-\\d+"
30
30
  mode: "inject"
@@ -325,6 +325,8 @@ RECENT_HIL=$(find . -maxdepth 3 \( -name "humaninloop*.md" -o -name "humaninloop
325
325
 
326
326
  **Skip this section if `tillend_mode` is not true.** Proceed to section 5.
327
327
 
328
+ **`LOOP_ASYNC_PENDING` escape hatch:** If the calling skill set `LOOP_ASYNC_PENDING=true` before invoking the postamble (because it spawned background agents whose results have not yet arrived in this turn), skip the empty-round check entirely — treat this round as non-empty and go directly to section 6 to schedule the next run. The next scheduled run will evaluate results from the agents that are currently in-flight. Output: `[tillend] Async work in flight — deferring empty-round check to run <next_rep>.`
329
+
328
330
  After each run in tillend mode, evaluate whether this run produced **zero findings and zero actions**. The loop stops only when a complete round finds nothing new and makes no changes — not when the AI predicts there is nothing left.
329
331
 
330
332
  **You (the AI running the loop) must now assess your own output from this run.** Answer these two questions:
@@ -459,6 +461,8 @@ curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
459
461
 
460
462
  ## Notes for Calling Commands
461
463
 
464
+ - **Async agents in loop mode**: In `--repeat` or `--tillend` mode, all subagents MUST be synchronous (`run_in_background: false` or omitted). The postamble's empty-round check runs in the same turn as the command — background agents complete in a later turn and their output is invisible to the check. If a skill must go async (e.g. it needs to spawn a long-running agent), set `LOOP_ASYNC_PENDING=true` before invoking the postamble; section 4 will skip the empty-round check and schedule the next run unconditionally. The next run will then evaluate the results.
465
+ - **Loop state is durable across turns**: The counter and all loop parameters survive across `ScheduleWakeup` fires via two mechanisms: (1) `--rep <N> --loop <ID>` is re-encoded in every ScheduleWakeup prompt, and (2) the `.monomind/loops/<ID>.json` file stores current state persistently. There is no in-memory counter — the counter is always re-read from the prompt flags or the file.
462
466
  - **`<command_slug>`**: lowercase command name without namespace (`build` for `/mastermind:build`, `mastermind-idea` for `/mastermind:idea`)
463
467
  - **Dashboard**: the monomind panel reads `.monomind/loops/*.json`; `type` field is `"repeat"` or `"tillend"`; HIL status shows as `"hil:pending"`
464
468
  - **Stopping a loop**: create `.monomind/loops/${LOOP_ID}.stop` or delete the `.json` file; the next wake-up detects it
@@ -347,6 +347,21 @@ curl -s -o /dev/null -X POST "${CTRL_URL}/api/mastermind/event" \
347
347
  '{type:"session:start",session:$sid,prompt:$prompt,mode:$mode,project:$proj,ts:(now*1000|floor)}')" || true
348
348
  ```
349
349
 
350
+ ### Step 3.5 — Design Gate
351
+
352
+ <HARD-GATE>
353
+ STOP before Step 4. If `domains_needed` includes `build` (or any code/feature/fix work):
354
+
355
+ 1. Invoke `Skill("mastermind:design")` NOW — pass `resolved_prompt` as the task description
356
+ 2. Do NOT write any code, do NOT spawn any domain managers, do NOT proceed to Step 4 until the design skill returns with an approved spec
357
+ 3. When design returns: save the approved spec as `build_spec` in `current.json`
358
+ 4. Only then continue to Step 4
359
+
360
+ If `domains_needed` contains ONLY non-build domains (marketing, sales, research, content, ops, finance, idea): skip this gate and go directly to Step 4.
361
+
362
+ Rationale: without a design gate, master spawns agents before the user has confirmed what to build. The design gate replicates the superpowers workflow: questions → approaches → spec approval → plan → execute.
363
+ </HARD-GATE>
364
+
350
365
  ### Step 4 — Decompose
351
366
 
352
367
  For each domain in `domains_needed`, assess complexity:
@@ -423,7 +438,39 @@ If mode = confirm: show plan and wait for user response. Valid responses:
423
438
  - Any modification (e.g. "add sales domain", "remove marketing") — apply the change, re-show the plan, wait again
424
439
  - "cancel" or "stop" — emit `session:complete` with `status: blocked`, reason "cancelled by user", then STOP
425
440
 
426
- If mode = auto: proceed immediately.
441
+ After the user says "go" (and `build` is in `domains_needed`), ask once:
442
+ > "For the build work: **subagents** (recommended — fresh agent per task with 2-stage review, like mastermind:taskdev) or **inline** (direct execution, mastermind:execute)?"
443
+
444
+ - "subagents" → `build_exec_mode = "taskdev"`
445
+ - "inline" → `build_exec_mode = "execute"`
446
+ - No answer / skipped → `build_exec_mode = "taskdev"` (default)
447
+
448
+ If mode = auto: `build_exec_mode = "taskdev"` (default).
449
+
450
+ **Persist `build_exec_mode` to `current.json`** (required — Phase C reads it from there):
451
+
452
+ ```bash
453
+ REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
454
+ _get_mono_dir() {
455
+ local w="${1:-$(pwd)}"
456
+ if [ -d "$w/.git" ]; then echo "$w/.git/monomind"; return; fi
457
+ if [ -f "$w/.git" ]; then
458
+ local m; m=$(grep '^gitdir:' "$w/.git" | sed 's/gitdir: *//')
459
+ [ -z "$m" ] && { echo "$w/.monomind"; return; }
460
+ [[ "$m" != /* ]] && m="$w/$m"
461
+ echo "$(dirname "$(dirname "$m")")/monomind"; return
462
+ fi
463
+ echo "$w/.monomind"
464
+ }
465
+ MONO_DIR=$(_get_mono_dir "$REPO_ROOT")
466
+ SESSION_STATE="$MONO_DIR/sessions/current.json"
467
+ # LLM: replace EXEC_MODE_VALUE with the resolved value: taskdev or execute
468
+ build_exec_mode="EXEC_MODE_VALUE"
469
+ [ "$build_exec_mode" = "EXEC_MODE_VALUE" ] && build_exec_mode="taskdev"
470
+ jq --arg m "$build_exec_mode" '. + {build_exec_mode: $m}' \
471
+ "$SESSION_STATE" > "$SESSION_STATE.tmp" && mv "$SESSION_STATE.tmp" "$SESSION_STATE"
472
+ echo "build_exec_mode=$build_exec_mode written to current.json"
473
+ ```
427
474
 
428
475
  ### Step 6 — Monotask Setup
429
476
 
@@ -678,12 +725,15 @@ for domain in $(jq -r '.domains_needed[]? // empty' "$SESSION_STATE" | grep -v '
678
725
  if [ -z "$board_id" ]; then
679
726
  echo "WARN: DOMAIN=$domain has no board_id — Step 6 may not have run or monotask is missing. Task agent will run without board tracking."
680
727
  fi
728
+ exec_mode=""
729
+ [ "$domain" = "build" ] && exec_mode=$(jq -r '.build_exec_mode // "taskdev"' "$SESSION_STATE")
681
730
  echo "DOMAIN=$domain \
682
731
  MANAGER=$(jq -r --arg d "$domain" '.domain_managers[$d] // "coordinator"' "$SESSION_STATE") \
683
732
  BOARD=$board_id \
684
733
  TODO=$(jq -r --arg d "$domain" '.todo_cols[$d] // ""' "$SESSION_STATE") \
685
734
  DOING=$(jq -r --arg d "$domain" '.doing_cols[$d] // ""' "$SESSION_STATE") \
686
735
  DONE=$(jq -r --arg d "$domain" '.done_cols[$d] // ""' "$SESSION_STATE") \
736
+ EXEC_MODE=$exec_mode \
687
737
  GOAL=$(jq -r --arg d "$domain" '.domain_goals[$d] // .prompt' "$SESSION_STATE" | tr -d '\n')"
688
738
  done
689
739
  ```
@@ -698,6 +748,7 @@ Each Task call must include a complete briefing following the Monotask Task Brie
698
748
  - Instruction to create monotask cards directly using `monotask card create $BOARD_ID $COL_TODO_ID "<title>" --json` for all sub-tasks
699
749
  - Instruction to use `Skill("mastermind:do")` to execute tasks (Task agents have Skill tool access — do NOT use slash command syntax)
700
750
  - Instruction to spawn specialized agents using the domain-appropriate swarm topology
751
+ - **For the `build` domain only:** include `build_exec_mode` (value: `"taskdev"` or `"execute"`) and instruct the manager: "Use `Skill("mastermind:taskdev")` if build_exec_mode is `taskdev`, or `Skill("mastermind:execute")` if `execute`. This was chosen by the user in Step 5."
701
752
  - Instruction to return the unified output schema when done
702
753
 
703
754
  Example Task call for Development Manager. Substitute all **pre-known** `<…>` placeholders (project_name, SESSION_ID, board/col IDs, goals, manager name) before calling Task. Placeholders like `<status>`, `<path1>`, `<action1>` are filled at runtime by the spawned agent — do not attempt to substitute them. `subagent_type` is the **string value** of `$domain_manager_build` (e.g. `"Backend Architect"`), not a variable reference.
@@ -1 +1 @@
1
- {"sessionId":"db24c21a-e929-4aba-911b-4cdb7fc5c3d5","pid":2346,"procStart":"Wed Jun 24 16:31:36 2026","acquiredAt":1782380072591}
1
+ {"sessionId":"361cc64c-bfbe-4774-a739-e3b982e26b21","pid":85888,"procStart":"Thu Jul 2 11:48:13 2026","acquiredAt":1782999721096}
@@ -27,6 +27,8 @@ If the stop file exists:
27
27
 
28
28
  ## Step 2 — Tillend termination check (tillend mode only — skip for fixed-count)
29
29
 
30
+ **`LOOP_ASYNC_PENDING` escape hatch:** If the calling skill set `LOOP_ASYNC_PENDING=true` before invoking this postamble (because it spawned background agents whose results have not yet arrived), skip the empty-round check and go directly to Step 4 to schedule the next run. Output: `[tillend] Async work in flight — deferring empty-round check to run <next_rep>.`
31
+
30
32
  Evaluate now whether this run produced **zero findings AND zero actions**:
31
33
 
32
34
  - **Findings**: issues found, problems detected, items flagged, security vulnerabilities, tasks discovered, errors reported
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "monomind",
3
- "version": "1.17.0",
3
+ "version": "1.17.2",
4
4
  "description": "Monomind - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -24,7 +24,7 @@ capability:
24
24
  model_preference: opus
25
25
  termination: All identified vulnerabilities have remediation steps documented
26
26
  triggers:
27
- - pattern: "\\b(auth|jwt|session|oauth|saml|oidc|csrf|xss|sql.injection|ssrf|rce|lfi)\\b"
27
+ - pattern: "\\b(jwt|oauth|saml|oidc|csrf|xss|sql.injection|ssrf|rce|lfi|session\\W{0,3}(hijack|fixation|token\\W{0,3}theft)\\w*|auth\\w*\\W{0,3}(bypass|bug|vuln|flaw|exploit)\\w*)\\b"
28
28
  mode: "inject"
29
29
  - pattern: "CVE-\\d{4}-\\d+"
30
30
  mode: "inject"
@@ -325,6 +325,8 @@ RECENT_HIL=$(find . -maxdepth 3 \( -name "humaninloop*.md" -o -name "humaninloop
325
325
 
326
326
  **Skip this section if `tillend_mode` is not true.** Proceed to section 5.
327
327
 
328
+ **`LOOP_ASYNC_PENDING` escape hatch:** If the calling skill set `LOOP_ASYNC_PENDING=true` before invoking the postamble (because it spawned background agents whose results have not yet arrived in this turn), skip the empty-round check entirely — treat this round as non-empty and go directly to section 6 to schedule the next run. The next scheduled run will evaluate results from the agents that are currently in-flight. Output: `[tillend] Async work in flight — deferring empty-round check to run <next_rep>.`
329
+
328
330
  After each run in tillend mode, evaluate whether this run produced **zero findings and zero actions**. The loop stops only when a complete round finds nothing new and makes no changes — not when the AI predicts there is nothing left.
329
331
 
330
332
  **You (the AI running the loop) must now assess your own output from this run.** Answer these two questions:
@@ -459,6 +461,8 @@ curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
459
461
 
460
462
  ## Notes for Calling Commands
461
463
 
464
+ - **Async agents in loop mode**: In `--repeat` or `--tillend` mode, all subagents MUST be synchronous (`run_in_background: false` or omitted). The postamble's empty-round check runs in the same turn as the command — background agents complete in a later turn and their output is invisible to the check. If a skill must go async (e.g. it needs to spawn a long-running agent), set `LOOP_ASYNC_PENDING=true` before invoking the postamble; section 4 will skip the empty-round check and schedule the next run unconditionally. The next run will then evaluate the results.
465
+ - **Loop state is durable across turns**: The counter and all loop parameters survive across `ScheduleWakeup` fires via two mechanisms: (1) `--rep <N> --loop <ID>` is re-encoded in every ScheduleWakeup prompt, and (2) the `.monomind/loops/<ID>.json` file stores current state persistently. There is no in-memory counter — the counter is always re-read from the prompt flags or the file.
462
466
  - **`<command_slug>`**: lowercase command name without namespace (`build` for `/mastermind:build`, `mastermind-idea` for `/mastermind:idea`)
463
467
  - **Dashboard**: the monomind panel reads `.monomind/loops/*.json`; `type` field is `"repeat"` or `"tillend"`; HIL status shows as `"hil:pending"`
464
468
  - **Stopping a loop**: create `.monomind/loops/${LOOP_ID}.stop` or delete the `.json` file; the next wake-up detects it
@@ -347,6 +347,21 @@ curl -s -o /dev/null -X POST "${CTRL_URL}/api/mastermind/event" \
347
347
  '{type:"session:start",session:$sid,prompt:$prompt,mode:$mode,project:$proj,ts:(now*1000|floor)}')" || true
348
348
  ```
349
349
 
350
+ ### Step 3.5 — Design Gate
351
+
352
+ <HARD-GATE>
353
+ STOP before Step 4. If `domains_needed` includes `build` (or any code/feature/fix work):
354
+
355
+ 1. Invoke `Skill("mastermind:design")` NOW — pass `resolved_prompt` as the task description
356
+ 2. Do NOT write any code, do NOT spawn any domain managers, do NOT proceed to Step 4 until the design skill returns with an approved spec
357
+ 3. When design returns: save the approved spec as `build_spec` in `current.json`
358
+ 4. Only then continue to Step 4
359
+
360
+ If `domains_needed` contains ONLY non-build domains (marketing, sales, research, content, ops, finance, idea): skip this gate and go directly to Step 4.
361
+
362
+ Rationale: without a design gate, master spawns agents before the user has confirmed what to build. The design gate replicates the superpowers workflow: questions → approaches → spec approval → plan → execute.
363
+ </HARD-GATE>
364
+
350
365
  ### Step 4 — Decompose
351
366
 
352
367
  For each domain in `domains_needed`, assess complexity:
@@ -423,7 +438,39 @@ If mode = confirm: show plan and wait for user response. Valid responses:
423
438
  - Any modification (e.g. "add sales domain", "remove marketing") — apply the change, re-show the plan, wait again
424
439
  - "cancel" or "stop" — emit `session:complete` with `status: blocked`, reason "cancelled by user", then STOP
425
440
 
426
- If mode = auto: proceed immediately.
441
+ After the user says "go" (and `build` is in `domains_needed`), ask once:
442
+ > "For the build work: **subagents** (recommended — fresh agent per task with 2-stage review, like mastermind:taskdev) or **inline** (direct execution, mastermind:execute)?"
443
+
444
+ - "subagents" → `build_exec_mode = "taskdev"`
445
+ - "inline" → `build_exec_mode = "execute"`
446
+ - No answer / skipped → `build_exec_mode = "taskdev"` (default)
447
+
448
+ If mode = auto: `build_exec_mode = "taskdev"` (default).
449
+
450
+ **Persist `build_exec_mode` to `current.json`** (required — Phase C reads it from there):
451
+
452
+ ```bash
453
+ REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
454
+ _get_mono_dir() {
455
+ local w="${1:-$(pwd)}"
456
+ if [ -d "$w/.git" ]; then echo "$w/.git/monomind"; return; fi
457
+ if [ -f "$w/.git" ]; then
458
+ local m; m=$(grep '^gitdir:' "$w/.git" | sed 's/gitdir: *//')
459
+ [ -z "$m" ] && { echo "$w/.monomind"; return; }
460
+ [[ "$m" != /* ]] && m="$w/$m"
461
+ echo "$(dirname "$(dirname "$m")")/monomind"; return
462
+ fi
463
+ echo "$w/.monomind"
464
+ }
465
+ MONO_DIR=$(_get_mono_dir "$REPO_ROOT")
466
+ SESSION_STATE="$MONO_DIR/sessions/current.json"
467
+ # LLM: replace EXEC_MODE_VALUE with the resolved value: taskdev or execute
468
+ build_exec_mode="EXEC_MODE_VALUE"
469
+ [ "$build_exec_mode" = "EXEC_MODE_VALUE" ] && build_exec_mode="taskdev"
470
+ jq --arg m "$build_exec_mode" '. + {build_exec_mode: $m}' \
471
+ "$SESSION_STATE" > "$SESSION_STATE.tmp" && mv "$SESSION_STATE.tmp" "$SESSION_STATE"
472
+ echo "build_exec_mode=$build_exec_mode written to current.json"
473
+ ```
427
474
 
428
475
  ### Step 6 — Monotask Setup
429
476
 
@@ -678,12 +725,15 @@ for domain in $(jq -r '.domains_needed[]? // empty' "$SESSION_STATE" | grep -v '
678
725
  if [ -z "$board_id" ]; then
679
726
  echo "WARN: DOMAIN=$domain has no board_id — Step 6 may not have run or monotask is missing. Task agent will run without board tracking."
680
727
  fi
728
+ exec_mode=""
729
+ [ "$domain" = "build" ] && exec_mode=$(jq -r '.build_exec_mode // "taskdev"' "$SESSION_STATE")
681
730
  echo "DOMAIN=$domain \
682
731
  MANAGER=$(jq -r --arg d "$domain" '.domain_managers[$d] // "coordinator"' "$SESSION_STATE") \
683
732
  BOARD=$board_id \
684
733
  TODO=$(jq -r --arg d "$domain" '.todo_cols[$d] // ""' "$SESSION_STATE") \
685
734
  DOING=$(jq -r --arg d "$domain" '.doing_cols[$d] // ""' "$SESSION_STATE") \
686
735
  DONE=$(jq -r --arg d "$domain" '.done_cols[$d] // ""' "$SESSION_STATE") \
736
+ EXEC_MODE=$exec_mode \
687
737
  GOAL=$(jq -r --arg d "$domain" '.domain_goals[$d] // .prompt' "$SESSION_STATE" | tr -d '\n')"
688
738
  done
689
739
  ```
@@ -698,6 +748,7 @@ Each Task call must include a complete briefing following the Monotask Task Brie
698
748
  - Instruction to create monotask cards directly using `monotask card create $BOARD_ID $COL_TODO_ID "<title>" --json` for all sub-tasks
699
749
  - Instruction to use `Skill("mastermind:do")` to execute tasks (Task agents have Skill tool access — do NOT use slash command syntax)
700
750
  - Instruction to spawn specialized agents using the domain-appropriate swarm topology
751
+ - **For the `build` domain only:** include `build_exec_mode` (value: `"taskdev"` or `"execute"`) and instruct the manager: "Use `Skill("mastermind:taskdev")` if build_exec_mode is `taskdev`, or `Skill("mastermind:execute")` if `execute`. This was chosen by the user in Step 5."
701
752
  - Instruction to return the unified output schema when done
702
753
 
703
754
  Example Task call for Development Manager. Substitute all **pre-known** `<…>` placeholders (project_name, SESSION_ID, board/col IDs, goals, manager name) before calling Task. Placeholders like `<status>`, `<path1>`, `<action1>` are filled at runtime by the spawned agent — do not attempt to substitute them. `subagent_type` is the **string value** of `$domain_manager_build` (e.g. `"Backend Architect"`), not a variable reference.
@@ -27,6 +27,8 @@ If the stop file exists:
27
27
 
28
28
  ## Step 2 — Tillend termination check (tillend mode only — skip for fixed-count)
29
29
 
30
+ **`LOOP_ASYNC_PENDING` escape hatch:** If the calling skill set `LOOP_ASYNC_PENDING=true` before invoking this postamble (because it spawned background agents whose results have not yet arrived), skip the empty-round check and go directly to Step 4 to schedule the next run. Output: `[tillend] Async work in flight — deferring empty-round check to run <next_rep>.`
31
+
30
32
  Evaluate now whether this run produced **zero findings AND zero actions**:
31
33
 
32
34
  - **Findings**: issues found, problems detected, items flagged, security vulnerabilities, tasks discovered, errors reported
@@ -1,85 +1,68 @@
1
1
  import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- // Shared mock instance so tests can override `create` per-test
3
- const mockCreate = vi.fn().mockResolvedValue({
4
- content: [{
5
- type: 'text',
6
- text: JSON.stringify({
7
- id: 'linkedin:comment_post',
8
- platform: 'linkedin',
9
- name: 'Comment on Post',
10
- params: ['post_url', 'text'],
11
- steps: [
12
- { type: 'navigate', url: '{{params.post_url}}' },
13
- { type: 'find', selectors: ['.comment-box'], as: 'box' },
14
- { type: 'click', target: '{{box}}' },
15
- { type: 'type', target: '{{box}}', text: '{{params.text}}', humanDelay: true },
16
- { type: 'wait', condition: 'network_idle', timeout: 3000 },
17
- ],
18
- }),
19
- }],
2
+ import { EventEmitter } from 'events';
3
+ // Mock child_process.spawn so tests don't invoke the real claude CLI
4
+ vi.mock('child_process', async (importOriginal) => {
5
+ const actual = await importOriginal();
6
+ return { ...actual, spawn: vi.fn() };
20
7
  });
21
- // Mock Anthropic SDK before importing analyzer
22
- vi.mock('@anthropic-ai/sdk', () => {
23
- const MockAnthropic = vi.fn(function () {
24
- this.messages = { create: mockCreate };
8
+ async function makeSpawnMock(stdout, exitCode = 0) {
9
+ const { spawn } = vi.mocked(await import('child_process'));
10
+ spawn.mockImplementationOnce(() => {
11
+ const proc = new EventEmitter();
12
+ proc.stdout = new EventEmitter();
13
+ proc.stderr = new EventEmitter();
14
+ proc.kill = vi.fn();
15
+ setTimeout(() => {
16
+ proc.stdout.emit('data', Buffer.from(stdout));
17
+ proc.emit('close', exitCode);
18
+ }, 0);
19
+ return proc;
25
20
  });
26
- return { default: MockAnthropic };
27
- });
21
+ }
22
+ const VALID_ACTION_DEF = {
23
+ id: 'linkedin:comment_post',
24
+ platform: 'linkedin',
25
+ name: 'Comment on Post',
26
+ params: ['post_url', 'text'],
27
+ steps: [
28
+ { type: 'navigate', url: '{{params.post_url}}' },
29
+ { type: 'find', selectors: ['.comment-box'], as: 'box' },
30
+ { type: 'click', target: '{{box}}' },
31
+ { type: 'type', target: '{{box}}', text: '{{params.text}}', humanDelay: true },
32
+ { type: 'wait', condition: 'network_idle', timeout: 3000 },
33
+ ],
34
+ };
28
35
  function mockPage(url = 'https://linkedin.com/feed', title = 'LinkedIn') {
29
36
  return {
30
37
  url: vi.fn().mockResolvedValue(url),
31
38
  evaluate: vi.fn().mockImplementation((expr) => {
32
39
  if (expr === 'document.title')
33
40
  return Promise.resolve(title);
34
- return Promise.resolve('[]'); // empty elements for DOM capture
41
+ return Promise.resolve('[]');
35
42
  }),
36
43
  };
37
44
  }
38
45
  describe('analyzePageForAction', () => {
39
46
  let analyzePageForAction;
40
47
  beforeEach(async () => {
41
- mockCreate.mockResolvedValue({
42
- content: [{
43
- type: 'text',
44
- text: JSON.stringify({
45
- id: 'linkedin:comment_post',
46
- platform: 'linkedin',
47
- name: 'Comment on Post',
48
- params: ['post_url', 'text'],
49
- steps: [
50
- { type: 'navigate', url: '{{params.post_url}}' },
51
- { type: 'find', selectors: ['.comment-box'], as: 'box' },
52
- { type: 'click', target: '{{box}}' },
53
- { type: 'type', target: '{{box}}', text: '{{params.text}}', humanDelay: true },
54
- { type: 'wait', condition: 'network_idle', timeout: 3000 },
55
- ],
56
- }),
57
- }],
58
- });
48
+ vi.resetModules();
59
49
  const mod = await import('@monoes/monobrowse');
60
50
  analyzePageForAction = mod.analyzePageForAction;
61
- });
62
- it('returns a valid ActionDef from mocked Claude response', async () => {
63
- const page = mockPage();
64
- // Pass a dummy key via options (Anthropic SDK is mocked no real call is made)
65
- const result = await analyzePageForAction(page, 'comment on a LinkedIn post', { apiKey: 'sk-test' });
51
+ }, 20000); // monolean: re-importing the workspace package can exceed the 10s default under full-suite parallel load
52
+ it('returns a valid ActionDef from mocked claude --print response', async () => {
53
+ await makeSpawnMock(JSON.stringify(VALID_ACTION_DEF));
54
+ const result = await analyzePageForAction(mockPage(), 'comment on a LinkedIn post');
66
55
  expect(result.id).toBe('linkedin:comment_post');
67
56
  expect(result.steps).toHaveLength(5);
68
57
  expect(result.params).toContain('text');
69
58
  });
70
- it('throws on invalid JSON from Claude', async () => {
71
- mockCreate.mockResolvedValueOnce({
72
- content: [{ type: 'text', text: 'not json at all' }],
73
- });
74
- const page = mockPage();
75
- await expect(analyzePageForAction(page, 'test', { apiKey: 'sk-test' })).rejects.toThrow('invalid JSON');
59
+ it('throws on invalid JSON from claude', async () => {
60
+ await makeSpawnMock('not json at all');
61
+ await expect(analyzePageForAction(mockPage(), 'test')).rejects.toThrow('invalid JSON');
76
62
  });
77
63
  it('throws when ActionDef is missing id', async () => {
78
- mockCreate.mockResolvedValueOnce({
79
- content: [{ type: 'text', text: JSON.stringify({ steps: [] }) }],
80
- });
81
- const page = mockPage();
82
- await expect(analyzePageForAction(page, 'test', { apiKey: 'sk-test' })).rejects.toThrow('invalid ActionDef');
64
+ await makeSpawnMock(JSON.stringify({ steps: [] }));
65
+ await expect(analyzePageForAction(mockPage(), 'test')).rejects.toThrow('invalid ActionDef');
83
66
  });
84
67
  });
85
68
  //# sourceMappingURL=browse-analyzer.test.js.map
@@ -7,6 +7,7 @@ type AgentRegistryEntry = {
7
7
  name: string;
8
8
  version: string;
9
9
  category: string;
10
+ description: string;
10
11
  capabilities: string[];
11
12
  taskTypes: string[];
12
13
  tools: string[];
@@ -32,6 +33,13 @@ type AgentRegistry = {
32
33
  * @returns The built AgentRegistry object.
33
34
  */
34
35
  export declare function buildRegistry(agentsRoot: string, outputPath?: string): AgentRegistry;
36
+ /**
37
+ * Compute the ordered list of agent-definition roots for `cwd`: extras
38
+ * (canonical, from MONOMIND_EXTRA_AGENT_PATHS or a sibling `agency-agents`
39
+ * dir) first, then the project's `.claude/agents`. Shared by CLI startup
40
+ * and `monomind doctor` so both build the registry the same way.
41
+ */
42
+ export declare function computeAgentRoots(cwd: string): string[];
35
43
  /**
36
44
  * Build a unified agent registry from multiple root directories, deduplicating
37
45
  * by slug. When the same slug appears in more than one root, the entry from the
@@ -132,6 +132,27 @@ function categoryFromPath(filePath, agentsRoot) {
132
132
  export function buildRegistry(agentsRoot, outputPath) {
133
133
  return buildUnifiedRegistry([agentsRoot], outputPath);
134
134
  }
135
+ /**
136
+ * Compute the ordered list of agent-definition roots for `cwd`: extras
137
+ * (canonical, from MONOMIND_EXTRA_AGENT_PATHS or a sibling `agency-agents`
138
+ * dir) first, then the project's `.claude/agents`. Shared by CLI startup
139
+ * and `monomind doctor` so both build the registry the same way.
140
+ */
141
+ export function computeAgentRoots(cwd) {
142
+ const devAgentsRoot = join(cwd, '.claude', 'agents');
143
+ const extraPaths = process.env.MONOMIND_EXTRA_AGENT_PATHS
144
+ ? process.env.MONOMIND_EXTRA_AGENT_PATHS.split(':').filter(Boolean)
145
+ : [];
146
+ const siblingExtraPath = join(cwd, '..', 'agency-agents');
147
+ if (extraPaths.length === 0) {
148
+ try {
149
+ if (statSync(siblingExtraPath).isDirectory())
150
+ extraPaths.push(siblingExtraPath);
151
+ }
152
+ catch { /* sibling path doesn't exist */ }
153
+ }
154
+ return [...extraPaths, devAgentsRoot];
155
+ }
135
156
  /**
136
157
  * Build a unified agent registry from multiple root directories, deduplicating
137
158
  * by slug. When the same slug appears in more than one root, the entry from the
@@ -178,6 +199,7 @@ export function buildUnifiedRegistry(roots, outputPath) {
178
199
  version: (typeof fm.version === 'string' ? fm.version : undefined) || '0.0.0',
179
200
  category: (typeof fm.category === 'string' ? fm.category : undefined) ||
180
201
  categoryFromPath(file, root),
202
+ description: typeof fm.description === 'string' ? fm.description : '',
181
203
  capabilities: toStringArray(fm.capabilities),
182
204
  taskTypes: toStringArray(fm.taskTypes ?? fm['task-types'] ?? fm.task_types),
183
205
  tools: toStringArray(fm.tools),
@@ -30,6 +30,24 @@ export function getDashboardServer(port = DEFAULT_PORT) {
30
30
  res.end(JSON.stringify(recentRuns));
31
31
  return;
32
32
  }
33
+ if (req.method === 'POST' && req.url === '/api/mastermind/event') {
34
+ const chunks = [];
35
+ req.on('data', (chunk) => chunks.push(chunk));
36
+ req.on('end', () => {
37
+ try {
38
+ const body = Buffer.concat(chunks).toString('utf8');
39
+ JSON.parse(body); // validate before broadcast
40
+ for (const client of clients) {
41
+ if (client.readyState === client.OPEN)
42
+ client.send(body);
43
+ }
44
+ }
45
+ catch { }
46
+ res.writeHead(200, { 'Content-Type': 'application/json' });
47
+ res.end('{"ok":true}');
48
+ });
49
+ return;
50
+ }
33
51
  res.writeHead(404);
34
52
  res.end();
35
53
  });