@monoes/monomindcli 1.9.16 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/mastermind/_repeat.md +182 -39
- package/.claude/commands/mastermind/architect.md +17 -11
- package/.claude/commands/mastermind/brain.md +4 -0
- package/.claude/commands/mastermind/build.md +4 -0
- package/.claude/commands/mastermind/content.md +4 -0
- package/.claude/commands/mastermind/createorg.md +5 -3
- package/.claude/commands/mastermind/finance.md +4 -0
- package/.claude/commands/mastermind/idea.md +4 -0
- package/.claude/commands/mastermind/marketing.md +4 -0
- package/.claude/commands/mastermind/master.md +100 -46
- package/.claude/commands/mastermind/ops.md +4 -0
- package/.claude/commands/mastermind/release.md +4 -0
- package/.claude/commands/mastermind/research.md +4 -0
- package/.claude/commands/mastermind/review.md +4 -0
- package/.claude/commands/mastermind/runorg.md +5 -3
- package/.claude/commands/mastermind/sales.md +4 -0
- package/.claude/commands/mastermind/techport.md +9 -0
- package/.claude/commands/monomind/do.md +5 -1
- package/.claude/commands/monomind/idea.md +5 -1
- package/.claude/commands/monomind/improve.md +5 -1
- package/.claude/commands/monomind/repeat.md +85 -29
- package/.claude/commands/monomind/review.md +6 -2
- package/.claude/commands/monomind/understand.md +10 -8
- package/.claude/helpers/extras-registry.json +235 -235
- package/.claude/helpers/graphify-freshen.cjs +13 -1
- package/.claude/helpers/hook-handler.cjs +1 -1
- package/.claude/helpers/router.cjs +4 -1
- package/.claude/skills/mastermind/_protocol.md +37 -21
- package/.claude/skills/mastermind/access.md +236 -0
- package/.claude/skills/mastermind/activity.md +191 -0
- package/.claude/skills/mastermind/adapter-manager.md +259 -0
- package/.claude/skills/mastermind/adapters.md +204 -0
- package/.claude/skills/mastermind/agent-detail.md +242 -0
- package/.claude/skills/mastermind/agents.md +178 -0
- package/.claude/skills/mastermind/approval-detail.md +259 -0
- package/.claude/skills/mastermind/approve.md +181 -0
- package/.claude/skills/mastermind/architect.md +24 -8
- package/.claude/skills/mastermind/backup.md +197 -0
- package/.claude/skills/mastermind/bootstrap.md +190 -0
- package/.claude/skills/mastermind/budgets.md +237 -0
- package/.claude/skills/mastermind/companies.md +256 -0
- package/.claude/skills/mastermind/costs.md +151 -0
- package/.claude/skills/mastermind/createorg.md +23 -5
- package/.claude/skills/mastermind/diagnose.md +249 -0
- package/.claude/skills/mastermind/env.md +198 -0
- package/.claude/skills/mastermind/environments.md +250 -0
- package/.claude/skills/mastermind/export.md +324 -0
- package/.claude/skills/mastermind/goal-detail.md +255 -0
- package/.claude/skills/mastermind/goals.md +149 -0
- package/.claude/skills/mastermind/heartbeat.md +164 -0
- package/.claude/skills/mastermind/idea.md +318 -186
- package/.claude/skills/mastermind/import.md +281 -0
- package/.claude/skills/mastermind/inbox.md +214 -0
- package/.claude/skills/mastermind/instance-settings.md +315 -0
- package/.claude/skills/mastermind/instance.md +231 -0
- package/.claude/skills/mastermind/invite-landing.md +227 -0
- package/.claude/skills/mastermind/invites.md +254 -0
- package/.claude/skills/mastermind/issue-detail.md +291 -0
- package/.claude/skills/mastermind/issues.md +235 -0
- package/.claude/skills/mastermind/join-queue.md +170 -0
- package/.claude/skills/mastermind/liveness.md +392 -0
- package/.claude/skills/mastermind/memory.md +321 -0
- package/.claude/skills/mastermind/my-issues.md +146 -0
- package/.claude/skills/mastermind/new-agent.md +241 -0
- package/.claude/skills/mastermind/org-chart.md +207 -0
- package/.claude/skills/mastermind/org-settings.md +217 -0
- package/.claude/skills/mastermind/plan-to-tasks.md +136 -0
- package/.claude/skills/mastermind/plugin-manager.md +241 -0
- package/.claude/skills/mastermind/plugin-settings.md +273 -0
- package/.claude/skills/mastermind/plugins.md +190 -0
- package/.claude/skills/mastermind/profile.md +187 -0
- package/.claude/skills/mastermind/project-detail.md +249 -0
- package/.claude/skills/mastermind/project-workspace.md +244 -0
- package/.claude/skills/mastermind/projects.md +164 -0
- package/.claude/skills/mastermind/routine-detail.md +253 -0
- package/.claude/skills/mastermind/routines.md +202 -0
- package/.claude/skills/mastermind/runorg.md +74 -9
- package/.claude/skills/mastermind/search.md +186 -0
- package/.claude/skills/mastermind/secrets.md +199 -0
- package/.claude/skills/mastermind/skills.md +156 -0
- package/.claude/skills/mastermind/tasks.md +149 -0
- package/.claude/skills/mastermind/techport.md +5 -5
- package/.claude/skills/mastermind/threads.md +259 -0
- package/.claude/skills/mastermind/tree-control.md +250 -0
- package/.claude/skills/mastermind/wiki.md +314 -0
- package/.claude/skills/mastermind/workspace-detail.md +317 -0
- package/.claude/skills/mastermind/workspaces.md +261 -0
- package/.claude/skills/mastermind/worktree.md +187 -0
- package/dist/src/init/executor.js +8 -8
- package/dist/src/init/executor.js.map +1 -1
- package/dist/src/init/statusline-generator.d.ts.map +1 -1
- package/dist/src/init/statusline-generator.js +12 -0
- package/dist/src/init/statusline-generator.js.map +1 -1
- package/dist/src/ui/.monomind/data/ranked-context.json +1 -1
- package/dist/src/ui/.monomind/loops/mastermind-review-1778664132789.json +16 -0
- package/dist/src/ui/.monomind/sessions/current.json +5 -5
- package/dist/src/ui/.monomind/sessions/session-1776778451399.json +15 -0
- package/dist/src/ui/dashboard.html +3030 -181
- package/dist/src/ui/data/mastermind-events.jsonl +8 -0
- package/dist/src/ui/data/mastermind-sessions.json +1 -0
- package/dist/src/ui/server.mjs +738 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/.claude/skills/.monomind/data/ranked-context.json +0 -5
- package/.claude/skills/.monomind/sessions/current.json +0 -13
- package/.claude/skills/.monomind/sessions/session-1777829336455.json +0 -15
- package/.claude/skills/.monomind/sessions/session-1777831614725.json +0 -15
- package/.claude/skills/.monomind/sessions/session-1777832095857.json +0 -15
- package/.claude/skills/.monomind/sessions/session-1777839814183.json +0 -15
- package/.claude/skills/.monomind/sessions/session-1777841847131.json +0 -15
- package/.claude/skills/.monomind/sessions/session-1777843309463.json +0 -15
- package/.claude/skills/.monomind/sessions/session-1777880867159.json +0 -15
- package/.claude/skills/.monomind/sessions/session-1777881884593.json +0 -15
- package/.claude/skills/.monomind/sessions/session-1777884090471.json +0 -15
- package/.claude/skills/.monomind/sessions/session-1777884808221.json +0 -15
- package/.claude/skills/.monomind/sessions/session-1777885672155.json +0 -15
- package/.claude/skills/.monomind/sessions/session-1777886852818.json +0 -15
- package/.claude/skills/.monomind/sessions/session-1777896532690.json +0 -15
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mastermind-budgets
|
|
3
|
+
description: Mastermind budgets — view, set, and track token/cost budgets for agents and the entire org. Shows current spend vs. limits, alerts on overages, and lets board members adjust per-agent or org-wide budgets. Reads from -budgets.json org state files.
|
|
4
|
+
type: domain-skill
|
|
5
|
+
default_mode: auto
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Mastermind Budgets
|
|
9
|
+
|
|
10
|
+
This skill is invoked by `mastermind:budgets` or directly via `/mastermind:budgets`.
|
|
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 budgets for (required)
|
|
18
|
+
- `action`: show | set | reset | alert
|
|
19
|
+
- `agent_id`: scope to a specific agent (optional — omit for org-wide)
|
|
20
|
+
- `limit_tokens`: token limit to set (for set)
|
|
21
|
+
- `limit_usd`: USD cost limit to set (for set)
|
|
22
|
+
- `caller`: command | master
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Step 0 — Brain Load (standalone only)
|
|
27
|
+
|
|
28
|
+
If `caller` is not "command", load brain context following _protocol.md Brain Load Procedure with namespace: `ops`.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Step 1 — Load Org and Budget File
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
orgFile=".monomind/orgs/${org_name}.json"
|
|
36
|
+
[ ! -f "$orgFile" ] && { echo "ERROR: Org '${org_name}' not found."; exit 1; }
|
|
37
|
+
|
|
38
|
+
budgetsFile=".monomind/orgs/${org_name}-budgets.json"
|
|
39
|
+
stateFile=".monomind/orgs/${org_name}-state.json"
|
|
40
|
+
|
|
41
|
+
if [ ! -f "$budgetsFile" ]; then
|
|
42
|
+
echo '{"org_budget":{},"agent_budgets":{},"period":"monthly","currency":"USD"}' > "$budgetsFile"
|
|
43
|
+
fi
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Step 2 — Execute Action
|
|
49
|
+
|
|
50
|
+
### show (default)
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
echo "BUDGETS — $org_name"
|
|
54
|
+
echo "════════════════════════════════════════════════════════"
|
|
55
|
+
|
|
56
|
+
python3 - "$orgFile" "$budgetsFile" "$stateFile" "${agent_id:-}" <<'PYEOF'
|
|
57
|
+
import json, sys, os
|
|
58
|
+
|
|
59
|
+
orgData = json.load(open(sys.argv[1]))
|
|
60
|
+
budgetData = json.load(open(sys.argv[2]))
|
|
61
|
+
statePath = sys.argv[3]
|
|
62
|
+
agentFilter= sys.argv[4]
|
|
63
|
+
|
|
64
|
+
# Load heartbeat state for per-agent token usage
|
|
65
|
+
agentTokens = {}
|
|
66
|
+
if os.path.exists(statePath):
|
|
67
|
+
try:
|
|
68
|
+
state = json.load(open(statePath))
|
|
69
|
+
for r in state.get("roles", []):
|
|
70
|
+
rid = r.get("id","")
|
|
71
|
+
agentTokens[rid] = {
|
|
72
|
+
"tokensIn": r.get("tokens_in", 0),
|
|
73
|
+
"tokensOut": r.get("tokens_out", 0),
|
|
74
|
+
"totalCostUsd": r.get("total_cost_usd", 0.0),
|
|
75
|
+
}
|
|
76
|
+
except: pass
|
|
77
|
+
|
|
78
|
+
orgBudget = budgetData.get("org_budget", {})
|
|
79
|
+
agentBudgets= budgetData.get("agent_budgets", {})
|
|
80
|
+
period = budgetData.get("period", "monthly")
|
|
81
|
+
|
|
82
|
+
print(f" Period: {period}")
|
|
83
|
+
print()
|
|
84
|
+
|
|
85
|
+
# Org-wide budget
|
|
86
|
+
orgLimitTokens = orgBudget.get("limit_tokens")
|
|
87
|
+
orgLimitUsd = orgBudget.get("limit_usd")
|
|
88
|
+
orgSpentTokens = sum(v.get("tokensIn",0) + v.get("tokensOut",0) for v in agentTokens.values())
|
|
89
|
+
orgSpentUsd = sum(v.get("totalCostUsd",0) for v in agentTokens.values())
|
|
90
|
+
|
|
91
|
+
print("ORG BUDGET")
|
|
92
|
+
print("────────────────────────────────────────────────────────")
|
|
93
|
+
print(f" Tokens spent: {orgSpentTokens:>12,}")
|
|
94
|
+
if orgLimitTokens:
|
|
95
|
+
pct = orgSpentTokens / orgLimitTokens * 100
|
|
96
|
+
bar = "█" * int(pct / 5) + "░" * (20 - int(pct / 5))
|
|
97
|
+
print(f" Token limit: {orgLimitTokens:>12,} [{bar}] {pct:.1f}%")
|
|
98
|
+
print(f" Cost (USD): ${orgSpentUsd:>11.4f}")
|
|
99
|
+
if orgLimitUsd:
|
|
100
|
+
pct = orgSpentUsd / orgLimitUsd * 100
|
|
101
|
+
bar = "█" * int(pct / 5) + "░" * (20 - int(pct / 5))
|
|
102
|
+
print(f" Cost limit: ${orgLimitUsd:>11.2f} [{bar}] {pct:.1f}%")
|
|
103
|
+
print()
|
|
104
|
+
|
|
105
|
+
# Per-agent budgets
|
|
106
|
+
roles = orgData.get("roles", [])
|
|
107
|
+
if agentFilter:
|
|
108
|
+
roles = [r for r in roles if r.get("id") == agentFilter]
|
|
109
|
+
|
|
110
|
+
print("AGENT BUDGETS")
|
|
111
|
+
print("────────────────────────────────────────────────────────")
|
|
112
|
+
print(f" {'AGENT':<28} {'TOKENS IN':<12} {'TOKENS OUT':<12} {'COST USD':<12} {'LIMIT USD'}")
|
|
113
|
+
print(" " + "─" * 82)
|
|
114
|
+
|
|
115
|
+
for role in roles:
|
|
116
|
+
rid = role.get("id","?")
|
|
117
|
+
title = role.get("title", rid)[:26]
|
|
118
|
+
tok = agentTokens.get(rid, {})
|
|
119
|
+
tIn = tok.get("tokensIn", 0)
|
|
120
|
+
tOut = tok.get("tokensOut", 0)
|
|
121
|
+
cost = tok.get("totalCostUsd", 0.0)
|
|
122
|
+
lim = agentBudgets.get(rid, {}).get("limit_usd")
|
|
123
|
+
limStr= f"${lim:.2f}" if lim else "—"
|
|
124
|
+
|
|
125
|
+
overBudget = lim and cost > lim
|
|
126
|
+
flag = " ⚠ OVER" if overBudget else ""
|
|
127
|
+
print(f" {title:<28} {tIn:<12,} {tOut:<12,} ${cost:<11.4f} {limStr}{flag}")
|
|
128
|
+
|
|
129
|
+
if not roles:
|
|
130
|
+
print(" (no agents)")
|
|
131
|
+
PYEOF
|
|
132
|
+
|
|
133
|
+
echo ""
|
|
134
|
+
echo " Set limit: /mastermind:budgets --org $org_name --action set --agent-id <id> --limit-usd 5.00"
|
|
135
|
+
echo " Reset: /mastermind:budgets --org $org_name --action reset"
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### set
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
142
|
+
|
|
143
|
+
python3 - "$budgetsFile" "${agent_id:-}" "${limit_tokens:-}" "${limit_usd:-}" "$ts" <<'PYEOF'
|
|
144
|
+
import json, sys
|
|
145
|
+
|
|
146
|
+
path, agentId, limitTok, limitUsd, ts = sys.argv[1:]
|
|
147
|
+
|
|
148
|
+
data = json.load(open(path))
|
|
149
|
+
|
|
150
|
+
if agentId:
|
|
151
|
+
entry = data.setdefault("agent_budgets", {}).setdefault(agentId, {})
|
|
152
|
+
if limitTok: entry["limit_tokens"] = int(limitTok)
|
|
153
|
+
if limitUsd: entry["limit_usd"] = float(limitUsd)
|
|
154
|
+
entry["updatedAt"] = ts
|
|
155
|
+
print(f" Budget set for agent '{agentId}':")
|
|
156
|
+
if limitTok: print(f" Token limit: {int(limitTok):,}")
|
|
157
|
+
if limitUsd: print(f" USD limit: ${float(limitUsd):.2f}")
|
|
158
|
+
else:
|
|
159
|
+
org = data.setdefault("org_budget", {})
|
|
160
|
+
if limitTok: org["limit_tokens"] = int(limitTok)
|
|
161
|
+
if limitUsd: org["limit_usd"] = float(limitUsd)
|
|
162
|
+
org["updatedAt"] = ts
|
|
163
|
+
print(f" Org-wide budget updated:")
|
|
164
|
+
if limitTok: print(f" Token limit: {int(limitTok):,}")
|
|
165
|
+
if limitUsd: print(f" USD limit: ${float(limitUsd):.2f}")
|
|
166
|
+
|
|
167
|
+
with open(path, "w") as f:
|
|
168
|
+
json.dump(data, f, indent=2)
|
|
169
|
+
PYEOF
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### reset
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
176
|
+
echo '{"org_budget":{},"agent_budgets":{},"period":"monthly","currency":"USD","resetAt":"'"$ts"'"}' > "$budgetsFile"
|
|
177
|
+
echo " Budget counters reset for '$org_name' ($ts)"
|
|
178
|
+
echo " Note: resets limit configs too. Re-run --action set to restore limits."
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### alert
|
|
182
|
+
|
|
183
|
+
Check for agents over their budget limits:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
echo "BUDGET ALERTS — $org_name"
|
|
187
|
+
echo "────────────────────────────────────────────────────────"
|
|
188
|
+
|
|
189
|
+
python3 - "$orgFile" "$budgetsFile" "$stateFile" <<'PYEOF'
|
|
190
|
+
import json, sys, os
|
|
191
|
+
|
|
192
|
+
orgData = json.load(open(sys.argv[1]))
|
|
193
|
+
budgetData = json.load(open(sys.argv[2]))
|
|
194
|
+
statePath = sys.argv[3]
|
|
195
|
+
|
|
196
|
+
agentTokens = {}
|
|
197
|
+
if os.path.exists(statePath):
|
|
198
|
+
try:
|
|
199
|
+
state = json.load(open(statePath))
|
|
200
|
+
for r in state.get("roles", []):
|
|
201
|
+
rid = r.get("id","")
|
|
202
|
+
agentTokens[rid] = r.get("total_cost_usd", 0.0)
|
|
203
|
+
except: pass
|
|
204
|
+
|
|
205
|
+
agentBudgets = budgetData.get("agent_budgets", {})
|
|
206
|
+
alerts = []
|
|
207
|
+
for rid, ab in agentBudgets.items():
|
|
208
|
+
lim = ab.get("limit_usd")
|
|
209
|
+
spent = agentTokens.get(rid, 0.0)
|
|
210
|
+
if lim and spent > lim:
|
|
211
|
+
alerts.append((rid, spent, lim))
|
|
212
|
+
|
|
213
|
+
if not alerts:
|
|
214
|
+
print(" ✓ All agents within budget.")
|
|
215
|
+
else:
|
|
216
|
+
print(f" ⚠ {len(alerts)} agent(s) over budget:")
|
|
217
|
+
for rid, spent, lim in alerts:
|
|
218
|
+
print(f" {rid}: spent ${spent:.4f} / limit ${lim:.2f}")
|
|
219
|
+
PYEOF
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Step 3 — Return Output
|
|
225
|
+
|
|
226
|
+
```yaml
|
|
227
|
+
domain: ops
|
|
228
|
+
status: complete
|
|
229
|
+
action: <action>
|
|
230
|
+
org_name: <org_name>
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Step 4 — Brain Write (standalone only)
|
|
236
|
+
|
|
237
|
+
If `caller` is not "command", follow _protocol.md Brain Write Procedure for domain `ops`.
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mastermind-companies
|
|
3
|
+
description: Mastermind companies — multi-org management. List all orgs with agent/issue stats, switch the active org, rename an org, delete an org (with confirmation), and view per-org summary. Mirrors Companies.tsx.
|
|
4
|
+
type: domain-skill
|
|
5
|
+
default_mode: confirm
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Mastermind Companies
|
|
9
|
+
|
|
10
|
+
This skill is invoked by `mastermind:companies` or directly via `/mastermind:companies`.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Inputs
|
|
15
|
+
|
|
16
|
+
- `brain_context`: BRAIN CONTEXT block (injected by command, or loaded below if standalone)
|
|
17
|
+
- `action`: list | select | rename | delete | stats | create
|
|
18
|
+
- `org_name`: org name to target (required for select/rename/delete/stats)
|
|
19
|
+
- `new_name`: new org name (required for rename)
|
|
20
|
+
- `confirm`: yes (required second step for delete)
|
|
21
|
+
- `caller`: command | master
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Step 0 — Brain Load (standalone only)
|
|
26
|
+
|
|
27
|
+
If `caller` is not "command", load brain context following _protocol.md Brain Load Procedure with namespace: `ops`.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Step 1 — Locate All Orgs
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
orgsDir=".monomind/orgs"
|
|
35
|
+
[ ! -d "$orgsDir" ] && { echo "No orgs directory found. Run /mastermind:createorg to create your first org."; exit 0; }
|
|
36
|
+
|
|
37
|
+
# Collect all org names from .json files (not -members, -issues, etc.)
|
|
38
|
+
orgNames=$(ls "$orgsDir"/*.json 2>/dev/null | grep -v '\-' | xargs -I{} basename {} .json | sort)
|
|
39
|
+
|
|
40
|
+
# Track active org
|
|
41
|
+
activeFile=".monomind/active-org"
|
|
42
|
+
activeOrg=$([ -f "$activeFile" ] && cat "$activeFile" || echo "")
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Step 2 — Execute Action
|
|
48
|
+
|
|
49
|
+
### list (default)
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
echo "ORGS"
|
|
53
|
+
echo "────────────────────────────────────────────────────────"
|
|
54
|
+
printf "%-22s %-8s %-8s %-8s %s\n" "NAME" "AGENTS" "ISSUES" "MEMBERS" "STATUS"
|
|
55
|
+
echo "────────────────────────────────────────────────────────"
|
|
56
|
+
|
|
57
|
+
[ -z "$orgNames" ] && { echo " No orgs found. Create one: /mastermind:createorg"; exit 0; }
|
|
58
|
+
|
|
59
|
+
echo "$orgNames" | while read -r name; do
|
|
60
|
+
orgFile="$orgsDir/${name}.json"
|
|
61
|
+
[ ! -f "$orgFile" ] && continue
|
|
62
|
+
|
|
63
|
+
agents=$(jq -r '(.roles // []) | length' "$orgFile" 2>/dev/null || echo 0)
|
|
64
|
+
issues=$([ -f "$orgsDir/${name}-issues.json" ] && jq '[.issues[] | select(.status == "open" or .status == "in_progress")] | length' "$orgsDir/${name}-issues.json" 2>/dev/null || echo 0)
|
|
65
|
+
members=$([ -f "$orgsDir/${name}-members.json" ] && jq '.members | length' "$orgsDir/${name}-members.json" 2>/dev/null || echo 0)
|
|
66
|
+
status=$([ "$name" = "$activeOrg" ] && echo "● ACTIVE" || echo "")
|
|
67
|
+
|
|
68
|
+
printf "%-22s %-8s %-8s %-8s %s\n" "$name" "$agents" "$issues" "$members" "$status"
|
|
69
|
+
done
|
|
70
|
+
|
|
71
|
+
echo ""
|
|
72
|
+
total=$(echo "$orgNames" | wc -w | tr -d ' ')
|
|
73
|
+
echo " Total orgs: $total"
|
|
74
|
+
[ -n "$activeOrg" ] && echo " Active org: $activeOrg"
|
|
75
|
+
echo ""
|
|
76
|
+
echo " Select: --action select --org-name <name>"
|
|
77
|
+
echo " Rename: --action rename --org-name <name> --new-name <new>"
|
|
78
|
+
echo " Delete: --action delete --org-name <name>"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### select
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
[ -z "$org_name" ] && { echo "ERROR: --org-name required."; exit 1; }
|
|
85
|
+
|
|
86
|
+
orgFile="$orgsDir/${org_name}.json"
|
|
87
|
+
[ ! -f "$orgFile" ] && { echo "ERROR: Org '$org_name' not found."; exit 1; }
|
|
88
|
+
|
|
89
|
+
echo "$org_name" > "$activeFile"
|
|
90
|
+
echo "Active org set to: $org_name"
|
|
91
|
+
echo " All mastermind commands will default to this org."
|
|
92
|
+
echo " To use a different org, pass --org-name explicitly."
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### rename
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
[ -z "$org_name" ] && { echo "ERROR: --org-name required."; exit 1; }
|
|
99
|
+
[ -z "$new_name" ] && { echo "ERROR: --new-name required."; exit 1; }
|
|
100
|
+
|
|
101
|
+
# Validate new name
|
|
102
|
+
echo "$new_name" | grep -qE '^[a-z0-9][a-z0-9_-]*$' || {
|
|
103
|
+
echo "ERROR: --new-name must be lowercase alphanumeric with hyphens/underscores (e.g. my-org)."
|
|
104
|
+
exit 1
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
orgFile="$orgsDir/${org_name}.json"
|
|
108
|
+
[ ! -f "$orgFile" ] && { echo "ERROR: Org '$org_name' not found."; exit 1; }
|
|
109
|
+
|
|
110
|
+
[ -f "$orgsDir/${new_name}.json" ] && { echo "ERROR: Org '$new_name' already exists."; exit 1; }
|
|
111
|
+
|
|
112
|
+
# Rename main config file
|
|
113
|
+
mv "$orgFile" "$orgsDir/${new_name}.json"
|
|
114
|
+
|
|
115
|
+
# Rename all associated files
|
|
116
|
+
for suffix in members issues goals projects routines approvals adapters plugins environments workspaces activity threads budgets; do
|
|
117
|
+
f="$orgsDir/${org_name}-${suffix}.json"
|
|
118
|
+
[ -f "$f" ] && mv "$f" "$orgsDir/${new_name}-${suffix}.json"
|
|
119
|
+
f2="$orgsDir/${org_name}-${suffix}.jsonl"
|
|
120
|
+
[ -f "$f2" ] && mv "$f2" "$orgsDir/${new_name}-${suffix}.jsonl"
|
|
121
|
+
done
|
|
122
|
+
|
|
123
|
+
# Update name field inside config
|
|
124
|
+
tmp="$orgsDir/${new_name}.json.tmp"
|
|
125
|
+
jq --arg n "$new_name" '.name = $n' "$orgsDir/${new_name}.json" > "$tmp" && mv "$tmp" "$orgsDir/${new_name}.json"
|
|
126
|
+
|
|
127
|
+
# Update active org pointer if needed
|
|
128
|
+
[ "$(cat "$activeFile" 2>/dev/null)" = "$org_name" ] && echo "$new_name" > "$activeFile"
|
|
129
|
+
|
|
130
|
+
echo "Org renamed: $org_name → $new_name"
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### delete
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
[ -z "$org_name" ] && { echo "ERROR: --org-name required."; exit 1; }
|
|
137
|
+
|
|
138
|
+
orgFile="$orgsDir/${org_name}.json"
|
|
139
|
+
[ ! -f "$orgFile" ] && { echo "ERROR: Org '$org_name' not found."; exit 1; }
|
|
140
|
+
|
|
141
|
+
if [ "${confirm:-}" != "yes" ]; then
|
|
142
|
+
agentCount=$(jq '(.roles // []) | length' "$orgFile" 2>/dev/null || echo "?")
|
|
143
|
+
echo "DELETE CONFIRMATION REQUIRED"
|
|
144
|
+
echo "────────────────────────────────────────────────────────"
|
|
145
|
+
echo " Org: $org_name"
|
|
146
|
+
echo " Agents: $agentCount"
|
|
147
|
+
echo ""
|
|
148
|
+
echo " This will permanently delete the org and ALL associated data."
|
|
149
|
+
echo " To confirm: --action delete --org-name $org_name --confirm yes"
|
|
150
|
+
exit 0
|
|
151
|
+
fi
|
|
152
|
+
|
|
153
|
+
# Delete all org files
|
|
154
|
+
rm -f "$orgsDir/${org_name}.json"
|
|
155
|
+
for suffix in members issues goals projects routines approvals adapters plugins environments workspaces activity threads budgets project-workspaces approval-comments; do
|
|
156
|
+
rm -f "$orgsDir/${org_name}-${suffix}.json"
|
|
157
|
+
rm -f "$orgsDir/${org_name}-${suffix}.jsonl"
|
|
158
|
+
done
|
|
159
|
+
|
|
160
|
+
# Clear active org if it was this org
|
|
161
|
+
[ "$(cat "$activeFile" 2>/dev/null)" = "$org_name" ] && rm -f "$activeFile"
|
|
162
|
+
|
|
163
|
+
echo "Org '$org_name' deleted permanently."
|
|
164
|
+
echo " All associated data removed."
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### stats
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
[ -z "$org_name" ] && { echo "ERROR: --org-name required."; exit 1; }
|
|
171
|
+
|
|
172
|
+
orgFile="$orgsDir/${org_name}.json"
|
|
173
|
+
[ ! -f "$orgFile" ] && { echo "ERROR: Org '$org_name' not found."; exit 1; }
|
|
174
|
+
|
|
175
|
+
echo "ORG STATS — $org_name"
|
|
176
|
+
echo "────────────────────────────────────────────────────────"
|
|
177
|
+
|
|
178
|
+
# Agent stats
|
|
179
|
+
agents=$(jq '(.roles // []) | length' "$orgFile")
|
|
180
|
+
gov=$(jq -r '.governance // "(not set)"' "$orgFile")
|
|
181
|
+
echo " Agents: $agents"
|
|
182
|
+
echo " Governance: $gov"
|
|
183
|
+
|
|
184
|
+
# Issues
|
|
185
|
+
if [ -f "$orgsDir/${org_name}-issues.json" ]; then
|
|
186
|
+
total=$(jq '.issues | length' "$orgsDir/${org_name}-issues.json")
|
|
187
|
+
open=$(jq '[.issues[] | select(.status == "open" or .status == "in_progress")] | length' "$orgsDir/${org_name}-issues.json")
|
|
188
|
+
done=$(jq '[.issues[] | select(.status == "done")] | length' "$orgsDir/${org_name}-issues.json")
|
|
189
|
+
echo " Issues: $total total ($open open, $done done)"
|
|
190
|
+
fi
|
|
191
|
+
|
|
192
|
+
# Goals
|
|
193
|
+
[ -f "$orgsDir/${org_name}-goals.json" ] && echo " Goals: $(jq '.goals | length' "$orgsDir/${org_name}-goals.json")"
|
|
194
|
+
|
|
195
|
+
# Projects
|
|
196
|
+
[ -f "$orgsDir/${org_name}-projects.json" ] && echo " Projects: $(jq '.projects | length' "$orgsDir/${org_name}-projects.json")"
|
|
197
|
+
|
|
198
|
+
# Members
|
|
199
|
+
[ -f "$orgsDir/${org_name}-members.json" ] && echo " Members: $(jq '.members | length' "$orgsDir/${org_name}-members.json")"
|
|
200
|
+
|
|
201
|
+
# Routines
|
|
202
|
+
[ -f "$orgsDir/${org_name}-routines.json" ] && echo " Routines: $(jq '.routines | length' "$orgsDir/${org_name}-routines.json")"
|
|
203
|
+
|
|
204
|
+
# Pending approvals
|
|
205
|
+
if [ -f "$orgsDir/${org_name}-approvals.json" ]; then
|
|
206
|
+
pending=$(jq '[.approvals[] | select(.status == "pending")] | length' "$orgsDir/${org_name}-approvals.json")
|
|
207
|
+
echo " Pending approvals: $pending"
|
|
208
|
+
fi
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### create
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
[ -z "$org_name" ] && { echo "ERROR: --org-name required."; exit 1; }
|
|
215
|
+
|
|
216
|
+
echo "$org_name" | grep -qE '^[a-z0-9][a-z0-9_-]*$' || {
|
|
217
|
+
echo "ERROR: --org-name must be lowercase alphanumeric with hyphens/underscores."
|
|
218
|
+
exit 1
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
orgFile="$orgsDir/${org_name}.json"
|
|
222
|
+
[ -f "$orgFile" ] && { echo "ERROR: Org '$org_name' already exists."; exit 1; }
|
|
223
|
+
|
|
224
|
+
mkdir -p "$orgsDir"
|
|
225
|
+
ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
226
|
+
cat > "$orgFile" <<EOF
|
|
227
|
+
{
|
|
228
|
+
"name": "$org_name",
|
|
229
|
+
"governance": "auto",
|
|
230
|
+
"roles": [],
|
|
231
|
+
"created_at": "$ts"
|
|
232
|
+
}
|
|
233
|
+
EOF
|
|
234
|
+
|
|
235
|
+
echo "Org '$org_name' created."
|
|
236
|
+
echo " Add agents: /mastermind:new-agent --org $org_name --title 'My Agent'"
|
|
237
|
+
echo " Run: /mastermind:runorg --org $org_name"
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Step 3 — Return Output
|
|
243
|
+
|
|
244
|
+
```yaml
|
|
245
|
+
domain: ops
|
|
246
|
+
status: complete
|
|
247
|
+
action: <action>
|
|
248
|
+
org_name: <org_name>
|
|
249
|
+
active_org: <active_org>
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Step 4 — Brain Write (standalone only)
|
|
255
|
+
|
|
256
|
+
If `caller` is not "command", follow _protocol.md Brain Write Procedure for domain `ops`.
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mastermind-costs
|
|
3
|
+
description: Mastermind costs — track token spend and budget burn rate per agent in a running org. Shows current window spend, budget policy, and alerts when approaching limits.
|
|
4
|
+
type: domain-skill
|
|
5
|
+
default_mode: auto
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Mastermind Costs
|
|
9
|
+
|
|
10
|
+
This skill is invoked by `mastermind:costs` or directly via `/mastermind:costs`.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Inputs
|
|
15
|
+
|
|
16
|
+
- `brain_context`: BRAIN CONTEXT block
|
|
17
|
+
- `org_name`: org to inspect costs for
|
|
18
|
+
- `action`: report | set-budget | alert
|
|
19
|
+
- `budget_tokens`: max token budget for the org run (for set-budget)
|
|
20
|
+
- `alert_threshold`: fraction of budget that triggers alert (default 0.8)
|
|
21
|
+
- `caller`: command | master
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Step 0 — Brain Load (standalone only)
|
|
26
|
+
|
|
27
|
+
If `caller` is not "command", load brain context following _protocol.md Brain Load Procedure with namespace: `ops`.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Step 1 — Load Cost Data
|
|
32
|
+
|
|
33
|
+
Cost data is pulled from monomind's token summary and the org state file:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
orgFile=".monomind/orgs/${org_name}.json"
|
|
37
|
+
stateFile=".monomind/orgs/${org_name}-state.json"
|
|
38
|
+
tokenSummary=".monomind/metrics/token-summary.json"
|
|
39
|
+
|
|
40
|
+
memNs="org:${org_name}"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Step 2 — Execute Action
|
|
46
|
+
|
|
47
|
+
### report (default)
|
|
48
|
+
|
|
49
|
+
Aggregate token usage per agent from state file and token summary:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# Get total token usage from the global summary for context
|
|
53
|
+
if [ -f "$tokenSummary" ]; then
|
|
54
|
+
echo "=== GLOBAL TOKEN SUMMARY ==="
|
|
55
|
+
jq -r '"Today: $\(.today.cost | tostring) calls: \(.today.calls)"' "$tokenSummary" 2>/dev/null || true
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
echo ""
|
|
59
|
+
echo "=== ORG SPEND — ${org_name} ==="
|
|
60
|
+
|
|
61
|
+
# Per-agent spend from state file
|
|
62
|
+
if [ -f "$stateFile" ]; then
|
|
63
|
+
jq -r '.agents // {} | to_entries[] | " \(.key): tokens_in=\(.value.tokens_in // 0) tokens_out=\(.value.tokens_out // 0) status=\(.value.status // "unknown")"' "$stateFile" 2>/dev/null
|
|
64
|
+
else
|
|
65
|
+
echo " (no state file — org has not run yet)"
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# Budget check
|
|
69
|
+
budget=$(jq -r '.run_config.budget_tokens // "unlimited"' "$orgFile" 2>/dev/null)
|
|
70
|
+
echo ""
|
|
71
|
+
echo "Budget: $budget tokens"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Render as:
|
|
75
|
+
```
|
|
76
|
+
COSTS — org: <org_name>
|
|
77
|
+
──────────────────────────────────────────────
|
|
78
|
+
AGENT STATUS TOKENS IN TOKENS OUT EST. COST
|
|
79
|
+
boss running 48,230 12,100 ~$0.42
|
|
80
|
+
content-writer idle 21,400 8,900 ~$0.18
|
|
81
|
+
reviewer waiting 5,100 2,300 ~$0.05
|
|
82
|
+
──────────────────────────────────────────────
|
|
83
|
+
TOTAL 74,730 23,300 ~$0.65
|
|
84
|
+
BUDGET: 500,000 tokens | USED: 14.8% | REMAINING: 85.2%
|
|
85
|
+
|
|
86
|
+
BURN RATE: ~4,900 tokens/hour | EST. RUNWAY: ~87 hours
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Calculate costs using approximate rates: input ~$3/1M tokens, output ~$15/1M tokens (Sonnet-class).
|
|
90
|
+
|
|
91
|
+
### set-budget
|
|
92
|
+
|
|
93
|
+
Write budget to org config:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
tmp="${orgFile}.tmp"
|
|
97
|
+
jq --argjson budget "$budget_tokens" \
|
|
98
|
+
--argjson threshold "${alert_threshold:-0.8}" \
|
|
99
|
+
'.run_config.budget_tokens = $budget | .run_config.alert_threshold = $threshold' \
|
|
100
|
+
"$orgFile" > "$tmp" && mv "$tmp" "$orgFile"
|
|
101
|
+
echo "Budget set: $budget_tokens tokens (alert at $(echo "$alert_threshold * 100" | bc)%)"
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### alert
|
|
105
|
+
|
|
106
|
+
Check current usage against budget and emit alert if over threshold:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
budget=$(jq -r '.run_config.budget_tokens // 0' "$orgFile")
|
|
110
|
+
threshold=$(jq -r '.run_config.alert_threshold // 0.8' "$orgFile")
|
|
111
|
+
|
|
112
|
+
if [ "$budget" -gt 0 ] && [ -f "$stateFile" ]; then
|
|
113
|
+
total_in=$(jq '[.agents // {} | to_entries[] | .value.tokens_in // 0] | add // 0' "$stateFile")
|
|
114
|
+
total_out=$(jq '[.agents // {} | to_entries[] | .value.tokens_out // 0] | add // 0' "$stateFile")
|
|
115
|
+
total=$((total_in + total_out))
|
|
116
|
+
|
|
117
|
+
# Use awk for float comparison
|
|
118
|
+
over=$(awk -v total="$total" -v budget="$budget" -v threshold="$threshold" \
|
|
119
|
+
'BEGIN { print (total/budget >= threshold) ? "yes" : "no" }')
|
|
120
|
+
|
|
121
|
+
if [ "$over" = "yes" ]; then
|
|
122
|
+
echo "ALERT: Org $org_name has used $(awk -v t=$total -v b=$budget 'BEGIN{printf "%.1f", t/b*100}')% of budget"
|
|
123
|
+
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
|
124
|
+
CTRL_URL=$(jq -r '.url // "http://localhost:4242"' "$REPO_ROOT/.monomind/control.json" 2>/dev/null || echo "http://localhost:4242")
|
|
125
|
+
curl -s -X POST "${CTRL_URL}/api/mastermind/event" \
|
|
126
|
+
-H "Content-Type: application/json" \
|
|
127
|
+
-d "$(jq -cn --arg org "$org_name" --argjson total $total --argjson budget $budget \
|
|
128
|
+
'{type:"org:budget:alert",org:$org,tokens_used:$total,budget:$budget,ts:(now*1000|floor)}')" || true
|
|
129
|
+
fi
|
|
130
|
+
fi
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Step 3 — Return Output
|
|
136
|
+
|
|
137
|
+
```yaml
|
|
138
|
+
domain: ops
|
|
139
|
+
status: complete
|
|
140
|
+
action: <action>
|
|
141
|
+
org: <org_name>
|
|
142
|
+
total_tokens: <N>
|
|
143
|
+
budget_tokens: <N>
|
|
144
|
+
usage_pct: <N>%
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Step 4 — Brain Write (standalone only)
|
|
150
|
+
|
|
151
|
+
If `caller` is not "command", follow _protocol.md Brain Write Procedure for domain `ops`.
|