@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
@@ -0,0 +1,145 @@
1
+ import { describe, it, expect, beforeEach, afterEach, afterAll } from 'bun:test';
2
+ import { createTestDb, cleanupAllTestDbs } from '../test/setup';
3
+ import { buildAgent, buildPlan, buildTask } from '../test/factories';
4
+ import { emit } from './emit';
5
+ import { getEventsSince } from './getEventsSince';
6
+ import { getEventsByPlan } from './getEventsByPlan';
7
+ import { getEventsByAgent } from './getEventsByAgent';
8
+ import { getRecentEvents } from './getRecentEvents';
9
+ describe('Events Module', () => {
10
+ let testDb;
11
+ beforeEach(() => {
12
+ testDb = createTestDb();
13
+ });
14
+ afterEach(() => {
15
+ testDb.cleanup();
16
+ });
17
+ afterAll(() => {
18
+ cleanupAllTestDbs();
19
+ });
20
+ describe('emit', () => {
21
+ it('creates a new event', () => {
22
+ const { agent } = buildAgent(testDb.db, { label: 'test' });
23
+ const event = emit(testDb.db, {
24
+ agent_id: agent.id,
25
+ type: 'note',
26
+ content: 'Test note',
27
+ });
28
+ expect(event.id).toMatch(/^evt_[a-f0-9]{6}_\d+$/);
29
+ expect(event.agent_id).toBe(agent.id);
30
+ expect(event.event_type).toBe('note');
31
+ expect(event.content).toBe('Test note');
32
+ expect(event.timestamp).toBeDefined();
33
+ });
34
+ it('creates event with plan and task', () => {
35
+ const { task, plan, agent } = buildTask(testDb.db);
36
+ const event = emit(testDb.db, {
37
+ agent_id: agent.id,
38
+ plan_id: plan.id,
39
+ task_id: task.id,
40
+ type: 'task:start',
41
+ content: 'Starting task',
42
+ });
43
+ expect(event.plan_id).toBe(plan.id);
44
+ expect(event.task_id).toBe(task.id);
45
+ });
46
+ it('creates event with branch and worktree', () => {
47
+ const { agent } = buildAgent(testDb.db);
48
+ const event = emit(testDb.db, {
49
+ agent_id: agent.id,
50
+ branch: 'feature/auth',
51
+ type: 'context',
52
+ content: 'Working on auth',
53
+ });
54
+ expect(event.branch).toBe('feature/auth');
55
+ });
56
+ it('stores metadata as JSON', () => {
57
+ const { agent } = buildAgent(testDb.db);
58
+ const event = emit(testDb.db, {
59
+ agent_id: agent.id,
60
+ type: 'decision',
61
+ content: 'Chose option A',
62
+ metadata: { reason: 'simpler', alternatives: ['B', 'C'] },
63
+ });
64
+ expect(event.metadata).toBe('{"reason":"simpler","alternatives":["B","C"]}');
65
+ expect(JSON.parse(event.metadata)).toEqual({ reason: 'simpler', alternatives: ['B', 'C'] });
66
+ });
67
+ it('increments sequence for same hex', () => {
68
+ const { agent } = buildAgent(testDb.db);
69
+ const event1 = emit(testDb.db, { agent_id: agent.id, type: 'note', content: 'First' });
70
+ const event2 = emit(testDb.db, { agent_id: agent.id, type: 'note', content: 'Second' });
71
+ const seq1 = parseInt(event1.id.split('_')[2]);
72
+ const seq2 = parseInt(event2.id.split('_')[2]);
73
+ expect(seq2).toBe(seq1 + 1);
74
+ });
75
+ });
76
+ describe('getEventsSince', () => {
77
+ it('returns events after sequence number', () => {
78
+ const { agent } = buildAgent(testDb.db);
79
+ const event1 = emit(testDb.db, { agent_id: agent.id, type: 'note', content: 'First' });
80
+ emit(testDb.db, { agent_id: agent.id, type: 'note', content: 'Second' });
81
+ const events = getEventsSince(testDb.db, event1.seq);
82
+ expect(events).toHaveLength(1);
83
+ expect(events[0].content).toBe('Second');
84
+ });
85
+ it('returns events after timestamp', () => {
86
+ const { agent } = buildAgent(testDb.db);
87
+ emit(testDb.db, { agent_id: agent.id, type: 'note', content: 'First' });
88
+ emit(testDb.db, { agent_id: agent.id, type: 'note', content: 'Second' });
89
+ const events = getEventsSince(testDb.db, '2020/01/01 00:00:00 UTC');
90
+ expect(events.length).toBeGreaterThanOrEqual(2);
91
+ });
92
+ it('returns empty array when no events after sequence', () => {
93
+ const { agent } = buildAgent(testDb.db);
94
+ const event = emit(testDb.db, { agent_id: agent.id, type: 'note', content: 'Only event' });
95
+ const events = getEventsSince(testDb.db, event.seq);
96
+ expect(events).toHaveLength(0);
97
+ });
98
+ it('returns empty array when no events after timestamp', () => {
99
+ const { agent } = buildAgent(testDb.db);
100
+ emit(testDb.db, { agent_id: agent.id, type: 'note', content: 'Old event' });
101
+ const events = getEventsSince(testDb.db, '2099/01/01 00:00:00 UTC');
102
+ expect(events).toHaveLength(0);
103
+ });
104
+ });
105
+ describe('getEventsByPlan', () => {
106
+ it('returns events for specific plan', () => {
107
+ const { plan, agent } = buildPlan(testDb.db);
108
+ emit(testDb.db, { agent_id: agent.id, plan_id: plan.id, type: 'plan:create', content: 'Created' });
109
+ emit(testDb.db, { agent_id: agent.id, type: 'note', content: 'Unrelated' });
110
+ const events = getEventsByPlan(testDb.db, plan.id);
111
+ expect(events).toHaveLength(1);
112
+ expect(events[0].plan_id).toBe(plan.id);
113
+ });
114
+ });
115
+ describe('getEventsByAgent', () => {
116
+ it('returns events for specific agent', () => {
117
+ const { agent: agent1 } = buildAgent(testDb.db, { label: 'one' });
118
+ const { agent: agent2 } = buildAgent(testDb.db, { label: 'two' });
119
+ emit(testDb.db, { agent_id: agent1.id, type: 'note', content: 'Agent 1 event' });
120
+ emit(testDb.db, { agent_id: agent2.id, type: 'note', content: 'Agent 2 event' });
121
+ const events = getEventsByAgent(testDb.db, agent1.id);
122
+ expect(events).toHaveLength(1);
123
+ expect(events[0].agent_id).toBe(agent1.id);
124
+ });
125
+ });
126
+ describe('getRecentEvents', () => {
127
+ it('returns limited recent events', () => {
128
+ const { agent } = buildAgent(testDb.db);
129
+ for (let i = 0; i < 10; i++) {
130
+ emit(testDb.db, { agent_id: agent.id, type: 'note', content: `Event ${i}` });
131
+ }
132
+ const events = getRecentEvents(testDb.db, 5);
133
+ expect(events).toHaveLength(5);
134
+ });
135
+ it('returns events in chronological order (oldest first)', () => {
136
+ const { agent } = buildAgent(testDb.db);
137
+ emit(testDb.db, { agent_id: agent.id, type: 'note', content: 'First' });
138
+ emit(testDb.db, { agent_id: agent.id, type: 'note', content: 'Second' });
139
+ emit(testDb.db, { agent_id: agent.id, type: 'note', content: 'Third' });
140
+ const events = getRecentEvents(testDb.db, 3);
141
+ expect(events[0].content).toBe('First');
142
+ expect(events[2].content).toBe('Third');
143
+ });
144
+ });
145
+ });
@@ -0,0 +1,6 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ import type { Event } from './types';
3
+ /**
4
+ * Get all events for a specific agent
5
+ */
6
+ export declare function getEventsByAgent(db: Database, agentId: string, limit?: number): Event[];
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Get all events for a specific agent
3
+ */
4
+ export function getEventsByAgent(db, agentId, limit) {
5
+ const sql = limit
6
+ ? `SELECT * FROM events WHERE agent_id = ? ORDER BY seq DESC LIMIT ?`
7
+ : `SELECT * FROM events WHERE agent_id = ? ORDER BY seq ASC`;
8
+ const stmt = db.prepare(sql);
9
+ if (limit) {
10
+ const results = stmt.all(agentId, limit);
11
+ return results.reverse();
12
+ }
13
+ return stmt.all(agentId);
14
+ }
@@ -0,0 +1,6 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ import type { Event } from './types';
3
+ /**
4
+ * Get events for a specific branch
5
+ */
6
+ export declare function getEventsByBranch(db: Database, branch: string, limit?: number): Event[];
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Get events for a specific branch
3
+ */
4
+ export function getEventsByBranch(db, branch, limit = 100) {
5
+ const stmt = db.prepare(`
6
+ SELECT * FROM events
7
+ WHERE branch = ?
8
+ ORDER BY seq DESC
9
+ LIMIT ?
10
+ `);
11
+ return stmt.all(branch, limit);
12
+ }
@@ -0,0 +1,6 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ import type { Event } from './types';
3
+ /**
4
+ * Get all events for a specific plan
5
+ */
6
+ export declare function getEventsByPlan(db: Database, planId: string, limit?: number): Event[];
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Get all events for a specific plan
3
+ */
4
+ export function getEventsByPlan(db, planId, limit) {
5
+ const sql = limit
6
+ ? `SELECT * FROM events WHERE plan_id = ? ORDER BY seq DESC LIMIT ?`
7
+ : `SELECT * FROM events WHERE plan_id = ? ORDER BY seq ASC`;
8
+ const stmt = db.prepare(sql);
9
+ if (limit) {
10
+ const results = stmt.all(planId, limit);
11
+ return results.reverse(); // Return in chronological order
12
+ }
13
+ return stmt.all(planId);
14
+ }
@@ -0,0 +1,6 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ import type { Event } from './types';
3
+ /**
4
+ * Get events for a specific worktree
5
+ */
6
+ export declare function getEventsByWorktree(db: Database, worktreeId: string, limit?: number): Event[];
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Get events for a specific worktree
3
+ */
4
+ export function getEventsByWorktree(db, worktreeId, limit = 100) {
5
+ const stmt = db.prepare(`
6
+ SELECT * FROM events
7
+ WHERE worktree_id = ?
8
+ ORDER BY seq DESC
9
+ LIMIT ?
10
+ `);
11
+ return stmt.all(worktreeId, limit);
12
+ }
@@ -0,0 +1,12 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ import type { Event } from './types';
3
+ /**
4
+ * Get all events since a given point
5
+ *
6
+ * @param since - Either a sequence number (number) or timestamp (string)
7
+ * - number: Get events with seq > since (exclusive, efficient for polling)
8
+ * - string: Get events with timestamp > since (for time-based queries)
9
+ *
10
+ * Used for polling: "What happened since I last checked?"
11
+ */
12
+ export declare function getEventsSince(db: Database, since: number | string, limit?: number): Event[];
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Get all events since a given point
3
+ *
4
+ * @param since - Either a sequence number (number) or timestamp (string)
5
+ * - number: Get events with seq > since (exclusive, efficient for polling)
6
+ * - string: Get events with timestamp > since (for time-based queries)
7
+ *
8
+ * Used for polling: "What happened since I last checked?"
9
+ */
10
+ export function getEventsSince(db, since, limit) {
11
+ if (typeof since === 'number') {
12
+ // Sequence-based query (most efficient for polling)
13
+ if (limit) {
14
+ const stmt = db.prepare(`
15
+ SELECT * FROM events
16
+ WHERE seq > ?
17
+ ORDER BY seq ASC
18
+ LIMIT ?
19
+ `);
20
+ return stmt.all(since, limit);
21
+ }
22
+ const stmt = db.prepare(`
23
+ SELECT * FROM events
24
+ WHERE seq > ?
25
+ ORDER BY seq ASC
26
+ `);
27
+ return stmt.all(since);
28
+ }
29
+ else {
30
+ // Timestamp-based query
31
+ if (limit) {
32
+ const stmt = db.prepare(`
33
+ SELECT * FROM events
34
+ WHERE timestamp > ?
35
+ ORDER BY seq ASC
36
+ LIMIT ?
37
+ `);
38
+ return stmt.all(since, limit);
39
+ }
40
+ const stmt = db.prepare(`
41
+ SELECT * FROM events
42
+ WHERE timestamp > ?
43
+ ORDER BY seq ASC
44
+ `);
45
+ return stmt.all(since);
46
+ }
47
+ }
@@ -0,0 +1,6 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ import type { Event } from './types';
3
+ /**
4
+ * Get the most recent events
5
+ */
6
+ export declare function getRecentEvents(db: Database, limit?: number): Event[];
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Get the most recent events
3
+ */
4
+ export function getRecentEvents(db, limit = 50) {
5
+ const stmt = db.prepare(`
6
+ SELECT * FROM events
7
+ ORDER BY seq DESC
8
+ LIMIT ?
9
+ `);
10
+ const results = stmt.all(limit);
11
+ return results.reverse(); // Chronological order
12
+ }
@@ -0,0 +1,8 @@
1
+ export type { EventType, Event, EventInput } from './types';
2
+ export { emit } from './emit';
3
+ export { getEventsSince } from './getEventsSince';
4
+ export { getEventsByPlan } from './getEventsByPlan';
5
+ export { getEventsByAgent } from './getEventsByAgent';
6
+ export { getEventsByWorktree } from './getEventsByWorktree';
7
+ export { getEventsByBranch } from './getEventsByBranch';
8
+ export { getRecentEvents } from './getRecentEvents';
@@ -0,0 +1,9 @@
1
+ // Emit
2
+ export { emit } from './emit';
3
+ // Query
4
+ export { getEventsSince } from './getEventsSince';
5
+ export { getEventsByPlan } from './getEventsByPlan';
6
+ export { getEventsByAgent } from './getEventsByAgent';
7
+ export { getEventsByWorktree } from './getEventsByWorktree';
8
+ export { getEventsByBranch } from './getEventsByBranch';
9
+ export { getRecentEvents } from './getRecentEvents';
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Event types in the hivemind system
3
+ */
4
+ export type EventType = 'agent:register' | 'agent:unregister' | 'agent:dead' | 'worktree:register' | 'worktree:switch' | 'worktree:stale' | 'plan:create' | 'plan:sync' | 'plan:join' | 'plan:leave' | 'plan:complete' | 'task:create' | 'task:claim' | 'task:unclaim' | 'task:start' | 'task:block' | 'task:unblock' | 'task:complete' | 'decision' | 'question' | 'answer' | 'context' | 'note';
5
+ /**
6
+ * Event record as stored in DB
7
+ */
8
+ export type Event = {
9
+ id: string;
10
+ hex: string;
11
+ seq: number;
12
+ timestamp: string;
13
+ agent_id: string;
14
+ plan_id: string | null;
15
+ task_id: string | null;
16
+ worktree_id: string | null;
17
+ branch: string | null;
18
+ event_type: EventType;
19
+ content: string | null;
20
+ metadata: string | null;
21
+ };
22
+ /**
23
+ * Input for creating an event
24
+ */
25
+ export type EventInput = {
26
+ agent_id?: string;
27
+ plan_id?: string;
28
+ task_id?: string;
29
+ worktree_id?: string;
30
+ branch?: string;
31
+ type: EventType;
32
+ content?: string;
33
+ metadata?: Record<string, unknown>;
34
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Get current git branch name
3
+ */
4
+ export declare function getBranch(): string | null;
@@ -0,0 +1,14 @@
1
+ import { execSync } from 'child_process';
2
+ /**
3
+ * Get current git branch name
4
+ */
5
+ export function getBranch() {
6
+ try {
7
+ return execSync('git branch --show-current', { stdio: 'pipe' })
8
+ .toString()
9
+ .trim() || null;
10
+ }
11
+ catch {
12
+ return null;
13
+ }
14
+ }
@@ -0,0 +1,5 @@
1
+ import { type Worktree } from './getWorktrees';
2
+ /**
3
+ * Get the worktree for current directory
4
+ */
5
+ export declare function getCurrentWorktree(): Worktree | null;
@@ -0,0 +1,15 @@
1
+ import { getWorktrees } from './getWorktrees';
2
+ /**
3
+ * Get the worktree for current directory
4
+ */
5
+ export function getCurrentWorktree() {
6
+ const cwd = process.cwd();
7
+ const worktrees = getWorktrees();
8
+ // Find worktree that contains current directory
9
+ for (const wt of worktrees) {
10
+ if (cwd.startsWith(wt.path)) {
11
+ return wt;
12
+ }
13
+ }
14
+ return null;
15
+ }
@@ -0,0 +1,10 @@
1
+ export type GitInfo = {
2
+ isRepo: boolean;
3
+ repoName: string | null;
4
+ branch: string | null;
5
+ root: string | null;
6
+ };
7
+ /**
8
+ * Get all git info for current directory
9
+ */
10
+ export declare function getGitInfo(): GitInfo;
@@ -0,0 +1,23 @@
1
+ import { isGitRepo } from './isGitRepo';
2
+ import { getRepoName } from './getRepoName';
3
+ import { getBranch } from './getBranch';
4
+ import { getRepoRoot } from './getRepoRoot';
5
+ /**
6
+ * Get all git info for current directory
7
+ */
8
+ export function getGitInfo() {
9
+ if (!isGitRepo()) {
10
+ return {
11
+ isRepo: false,
12
+ repoName: null,
13
+ branch: null,
14
+ root: null,
15
+ };
16
+ }
17
+ return {
18
+ isRepo: true,
19
+ repoName: getRepoName(),
20
+ branch: getBranch(),
21
+ root: getRepoRoot(),
22
+ };
23
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Get repository name from git remote or directory
3
+ */
4
+ export declare function getRepoName(): string | null;
@@ -0,0 +1,32 @@
1
+ import { execSync } from 'child_process';
2
+ /**
3
+ * Get repository name from git remote or directory
4
+ */
5
+ export function getRepoName() {
6
+ try {
7
+ // Try to get from remote origin
8
+ const remote = execSync('git remote get-url origin', { stdio: 'pipe' })
9
+ .toString()
10
+ .trim();
11
+ // Extract repo name from URL
12
+ // git@github.com:user/repo.git -> repo
13
+ // https://github.com/user/repo.git -> repo
14
+ const match = remote.match(/\/([^/]+?)(\.git)?$/);
15
+ if (match) {
16
+ return match[1];
17
+ }
18
+ }
19
+ catch {
20
+ // No remote, try repo root directory name
21
+ try {
22
+ const root = execSync('git rev-parse --show-toplevel', { stdio: 'pipe' })
23
+ .toString()
24
+ .trim();
25
+ return root.split('/').pop() || null;
26
+ }
27
+ catch {
28
+ return null;
29
+ }
30
+ }
31
+ return null;
32
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Get git repository root path
3
+ */
4
+ export declare function getRepoRoot(): string | null;
@@ -0,0 +1,14 @@
1
+ import { execSync } from 'child_process';
2
+ /**
3
+ * Get git repository root path
4
+ */
5
+ export function getRepoRoot() {
6
+ try {
7
+ return execSync('git rev-parse --show-toplevel', { stdio: 'pipe' })
8
+ .toString()
9
+ .trim() || null;
10
+ }
11
+ catch {
12
+ return null;
13
+ }
14
+ }
@@ -0,0 +1,10 @@
1
+ export type Worktree = {
2
+ path: string;
3
+ branch: string | null;
4
+ commit: string;
5
+ isMain: boolean;
6
+ };
7
+ /**
8
+ * Get all git worktrees for the repository
9
+ */
10
+ export declare function getWorktrees(): Worktree[];
@@ -0,0 +1,39 @@
1
+ import { execSync } from 'child_process';
2
+ /**
3
+ * Get all git worktrees for the repository
4
+ */
5
+ export function getWorktrees() {
6
+ try {
7
+ const output = execSync('git worktree list --porcelain', { stdio: 'pipe' })
8
+ .toString()
9
+ .trim();
10
+ if (!output)
11
+ return [];
12
+ const worktrees = [];
13
+ let current = {};
14
+ for (const line of output.split('\n')) {
15
+ if (line.startsWith('worktree ')) {
16
+ if (current.path) {
17
+ worktrees.push(current);
18
+ }
19
+ current = { path: line.slice(9), isMain: worktrees.length === 0 };
20
+ }
21
+ else if (line.startsWith('HEAD ')) {
22
+ current.commit = line.slice(5);
23
+ }
24
+ else if (line.startsWith('branch ')) {
25
+ current.branch = line.slice(7).replace('refs/heads/', '');
26
+ }
27
+ else if (line === 'detached') {
28
+ current.branch = null;
29
+ }
30
+ }
31
+ if (current.path) {
32
+ worktrees.push(current);
33
+ }
34
+ return worktrees;
35
+ }
36
+ catch {
37
+ return [];
38
+ }
39
+ }
@@ -0,0 +1,9 @@
1
+ export { isGitRepo } from './isGitRepo';
2
+ export { getRepoName } from './getRepoName';
3
+ export { getBranch } from './getBranch';
4
+ export { getRepoRoot } from './getRepoRoot';
5
+ export { getGitInfo } from './getGitInfo';
6
+ export type { GitInfo } from './getGitInfo';
7
+ export { getWorktrees } from './getWorktrees';
8
+ export type { Worktree } from './getWorktrees';
9
+ export { getCurrentWorktree } from './getCurrentWorktree';
@@ -0,0 +1,7 @@
1
+ export { isGitRepo } from './isGitRepo';
2
+ export { getRepoName } from './getRepoName';
3
+ export { getBranch } from './getBranch';
4
+ export { getRepoRoot } from './getRepoRoot';
5
+ export { getGitInfo } from './getGitInfo';
6
+ export { getWorktrees } from './getWorktrees';
7
+ export { getCurrentWorktree } from './getCurrentWorktree';
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Check if current directory is inside a git repo
3
+ */
4
+ export declare function isGitRepo(): boolean;
@@ -0,0 +1,13 @@
1
+ import { execSync } from 'child_process';
2
+ /**
3
+ * Check if current directory is inside a git repo
4
+ */
5
+ export function isGitRepo() {
6
+ try {
7
+ execSync('git rev-parse --is-inside-work-tree', { stdio: 'pipe' });
8
+ return true;
9
+ }
10
+ catch {
11
+ return false;
12
+ }
13
+ }
@@ -0,0 +1 @@
1
+ export { runSessionStartHook } from './sessionStart';
@@ -0,0 +1 @@
1
+ export { runSessionStartHook } from './sessionStart';
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Session start hook - auto-join hivemind when starting a Claude session
3
+ *
4
+ * Installation: Add to .claude/settings.json:
5
+ * "hooks": {
6
+ * "SessionStart": [{
7
+ * "matcher": "startup",
8
+ * "hooks": [{ "type": "command", "command": "bun run /path/to/hivemind/src/hooks/sessionStart.ts" }]
9
+ * }]
10
+ * }
11
+ *
12
+ * Claude Code passes JSON via stdin with session_id, transcript_path, cwd, etc.
13
+ */
14
+ type HookInput = {
15
+ session_id?: string;
16
+ transcript_path?: string;
17
+ cwd?: string;
18
+ permission_mode?: string;
19
+ };
20
+ export declare function runSessionStartHook(input?: HookInput): void;
21
+ export {};