monomind 2.3.1 → 2.3.3

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 (42) hide show
  1. package/package.json +1 -1
  2. package/packages/@monomind/cli/.claude/commands/mastermind/_repeat.md +9 -9
  3. package/packages/@monomind/cli/.claude/commands/mastermind/approve.md +2 -2
  4. package/packages/@monomind/cli/.claude/commands/mastermind/architect.md +3 -3
  5. package/packages/@monomind/cli/.claude/commands/mastermind/createorg.md +4 -4
  6. package/packages/@monomind/cli/.claude/commands/mastermind/master.md +3 -3
  7. package/packages/@monomind/cli/.claude/commands/mastermind/repeat.md +5 -5
  8. package/packages/@monomind/cli/.claude/commands/mastermind/runorg.md +3 -3
  9. package/packages/@monomind/cli/.claude/commands/mastermind/stoporg.md +1 -1
  10. package/packages/@monomind/cli/.claude/helpers/control-start.cjs +52 -4
  11. package/packages/@monomind/cli/.claude/helpers/event-logger.cjs +9 -1
  12. package/packages/@monomind/cli/.claude/helpers/handlers/capture-handler.cjs +9 -1
  13. package/packages/@monomind/cli/.claude/helpers/handlers/route-handler.cjs +65 -0
  14. package/packages/@monomind/cli/.claude/helpers/handlers/session-restore-handler.cjs +59 -0
  15. package/packages/@monomind/cli/.claude/skills/mastermind-skills/_protocol.md +1 -1
  16. package/packages/@monomind/cli/.claude/skills/mastermind-skills/_repeat.md +3 -3
  17. package/packages/@monomind/cli/.claude/skills/mastermind-skills/adapters.md +1 -1
  18. package/packages/@monomind/cli/.claude/skills/mastermind-skills/agents.md +1 -1
  19. package/packages/@monomind/cli/.claude/skills/mastermind-skills/architect.md +8 -8
  20. package/packages/@monomind/cli/.claude/skills/mastermind-skills/backup.md +1 -1
  21. package/packages/@monomind/cli/.claude/skills/mastermind-skills/bootstrap.md +1 -1
  22. package/packages/@monomind/cli/.claude/skills/mastermind-skills/costs.md +1 -1
  23. package/packages/@monomind/cli/.claude/skills/mastermind-skills/heartbeat.md +2 -2
  24. package/packages/@monomind/cli/.claude/skills/mastermind-skills/idea.md +1 -1
  25. package/packages/@monomind/cli/.claude/skills/mastermind-skills/monitor.md +2 -2
  26. package/packages/@monomind/cli/.claude/skills/mastermind-skills/org-settings.md +1 -1
  27. package/packages/@monomind/cli/.claude/skills/mastermind-skills/plugins.md +1 -1
  28. package/packages/@monomind/cli/.claude/skills/mastermind-skills/runorg.md +28 -22
  29. package/packages/@monomind/cli/.claude/skills/mastermind-skills/stoporg.md +2 -2
  30. package/packages/@monomind/cli/dist/src/commands/cleanup.d.ts +14 -0
  31. package/packages/@monomind/cli/dist/src/commands/cleanup.js +99 -0
  32. package/packages/@monomind/cli/dist/src/commands/doctor-project-checks.d.ts +4 -0
  33. package/packages/@monomind/cli/dist/src/commands/doctor-project-checks.js +25 -0
  34. package/packages/@monomind/cli/dist/src/commands/doctor.js +3 -1
  35. package/packages/@monomind/cli/dist/src/knowledge/document-pipeline.js +39 -5
  36. package/packages/@monomind/cli/dist/src/memory/memory-bridge.js +11 -1
  37. package/packages/@monomind/cli/dist/src/orgrt/daemon.d.ts +17 -0
  38. package/packages/@monomind/cli/dist/src/orgrt/daemon.js +90 -1
  39. package/packages/@monomind/cli/dist/src/orgrt/session.d.ts +2 -0
  40. package/packages/@monomind/cli/dist/src/orgrt/session.js +5 -0
  41. package/packages/@monomind/cli/dist/src/ui/server.mjs +75 -0
  42. package/packages/@monomind/cli/package.json +2 -2
@@ -139,7 +139,7 @@ Emit `org:adapter:installed` event:
139
139
  ```bash
140
140
  REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
141
141
  CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
142
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
142
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
143
143
  -H "Content-Type: application/json" \
144
144
  -d "$(jq -cn --arg org "$org_name" --arg type "$adapter_type" --arg pkg "$package_name" \
145
145
  '{type:"org:adapter:installed",org:$org,adapter:$type,package:$pkg,ts:(now*1000|floor)}')" || true
@@ -126,7 +126,7 @@ Then emit `org:agent:hired` event to dashboard:
126
126
  ```bash
127
127
  REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
128
128
  CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
129
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
129
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
130
130
  -H "Content-Type: application/json" \
131
131
  -d "$(jq -cn --arg org "$org_name" --arg role "$agent_id" --arg title "$title" \
132
132
  '{type:"org:agent:hired",org:$org,role:$role,title:$title,ts:(now*1000|floor)}')" || true
@@ -67,7 +67,7 @@ If this skill is invoked directly (not by master):
67
67
  ```bash
68
68
  REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
69
69
  CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
70
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
70
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
71
71
  -H "Content-Type: application/json" \
72
72
  -d '{"type":"session:start","session":"<sessionId>","domain":"architect","ts":'"$(date +%s)"'000}' || true
73
73
  ```
