@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
@@ -30,10 +30,22 @@ function resolveMonographEntry(dir) {
30
30
  return null;
31
31
  })();
32
32
 
33
+ // Global npm installation (covers `npm install -g @monomind/cli` and homebrew installs)
34
+ const globalNpmMonograph = (() => {
35
+ try {
36
+ const { execSync } = require('child_process');
37
+ const globalRoot = execSync('npm root -g', { encoding: 'utf-8', timeout: 5000 }).trim();
38
+ const p = path.join(globalRoot, '@monoes', 'monograph', 'dist', 'src', 'index.js');
39
+ return p;
40
+ } catch { return null; }
41
+ })();
42
+
33
43
  const candidates = [
34
44
  // pnpm store version (most reliable — pre-built, isolated from workspace source changes)
35
45
  pnpmStore,
36
- // Monorepo: monobrain root is the monograph package
46
+ // Global npm / homebrew install of @monomind/cli (most common for npx/global users)
47
+ globalNpmMonograph,
48
+ // Monorepo: monomind root is the monograph package
37
49
  path.join(dir, 'dist', 'src', 'index.js'),
38
50
  // Monorepo: monograph lives under packages/@monomind/monograph (pnpm workspace)
39
51
  path.join(dir, 'packages', '@monomind', 'monograph', 'dist', 'src', 'index.js'),
@@ -1581,7 +1581,7 @@ const handlers = {
1581
1581
  var isArchitectLevel = ['architect', 'system-architect', 'software-architect'].includes(taskAgent)
1582
1582
  || /\b(architecture|design decision|adr|trade-?off|migration strategy)\b/.test(taskDescAdr);
1583
1583
  if (isArchitectLevel && taskDescAdr.length > 30) {
1584
- var adrDir = path.join(CWD, adrCfg.directory || 'docs/adr');
1584
+ var adrDir = path.join(CWD, adrCfg.directory || 'docs/adrs');
1585
1585
  fs.mkdirSync(adrDir, { recursive: true });
1586
1586
  var adrNum = (fs.readdirSync(adrDir).filter(function(f) { return f.endsWith('.md'); }).length + 1)
1587
1587
  .toString().padStart(4, '0');
@@ -425,7 +425,10 @@ function loadExtrasAgent(slug) {
425
425
  );
426
426
  if (!entry) return null;
427
427
  try {
428
- return { ...entry, content: fs.readFileSync(entry.filePath, 'utf8') };
428
+ const resolvedPath = path.isAbsolute(entry.filePath)
429
+ ? entry.filePath
430
+ : path.resolve(__dirname, entry.filePath);
431
+ return { ...entry, content: fs.readFileSync(resolvedPath, 'utf8') };
429
432
  } catch (e) { return null; }
430
433
  }
431
434
 
@@ -15,17 +15,17 @@ This file is a reference loaded by mastermind domain skills and master. It is NE
15
15
  Execute at the START of every mastermind run (master or standalone domain command). Load in this order:
16
16
 
17
17
  **Step A — Tier 3 core principles (all domains):**
18
- Try `mcp__monobrain__agentdb_hierarchical-recall` with query `"mastermind principles"`, topK 20.
18
+ Try `mcp__monomind__agentdb_hierarchical-recall` with query `"mastermind principles"`, topK 20.
19
19
  If it returns `"AgentDB bridge not available"` or any error, fall back to:
20
- `mcp__monobrain__memory_search` with query `"mastermind principles"`, namespace `"mastermind:principles"`, limit 20.
20
+ `mcp__monomind__memory_search` with query `"mastermind principles"`, namespace `"mastermind:principles"`, limit 20.
21
21
 
22
22
  **Step B — Tier 2 weekly summary for this domain:**
23
- Try `mcp__monobrain__agentdb_context-synthesize` with query `[current prompt keywords]`, maxEntries 10.
23
+ Try `mcp__monomind__agentdb_context-synthesize` with query `[current prompt keywords]`, maxEntries 10.
24
24
  If it fails, fall back to:
25
- `mcp__monobrain__memory_search` with query `[current prompt keywords]`, namespace `"mastermind:<domain>:weekly"`, limit 10.
25
+ `mcp__monomind__memory_search` with query `[current prompt keywords]`, namespace `"mastermind:<domain>:weekly"`, limit 10.
26
26
 
27
27
  **Step C — Relevant graph nodes:**
28
- Call `mcp__monobrain__graphify_query` with question `[3-5 keywords extracted from current prompt]`, depth 2.
28
+ Call `mcp__monomind__monograph_query` with question `[3-5 keywords extracted from current prompt]`, depth 2.
29
29
  If the graph is not built yet (error: "No graph found"), skip this tier — continue without graph context.
30
30
 
31
31
  Combine all results into a **BRAIN CONTEXT** block. Insert this block before any planning, decomposition, or agent spawning step. Format:
@@ -55,20 +55,20 @@ score = confidence × (1 / (days_since_run + 1)) × log(uses + 1)
55
55
  - `uses`: 1 (first write)
56
56
 
57
57
  **Step 2 — Append to Tier 1 raw log:**
58
- Try `mcp__monobrain__agentdb_hierarchical-store` with:
58
+ Try `mcp__monomind__agentdb_hierarchical-store` with:
59
59
  - namespace: `mastermind:<domain>:raw`
60
60
  - content: [full unified output schema YAML from this run, as a string]
61
61
  - metadata: `{ score, project, run_id, date: ISO8601, domain }`
62
62
 
63
- If AgentDB is unavailable, fall back to `mcp__monobrain__memory_store`:
63
+ If AgentDB is unavailable, fall back to `mcp__monomind__memory_store`:
64
64
  - key: `mastermind:<domain>:run:<run_id>`
65
65
  - value: [JSON-encoded unified output schema]
66
66
  - namespace: `mastermind:<domain>:raw`
67
67
  - tags: `["mastermind", "<domain>", "run"]`
68
68
 
69
69
  **Step 3 — Check weekly compaction trigger:**
70
- Try `mcp__monobrain__agentdb_health` on namespace `mastermind:<domain>:raw`.
71
- If unavailable, call `mcp__monobrain__memory_stats` and check entry count manually.
70
+ Try `mcp__monomind__agentdb_health` on namespace `mastermind:<domain>:raw`.
71
+ If unavailable, call `mcp__monomind__memory_stats` and check entry count manually.
72
72
  If `entry_count >= 20` OR `days_since_last_compaction >= 7`:
73
73
  1. Retrieve all Tier 1 entries since last compaction
74
74
  2. Produce a per-domain weekly summary (use LLM synthesis: "Summarize the key decisions, patterns, and lessons from these run logs in under 300 words")
@@ -130,9 +130,10 @@ score = confidence × (1 / (days_since_run + 1)) × log(uses + 1)
130
130
 
131
131
  ## Real-Time Dashboard Event Logging
132
132
 
133
- Every mastermind run MUST emit structured events to the live dashboard via WebFetch. The dashboard at `docs/mastermind-diagram.html` listens on SSE and animates each event in real time.
133
+ Every mastermind run MUST emit structured events to the live dashboard via curl (NOT WebFetch — WebFetch is blocked for localhost in Claude Code runtimes). The dashboard at `docs/mastermind-diagram.html` listens on SSE and animates each event in real time.
134
134
 
135
- **Dashboard endpoint:** `http://localhost:4242/api/mastermind/event`
135
+ **Dashboard endpoint:** `<CTRL_URL>/api/mastermind/event`
136
+ Resolve `CTRL_URL` at runtime: `jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242"` — the control server port auto-increments on collision and writes the actual URL to `.monomind/control.json`.
136
137
  **Method:** POST, `Content-Type: application/json`
137
138
 
138
139
  ### Event Types and When to Emit
@@ -210,10 +211,12 @@ Every mastermind run MUST emit structured events to the live dashboard via WebFe
210
211
 
211
212
  ### How to Emit (curl-first — WebFetch is blocked for localhost in Claude Code runtimes)
212
213
 
213
- **Always use curl via Bash.** WebFetch is restricted for `localhost` URLs in Claude Code agent runtimes and will return ECONNREFUSED even when the server is running. Use this pattern:
214
+ **Always use curl via Bash.** WebFetch is restricted for `localhost` URLs in Claude Code agent runtimes and will return ECONNREFUSED even when the server is running. **Always resolve the control URL dynamically** — the server auto-increments the port on collision. Use this pattern:
214
215
 
215
216
  ```bash
216
- curl -s -o /dev/null -X POST "http://localhost:4242/api/mastermind/event" \
217
+ REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
218
+ CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
219
+ curl -s -o /dev/null -X POST "${CTRL_URL}/api/mastermind/event" \
217
220
  -H "Content-Type: application/json" \
218
221
  -d "$(jq -cn \
219
222
  --arg sid "$SESSION_ID" \
@@ -279,7 +282,8 @@ space_id=$(monotask space list 2>/dev/null | awk -F'|' '{gsub(/^ +| +$/,"",$1);g
279
282
  [ -z "$space_id" ] && { echo "ERROR: Could not find or create space '$project_name' — verify monotask is installed (monotask --version)"; exit 1; }
280
283
 
281
284
  # Step 2 — Find existing board by canonical name or create
282
- board_id=$(monotask board list 2>/dev/null | awk -F'|' '{gsub(/^ +| +$/,"",$1);gsub(/^ +| +$/,"",$2);if($2==n)print $1}' n="$canonical" | head -1)
285
+ # board list format is "uuid: name" (colon-space separator, NOT pipe)
286
+ board_id=$(monotask board list 2>/dev/null | awk -F': ' '{gsub(/^ +| +$/,"",$1);gsub(/^ +| +$/,"",$2);if($2==n)print $1}' n="$canonical" | head -1)
283
287
  if [ -n "$board_id" ]; then
284
288
  # Step 3a — Fetch column IDs from existing board
285
289
  cols_json=$(monotask column list "$board_id" --json 2>/dev/null || echo '[]')
@@ -291,9 +295,10 @@ else
291
295
  board_id=$(monotask board create --space "$space_id" "$canonical" --json | jq -r '.id // empty')
292
296
  [ -z "$board_id" ] && { echo "ERROR: Failed to create board '$canonical'"; exit 1; }
293
297
  monotask space boards add "$space_id" "$board_id" >/dev/null 2>&1 || true
294
- todo_col=$(monotask column create "$board_id" "Todo" --json | jq -r '.id')
295
- doing_col=$(monotask column create "$board_id" "Doing" --json | jq -r '.id')
296
- done_col=$(monotask column create "$board_id" "Done" --json | jq -r '.id')
298
+ todo_col=$(monotask column create "$board_id" "Todo" --json | jq -r '.id // empty')
299
+ doing_col=$(monotask column create "$board_id" "Doing" --json | jq -r '.id // empty')
300
+ done_col=$(monotask column create "$board_id" "Done" --json | jq -r '.id // empty')
301
+ [ -z "$todo_col" ] && { echo "ERROR: Failed to create Todo column for $canonical"; exit 1; }
297
302
  fi
298
303
  ```
299
304
 
@@ -309,7 +314,8 @@ space_id=$(monotask space list 2>/dev/null | awk -F'|' '{gsub(/^ +| +$/,"",$1);g
309
314
  state_patch='{}'
310
315
  for domain in build marketing ops; do # substitute actual domain list
311
316
  canonical="${project_name}-${domain}"
312
- board_id=$(monotask board list 2>/dev/null | awk -F'|' '{gsub(/^ +| +$/,"",$1);gsub(/^ +| +$/,"",$2);if($2==n)print $1}' n="$canonical" | head -1)
317
+ # board list format is "uuid: name" (colon-space separator, NOT pipe)
318
+ board_id=$(monotask board list 2>/dev/null | awk -F': ' '{gsub(/^ +| +$/,"",$1);gsub(/^ +| +$/,"",$2);if($2==n)print $1}' n="$canonical" | head -1)
313
319
  if [ -n "$board_id" ]; then
314
320
  cols_json=$(monotask column list "$board_id" --json 2>/dev/null || echo '[]')
315
321
  todo_col=$(echo "$cols_json" | jq -r '[.[] | select(.title=="Todo" or .title=="Backlog")] | .[0].id // empty')
@@ -319,9 +325,10 @@ for domain in build marketing ops; do # substitute actual domain list
319
325
  board_id=$(monotask board create --space "$space_id" "$canonical" --json | jq -r '.id // empty')
320
326
  [ -z "$board_id" ] && { echo "ERROR: Failed to create $canonical board"; exit 1; }
321
327
  monotask space boards add "$space_id" "$board_id" >/dev/null 2>&1 || true
322
- todo_col=$(monotask column create "$board_id" "Todo" --json | jq -r '.id')
323
- doing_col=$(monotask column create "$board_id" "Doing" --json | jq -r '.id')
324
- done_col=$(monotask column create "$board_id" "Done" --json | jq -r '.id')
328
+ todo_col=$(monotask column create "$board_id" "Todo" --json | jq -r '.id // empty')
329
+ doing_col=$(monotask column create "$board_id" "Doing" --json | jq -r '.id // empty')
330
+ done_col=$(monotask column create "$board_id" "Done" --json | jq -r '.id // empty')
331
+ [ -z "$todo_col" ] && { echo "ERROR: Failed to create Todo column for $canonical"; exit 1; }
325
332
  fi
326
333
  state_patch=$(echo "$state_patch" | jq \
327
334
  --arg d "$domain" --arg b "$board_id" \
@@ -0,0 +1,236 @@
1
+ ---
2
+ name: mastermind-access
3
+ description: Mastermind access — manage org membership roles (owner/admin/operator/viewer), granular permission grants, invite tokens, and pending join requests. Controls who can do what inside an org.
4
+ type: domain-skill
5
+ default_mode: confirm
6
+ ---
7
+
8
+ # Mastermind Access
9
+
10
+ This skill is invoked by `mastermind:access` or directly via `/mastermind:access`.
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 access for (required)
18
+ - `action`: list | invite | set-role | grant | revoke | remove | suspend | join-requests | approve-join | reject-join
19
+ - `member_id`: member slug/id (required for set-role/grant/revoke/remove/suspend)
20
+ - `role`: owner | admin | operator | viewer (required for invite/set-role)
21
+ - `permission`: permission key to grant/revoke (see Permission Keys below)
22
+ - `request_id`: join request id (required for approve-join/reject-join)
23
+ - `caller`: command | master
24
+
25
+ ---
26
+
27
+ ## Permission Keys
28
+
29
+ | Key | Label | Implied by role |
30
+ |-----|-------|-----------------|
31
+ | `agents:create` | Create agents | owner, admin |
32
+ | `users:invite` | Invite members | owner, admin |
33
+ | `users:manage_permissions` | Manage members | owner |
34
+ | `tasks:assign` | Assign tasks | owner, admin, operator |
35
+ | `tasks:assign_scope` | Assign scoped tasks | — (explicit only) |
36
+ | `tasks:manage_active_checkouts` | Manage active checkouts | — (explicit only) |
37
+ | `joins:approve` | Approve join requests | owner, admin |
38
+ | `environments:manage` | Manage environments | owner, admin |
39
+
40
+ **Role implied grants:**
41
+ - `owner` → all permissions
42
+ - `admin` → agents:create, users:invite, tasks:assign, joins:approve, environments:manage
43
+ - `operator` → tasks:assign
44
+ - `viewer` → (none)
45
+
46
+ ---
47
+
48
+ ## Step 0 — Brain Load (standalone only)
49
+
50
+ If `caller` is not "command", load brain context following _protocol.md Brain Load Procedure with namespace: `ops`.
51
+
52
+ ---
53
+
54
+ ## Step 1 — Load Members File
55
+
56
+ ```bash
57
+ orgFile=".monomind/orgs/${org_name}.json"
58
+ [ ! -f "$orgFile" ] && { echo "ERROR: Org '${org_name}' not found."; exit 1; }
59
+
60
+ membersFile=".monomind/orgs/${org_name}-members.json"
61
+ [ ! -f "$membersFile" ] && cat > "$membersFile" <<'EOF'
62
+ {"members":[],"join_requests":[]}
63
+ EOF
64
+ ```
65
+
66
+ ---
67
+
68
+ ## Step 2 — Execute Action
69
+
70
+ ### list (default)
71
+
72
+ ```bash
73
+ echo "MEMBERS — org: $org_name"
74
+ echo "────────────────────────────────────────────────────────"
75
+ printf "%-20s %-10s %-12s %s\n" "ID" "ROLE" "STATUS" "EXPLICIT GRANTS"
76
+ echo "────────────────────────────────────────────────────────"
77
+
78
+ count=$(jq '.members | length' "$membersFile")
79
+ if [ "$count" -eq 0 ]; then
80
+ echo " No members. Use --action invite to add the first member."
81
+ else
82
+ jq -r '.members[] |
83
+ [.id, (.role // "viewer"), (.status // "active"),
84
+ ((.grants // []) | join(", ") | if . == "" then "(none)" else . end)]
85
+ | @tsv' "$membersFile" | while IFS=$'\t' read -r id role status grants; do
86
+ printf "%-20s %-10s %-12s %s\n" "$id" "$role" "$status" "$grants"
87
+ done
88
+ fi
89
+
90
+ # Show pending join requests
91
+ pending=$(jq '[.join_requests[] | select(.status == "pending")] | length' "$membersFile")
92
+ [ "$pending" -gt 0 ] && echo "" && echo " ⚠ $pending pending join request(s). Run --action join-requests to review."
93
+ ```
94
+
95
+ ### invite
96
+
97
+ Generate an invite token for a new member:
98
+
99
+ ```bash
100
+ [ -z "$role" ] && role="operator"
101
+ case "$role" in owner|admin|operator|viewer) : ;; *)
102
+ echo "ERROR: --role must be one of: owner, admin, operator, viewer"; exit 1 ;;
103
+ esac
104
+
105
+ inviteToken=$(openssl rand -hex 16 2>/dev/null || python3 -c "import secrets; print(secrets.token_hex(16))")
106
+ inviteUrl="https://monomind.local/invite?org=${org_name}&token=${inviteToken}&role=${role}"
107
+ ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
108
+
109
+ tmp="${membersFile}.tmp"
110
+ jq --arg token "$inviteToken" --arg role "$role" --arg ts "$ts" --arg url "$inviteUrl" \
111
+ '.join_requests += [{"id":$token,"type":"invite","role":$role,"status":"pending","token":$token,"inviteUrl":$url,"createdAt":$ts}]' \
112
+ "$membersFile" > "$tmp" && mv "$tmp" "$membersFile"
113
+
114
+ echo "INVITE CREATED"
115
+ echo " Role: $role"
116
+ echo " Token: $inviteToken"
117
+ echo " URL: $inviteUrl"
118
+ echo ""
119
+ echo "Share this URL or token with the new member."
120
+ ```
121
+
122
+ ### set-role
123
+
124
+ ```bash
125
+ [ -z "$member_id" ] && { echo "ERROR: --member-id required."; exit 1; }
126
+ [ -z "$role" ] && { echo "ERROR: --role required."; exit 1; }
127
+ case "$role" in owner|admin|operator|viewer) : ;; *)
128
+ echo "ERROR: --role must be one of: owner, admin, operator, viewer"; exit 1 ;;
129
+ esac
130
+
131
+ tmp="${membersFile}.tmp"
132
+ jq --arg id "$member_id" --arg role "$role" \
133
+ '.members = [.members[] | if .id == $id then .role = $role else . end]' \
134
+ "$membersFile" > "$tmp" && mv "$tmp" "$membersFile"
135
+ echo "Member '$member_id' role → $role"
136
+ ```
137
+
138
+ ### grant / revoke
139
+
140
+ ```bash
141
+ [ -z "$member_id" ] && { echo "ERROR: --member-id required."; exit 1; }
142
+ [ -z "$permission" ] && { echo "ERROR: --permission required (e.g. agents:create)."; exit 1; }
143
+
144
+ validKeys="agents:create users:invite users:manage_permissions tasks:assign tasks:assign_scope tasks:manage_active_checkouts joins:approve environments:manage"
145
+ echo "$validKeys" | grep -qw "$permission" || { echo "ERROR: Unknown permission '$permission'."; exit 1; }
146
+
147
+ tmp="${membersFile}.tmp"
148
+ if [ "$action" = "grant" ]; then
149
+ jq --arg id "$member_id" --arg perm "$permission" \
150
+ '.members = [.members[] | if .id == $id then .grants = ((.grants // []) + [$perm] | unique) else . end]' \
151
+ "$membersFile" > "$tmp" && mv "$tmp" "$membersFile"
152
+ echo "Granted: $permission → $member_id"
153
+ else
154
+ jq --arg id "$member_id" --arg perm "$permission" \
155
+ '.members = [.members[] | if .id == $id then .grants = ((.grants // []) | map(select(. != $perm))) else . end]' \
156
+ "$membersFile" > "$tmp" && mv "$tmp" "$membersFile"
157
+ echo "Revoked: $permission from $member_id"
158
+ fi
159
+ ```
160
+
161
+ ### remove
162
+
163
+ ```bash
164
+ [ -z "$member_id" ] && { echo "ERROR: --member-id required."; exit 1; }
165
+ tmp="${membersFile}.tmp"
166
+ jq --arg id "$member_id" '.members = [.members[] | select(.id != $id)]' \
167
+ "$membersFile" > "$tmp" && mv "$tmp" "$membersFile"
168
+ echo "Removed member: $member_id"
169
+ ```
170
+
171
+ ### suspend
172
+
173
+ ```bash
174
+ [ -z "$member_id" ] && { echo "ERROR: --member-id required."; exit 1; }
175
+ tmp="${membersFile}.tmp"
176
+ jq --arg id "$member_id" \
177
+ '.members = [.members[] | if .id == $id then .status = "suspended" else . end]' \
178
+ "$membersFile" > "$tmp" && mv "$tmp" "$membersFile"
179
+ echo "Member '$member_id' suspended. They cannot perform org actions until reinstated."
180
+ ```
181
+
182
+ ### join-requests
183
+
184
+ List pending join requests:
185
+
186
+ ```bash
187
+ echo "JOIN REQUESTS — org: $org_name"
188
+ echo "────────────────────────────────────────────────────────"
189
+ jq -r '.join_requests[] | select(.status == "pending") |
190
+ "[\(.id)] type=\(.type // "join") role=\(.role // "viewer") created=\(.createdAt // "?")
191
+ → /mastermind:access --org '"$org_name"' --action approve-join --request-id \(.id)
192
+ → /mastermind:access --org '"$org_name"' --action reject-join --request-id \(.id)"
193
+ ' "$membersFile" 2>/dev/null || echo " No pending join requests."
194
+ ```
195
+
196
+ ### approve-join / reject-join
197
+
198
+ ```bash
199
+ [ -z "$request_id" ] && { echo "ERROR: --request-id required."; exit 1; }
200
+ newStatus=$([ "$action" = "approve-join" ] && echo "approved" || echo "rejected")
201
+
202
+ tmp="${membersFile}.tmp"
203
+ if [ "$action" = "approve-join" ]; then
204
+ # Get role from request, create member record
205
+ requestRole=$(jq -r --arg id "$request_id" '.join_requests[] | select(.id == $id) | .role // "viewer"' "$membersFile")
206
+ jq --arg id "$request_id" --arg status "$newStatus" --arg role "$requestRole" --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
207
+ '.join_requests = [.join_requests[] | if .id == $id then .status = $status | .resolvedAt = $ts else . end] |
208
+ .members += [{"id":$id,"role":$role,"status":"active","grants":[],"joinedAt":$ts}]' \
209
+ "$membersFile" > "$tmp" && mv "$tmp" "$membersFile"
210
+ echo "Approved join request $request_id (role: $requestRole). Member added."
211
+ else
212
+ jq --arg id "$request_id" --arg status "$newStatus" --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
213
+ '.join_requests = [.join_requests[] | if .id == $id then .status = $status | .resolvedAt = $ts else . end]' \
214
+ "$membersFile" > "$tmp" && mv "$tmp" "$membersFile"
215
+ echo "Rejected join request $request_id."
216
+ fi
217
+ ```
218
+
219
+ ---
220
+
221
+ ## Step 3 — Return Output
222
+
223
+ ```yaml
224
+ domain: ops
225
+ status: complete
226
+ action: <action>
227
+ org: <org_name>
228
+ members_count: <N>
229
+ pending_requests: <N>
230
+ ```
231
+
232
+ ---
233
+
234
+ ## Step 4 — Brain Write (standalone only)
235
+
236
+ If `caller` is not "command", follow _protocol.md Brain Write Procedure for domain `ops`.
@@ -0,0 +1,191 @@
1
+ ---
2
+ name: mastermind-activity
3
+ description: Mastermind activity — full activity feed for an org with event-type filtering, actor lookups, and entity name resolution. Fetches up to 200 recent events across issue, project, goal, agent, and routine entity types. Mirrors Activity.tsx.
4
+ type: domain-skill
5
+ default_mode: auto
6
+ ---
7
+
8
+ # Mastermind Activity
9
+
10
+ This skill is invoked by `mastermind:activity` or directly via `/mastermind:activity`.
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 query (required)
18
+ - `action`: list | filter | export | stats
19
+ - `entity_type`: issue | project | goal | agent | routine | all (default: all)
20
+ - `actor_id`: filter by agent or user id (optional)
21
+ - `limit`: max events to show (default: 50, max: 200)
22
+ - `since`: ISO timestamp — show events after this time (optional)
23
+ - `output_file`: file path to export JSONL (for export action)
24
+ - `caller`: command | master
25
+
26
+ ---
27
+
28
+ ## Event Entity Types
29
+
30
+ | Type | Description |
31
+ |------|-------------|
32
+ | `issue` | Issue created, status changed, assigned, closed |
33
+ | `project` | Project created, updated, archived |
34
+ | `goal` | Goal created, progress updated, closed |
35
+ | `agent` | Agent started, stopped, errored, completed |
36
+ | `routine` | Routine triggered, run started, run completed |
37
+ | `approval` | Approval requested, approved, rejected |
38
+ | `workspace` | Workspace provisioned, teardown |
39
+
40
+ ---
41
+
42
+ ## Step 0 — Brain Load (standalone only)
43
+
44
+ If `caller` is not "command", load brain context following _protocol.md Brain Load Procedure with namespace: `ops`.
45
+
46
+ ---
47
+
48
+ ## Step 1 — Load Activity Log
49
+
50
+ ```bash
51
+ orgFile=".monomind/orgs/${org_name}.json"
52
+ [ ! -f "$orgFile" ] && { echo "ERROR: Org '${org_name}' not found."; exit 1; }
53
+
54
+ activityFile=".monomind/orgs/${org_name}-activity.jsonl"
55
+ limit="${limit:-50}"
56
+ entityType="${entity_type:-all}"
57
+ ```
58
+
59
+ ---
60
+
61
+ ## Step 2 — Execute Action
62
+
63
+ ### list (default)
64
+
65
+ ```bash
66
+ echo "ACTIVITY — org: $org_name"
67
+ echo " Filter: type=${entityType} limit=${limit}${actor_id:+ actor=${actor_id}}${since:+ since=${since}}"
68
+ echo "────────────────────────────────────────────────────────"
69
+ printf "%-20s %-12s %-14s %-24s %s\n" "TIMESTAMP" "ENTITY TYPE" "ACTOR" "ENTITY" "EVENT"
70
+ echo "────────────────────────────────────────────────────────"
71
+
72
+ if [ ! -f "$activityFile" ]; then
73
+ # Fall back to mastermind-events.jsonl
74
+ eventSrc="data/mastermind-events.jsonl"
75
+ if [ ! -f "$eventSrc" ]; then
76
+ echo " No activity log found. Activity is written when agents run."
77
+ exit 0
78
+ fi
79
+ activityFile="$eventSrc"
80
+ fi
81
+
82
+ tail -${limit} "$activityFile" | while IFS= read -r line; do
83
+ et=$(echo "$line" | jq -r '.entityType // .type // "unknown"')
84
+ actor=$(echo "$line" | jq -r '.actorId // .actor // "-"' | cut -c1-12)
85
+ entity=$(echo "$line" | jq -r '(.entityId // .entity // "-")' | cut -c1-22)
86
+ event=$(echo "$line" | jq -r '.event // .action // .type // "-"')
87
+ ts=$(echo "$line" | jq -r '.ts // .timestamp // .createdAt // "-"' | cut -c1-19)
88
+
89
+ # Apply filters
90
+ [ "$entityType" != "all" ] && [ "$et" != "$entityType" ] && continue
91
+ [ -n "$actor_id" ] && [ "$actor" != "$actor_id" ] && continue
92
+ [ -n "$since" ] && [ "$ts" \< "$since" ] && continue
93
+
94
+ printf "%-20s %-12s %-14s %-24s %s\n" "$ts" "$et" "$actor" "$entity" "$event"
95
+ done
96
+
97
+ echo ""
98
+ echo " Showing last $limit events. Use --limit N for more (max 200)."
99
+ ```
100
+
101
+ ### filter
102
+
103
+ ```bash
104
+ echo "ACTIVITY FILTER — org: $org_name type=$entityType"
105
+ echo "────────────────────────────────────────────────────────"
106
+
107
+ if [ ! -f "$activityFile" ]; then
108
+ echo " No activity log found."
109
+ exit 0
110
+ fi
111
+
112
+ # Show available entity types in the log
113
+ echo "AVAILABLE ENTITY TYPES IN LOG:"
114
+ tail -200 "$activityFile" | jq -r '.entityType // .type // "unknown"' 2>/dev/null | sort | uniq -c | sort -rn | while read -r cnt et; do
115
+ printf " %-20s %d events\n" "$et" "$cnt"
116
+ done
117
+
118
+ echo ""
119
+ echo " Filter: /mastermind:activity --org $org_name --action list --entity-type <type>"
120
+ ```
121
+
122
+ ### stats
123
+
124
+ ```bash
125
+ echo "ACTIVITY STATS — org: $org_name"
126
+ echo "────────────────────────────────────────────────────────"
127
+
128
+ if [ ! -f "$activityFile" ]; then
129
+ echo " No activity log found."
130
+ exit 0
131
+ fi
132
+
133
+ total=$(wc -l < "$activityFile" | tr -d ' ')
134
+ echo " Total events: $total"
135
+ echo ""
136
+
137
+ echo "BY ENTITY TYPE:"
138
+ tail -200 "$activityFile" | jq -r '.entityType // .type // "unknown"' 2>/dev/null | sort | uniq -c | sort -rn | \
139
+ while read -r cnt et; do printf " %-20s %d\n" "$et" "$cnt"; done
140
+
141
+ echo ""
142
+ echo "BY ACTOR (top 5):"
143
+ tail -200 "$activityFile" | jq -r '.actorId // .actor // "-"' 2>/dev/null | sort | uniq -c | sort -rn | head -5 | \
144
+ while read -r cnt actor; do printf " %-28s %d\n" "$actor" "$cnt"; done
145
+
146
+ # Last 24h
147
+ cutoff=$(date -u -v-24H +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u --date="24 hours ago" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null)
148
+ last24=$(tail -200 "$activityFile" | jq -r --arg c "$cutoff" 'select((.ts // .timestamp // .createdAt // "") >= $c) | .entityType' 2>/dev/null | wc -l | tr -d ' ')
149
+ echo ""
150
+ echo " Events in last 24h: $last24"
151
+ ```
152
+
153
+ ### export
154
+
155
+ ```bash
156
+ outFile="${output_file:-.monomind/exports/${org_name}-activity-$(date +%Y%m%d%H%M%S).jsonl}"
157
+ mkdir -p "$(dirname "$outFile")"
158
+
159
+ if [ ! -f "$activityFile" ]; then
160
+ echo " No activity log to export."
161
+ exit 0
162
+ fi
163
+
164
+ if [ "$entityType" != "all" ]; then
165
+ jq -r --arg et "$entityType" 'select(.entityType == $et or .type == $et)' "$activityFile" > "$outFile"
166
+ else
167
+ cp "$activityFile" "$outFile"
168
+ fi
169
+
170
+ lines=$(wc -l < "$outFile" | tr -d ' ')
171
+ echo "Activity exported: $outFile ($lines events)"
172
+ ```
173
+
174
+ ---
175
+
176
+ ## Step 3 — Return Output
177
+
178
+ ```yaml
179
+ domain: ops
180
+ status: complete
181
+ action: <action>
182
+ org: <org_name>
183
+ entity_type: <entity_type>
184
+ events_shown: <N>
185
+ ```
186
+
187
+ ---
188
+
189
+ ## Step 4 — Brain Write (standalone only)
190
+
191
+ If `caller` is not "command", follow _protocol.md Brain Write Procedure for domain `ops`.