@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.
Files changed (118) hide show
  1. package/.claude/commands/mastermind/_repeat.md +182 -39
  2. package/.claude/commands/mastermind/architect.md +17 -11
  3. package/.claude/commands/mastermind/brain.md +4 -0
  4. package/.claude/commands/mastermind/build.md +4 -0
  5. package/.claude/commands/mastermind/content.md +4 -0
  6. package/.claude/commands/mastermind/createorg.md +5 -3
  7. package/.claude/commands/mastermind/finance.md +4 -0
  8. package/.claude/commands/mastermind/idea.md +4 -0
  9. package/.claude/commands/mastermind/marketing.md +4 -0
  10. package/.claude/commands/mastermind/master.md +63 -37
  11. package/.claude/commands/mastermind/ops.md +4 -0
  12. package/.claude/commands/mastermind/release.md +4 -0
  13. package/.claude/commands/mastermind/research.md +4 -0
  14. package/.claude/commands/mastermind/review.md +4 -0
  15. package/.claude/commands/mastermind/runorg.md +5 -3
  16. package/.claude/commands/mastermind/sales.md +4 -0
  17. package/.claude/commands/mastermind/techport.md +9 -0
  18. package/.claude/commands/monomind/do.md +5 -1
  19. package/.claude/commands/monomind/idea.md +5 -1
  20. package/.claude/commands/monomind/improve.md +5 -1
  21. package/.claude/commands/monomind/repeat.md +85 -29
  22. package/.claude/commands/monomind/review.md +6 -2
  23. package/.claude/commands/monomind/understand.md +10 -8
  24. package/.claude/helpers/extras-registry.json +235 -235
  25. package/.claude/helpers/graphify-freshen.cjs +13 -1
  26. package/.claude/helpers/hook-handler.cjs +1 -1
  27. package/.claude/helpers/router.cjs +4 -1
  28. package/.claude/skills/mastermind/_protocol.md +28 -21
  29. package/.claude/skills/mastermind/access.md +236 -0
  30. package/.claude/skills/mastermind/activity.md +191 -0
  31. package/.claude/skills/mastermind/adapter-manager.md +259 -0
  32. package/.claude/skills/mastermind/adapters.md +204 -0
  33. package/.claude/skills/mastermind/agent-detail.md +242 -0
  34. package/.claude/skills/mastermind/agents.md +178 -0
  35. package/.claude/skills/mastermind/approval-detail.md +259 -0
  36. package/.claude/skills/mastermind/approve.md +181 -0
  37. package/.claude/skills/mastermind/architect.md +24 -8
  38. package/.claude/skills/mastermind/backup.md +197 -0
  39. package/.claude/skills/mastermind/bootstrap.md +190 -0
  40. package/.claude/skills/mastermind/budgets.md +237 -0
  41. package/.claude/skills/mastermind/companies.md +256 -0
  42. package/.claude/skills/mastermind/costs.md +151 -0
  43. package/.claude/skills/mastermind/createorg.md +23 -5
  44. package/.claude/skills/mastermind/diagnose.md +249 -0
  45. package/.claude/skills/mastermind/env.md +198 -0
  46. package/.claude/skills/mastermind/environments.md +250 -0
  47. package/.claude/skills/mastermind/export.md +324 -0
  48. package/.claude/skills/mastermind/goal-detail.md +255 -0
  49. package/.claude/skills/mastermind/goals.md +149 -0
  50. package/.claude/skills/mastermind/heartbeat.md +164 -0
  51. package/.claude/skills/mastermind/idea.md +250 -122
  52. package/.claude/skills/mastermind/import.md +281 -0
  53. package/.claude/skills/mastermind/inbox.md +214 -0
  54. package/.claude/skills/mastermind/instance-settings.md +315 -0
  55. package/.claude/skills/mastermind/instance.md +231 -0
  56. package/.claude/skills/mastermind/invite-landing.md +227 -0
  57. package/.claude/skills/mastermind/invites.md +254 -0
  58. package/.claude/skills/mastermind/issue-detail.md +291 -0
  59. package/.claude/skills/mastermind/issues.md +235 -0
  60. package/.claude/skills/mastermind/join-queue.md +170 -0
  61. package/.claude/skills/mastermind/liveness.md +392 -0
  62. package/.claude/skills/mastermind/memory.md +321 -0
  63. package/.claude/skills/mastermind/my-issues.md +146 -0
  64. package/.claude/skills/mastermind/new-agent.md +241 -0
  65. package/.claude/skills/mastermind/org-chart.md +207 -0
  66. package/.claude/skills/mastermind/org-settings.md +217 -0
  67. package/.claude/skills/mastermind/plan-to-tasks.md +136 -0
  68. package/.claude/skills/mastermind/plugin-manager.md +241 -0
  69. package/.claude/skills/mastermind/plugin-settings.md +273 -0
  70. package/.claude/skills/mastermind/plugins.md +190 -0
  71. package/.claude/skills/mastermind/profile.md +187 -0
  72. package/.claude/skills/mastermind/project-detail.md +249 -0
  73. package/.claude/skills/mastermind/project-workspace.md +244 -0
  74. package/.claude/skills/mastermind/projects.md +164 -0
  75. package/.claude/skills/mastermind/routine-detail.md +253 -0
  76. package/.claude/skills/mastermind/routines.md +202 -0
  77. package/.claude/skills/mastermind/runorg.md +74 -9
  78. package/.claude/skills/mastermind/search.md +186 -0
  79. package/.claude/skills/mastermind/secrets.md +199 -0
  80. package/.claude/skills/mastermind/skills.md +156 -0
  81. package/.claude/skills/mastermind/tasks.md +149 -0
  82. package/.claude/skills/mastermind/techport.md +5 -5
  83. package/.claude/skills/mastermind/threads.md +259 -0
  84. package/.claude/skills/mastermind/tree-control.md +250 -0
  85. package/.claude/skills/mastermind/wiki.md +314 -0
  86. package/.claude/skills/mastermind/workspace-detail.md +317 -0
  87. package/.claude/skills/mastermind/workspaces.md +261 -0
  88. package/.claude/skills/mastermind/worktree.md +187 -0
  89. package/dist/src/init/executor.js +8 -8
  90. package/dist/src/init/executor.js.map +1 -1
  91. package/dist/src/init/statusline-generator.d.ts.map +1 -1
  92. package/dist/src/init/statusline-generator.js +12 -0
  93. package/dist/src/init/statusline-generator.js.map +1 -1
  94. package/dist/src/ui/.monomind/data/ranked-context.json +1 -1
  95. package/dist/src/ui/.monomind/loops/mastermind-review-1778664132789.json +16 -0
  96. package/dist/src/ui/.monomind/sessions/current.json +5 -5
  97. package/dist/src/ui/.monomind/sessions/session-1776778451399.json +15 -0
  98. package/dist/src/ui/dashboard.html +3030 -181
  99. package/dist/src/ui/data/mastermind-events.jsonl +8 -0
  100. package/dist/src/ui/data/mastermind-sessions.json +1 -0
  101. package/dist/src/ui/server.mjs +738 -0
  102. package/dist/tsconfig.tsbuildinfo +1 -1
  103. package/package.json +1 -1
  104. package/.claude/skills/.monomind/data/ranked-context.json +0 -5
  105. package/.claude/skills/.monomind/sessions/current.json +0 -13
  106. package/.claude/skills/.monomind/sessions/session-1777829336455.json +0 -15
  107. package/.claude/skills/.monomind/sessions/session-1777831614725.json +0 -15
  108. package/.claude/skills/.monomind/sessions/session-1777832095857.json +0 -15
  109. package/.claude/skills/.monomind/sessions/session-1777839814183.json +0 -15
  110. package/.claude/skills/.monomind/sessions/session-1777841847131.json +0 -15
  111. package/.claude/skills/.monomind/sessions/session-1777843309463.json +0 -15
  112. package/.claude/skills/.monomind/sessions/session-1777880867159.json +0 -15
  113. package/.claude/skills/.monomind/sessions/session-1777881884593.json +0 -15
  114. package/.claude/skills/.monomind/sessions/session-1777884090471.json +0 -15
  115. package/.claude/skills/.monomind/sessions/session-1777884808221.json +0 -15
  116. package/.claude/skills/.monomind/sessions/session-1777885672155.json +0 -15
  117. package/.claude/skills/.monomind/sessions/session-1777886852818.json +0 -15
  118. package/.claude/skills/.monomind/sessions/session-1777896532690.json +0 -15