@@ -104,7 +104,7 @@ If this skill is invoked directly (not by master):
104
104
  ```bash
105
105
  REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
106
106
  CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
107
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
107
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
108
108
  -H "Content-Type: application/json" \
109
109
  -d '{"type":"session:complete","session":"<sessionId>","domain":"architect","ts":'"$(date +%s)"'000}' || true
110
110
  ```
@@ -294,7 +294,7 @@ Then emit `agent:spawn` for the manager and spawn it. Before executing the curl
294
294
  ```bash
295
295
  REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
296
296
  CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
297
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
297
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
298
298
  -H "Content-Type: application/json" \
299
299
  -d '{"type":"agent:spawn","session":"<sessionId>","domain":"architect","agent":"<manager_agent_type lowercased and hyphenated, e.g. software-architect>","task":"coordinate architecture analysis","ts":'"$(date +%s)"'000}'
300
300
  ```
@@ -444,7 +444,7 @@ BEFORE spawning each agent, emit agent:spawn to the live dashboard. Before each
444
444
  \`\`\`bash
445
445
  REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
446
446
  CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
447
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
447
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
448
448
  -H "Content-Type: application/json" \
449
449
  -d '{"type":"agent:spawn","session":"<sessionId>","domain":"architect","agent":"<subagent_type lowercased and hyphenated, e.g. software-architect>","task":"<stream-description>","ts":'"$(date +%s)"'000}'
450
450
  \`\`\`
@@ -453,7 +453,7 @@ If handing off artifacts to another domain (e.g. build for refactoring implement
453
453
  \`\`\`bash
454
454
  REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
455
455
  CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
456
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
456
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
457
457
  -H "Content-Type: application/json" \
458
458
  -d '{"type":"intercom","session":"<sessionId>","from":"architect","to":"<domain>","msg":"<msg>","ts":'"$(date +%s)"'000}'
459
459
  \`\`\`
@@ -464,7 +464,7 @@ BEFORE returning, emit domain:complete to the live dashboard:
464
464
  \`\`\`bash
465
465
  REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
466
466
  CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
467
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
467
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
468
468
  -H "Content-Type: application/json" \
469
469
  -d '{"type":"domain:complete","session":"<sessionId>","domain":"architect","status":"<choose: complete | partial | blocked>","artifacts":[],"decisions":[],"ts":'"$(date +%s)"'000}'
470
470
  \`\`\`
@@ -552,7 +552,7 @@ Before executing the curl blocks below and when constructing the Task() descript
552
552
  ```bash
553
553
  REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
554
554
  CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
555
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
555
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
556
556
  -H "Content-Type: application/json" \
557
557
  -d '{"type":"agent:spawn","session":"<sessionId>","domain":"architect","agent":"<resolved agent slug, e.g. software-architect>","task":"<prompt>","ts":'"$(date +%s)"'000}'
558
558
  ```
@@ -568,7 +568,7 @@ Before executing the curl blocks below and when constructing the Task() descript
568
568
  ```bash
569
569
  REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
570
570
  CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
571
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
571
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
572
572
  -H "Content-Type: application/json" \
573
573
  -d '{"type":"domain:complete","session":"<sessionId>","domain":"architect","status":"complete","artifacts":[],"decisions":[],"ts":'"$(date +%s)"'000}'
574
574
  ```
@@ -93,7 +93,7 @@ Emit `org:backup:created` event:
93
93
  ```bash
94
94
  REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
95
95
  CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
96
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
96
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
97
97
  -H "Content-Type: application/json" \
98
98
  -d "$(jq -cn --arg org "$org_name" --arg file "$archiveName" \
99
99
  '{type:"org:backup:created",org:$org,file:$file,ts:(now*1000|floor)}')" || true
