monomind 1.17.0 → 1.17.1
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/.claude/agents/engineering/engineering-security-engineer.md +1 -1
- package/.claude/commands/mastermind/_repeat.md +4 -0
- package/.claude/commands/mastermind/master.md +52 -1
- package/.claude/scheduled_tasks.lock +1 -1
- package/.claude/skills/mastermind/_repeat.md +2 -0
- package/package.json +1 -1
- package/packages/@monomind/cli/.claude/agents/engineering/engineering-security-engineer.md +1 -1
- package/packages/@monomind/cli/.claude/commands/mastermind/_repeat.md +4 -0
- package/packages/@monomind/cli/.claude/commands/mastermind/master.md +52 -1
- package/packages/@monomind/cli/.claude/skills/mastermind/_repeat.md +2 -0
- package/packages/@monomind/cli/dist/src/__tests__/browse-analyzer.test.js +42 -59
- package/packages/@monomind/cli/dist/src/browser/dashboard/server.js +18 -0
- package/packages/@monomind/cli/dist/src/browser/dashboard/ui.html +37 -125
- package/packages/@monomind/cli/dist/src/commands/agent-lifecycle.d.ts +17 -0
- package/packages/@monomind/cli/dist/src/commands/agent-lifecycle.js +320 -0
- package/packages/@monomind/cli/dist/src/commands/agent-ops.d.ts +9 -0
- package/packages/@monomind/cli/dist/src/commands/agent-ops.js +329 -0
- package/packages/@monomind/cli/dist/src/commands/agent.js +5 -907
- package/packages/@monomind/cli/dist/src/commands/analyze-ast.d.ts +26 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-ast.js +284 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-boundaries.d.ts +14 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-boundaries.js +295 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-diff.d.ts +8 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-diff.js +395 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-graph.d.ts +14 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-graph.js +304 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-imports.d.ts +11 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-imports.js +287 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-symbols.d.ts +14 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-symbols.js +302 -0
- package/packages/@monomind/cli/dist/src/commands/analyze.d.ts +38 -0
- package/packages/@monomind/cli/dist/src/commands/analyze.js +12 -1827
- package/packages/@monomind/cli/dist/src/commands/doctor-env-checks.d.ts +26 -0
- package/packages/@monomind/cli/dist/src/commands/doctor-env-checks.js +189 -0
- package/packages/@monomind/cli/dist/src/commands/doctor-project-checks.d.ts +19 -0
- package/packages/@monomind/cli/dist/src/commands/doctor-project-checks.js +388 -0
- package/packages/@monomind/cli/dist/src/commands/doctor.js +51 -942
- package/packages/@monomind/cli/dist/src/commands/hive-mind-comms.d.ts +11 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind-comms.js +242 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind-helpers.d.ts +35 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind-helpers.js +203 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind-ops.d.ts +8 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind-ops.js +233 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind-spawn.d.ts +12 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind-spawn.js +274 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind.js +10 -1129
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-commands.d.ts +4 -4
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-commands.js +19 -819
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-gaps.d.ts +7 -0
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-gaps.js +334 -0
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-routing.d.ts +7 -0
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-routing.js +399 -0
- package/packages/@monomind/cli/dist/src/commands/init-subcommands.d.ts +8 -0
- package/packages/@monomind/cli/dist/src/commands/init-subcommands.js +156 -0
- package/packages/@monomind/cli/dist/src/commands/init-upgrade.d.ts +6 -0
- package/packages/@monomind/cli/dist/src/commands/init-upgrade.js +203 -0
- package/packages/@monomind/cli/dist/src/commands/init-wizard.d.ts +6 -0
- package/packages/@monomind/cli/dist/src/commands/init-wizard.js +246 -0
- package/packages/@monomind/cli/dist/src/commands/init.js +6 -623
- package/packages/@monomind/cli/dist/src/commands/memory-admin.d.ts +10 -0
- package/packages/@monomind/cli/dist/src/commands/memory-admin.js +433 -0
- package/packages/@monomind/cli/dist/src/commands/memory-crud.d.ts +9 -0
- package/packages/@monomind/cli/dist/src/commands/memory-crud.js +342 -0
- package/packages/@monomind/cli/dist/src/commands/memory-list.d.ts +10 -0
- package/packages/@monomind/cli/dist/src/commands/memory-list.js +321 -0
- package/packages/@monomind/cli/dist/src/commands/memory-transfer.d.ts +9 -0
- package/packages/@monomind/cli/dist/src/commands/memory-transfer.js +372 -0
- package/packages/@monomind/cli/dist/src/commands/memory.d.ts +6 -0
- package/packages/@monomind/cli/dist/src/commands/memory.js +10 -1441
- package/packages/@monomind/cli/dist/src/commands/neural-core.d.ts +8 -0
- package/packages/@monomind/cli/dist/src/commands/neural-core.js +274 -0
- package/packages/@monomind/cli/dist/src/commands/neural-optimize.d.ts +7 -0
- package/packages/@monomind/cli/dist/src/commands/neural-optimize.js +332 -0
- package/packages/@monomind/cli/dist/src/commands/neural-registry.d.ts +7 -0
- package/packages/@monomind/cli/dist/src/commands/neural-registry.js +290 -0
- package/packages/@monomind/cli/dist/src/commands/neural.js +3 -974
- package/packages/@monomind/cli/dist/src/commands/platforms.js +327 -7
- package/packages/@monomind/cli/dist/src/commands/security-cve.d.ts +6 -0
- package/packages/@monomind/cli/dist/src/commands/security-cve.js +310 -0
- package/packages/@monomind/cli/dist/src/commands/security-misc.d.ts +9 -0
- package/packages/@monomind/cli/dist/src/commands/security-misc.js +293 -0
- package/packages/@monomind/cli/dist/src/commands/security-scan.d.ts +18 -0
- package/packages/@monomind/cli/dist/src/commands/security-scan.js +328 -0
- package/packages/@monomind/cli/dist/src/commands/security.js +3 -958
- package/packages/@monomind/cli/dist/src/commands/session.js +1 -1
- package/packages/@monomind/cli/dist/src/commands/swarm.js +23 -17
- package/packages/@monomind/cli/dist/src/mcp-tools/swarm-tools.js +77 -0
- package/packages/@monomind/cli/dist/src/parser.js +11 -6
- package/packages/@monomind/cli/dist/src/routing/llm-caller.js +1 -2
- package/packages/@monomind/cli/package.json +2 -3
- 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(
|
|
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
|
-
|
|
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":"
|
|
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.
|
|
3
|
+
"version": "1.17.1",
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
22
|
-
vi.
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
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('[]');
|
|
41
|
+
return Promise.resolve('[]');
|
|
35
42
|
}),
|
|
36
43
|
};
|
|
37
44
|
}
|
|
38
45
|
describe('analyzePageForAction', () => {
|
|
39
46
|
let analyzePageForAction;
|
|
40
47
|
beforeEach(async () => {
|
|
41
|
-
|
|
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
|
|
63
|
-
|
|
64
|
-
|
|
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
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
79
|
-
|
|
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
|
|
@@ -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
|
});
|