@@ -0,0 +1,227 @@
1
+ ---
2
+ name: mastermind-invite-landing
3
+ description: Mastermind invite-landing — accept an org invite as a human member or as an agent. Validates the invite token, presents org/role info, and processes join as human (name/email) or as an agent (adapter type, model, agent name, config). Mirrors InviteLanding.tsx.
4
+ type: domain-skill
5
+ default_mode: confirm
6
+ ---
7
+
8
+ # Mastermind Invite Landing
9
+
10
+ This skill is invoked by `mastermind:invite-landing` or directly via `/mastermind:invite-landing`.
11
+
12
+ ---
13
+
14
+ ## Inputs
15
+
16
+ - `brain_context`: BRAIN CONTEXT block (injected by command, or loaded below if standalone)
17
+ - `action`: show | accept-human | accept-agent | status
18
+ - `token`: invite token (required; extract from monomind://invite?... URL or share link)
19
+ - `org_name`: org name (required unless extractable from token record)
20
+ - `join_as`: human | agent (for accept actions; default: agent in CLI context)
21
+ - `display_name`: human display name (for accept-human)
22
+ - `email`: email address (for accept-human)
23
+ - `agent_name`: agent display name (for accept-agent)
24
+ - `adapter_type`: claude-local | gemini-local | codex-local | cursor | opencode | hermes | http | acpx (for accept-agent; default: claude-local)
25
+ - `model`: model override (for accept-agent; uses adapter default if omitted)
26
+ - `caller`: command | master
27
+
28
+ ---
29
+
30
+ ## Join Modes
31
+
32
+ | Mode | Who | Required Fields |
33
+ |------|-----|-----------------|
34
+ | `human` | A human operator joining the org | `display_name`, `email` |
35
+ | `agent` | An AI agent joining the org | `agent_name`, `adapter_type` |
36
+
37
+ ---
38
+
39
+ ## Step 0 — Brain Load (standalone only)
40
+
41
+ If `caller` is not "command", load brain context following _protocol.md Brain Load Procedure with namespace: `ops`.
42
+
43
+ ---
44
+
45
+ ## Step 1 — Validate Token
46
+
47
+ ```bash
48
+ [ -z "$token" ] && { echo "ERROR: --token required. Extract from invite URL: monomind://invite?org=...&token=...&role=..."; exit 1; }
49
+
50
+ # Try to find org from token if org_name not given
51
+ if [ -z "$org_name" ]; then
52
+ # Search all members files for a matching invite token
53
+ for mf in .monomind/orgs/*-members.json; do
54
+ [ -f "$mf" ] || continue
55
+ match=$(jq -r --arg t "$token" '.join_requests[] | select(.token == $t or .id == $t) | .id' "$mf" 2>/dev/null | head -1)
56
+ if [ -n "$match" ]; then
57
+ org_name=$(basename "$mf" "-members.json")
58
+ break
59
+ fi
60
+ done
61
+ [ -z "$org_name" ] && { echo "ERROR: Could not find org for token '$token'. Specify --org-name explicitly."; exit 1; }
62
+ fi
63
+
64
+ membersFile=".monomind/orgs/${org_name}-members.json"
65
+ [ ! -f "$membersFile" ] && { echo "ERROR: Org '$org_name' members file not found."; exit 1; }
66
+
67
+ inviteDef=$(jq -r --arg t "$token" \
68
+ '.join_requests[] | select((.token == $t or .id == $t) and .type == "invite")' \
69
+ "$membersFile" | head -c 4096)
70
+ [ -z "$inviteDef" ] && { echo "ERROR: Invite token '$token' not found or already used."; exit 1; }
71
+
72
+ inviteStatus=$(echo "$inviteDef" | jq -r '.status // "pending"')
73
+ inviteRole=$(echo "$inviteDef" | jq -r '.role // "operator"')
74
+ ```
75
+
76
+ ---
77
+
78
+ ## Step 2 — Execute Action
79
+
80
+ ### show (default)
81
+
82
+ ```bash
83
+ echo "INVITE — org: $org_name"
84
+ echo "────────────────────────────────────────────────────────"
85
+ echo " Token: ${token:0:16}…"
86
+ echo " Role: $inviteRole"
87
+ echo " Status: $inviteStatus"
88
+
89
+ inviteCreatedAt=$(echo "$inviteDef" | jq -r '.createdAt // "-"')
90
+ echo " Created: $inviteCreatedAt"
91
+ echo ""
92
+
93
+ if [ "$inviteStatus" = "revoked" ]; then
94
+ echo " This invite has been revoked and can no longer be used."
95
+ elif [ "$inviteStatus" = "accepted" ]; then
96
+ echo " This invite has already been accepted."
97
+ else
98
+ echo " To join as an agent (default in CLI):"
99
+ echo " --action accept-agent --token $token --org-name $org_name --agent-name 'My Agent' --adapter-type claude-local"
100
+ echo ""
101
+ echo " To join as a human:"
102
+ echo " --action accept-human --token $token --org-name $org_name --display-name 'Your Name' --email 'you@example.com'"
103
+ fi
104
+ ```
105
+
106
+ ### accept-human
107
+
108
+ ```bash
109
+ [ "$inviteStatus" != "pending" ] && { echo "ERROR: Invite status is '$inviteStatus' — cannot accept."; exit 1; }
110
+ [ -z "$display_name" ] && { echo "ERROR: --display-name required for human join."; exit 1; }
111
+ [ -z "$email" ] && { echo "ERROR: --email required for human join."; exit 1; }
112
+
113
+ ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
114
+ memberId="human-$(echo "${email}" | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9' '-' | sed 's/^-//;s/-$//')"
115
+
116
+ tmp="${membersFile}.tmp"
117
+ # Mark invite accepted and add member record
118
+ jq --arg token "$token" --arg mid "$memberId" --arg name "$display_name" \
119
+ --arg email "$email" --arg role "$inviteRole" --arg ts "$ts" \
120
+ '.join_requests = [.join_requests[] | if (.token == $token or .id == $token) then
121
+ .status = "accepted" | .acceptedAt = $ts | .acceptedAs = "human"
122
+ else . end] |
123
+ .members += [{"id":$mid,"displayName":$name,"email":$email,"role":$role,
124
+ "memberType":"human","status":"active","joinedAt":$ts}]' \
125
+ "$membersFile" > "$tmp" && mv "$tmp" "$membersFile"
126
+
127
+ echo "JOINED as human: $display_name"
128
+ echo " Member ID: $memberId"
129
+ echo " Role: $inviteRole"
130
+ echo " Org: $org_name"
131
+ echo " Email: $email"
132
+ echo " Joined at: $ts"
133
+ ```
134
+
135
+ ### accept-agent
136
+
137
+ ```bash
138
+ [ "$inviteStatus" != "pending" ] && { echo "ERROR: Invite status is '$inviteStatus' — cannot accept."; exit 1; }
139
+ [ -z "$agent_name" ] && { echo "ERROR: --agent-name required for agent join."; exit 1; }
140
+
141
+ adapterType="${adapter_type:-claude-local}"
142
+ case "$adapterType" in
143
+ claude-local|gemini-local|codex-local|cursor|opencode-local|hermes-local|http|acpx) : ;;
144
+ *) echo "ERROR: Unknown adapter type '$adapterType'. Choose: claude-local, gemini-local, codex-local, cursor, opencode-local, hermes-local, http, acpx"; exit 1 ;;
145
+ esac
146
+
147
+ # Resolve default model
148
+ modelId="${model}"
149
+ if [ -z "$modelId" ]; then
150
+ case "$adapterType" in
151
+ claude-local) modelId="claude-sonnet-4-6" ;;
152
+ gemini-local) modelId="gemini-2.0-flash" ;;
153
+ codex-local) modelId="gpt-4o" ;;
154
+ cursor) modelId="cursor-default" ;;
155
+ opencode-local) modelId="opencode-default" ;;
156
+ hermes-local) modelId="hermes-3" ;;
157
+ *) modelId="custom" ;;
158
+ esac
159
+ fi
160
+
161
+ # Generate agent slug
162
+ agentSlug=$(echo "$agent_name" | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9' '-' | sed 's/^-//;s/-$//')
163
+ agentId="agent-${agentSlug}"
164
+ ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
165
+
166
+ # Add to members file
167
+ tmp="${membersFile}.tmp"
168
+ jq --arg token "$token" --arg mid "$agentId" --arg name "$agent_name" \
169
+ --arg role "$inviteRole" --arg adapter "$adapterType" --arg model "$modelId" --arg ts "$ts" \
170
+ '.join_requests = [.join_requests[] | if (.token == $token or .id == $token) then
171
+ .status = "accepted" | .acceptedAt = $ts | .acceptedAs = "agent"
172
+ else . end] |
173
+ .members += [{"id":$mid,"displayName":$name,"role":$role,
174
+ "memberType":"agent","adapter":{"type":$adapter,"model":$model},
175
+ "status":"active","joinedAt":$ts}]' \
176
+ "$membersFile" > "$tmp" && mv "$tmp" "$membersFile"
177
+
178
+ # Also add to org roles file so the agent can run
179
+ orgFile=".monomind/orgs/${org_name}.json"
180
+ if [ -f "$orgFile" ]; then
181
+ dupCheck=$(jq -r --arg id "$agentId" '[.roles[] | select(.id == $id)] | length' "$orgFile")
182
+ if [ "$dupCheck" -eq 0 ]; then
183
+ tmp2="${orgFile}.tmp"
184
+ jq --arg id "$agentId" --arg title "$agent_name" \
185
+ --arg adapter "$adapterType" --arg model "$modelId" --arg ts "$ts" \
186
+ '.roles += [{"id":$id,"title":$title,"adapter":{"type":$adapter,"model":$model,"max_tokens":8192},
187
+ "reports_to":null,"governance":null,"skills":[],"created_at":$ts}]' \
188
+ "$orgFile" > "$tmp2" && mv "$tmp2" "$orgFile"
189
+ fi
190
+ fi
191
+
192
+ echo "JOINED as agent: $agent_name"
193
+ echo " Agent ID: $agentId"
194
+ echo " Role: $inviteRole"
195
+ echo " Adapter: $adapterType / $modelId"
196
+ echo " Org: $org_name"
197
+ echo " Joined at: $ts"
198
+ echo ""
199
+ echo " View agent: /mastermind:agent-detail --org $org_name --agent-id $agentId"
200
+ ```
201
+
202
+ ### status
203
+
204
+ ```bash
205
+ echo "INVITE STATUS — token: ${token:0:16}…"
206
+ echo "────────────────────────────────────────────────────────"
207
+ echo "$inviteDef" | jq '{status, role, createdAt, acceptedAt, acceptedAs}'
208
+ ```
209
+
210
+ ---
211
+
212
+ ## Step 3 — Return Output
213
+
214
+ ```yaml
215
+ domain: ops
216
+ status: complete
217
+ action: <action>
218
+ org: <org_name>
219
+ invite_status: <status>
220
+ role: <role>
221
+ ```
222
+
223
+ ---
224
+
225
+ ## Step 4 — Brain Write (standalone only)
226
+
227
+ If `caller` is not "command", follow _protocol.md Brain Write Procedure for domain `ops`.
@@ -0,0 +1,254 @@
1
+ ---
2
+ name: mastermind-invites
3
+ description: Mastermind invites — manage org invitations and join request queue. Create/revoke invites with role assignment, view invite history, review pending join requests (human and agent), and approve or reject them. Merges CompanyInvites and JoinRequestQueue pages.
4
+ type: domain-skill
5
+ default_mode: confirm
6
+ ---
7
+
8
+ # Mastermind Invites
9
+
10
+ This skill is invoked by `mastermind:invites` or directly via `/mastermind:invites`.
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 invites for (required)
18
+ - `action`: list | create | revoke | copy-url | join-queue | approve-join | reject-join
19
+ - `role`: owner | admin | operator | viewer (for create; default: operator)
20
+ - `invite_id`: invite id/token (for revoke/copy-url)
21
+ - `request_id`: join request id (for approve-join/reject-join)
22
+ - `request_type`: all | human | agent (for join-queue; default: all)
23
+ - `status_filter`: pending_approval | approved | rejected (for join-queue; default: pending_approval)
24
+ - `caller`: command | master
25
+
26
+ ---
27
+
28
+ ## Invite Roles
29
+
30
+ | Role | Can Do |
31
+ |------|--------|
32
+ | `owner` | Full control — all permissions |
33
+ | `admin` | Create agents, invite users, assign tasks, approve joins, manage environments |
34
+ | `operator` | Assign tasks, run routines |
35
+ | `viewer` | Read-only access |
36
+
37
+ ---
38
+
39
+ ## Step 0 — Brain Load (standalone only)
40
+
41
+ If `caller` is not "command", load brain context following _protocol.md Brain Load Procedure with namespace: `ops`.
42
+
43
+ ---
44
+
45
+ ## Step 1 — Load Members File
46
+
47
+ ```bash
48
+ orgFile=".monomind/orgs/${org_name}.json"
49
+ [ ! -f "$orgFile" ] && { echo "ERROR: Org '${org_name}' not found."; exit 1; }
50
+
51
+ membersFile=".monomind/orgs/${org_name}-members.json"
52
+ [ ! -f "$membersFile" ] && echo '{"members":[],"join_requests":[]}' > "$membersFile"
53
+ ```
54
+
55
+ ---
56
+
57
+ ## Step 2 — Execute Action
58
+
59
+ ### list (default)
60
+
61
+ Show invite history and member count:
62
+
63
+ ```bash
64
+ echo "INVITES — org: $org_name"
65
+ echo "────────────────────────────────────────────────────────"
66
+
67
+ # Active invites (pending in join_requests with type=invite)
68
+ activeInvites=$(jq '[.join_requests[] | select(.type == "invite" and .status == "pending")] | length' "$membersFile")
69
+ totalMembers=$(jq '.members | length' "$membersFile")
70
+ pendingJoins=$(jq '[.join_requests[] | select(.type != "invite" and .status == "pending_approval")] | length' "$membersFile")
71
+
72
+ echo " Members: $totalMembers"
73
+ echo " Pending invites: $activeInvites"
74
+ echo " Pending joins: $pendingJoins"
75
+ echo ""
76
+
77
+ if [ "$activeInvites" -gt 0 ]; then
78
+ echo "ACTIVE INVITES"
79
+ printf " %-28s %-10s %-20s %s\n" "TOKEN" "ROLE" "CREATED" "URL"
80
+ echo " ────────────────────────────────────────────────────────"
81
+ jq -r '.join_requests[] | select(.type == "invite" and .status == "pending") |
82
+ [.token, (.role // "operator"), (.createdAt // "-"), (.inviteUrl // "-")] | @tsv' \
83
+ "$membersFile" | while IFS=$'\t' read -r tok role ts url; do
84
+ printf " %-28s %-10s %-20s %s\n" "${tok:0:24}…" "$role" "$ts" "${url:0:40}…"
85
+ done
86
+ fi
87
+
88
+ echo ""
89
+ echo " To create a new invite: --action create --role <role>"
90
+ [ "$pendingJoins" -gt 0 ] && echo " To review join requests: --action join-queue"
91
+ ```
92
+
93
+ ### create
94
+
95
+ ```bash
96
+ role="${role:-operator}"
97
+ case "$role" in owner|admin|operator|viewer) : ;; *)
98
+ echo "ERROR: --role must be: owner, admin, operator, viewer"; exit 1 ;;
99
+ esac
100
+
101
+ token=$(openssl rand -hex 20 2>/dev/null || python3 -c "import secrets; print(secrets.token_hex(20))")
102
+ inviteUrl="monomind://invite?org=${org_name}&token=${token}&role=${role}"
103
+ ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
104
+
105
+ tmp="${membersFile}.tmp"
106
+ jq --arg token "$token" --arg role "$role" --arg ts "$ts" --arg url "$inviteUrl" \
107
+ '.join_requests += [{"id":$token,"type":"invite","role":$role,"status":"pending",
108
+ "token":$token,"inviteUrl":$url,"createdAt":$ts}]' \
109
+ "$membersFile" > "$tmp" && mv "$tmp" "$membersFile"
110
+
111
+ echo "INVITE CREATED"
112
+ echo "────────────────────────────────────────────────────────"
113
+ echo " Role: $role"
114
+ echo " Token: $token"
115
+ echo " URL: $inviteUrl"
116
+ echo " Time: $ts"
117
+ echo ""
118
+ echo "Share the token or URL with the invitee."
119
+ echo "To copy URL hint: --action copy-url --invite-id $token"
120
+ ```
121
+
122
+ ### revoke
123
+
124
+ ```bash
125
+ [ -z "$invite_id" ] && { echo "ERROR: --invite-id required."; exit 1; }
126
+
127
+ # Find the invite
128
+ inviteExists=$(jq -r --arg id "$invite_id" \
129
+ '[.join_requests[] | select((.id == $id or .token == $id) and .type == "invite")] | length' \
130
+ "$membersFile")
131
+ [ "$inviteExists" -eq 0 ] && { echo "ERROR: Invite '$invite_id' not found or already resolved."; exit 1; }
132
+
133
+ ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
134
+ tmp="${membersFile}.tmp"
135
+ jq --arg id "$invite_id" --arg ts "$ts" \
136
+ '.join_requests = [.join_requests[] | if (.id == $id or .token == $id) then
137
+ .status = "revoked" | .resolvedAt = $ts
138
+ else . end]' \
139
+ "$membersFile" > "$tmp" && mv "$tmp" "$membersFile"
140
+
141
+ echo "Invite revoked: $invite_id"
142
+ echo " Revoked at: $ts"
143
+ ```
144
+
145
+ ### copy-url
146
+
147
+ ```bash
148
+ [ -z "$invite_id" ] && { echo "ERROR: --invite-id required."; exit 1; }
149
+
150
+ inviteUrl=$(jq -r --arg id "$invite_id" \
151
+ '.join_requests[] | select(.id == $id or .token == $id) | .inviteUrl // ""' \
152
+ "$membersFile")
153
+ [ -z "$inviteUrl" ] && { echo "ERROR: Invite '$invite_id' not found."; exit 1; }
154
+
155
+ echo "INVITE URL"
156
+ echo "────────────────────────────────────────────────────────"
157
+ echo "$inviteUrl"
158
+ echo ""
159
+ echo "(Copy the URL above to share with the invitee)"
160
+ # Try to copy to clipboard if pbcopy/xclip available
161
+ echo "$inviteUrl" | pbcopy 2>/dev/null && echo "(Copied to clipboard)" || true
162
+ ```
163
+
164
+ ### join-queue
165
+
166
+ ```bash
167
+ statusFilter="${status_filter:-pending_approval}"
168
+ typeFilter="${request_type:-all}"
169
+
170
+ echo "JOIN REQUEST QUEUE — org: $org_name"
171
+ echo " Filter: status=$statusFilter type=$typeFilter"
172
+ echo "────────────────────────────────────────────────────────"
173
+
174
+ jq -r --arg st "$statusFilter" --arg type "$typeFilter" '
175
+ .join_requests[] |
176
+ select(
177
+ (.status == $st) and
178
+ (.type != "invite") and
179
+ (if $type == "all" then true
180
+ elif $type == "human" then (.requestType == "human" or .requestType == null)
181
+ else .requestType == "agent"
182
+ end)
183
+ ) |
184
+ [.id, (.requestType // "human"), (.role // "viewer"), (.createdAt // "-"), (.message // "(no message)")] | @tsv
185
+ ' "$membersFile" | while IFS=$'\t' read -r id rtype role ts msg; do
186
+ echo ""
187
+ echo " [$id] type=$rtype role=$role at=$ts"
188
+ echo " Message: $msg"
189
+ echo " → approve: --action approve-join --request-id $id"
190
+ echo " → reject: --action reject-join --request-id $id"
191
+ done
192
+
193
+ total=$(jq --arg st "$statusFilter" '[.join_requests[] | select(.status == $st and .type != "invite")] | length' "$membersFile")
194
+ [ "$total" -eq 0 ] && echo " No join requests with status='$statusFilter'."
195
+ echo ""
196
+ echo "Total ($statusFilter): $total"
197
+ ```
198
+
199
+ ### approve-join
200
+
201
+ ```bash
202
+ [ -z "$request_id" ] && { echo "ERROR: --request-id required."; exit 1; }
203
+
204
+ reqRole=$(jq -r --arg id "$request_id" \
205
+ '.join_requests[] | select(.id == $id) | .role // "viewer"' "$membersFile")
206
+ [ -z "$reqRole" ] && { echo "ERROR: Request '$request_id' not found."; exit 1; }
207
+
208
+ ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
209
+ tmp="${membersFile}.tmp"
210
+ jq --arg id "$request_id" --arg role "$reqRole" --arg ts "$ts" \
211
+ '.join_requests = [.join_requests[] | if .id == $id then .status = "approved" | .resolvedAt = $ts else . end] |
212
+ .members += [{"id":$id,"role":$role,"status":"active","grants":[],"joinedAt":$ts}]' \
213
+ "$membersFile" > "$tmp" && mv "$tmp" "$membersFile"
214
+
215
+ echo "Join request '$request_id' approved."
216
+ echo " Role: $reqRole | Joined: $ts"
217
+ echo " Member added. View members: /mastermind:access --org $org_name --action list"
218
+ ```
219
+
220
+ ### reject-join
221
+
222
+ ```bash
223
+ [ -z "$request_id" ] && { echo "ERROR: --request-id required."; exit 1; }
224
+
225
+ exists=$(jq -r --arg id "$request_id" '[.join_requests[] | select(.id == $id)] | length' "$membersFile")
226
+ [ "$exists" -eq 0 ] && { echo "ERROR: Request '$request_id' not found."; exit 1; }
227
+
228
+ ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
229
+ tmp="${membersFile}.tmp"
230
+ jq --arg id "$request_id" --arg ts "$ts" \
231
+ '.join_requests = [.join_requests[] | if .id == $id then .status = "rejected" | .resolvedAt = $ts else . end]' \
232
+ "$membersFile" > "$tmp" && mv "$tmp" "$membersFile"
233
+
234
+ echo "Join request '$request_id' rejected."
235
+ ```
236
+
237
+ ---
238
+
239
+ ## Step 3 — Return Output
240
+
241
+ ```yaml
242
+ domain: ops
243
+ status: complete
244
+ action: <action>
245
+ org: <org_name>
246
+ pending_invites: <N>
247
+ pending_joins: <N>
248
+ ```
249
+
250
+ ---
251
+
252
+ ## Step 4 — Brain Write (standalone only)
253
+
254
+ If `caller` is not "command", follow _protocol.md Brain Write Procedure for domain `ops`.