@monoes/monomindcli 1.9.17 → 1.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/mastermind/_repeat.md +182 -39
- package/.claude/commands/mastermind/architect.md +17 -11
- package/.claude/commands/mastermind/brain.md +4 -0
- package/.claude/commands/mastermind/build.md +4 -0
- package/.claude/commands/mastermind/content.md +4 -0
- package/.claude/commands/mastermind/createorg.md +5 -3
- package/.claude/commands/mastermind/finance.md +4 -0
- package/.claude/commands/mastermind/idea.md +4 -0
- package/.claude/commands/mastermind/marketing.md +4 -0
- package/.claude/commands/mastermind/master.md +63 -37
- package/.claude/commands/mastermind/ops.md +4 -0
- package/.claude/commands/mastermind/release.md +4 -0
- package/.claude/commands/mastermind/research.md +4 -0
- package/.claude/commands/mastermind/review.md +4 -0
- package/.claude/commands/mastermind/runorg.md +5 -3
- package/.claude/commands/mastermind/sales.md +4 -0
- package/.claude/commands/mastermind/techport.md +9 -0
- package/.claude/commands/monomind/do.md +5 -1
- package/.claude/commands/monomind/idea.md +5 -1
- package/.claude/commands/monomind/improve.md +5 -1
- package/.claude/commands/monomind/repeat.md +85 -29
- package/.claude/commands/monomind/review.md +6 -2
- package/.claude/commands/monomind/understand.md +10 -8
- package/.claude/helpers/extras-registry.json +235 -235
- package/.claude/helpers/graphify-freshen.cjs +13 -1
- package/.claude/helpers/hook-handler.cjs +1 -1
- package/.claude/helpers/router.cjs +4 -1
- package/.claude/skills/mastermind/_protocol.md +28 -21
- package/.claude/skills/mastermind/access.md +236 -0
- package/.claude/skills/mastermind/activity.md +191 -0
- package/.claude/skills/mastermind/adapter-manager.md +259 -0
- package/.claude/skills/mastermind/adapters.md +204 -0
- package/.claude/skills/mastermind/agent-detail.md +242 -0
- package/.claude/skills/mastermind/agents.md +178 -0
- package/.claude/skills/mastermind/approval-detail.md +259 -0
- package/.claude/skills/mastermind/approve.md +181 -0
- package/.claude/skills/mastermind/architect.md +24 -8
- package/.claude/skills/mastermind/backup.md +197 -0
- package/.claude/skills/mastermind/bootstrap.md +190 -0
- package/.claude/skills/mastermind/budgets.md +237 -0
- package/.claude/skills/mastermind/companies.md +256 -0
- package/.claude/skills/mastermind/costs.md +151 -0
- package/.claude/skills/mastermind/createorg.md +23 -5
- package/.claude/skills/mastermind/diagnose.md +249 -0
- package/.claude/skills/mastermind/env.md +198 -0
- package/.claude/skills/mastermind/environments.md +250 -0
- package/.claude/skills/mastermind/export.md +324 -0
- package/.claude/skills/mastermind/goal-detail.md +255 -0
- package/.claude/skills/mastermind/goals.md +149 -0
- package/.claude/skills/mastermind/heartbeat.md +164 -0
- package/.claude/skills/mastermind/idea.md +250 -122
- package/.claude/skills/mastermind/import.md +281 -0
- package/.claude/skills/mastermind/inbox.md +214 -0
- package/.claude/skills/mastermind/instance-settings.md +315 -0
- package/.claude/skills/mastermind/instance.md +231 -0
- package/.claude/skills/mastermind/invite-landing.md +227 -0
- package/.claude/skills/mastermind/invites.md +254 -0
- package/.claude/skills/mastermind/issue-detail.md +291 -0
- package/.claude/skills/mastermind/issues.md +235 -0
- package/.claude/skills/mastermind/join-queue.md +170 -0
- package/.claude/skills/mastermind/liveness.md +392 -0
- package/.claude/skills/mastermind/memory.md +321 -0
- package/.claude/skills/mastermind/my-issues.md +146 -0
- package/.claude/skills/mastermind/new-agent.md +241 -0
- package/.claude/skills/mastermind/org-chart.md +207 -0
- package/.claude/skills/mastermind/org-settings.md +217 -0
- package/.claude/skills/mastermind/plan-to-tasks.md +136 -0
- package/.claude/skills/mastermind/plugin-manager.md +241 -0
- package/.claude/skills/mastermind/plugin-settings.md +273 -0
- package/.claude/skills/mastermind/plugins.md +190 -0
- package/.claude/skills/mastermind/profile.md +187 -0
- package/.claude/skills/mastermind/project-detail.md +249 -0
- package/.claude/skills/mastermind/project-workspace.md +244 -0
- package/.claude/skills/mastermind/projects.md +164 -0
- package/.claude/skills/mastermind/routine-detail.md +253 -0
- package/.claude/skills/mastermind/routines.md +202 -0
- package/.claude/skills/mastermind/runorg.md +74 -9
- package/.claude/skills/mastermind/search.md +186 -0
- package/.claude/skills/mastermind/secrets.md +199 -0
- package/.claude/skills/mastermind/skills.md +156 -0
- package/.claude/skills/mastermind/tasks.md +149 -0
- package/.claude/skills/mastermind/techport.md +5 -5
- package/.claude/skills/mastermind/threads.md +259 -0
- package/.claude/skills/mastermind/tree-control.md +250 -0
- package/.claude/skills/mastermind/wiki.md +314 -0
- package/.claude/skills/mastermind/workspace-detail.md +317 -0
- package/.claude/skills/mastermind/workspaces.md +261 -0
- package/.claude/skills/mastermind/worktree.md +187 -0
- package/dist/src/init/executor.js +8 -8
- package/dist/src/init/executor.js.map +1 -1
- package/dist/src/init/statusline-generator.d.ts.map +1 -1
- package/dist/src/init/statusline-generator.js +12 -0
- package/dist/src/init/statusline-generator.js.map +1 -1
- package/dist/src/ui/.monomind/data/ranked-context.json +1 -1
- package/dist/src/ui/.monomind/loops/mastermind-review-1778664132789.json +16 -0
- package/dist/src/ui/.monomind/sessions/current.json +5 -5
- package/dist/src/ui/.monomind/sessions/session-1776778451399.json +15 -0
- package/dist/src/ui/dashboard.html +3030 -181
- package/dist/src/ui/data/mastermind-events.jsonl +8 -0
- package/dist/src/ui/data/mastermind-sessions.json +1 -0
- package/dist/src/ui/server.mjs +738 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- 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
|
@@ -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
|
-
//
|
|
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/
|
|
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
|
-
|
|
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 `
|
|
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
|
-
`
|
|
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 `
|
|
23
|
+
Try `mcp__monomind__agentdb_context-synthesize` with query `[current prompt keywords]`, maxEntries 10.
|
|
24
24
|
If it fails, fall back to:
|
|
25
|
-
`
|
|
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 `
|
|
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 `
|
|
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 `
|
|
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 `
|
|
71
|
-
If unavailable, call `
|
|
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:**
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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`.
|