monomind 2.0.3 → 2.1.3

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 (113) hide show
  1. package/README.md +74 -92
  2. package/package.json +11 -7
  3. package/packages/@monomind/cli/.claude/helpers/control-start.cjs +13 -20
  4. package/packages/@monomind/cli/.claude/helpers/event-logger.cjs +109 -6
  5. package/packages/@monomind/cli/.claude/helpers/graphify-freshen.cjs +19 -17
  6. package/packages/@monomind/cli/.claude/helpers/handlers/agent-start-handler.cjs +60 -28
  7. package/packages/@monomind/cli/.claude/helpers/handlers/capture-handler.cjs +99 -28
  8. package/packages/@monomind/cli/.claude/helpers/handlers/edit-handler.cjs +27 -9
  9. package/packages/@monomind/cli/.claude/helpers/handlers/gates-handler.cjs +18 -9
  10. package/packages/@monomind/cli/.claude/helpers/handlers/loops-status-handler.cjs +1 -1
  11. package/packages/@monomind/cli/.claude/helpers/handlers/route-handler.cjs +33 -4
  12. package/packages/@monomind/cli/.claude/helpers/handlers/session-handler.cjs +75 -45
  13. package/packages/@monomind/cli/.claude/helpers/handlers/session-restore-handler.cjs +0 -12
  14. package/packages/@monomind/cli/.claude/helpers/handlers/task-handler.cjs +53 -22
  15. package/packages/@monomind/cli/.claude/helpers/hook-handler.cjs +75 -4
  16. package/packages/@monomind/cli/.claude/helpers/intelligence.cjs +94 -15
  17. package/packages/@monomind/cli/.claude/helpers/session.cjs +6 -1
  18. package/packages/@monomind/cli/.claude/helpers/statusline.cjs +10 -10
  19. package/packages/@monomind/cli/.claude/helpers/token-tracker.cjs +3 -3
  20. package/packages/@monomind/cli/.claude/helpers/utils/agent-registrations.cjs +41 -0
  21. package/packages/@monomind/cli/.claude/helpers/utils/fs-helpers.cjs +183 -0
  22. package/packages/@monomind/cli/.claude/helpers/utils/micro-agents.cjs +10 -6
  23. package/packages/@monomind/cli/.claude/helpers/utils/monograph.cjs +73 -2
  24. package/packages/@monomind/cli/.claude/helpers/utils/telemetry.cjs +35 -19
  25. package/packages/@monomind/cli/README.md +74 -92
  26. package/packages/@monomind/cli/bin/cli.js +10 -1
  27. package/packages/@monomind/cli/dist/src/browser/workflow/store.d.ts +2 -2
  28. package/packages/@monomind/cli/dist/src/commands/agent-ops.js +9 -15
  29. package/packages/@monomind/cli/dist/src/commands/browse.d.ts +1 -1
  30. package/packages/@monomind/cli/dist/src/commands/cleanup.js +20 -4
  31. package/packages/@monomind/cli/dist/src/commands/completions.js +66 -261
  32. package/packages/@monomind/cli/dist/src/commands/doctor-env-checks.js +5 -0
  33. package/packages/@monomind/cli/dist/src/commands/doctor.js +7 -2
  34. package/packages/@monomind/cli/dist/src/commands/hooks-workers.js +7 -0
  35. package/packages/@monomind/cli/dist/src/commands/index.js +3 -0
  36. package/packages/@monomind/cli/dist/src/commands/mcp.js +11 -6
  37. package/packages/@monomind/cli/dist/src/commands/memory-admin.js +12 -6
  38. package/packages/@monomind/cli/dist/src/commands/memory-transfer.js +6 -13
  39. package/packages/@monomind/cli/dist/src/commands/monograph.js +3 -3
  40. package/packages/@monomind/cli/dist/src/commands/org.d.ts +3 -1
  41. package/packages/@monomind/cli/dist/src/commands/org.js +321 -127
  42. package/packages/@monomind/cli/dist/src/commands/platforms.d.ts +1 -1
  43. package/packages/@monomind/cli/dist/src/commands/start.js +142 -37
  44. package/packages/@monomind/cli/dist/src/commands/tokens.js +21 -3
  45. package/packages/@monomind/cli/dist/src/config-adapter.js +37 -17
  46. package/packages/@monomind/cli/dist/src/index.js +64 -28
  47. package/packages/@monomind/cli/dist/src/init/claudemd-generator.js +44 -11
  48. package/packages/@monomind/cli/dist/src/init/executor.js +93 -55
  49. package/packages/@monomind/cli/dist/src/mcp-client.d.ts +8 -3
  50. package/packages/@monomind/cli/dist/src/mcp-client.js +37 -3
  51. package/packages/@monomind/cli/dist/src/mcp-server.js +8 -2
  52. package/packages/@monomind/cli/dist/src/mcp-tools/agent-tools.d.ts +20 -0
  53. package/packages/@monomind/cli/dist/src/mcp-tools/agent-tools.js +10 -5
  54. package/packages/@monomind/cli/dist/src/mcp-tools/auto-install.d.ts +8 -8
  55. package/packages/@monomind/cli/dist/src/mcp-tools/coherence/types.d.ts +31 -31
  56. package/packages/@monomind/cli/dist/src/mcp-tools/github-tools.js +30 -8
  57. package/packages/@monomind/cli/dist/src/mcp-tools/graphify-tools.js +11 -2
  58. package/packages/@monomind/cli/dist/src/mcp-tools/hive-mind-tools.js +56 -35
  59. package/packages/@monomind/cli/dist/src/mcp-tools/knowledge-tools.js +9 -1
  60. package/packages/@monomind/cli/dist/src/mcp-tools/monograph-compat.js +56 -14
  61. package/packages/@monomind/cli/dist/src/mcp-tools/monograph-tools.js +88 -41
  62. package/packages/@monomind/cli/dist/src/mcp-tools/quality/coverage-analysis/prioritize-gaps.d.ts +36 -36
  63. package/packages/@monomind/cli/dist/src/mcp-tools/quality/security-compliance/detect-secrets.d.ts +4 -4
  64. package/packages/@monomind/cli/dist/src/mcp-tools/swarm-tools.js +6 -4
  65. package/packages/@monomind/cli/dist/src/mcp-tools/system-tools.js +4 -4
  66. package/packages/@monomind/cli/dist/src/mcp-tools/types.d.ts +20 -0
  67. package/packages/@monomind/cli/dist/src/mcp-tools/types.js +36 -1
  68. package/packages/@monomind/cli/dist/src/memory/ewc-consolidation.d.ts +12 -0
  69. package/packages/@monomind/cli/dist/src/memory/hnsw-operations.d.ts +13 -1
  70. package/packages/@monomind/cli/dist/src/memory/hnsw-operations.js +66 -14
  71. package/packages/@monomind/cli/dist/src/memory/intelligence.js +99 -3
  72. package/packages/@monomind/cli/dist/src/memory/memory-bridge.d.ts +7 -0
  73. package/packages/@monomind/cli/dist/src/memory/memory-bridge.js +20 -0
  74. package/packages/@monomind/cli/dist/src/memory/sona-optimizer.d.ts +12 -0
  75. package/packages/@monomind/cli/dist/src/orgrt/broker.d.ts +26 -0
  76. package/packages/@monomind/cli/dist/src/orgrt/broker.js +73 -0
  77. package/packages/@monomind/cli/dist/src/orgrt/bus.d.ts +5 -1
  78. package/packages/@monomind/cli/dist/src/orgrt/bus.js +5 -1
  79. package/packages/@monomind/cli/dist/src/orgrt/daemon.d.ts +46 -0
  80. package/packages/@monomind/cli/dist/src/orgrt/daemon.js +201 -18
  81. package/packages/@monomind/cli/dist/src/orgrt/forwarder.d.ts +10 -5
  82. package/packages/@monomind/cli/dist/src/orgrt/forwarder.js +138 -8
  83. package/packages/@monomind/cli/dist/src/orgrt/inbox.d.ts +11 -0
  84. package/packages/@monomind/cli/dist/src/orgrt/inbox.js +56 -0
  85. package/packages/@monomind/cli/dist/src/orgrt/policy.d.ts +5 -1
  86. package/packages/@monomind/cli/dist/src/orgrt/policy.js +58 -5
  87. package/packages/@monomind/cli/dist/src/orgrt/provider.js +1 -0
  88. package/packages/@monomind/cli/dist/src/orgrt/scheduler.d.ts +13 -0
  89. package/packages/@monomind/cli/dist/src/orgrt/scheduler.js +48 -0
  90. package/packages/@monomind/cli/dist/src/orgrt/server.d.ts +9 -0
  91. package/packages/@monomind/cli/dist/src/orgrt/server.js +39 -0
  92. package/packages/@monomind/cli/dist/src/orgrt/session.d.ts +10 -1
  93. package/packages/@monomind/cli/dist/src/orgrt/session.js +24 -1
  94. package/packages/@monomind/cli/dist/src/orgrt/test-loop.d.ts +12 -0
  95. package/packages/@monomind/cli/dist/src/orgrt/test-loop.js +108 -0
  96. package/packages/@monomind/cli/dist/src/orgrt/types.d.ts +52 -52
  97. package/packages/@monomind/cli/dist/src/output.d.ts +29 -29
  98. package/packages/@monomind/cli/dist/src/output.js +22 -7
  99. package/packages/@monomind/cli/dist/src/parser.d.ts +32 -0
  100. package/packages/@monomind/cli/dist/src/parser.js +130 -5
  101. package/packages/@monomind/cli/dist/src/routing/embed-worker.js +13 -1
  102. package/packages/@monomind/cli/dist/src/routing/route-layer-factory.js +31 -0
  103. package/packages/@monomind/cli/dist/src/services/config-file-manager.d.ts +21 -0
  104. package/packages/@monomind/cli/dist/src/services/config-file-manager.js +60 -7
  105. package/packages/@monomind/cli/dist/src/services/crash-reporter.js +2 -1
  106. package/packages/@monomind/cli/dist/src/types.d.ts +2 -2
  107. package/packages/@monomind/cli/dist/src/ui/dashboard.html +243 -49
  108. package/packages/@monomind/cli/dist/src/ui/orgs-files.js +192 -0
  109. package/packages/@monomind/cli/dist/src/ui/orgs.html +79 -11
  110. package/packages/@monomind/cli/dist/src/ui/server.mjs +346 -139
  111. package/packages/@monomind/cli/dist/src/utils/input-guards.d.ts +9 -0
  112. package/packages/@monomind/cli/dist/src/utils/input-guards.js +39 -5
  113. package/packages/@monomind/cli/package.json +15 -15
