@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,6 @@
1
+ import { homedir } from 'os';
2
+ import { join } from 'path';
3
+ /**
4
+ * Base hivemind directory: ~/.hivemind
5
+ */
6
+ export const HIVEMIND_BASE = process.env.HIVEMIND_BASE || join(homedir(), '.hivemind');
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,141 @@
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 { nextEventSeq } from './nextEventSeq';
5
+ import { nextTaskSeq } from './nextTaskSeq';
6
+ describe('SQLite Database', () => {
7
+ let testDb;
8
+ beforeEach(() => {
9
+ testDb = createTestDb();
10
+ });
11
+ afterEach(() => {
12
+ testDb.cleanup();
13
+ });
14
+ afterAll(() => {
15
+ cleanupAllTestDbs();
16
+ });
17
+ describe('schema initialization', () => {
18
+ it('creates all required tables', () => {
19
+ const tables = testDb.db
20
+ .prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'`)
21
+ .all();
22
+ const tableNames = tables.map((t) => t.name);
23
+ expect(tableNames).toContain('agents');
24
+ expect(tableNames).toContain('worktrees');
25
+ expect(tableNames).toContain('plans');
26
+ expect(tableNames).toContain('tasks');
27
+ expect(tableNames).toContain('events');
28
+ });
29
+ it('creates indexes', () => {
30
+ const indexes = testDb.db
31
+ .prepare(`SELECT name FROM sqlite_master WHERE type='index' AND name LIKE 'idx_%'`)
32
+ .all();
33
+ expect(indexes.length).toBeGreaterThan(0);
34
+ });
35
+ });
36
+ describe('WAL mode', () => {
37
+ it('uses WAL journal mode', () => {
38
+ const result = testDb.db.query('PRAGMA journal_mode').get();
39
+ expect(result.journal_mode).toBe('wal');
40
+ });
41
+ });
42
+ describe('basic CRUD operations', () => {
43
+ it('can insert and select agents', () => {
44
+ const { agent } = buildAgent(testDb.db, { label: 'test' });
45
+ const retrieved = testDb.db.prepare(`SELECT * FROM agents WHERE id = ?`).get(agent.id);
46
+ expect(retrieved).toBeDefined();
47
+ expect(retrieved.id).toBe(agent.id);
48
+ expect(retrieved.status).toBe('active');
49
+ });
50
+ it('can insert and select plans', () => {
51
+ const { plan } = buildPlan(testDb.db, { title: 'Test Plan' });
52
+ const retrieved = testDb.db.prepare(`SELECT * FROM plans WHERE id = ?`).get(plan.id);
53
+ expect(retrieved).toBeDefined();
54
+ expect(retrieved.title).toBe('Test Plan');
55
+ });
56
+ it('can insert and select tasks', () => {
57
+ const { task } = buildTask(testDb.db, { title: 'Test Task' });
58
+ const retrieved = testDb.db.prepare(`SELECT * FROM tasks WHERE id = ?`).get(task.id);
59
+ expect(retrieved).toBeDefined();
60
+ expect(retrieved.title).toBe('Test Task');
61
+ expect(retrieved.status).toBe('pending');
62
+ });
63
+ it('can insert and select events', () => {
64
+ const { agent } = buildAgent(testDb.db);
65
+ const eventId = 'evt_abc123_1';
66
+ testDb.db
67
+ .prepare(`INSERT INTO events (id, hex, seq, timestamp, agent_id, event_type, content)
68
+ VALUES (?, ?, ?, ?, ?, ?, ?)`)
69
+ .run(eventId, 'abc123', 1, '2024/01/01 00:00:00 UTC', agent.id, 'note', 'Test event');
70
+ const event = testDb.db.prepare(`SELECT * FROM events WHERE id = ?`).get(eventId);
71
+ expect(event).toBeDefined();
72
+ expect(event.event_type).toBe('note');
73
+ expect(event.content).toBe('Test event');
74
+ });
75
+ });
76
+ describe('foreign key constraints', () => {
77
+ it('enforces task->plan foreign key', () => {
78
+ expect(() => {
79
+ testDb.db
80
+ .prepare(`INSERT INTO tasks (id, plan_hex, seq, plan_id, title, status)
81
+ VALUES (?, ?, ?, ?, ?, ?)`)
82
+ .run('tsk_test01_01', 'test01', '01', 'nonexistent_plan', 'Test Task', 'pending');
83
+ }).toThrow();
84
+ });
85
+ });
86
+ });
87
+ describe('nextEventSeq', () => {
88
+ let testDb;
89
+ beforeEach(() => {
90
+ testDb = createTestDb();
91
+ });
92
+ afterEach(() => {
93
+ testDb.cleanup();
94
+ });
95
+ it('returns 1 for first event', () => {
96
+ const seq = nextEventSeq(testDb.db);
97
+ expect(seq).toBe(1);
98
+ });
99
+ it('increments sequence atomically', () => {
100
+ const seq1 = nextEventSeq(testDb.db);
101
+ const seq2 = nextEventSeq(testDb.db);
102
+ const seq3 = nextEventSeq(testDb.db);
103
+ expect(seq1).toBe(1);
104
+ expect(seq2).toBe(2);
105
+ expect(seq3).toBe(3);
106
+ });
107
+ });
108
+ describe('nextTaskSeq', () => {
109
+ let testDb;
110
+ beforeEach(() => {
111
+ testDb = createTestDb();
112
+ });
113
+ afterEach(() => {
114
+ testDb.cleanup();
115
+ });
116
+ it('returns 1 for first task in plan', () => {
117
+ const { plan } = buildPlan(testDb.db);
118
+ const seq = nextTaskSeq(testDb.db, plan.id);
119
+ expect(seq).toBe(1);
120
+ });
121
+ it('increments sequence as tasks are added', () => {
122
+ const { plan } = buildPlan(testDb.db);
123
+ // Insert first task using factory
124
+ buildTask(testDb.db, { plan, title: 'Task 1' });
125
+ const seq = nextTaskSeq(testDb.db, plan.id);
126
+ expect(seq).toBe(2);
127
+ });
128
+ it('only counts top-level tasks (not subtasks)', () => {
129
+ const { plan } = buildPlan(testDb.db);
130
+ const { task: parentTask } = buildTask(testDb.db, { plan, title: 'Task 1' });
131
+ // Insert a subtask manually (factories don't support this yet)
132
+ const planHex = parentTask.id.split('_')[1];
133
+ testDb.db
134
+ .prepare(`INSERT INTO tasks (id, plan_hex, seq, plan_id, title, status, parent_task_id)
135
+ VALUES (?, ?, ?, ?, ?, ?, ?)`)
136
+ .run(`tsk_${planHex}_001.1`, planHex, '001.1', plan.id, 'Subtask 1', 'pending', parentTask.id);
137
+ // Should still be 2 (subtask doesn't count)
138
+ const seq = nextTaskSeq(testDb.db, plan.id);
139
+ expect(seq).toBe(2);
140
+ });
141
+ });
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Ensure all project directories exist
3
+ */
4
+ export declare function ensureProjectDirs(projectName: string): void;
@@ -0,0 +1,12 @@
1
+ import { mkdirSync } from 'fs';
2
+ import { getProjectPaths } from './getProjectPaths';
3
+ /**
4
+ * Ensure all project directories exist
5
+ */
6
+ export function ensureProjectDirs(projectName) {
7
+ const paths = getProjectPaths(projectName);
8
+ mkdirSync(paths.projectDir, { recursive: true });
9
+ mkdirSync(paths.journalDir, { recursive: true });
10
+ mkdirSync(paths.plansDir, { recursive: true });
11
+ mkdirSync(paths.agentsDir, { recursive: true });
12
+ }
@@ -0,0 +1,19 @@
1
+ import { Database } from 'bun:sqlite';
2
+ /**
3
+ * Get database connection for a project
4
+ *
5
+ * Caches connections and initializes if needed
6
+ */
7
+ export declare function getConnection(projectName: string): Database;
8
+ /**
9
+ * Close and clear a cached connection
10
+ *
11
+ * Call this before deleting/resetting the database
12
+ */
13
+ export declare function closeConnection(projectName: string): void;
14
+ /**
15
+ * Set a connection in the cache
16
+ *
17
+ * Used after reset to cache the new connection
18
+ */
19
+ export declare function setConnection(projectName: string, db: Database): void;
@@ -0,0 +1,51 @@
1
+ import { Database } from 'bun:sqlite';
2
+ import { existsSync } from 'fs';
3
+ import { getProjectPaths } from './getProjectPaths';
4
+ import { initializeDb } from './initializeDb';
5
+ const connections = new Map();
6
+ /**
7
+ * Get database connection for a project
8
+ *
9
+ * Caches connections and initializes if needed
10
+ */
11
+ export function getConnection(projectName) {
12
+ if (connections.has(projectName)) {
13
+ return connections.get(projectName);
14
+ }
15
+ const paths = getProjectPaths(projectName);
16
+ let db;
17
+ if (existsSync(paths.dbPath)) {
18
+ db = new Database(paths.dbPath);
19
+ db.exec('PRAGMA journal_mode = WAL');
20
+ }
21
+ else {
22
+ db = initializeDb(projectName);
23
+ }
24
+ connections.set(projectName, db);
25
+ return db;
26
+ }
27
+ /**
28
+ * Close and clear a cached connection
29
+ *
30
+ * Call this before deleting/resetting the database
31
+ */
32
+ export function closeConnection(projectName) {
33
+ const conn = connections.get(projectName);
34
+ if (conn) {
35
+ try {
36
+ conn.close();
37
+ }
38
+ catch {
39
+ // Ignore close errors
40
+ }
41
+ connections.delete(projectName);
42
+ }
43
+ }
44
+ /**
45
+ * Set a connection in the cache
46
+ *
47
+ * Used after reset to cache the new connection
48
+ */
49
+ export function setConnection(projectName, db) {
50
+ connections.set(projectName, db);
51
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Get current project name
3
+ *
4
+ * Priority:
5
+ * 1. HIVEMIND_PROJECT env var
6
+ * 2. Current directory name
7
+ */
8
+ export declare function getCurrentProject(): string;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Get current project name
3
+ *
4
+ * Priority:
5
+ * 1. HIVEMIND_PROJECT env var
6
+ * 2. Current directory name
7
+ */
8
+ export function getCurrentProject() {
9
+ if (process.env.HIVEMIND_PROJECT) {
10
+ return process.env.HIVEMIND_PROJECT;
11
+ }
12
+ const cwd = process.cwd();
13
+ return cwd.split('/').pop() || 'default';
14
+ }
@@ -0,0 +1,21 @@
1
+ export type ProjectPaths = {
2
+ projectDir: string;
3
+ dbPath: string;
4
+ streamPath: string;
5
+ journalDir: string;
6
+ plansDir: string;
7
+ agentsDir: string;
8
+ };
9
+ /**
10
+ * Get project-specific paths
11
+ *
12
+ * Structure:
13
+ * ~/.hivemind/
14
+ * └── claude_hivemind_<project>/
15
+ * ├── hivemind.db
16
+ * ├── stream.jsonl
17
+ * ├── journal/
18
+ * ├── plans/
19
+ * └── agents/
20
+ */
21
+ export declare function getProjectPaths(projectName: string): ProjectPaths;
@@ -0,0 +1,26 @@
1
+ import { join } from 'path';
2
+ import { HIVEMIND_BASE } from './constants';
3
+ /**
4
+ * Get project-specific paths
5
+ *
6
+ * Structure:
7
+ * ~/.hivemind/
8
+ * └── claude_hivemind_<project>/
9
+ * ├── hivemind.db
10
+ * ├── stream.jsonl
11
+ * ├── journal/
12
+ * ├── plans/
13
+ * └── agents/
14
+ */
15
+ export function getProjectPaths(projectName) {
16
+ const sanitized = projectName.toLowerCase().replace(/[^a-z0-9]/g, '_');
17
+ const projectDir = join(HIVEMIND_BASE, `claude_hivemind_${sanitized}`);
18
+ return {
19
+ projectDir,
20
+ dbPath: join(projectDir, 'hivemind.db'),
21
+ streamPath: join(projectDir, 'stream.jsonl'),
22
+ journalDir: join(projectDir, 'journal'),
23
+ plansDir: join(projectDir, 'plans'),
24
+ agentsDir: join(projectDir, 'agents'),
25
+ };
26
+ }
@@ -0,0 +1,10 @@
1
+ export { HIVEMIND_BASE } from './constants';
2
+ export { getProjectPaths } from './getProjectPaths';
3
+ export type { ProjectPaths } from './getProjectPaths';
4
+ export { getCurrentProject } from './getCurrentProject';
5
+ export { ensureProjectDirs } from './ensureProjectDirs';
6
+ export { initializeDb } from './initializeDb';
7
+ export { getConnection } from './getConnection';
8
+ export { nextEventSeq } from './nextEventSeq';
9
+ export { nextTaskSeq } from './nextTaskSeq';
10
+ export { nextSubtaskSeq } from './nextSubtaskSeq';
@@ -0,0 +1,13 @@
1
+ // Constants
2
+ export { HIVEMIND_BASE } from './constants';
3
+ // Paths
4
+ export { getProjectPaths } from './getProjectPaths';
5
+ export { getCurrentProject } from './getCurrentProject';
6
+ export { ensureProjectDirs } from './ensureProjectDirs';
7
+ // Database
8
+ export { initializeDb } from './initializeDb';
9
+ export { getConnection } from './getConnection';
10
+ // Sequences
11
+ export { nextEventSeq } from './nextEventSeq';
12
+ export { nextTaskSeq } from './nextTaskSeq';
13
+ export { nextSubtaskSeq } from './nextSubtaskSeq';
@@ -0,0 +1,7 @@
1
+ import { Database } from 'bun:sqlite';
2
+ /**
3
+ * Initialize the database for a project
4
+ *
5
+ * Creates directories and runs schema
6
+ */
7
+ export declare function initializeDb(projectName: string): Database;
@@ -0,0 +1,23 @@
1
+ import { Database } from 'bun:sqlite';
2
+ import { readFileSync } from 'fs';
3
+ import { join, dirname } from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import { getProjectPaths } from './getProjectPaths';
6
+ import { ensureProjectDirs } from './ensureProjectDirs';
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+ /**
9
+ * Initialize the database for a project
10
+ *
11
+ * Creates directories and runs schema
12
+ */
13
+ export function initializeDb(projectName) {
14
+ ensureProjectDirs(projectName);
15
+ const paths = getProjectPaths(projectName);
16
+ const db = new Database(paths.dbPath);
17
+ // Enable WAL mode for better concurrent access
18
+ db.exec('PRAGMA journal_mode = WAL');
19
+ // Run schema
20
+ const schema = readFileSync(join(__dirname, 'schema.sql'), 'utf-8');
21
+ db.exec(schema);
22
+ return db;
23
+ }
@@ -0,0 +1,5 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ /**
3
+ * Get next event sequence number (atomic increment)
4
+ */
5
+ export declare function nextEventSeq(db: Database): number;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Get next event sequence number (atomic increment)
3
+ */
4
+ export function nextEventSeq(db) {
5
+ const stmt = db.prepare(`
6
+ UPDATE sequences
7
+ SET value = value + 1
8
+ WHERE name = 'events'
9
+ RETURNING value
10
+ `);
11
+ const result = stmt.get();
12
+ return result.value;
13
+ }
@@ -0,0 +1,5 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ /**
3
+ * Get next subtask sequence number for a parent task
4
+ */
5
+ export declare function nextSubtaskSeq(db: Database, parentTaskId: string): number;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Get next subtask sequence number for a parent task
3
+ */
4
+ export function nextSubtaskSeq(db, parentTaskId) {
5
+ const stmt = db.prepare(`
6
+ SELECT COUNT(*) as count
7
+ FROM tasks
8
+ WHERE parent_task_id = ?
9
+ `);
10
+ const result = stmt.get(parentTaskId);
11
+ return result.count + 1;
12
+ }
@@ -0,0 +1,5 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ /**
3
+ * Get next task sequence number for a plan
4
+ */
5
+ export declare function nextTaskSeq(db: Database, planId: string): number;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Get next task sequence number for a plan
3
+ */
4
+ export function nextTaskSeq(db, planId) {
5
+ const stmt = db.prepare(`
6
+ SELECT COUNT(*) as count
7
+ FROM tasks
8
+ WHERE plan_id = ?
9
+ AND seq NOT LIKE '%.%'
10
+ `);
11
+ const result = stmt.get(planId);
12
+ return result.count + 1;
13
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Reset the database for a project
3
+ *
4
+ * Deletes the database files and recreates with fresh schema.
5
+ * Use this when schema changes require a clean slate.
6
+ */
7
+ export declare function resetDb(projectName: string): {
8
+ deleted: string[];
9
+ created: string;
10
+ };
@@ -0,0 +1,36 @@
1
+ import { existsSync, unlinkSync } from 'fs';
2
+ import { getProjectPaths } from './getProjectPaths';
3
+ import { initializeDb } from './initializeDb';
4
+ import { closeConnection, setConnection } from './getConnection';
5
+ /**
6
+ * Reset the database for a project
7
+ *
8
+ * Deletes the database files and recreates with fresh schema.
9
+ * Use this when schema changes require a clean slate.
10
+ */
11
+ export function resetDb(projectName) {
12
+ const paths = getProjectPaths(projectName);
13
+ const deleted = [];
14
+ // Close any existing connection first
15
+ closeConnection(projectName);
16
+ // Delete database files (main + WAL files)
17
+ const filesToDelete = [
18
+ paths.dbPath,
19
+ `${paths.dbPath}-wal`,
20
+ `${paths.dbPath}-shm`,
21
+ ];
22
+ for (const file of filesToDelete) {
23
+ if (existsSync(file)) {
24
+ unlinkSync(file);
25
+ deleted.push(file);
26
+ }
27
+ }
28
+ // Reinitialize with fresh schema
29
+ const db = initializeDb(projectName);
30
+ // Cache the new connection
31
+ setConnection(projectName, db);
32
+ return {
33
+ deleted,
34
+ created: paths.dbPath,
35
+ };
36
+ }
@@ -0,0 +1,6 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ import type { EventInput, Event } from './types';
3
+ /**
4
+ * Emit an event to the hivemind log
5
+ */
6
+ export declare function emit(db: Database, input: EventInput): Event;
@@ -0,0 +1,31 @@
1
+ import { makeEventId } from '../ids/makeEventId';
2
+ import { now } from '../datetime/now';
3
+ import { nextEventSeq } from '../db/nextEventSeq';
4
+ /**
5
+ * Emit an event to the hivemind log
6
+ */
7
+ export function emit(db, input) {
8
+ const seq = nextEventSeq(db);
9
+ const { id, hex } = makeEventId(seq);
10
+ const timestamp = now();
11
+ const stmt = db.prepare(`
12
+ INSERT INTO events (id, hex, seq, timestamp, agent_id, plan_id, task_id, worktree_id, branch, event_type, content, metadata)
13
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
14
+ `);
15
+ const agentId = input.agent_id ?? 'system';
16
+ stmt.run(id, hex, seq, timestamp, agentId, input.plan_id ?? null, input.task_id ?? null, input.worktree_id ?? null, input.branch ?? null, input.type, input.content ?? null, input.metadata ? JSON.stringify(input.metadata) : null);
17
+ return {
18
+ id,
19
+ hex,
20
+ seq,
21
+ timestamp,
22
+ agent_id: agentId,
23
+ plan_id: input.plan_id ?? null,
24
+ task_id: input.task_id ?? null,
25
+ worktree_id: input.worktree_id ?? null,
26
+ branch: input.branch ?? null,
27
+ event_type: input.type,
28
+ content: input.content ?? null,
29
+ metadata: input.metadata ? JSON.stringify(input.metadata) : null,
30
+ };
31
+ }
@@ -0,0 +1 @@
1
+ export {};