@@ -151,7 +151,7 @@ Emit `org:bootstrap:complete` event:
151
151
  ```bash
152
152
  REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
153
153
  CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
154
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
154
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
155
155
  -H "Content-Type: application/json" \
156
156
  -d "$(jq -cn --arg org "$org_name" --arg boss "$boss_role_id" \
157
157
  '{type:"org:bootstrap:complete",org:$org,boss:$boss,ts:(now*1000|floor)}')" || true
@@ -122,7 +122,7 @@ if [ "$budget" -gt 0 ] && [ -f "$stateFile" ]; then
122
122
  echo "ALERT: Org $org_name has used $(awk -v t=$total -v b=$budget 'BEGIN{printf "%.1f", t/b*100}')% of budget"
123
123
  REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
124
124
  CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
125
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
125
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
126
126
  -H "Content-Type: application/json" \
127
127
  -d "$(jq -cn --arg org "$org_name" --argjson total $total --argjson budget $budget \
128
128
  '{type:"org:budget:alert",org:$org,tokens_used:$total,budget:$budget,ts:(now*1000|floor)}')" || true
@@ -71,7 +71,7 @@ jq --arg id "$agent_id" --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg source "
71
71
 
72
72
  REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
73
73
  CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
74
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
74
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
75
75
  -H "Content-Type: application/json" \
76
76
  -d "$(jq -cn \
77
77
  --arg org "$org_name" \
@@ -123,7 +123,7 @@ HEARTBEAT PROCEDURE:
123
123
 
124
124
  4. Report completion — emit heartbeat:complete event:
125
125
  CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
126
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "Content-Type: application/json" \
126
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" -H "Content-Type: application/json" \
127
127
  -d "$(jq -cn --arg org "${org_name}" --arg role "${agent_id}" --arg title "${agentTitle}" \
128
128
  '{type:"org:heartbeat:complete",org:$org,role:$role,title:$title,ts:(now*1000|floor)}')" || true
129
129
 
@@ -1089,7 +1089,7 @@ if [ -n "$SESSION_ID" ]; then
1089
1089
  --argjson next_actions '["<next_action_1>","<next_action_2>"]' \
1090
1090
  '{domain:$domain,status:$status,artifacts:$artifacts,next_actions:$next_actions}' \
1091
1091
  > "$MONO_DIR/sessions/${SESSION_ID}/idea.json"
1092
- curl -s -o /dev/null -X POST "${CTRL_URL}/api/mastermind/event" \
1092
+ curl -s -o /dev/null -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
1093
1093
  -H "Content-Type: application/json" \
1094
1094
  -d "$(jq -cn --arg sid "$SESSION_ID" --arg status "<status>" \
1095
1095
  '{type:"domain:complete",session:$sid,domain:"idea",status:$status,ts:(now*1000|floor)}')" || true
@@ -384,7 +384,7 @@ jq --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" '.last_tick = $ts' "$cfg" > "$tmp"
384
384
  REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
385
385
  CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
386
386
  SESSION_ID="monitor-${name}-$(date -u +%Y%m%dT%H%M%S)"
387
- curl -s -o /dev/null -X POST "${CTRL_URL}/api/mastermind/event" \
387
+ curl -s -o /dev/null -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
388
388
  -H "Content-Type: application/json" \
389
389
  -d "$(jq -cn --arg sid "$SESSION_ID" --arg name "$name" \
390
390
  '{type:"monitor:tick",session:$sid,monitor:$name,ts:(now*1000|floor)}')" || true
@@ -959,7 +959,7 @@ jq '.stats.done += 1' "$cfg" > "$tmp" && mv "$tmp" "$cfg"
959
959
 
960
960
  **Emit dashboard event:**
961
961
  ```bash
962
- curl -s -o /dev/null -X POST "${CTRL_URL}/api/mastermind/event" \
962
+ curl -s -o /dev/null -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
963
963
  -H "Content-Type: application/json" \
964
964
  -d "$(jq -cn --arg sid "$SESSION_ID" --arg name "$name" --arg title "$task_title" \
965
965
  '{type:"monitor:task:done",session:$sid,monitor:$name,task:$title,ts:(now*1000|floor)}')" || true
@@ -161,7 +161,7 @@ Emit `org:settings:updated` event:
161
161
  ```bash
162
162
  REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
163
163
  CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
164
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
164
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
165
165
  -H "Content-Type: application/json" \
166
166
  -d "$(jq -cn --arg org "$org_name" --arg field "$field" --arg value "$value" \
167
167
  '{type:"org:settings:updated",org:$org,field:$field,value:$value,ts:(now*1000|floor)}')" || true
@@ -130,7 +130,7 @@ Emit `org:plugin:installed` event:
130
130
  ```bash
131
131
  REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
132
132
  CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
133
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
133
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
134
134
  -H "Content-Type: application/json" \
135
135
  -d "$(jq -cn --arg org "${org_name:-global}" --arg id "$pluginId" --arg pkg "$package_name" \
136
136
  '{type:"org:plugin:installed",org:$org,plugin:$id,package:$pkg,ts:(now*1000|floor)}')" || true
@@ -117,7 +117,7 @@ CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control
117
117
  ### 1.6.3 — Emit org:start event
118
118
 
119
119
  ```bash
120
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
120
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
121
121
  -H "Content-Type: application/json" \
122
122
  -d "$(jq -cn \
123
123
  --arg session "$sessionId" \
@@ -161,7 +161,7 @@ If `LOOP_STATUS == "active"` or `LOOP_STATUS == "paused"`:
161
161
 
162
162
  2. Emit `org:stop` to signal this iteration is pausing:
163
163
  ```bash
164
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
164
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
165
165
  -H "Content-Type: application/json" \
166
166
  -d "$(jq -cn \
167
167
  --arg org "$orgName" \
@@ -182,7 +182,7 @@ If `LOOP_STATUS == "active"` or `LOOP_STATUS == "paused"`:
182
182
  || python3 -c "import datetime; print(datetime.datetime.utcfromtimestamp($next_ts).strftime('%Y-%m-%dT%H:%M:%SZ'))")
183
183
  tmp="${orgFile}.tmp"
184
184
  jq --arg next "$next_iso" '.loop.next_run = $next' "$orgFile" > "$tmp" && mv "$tmp" "$orgFile"
185
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
185
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
186
186
  -H "Content-Type: application/json" \
187
187
  -d "$(jq -cn --arg org "$orgName" --arg next "$next_iso" --arg proj "$REPO_ROOT" \
188
188
  '{type:"org:loop:scheduled",org:$org,next_run:$next,project:$proj,ts:(now*1000|floor)}')" || true
@@ -312,10 +312,12 @@ fi
312
312
  # Resolve git-safe monomind directory
313
313
  REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
314
314
  CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
