@inixiative/hivemind 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (287) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +115 -0
  3. package/dist/agents/agents.test.d.ts +1 -0
  4. package/dist/agents/agents.test.js +167 -0
  5. package/dist/agents/getActiveAgents.d.ts +6 -0
  6. package/dist/agents/getActiveAgents.js +11 -0
  7. package/dist/agents/getAgent.d.ts +6 -0
  8. package/dist/agents/getAgent.js +7 -0
  9. package/dist/agents/getAgentBySessionId.d.ts +10 -0
  10. package/dist/agents/getAgentBySessionId.js +17 -0
  11. package/dist/agents/index.d.ts +10 -0
  12. package/dist/agents/index.js +12 -0
  13. package/dist/agents/markAgentDead.d.ts +6 -0
  14. package/dist/agents/markAgentDead.js +26 -0
  15. package/dist/agents/markAgentIdle.d.ts +5 -0
  16. package/dist/agents/markAgentIdle.js +12 -0
  17. package/dist/agents/registerAgent.d.ts +6 -0
  18. package/dist/agents/registerAgent.js +29 -0
  19. package/dist/agents/types.d.ts +30 -0
  20. package/dist/agents/types.js +1 -0
  21. package/dist/agents/unregisterAgent.d.ts +5 -0
  22. package/dist/agents/unregisterAgent.js +8 -0
  23. package/dist/agents/updateAgentContext.d.ts +5 -0
  24. package/dist/agents/updateAgentContext.js +12 -0
  25. package/dist/agents/updateAgentTask.d.ts +5 -0
  26. package/dist/agents/updateAgentTask.js +12 -0
  27. package/dist/agents/updateAgentWorktree.d.ts +5 -0
  28. package/dist/agents/updateAgentWorktree.js +12 -0
  29. package/dist/cli/index.d.ts +8 -0
  30. package/dist/cli/index.js +8 -0
  31. package/dist/cli/init.d.ts +14 -0
  32. package/dist/cli/init.js +71 -0
  33. package/dist/cli/install.d.ts +8 -0
  34. package/dist/cli/install.js +47 -0
  35. package/dist/cli/join.d.ts +9 -0
  36. package/dist/cli/join.js +38 -0
  37. package/dist/cli/registerMcp.d.ts +28 -0
  38. package/dist/cli/registerMcp.js +138 -0
  39. package/dist/cli/status.d.ts +8 -0
  40. package/dist/cli/status.js +82 -0
  41. package/dist/cli/watch.d.ts +6 -0
  42. package/dist/cli/watch.js +68 -0
  43. package/dist/cli.d.ts +12 -0
  44. package/dist/cli.js +49 -0
  45. package/dist/coordinator/coordinator.test.d.ts +1 -0
  46. package/dist/coordinator/coordinator.test.js +171 -0
  47. package/dist/coordinator/index.d.ts +16 -0
  48. package/dist/coordinator/index.js +166 -0
  49. package/dist/coordinator/spawn.d.ts +22 -0
  50. package/dist/coordinator/spawn.js +66 -0
  51. package/dist/datetime/datetime.test.d.ts +1 -0
  52. package/dist/datetime/datetime.test.js +63 -0
  53. package/dist/datetime/formatDate.d.ts +6 -0
  54. package/dist/datetime/formatDate.js +11 -0
  55. package/dist/datetime/formatDatetime.d.ts +6 -0
  56. package/dist/datetime/formatDatetime.js +12 -0
  57. package/dist/datetime/formatTime.d.ts +6 -0
  58. package/dist/datetime/formatTime.js +11 -0
  59. package/dist/datetime/index.d.ts +4 -0
  60. package/dist/datetime/index.js +7 -0
  61. package/dist/datetime/isStale.d.ts +10 -0
  62. package/dist/datetime/isStale.js +18 -0
  63. package/dist/datetime/now.d.ts +6 -0
  64. package/dist/datetime/now.js +9 -0
  65. package/dist/datetime/parseDatetime.d.ts +7 -0
  66. package/dist/datetime/parseDatetime.js +28 -0
  67. package/dist/db/constants.d.ts +4 -0
  68. package/dist/db/constants.js +6 -0
  69. package/dist/db/db.test.d.ts +1 -0
  70. package/dist/db/db.test.js +141 -0
  71. package/dist/db/ensureProjectDirs.d.ts +4 -0
  72. package/dist/db/ensureProjectDirs.js +12 -0
  73. package/dist/db/getConnection.d.ts +19 -0
  74. package/dist/db/getConnection.js +51 -0
  75. package/dist/db/getCurrentProject.d.ts +8 -0
  76. package/dist/db/getCurrentProject.js +14 -0
  77. package/dist/db/getProjectPaths.d.ts +21 -0
  78. package/dist/db/getProjectPaths.js +26 -0
  79. package/dist/db/index.d.ts +10 -0
  80. package/dist/db/index.js +13 -0
  81. package/dist/db/initializeDb.d.ts +7 -0
  82. package/dist/db/initializeDb.js +23 -0
  83. package/dist/db/nextEventSeq.d.ts +5 -0
  84. package/dist/db/nextEventSeq.js +13 -0
  85. package/dist/db/nextSubtaskSeq.d.ts +5 -0
  86. package/dist/db/nextSubtaskSeq.js +12 -0
  87. package/dist/db/nextTaskSeq.d.ts +5 -0
  88. package/dist/db/nextTaskSeq.js +13 -0
  89. package/dist/db/resetDb.d.ts +10 -0
  90. package/dist/db/resetDb.js +36 -0
  91. package/dist/events/emit.d.ts +6 -0
  92. package/dist/events/emit.js +31 -0
  93. package/dist/events/events.test.d.ts +1 -0
  94. package/dist/events/events.test.js +145 -0
  95. package/dist/events/getEventsByAgent.d.ts +6 -0
  96. package/dist/events/getEventsByAgent.js +14 -0
  97. package/dist/events/getEventsByBranch.d.ts +6 -0
  98. package/dist/events/getEventsByBranch.js +12 -0
  99. package/dist/events/getEventsByPlan.d.ts +6 -0
  100. package/dist/events/getEventsByPlan.js +14 -0
  101. package/dist/events/getEventsByWorktree.d.ts +6 -0
  102. package/dist/events/getEventsByWorktree.js +12 -0
  103. package/dist/events/getEventsSince.d.ts +12 -0
  104. package/dist/events/getEventsSince.js +47 -0
  105. package/dist/events/getRecentEvents.d.ts +6 -0
  106. package/dist/events/getRecentEvents.js +12 -0
  107. package/dist/events/index.d.ts +8 -0
  108. package/dist/events/index.js +9 -0
  109. package/dist/events/types.d.ts +34 -0
  110. package/dist/events/types.js +1 -0
  111. package/dist/git/getBranch.d.ts +4 -0
  112. package/dist/git/getBranch.js +14 -0
  113. package/dist/git/getCurrentWorktree.d.ts +5 -0
  114. package/dist/git/getCurrentWorktree.js +15 -0
  115. package/dist/git/getGitInfo.d.ts +10 -0
  116. package/dist/git/getGitInfo.js +23 -0
  117. package/dist/git/getRepoName.d.ts +4 -0
  118. package/dist/git/getRepoName.js +32 -0
  119. package/dist/git/getRepoRoot.d.ts +4 -0
  120. package/dist/git/getRepoRoot.js +14 -0
  121. package/dist/git/getWorktrees.d.ts +10 -0
  122. package/dist/git/getWorktrees.js +39 -0
  123. package/dist/git/index.d.ts +9 -0
  124. package/dist/git/index.js +7 -0
  125. package/dist/git/isGitRepo.d.ts +4 -0
  126. package/dist/git/isGitRepo.js +13 -0
  127. package/dist/hooks/index.d.ts +1 -0
  128. package/dist/hooks/index.js +1 -0
  129. package/dist/hooks/sessionStart.d.ts +21 -0
  130. package/dist/hooks/sessionStart.js +93 -0
  131. package/dist/ids/generateHex.d.ts +4 -0
  132. package/dist/ids/generateHex.js +7 -0
  133. package/dist/ids/getParentTaskId.d.ts +7 -0
  134. package/dist/ids/getParentTaskId.js +15 -0
  135. package/dist/ids/getPlanHexFromTaskId.d.ts +6 -0
  136. package/dist/ids/getPlanHexFromTaskId.js +9 -0
  137. package/dist/ids/ids.test.d.ts +1 -0
  138. package/dist/ids/ids.test.js +215 -0
  139. package/dist/ids/index.d.ts +16 -0
  140. package/dist/ids/index.js +17 -0
  141. package/dist/ids/isSubtask.d.ts +7 -0
  142. package/dist/ids/isSubtask.js +11 -0
  143. package/dist/ids/isValidId.d.ts +9 -0
  144. package/dist/ids/isValidId.js +22 -0
  145. package/dist/ids/makeAgentId.d.ts +8 -0
  146. package/dist/ids/makeAgentId.js +15 -0
  147. package/dist/ids/makeEventId.d.ts +11 -0
  148. package/dist/ids/makeEventId.js +12 -0
  149. package/dist/ids/makePlanId.d.ts +11 -0
  150. package/dist/ids/makePlanId.js +15 -0
  151. package/dist/ids/makeSubtaskId.d.ts +8 -0
  152. package/dist/ids/makeSubtaskId.js +15 -0
  153. package/dist/ids/makeTaskId.d.ts +8 -0
  154. package/dist/ids/makeTaskId.js +14 -0
  155. package/dist/ids/makeWorktreeId.d.ts +5 -0
  156. package/dist/ids/makeWorktreeId.js +12 -0
  157. package/dist/ids/parseId.d.ts +11 -0
  158. package/dist/ids/parseId.js +26 -0
  159. package/dist/ids/sanitizeLabel.d.ts +7 -0
  160. package/dist/ids/sanitizeLabel.js +12 -0
  161. package/dist/ids/typedIds.d.ts +34 -0
  162. package/dist/ids/typedIds.js +22 -0
  163. package/dist/ids/types.d.ts +14 -0
  164. package/dist/ids/types.js +1 -0
  165. package/dist/init/claudeConfig.d.ts +39 -0
  166. package/dist/init/claudeConfig.js +161 -0
  167. package/dist/llm/extractTasks.d.ts +28 -0
  168. package/dist/llm/extractTasks.js +108 -0
  169. package/dist/llm/index.d.ts +2 -0
  170. package/dist/llm/index.js +2 -0
  171. package/dist/llm/reconcileTasks.d.ts +21 -0
  172. package/dist/llm/reconcileTasks.js +82 -0
  173. package/dist/mcp/server.d.ts +2 -0
  174. package/dist/mcp/server.js +100 -0
  175. package/dist/mcp/tools/emitEvent.d.ts +62 -0
  176. package/dist/mcp/tools/emitEvent.js +84 -0
  177. package/dist/mcp/tools/events.d.ts +55 -0
  178. package/dist/mcp/tools/events.js +56 -0
  179. package/dist/mcp/tools/index.d.ts +18 -0
  180. package/dist/mcp/tools/index.js +13 -0
  181. package/dist/mcp/tools/query.d.ts +54 -0
  182. package/dist/mcp/tools/query.js +70 -0
  183. package/dist/mcp/tools/register.d.ts +47 -0
  184. package/dist/mcp/tools/register.js +79 -0
  185. package/dist/mcp/tools/reset.d.ts +38 -0
  186. package/dist/mcp/tools/reset.js +56 -0
  187. package/dist/mcp/tools/setup.d.ts +42 -0
  188. package/dist/mcp/tools/setup.js +75 -0
  189. package/dist/mcp/tools/status.d.ts +44 -0
  190. package/dist/mcp/tools/status.js +74 -0
  191. package/dist/mcp/tools/tasks.d.ts +116 -0
  192. package/dist/mcp/tools/tasks.js +143 -0
  193. package/dist/mcp/tools/worktreeCleanup.d.ts +38 -0
  194. package/dist/mcp/tools/worktreeCleanup.js +67 -0
  195. package/dist/plans/createPlan.d.ts +6 -0
  196. package/dist/plans/createPlan.js +29 -0
  197. package/dist/plans/getActivePlans.d.ts +6 -0
  198. package/dist/plans/getActivePlans.js +11 -0
  199. package/dist/plans/getPlan.d.ts +6 -0
  200. package/dist/plans/getPlan.js +7 -0
  201. package/dist/plans/index.d.ts +5 -0
  202. package/dist/plans/index.js +5 -0
  203. package/dist/plans/plans.test.d.ts +1 -0
  204. package/dist/plans/plans.test.js +107 -0
  205. package/dist/plans/types.d.ts +32 -0
  206. package/dist/plans/types.js +1 -0
  207. package/dist/plans/updatePlanStatus.d.ts +6 -0
  208. package/dist/plans/updatePlanStatus.js +8 -0
  209. package/dist/tasks/assignTask.d.ts +7 -0
  210. package/dist/tasks/assignTask.js +20 -0
  211. package/dist/tasks/blockTask.d.ts +5 -0
  212. package/dist/tasks/blockTask.js +12 -0
  213. package/dist/tasks/claimTask.d.ts +7 -0
  214. package/dist/tasks/claimTask.js +21 -0
  215. package/dist/tasks/completeTask.d.ts +6 -0
  216. package/dist/tasks/completeTask.js +18 -0
  217. package/dist/tasks/createTask.d.ts +6 -0
  218. package/dist/tasks/createTask.js +36 -0
  219. package/dist/tasks/getPendingTasks.d.ts +6 -0
  220. package/dist/tasks/getPendingTasks.js +19 -0
  221. package/dist/tasks/getTask.d.ts +6 -0
  222. package/dist/tasks/getTask.js +7 -0
  223. package/dist/tasks/getTasksByPlan.d.ts +6 -0
  224. package/dist/tasks/getTasksByPlan.js +11 -0
  225. package/dist/tasks/index.d.ts +11 -0
  226. package/dist/tasks/index.js +12 -0
  227. package/dist/tasks/startTask.d.ts +5 -0
  228. package/dist/tasks/startTask.js +12 -0
  229. package/dist/tasks/tasks.test.d.ts +1 -0
  230. package/dist/tasks/tasks.test.js +209 -0
  231. package/dist/tasks/types.d.ts +36 -0
  232. package/dist/tasks/types.js +1 -0
  233. package/dist/tasks/unclaimTask.d.ts +5 -0
  234. package/dist/tasks/unclaimTask.js +12 -0
  235. package/dist/test/factories/agentFactory.d.ts +13 -0
  236. package/dist/test/factories/agentFactory.js +5 -0
  237. package/dist/test/factories/eventFactory.d.ts +20 -0
  238. package/dist/test/factories/eventFactory.js +16 -0
  239. package/dist/test/factories/factories.test.d.ts +1 -0
  240. package/dist/test/factories/factories.test.js +101 -0
  241. package/dist/test/factories/index.d.ts +4 -0
  242. package/dist/test/factories/index.js +4 -0
  243. package/dist/test/factories/planFactory.d.ts +15 -0
  244. package/dist/test/factories/planFactory.js +14 -0
  245. package/dist/test/factories/taskFactory.d.ts +22 -0
  246. package/dist/test/factories/taskFactory.js +44 -0
  247. package/dist/test/multiAgentDemo.d.ts +16 -0
  248. package/dist/test/multiAgentDemo.js +20 -0
  249. package/dist/test/multiAgentDemo.test.d.ts +1 -0
  250. package/dist/test/multiAgentDemo.test.js +14 -0
  251. package/dist/test/setup.d.ts +9 -0
  252. package/dist/test/setup.js +50 -0
  253. package/dist/utils/index.d.ts +5 -0
  254. package/dist/utils/index.js +5 -0
  255. package/dist/utils/math.d.ts +6 -0
  256. package/dist/utils/math.js +10 -0
  257. package/dist/utils/math.test.d.ts +1 -0
  258. package/dist/utils/math.test.js +26 -0
  259. package/dist/utils/string.d.ts +11 -0
  260. package/dist/utils/string.js +17 -0
  261. package/dist/utils/string.test.d.ts +1 -0
  262. package/dist/utils/string.test.js +30 -0
  263. package/dist/watcher/index.d.ts +1 -0
  264. package/dist/watcher/index.js +1 -0
  265. package/dist/watcher/planWatcher.d.ts +31 -0
  266. package/dist/watcher/planWatcher.js +171 -0
  267. package/dist/worktrees/getAllWorktrees.d.ts +6 -0
  268. package/dist/worktrees/getAllWorktrees.js +10 -0
  269. package/dist/worktrees/getWorktreeById.d.ts +6 -0
  270. package/dist/worktrees/getWorktreeById.js +7 -0
  271. package/dist/worktrees/getWorktreeByPath.d.ts +6 -0
  272. package/dist/worktrees/getWorktreeByPath.js +7 -0
  273. package/dist/worktrees/index.d.ts +9 -0
  274. package/dist/worktrees/index.js +13 -0
  275. package/dist/worktrees/registerWorktree.d.ts +6 -0
  276. package/dist/worktrees/registerWorktree.js +28 -0
  277. package/dist/worktrees/removeWorktree.d.ts +6 -0
  278. package/dist/worktrees/removeWorktree.js +9 -0
  279. package/dist/worktrees/syncWorktreesFromGit.d.ts +7 -0
  280. package/dist/worktrees/syncWorktreesFromGit.js +51 -0
  281. package/dist/worktrees/types.d.ts +29 -0
  282. package/dist/worktrees/types.js +1 -0
  283. package/dist/worktrees/updateWorktreeCommit.d.ts +5 -0
  284. package/dist/worktrees/updateWorktreeCommit.js +13 -0
  285. package/dist/worktrees/updateWorktreeSeen.d.ts +5 -0
  286. package/dist/worktrees/updateWorktreeSeen.js +13 -0
  287. package/package.json +58 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Aron Greenspan / inixiative
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,115 @@
1
+ # Hivemind
2
+
3
+ Multi-agent coordination for Claude Code. Shared event log, plans, and tasks across multiple Claude sessions.
4
+
5
+ ## Installation
6
+
7
+ ### From npm (recommended)
8
+
9
+ ```bash
10
+ npm install -g @inixiative/hivemind
11
+ ```
12
+
13
+ Then add to Claude Code:
14
+
15
+ ```bash
16
+ claude mcp add hivemind -- hivemind-mcp
17
+ ```
18
+
19
+ Or manually add to `~/.claude/settings.json`:
20
+
21
+ ```json
22
+ {
23
+ "mcpServers": {
24
+ "hivemind": {
25
+ "command": "hivemind-mcp"
26
+ }
27
+ }
28
+ }
29
+ ```
30
+
31
+ ### From source
32
+
33
+ ```bash
34
+ git clone https://github.com/inixiative/hivemind.git
35
+ cd hivemind
36
+ ./setup.sh
37
+ ```
38
+
39
+ Restart Claude Code and hivemind is active.
40
+
41
+ On startup, Claude receives context about the hivemind state:
42
+ ```
43
+ hivemind: agt_7a3f2b joined myproject (main)
44
+ session: abc123-def456
45
+ active: agt_c4d5e6_alice
46
+ ```
47
+
48
+ This info goes into Claude's system context (not printed to terminal). Claude knows its agent ID and can see other active agents.
49
+
50
+ ## How It Works
51
+
52
+ Agents are tracked by **process ID (PID)**, not heartbeats:
53
+ - SessionStart hook registers agent with Claude's PID
54
+ - Coordinator monitors PIDs every 30 seconds
55
+ - When Claude exits, coordinator detects dead PID and marks agent dead
56
+ - No polling or heartbeats required
57
+
58
+ See [ARCHITECTURE.md](ARCHITECTURE.md) for details.
59
+
60
+ ## Tools
61
+
62
+ | Tool | Description | Auto |
63
+ |------|-------------|------|
64
+ | `hivemind_status` | Get status (agents, plans, events) | ✓ |
65
+ | `hivemind_events` | Get recent events | ✓ |
66
+ | `hivemind_query` | Query events with filters | ✓ |
67
+ | `hivemind_claim_task` | Claim a task | ✓ |
68
+ | `hivemind_start_task` | Mark task in progress | ✓ |
69
+ | `hivemind_complete_task` | Mark task done | ✓ |
70
+ | `hivemind_worktree_cleanup` | Clean up stale worktrees | ✓ |
71
+ | `hivemind_setup` | Initialize project | ✓ |
72
+ | `hivemind_register` | Register this agent | ✓ |
73
+ | `hivemind_emit` | Emit event to log | ✗ |
74
+ | `hivemind_reset` | Reset database | ✗ |
75
+
76
+ Tools marked ✓ are auto-approved after `./setup.sh`. Tools marked ✗ require confirmation.
77
+
78
+ ## CLI
79
+
80
+ ```bash
81
+ hivemind install # One-time global setup
82
+ hivemind init # Register current project
83
+ hivemind status # Show project status
84
+ hivemind watch # Live tail of events
85
+ hivemind join # Join as an agent
86
+ ```
87
+
88
+ ## ID Format
89
+
90
+ `{type}_{6hex}[_{label}]`
91
+
92
+ - `agt_7a3f2b_alice` - Agent
93
+ - `pln_e9d2c1_auth` - Plan
94
+ - `tsk_e9d2c1_001` - Task (inherits plan hex)
95
+ - `evt_f1a2b3_00001` - Event
96
+ - `wkt_a1b2c3` - Worktree
97
+
98
+ ## Event Types
99
+
100
+ ```
101
+ agent:register/unregister
102
+ plan:create/join/complete
103
+ task:create/claim/start/complete/block
104
+ decision, question, answer, note
105
+ ```
106
+
107
+ ## Database
108
+
109
+ Stored at `~/.hivemind/claude_hivemind_{project}/hivemind.db`
110
+
111
+ Tables: agents, plans, tasks, events, worktrees
112
+
113
+ ## Timestamps
114
+
115
+ Format: `yyyy/mm/dd hh:mm:ss TZ`
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,167 @@
1
+ import { describe, it, expect, beforeEach, afterEach, afterAll } from 'bun:test';
2
+ import { createTestDb, cleanupAllTestDbs } from '../test/setup';
3
+ import { buildAgent, buildTask } from '../test/factories';
4
+ import { registerAgent } from './registerAgent';
5
+ import { getAgent } from './getAgent';
6
+ import { getActiveAgents } from './getActiveAgents';
7
+ import { markAgentDead } from './markAgentDead';
8
+ import { markAgentIdle } from './markAgentIdle';
9
+ import { updateAgentContext } from './updateAgentContext';
10
+ import { updateAgentTask } from './updateAgentTask';
11
+ import { unregisterAgent } from './unregisterAgent';
12
+ describe('Agents Module', () => {
13
+ let testDb;
14
+ beforeEach(() => {
15
+ testDb = createTestDb();
16
+ });
17
+ afterEach(() => {
18
+ testDb.cleanup();
19
+ });
20
+ afterAll(() => {
21
+ cleanupAllTestDbs();
22
+ });
23
+ describe('registerAgent', () => {
24
+ it('creates a new agent with generated id', () => {
25
+ const agent = registerAgent(testDb.db, {});
26
+ expect(agent.id).toMatch(/^agt_[a-f0-9]{6}$/);
27
+ expect(agent.status).toBe('active');
28
+ expect(agent.created_at).toBeDefined();
29
+ });
30
+ it('creates agent with label', () => {
31
+ const agent = registerAgent(testDb.db, { label: 'main' });
32
+ expect(agent.id).toMatch(/^agt_[a-f0-9]{6}_main$/);
33
+ expect(agent.label).toBe('main');
34
+ });
35
+ it('creates agent with context summary', () => {
36
+ const agent = registerAgent(testDb.db, {
37
+ context_summary: 'Working on auth feature',
38
+ });
39
+ expect(agent.context_summary).toBe('Working on auth feature');
40
+ });
41
+ });
42
+ describe('getAgent', () => {
43
+ it('retrieves existing agent', () => {
44
+ const { agent: created } = buildAgent(testDb.db, { label: 'test' });
45
+ const retrieved = getAgent(testDb.db, created.id);
46
+ expect(retrieved).toBeDefined();
47
+ expect(retrieved.id).toBe(created.id);
48
+ expect(retrieved.label).toBe('test');
49
+ });
50
+ it('returns null for non-existent agent', () => {
51
+ const agent = getAgent(testDb.db, 'agt_nonexistent');
52
+ expect(agent).toBeNull();
53
+ });
54
+ });
55
+ describe('getActiveAgents', () => {
56
+ it('returns only active agents', () => {
57
+ const { agent: agent1 } = buildAgent(testDb.db, { label: 'one' });
58
+ const { agent: agent2 } = buildAgent(testDb.db, { label: 'two' });
59
+ markAgentDead(testDb.db, agent2.id);
60
+ const active = getActiveAgents(testDb.db);
61
+ expect(active).toHaveLength(1);
62
+ expect(active[0].id).toBe(agent1.id);
63
+ });
64
+ it('returns empty array when no active agents', () => {
65
+ const active = getActiveAgents(testDb.db);
66
+ expect(active).toHaveLength(0);
67
+ });
68
+ });
69
+ describe('markAgentDead', () => {
70
+ it('sets status to dead', () => {
71
+ const { agent } = buildAgent(testDb.db);
72
+ markAgentDead(testDb.db, agent.id);
73
+ const retrieved = getAgent(testDb.db, agent.id);
74
+ expect(retrieved.status).toBe('dead');
75
+ });
76
+ it('releases claimed tasks back to pending', () => {
77
+ const { agent } = buildAgent(testDb.db);
78
+ const { task } = buildTask(testDb.db, {
79
+ agent,
80
+ status: 'claimed',
81
+ claimed_by: agent,
82
+ });
83
+ markAgentDead(testDb.db, agent.id);
84
+ const updated = testDb.db.prepare(`SELECT * FROM tasks WHERE id = ?`).get(task.id);
85
+ expect(updated.status).toBe('pending');
86
+ expect(updated.claimed_by).toBeNull();
87
+ expect(updated.claimed_at).toBeNull();
88
+ });
89
+ it('releases in_progress tasks back to pending', () => {
90
+ const { agent } = buildAgent(testDb.db);
91
+ const { task } = buildTask(testDb.db, {
92
+ agent,
93
+ status: 'in_progress',
94
+ claimed_by: agent,
95
+ });
96
+ markAgentDead(testDb.db, agent.id);
97
+ const updated = testDb.db.prepare(`SELECT * FROM tasks WHERE id = ?`).get(task.id);
98
+ expect(updated.status).toBe('pending');
99
+ expect(updated.claimed_by).toBeNull();
100
+ });
101
+ it('clears agent current_task_id and current_plan_id', () => {
102
+ const { agent } = buildAgent(testDb.db);
103
+ const { task, plan } = buildTask(testDb.db, { agent });
104
+ updateAgentTask(testDb.db, agent.id, plan.id, task.id);
105
+ let retrieved = getAgent(testDb.db, agent.id);
106
+ expect(retrieved.current_plan_id).toBe(plan.id);
107
+ expect(retrieved.current_task_id).toBe(task.id);
108
+ markAgentDead(testDb.db, agent.id);
109
+ retrieved = getAgent(testDb.db, agent.id);
110
+ expect(retrieved.current_plan_id).toBeNull();
111
+ expect(retrieved.current_task_id).toBeNull();
112
+ });
113
+ it('does not affect done tasks', () => {
114
+ const { agent } = buildAgent(testDb.db);
115
+ const { task } = buildTask(testDb.db, {
116
+ agent,
117
+ status: 'done',
118
+ claimed_by: agent,
119
+ });
120
+ markAgentDead(testDb.db, agent.id);
121
+ const updated = testDb.db.prepare(`SELECT * FROM tasks WHERE id = ?`).get(task.id);
122
+ expect(updated.status).toBe('done');
123
+ expect(updated.claimed_by).toBe(agent.id);
124
+ });
125
+ });
126
+ describe('markAgentIdle', () => {
127
+ it('sets status to idle', () => {
128
+ const { agent } = buildAgent(testDb.db);
129
+ markAgentIdle(testDb.db, agent.id);
130
+ const retrieved = getAgent(testDb.db, agent.id);
131
+ expect(retrieved.status).toBe('idle');
132
+ });
133
+ });
134
+ describe('updateAgentContext', () => {
135
+ it('updates context summary', () => {
136
+ const { agent } = buildAgent(testDb.db);
137
+ updateAgentContext(testDb.db, agent.id, 'New context');
138
+ const retrieved = getAgent(testDb.db, agent.id);
139
+ expect(retrieved.context_summary).toBe('New context');
140
+ });
141
+ });
142
+ describe('updateAgentTask', () => {
143
+ it('updates current plan and task id', () => {
144
+ const { agent } = buildAgent(testDb.db);
145
+ const { task, plan } = buildTask(testDb.db, { agent });
146
+ updateAgentTask(testDb.db, agent.id, plan.id, task.id);
147
+ const retrieved = getAgent(testDb.db, agent.id);
148
+ expect(retrieved.current_plan_id).toBe(plan.id);
149
+ expect(retrieved.current_task_id).toBe(task.id);
150
+ });
151
+ it('clears plan and task with null', () => {
152
+ const { agent } = buildAgent(testDb.db);
153
+ updateAgentTask(testDb.db, agent.id, null, null);
154
+ const retrieved = getAgent(testDb.db, agent.id);
155
+ expect(retrieved.current_plan_id).toBeNull();
156
+ expect(retrieved.current_task_id).toBeNull();
157
+ });
158
+ });
159
+ describe('unregisterAgent', () => {
160
+ it('removes agent from database', () => {
161
+ const { agent } = buildAgent(testDb.db);
162
+ unregisterAgent(testDb.db, agent.id);
163
+ const retrieved = getAgent(testDb.db, agent.id);
164
+ expect(retrieved).toBeNull();
165
+ });
166
+ });
167
+ });
@@ -0,0 +1,6 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ import type { Agent } from './types';
3
+ /**
4
+ * Get all active agents
5
+ */
6
+ export declare function getActiveAgents(db: Database): Agent[];
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Get all active agents
3
+ */
4
+ export function getActiveAgents(db) {
5
+ const stmt = db.prepare(`
6
+ SELECT * FROM agents
7
+ WHERE status = 'active'
8
+ ORDER BY created_at DESC
9
+ `);
10
+ return stmt.all();
11
+ }
@@ -0,0 +1,6 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ import type { Agent } from './types';
3
+ /**
4
+ * Get an agent by ID
5
+ */
6
+ export declare function getAgent(db: Database, agentId: string): Agent | null;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Get an agent by ID
3
+ */
4
+ export function getAgent(db, agentId) {
5
+ const stmt = db.prepare('SELECT * FROM agents WHERE id = ?');
6
+ return stmt.get(agentId) ?? null;
7
+ }
@@ -0,0 +1,10 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ import type { Agent } from './types';
3
+ /**
4
+ * Get an active agent by session ID
5
+ *
6
+ * Used for MCP tools to find "their" agent without needing the agent_id.
7
+ * The session_id is passed by Claude Code to hooks and can be used to
8
+ * correlate the hook-registered agent with subsequent MCP calls.
9
+ */
10
+ export declare function getAgentBySessionId(db: Database, sessionId: string): Agent | null;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Get an active agent by session ID
3
+ *
4
+ * Used for MCP tools to find "their" agent without needing the agent_id.
5
+ * The session_id is passed by Claude Code to hooks and can be used to
6
+ * correlate the hook-registered agent with subsequent MCP calls.
7
+ */
8
+ export function getAgentBySessionId(db, sessionId) {
9
+ const stmt = db.prepare(`
10
+ SELECT * FROM agents
11
+ WHERE session_id = ?
12
+ AND status = 'active'
13
+ ORDER BY created_at DESC
14
+ LIMIT 1
15
+ `);
16
+ return stmt.get(sessionId) ?? null;
17
+ }
@@ -0,0 +1,10 @@
1
+ export type { AgentStatus, Agent, RegisterAgentInput } from './types';
2
+ export { registerAgent } from './registerAgent';
3
+ export { unregisterAgent } from './unregisterAgent';
4
+ export { getAgent } from './getAgent';
5
+ export { getActiveAgents } from './getActiveAgents';
6
+ export { updateAgentContext } from './updateAgentContext';
7
+ export { updateAgentTask } from './updateAgentTask';
8
+ export { updateAgentWorktree } from './updateAgentWorktree';
9
+ export { markAgentDead } from './markAgentDead';
10
+ export { markAgentIdle } from './markAgentIdle';
@@ -0,0 +1,12 @@
1
+ // Registration
2
+ export { registerAgent } from './registerAgent';
3
+ export { unregisterAgent } from './unregisterAgent';
4
+ // Query
5
+ export { getAgent } from './getAgent';
6
+ export { getActiveAgents } from './getActiveAgents';
7
+ // Updates
8
+ export { updateAgentContext } from './updateAgentContext';
9
+ export { updateAgentTask } from './updateAgentTask';
10
+ export { updateAgentWorktree } from './updateAgentWorktree';
11
+ export { markAgentDead } from './markAgentDead';
12
+ export { markAgentIdle } from './markAgentIdle';
@@ -0,0 +1,6 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ /**
3
+ * Mark an agent as dead (no longer responding)
4
+ * Also releases any tasks the agent had claimed back to pending
5
+ */
6
+ export declare function markAgentDead(db: Database, agentId: string): boolean;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Mark an agent as dead (no longer responding)
3
+ * Also releases any tasks the agent had claimed back to pending
4
+ */
5
+ export function markAgentDead(db, agentId) {
6
+ // Release any tasks this agent had claimed or was working on
7
+ const releaseTasksStmt = db.prepare(`
8
+ UPDATE tasks
9
+ SET status = 'pending',
10
+ claimed_by = NULL,
11
+ claimed_at = NULL
12
+ WHERE claimed_by = ?
13
+ AND status IN ('claimed', 'in_progress')
14
+ `);
15
+ releaseTasksStmt.run(agentId);
16
+ // Mark the agent as dead and clear its current task/plan references
17
+ const stmt = db.prepare(`
18
+ UPDATE agents
19
+ SET status = 'dead',
20
+ current_task_id = NULL,
21
+ current_plan_id = NULL
22
+ WHERE id = ?
23
+ `);
24
+ const result = stmt.run(agentId);
25
+ return result.changes > 0;
26
+ }
@@ -0,0 +1,5 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ /**
3
+ * Mark an agent as idle (alive but not actively working)
4
+ */
5
+ export declare function markAgentIdle(db: Database, agentId: string): boolean;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Mark an agent as idle (alive but not actively working)
3
+ */
4
+ export function markAgentIdle(db, agentId) {
5
+ const stmt = db.prepare(`
6
+ UPDATE agents
7
+ SET status = 'idle'
8
+ WHERE id = ?
9
+ `);
10
+ const result = stmt.run(agentId);
11
+ return result.changes > 0;
12
+ }
@@ -0,0 +1,6 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ import type { Agent, RegisterAgentInput } from './types';
3
+ /**
4
+ * Register a new agent in the hivemind
5
+ */
6
+ export declare function registerAgent(db: Database, input?: RegisterAgentInput): Agent;
@@ -0,0 +1,29 @@
1
+ import { makeAgentId } from '../ids/makeAgentId';
2
+ import { parseId } from '../ids/parseId';
3
+ import { now } from '../datetime/now';
4
+ /**
5
+ * Register a new agent in the hivemind
6
+ */
7
+ export function registerAgent(db, input = {}) {
8
+ const id = makeAgentId(input.label);
9
+ const parsed = parseId(id);
10
+ const timestamp = now();
11
+ const stmt = db.prepare(`
12
+ INSERT INTO agents (id, hex, label, status, pid, session_id, worktree_id, context_summary, created_at)
13
+ VALUES (?, ?, ?, 'active', ?, ?, ?, ?, ?)
14
+ `);
15
+ stmt.run(id, parsed.hex, parsed.label ?? null, input.pid ?? null, input.session_id ?? null, input.worktree_id ?? null, input.context_summary ?? null, timestamp);
16
+ return {
17
+ id,
18
+ hex: parsed.hex,
19
+ label: parsed.label ?? null,
20
+ status: 'active',
21
+ pid: input.pid ?? null,
22
+ session_id: input.session_id ?? null,
23
+ current_plan_id: null,
24
+ current_task_id: null,
25
+ worktree_id: input.worktree_id ?? null,
26
+ context_summary: input.context_summary ?? null,
27
+ created_at: timestamp,
28
+ };
29
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Agent status values
3
+ */
4
+ export type AgentStatus = 'active' | 'idle' | 'dead';
5
+ /**
6
+ * Agent record as stored in DB
7
+ */
8
+ export type Agent = {
9
+ id: string;
10
+ hex: string;
11
+ label: string | null;
12
+ status: AgentStatus;
13
+ pid: number | null;
14
+ session_id: string | null;
15
+ current_plan_id: string | null;
16
+ current_task_id: string | null;
17
+ worktree_id: string | null;
18
+ context_summary: string | null;
19
+ created_at: string;
20
+ };
21
+ /**
22
+ * Input for registering an agent
23
+ */
24
+ export type RegisterAgentInput = {
25
+ label?: string;
26
+ pid?: number;
27
+ session_id?: string;
28
+ worktree_id?: string;
29
+ context_summary?: string;
30
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ /**
3
+ * Remove an agent from the hivemind
4
+ */
5
+ export declare function unregisterAgent(db: Database, agentId: string): boolean;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Remove an agent from the hivemind
3
+ */
4
+ export function unregisterAgent(db, agentId) {
5
+ const stmt = db.prepare('DELETE FROM agents WHERE id = ?');
6
+ const result = stmt.run(agentId);
7
+ return result.changes > 0;
8
+ }
@@ -0,0 +1,5 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ /**
3
+ * Update an agent's context summary (what it knows/is working on)
4
+ */
5
+ export declare function updateAgentContext(db: Database, agentId: string, contextSummary: string): boolean;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Update an agent's context summary (what it knows/is working on)
3
+ */
4
+ export function updateAgentContext(db, agentId, contextSummary) {
5
+ const stmt = db.prepare(`
6
+ UPDATE agents
7
+ SET context_summary = ?
8
+ WHERE id = ?
9
+ `);
10
+ const result = stmt.run(contextSummary, agentId);
11
+ return result.changes > 0;
12
+ }
@@ -0,0 +1,5 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ /**
3
+ * Update an agent's current plan and task
4
+ */
5
+ export declare function updateAgentTask(db: Database, agentId: string, planId: string | null, taskId: string | null): boolean;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Update an agent's current plan and task
3
+ */
4
+ export function updateAgentTask(db, agentId, planId, taskId) {
5
+ const stmt = db.prepare(`
6
+ UPDATE agents
7
+ SET current_plan_id = ?, current_task_id = ?
8
+ WHERE id = ?
9
+ `);
10
+ const result = stmt.run(planId, taskId, agentId);
11
+ return result.changes > 0;
12
+ }
@@ -0,0 +1,5 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ /**
3
+ * Update an agent's worktree assignment
4
+ */
5
+ export declare function updateAgentWorktree(db: Database, agentId: string, worktreeId: string | null): boolean;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Update an agent's worktree assignment
3
+ */
4
+ export function updateAgentWorktree(db, agentId, worktreeId) {
5
+ const stmt = db.prepare(`
6
+ UPDATE agents
7
+ SET worktree_id = ?
8
+ WHERE id = ?
9
+ `);
10
+ const result = stmt.run(worktreeId, agentId);
11
+ return result.changes > 0;
12
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * CLI commands
3
+ */
4
+ export { installCommand } from './install';
5
+ export { initCommand } from './init';
6
+ export { statusCommand } from './status';
7
+ export { joinCommand } from './join';
8
+ export { registerMcpServer, isMcpRegistered } from './registerMcp';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * CLI commands
3
+ */
4
+ export { installCommand } from './install';
5
+ export { initCommand } from './init';
6
+ export { statusCommand } from './status';
7
+ export { joinCommand } from './join';
8
+ export { registerMcpServer, isMcpRegistered } from './registerMcp';
@@ -0,0 +1,14 @@
1
+ /**
2
+ * hivemind init - Register a project with hivemind
3
+ *
4
+ * Run from a project directory to:
5
+ * 1. Create database in ~/.hivemind/<project>/
6
+ * 2. Set up .claude/settings.json hooks
7
+ * 3. Add CLAUDE.md instructions
8
+ */
9
+ type InitOptions = {
10
+ project?: string;
11
+ hooks?: boolean;
12
+ };
13
+ export declare function initCommand(options: InitOptions): Promise<void>;
14
+ export {};