@monoes/monomindcli 1.9.17 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/mastermind/_repeat.md +182 -39
- package/.claude/commands/mastermind/architect.md +17 -11
- package/.claude/commands/mastermind/brain.md +4 -0
- package/.claude/commands/mastermind/build.md +4 -0
- package/.claude/commands/mastermind/content.md +4 -0
- package/.claude/commands/mastermind/createorg.md +5 -3
- package/.claude/commands/mastermind/finance.md +4 -0
- package/.claude/commands/mastermind/idea.md +4 -0
- package/.claude/commands/mastermind/marketing.md +4 -0
- package/.claude/commands/mastermind/master.md +63 -37
- package/.claude/commands/mastermind/ops.md +4 -0
- package/.claude/commands/mastermind/release.md +4 -0
- package/.claude/commands/mastermind/research.md +4 -0
- package/.claude/commands/mastermind/review.md +4 -0
- package/.claude/commands/mastermind/runorg.md +5 -3
- package/.claude/commands/mastermind/sales.md +4 -0
- package/.claude/commands/mastermind/techport.md +9 -0
- package/.claude/commands/monomind/do.md +5 -1
- package/.claude/commands/monomind/idea.md +5 -1
- package/.claude/commands/monomind/improve.md +5 -1
- package/.claude/commands/monomind/repeat.md +85 -29
- package/.claude/commands/monomind/review.md +6 -2
- package/.claude/commands/monomind/understand.md +10 -8
- package/.claude/helpers/extras-registry.json +235 -235
- package/.claude/helpers/graphify-freshen.cjs +13 -1
- package/.claude/helpers/hook-handler.cjs +1 -1
- package/.claude/helpers/router.cjs +4 -1
- package/.claude/skills/mastermind/_protocol.md +28 -21
- package/.claude/skills/mastermind/access.md +236 -0
- package/.claude/skills/mastermind/activity.md +191 -0
- package/.claude/skills/mastermind/adapter-manager.md +259 -0
- package/.claude/skills/mastermind/adapters.md +204 -0
- package/.claude/skills/mastermind/agent-detail.md +242 -0
- package/.claude/skills/mastermind/agents.md +178 -0
- package/.claude/skills/mastermind/approval-detail.md +259 -0
- package/.claude/skills/mastermind/approve.md +181 -0
- package/.claude/skills/mastermind/architect.md +24 -8
- package/.claude/skills/mastermind/backup.md +197 -0
- package/.claude/skills/mastermind/bootstrap.md +190 -0
- package/.claude/skills/mastermind/budgets.md +237 -0
- package/.claude/skills/mastermind/companies.md +256 -0
- package/.claude/skills/mastermind/costs.md +151 -0
- package/.claude/skills/mastermind/createorg.md +23 -5
- package/.claude/skills/mastermind/diagnose.md +249 -0
- package/.claude/skills/mastermind/env.md +198 -0
- package/.claude/skills/mastermind/environments.md +250 -0
- package/.claude/skills/mastermind/export.md +324 -0
- package/.claude/skills/mastermind/goal-detail.md +255 -0
- package/.claude/skills/mastermind/goals.md +149 -0
- package/.claude/skills/mastermind/heartbeat.md +164 -0
- package/.claude/skills/mastermind/idea.md +250 -122
- package/.claude/skills/mastermind/import.md +281 -0
- package/.claude/skills/mastermind/inbox.md +214 -0
- package/.claude/skills/mastermind/instance-settings.md +315 -0
- package/.claude/skills/mastermind/instance.md +231 -0
- package/.claude/skills/mastermind/invite-landing.md +227 -0
- package/.claude/skills/mastermind/invites.md +254 -0
- package/.claude/skills/mastermind/issue-detail.md +291 -0
- package/.claude/skills/mastermind/issues.md +235 -0
- package/.claude/skills/mastermind/join-queue.md +170 -0
- package/.claude/skills/mastermind/liveness.md +392 -0
- package/.claude/skills/mastermind/memory.md +321 -0
- package/.claude/skills/mastermind/my-issues.md +146 -0
- package/.claude/skills/mastermind/new-agent.md +241 -0
- package/.claude/skills/mastermind/org-chart.md +207 -0
- package/.claude/skills/mastermind/org-settings.md +217 -0
- package/.claude/skills/mastermind/plan-to-tasks.md +136 -0
- package/.claude/skills/mastermind/plugin-manager.md +241 -0
- package/.claude/skills/mastermind/plugin-settings.md +273 -0
- package/.claude/skills/mastermind/plugins.md +190 -0
- package/.claude/skills/mastermind/profile.md +187 -0
- package/.claude/skills/mastermind/project-detail.md +249 -0
- package/.claude/skills/mastermind/project-workspace.md +244 -0
- package/.claude/skills/mastermind/projects.md +164 -0
- package/.claude/skills/mastermind/routine-detail.md +253 -0
- package/.claude/skills/mastermind/routines.md +202 -0
- package/.claude/skills/mastermind/runorg.md +74 -9
- package/.claude/skills/mastermind/search.md +186 -0
- package/.claude/skills/mastermind/secrets.md +199 -0
- package/.claude/skills/mastermind/skills.md +156 -0
- package/.claude/skills/mastermind/tasks.md +149 -0
- package/.claude/skills/mastermind/techport.md +5 -5
- package/.claude/skills/mastermind/threads.md +259 -0
- package/.claude/skills/mastermind/tree-control.md +250 -0
- package/.claude/skills/mastermind/wiki.md +314 -0
- package/.claude/skills/mastermind/workspace-detail.md +317 -0
- package/.claude/skills/mastermind/workspaces.md +261 -0
- package/.claude/skills/mastermind/worktree.md +187 -0
- package/dist/src/init/executor.js +8 -8
- package/dist/src/init/executor.js.map +1 -1
- package/dist/src/init/statusline-generator.d.ts.map +1 -1
- package/dist/src/init/statusline-generator.js +12 -0
- package/dist/src/init/statusline-generator.js.map +1 -1
- package/dist/src/ui/.monomind/data/ranked-context.json +1 -1
- package/dist/src/ui/.monomind/loops/mastermind-review-1778664132789.json +16 -0
- package/dist/src/ui/.monomind/sessions/current.json +5 -5
- package/dist/src/ui/.monomind/sessions/session-1776778451399.json +15 -0
- package/dist/src/ui/dashboard.html +3030 -181
- package/dist/src/ui/data/mastermind-events.jsonl +8 -0
- package/dist/src/ui/data/mastermind-sessions.json +1 -0
- package/dist/src/ui/server.mjs +738 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/.claude/skills/.monomind/data/ranked-context.json +0 -5
- package/.claude/skills/.monomind/sessions/current.json +0 -13
- package/.claude/skills/.monomind/sessions/session-1777829336455.json +0 -15
- package/.claude/skills/.monomind/sessions/session-1777831614725.json +0 -15
- package/.claude/skills/.monomind/sessions/session-1777832095857.json +0 -15
- package/.claude/skills/.monomind/sessions/session-1777839814183.json +0 -15
- package/.claude/skills/.monomind/sessions/session-1777841847131.json +0 -15
- package/.claude/skills/.monomind/sessions/session-1777843309463.json +0 -15
- package/.claude/skills/.monomind/sessions/session-1777880867159.json +0 -15
- package/.claude/skills/.monomind/sessions/session-1777881884593.json +0 -15
- package/.claude/skills/.monomind/sessions/session-1777884090471.json +0 -15
- package/.claude/skills/.monomind/sessions/session-1777884808221.json +0 -15
- package/.claude/skills/.monomind/sessions/session-1777885672155.json +0 -15
- package/.claude/skills/.monomind/sessions/session-1777886852818.json +0 -15
- package/.claude/skills/.monomind/sessions/session-1777896532690.json +0 -15
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mastermind-workspaces
|
|
3
|
+
description: Mastermind workspaces — manage isolated git worktree workspaces per project. List, attach, detach, stop, and prune workspaces. Grouped by project with running service counts and agent assignments.
|
|
4
|
+
type: domain-skill
|
|
5
|
+
default_mode: confirm
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Mastermind Workspaces
|
|
9
|
+
|
|
10
|
+
This skill is invoked by `mastermind:workspaces` or directly via `/mastermind:workspaces`.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Inputs
|
|
15
|
+
|
|
16
|
+
- `brain_context`: BRAIN CONTEXT block (injected by command, or loaded below if standalone)
|
|
17
|
+
- `org_name`: org to manage workspaces for (required)
|
|
18
|
+
- `action`: list | status | attach | detach | stop | prune
|
|
19
|
+
- `workspace_id`: workspace id (required for status/detach/stop)
|
|
20
|
+
- `project_id`: project to filter by (optional for list)
|
|
21
|
+
- `agent_id`: agent to assign to workspace (required for attach)
|
|
22
|
+
- `worktree_path`: filesystem path of the worktree (required for attach)
|
|
23
|
+
- `caller`: command | master
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Workspace Model
|
|
28
|
+
|
|
29
|
+
A workspace is a git worktree assigned to a project, optionally running services and assigned to an agent.
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"id": "ws-abc123",
|
|
34
|
+
"project_id": "project-slug",
|
|
35
|
+
"agent_id": "backend-dev",
|
|
36
|
+
"worktree_path": "/tmp/monomind/worktrees/project-slug-abc123",
|
|
37
|
+
"branch": "feat/my-feature",
|
|
38
|
+
"status": "active",
|
|
39
|
+
"services": ["dev-server", "db-watcher"],
|
|
40
|
+
"createdAt": "2026-01-01T00:00:00Z",
|
|
41
|
+
"lastActiveAt": "2026-01-02T00:00:00Z"
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Step 0 — Brain Load (standalone only)
|
|
48
|
+
|
|
49
|
+
If `caller` is not "command", load brain context following _protocol.md Brain Load Procedure with namespace: `ops`.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Step 1 — Load Workspace Data
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
orgFile=".monomind/orgs/${org_name}.json"
|
|
57
|
+
[ ! -f "$orgFile" ] && { echo "ERROR: Org '${org_name}' not found."; exit 1; }
|
|
58
|
+
|
|
59
|
+
wsFile=".monomind/orgs/${org_name}-workspaces.json"
|
|
60
|
+
[ ! -f "$wsFile" ] && echo '{"workspaces":[]}' > "$wsFile"
|
|
61
|
+
|
|
62
|
+
projectsFile=".monomind/orgs/${org_name}-projects.json"
|
|
63
|
+
worktreeRegistry=".monomind/orgs/${org_name}-worktrees.json"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Step 2 — Execute Action
|
|
69
|
+
|
|
70
|
+
### list (default)
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
echo "WORKSPACES — org: $org_name"
|
|
74
|
+
[ -n "$project_id" ] && echo "(filtered by project: $project_id)"
|
|
75
|
+
echo "────────────────────────────────────────────────────────"
|
|
76
|
+
|
|
77
|
+
total=$(jq '.workspaces | length' "$wsFile")
|
|
78
|
+
|
|
79
|
+
if [ "$total" -eq 0 ]; then
|
|
80
|
+
echo " No workspaces. Use --action attach to register one."
|
|
81
|
+
else
|
|
82
|
+
# Group by project
|
|
83
|
+
projects=$(jq -r --arg pid "${project_id:-}" '
|
|
84
|
+
.workspaces |
|
|
85
|
+
if $pid != "" then map(select(.project_id == $pid)) else . end |
|
|
86
|
+
[.[].project_id] | unique[]
|
|
87
|
+
' "$wsFile")
|
|
88
|
+
|
|
89
|
+
while IFS= read -r proj; do
|
|
90
|
+
[ -z "$proj" ] && continue
|
|
91
|
+
projName=$([ -f "$projectsFile" ] && jq -r --arg pid "$proj" '.projects[] | select(.id == $pid) | .name // $pid' "$projectsFile" || echo "$proj")
|
|
92
|
+
echo ""
|
|
93
|
+
echo " PROJECT: $projName"
|
|
94
|
+
echo " ──────────────────────────────────────────────────"
|
|
95
|
+
printf " %-18s %-14s %-18s %-12s %-6s %s\n" "ID" "STATUS" "AGENT" "BRANCH" "SVCS" "PATH"
|
|
96
|
+
|
|
97
|
+
jq -r --arg pid "$proj" '.workspaces[] | select(.project_id == $pid) |
|
|
98
|
+
[.id, (.status // "unknown"), (.agent_id // "(none)"),
|
|
99
|
+
(.branch // "?"), ((.services // []) | length | tostring),
|
|
100
|
+
(.worktree_path // "-")]
|
|
101
|
+
| @tsv' "$wsFile" | while IFS=$'\t' read -r id st ag br svc path; do
|
|
102
|
+
printf " %-18s %-14s %-18s %-12s %-6s %s\n" "$id" "$st" "$ag" "$br" "$svc" "$path"
|
|
103
|
+
done
|
|
104
|
+
done <<< "$projects"
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
echo ""
|
|
108
|
+
echo "Total: $total workspace(s)"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### status
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
[ -z "$workspace_id" ] && { echo "ERROR: --workspace-id required."; exit 1; }
|
|
115
|
+
wsDef=$(jq -r --arg id "$workspace_id" '.workspaces[] | select(.id == $id)' "$wsFile")
|
|
116
|
+
[ -z "$wsDef" ] && { echo "ERROR: Workspace '$workspace_id' not found."; exit 1; }
|
|
117
|
+
|
|
118
|
+
echo "WORKSPACE STATUS — $workspace_id"
|
|
119
|
+
echo "────────────────────────────────────────────────────────"
|
|
120
|
+
echo "$wsDef" | jq -r '
|
|
121
|
+
" ID: \(.id)",
|
|
122
|
+
" Project: \(.project_id // "-")",
|
|
123
|
+
" Agent: \(.agent_id // "(unassigned)")",
|
|
124
|
+
" Branch: \(.branch // "?")",
|
|
125
|
+
" Status: \(.status // "unknown")",
|
|
126
|
+
" Created: \(.createdAt // "-")",
|
|
127
|
+
" Last active: \(.lastActiveAt // "-")",
|
|
128
|
+
" Path: \(.worktree_path // "-")"
|
|
129
|
+
'
|
|
130
|
+
|
|
131
|
+
# Check if worktree path exists
|
|
132
|
+
path=$(echo "$wsDef" | jq -r '.worktree_path // ""')
|
|
133
|
+
if [ -n "$path" ]; then
|
|
134
|
+
if [ -d "$path" ]; then
|
|
135
|
+
echo ""
|
|
136
|
+
echo " Worktree: EXISTS at $path"
|
|
137
|
+
branch=$(git -C "$path" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "?")
|
|
138
|
+
echo " Git branch: $branch"
|
|
139
|
+
dirty=$(git -C "$path" status --porcelain 2>/dev/null | wc -l | tr -d ' ')
|
|
140
|
+
[ "$dirty" -gt 0 ] && echo " Uncommitted changes: $dirty file(s)"
|
|
141
|
+
else
|
|
142
|
+
echo " WARNING: Worktree path does not exist: $path"
|
|
143
|
+
fi
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
echo ""
|
|
147
|
+
echo "Services:"
|
|
148
|
+
echo "$wsDef" | jq -r '(.services // [])[] | " · \(.)"' || echo " (none)"
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### attach
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
[ -z "$project_id" ] && { echo "ERROR: --project-id required."; exit 1; }
|
|
155
|
+
[ -z "$worktree_path" ] && { echo "ERROR: --worktree-path required."; exit 1; }
|
|
156
|
+
|
|
157
|
+
[ ! -d "$worktree_path" ] && { echo "ERROR: Path does not exist: $worktree_path"; exit 1; }
|
|
158
|
+
|
|
159
|
+
wsId="ws-$(openssl rand -hex 4 2>/dev/null || python3 -c 'import secrets; print(secrets.token_hex(4))')"
|
|
160
|
+
branch=$(git -C "$worktree_path" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
|
|
161
|
+
ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
162
|
+
|
|
163
|
+
tmp="${wsFile}.tmp"
|
|
164
|
+
jq --arg id "$wsId" \
|
|
165
|
+
--arg pid "$project_id" \
|
|
166
|
+
--arg ag "${agent_id:-}" \
|
|
167
|
+
--arg path "$worktree_path" \
|
|
168
|
+
--arg branch "$branch" \
|
|
169
|
+
--arg ts "$ts" \
|
|
170
|
+
'.workspaces += [{"id":$id,"project_id":$pid,
|
|
171
|
+
"agent_id":(if $ag != "" then $ag else null end),
|
|
172
|
+
"worktree_path":$path,"branch":$branch,
|
|
173
|
+
"status":"active","services":[],"createdAt":$ts,"lastActiveAt":$ts}]' \
|
|
174
|
+
"$wsFile" > "$tmp" && mv "$tmp" "$wsFile"
|
|
175
|
+
|
|
176
|
+
echo "Workspace attached: $wsId"
|
|
177
|
+
echo " Project: $project_id"
|
|
178
|
+
echo " Agent: ${agent_id:-(unassigned)}"
|
|
179
|
+
echo " Branch: $branch"
|
|
180
|
+
echo " Path: $worktree_path"
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### detach
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
[ -z "$workspace_id" ] && { echo "ERROR: --workspace-id required."; exit 1; }
|
|
187
|
+
tmp="${wsFile}.tmp"
|
|
188
|
+
jq --arg id "$workspace_id" \
|
|
189
|
+
'.workspaces = [.workspaces[] | if .id == $id then .status = "detached" else . end]' \
|
|
190
|
+
"$wsFile" > "$tmp" && mv "$tmp" "$wsFile"
|
|
191
|
+
echo "Workspace '$workspace_id' → detached. The worktree is preserved on disk."
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### stop
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
[ -z "$workspace_id" ] && { echo "ERROR: --workspace-id required."; exit 1; }
|
|
198
|
+
wsDef=$(jq -r --arg id "$workspace_id" '.workspaces[] | select(.id == $id)' "$wsFile")
|
|
199
|
+
[ -z "$wsDef" ] && { echo "ERROR: Workspace '$workspace_id' not found."; exit 1; }
|
|
200
|
+
|
|
201
|
+
services=$(echo "$wsDef" | jq -r '(.services // [])[]')
|
|
202
|
+
if [ -n "$services" ]; then
|
|
203
|
+
echo "Stopping services for workspace $workspace_id…"
|
|
204
|
+
while IFS= read -r svc; do
|
|
205
|
+
echo " Stopping: $svc"
|
|
206
|
+
done <<< "$services"
|
|
207
|
+
fi
|
|
208
|
+
|
|
209
|
+
tmp="${wsFile}.tmp"
|
|
210
|
+
jq --arg id "$workspace_id" --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
|
211
|
+
'.workspaces = [.workspaces[] | if .id == $id then .status = "stopped" | .services = [] | .lastActiveAt = $ts else . end]' \
|
|
212
|
+
"$wsFile" > "$tmp" && mv "$tmp" "$wsFile"
|
|
213
|
+
|
|
214
|
+
echo "Workspace '$workspace_id' stopped."
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### prune
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
echo "Pruning stopped/detached workspaces for org '$org_name'…"
|
|
221
|
+
|
|
222
|
+
before=$(jq '.workspaces | length' "$wsFile")
|
|
223
|
+
removed=0
|
|
224
|
+
orphaned=0
|
|
225
|
+
|
|
226
|
+
tmp="${wsFile}.tmp"
|
|
227
|
+
jq '.workspaces = [.workspaces[] | select(.status != "stopped" and .status != "detached")]' \
|
|
228
|
+
"$wsFile" > "$tmp" && mv "$tmp" "$wsFile"
|
|
229
|
+
|
|
230
|
+
after=$(jq '.workspaces | length' "$wsFile")
|
|
231
|
+
removed=$((before - after))
|
|
232
|
+
|
|
233
|
+
# Find orphaned worktrees (path gone)
|
|
234
|
+
while IFS= read -r path; do
|
|
235
|
+
[ -z "$path" ] || [ "$path" = "null" ] && continue
|
|
236
|
+
[ ! -d "$path" ] && orphaned=$((orphaned + 1))
|
|
237
|
+
done < <(jq -r '.workspaces[].worktree_path // ""' "$wsFile")
|
|
238
|
+
|
|
239
|
+
echo " Removed $removed stopped/detached workspace record(s)."
|
|
240
|
+
[ "$orphaned" -gt 0 ] && echo " WARNING: $orphaned workspace(s) have missing worktree paths. Run --action status to investigate."
|
|
241
|
+
echo "Done. Active workspaces: $(jq '.workspaces | length' "$wsFile")"
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Step 3 — Return Output
|
|
247
|
+
|
|
248
|
+
```yaml
|
|
249
|
+
domain: ops
|
|
250
|
+
status: complete
|
|
251
|
+
action: <action>
|
|
252
|
+
org: <org_name>
|
|
253
|
+
workspace_id: <id if applicable>
|
|
254
|
+
workspaces_total: <N>
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## Step 4 — Brain Write (standalone only)
|
|
260
|
+
|
|
261
|
+
If `caller` is not "command", follow _protocol.md Brain Write Procedure for domain `ops`.
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mastermind-worktree
|
|
3
|
+
description: Mastermind worktree — create isolated git worktrees for org agents. Each agent gets its own branch and working directory, preventing conflicts when multiple agents edit code simultaneously.
|
|
4
|
+
type: domain-skill
|
|
5
|
+
default_mode: confirm
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Mastermind Worktree
|
|
9
|
+
|
|
10
|
+
This skill is invoked by `mastermind:worktree` or directly via `/mastermind:worktree`.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Inputs
|
|
15
|
+
|
|
16
|
+
- `brain_context`: BRAIN CONTEXT block
|
|
17
|
+
- `org_name`: org whose agents need worktrees
|
|
18
|
+
- `action`: list | create | assign | merge | cleanup | status
|
|
19
|
+
- `agent_id`: role id of the agent to create a worktree for
|
|
20
|
+
- `base_branch`: branch to create worktree from (default: current HEAD)
|
|
21
|
+
- `branch_name`: explicit branch name (default: `org/<org_name>/<agent_id>/<YYYYMMDD-HHMMSS>`)
|
|
22
|
+
- `worktree_path`: explicit path (default: `.monomind/worktrees/<org_name>/<agent_id>`)
|
|
23
|
+
- `caller`: command | master
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Step 0 — Brain Load (standalone only)
|
|
28
|
+
|
|
29
|
+
If `caller` is not "command", load brain context following _protocol.md Brain Load Procedure with namespace: `ops`.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Concept
|
|
34
|
+
|
|
35
|
+
When multiple agents in an org need to edit files concurrently (e.g. a content writer, designer, and developer all working on different parts of a codebase), git worktrees give each agent its own checkout of the repo on a dedicated branch. Changes are isolated until the org's boss (or a human) merges them.
|
|
36
|
+
|
|
37
|
+
Worktree registry is stored in `.monomind/orgs/<org_name>-worktrees.json`.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Step 1 — Load Worktree Registry
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
orgFile=".monomind/orgs/${org_name}.json"
|
|
45
|
+
worktreesFile=".monomind/orgs/${org_name}-worktrees.json"
|
|
46
|
+
[ ! -f "$worktreesFile" ] && echo '{"worktrees":[]}' > "$worktreesFile"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Step 2 — Execute Action
|
|
52
|
+
|
|
53
|
+
### list (default)
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
echo "WORKTREES — org: $org_name"
|
|
57
|
+
echo "──────────────────────────────────────────────"
|
|
58
|
+
git worktree list 2>/dev/null | head -20
|
|
59
|
+
|
|
60
|
+
echo ""
|
|
61
|
+
echo "REGISTRY:"
|
|
62
|
+
jq -r '.worktrees[] | "[\(.agent_id)] branch=\(.branch) path=\(.path) created=\(.created_at)"' \
|
|
63
|
+
"$worktreesFile" 2>/dev/null || echo "No registered worktrees."
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### create
|
|
67
|
+
|
|
68
|
+
Create a worktree for a specific agent:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
agentConfig=$(jq --arg id "$agent_id" '.roles[] | select(.id == $id)' "$orgFile")
|
|
72
|
+
[ -z "$agentConfig" ] && { echo "ERROR: Agent '$agent_id' not found in org."; exit 1; }
|
|
73
|
+
|
|
74
|
+
# Resolve branch name
|
|
75
|
+
timestamp=$(date +%Y%m%d-%H%M%S)
|
|
76
|
+
branch="${branch_name:-org/${org_name}/${agent_id}/${timestamp}}"
|
|
77
|
+
wtPath="${worktree_path:-.monomind/worktrees/${org_name}/${agent_id}}"
|
|
78
|
+
|
|
79
|
+
# Create the worktree
|
|
80
|
+
git worktree add -b "$branch" "$wtPath" "${base_branch:-HEAD}" 2>&1
|
|
81
|
+
if [ $? -ne 0 ]; then
|
|
82
|
+
echo "ERROR: Failed to create worktree. Check that git is initialized and the base branch exists."
|
|
83
|
+
exit 1
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
echo "Created worktree: $wtPath (branch: $branch)"
|
|
87
|
+
|
|
88
|
+
# Register in worktrees file
|
|
89
|
+
tmp="${worktreesFile}.tmp"
|
|
90
|
+
jq --arg agent "$agent_id" \
|
|
91
|
+
--arg branch "$branch" \
|
|
92
|
+
--arg path "$wtPath" \
|
|
93
|
+
--arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
|
94
|
+
'.worktrees = [.worktrees[] | select(.agent_id != $agent)] +
|
|
95
|
+
[{"agent_id":$agent,"branch":$branch,"path":$path,"status":"active","created_at":$ts}]' \
|
|
96
|
+
"$worktreesFile" > "$tmp" && mv "$tmp" "$worktreesFile"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Include worktree path in agent prompt when spawning:
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
WORKTREE: Your isolated working directory is ${wtPath}
|
|
103
|
+
All file edits should happen inside this directory.
|
|
104
|
+
Branch: ${branch}
|
|
105
|
+
To view your changes: git -C "${wtPath}" diff
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### merge
|
|
109
|
+
|
|
110
|
+
Merge a completed agent's worktree branch back to the base:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
wt=$(jq --arg id "$agent_id" '.worktrees[] | select(.agent_id == $id)' "$worktreesFile")
|
|
114
|
+
branch=$(echo "$wt" | jq -r '.branch')
|
|
115
|
+
wtPath=$(echo "$wt" | jq -r '.path')
|
|
116
|
+
|
|
117
|
+
echo "Merging branch $branch..."
|
|
118
|
+
git merge "$branch" --no-ff -m "org($org_name): merge $agent_id work from $branch"
|
|
119
|
+
if [ $? -eq 0 ]; then
|
|
120
|
+
echo "Merge successful. Removing worktree..."
|
|
121
|
+
git worktree remove "$wtPath" --force 2>/dev/null || true
|
|
122
|
+
git branch -d "$branch" 2>/dev/null || true
|
|
123
|
+
# Update registry
|
|
124
|
+
tmp="${worktreesFile}.tmp"
|
|
125
|
+
jq --arg id "$agent_id" \
|
|
126
|
+
'.worktrees = [.worktrees[] | if .agent_id == $id then .status = "merged" | .merged_at = (now|todate) else . end]' \
|
|
127
|
+
"$worktreesFile" > "$tmp" && mv "$tmp" "$worktreesFile"
|
|
128
|
+
echo "Worktree merged and removed."
|
|
129
|
+
else
|
|
130
|
+
echo "ERROR: Merge conflict on $branch. Resolve manually then run cleanup."
|
|
131
|
+
fi
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### status
|
|
135
|
+
|
|
136
|
+
Show diff summary for all active agent worktrees:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
jq -r '.worktrees[] | select(.status == "active") | "\(.agent_id) \(.path) \(.branch)"' \
|
|
140
|
+
"$worktreesFile" | while read -r agent path branch; do
|
|
141
|
+
echo "[$agent] branch=$branch"
|
|
142
|
+
git -C "$path" diff --stat 2>/dev/null | tail -3
|
|
143
|
+
echo ""
|
|
144
|
+
done
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### cleanup
|
|
148
|
+
|
|
149
|
+
Remove all merged or abandoned worktrees:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
git worktree prune 2>/dev/null
|
|
153
|
+
jq -r '.worktrees[] | select(.status != "active") | .path' "$worktreesFile" | while read -r path; do
|
|
154
|
+
[ -d "$path" ] && git worktree remove "$path" --force 2>/dev/null || true
|
|
155
|
+
done
|
|
156
|
+
echo "Pruned stale worktrees."
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Integration with runorg / heartbeat
|
|
162
|
+
|
|
163
|
+
When a boss agent assigns code-editing work to an agent, it should:
|
|
164
|
+
|
|
165
|
+
1. Call `/mastermind:worktree --action create --agent-id <role_id>`
|
|
166
|
+
2. Include the worktree path in the agent's task prompt
|
|
167
|
+
3. When the agent marks the task Done, call `/mastermind:worktree --action merge --agent-id <role_id>`
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Step 3 — Return Output
|
|
172
|
+
|
|
173
|
+
```yaml
|
|
174
|
+
domain: ops
|
|
175
|
+
status: complete
|
|
176
|
+
action: <action>
|
|
177
|
+
org: <org_name>
|
|
178
|
+
agent_id: <agent_id if applicable>
|
|
179
|
+
branch: <branch if applicable>
|
|
180
|
+
worktree_path: <path if applicable>
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Step 4 — Brain Write (standalone only)
|
|
186
|
+
|
|
187
|
+
If `caller` is not "command", follow _protocol.md Brain Write Procedure for domain `ops`.
|
|
@@ -181,7 +181,7 @@ const DIRECTORIES = {
|
|
|
181
181
|
* Execute initialization
|
|
182
182
|
*/
|
|
183
183
|
/**
|
|
184
|
-
* Remove legacy
|
|
184
|
+
* Remove legacy ruv-swarm configuration from existing project files.
|
|
185
185
|
* Safe to call even if no legacy config exists.
|
|
186
186
|
*/
|
|
187
187
|
function cleanupLegacyTools(targetDir) {
|
|
@@ -224,29 +224,29 @@ function cleanupLegacyTools(targetDir) {
|
|
|
224
224
|
}
|
|
225
225
|
catch { /* non-fatal */ }
|
|
226
226
|
}
|
|
227
|
-
// Clean
|
|
227
|
+
// Clean ruv-swarm from .claude/settings.json hooks and fix MCP package name
|
|
228
228
|
const settingsPath = path.join(targetDir, '.claude', 'settings.json');
|
|
229
229
|
if (fs.existsSync(settingsPath)) {
|
|
230
230
|
try {
|
|
231
231
|
const raw = fs.readFileSync(settingsPath, 'utf-8');
|
|
232
232
|
const settings = JSON.parse(raw);
|
|
233
233
|
let settingsChanged = false;
|
|
234
|
-
if (raw.includes('
|
|
235
|
-
// Remove
|
|
234
|
+
if (raw.includes('ruv-swarm')) {
|
|
235
|
+
// Remove legacy ruv-swarm hook entries from all hook arrays
|
|
236
236
|
const hookKeys = ['PreToolUse', 'PostToolUse', 'UserPromptSubmit', 'SessionStart', 'SessionEnd', 'Stop', 'SubagentStart', 'SubagentStop', 'PreCompact'];
|
|
237
237
|
for (const key of hookKeys) {
|
|
238
238
|
if (Array.isArray(settings.hooks?.[key])) {
|
|
239
239
|
const before = settings.hooks[key].length;
|
|
240
240
|
settings.hooks[key] = settings.hooks[key].filter((entry) => {
|
|
241
241
|
const str = JSON.stringify(entry);
|
|
242
|
-
return !str.includes('
|
|
242
|
+
return !str.includes('ruv-swarm');
|
|
243
243
|
});
|
|
244
244
|
if (settings.hooks[key].length !== before)
|
|
245
245
|
settingsChanged = true;
|
|
246
246
|
}
|
|
247
247
|
}
|
|
248
248
|
if (settingsChanged) {
|
|
249
|
-
cleaned.push('.claude/settings.json: removed
|
|
249
|
+
cleaned.push('.claude/settings.json: removed ruv-swarm hooks');
|
|
250
250
|
}
|
|
251
251
|
}
|
|
252
252
|
// Fix wrong MCP package name in mcpServers
|
|
@@ -283,7 +283,7 @@ export async function executeInit(options) {
|
|
|
283
283
|
};
|
|
284
284
|
const targetDir = options.targetDir;
|
|
285
285
|
try {
|
|
286
|
-
// Remove legacy
|
|
286
|
+
// Remove legacy ruv-swarm configs before writing new ones
|
|
287
287
|
const legacyCleaned = cleanupLegacyTools(targetDir);
|
|
288
288
|
for (const msg of legacyCleaned) {
|
|
289
289
|
result.created.files.push(`[cleaned] ${msg}`);
|
|
@@ -620,7 +620,7 @@ export async function executeUpgrade(targetDir, upgradeSettings = false) {
|
|
|
620
620
|
settingsUpdated: [],
|
|
621
621
|
};
|
|
622
622
|
try {
|
|
623
|
-
// Fix legacy
|
|
623
|
+
// Fix legacy ruv-swarm configs and old MCP package names
|
|
624
624
|
const legacyCleaned = cleanupLegacyTools(targetDir);
|
|
625
625
|
for (const msg of legacyCleaned) {
|
|
626
626
|
result.updated.push(`[cleaned] ${msg}`);
|