315
- GIT_COMMON=$(git rev-parse --git-common-dir 2>/dev/null || echo ".git")
315
+ GIT_COMMON=$(git rev-parse --git-common-dir 2>/dev/null || true)
316
+ # Non-git project: never fabricate a .git/ dir — fall back to .monomind
317
+ [ -z "$GIT_COMMON" ] && GIT_COMMON="__NOGIT__"
316
318
  if [[ "$GIT_COMMON" != /* ]]; then GIT_COMMON="$REPO_ROOT/$GIT_COMMON"; fi
317
319
  GIT_COMMON="${GIT_COMMON%/}"
318
- MONO_DIR="${GIT_COMMON}/monomind"
320
+ if [ "$GIT_COMMON" = "__NOGIT__" ] || [ "${GIT_COMMON#*__NOGIT__}" != "$GIT_COMMON" ]; then MONO_DIR="$REPO_ROOT/.monomind"; else MONO_DIR="${GIT_COMMON}/monomind"; fi
319
321
 
320
322
  # Create run file and write run:start
321
323
  runDir="${MONO_DIR}/orgs/${orgName}/runs"
@@ -329,7 +331,7 @@ jq -cn \
329
331
 
330
332
  # Emit session:start first — creates the chat tab session record and writes active-session.json
331
333
  # so capture-handler can tag all subsequent agent:spawn / agent:complete events with this session
332
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
334
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
333
335
  -H "Content-Type: application/json" \
334
336
  -d "$(jq -cn \
335
337
  --arg session "$sessionId" \
@@ -338,7 +340,7 @@ curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
338
340
  '{type:"session:start",session:$session,org:$org,prompt:$prompt,ts:(now*1000|floor)}')" || true
339
341
 
340
342
  # Emit org:start — must run here while CTRL_URL/runId/orgName are in scope
341
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
343
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
342
344
  -H "Content-Type: application/json" \
343
345
  -d "$(jq -cn \
344
346
  --arg session "$sessionId" \
@@ -457,7 +459,7 @@ APPROVAL GATE (when governance.policy is "board" or "strict"):
457
459
  '.approvals += [{"id":$id,"agent_id":$agent,"title":$title,"action":$action,"risk_level":$risk,"status":"pending","requested_at":(now|todate)}]' \
458
460
  "$approvalsFile" > "${approvalsFile}.tmp" && mv "${approvalsFile}.tmp" "$approvalsFile"
459
461
  # Emit approval request event so human sees it in dashboard
460
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "Content-Type: application/json" \
462
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" -H "Content-Type: application/json" \
461
463
  -d "$(jq -cn --arg org "${orgName}" --arg id "$approval_id" --arg title "<title>" \
462
464
  '{type:"org:approval:requested",org:$org,approval_id:$id,title:$title,ts:(now*1000|floor)}')" || true
463
465
  # Poll until approved or rejected (max 30 min).
@@ -478,11 +480,13 @@ DASHBOARD EVENT HELPER — resolve once at startup, reuse in every step:
478
480
  REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
479
481
  CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
480
482
  # Git-safe monomind dir — shared across all worktrees, survives branch switches:
481
- GIT_COMMON=$(git rev-parse --git-common-dir 2>/dev/null || echo ".git")
483
+ GIT_COMMON=$(git rev-parse --git-common-dir 2>/dev/null || true)
484
+ # Non-git project: never fabricate a .git/ dir — fall back to .monomind
485
+ [ -z "$GIT_COMMON" ] && GIT_COMMON="__NOGIT__"
482
486
  # Resolve to absolute path (git returns relative ".git" for main checkout)
483
487
  if [[ "$GIT_COMMON" != /* ]]; then GIT_COMMON="$REPO_ROOT/$GIT_COMMON"; fi
484
488
  GIT_COMMON="${GIT_COMMON%/}" # strip trailing slash if any
485
- MONO_DIR="${GIT_COMMON}/monomind"
489
+ if [ "$GIT_COMMON" = "__NOGIT__" ] || [ "${GIT_COMMON#*__NOGIT__}" != "$GIT_COMMON" ]; then MONO_DIR="$REPO_ROOT/.monomind"; else MONO_DIR="${GIT_COMMON}/monomind"; fi
486
490
  runFile="${MONO_DIR}/orgs/${orgName}/runs/${runId}.jsonl"
487
491
 
488
492
  ⚠️ DASHBOARD EVENTS ARE MANDATORY — run every curl below exactly as written.
@@ -497,7 +501,7 @@ If the file exists: write run:complete to run file, emit org:complete via curl,
497
501
  jq -cn --arg runId "${runId}" --arg org "${orgName}" \
498
502
  '{type:"run:complete",runId:$runId,org:$org,status:"stopped",ts:(now*1000|floor)}' >> "${runFile}" || true
499
503
  # REQUIRED — emit org:complete via curl (includes runId so it also gets appended):
500
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "Content-Type: application/json" \
504
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" -H "Content-Type: application/json" \
501
505
  -d "$(jq -cn --arg s "${sessionId}" --arg o "${orgName}" --arg rid "${runId}" --arg p "$REPO_ROOT" \
502
506
  '{type:"org:complete",session:$s,org:$o,runId:$rid,project:$p,ts:(now*1000|floor)}')" || true
503
507
 
@@ -511,7 +515,7 @@ OPERATING LOOP:
511
515
  if [ "$(echo "$_pending" | jq 'length')" -gt 0 ]; then
512
516
  echo "$_pending" | jq -r '.[] | .text // .msg // ""'
513
517
  jq '[.[] | if .status == "pending" then .status = "processed" else . end]' "$_threads" > "${_threads}.tmp" && mv "${_threads}.tmp" "$_threads" || true
514
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "Content-Type: application/json" \
518
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" -H "Content-Type: application/json" \
515
519
  -d "$(jq -cn --arg s "${sessionId}" --arg o "${orgName}" --arg rid "${runId}" \
516
520
  --arg msg "Received user message — adjusting plan if needed" \
517
521
  '{type:"org:comms",session:$s,org:$o,runId:$rid,from:"boss",to:"user",msg:$msg,ts:(now*1000|floor)}')" || true
@@ -529,12 +533,12 @@ OPERATING LOOP:
529
533
  monotask card move ${board_id} $CARD_ID ${doing_col}
530
534
  monotask card label add ${board_id} $CARD_ID "claimed"
531
535
  b) REQUIRED — emit org:comms to dashboard (include runId):
532
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "Content-Type: application/json" \
536
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" -H "Content-Type: application/json" \
533
537
  -d "$(jq -cn --arg s "${sessionId}" --arg o "${orgName}" --arg rid "${runId}" --arg p "$REPO_ROOT" \
534
538
  --arg role "<role_id>" --arg task "<card title>" \
535
539
  '{type:"org:comms",session:$s,org:$o,runId:$rid,from:"boss",to:$role,msg:("Assigning: "+$task),project:$p,ts:(now*1000|floor)}')" || true
536
540
  c) REQUIRED — emit org:agent:online before spawning (include runId):
537
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "Content-Type: application/json" \
541
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" -H "Content-Type: application/json" \
538
542
  -d "$(jq -cn --arg s "${sessionId}" --arg o "${orgName}" --arg rid "${runId}" --arg p "$REPO_ROOT" \
539
543
  --arg role "<role_id>" --arg title "<role title>" --arg at "<agent_type>" \
540
544
  '{type:"org:agent:online",session:$s,org:$o,runId:$rid,role:$role,title:$title,agent_type:$at,project:$p,ts:(now*1000|floor)}')" || true
@@ -549,23 +553,25 @@ OPERATING LOOP:
549
553
  DASHBOARD EVENTS — emit these at the start and end of your work (REQUIRED):
550
554
  REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
551
555
  CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
552
- GIT_COMMON=$(git rev-parse --git-common-dir 2>/dev/null || echo ".git")
556
+ GIT_COMMON=$(git rev-parse --git-common-dir 2>/dev/null || true)
557
+ # Non-git project: never fabricate a .git/ dir — fall back to .monomind
558
+ [ -z "$GIT_COMMON" ] && GIT_COMMON="__NOGIT__"
553
559
  if [[ "$GIT_COMMON" != /* ]]; then GIT_COMMON="$REPO_ROOT/$GIT_COMMON"; fi
554
560
  GIT_COMMON="${GIT_COMMON%/}"
555
- MONO_DIR="${GIT_COMMON}/monomind"
561
+ if [ "$GIT_COMMON" = "__NOGIT__" ] || [ "${GIT_COMMON#*__NOGIT__}" != "$GIT_COMMON" ]; then MONO_DIR="$REPO_ROOT/.monomind"; else MONO_DIR="${GIT_COMMON}/monomind"; fi
556
562
  runFile="${MONO_DIR}/orgs/${orgName}/runs/${runId}.jsonl"
557
563
  # On start — announce you are working:
558
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "Content-Type: application/json" \
564
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" -H "Content-Type: application/json" \
559
565
  -d "$(jq -cn --arg s "${sessionId}" --arg o "${orgName}" --arg rid "${runId}" \
560
566
  --arg from "<role_id>" --arg msg "Starting: <card title>" \
561
567
  '{type:"org:comms",session:$s,org:$o,runId:$rid,from:$from,to:"boss",msg:$msg,ts:(now*1000|floor)}')" || true
562
568
  # On completion — report back:
563
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "Content-Type: application/json" \
569
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" -H "Content-Type: application/json" \
564
570
  -d "$(jq -cn --arg s "${sessionId}" --arg o "${orgName}" --arg rid "${runId}" \
565
571
  --arg from "<role_id>" --arg msg "Completed: <one-sentence summary of output>" \
566
572
  '{type:"org:comms",session:$s,org:$o,runId:$rid,from:$from,to:"boss",msg:$msg,ts:(now*1000|floor)}')" || true
567
573
  # When your task is fully complete — signal offline:
568
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "Content-Type: application/json" \
574
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" -H "Content-Type: application/json" \
569
575
  -d "$(jq -cn --arg s "${sessionId}" --arg o "${orgName}" --arg rid "${runId}" \
570
576
  --arg from "<role_id>" \
571
577
  '{type:"org:agent:offline",session:$s,org:$o,runId:$rid,from:$from,ts:(now*1000|floor)}')" || true
@@ -588,7 +594,7 @@ OPERATING LOOP:
588
594
 
589
595
  6. Synthesize reports, make decisions, create new cards in Todo column based on progress toward goal.
590
596
  REQUIRED — after each decision, emit org:comms (include runId):
591
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "Content-Type: application/json" \
597
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" -H "Content-Type: application/json" \
592
598
  -d "$(jq -cn --arg s "${sessionId}" --arg o "${orgName}" --arg rid "${runId}" --arg p "$REPO_ROOT" \
593
599
  --arg msg "<one-sentence summary of decision or finding>" \
594
600
  '{type:"org:comms",session:$s,org:$o,runId:$rid,from:"boss",to:"all",msg:$msg,project:$p,ts:(now*1000|floor)}')" || true
@@ -601,7 +607,7 @@ OPERATING LOOP:
601
607
  echo "{\"type\":\"run:cycle:complete\",\"org\":\"${orgName}\",\"runId\":\"${runId}\",\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"pending\":${pending_count:-0}}" >> "$activityFile" 2>/dev/null || true
602
608
 
603
609
  8. REQUIRED — emit org:checkpoint with a one-sentence progress summary (include runId):
604
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "Content-Type: application/json" \
610
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" -H "Content-Type: application/json" \
605
611
  -d "$(jq -cn --arg s "${sessionId}" --arg o "${orgName}" --arg rid "${runId}" --arg p "$REPO_ROOT" \
606
612
  --arg prog "<one sentence: what was done this cycle and what is next>" \
607
613
  --argjson pend "${pending_count:-0}" \
@@ -705,7 +711,7 @@ If `caller` is not "command", follow _protocol.md Brain Write Procedure for doma
705
711
  When you create or modify a file that represents output of this org run, emit:
706
712
 
707
713
  ```bash
708
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
714
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
709
715
  -H "Content-Type: application/json" \
710
716
  -d "$(jq -cn \
711
717
  --arg s "${sessionId}" \
@@ -52,7 +52,7 @@ CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control
52
52
  npx -y monomind@latest org stop "${org_name}" \
53
53
  || { mkdir -p ".monomind/orgs/${org_name}"; date -u +%Y-%m-%dT%H:%M:%SZ > ".monomind/orgs/${org_name}/stop"; }
54
54
  # Also POST to the control server in case a dashboard-started instance is running
55
- curl -s -X POST "${CTRL_URL}/api/orgs/${org_name}/stop" >/dev/null 2>&1 || true
55
+ curl -s -X POST -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" "${CTRL_URL}/api/orgs/${org_name}/stop" >/dev/null 2>&1 || true
56
56
  echo "Stop requested for org '${org_name}' (v2 daemon exits within 2s)."
57
57
  ```
58
58
  - Exit.
@@ -92,7 +92,7 @@ REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
92
92
  CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
93
93
 
94
94
  # org:stop — signals dashboard to update status to stopped
95
- curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
95
+ curl -s -X POST "${CTRL_URL}/api/mastermind/event" -H "x-monomind-token: $(cat "${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/.monomind/dashboard-token" 2>/dev/null || true)" \
96
96
  -H "Content-Type: application/json" \
97
97
  -d "$(jq -cn \
98
98
  --arg session "${session_id:-manual}" \
@@ -11,6 +11,20 @@ interface StaleScratchItem {
11
11
  description: string;
12
12
  size: number;
13
13
  }
14
+ /**
15
+ * Find prunable entries under the per-project data base (default
16
+ * ~/.monomind/projects). Exported for tests — `baseDir`/`now` injectable.
17
+ *
18
+ * Classification per dir:
19
+ * - `origin.json` present and its recorded path still exists → keep the dir,
20
+ * but flag a leftover `lancedb/` subdir (dead since the SQLite engine swap).
21
+ * - `origin.json` present, recorded path gone → orphaned → prune.
22
+ * - no `origin.json` (pre-2.3.1 dirs can't prove their origin) → prune only
23
+ * when untouched for {@link UNKNOWN_DIR_MAX_AGE_MS} — or immediately with
24
+ * `--aggressive`, which treats unprovable dirs as junk (safe: every live
25
+ * project rewrites origin.json on its next memory access).
26
+ */
27
+ export declare function findOrphanedProjectData(baseDir: string, now: number, aggressive: boolean): StaleScratchItem[];
14
28
  /**
15
29
  * Find stale mastermind scratch under `.monomind/taskdev/` and `.monomind/loops/`.
16
30
  * Exported for tests. Never returns `progress.md` (the taskdev recovery ledger),
@@ -33,6 +33,63 @@ const KEEP_CONFIG_PATHS = [
33
33
  // monolean: manual flag only — upgrade path: invoke from the `cache` background worker so crashed-run scratch is pruned without anyone remembering the flag
34
34
  const SCRATCH_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000; // taskdev scratch older than this is stale
35
35
  const LOOP_STALE_GRACE_MS = 24 * 60 * 60 * 1000; // a live loop reschedules every <=1h; overdue by a day = abandoned
36
+ /** Orphaned per-project data (--data): ~/.monomind/projects/<slug> dirs whose
37
+ * source project is gone, plus dead lancedb/ dirs left by the pre-2.3.1 engine. */
38
+ const UNKNOWN_DIR_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1000;
39
+ /**
40
+ * Find prunable entries under the per-project data base (default
41
+ * ~/.monomind/projects). Exported for tests — `baseDir`/`now` injectable.
42
+ *
43
+ * Classification per dir:
44
+ * - `origin.json` present and its recorded path still exists → keep the dir,
45
+ * but flag a leftover `lancedb/` subdir (dead since the SQLite engine swap).
46
+ * - `origin.json` present, recorded path gone → orphaned → prune.
47
+ * - no `origin.json` (pre-2.3.1 dirs can't prove their origin) → prune only
48
+ * when untouched for {@link UNKNOWN_DIR_MAX_AGE_MS} — or immediately with
49
+ * `--aggressive`, which treats unprovable dirs as junk (safe: every live
50
+ * project rewrites origin.json on its next memory access).
51
+ */
52
+ export function findOrphanedProjectData(baseDir, now, aggressive) {
53
+ const out = [];
54
+ if (!existsSync(baseDir))
55
+ return out;
56
+ for (const name of readdirSync(baseDir)) {
57
+ if (name.startsWith('.'))
58
+ continue;
59
+ const dir = join(baseDir, name);
60
+ let mtime = 0;
61
+ try {
62
+ const st = lstatSync(dir);
63
+ if (!st.isDirectory())
64
+ continue;
65
+ mtime = st.mtimeMs;
66
+ }
67
+ catch {
68
+ continue;
69
+ }
70
+ const originFile = join(dir, 'origin.json');
71
+ let originPath = null;
72
+ let hasOrigin = false;
73
+ try {
74
+ originPath = String(JSON.parse(readFileSync(originFile, 'utf-8')).path ?? '');
75
+ hasOrigin = originPath.length > 0;
76
+ }
77
+ catch { /* no/corrupt marker */ }
78
+ if (hasOrigin && originPath && existsSync(originPath)) {
79
+ const lance = join(dir, 'lancedb');
80
+ if (existsSync(lance))
81
+ out.push({ path: lance, description: `dead lancedb store (project: ${originPath})`, size: 0 });
82
+ continue;
83
+ }
84
+ if (hasOrigin) {
85
+ out.push({ path: dir, description: `orphaned project data (origin gone: ${originPath})`, size: 0 });
86
+ }
87
+ else if (aggressive || now - mtime > UNKNOWN_DIR_MAX_AGE_MS) {
88
+ out.push({ path: dir, description: aggressive ? 'unverifiable project data (no origin marker)' : 'unverifiable project data (untouched >30d)', size: 0 });
89
+ }
90
+ }
91
+ return out;
92
+ }
36
93
  /**
37
94
  * Find stale mastermind scratch under `.monomind/taskdev/` and `.monomind/loops/`.
38
95
  * Exported for tests. Never returns `progress.md` (the taskdev recovery ledger),
@@ -208,6 +265,19 @@ export const cleanupCommand = {
208
265
  type: 'boolean',
209
266
  default: false,
210
267
  },
268
+ {
269
+ name: 'data',
270
+ short: 'd',
271
+ description: 'Prune orphaned per-project data in ~/.monomind/projects (gone projects, dead lancedb stores)',
272
+ type: 'boolean',
273
+ default: false,
274
+ },
275
+ {
276
+ name: 'aggressive',
277
+ description: 'With --data: also prune dirs that cannot prove their origin (pre-2.3.1, no origin marker)',
278
+ type: 'boolean',
279
+ default: false,
280
+ },
211
281
  ],
212
282
  examples: [
213
283
  {
@@ -232,6 +302,35 @@ export const cleanupCommand = {
232
302
  const keepConfig = ctx.flags['keep-config'] === true;
233
303
  const cwd = ctx.cwd;
234
304
  const dryRun = !force;
305
+ if (ctx.flags.data === true) {
306
+ const { homedir } = await import('os');
307
+ const baseDir = join(homedir(), '.monomind', 'projects');
308
+ output.writeln();
309
+ output.writeln(output.bold(dryRun ? 'Monomind Project-Data Cleanup (dry run)' : 'Monomind Project-Data Cleanup'));
310
+ output.writeln();
311
+ const orphans = findOrphanedProjectData(baseDir, Date.now(), ctx.flags.aggressive === true);
312
+ if (orphans.length === 0) {
313
+ output.writeln(output.info('No orphaned project data found.'));
314
+ return { success: true, message: 'Nothing to clean' };
315
+ }
316
+ let removed = 0;
317
+ for (const o of orphans) {
318
+ output.writeln(` ${dryRun ? 'would remove' : 'removing'}: ${o.path} (${o.description})`);
319
+ if (!dryRun) {
320
+ try {
321
+ rmSync(o.path, { recursive: true, force: true });
322
+ removed++;
323
+ }
324
+ catch { /* skip unremovable */ }
325
+ }
326
+ }
327
+ output.writeln();
328
+ if (dryRun) {
329
+ output.writeln(output.dim(` ${orphans.length} item(s). This was a dry run. Use --force to delete.`));
330
+ return { success: true, message: `Dry run: ${orphans.length} orphaned item(s) found`, data: { found: orphans, dryRun } };
331
+ }
332
+ return { success: true, message: `Removed ${removed} orphaned item(s)`, data: { found: orphans, removedCount: removed, dryRun } };
333
+ }
235
334
  if (ctx.flags.scratch === true) {
236
335
  const now = Date.now();
237
336
  output.writeln();
@@ -6,6 +6,10 @@ import type { HealthCheck } from './doctor-env-checks.js';
6
6
  export type { HealthCheck };
7
7
  export declare function checkConfigFile(): Promise<HealthCheck>;
8
8
  export declare function checkMemoryDatabase(): Promise<HealthCheck>;
9
+ /** Second Brain: when this project has an indexed knowledge base, semantic
10
+ * search needs the local embedding model. Its absence is silent (search
11
+ * degrades to keyword matching) — surface it here instead. */
12
+ export declare function checkSecondBrainModel(): Promise<HealthCheck>;
9
13
  export declare function checkApiKeys(): Promise<HealthCheck>;
10
14
  export declare function checkMcpServers(): Promise<HealthCheck>;
11
15
  export declare function checkMonograph(): Promise<HealthCheck>;
@@ -44,6 +44,31 @@ export async function checkMemoryDatabase() {
44
44
  }
45
45
  return { name: 'Memory Database', status: 'warn', message: 'Not initialized', fix: 'monomind memory configure --backend hybrid' };
46
46
  }