package/README.md CHANGED
@@ -5,8 +5,8 @@
5
5
  <h1 align="center">Monomind</h1>
6
6
 
7
7
  <p align="center">
8
- <strong>Hire an AI team. Set a goal. Walk away.</strong><br/>
9
- Autonomous Claude Code orchestration with persistent memory, self-coordinating agent orgs, and a codebase knowledge graph.
8
+ <strong>An open-source MCP server that extends Claude Code with a codebase knowledge graph, persistent memory, and multi-agent coordination.</strong><br/>
9
+ MIT licensed &middot; Fully local &middot; No data leaves your machine
10
10
  </p>
11
11
 
12
12
  <p align="center">
@@ -30,126 +30,105 @@
30
30
 
31
31
  ## What is Monomind?
32
32
 
33
- Claude Code is already powerful. Monomind makes it **run itself**.
33
+ Monomind is an **open-source CLI and MCP server** that plugs into Claude Code via the standard [Model Context Protocol](https://modelcontextprotocol.io/). It adds capabilities that Claude Code doesn't ship with out of the box:
34
34
 
35
- Install once. Wire it into Claude Code. Then instead of prompting Claude to do individual tasks, you tell Monomind what outcome you want and it assembles a team, coordinates the work, and delivers.
35
+ - **Codebase knowledge graph** tree-sitter parses your code into a SQLite-backed graph of files, functions, classes, and their relationships. Query imports, callers, and blast radius before making changes.
36
+ - **Persistent memory** — a JSON pattern store with episodic recall that survives across sessions. Agents and orgs share context without re-prompting.
37
+ - **Multi-agent coordination** — in-session, spawn ad-hoc agent teams via Claude Code's Task tool; for persistent background work, `monomind org run` starts a real SDK-backed daemon with policy-gated role agents and a live dashboard.
38
+ - **Reusable slash commands** — 30+ development workflows (build, review, debug, TDD, architecture) available as `/mastermind:*` commands inside Claude Code.
36
39
 
37
40
  ```bash
38
- # Assemble an AI content team and let it run
39
- /mastermind:createorg content-team "publish 3 SEO-optimized posts per week"
40
- /mastermind:runorg --org content-team
41
-
42
- # Or run the autonomous code improvement loop
43
- /mastermind:autodev --tillend --focus security
41
+ npm install -g monomind # MIT licensed, 0 external API calls
42
+ cd your-project && monomind init
43
+ claude mcp add monomind npx monomind mcp start
44
44
  ```
45
45
 
46
- That's it. Come back later.
46
+ ### Trust & Security
47
+
48
+ | Concern | Answer |
49
+ |---|---|
50
+ | **License** | [MIT](LICENSE) — use it however you want |
51
+ | **Data privacy** | Everything runs locally. No telemetry, no phone-home, no external API calls from Monomind itself. Your code stays on your machine. |
52
+ | **Dependencies** | Standard npm packages (tree-sitter, sql.js, zod). No native binaries. No post-install scripts that download code. |
53
+ | **Permissions** | Registers as an MCP server — Claude Code controls what tools are available and prompts you before executing anything sensitive. |
54
+ | **Source** | Fully open. Read every line at [github.com/monoes/monomind](https://github.com/monoes/monomind). |
55
+ | **Maintenance** | Active development, regular releases on npm. |
47
56
 
48
57
  ---
49
58
 
50
59
  ## 🏢 Autonomous Organizations
51
60
 
52
- > **This is the headline feature.** Build a persistent AI organization — roles, hierarchy, shared task board and start it as a background daemon that runs without you.
61
+ > **This is the headline feature.** `monomind org run` starts a persistent, SDK-backed daemon that runs an autonomous agent organization — roles, hierarchy, policy-gated tool access, a live dashboard until you stop it.
53
62
 
54
63
  ### The idea
55
64
 
56
- Every business function needs a team. Monomind lets you design that team in one command, then run it forever. The org persists across sessions. It checkpoints, recovers from failures, and coordinates its agents through a shared task board all automatically.
65
+ Every business function needs a team. Define the org once as a JSON file — goal, roles, who reports to whom, per-role tool/file/budget policy — then run it as a real background daemon backed by the Claude Agent SDK. It persists across sessions, serves a live WebSocket dashboard, and can discover and message other Monomind orgs running on the same machine.
57
66
 
58
67
  ```mermaid
59
68
  flowchart TD
60
69
  U(["You"])
61
- CO["/mastermind:createorg\nDefine goal + roles"]
62
- RO["/mastermind:runorg\nStart daemon"]
63
- BOSS["Boss Agent\ncoordinator"]
64
- W["Writer\nContent Creator"]
70
+ DEF["org.json\nGoal + roles + policy"]
71
+ RUN["monomind org run\nStart SDK daemon"]
72
+ BOSS["Boss Agent\nAgent SDK session"]
73
+ W["Writer"]
65
74
  S["SEO Specialist"]
66
75
  R["Reviewer"]
67
- M["Growth Marketer"]
68
- BOARD[("Shared\nTask Board")]
69
- MEM[("Persistent\nMemory")]
76
+ DASH[("Live Dashboard\n:4243")]
77
+ XORG[("Other orgs\ncross-process")]
70
78
 
71
- U --> CO --> RO --> BOSS
79
+ U --> DEF --> RUN --> BOSS
72
80
  BOSS -->|spawns| W
73
81
  BOSS -->|spawns| S
74
82
  BOSS -->|spawns| R
75
- BOSS -->|spawns| M
76
- BOSS <-->|claims + reports| BOARD
77
- W <-->|stores output| MEM
78
- S <-->|reads context| MEM
83
+ RUN <-->|serves| DASH
84
+ RUN <-.->|--cross-process| XORG
79
85
 
80
86
  style BOSS fill:#00D2AA22,stroke:#00D2AA
81
- style BOARD fill:#F59E0B22,stroke:#F59E0B
82
- style MEM fill:#8B5CF622,stroke:#8B5CF6
83
- ```
84
-
85
- ### Two commands to a running org
86
-
87
- **Step 1 — Design it:**
88
-
87
+ style DASH fill:#F59E0B22,stroke:#F59E0B
88
+ style XORG fill:#8B5CF622,stroke:#8B5CF6
89
89
  ```
90
- / mastermind:createorg content-team
91
- "Build and publish 3 blog posts per week on AI dev tools"
92
-
93
- Deriving roles from goal...
94
-
95
- ╔ ORG: content-team
96
- ║ TOPOLOGY: star (5 roles)
97
90
 
98
- boss → coordinator
99
- writer → Content Creator
100
- reviewer → reviewer
101
- marketer → Growth Hacker
102
- seo → SEO Specialist
91
+ ### Run one
103
92
 
104
- Type "go" to save, or describe changes.
105
- go
93
+ ```bash
94
+ # .monomind/orgs/<name>.json defines the org: goal, roles, policy.
95
+ # See .monomind/orgs/sample-team.json in a fresh `monomind init` for a working example.
106
96
 
107
- Saved .monomind/orgs/content-team.json
108
- → Run: /mastermind:runorg --org content-team
109
- ```
97
+ monomind org run content-team --task "Build and publish 3 blog posts per week"
110
98
 
111
- **Step 2 Start it:**
99
+ # Live dashboard: http://localhost:4243
100
+ # ✓ Boss agent (Claude Agent SDK session) spawns, reads the org goal,
101
+ # assigns work to role agents, coordinates until the task completes
102
+ # or you stop it.
112
103
 
113
- ```bash
114
- /mastermind:runorg --org content-team
115
-
116
- # Boss agent spawns in background
117
- # Coordinates all roles via shared task board
118
- # Checkpoints every 30 minutes
119
- # Loops until you stop it
104
+ monomind org status content-team # runtime state
105
+ monomind org stop content-team # request a graceful stop
106
+ monomind org list # every org + status
120
107
  ```
121
108
 
122
109
  ### What runs under the hood
123
110
 
124
111
  | What | How |
125
112
  |---|---|
126
- | **Boss agent** | Coordinator type, no supervisor owns the goal |
127
- | **Role agents** | Spawned on demand, specialized by task type |
128
- | **Task board** | Todo Doing Done, shared across all agents |
129
- | **Memory** | All output stored in an org-scoped memory namespace |
130
- | **Checkpoint** | State saved every 30 min survives crashes and restarts |
131
- | **Governance** | `auto` (free), `board` (approve sensitive), `strict` (approve all external actions) |
132
-
133
- ### Topology is auto-derived
134
-
135
- ```mermaid
136
- graph LR
137
- A["1-3 roles"] -->|mesh| B["All-to-all\ndirect comms"]
138
- C["4-6 roles"] -->|star| D["Boss to workers\nfan-out"]
139
- E["7+ roles"] -->|hierarchical| F["Boss to leads to workers\nmiddle management"]
140
- ```
113
+ | **OrgDaemon** | Hosts one or more orgs in a single process; real Claude Agent SDK sessions per role, not simulated |
114
+ | **PolicyEngine** | Per-role gates on tool access, file read/write scope, web access, token budget — enforced, with a full audit trail |
115
+ | **Live dashboard** | WebSocket-served at `:4243` by default (`--port` to change, `--serve=false` to disable) |
116
+ | **Cross-process comms** | `--cross-process` (default on) lets orgs on different `monomind` processes/projects discover and message each other |
117
+ | **Scheduling** | `monomind org serve` hosts orgs whose definition has a `schedule` field, running them on interval |
141
118
 
142
119
  ### Org management commands
143
120
 
144
121
  ```bash
145
- /mastermind:createorg <name> "<goal>" # design org from a goal
146
- /mastermind:runorg --org <name> # start as background daemon
147
- /mastermind:orgs # list all orgs + status
148
- /mastermind:orgstatus --org <name> # detailed status for one org
149
- /mastermind:stoporg --org <name> # stop a running org
150
- /mastermind:approve # review pending approval requests
122
+ monomind org run <name> [--task "..."] [--port N] [--cross-process] # start a daemon
123
+ monomind org stop <name> # request a running org to stop
124
+ monomind org status [name] # runtime state for one or all orgs
125
+ monomind org list # list every org + status
126
+ monomind org serve [--port N] # host-only mode, runs scheduled orgs
127
+ monomind org delete <name> # remove an org
151
128
  ```
152
129
 
130
+ > **Note:** the older `/mastermind:createorg` + `/mastermind:runorg` prompt-orchestrated flow is deprecated — it has no delivery guarantees or ground-truth event stream. It still runs for orgs not yet migrated, but new orgs should use `monomind org run` directly against a hand-authored `.monomind/orgs/<name>.json`.
131
+
153
132
  ---
154
133
 
155
134
  ## ⚡ The Autonomous Build Loop
@@ -206,11 +185,11 @@ claude mcp add monomind npx monomind mcp start
206
185
  monomind doctor --fix
207
186
  ```
208
187
 
209
- Open Claude Code. You now have 80+ slash commands available:
188
+ Open Claude Code. You now have 60+ slash commands available:
210
189
 
211
190
  ```bash
212
191
  /mastermind:autodev --tillend # start autonomous code loop
213
- /mastermind:createorg my-team # create your first AI org
192
+ monomind org run my-team # run your first AI org (see .monomind/orgs/sample-team.json)
214
193
  /mastermind:help # show all commands
215
194
  ```
216
195
 
@@ -262,7 +241,7 @@ Before touching any file, Monomind queries **Monograph** — a SQLite-backed kno
262
241
 
263
242
  ## 🎣 Hooks & Workers
264
243
 
265
- Monomind wires 22 hook events into Claude Code. Every edit, task, command, and session fires hooks that log patterns, route agents, and train the intelligence system.
244
+ Monomind wires 29 hook subcommands into Claude Code across edit, task, command, and session lifecycle events logging patterns, routing agents, and feeding the intelligence system.
266
245
 
267
246
  ```mermaid
268
247
  flowchart LR
@@ -296,7 +275,7 @@ const result = await fence.detect(userInput);
296
275
 
297
276
  ---
298
277
 
299
- ## 📋 80+ Slash Commands
278
+ ## 📋 60+ Slash Commands
300
279
 
301
280
  Everything runs from inside Claude Code via slash commands. Here's the highlight reel:
302
281
 
@@ -315,9 +294,9 @@ Everything runs from inside Claude Code via slash commands. Here's the highlight
315
294
  ### Organizations
316
295
  | Command | What it does |
317
296
  |---|---|
318
- | `/mastermind:createorg` | Design an autonomous agent org |
319
- | `/mastermind:runorg` | Start it as a background daemon |
320
- | `/mastermind:orgs` | List all orgs + status |
297
+ | `monomind org run <name>` | Start an org as a real SDK-backed daemon |
298
+ | `monomind org status` / `list` | Runtime state for one or all orgs |
299
+ | `monomind org stop <name>` | Request a graceful stop |
321
300
  | `/mastermind:approve` | Action pending approval requests |
322
301
 
323
302
  ### Business Domains
@@ -329,7 +308,7 @@ Everything runs from inside Claude Code via slash commands. Here's the highlight
329
308
  | `/mastermind:finance` | Budgets, invoicing, modeling |
330
309
  | `/mastermind:ops` | Operations and workflow automation |
331
310
 
332
- **[→ Full reference (80+ commands)](https://monoes.github.io/monomind/#slash)**
311
+ **[→ Full reference (60+ commands)](https://monoes.github.io/monomind/#slash)**
333
312
 
334
313
  ---
335
314
 
@@ -351,24 +330,27 @@ graph TD
351
330
  CC["Claude Code"]
352
331
  MCP["MCP Server\nmonomind mcp start"]
353
332
  D["Background Workers\n(@monomind/hooks, in-process)"]
333
+ ORG["OrgDaemon\nmonomind org run\nreal SDK sessions"]
354
334
 
355
- CC <-->|"MCP tools: monograph, memory, swarm"| MCP
335
+ CC <-->|"MCP tools: monograph, memory"| MCP
356
336
  MCP <--> D
357
337
 
358
338
  D --> ADB[("Memory store\npatterns + episodes")]
359
339
  D --> MG[("Monograph\ncode graph")]
360
- D --> HK["Hooks\n22 event types"]
361
- D --> SW["Swarm\n4 topologies\n3 consensus strategies"]
340
+ D --> HK["Hooks\n29 subcommands"]
362
341
 
363
- CC -->|"Task tool - spawns agents"| AG["Agent Swarm\narchitect, coder\ntester, reviewer\nsecurity, perf"]
342
+ CC -->|"Task tool - spawns agents"| AG["In-session agents\narchitect, coder\ntester, reviewer\nsecurity, perf"]
364
343
  AG <-->|reads and writes| ADB
344
+ ORG -->|"spawns, policy-gated"| RA["Role agents"]
345
+ ORG <-->|reads and writes| ADB
365
346
 
366
347
  style CC fill:#00D2AA22,stroke:#00D2AA
367
348
  style AG fill:#8B5CF622,stroke:#8B5CF6
368
349
  style ADB fill:#F59E0B22,stroke:#F59E0B
350
+ style ORG fill:#F59E0B22,stroke:#F59E0B
369
351
  ```
370
352
 
371
- **Claude Code handles all execution.** MCP tools only coordinate. Your data never leaves your machine.
353
+ **Claude Code's Task tool drives in-session multi-agent work; `monomind org run` drives persistent background orgs.** Your data never leaves your machine.
372
354
 
373
355
  ---
374
356
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "monomind",
3
- "version": "2.0.3",
4
- "description": "Monomind - Power toolkit for Claude Code. Autonomous agent orgs, codebase knowledge graph, keyword-routed specialist prompts, and 80+ slash commands. One init, then walk away.",
3
+ "version": "2.1.3",
4
+ "description": "Open-source CLI extension for Claude Code. Adds an MCP server with a codebase knowledge graph, persistent memory, multi-agent coordination, and reusable slash commands. MIT licensed, runs locally, no data leaves your machine.",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
7
7
  "bin": {
@@ -60,15 +60,20 @@
60
60
  "v1:security": "npm run security:audit && npm run security:test"
61
61
  },
62
62
  "dependencies": {
63
+ "@anthropic-ai/claude-agent-sdk": "^0.3.207",
63
64
  "@monoes/monobrowse": "^1.0.3",
64
65
  "@monoes/monograph": "^1.4.0",
66
+ "@noble/ed25519": "^2.1.0",
65
67
  "mammoth": "^1.12.0",
66
68
  "pdf-parse": "^2.4.5",
67
- "semver": "^7.6.0"
69
+ "semver": "^7.6.0",
70
+ "ws": "^8.21.0",
71
+ "yaml": "^2.8.0",
72
+ "zod": "^3.25.0"
68
73
  },
69
74
  "overrides": {
70
75
  "hono": ">=4.12.21",
71
- "ws": ">=8.20.1",
76
+ "ws": "$ws",
72
77
  "axios": ">=1.16.0",
73
78
  "qs": ">=6.15.2",
74
79
  "esbuild": ">=0.28.1",
@@ -135,10 +140,9 @@
135
140
  "devDependencies": {
136
141
  "@types/node": "^20.0.0",
137
142
  "tsx": "^4.21.0",
138
- "typescript": "^5.0.0",
143
+ "typescript": "^7.0.2",
139
144
  "vite": ">=8.0.16",
140
- "vitest": "^4.1.4",
141
- "zod": "^4.3.6"
145
+ "vitest": "^4.1.4"
142
146
  },
143
147
  "engines": {
144
148
  "node": ">=20.0.0"
@@ -12,6 +12,7 @@
12
12
  const fs = require('fs');
13
13
  const path = require('path');
14
14
  const { spawn } = require('child_process');
15
+ const { claimLock, releaseLock } = require('./utils/fs-helpers.cjs');
15
16
 
16
17
  const CWD = process.env.CLAUDE_PROJECT_DIR || process.cwd();
17
18
  const STATUS_FILE = path.join(CWD, '.monomind', 'control.json');
@@ -111,31 +112,23 @@ const LOCK_FILE = path.join(CWD, '.monomind', 'control.lock');
111
112
  * Atomically claim the spawn lock. Concurrent hook events (a busy session can
112
113
  * fire dozens of control-starts at once) must not each spawn a server — the
113
114
  * loser processes exit and let the winner's server come up. A stale lock
114
- * (dead pid or older than 30s) is broken and re-claimed.
115
+ * (older than 30s) is broken and re-claimed.
116
+ *
117
+ * P2-25: previously this broke a stale lock via unlink-then-writeFileSync(wx),
118
+ * which is a TOCTOU race — a second process that also decided the lock was
119
+ * stale could unlink the FIRST process's freshly-claimed (non-stale) lock
120
+ * between that process's unlink and its own write, letting a third process
121
+ * then also claim it. `claimLock` (utils/fs-helpers.cjs) fixes this by
122
+ * breaking stale locks with an atomic rename-to-claim instead: only one
123
+ * racing process can win the rename, and a loser retries from the top
124
+ * rather than proceeding as if it owns the lock.
115
125
  */
116
126
  function claimSpawnLock() {
117
- try { fs.mkdirSync(path.dirname(LOCK_FILE), { recursive: true }); } catch { /* ignore */ }
118
- try {
119
- fs.writeFileSync(LOCK_FILE, String(process.pid), { flag: 'wx' });
120
- return true;
121
- } catch {
122
- try {
123
- const stat = fs.statSync(LOCK_FILE);
124
- const holder = Number(fs.readFileSync(LOCK_FILE, 'utf-8'));
125
- if (Date.now() - stat.mtimeMs < 30_000 && isPidAlive(holder)) return false;
126
- fs.unlinkSync(LOCK_FILE);
127
- fs.writeFileSync(LOCK_FILE, String(process.pid), { flag: 'wx' });
128
- return true;
129
- } catch {
130
- return false;
131
- }
132
- }
127
+ return claimLock(LOCK_FILE, 30_000);
133
128
  }
134
129
 
135
130
  function releaseSpawnLock() {
136
- try {
137
- if (Number(fs.readFileSync(LOCK_FILE, 'utf-8')) === process.pid) fs.unlinkSync(LOCK_FILE);
138
- } catch { /* ignore */ }
131
+ releaseLock(LOCK_FILE);
139
132
  }
140
133
 
141
134
  async function main() {
@@ -22,15 +22,36 @@ safety.unref();
22
22
 
23
23
  const MAX_STDIN = 1024 * 1024; // 1 MiB
24
24
 
25
+ // P3-16: mirrors hook-handler.cjs's readStdin — byte-counted, discard-whole-
26
+ // payload-on-overflow. The old version did `if (data.length < MAX_STDIN)
27
+ // data += c`, which silently DROPPED characters once the cap was hit instead
28
+ // of aborting the read, so an oversized payload produced truncated, invalid
29
+ // JSON that then failed JSON.parse downstream with the whole event (and its
30
+ // toolName/sessionId attribution) discarded anyway — just with extra steps
31
+ // and no signal as to why. Discarding the entire payload up front (with a
32
+ // logged, flagged event) is equivalent in outcome but honest about it.
25
33
  function readStdin() {
26
34
  if (process.stdin.isTTY) return Promise.resolve('');
27
35
  return new Promise((resolve) => {
28
36
  let data = '';
29
- const t = setTimeout(() => { process.stdin.pause(); resolve(data); }, 800);
37
+ let byteCount = 0;
38
+ let truncated = false;
39
+ const t = setTimeout(() => { process.stdin.pause(); resolve({ data, truncated }); }, 800);
30
40
  process.stdin.setEncoding('utf8');
31
- process.stdin.on('data', c => { if (data.length < MAX_STDIN) data += c; });
32
- process.stdin.on('end', () => { clearTimeout(t); resolve(data); });
33
- process.stdin.on('error', () => { clearTimeout(t); resolve(data); });
41
+ process.stdin.on('data', c => {
42
+ if (truncated) return;
43
+ byteCount += Buffer.byteLength(c, 'utf8');
44
+ if (byteCount > MAX_STDIN) {
45
+ truncated = true;
46
+ process.stdin.pause();
47
+ clearTimeout(t);
48
+ resolve({ data: '', truncated: true }); // discard oversized input, same as hook-handler.cjs
49
+ return;
50
+ }
51
+ data += c;
52
+ });
53
+ process.stdin.on('end', () => { clearTimeout(t); resolve({ data, truncated }); });
54
+ process.stdin.on('error', () => { clearTimeout(t); resolve({ data, truncated }); });
34
55
  process.stdin.resume();
35
56
  });
36
57
  }
@@ -60,7 +81,14 @@ function forwardToDashboard(entry) {
60
81
  }
61
82
 
62
83
  async function main() {
63
- const raw = await readStdin();
84
+ const stdinResult = await readStdin();
85
+ const raw = stdinResult.data;
86
+ const stdinTruncated = stdinResult.truncated;
87
+ if (stdinTruncated) {
88
+ // Oversized payload was discarded whole (see readStdin) — log it so the
89
+ // drop is visible instead of silently vanishing as a JSON.parse failure.
90
+ console.error('[event-logger] stdin payload exceeded ' + MAX_STDIN + ' bytes; discarded (event=' + eventType + ')');
91
+ }
64
92
  let payload = {};
65
93
  try { payload = JSON.parse(raw); } catch {}
66
94
 
@@ -80,6 +108,7 @@ async function main() {
80
108
  toolResult: payload.toolResult || payload.tool_result || undefined,
81
109
  message: payload.message || payload.notification || payload.prompt || undefined,
82
110
  raw: (raw.length < 4096) ? payload : undefined,
111
+ truncated: stdinTruncated || undefined,
83
112
  };
84
113
 
85
114
  // Remove undefined keys
@@ -113,6 +142,13 @@ async function main() {
113
142
  fs.appendFileSync(allLog, JSON.stringify(entry) + '\n');
114
143
  } catch {}
115
144
 
145
+ // Rotation — event logs (daily + per-session) accumulate forever with no
146
+ // cleanup otherwise. Cheap: single readdir + stat pass, only on session-start
147
+ // (once per session, not per-event) so normal hook latency is unaffected.
148
+ if (eventType === 'session-start') {
149
+ rotateEventLogs(eventsDir);
150
+ }
151
+
116
152
  // Write to per-session log
117
153
  if (sessionId !== 'unknown') {
118
154
  try {
@@ -148,8 +184,75 @@ async function main() {
148
184
  forwardToDashboard(entry);
149
185
  }
150
186
 
187
+ // Detect route changes and worker metric updates by mtime and forward them too.
188
+ // These aren't Claude Code hook events — they're file writes from route-handler.cjs
189
+ // and the @monomind/hooks workers respectively — so we poll cheaply on every hook invocation.
190
+ forwardFileChanges(monoDir, CWD);
191
+
151
192
  clearTimeout(safety);
152
193
  process.exit(0);
153
194
  }
154
195
 
155
- main().catch(() => process.exit(0));
196
+ function forwardFileChanges(monoDir, workDir) {
197
+ try {
198
+ const cachePath = path.join(monoDir, 'dashboard-watch-cache.json');
199
+ let cache = {};
200
+ try { cache = JSON.parse(fs.readFileSync(cachePath, 'utf8')); } catch {}
201
+ let changed = false;
202
+
203
+ // Route changes (.monomind/last-route.json, written every prompt by route-handler.cjs)
204
+ const routePath = path.join(workDir, '.monomind', 'last-route.json');
205
+ try {
206
+ const routeMtime = fs.statSync(routePath).mtimeMs;
207
+ if (routeMtime !== cache.routeMtime) {
208
+ cache.routeMtime = routeMtime;
209
+ changed = true;
210
+ let route = null;
211
+ try { route = JSON.parse(fs.readFileSync(routePath, 'utf8')); } catch {}
212
+ forwardToDashboard({ hookType: 'route-change', ts: Date.now(), route });
213
+ }
214
+ } catch {}
215
+
216
+ // Worker-complete: newest mtime among .monomind/metrics/*.json (written by @monomind/hooks workers)
217
+ const metricsDir = path.join(workDir, '.monomind', 'metrics');
218
+ try {
219
+ const files = fs.readdirSync(metricsDir).filter(f => f.endsWith('.json') && !f.startsWith('.'));
220
+ let latestFile = null;
221
+ let latestMtime = cache.metricsMtime || 0;
222
+ for (const f of files) {
223
+ const mtime = fs.statSync(path.join(metricsDir, f)).mtimeMs;
224
+ if (mtime > latestMtime) { latestMtime = mtime; latestFile = f; }
225
+ }
226
+ if (latestFile) {
227
+ cache.metricsMtime = latestMtime;
228
+ changed = true;
229
+ forwardToDashboard({ hookType: 'daemon-complete', ts: Date.now(), metric: latestFile.replace(/\.json$/, '') });
230
+ }
231
+ } catch {}
232
+
233
+ if (changed) fs.writeFileSync(cachePath, JSON.stringify(cache));
234
+ } catch {}
235
+ }
236
+
237
+ // Delete any daily or per-session event log whose mtime is older than
238
+ // maxAgeMs (default 14 days). Single readdir + stat pass — no recursion.
239
+ function rotateEventLogs(eventsDir, maxAgeMs) {
240
+ maxAgeMs = maxAgeMs || 14 * 24 * 60 * 60 * 1000;
241
+ try {
242
+ const cutoff = Date.now() - maxAgeMs;
243
+ const entries = fs.readdirSync(eventsDir).filter(f => !f.startsWith('._'));
244
+ for (const f of entries) {
245
+ if (!f.endsWith('.jsonl')) continue;
246
+ const full = path.join(eventsDir, f);
247
+ try {
248
+ if (fs.statSync(full).mtimeMs < cutoff) fs.unlinkSync(full);
249
+ } catch {}
250
+ }
251
+ } catch {}
252
+ }
253
+
254
+ module.exports = { rotateEventLogs };
255
+
256
+ if (require.main === module) {
257
+ main().catch(() => process.exit(0));
258
+ }
@@ -5,6 +5,7 @@ const path = require('path');
5
5
  const fs = require('fs');
6
6
  const { spawn } = require('child_process');
7
7
  const { pathToFileURL } = require('url');
8
+ const { claimLock } = require('./utils/fs-helpers.cjs');
8
9
 
9
10
  const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
10
11
  const graphDir = path.join(projectDir, '.monomind', 'graph');
@@ -91,20 +92,18 @@ if (fs.existsSync(dbPath)) {
91
92
  }
92
93
 
93
94
  // Skip if another build is already in progress (avoids SQLite BUSY on concurrent init + session-start)
95
+ // P2-24: claim atomically (wx-create) instead of statSync-then-writeFileSync
96
+ // — two concurrent freshen triggers can both cross the same write-count
97
+ // threshold from parallel hook events, and a plain read-check-write lets
98
+ // both pass the check and both spawn a detached rebuild child. claimLock
99
+ // uses the same TOCTOU-safe stale-lock-break (atomic rename-to-claim) as
100
+ // control-start.cjs's spawn lock (see P2-25) — a lock older than 5 minutes
101
+ // is treated as abandoned and safely reclaimed.
94
102
  const lockPath = path.join(graphDir, 'build.lock');
95
- const now = Date.now();
96
- try {
97
- const stat = fs.statSync(lockPath);
98
- // Stale lock older than 5 minutes — remove it and proceed
99
- if (now - stat.mtimeMs < 5 * 60 * 1000) {
100
- console.log('[graph] build already in progress — skipping');
101
- process.exit(0);
102
- }
103
- fs.unlinkSync(lockPath);
104
- } catch { /* lock does not exist — proceed */ }
105
-
106
- // Write lock file; the build process removes it on completion
107
- try { fs.writeFileSync(lockPath, String(process.pid)); } catch { /* non-fatal */ }
103
+ if (!claimLock(lockPath, 5 * 60 * 1000)) {
104
+ console.log('[graph] build already in progress — skipping');
105
+ process.exit(0);
106
+ }
108
107
 
109
108
  // Spawn a detached node process to run buildAsync from @monoes/monograph (ESM).
110
109
  // After the build, VACUUM the DB if it has >50% bloat (reclaim space from
@@ -113,18 +112,21 @@ const dbPathStr = JSON.stringify(path.join(projectDir, '.monomind', 'monograph.d
113
112
  const script = `
114
113
  import { buildAsync } from ${JSON.stringify(pathToFileURL(entryPoint).href)};
115
114
  import { unlinkSync, statSync } from 'fs';
116
- import { execSync } from 'child_process';
115
+ import { execFileSync } from 'child_process';
117
116
  try {
118
117
  await buildAsync(${JSON.stringify(projectDir)});
119
118
  // Vacuum if bloat ratio is high — keeps openDb fast over time.
120
119
  try {
121
120
  const dbPath = ${dbPathStr};
122
121
  const fileMB = statSync(dbPath).size / 1024 / 1024;
122
+ // execFileSync with array argv — no shell involved, so a project
123
+ // directory name containing '"' or '$(...)' cannot break out and
124
+ // execute arbitrary commands (P3-8).
123
125
  const liveMB = parseInt(
124
- execSync('sqlite3 "' + dbPath + '" "SELECT SUM(pgsize)/1024/1024 FROM dbstat;"',
125
- { encoding: 'utf-8' }).trim(), 10);
126
+ execFileSync('sqlite3', [dbPath, 'SELECT SUM(pgsize)/1024/1024 FROM dbstat;'],
127
+ { encoding: 'utf-8', timeout: 30000 }).trim(), 10);
126
128
  if (fileMB > 100 && liveMB / fileMB < 0.5) {
127
- execSync('sqlite3 "' + dbPath + '" "VACUUM;"', { timeout: 120000 });
129
+ execFileSync('sqlite3', [dbPath, 'VACUUM;'], { timeout: 120000 });
128
130
  }
129
131
  } catch (_) {}
130
132
  } finally {