@monoes/monomindcli 1.9.16 → 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 +100 -46
- 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 +37 -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 +318 -186
- 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,181 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mastermind-approve
|
|
3
|
+
description: Mastermind approve — review and action pending approval requests from agents in a running org. Agents can request human approval before proceeding with sensitive actions.
|
|
4
|
+
type: domain-skill
|
|
5
|
+
default_mode: confirm
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Mastermind Approve
|
|
9
|
+
|
|
10
|
+
This skill is invoked by `mastermind:approve` or directly via `/mastermind:approve`.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Inputs
|
|
15
|
+
|
|
16
|
+
- `brain_context`: BRAIN CONTEXT block
|
|
17
|
+
- `org_name`: org to check approvals for
|
|
18
|
+
- `action`: list | approve | reject | inspect
|
|
19
|
+
- `approval_id`: id of the specific approval request (for approve/reject/inspect)
|
|
20
|
+
- `reason`: optional reason for rejection
|
|
21
|
+
- `caller`: command | master
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Step 0 — Brain Load (standalone only)
|
|
26
|
+
|
|
27
|
+
If `caller` is not "command", load brain context following _protocol.md Brain Load Procedure with namespace: `ops`.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Step 1 — Load Approvals
|
|
32
|
+
|
|
33
|
+
Approvals are stored in `.monomind/orgs/<org_name>-approvals.json`:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
orgFile=".monomind/orgs/${org_name}.json"
|
|
37
|
+
approvalsFile=".monomind/orgs/${org_name}-approvals.json"
|
|
38
|
+
[ ! -f "$approvalsFile" ] && echo '{"approvals":[]}' > "$approvalsFile"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Step 2 — Execute Action
|
|
44
|
+
|
|
45
|
+
### list (default)
|
|
46
|
+
|
|
47
|
+
Show pending approval requests from agents:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
jq -r '
|
|
51
|
+
.approvals | map(select(.status == "pending")) |
|
|
52
|
+
if length == 0 then "No pending approvals." else
|
|
53
|
+
.[] | "[\(.id)] \(.agent_id): \(.title)\n Action: \(.action)\n Risk: \(.risk_level // "low")\n Requested: \(.requested_at)"
|
|
54
|
+
end
|
|
55
|
+
' "$approvalsFile"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Render as:
|
|
59
|
+
```
|
|
60
|
+
PENDING APPROVALS — org: <org_name>
|
|
61
|
+
──────────────────────────────────────────────────
|
|
62
|
+
[req-001] content-writer: Publish to external blog
|
|
63
|
+
Action: POST to https://blog.example.com/api/posts
|
|
64
|
+
Risk: medium
|
|
65
|
+
Requested: 2 min ago
|
|
66
|
+
|
|
67
|
+
[req-002] marketer: Send email campaign
|
|
68
|
+
Action: Send to 1,200 subscribers
|
|
69
|
+
Risk: high ← requires explicit approval
|
|
70
|
+
Requested: 5 min ago
|
|
71
|
+
──────────────────────────────────────────────────
|
|
72
|
+
2 pending | Type "approve <id>" or "reject <id> <reason>"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### inspect
|
|
76
|
+
|
|
77
|
+
Show full details of a single approval request:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
jq --arg id "$approval_id" '.approvals[] | select(.id == $id)' "$approvalsFile"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Print the agent's requested action, context, and any supporting evidence they provided.
|
|
84
|
+
|
|
85
|
+
### approve
|
|
86
|
+
|
|
87
|
+
Update approval status and notify the waiting agent via memory:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
tmp="${approvalsFile}.tmp"
|
|
91
|
+
jq --arg id "$approval_id" \
|
|
92
|
+
'.approvals = [.approvals[] | if .id == $id then .status = "approved" | .resolved_at = (now|todate) | .resolved_by = "human" else . end]' \
|
|
93
|
+
"$approvalsFile" > "$tmp" && mv "$tmp" "$approvalsFile"
|
|
94
|
+
|
|
95
|
+
# Notify the agent via memory so it can proceed
|
|
96
|
+
approval=$(jq --arg id "$approval_id" '.approvals[] | select(.id == $id)' "$approvalsFile")
|
|
97
|
+
agent_id=$(echo "$approval" | jq -r '.agent_id')
|
|
98
|
+
memNs="org:${org_name}"
|
|
99
|
+
npx monomind@latest memory store \
|
|
100
|
+
--key "${memNs}:approval:${approval_id}" \
|
|
101
|
+
--namespace "$memNs" \
|
|
102
|
+
--value '{"status":"approved","approval_id":"'"$approval_id"'","ts":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'"}'
|
|
103
|
+
|
|
104
|
+
echo "Approved request $approval_id — agent $agent_id may proceed."
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Emit `org:approval:approved` event to dashboard.
|
|
108
|
+
|
|
109
|
+
### reject
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
tmp="${approvalsFile}.tmp"
|
|
113
|
+
jq --arg id "$approval_id" --arg reason "${reason:-No reason given}" \
|
|
114
|
+
'.approvals = [.approvals[] | if .id == $id then .status = "rejected" | .resolved_at = (now|todate) | .resolved_by = "human" | .rejection_reason = $reason else . end]' \
|
|
115
|
+
"$approvalsFile" > "$tmp" && mv "$tmp" "$approvalsFile"
|
|
116
|
+
|
|
117
|
+
approval=$(jq --arg id "$approval_id" '.approvals[] | select(.id == $id)' "$approvalsFile")
|
|
118
|
+
agent_id=$(echo "$approval" | jq -r '.agent_id')
|
|
119
|
+
memNs="org:${org_name}"
|
|
120
|
+
npx monomind@latest memory store \
|
|
121
|
+
--key "${memNs}:approval:${approval_id}" \
|
|
122
|
+
--namespace "$memNs" \
|
|
123
|
+
--value '{"status":"rejected","approval_id":"'"$approval_id"'","reason":"'"${reason:-No reason given}"'","ts":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'"}'
|
|
124
|
+
|
|
125
|
+
echo "Rejected request $approval_id. Agent $agent_id has been notified."
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Emit `org:approval:rejected` event.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## How Agents Request Approval
|
|
133
|
+
|
|
134
|
+
Agents that need human approval before a sensitive action should:
|
|
135
|
+
|
|
136
|
+
1. Write to the approvals file:
|
|
137
|
+
```bash
|
|
138
|
+
approvalsFile=".monomind/orgs/${orgName}-approvals.json"
|
|
139
|
+
[ ! -f "$approvalsFile" ] && echo '{"approvals":[]}' > "$approvalsFile"
|
|
140
|
+
approval_id="req-$(date +%s)"
|
|
141
|
+
tmp="${approvalsFile}.tmp"
|
|
142
|
+
jq --arg id "$approval_id" \
|
|
143
|
+
--arg agent "$role_id" \
|
|
144
|
+
--arg title "$action_title" \
|
|
145
|
+
--arg action "$action_description" \
|
|
146
|
+
--arg risk "medium" \
|
|
147
|
+
'.approvals += [{"id":$id,"agent_id":$agent,"title":$title,"action":$action,"risk_level":$risk,"status":"pending","requested_at":(now|todate)}]' \
|
|
148
|
+
"$approvalsFile" > "$tmp" && mv "$tmp" "$approvalsFile"
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
2. Emit `org:approval:requested` event to dashboard (so the human is notified).
|
|
152
|
+
|
|
153
|
+
3. Poll memory for the approval decision:
|
|
154
|
+
```bash
|
|
155
|
+
while true; do
|
|
156
|
+
result=$(npx monomind@latest memory search --query "approval:${approval_id}" --namespace "${memNs}" 2>/dev/null)
|
|
157
|
+
status=$(echo "$result" | jq -r '.[0].value.status // ""' 2>/dev/null)
|
|
158
|
+
[ "$status" = "approved" ] && break
|
|
159
|
+
[ "$status" = "rejected" ] && { echo "Action rejected: $(echo $result | jq -r '.[0].value.reason // ""')"; exit 1; }
|
|
160
|
+
sleep 30
|
|
161
|
+
done
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Step 3 — Return Output
|
|
167
|
+
|
|
168
|
+
```yaml
|
|
169
|
+
domain: ops
|
|
170
|
+
status: complete
|
|
171
|
+
action: <action>
|
|
172
|
+
org: <org_name>
|
|
173
|
+
approval_id: <approval_id if applicable>
|
|
174
|
+
pending_count: <N>
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Step 4 — Brain Write (standalone only)
|
|
180
|
+
|
|
181
|
+
If `caller` is not "command", follow _protocol.md Brain Write Procedure for domain `ops`.
|
|
@@ -65,7 +65,9 @@ If this skill is invoked directly (not by master):
|
|
|
65
65
|
|
|
66
66
|
1. If `sessionId` was not provided by the caller: generate it by taking the current UTC datetime formatted as `YYYYMMDDTHHmmss` and prefixing with `mm-` (e.g. `mm-20260506T142345`). Then emit `session:start` (if `caller` is `standalone` — skip if `command` or `master`, as the caller emits it). Before executing the curl below, substitute the generated sessionId for `<sessionId>`:
|
|
67
67
|
```bash
|
|
68
|
-
|
|
68
|
+
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
|
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" \
|
|
69
71
|
-H "Content-Type: application/json" \
|
|
70
72
|
-d '{"type":"session:start","session":"<sessionId>","domain":"architect","ts":'"$(date +%s)"'000}' || true
|
|
71
73
|
```
|
|
@@ -100,7 +102,9 @@ If this skill is invoked directly (not by master):
|
|
|
100
102
|
6. Proceed with complexity assessment below
|
|
101
103
|
7. At end: emit `session:complete` (if `caller` is `standalone`). Before executing the curl below, substitute the resolved sessionId for `<sessionId>`:
|
|
102
104
|
```bash
|
|
103
|
-
|
|
105
|
+
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
|
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" \
|
|
104
108
|
-H "Content-Type: application/json" \
|
|
105
109
|
-d '{"type":"session:complete","session":"<sessionId>","domain":"architect","ts":'"$(date +%s)"'000}' || true
|
|
106
110
|
```
|
|
@@ -289,7 +293,9 @@ Before spawning, resolve two values:
|
|
|
289
293
|
Then emit `agent:spawn` for the manager and spawn it. Before executing the curl below, substitute the resolved sessionId for `<sessionId>`:
|
|
290
294
|
|
|
291
295
|
```bash
|
|
292
|
-
|
|
296
|
+
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
|
297
|
+
CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
|
|
298
|
+
curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
|
|
293
299
|
-H "Content-Type: application/json" \
|
|
294
300
|
-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}'
|
|
295
301
|
```
|
|
@@ -437,14 +443,18 @@ The `description` must be the complete briefing, including CONTEXT, BRAIN MEMORY
|
|
|
437
443
|
|
|
438
444
|
BEFORE spawning each agent, emit agent:spawn to the live dashboard. Before each of the following curl commands executes, `<sessionId>` must already be substituted with the session ID from the CONTEXT header Session field above. When substituting `<stream-description>` and other free-text fields into the JSON payload, strip any double-quotes and backslashes from the value to keep the JSON well-formed:
|
|
439
445
|
\`\`\`bash
|
|
440
|
-
|
|
446
|
+
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
|
447
|
+
CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
|
|
448
|
+
curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
|
|
441
449
|
-H "Content-Type: application/json" \
|
|
442
450
|
-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}'
|
|
443
451
|
\`\`\`
|
|
444
452
|
|
|
445
453
|
If handing off artifacts to another domain (e.g. build for refactoring implementation, review for post-restructure check), emit intercom. When substituting `<msg>` and other free-text fields, strip any double-quotes and backslashes from the value to keep the JSON well-formed:
|
|
446
454
|
\`\`\`bash
|
|
447
|
-
|
|
455
|
+
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
|
456
|
+
CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
|
|
457
|
+
curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
|
|
448
458
|
-H "Content-Type: application/json" \
|
|
449
459
|
-d '{"type":"intercom","session":"<sessionId>","from":"architect","to":"<domain>","msg":"<msg>","ts":'"$(date +%s)"'000}'
|
|
450
460
|
\`\`\`
|
|
@@ -453,7 +463,9 @@ To track task execution, log progress via \`monotask card comment add "$BOARD_ID
|
|
|
453
463
|
|
|
454
464
|
BEFORE returning, emit domain:complete to the live dashboard:
|
|
455
465
|
\`\`\`bash
|
|
456
|
-
|
|
466
|
+
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
|
467
|
+
CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
|
|
468
|
+
curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
|
|
457
469
|
-H "Content-Type: application/json" \
|
|
458
470
|
-d '{"type":"domain:complete","session":"<sessionId>","domain":"architect","status":"<choose: complete | partial | blocked>","artifacts":[],"decisions":[],"ts":'"$(date +%s)"'000}'
|
|
459
471
|
\`\`\`
|
|
@@ -539,7 +551,9 @@ Before executing the curl blocks below and when constructing the Task() descript
|
|
|
539
551
|
|
|
540
552
|
1. Emit `agent:spawn` to the dashboard (use the resolved agent slug):
|
|
541
553
|
```bash
|
|
542
|
-
|
|
554
|
+
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
|
555
|
+
CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
|
|
556
|
+
curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
|
|
543
557
|
-H "Content-Type: application/json" \
|
|
544
558
|
-d '{"type":"agent:spawn","session":"<sessionId>","domain":"architect","agent":"<resolved agent slug, e.g. software-architect>","task":"<prompt>","ts":'"$(date +%s)"'000}'
|
|
545
559
|
```
|
|
@@ -553,7 +567,9 @@ Before executing the curl blocks below and when constructing the Task() descript
|
|
|
553
567
|
3. Include current directory structure context (`find . -maxdepth 3 -type d | grep -Ev "(^|/)(node_modules|dist|\.git)(/|$)"`)
|
|
554
568
|
4. Collect output, then emit `domain:complete`:
|
|
555
569
|
```bash
|
|
556
|
-
|
|
570
|
+
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
|
571
|
+
CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
|
|
572
|
+
curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
|
|
557
573
|
-H "Content-Type: application/json" \
|
|
558
574
|
-d '{"type":"domain:complete","session":"<sessionId>","domain":"architect","status":"complete","artifacts":[],"decisions":[],"ts":'"$(date +%s)"'000}'
|
|
559
575
|
```
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mastermind-backup
|
|
3
|
+
description: Mastermind backup — create, list, and restore compressed org backups. Archives org config, state, goals, routines, approvals, projects, and activity log into a timestamped tarball.
|
|
4
|
+
type: domain-skill
|
|
5
|
+
default_mode: confirm
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Mastermind Backup
|
|
9
|
+
|
|
10
|
+
This skill is invoked by `mastermind:backup` or directly via `/mastermind:backup`.
|
|
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 back up or restore (optional for `list`; required for `create`/`restore`)
|
|
18
|
+
- `action`: create | list | restore | delete
|
|
19
|
+
- `backup_file`: path to backup archive (required for `restore`/`delete`; auto-selected for `restore` if omitted → latest)
|
|
20
|
+
- `backup_dir`: directory to store backups (default: `.monomind/backups`)
|
|
21
|
+
- `include_state`: whether to include runtime state file (default: false — state is ephemeral)
|
|
22
|
+
- `caller`: command | master
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Step 0 — Brain Load (standalone only)
|
|
27
|
+
|
|
28
|
+
If `caller` is not "command", load brain context following _protocol.md Brain Load Procedure with namespace: `ops`.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Step 1 — Ensure Backup Directory
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
backupDir="${backup_dir:-.monomind/backups}"
|
|
36
|
+
mkdir -p "$backupDir"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Step 2 — Execute Action
|
|
42
|
+
|
|
43
|
+
### create
|
|
44
|
+
|
|
45
|
+
Create a compressed backup of all org data files:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
[ -z "$org_name" ] && { echo "ERROR: --org required for create."; exit 1; }
|
|
49
|
+
orgFile=".monomind/orgs/${org_name}.json"
|
|
50
|
+
[ ! -f "$orgFile" ] && { echo "ERROR: Org '${org_name}' not found."; exit 1; }
|
|
51
|
+
|
|
52
|
+
timestamp=$(date +%Y%m%d-%H%M%S)
|
|
53
|
+
archiveName="${org_name}-${timestamp}.tar.gz"
|
|
54
|
+
archivePath="${backupDir}/${archiveName}"
|
|
55
|
+
|
|
56
|
+
# Collect files to back up
|
|
57
|
+
filesToBackup=""
|
|
58
|
+
for suffix in "" "-goals" "-routines" "-approvals" "-projects" "-worktrees" "-bootstrap"; do
|
|
59
|
+
f=".monomind/orgs/${org_name}${suffix}.json"
|
|
60
|
+
[ -f "$f" ] && filesToBackup="$filesToBackup $f"
|
|
61
|
+
done
|
|
62
|
+
|
|
63
|
+
# Optionally include runtime state
|
|
64
|
+
if [ "${include_state:-false}" = "true" ]; then
|
|
65
|
+
stateFile=".monomind/orgs/${org_name}-state.json"
|
|
66
|
+
[ -f "$stateFile" ] && filesToBackup="$filesToBackup $stateFile"
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
# Include recent activity (last 500 lines filtered by org)
|
|
70
|
+
eventsFile="data/mastermind-events.jsonl"
|
|
71
|
+
if [ -f "$eventsFile" ]; then
|
|
72
|
+
tmpEvents=$(mktemp /tmp/backup-events-XXXXXX.jsonl)
|
|
73
|
+
grep "\"org\":\"${org_name}\"" "$eventsFile" | tail -500 > "$tmpEvents" || true
|
|
74
|
+
filesToBackup="$filesToBackup $tmpEvents"
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
# Create archive
|
|
78
|
+
tar -czf "$archivePath" $filesToBackup 2>/dev/null
|
|
79
|
+
[ -n "$tmpEvents" ] && rm -f "$tmpEvents"
|
|
80
|
+
|
|
81
|
+
if [ -f "$archivePath" ]; then
|
|
82
|
+
sizeKb=$(du -k "$archivePath" | awk '{print $1}')
|
|
83
|
+
echo "Backup created: $archivePath (${sizeKb}KB)"
|
|
84
|
+
echo "Files included: $(echo $filesToBackup | wc -w | tr -d ' ')"
|
|
85
|
+
else
|
|
86
|
+
echo "ERROR: Failed to create backup archive."
|
|
87
|
+
exit 1
|
|
88
|
+
fi
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Emit `org:backup:created` event:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
|
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" \
|
|
97
|
+
-H "Content-Type: application/json" \
|
|
98
|
+
-d "$(jq -cn --arg org "$org_name" --arg file "$archiveName" \
|
|
99
|
+
'{type:"org:backup:created",org:$org,file:$file,ts:(now*1000|floor)}')" || true
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### list
|
|
103
|
+
|
|
104
|
+
Show all available backups:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
echo "BACKUPS — ${backup_dir:-.monomind/backups}"
|
|
108
|
+
echo "──────────────────────────────────────────────"
|
|
109
|
+
printf "%-45s %-12s %s\n" "FILE" "SIZE" "ORG"
|
|
110
|
+
echo "──────────────────────────────────────────────"
|
|
111
|
+
|
|
112
|
+
found=0
|
|
113
|
+
for f in "${backupDir}"/*.tar.gz; do
|
|
114
|
+
[ -f "$f" ] || continue
|
|
115
|
+
name=$(basename "$f")
|
|
116
|
+
sizeKb=$(du -k "$f" | awk '{print $1}')
|
|
117
|
+
orgPart=$(echo "$name" | sed 's/-[0-9]\{8\}-[0-9]\{6\}\.tar\.gz$//')
|
|
118
|
+
if [ -z "$org_name" ] || [ "$orgPart" = "$org_name" ]; then
|
|
119
|
+
printf "%-45s %-12s %s\n" "$name" "${sizeKb}KB" "$orgPart"
|
|
120
|
+
found=$((found + 1))
|
|
121
|
+
fi
|
|
122
|
+
done
|
|
123
|
+
|
|
124
|
+
[ "$found" -eq 0 ] && echo " No backups found."
|
|
125
|
+
echo ""
|
|
126
|
+
echo "Total: $found backup(s)"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### restore
|
|
130
|
+
|
|
131
|
+
Restore org data from a backup archive:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
# Auto-select latest backup if not specified
|
|
135
|
+
if [ -z "$backup_file" ]; then
|
|
136
|
+
if [ -n "$org_name" ]; then
|
|
137
|
+
backup_file=$(ls "${backupDir}/${org_name}-"*.tar.gz 2>/dev/null | sort | tail -1)
|
|
138
|
+
fi
|
|
139
|
+
[ -z "$backup_file" ] && { echo "ERROR: No backup file found. Pass --backup-file <path>."; exit 1; }
|
|
140
|
+
fi
|
|
141
|
+
|
|
142
|
+
[ ! -f "$backup_file" ] && { echo "ERROR: Backup file not found: $backup_file"; exit 1; }
|
|
143
|
+
|
|
144
|
+
echo "Restoring from: $backup_file"
|
|
145
|
+
echo "This will overwrite existing org data. Confirm? (skipped in auto mode)"
|
|
146
|
+
|
|
147
|
+
# Extract to a temp dir first, then copy
|
|
148
|
+
tmpDir=$(mktemp -d /tmp/mastermind-restore-XXXXXX)
|
|
149
|
+
tar -xzf "$backup_file" -C "$tmpDir" 2>/dev/null
|
|
150
|
+
|
|
151
|
+
# Move restored files to correct locations
|
|
152
|
+
find "$tmpDir" -name "*.json" | while read -r f; do
|
|
153
|
+
dest=$(echo "$f" | sed "s|$tmpDir||")
|
|
154
|
+
destDir=$(dirname "$dest")
|
|
155
|
+
mkdir -p "$destDir" 2>/dev/null || true
|
|
156
|
+
cp "$f" "$dest" && echo " Restored: $dest"
|
|
157
|
+
done
|
|
158
|
+
|
|
159
|
+
# Handle events file
|
|
160
|
+
find "$tmpDir" -name "*.jsonl" | while read -r f; do
|
|
161
|
+
dest=$(echo "$f" | sed "s|$tmpDir||")
|
|
162
|
+
mkdir -p "$(dirname "$dest")" 2>/dev/null || true
|
|
163
|
+
cat "$f" >> "$dest" && echo " Appended events from backup"
|
|
164
|
+
done
|
|
165
|
+
|
|
166
|
+
rm -rf "$tmpDir"
|
|
167
|
+
echo "Restore complete."
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### delete
|
|
171
|
+
|
|
172
|
+
Remove a specific backup archive:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
[ -z "$backup_file" ] && { echo "ERROR: --backup-file required for delete."; exit 1; }
|
|
176
|
+
[ ! -f "$backup_file" ] && { echo "ERROR: File not found: $backup_file"; exit 1; }
|
|
177
|
+
rm "$backup_file"
|
|
178
|
+
echo "Deleted: $backup_file"
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Step 3 — Return Output
|
|
184
|
+
|
|
185
|
+
```yaml
|
|
186
|
+
domain: ops
|
|
187
|
+
status: complete
|
|
188
|
+
action: <action>
|
|
189
|
+
org: <org_name>
|
|
190
|
+
backup_file: <path if created>
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Step 4 — Brain Write (standalone only)
|
|
196
|
+
|
|
197
|
+
If `caller` is not "command", follow _protocol.md Brain Write Procedure for domain `ops`.
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mastermind-bootstrap
|
|
3
|
+
description: Mastermind bootstrap — one-time org initialization that primes the CEO/boss agent with org context, goal hierarchy, and a signed invite token. Run after createorg before first runorg.
|
|
4
|
+
type: domain-skill
|
|
5
|
+
default_mode: confirm
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Mastermind Bootstrap
|
|
9
|
+
|
|
10
|
+
This skill is invoked by `mastermind:bootstrap` or directly via `/mastermind:bootstrap`.
|
|
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 bootstrap (required)
|
|
18
|
+
- `action`: init | token | status | reset
|
|
19
|
+
- `boss_role_id`: role ID of the CEO/boss agent (default: auto-detect by `reports_to: null`)
|
|
20
|
+
- `caller`: command | master
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Step 0 — Brain Load (standalone only)
|
|
25
|
+
|
|
26
|
+
If `caller` is not "command", load brain context following _protocol.md Brain Load Procedure with namespace: `ops`.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Step 1 — Resolve Org
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
orgFile=".monomind/orgs/${org_name}.json"
|
|
34
|
+
[ ! -f "$orgFile" ] && { echo "ERROR: Org '${org_name}' not found. Run /mastermind:createorg first."; exit 1; }
|
|
35
|
+
|
|
36
|
+
bootstrapFile=".monomind/orgs/${org_name}-bootstrap.json"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Step 2 — Execute Action
|
|
42
|
+
|
|
43
|
+
### status (check bootstrap state)
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
if [ -f "$bootstrapFile" ]; then
|
|
47
|
+
echo "BOOTSTRAP STATUS — ${org_name}"
|
|
48
|
+
echo "──────────────────────────────────"
|
|
49
|
+
jq -r '
|
|
50
|
+
" State: \(.state // "unknown")",
|
|
51
|
+
" Boss Role: \(.boss_role_id // "-")",
|
|
52
|
+
" Bootstrapped:\(.bootstrapped_at // "never")",
|
|
53
|
+
" Token: \(if .invite_token then (.invite_token | .[0:8]) + "..." else "none" end)",
|
|
54
|
+
" Token used: \(.token_used // false)"
|
|
55
|
+
' "$bootstrapFile"
|
|
56
|
+
else
|
|
57
|
+
echo "Not bootstrapped. Run /mastermind:bootstrap --org ${org_name} --action init"
|
|
58
|
+
fi
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### token (generate a fresh invite token)
|
|
62
|
+
|
|
63
|
+
Generate a signed one-time token the boss agent uses to authenticate its first session:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
token=$(openssl rand -hex 24 2>/dev/null || python3 -c "import secrets; print(secrets.token_hex(24))")
|
|
67
|
+
ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
68
|
+
|
|
69
|
+
echo "INVITE TOKEN — ${org_name}"
|
|
70
|
+
echo "──────────────────────────────────"
|
|
71
|
+
echo " Token: $token"
|
|
72
|
+
echo " Valid until first use."
|
|
73
|
+
echo ""
|
|
74
|
+
|
|
75
|
+
# Store token in bootstrap file
|
|
76
|
+
[ ! -f "$bootstrapFile" ] && echo '{}' > "$bootstrapFile"
|
|
77
|
+
tmp="${bootstrapFile}.tmp"
|
|
78
|
+
jq --arg token "$token" --arg ts "$ts" \
|
|
79
|
+
'.invite_token = $token | .token_generated_at = $ts | .token_used = false' \
|
|
80
|
+
"$bootstrapFile" > "$tmp" && mv "$tmp" "$bootstrapFile"
|
|
81
|
+
echo "Token stored. Include in boss agent prompt as: INVITE_TOKEN=${token}"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### init (full bootstrap)
|
|
85
|
+
|
|
86
|
+
Initialize org bootstrap: detect boss, generate token, write primer context:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# Auto-detect boss (role with no reports_to or reports_to: null)
|
|
90
|
+
if [ -z "$boss_role_id" ]; then
|
|
91
|
+
boss_role_id=$(jq -r '.roles[] | select(.reports_to == null or .reports_to == "") | .id' "$orgFile" | head -1)
|
|
92
|
+
fi
|
|
93
|
+
[ -z "$boss_role_id" ] && { echo "ERROR: Could not detect boss role. Pass --boss-role-id <id>."; exit 1; }
|
|
94
|
+
|
|
95
|
+
bossConfig=$(jq --arg id "$boss_role_id" '.roles[] | select(.id == $id)' "$orgFile")
|
|
96
|
+
bossTitle=$(echo "$bossConfig" | jq -r '.title // "CEO"')
|
|
97
|
+
orgGoal=$(jq -r '.goal // "No goal set"' "$orgFile")
|
|
98
|
+
orgTopology=$(jq -r '.topology // "hierarchical"' "$orgFile")
|
|
99
|
+
roleCount=$(jq '.roles | length' "$orgFile")
|
|
100
|
+
governance=$(jq -r '.governance.policy // "auto"' "$orgFile")
|
|
101
|
+
|
|
102
|
+
# Generate invite token
|
|
103
|
+
token=$(openssl rand -hex 24 2>/dev/null || python3 -c "import secrets; print(secrets.token_hex(24))")
|
|
104
|
+
ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
105
|
+
|
|
106
|
+
# Write bootstrap record
|
|
107
|
+
cat > "$bootstrapFile" <<EOF
|
|
108
|
+
{
|
|
109
|
+
"org": "${org_name}",
|
|
110
|
+
"state": "initialized",
|
|
111
|
+
"boss_role_id": "${boss_role_id}",
|
|
112
|
+
"boss_title": "${bossTitle}",
|
|
113
|
+
"org_goal": "${orgGoal}",
|
|
114
|
+
"topology": "${orgTopology}",
|
|
115
|
+
"role_count": ${roleCount},
|
|
116
|
+
"governance": "${governance}",
|
|
117
|
+
"invite_token": "${token}",
|
|
118
|
+
"token_used": false,
|
|
119
|
+
"token_generated_at": "${ts}",
|
|
120
|
+
"bootstrapped_at": "${ts}"
|
|
121
|
+
}
|
|
122
|
+
EOF
|
|
123
|
+
|
|
124
|
+
echo "BOOTSTRAP COMPLETE — ${org_name}"
|
|
125
|
+
echo "──────────────────────────────────────────"
|
|
126
|
+
echo " Boss: ${bossTitle} (${boss_role_id})"
|
|
127
|
+
echo " Goal: ${orgGoal}"
|
|
128
|
+
echo " Topology: ${orgTopology}"
|
|
129
|
+
echo " Agents: ${roleCount}"
|
|
130
|
+
echo " Governance: ${governance}"
|
|
131
|
+
echo " Token: ${token}"
|
|
132
|
+
echo ""
|
|
133
|
+
echo "CEO PRIMER (include in boss agent prompt):"
|
|
134
|
+
echo "──────────────────────────────────────────"
|
|
135
|
+
cat <<PRIMER
|
|
136
|
+
ORG: ${org_name}
|
|
137
|
+
ROLE: ${bossTitle}
|
|
138
|
+
GOAL: ${orgGoal}
|
|
139
|
+
GOVERNANCE: ${governance}
|
|
140
|
+
INVITE_TOKEN: ${token}
|
|
141
|
+
|
|
142
|
+
You are the autonomous CEO of org '${org_name}'. Your mission: ${orgGoal}.
|
|
143
|
+
You lead ${roleCount} agents in a ${orgTopology} topology.
|
|
144
|
+
Governance mode: ${governance}. In 'board' or 'strict' mode, pause before high-risk actions and emit approval requests.
|
|
145
|
+
Use your INVITE_TOKEN for your first heartbeat. Token is single-use.
|
|
146
|
+
PRIMER
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Emit `org:bootstrap:complete` event:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
|
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" \
|
|
155
|
+
-H "Content-Type: application/json" \
|
|
156
|
+
-d "$(jq -cn --arg org "$org_name" --arg boss "$boss_role_id" \
|
|
157
|
+
'{type:"org:bootstrap:complete",org:$org,boss:$boss,ts:(now*1000|floor)}')" || true
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### reset
|
|
161
|
+
|
|
162
|
+
Clear bootstrap state (next init will regenerate token):
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
if [ -f "$bootstrapFile" ]; then
|
|
166
|
+
rm "$bootstrapFile"
|
|
167
|
+
echo "Bootstrap state cleared for '${org_name}'. Run --action init to re-bootstrap."
|
|
168
|
+
else
|
|
169
|
+
echo "No bootstrap file found for '${org_name}'."
|
|
170
|
+
fi
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Step 3 — Return Output
|
|
176
|
+
|
|
177
|
+
```yaml
|
|
178
|
+
domain: ops
|
|
179
|
+
status: complete
|
|
180
|
+
action: <action>
|
|
181
|
+
org: <org_name>
|
|
182
|
+
boss_role_id: <boss_role_id>
|
|
183
|
+
state: initialized | token | reset
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Step 4 — Brain Write (standalone only)
|
|
189
|
+
|
|
190
|
+
If `caller` is not "command", follow _protocol.md Brain Write Procedure for domain `ops`.
|