47
+ /** Second Brain: when this project has an indexed knowledge base, semantic
48
+ * search needs the local embedding model. Its absence is silent (search
49
+ * degrades to keyword matching) — surface it here instead. */
50
+ export async function checkSecondBrainModel() {
51
+ const name = 'Second Brain Model';
52
+ if (!existsSync(join(process.cwd(), '.monomind', 'knowledge', 'chunks.jsonl'))) {
53
+ return { name, status: 'pass', message: 'No knowledge base in this project (nothing to check)' };
54
+ }
55
+ let pkgDir;
56
+ try {
57
+ // exports map blocks package.json — resolve the entry file and walk up
58
+ const entry = await import.meta.resolve?.('@huggingface/transformers');
59
+ if (!entry)
60
+ throw new Error('resolver unavailable');
61
+ pkgDir = fileURLToPath(entry).replace(/\/dist\/.*$/, '');
62
+ }
63
+ catch {
64
+ return { name, status: 'warn', message: '@huggingface/transformers not installed — semantic search degraded to keyword matching', fix: 'reinstall monomind (the model dependency is optional and may have failed to build)' };
65
+ }
66
+ const modelCache = join(pkgDir, '.cache', 'Xenova');
67
+ if (existsSync(modelCache)) {
68
+ return { name, status: 'pass', message: 'Local embedding model cached — semantic search active' };
69
+ }
70
+ return { name, status: 'warn', message: 'Embedding model not downloaded yet — searches use keyword matching until it is', fix: 'run once while online: monomind doc search -q "warmup" (downloads ~90MB locally, one time)' };
71
+ }
47
72
  export async function checkApiKeys() {
48
73
  const keys = ['ANTHROPIC_API_KEY', 'CLAUDE_API_KEY', 'OPENAI_API_KEY'];
49
74
  const found = keys.filter(k => process.env[k]);
@@ -7,7 +7,7 @@
7
7
  import * as path from 'path';
8
8
  import { output } from '../output.js';
9
9
  import { checkNodeVersion, checkNpmVersion, checkGit, checkGitRepo, checkDiskSpace, checkBuildTools, checkVersionFreshness, checkClaudeCode, installClaudeCode, } from './doctor-env-checks.js';
10
- import { checkConfigFile, checkMemoryDatabase, checkApiKeys, checkMcpServers, checkMonograph, checkMonographFreshness, checkMonoesMemory, checkHelpersFresh, fixStaleHelpers, checkMonoesIntegration, checkGuidanceGates, checkGitignoreCoverage, checkAgentRegistry, checkMemoryProficiency, checkMetricsFreshness, checkSecurityAuditFindings, } from './doctor-project-checks.js';
10
+ import { checkConfigFile, checkMemoryDatabase, checkApiKeys, checkMcpServers, checkMonograph, checkMonographFreshness, checkMonoesMemory, checkHelpersFresh, fixStaleHelpers, checkMonoesIntegration, checkGuidanceGates, checkGitignoreCoverage, checkAgentRegistry, checkMemoryProficiency, checkMetricsFreshness, checkSecurityAuditFindings, checkSecondBrainModel, } from './doctor-project-checks.js';
11
11
  import { checkMonoesTools, fixMonoesTools } from './doctor-monoes-checks.js';
12
12
  function formatCheck(check) {
13
13
  const icon = check.status === 'pass' ? output.success('✓') :
@@ -65,6 +65,7 @@ export const doctorCommand = {
65
65
  checkMonograph, checkMonoesMemory, checkHelpersFresh, checkMonoesIntegration,
66
66
  checkGuidanceGates, checkAgentRegistry, checkGit, checkApiKeys,
67
67
  checkMemoryProficiency, checkMetricsFreshness, checkSecurityAuditFindings,
68
+ checkSecondBrainModel,
68
69
  ];
69
70
  const codeOnlyChecks = [
70
71
  checkGitRepo, checkMcpServers,
@@ -78,6 +79,7 @@ export const doctorCommand = {
78
79
  node: checkNodeVersion, npm: checkNpmVersion, claude: checkClaudeCode,
79
80
  config: checkConfigFile, memory: checkMemoryDatabase,
80
81
  api: checkApiKeys, git: checkGit, mcp: checkMcpServers, disk: checkDiskSpace,
82
+ 'second-brain': checkSecondBrainModel,
81
83
  typescript: checkBuildTools, monograph: checkMonograph,
82
84
  'graph-freshness': checkMonographFreshness, 'memory-pkg': checkMonoesMemory,
83
85
  helpers: checkHelpersFresh, monoes: checkMonoesIntegration,