@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,171 @@
1
+ /**
2
+ * Plan Watcher - watches ~/.claude/plans/ for plan mode files
3
+ *
4
+ * When Claude enters plan mode, it creates markdown files like:
5
+ * ~/.claude/plans/purrfect-watching-lamport.md
6
+ *
7
+ * This watcher detects new/changed plans and syncs them to hivemind.
8
+ * Uses LLM (Claude CLI) to extract tasks from markdown.
9
+ */
10
+ import { watch, existsSync, readFileSync, readdirSync } from 'fs';
11
+ import { join, basename } from 'path';
12
+ import { homedir } from 'os';
13
+ import { getConnection } from '../db/getConnection';
14
+ import { createPlan } from '../plans/createPlan';
15
+ import { getPlan } from '../plans/getPlan';
16
+ import { updatePlanStatus } from '../plans/updatePlanStatus';
17
+ import { emit } from '../events/emit';
18
+ import { extractTasksFromPlan } from '../llm/extractTasks';
19
+ import { reconcileTasks } from '../llm/reconcileTasks';
20
+ const CLAUDE_PLANS_DIR = join(homedir(), '.claude', 'plans');
21
+ // Track known plan files to detect deletions
22
+ const knownFiles = new Map(); // filename -> plan_id
23
+ // Debounce
24
+ const lastSync = new Map();
25
+ const DEBOUNCE_MS = 1000;
26
+ /**
27
+ * Parse plan title from markdown content
28
+ * Looks for first # heading
29
+ */
30
+ function parsePlanTitle(content) {
31
+ const match = content.match(/^#\s+(.+)$/m);
32
+ return match ? match[1].trim() : null;
33
+ }
34
+ /**
35
+ * Sync a single plan file to hivemind
36
+ * Uses LLM to extract tasks, then reconciles with DB
37
+ */
38
+ export async function syncPlanFile(project, filename) {
39
+ if (!filename.endsWith('.md')) {
40
+ return { success: false, message: 'Not a markdown file' };
41
+ }
42
+ // Debounce
43
+ const now = Date.now();
44
+ const last = lastSync.get(filename) ?? 0;
45
+ if (now - last < DEBOUNCE_MS) {
46
+ return { success: false, message: 'Debounced' };
47
+ }
48
+ lastSync.set(filename, now);
49
+ const filePath = join(CLAUDE_PLANS_DIR, filename);
50
+ if (!existsSync(filePath)) {
51
+ // File was deleted - mark plan as complete
52
+ const planId = knownFiles.get(filename);
53
+ if (planId) {
54
+ const db = getConnection(project);
55
+ updatePlanStatus(db, planId, 'complete');
56
+ knownFiles.delete(filename);
57
+ return { success: true, planId, message: `Plan ${planId} marked complete (file deleted)` };
58
+ }
59
+ return { success: false, message: 'File not found' };
60
+ }
61
+ // Read and parse
62
+ const content = readFileSync(filePath, 'utf-8');
63
+ const title = parsePlanTitle(content) || basename(filename, '.md');
64
+ const db = getConnection(project);
65
+ // Check if plan already exists
66
+ let plan = getPlan(db, knownFiles.get(filename) || '');
67
+ const isNew = !plan;
68
+ if (!plan) {
69
+ // Create new plan
70
+ plan = createPlan(db, {
71
+ title,
72
+ description: content.slice(0, 500),
73
+ label: basename(filename, '.md'),
74
+ });
75
+ knownFiles.set(filename, plan.id);
76
+ emit(db, {
77
+ type: 'plan:create',
78
+ content: `New plan: ${title}`,
79
+ plan_id: plan.id,
80
+ });
81
+ }
82
+ // Extract tasks using LLM
83
+ let tasks;
84
+ try {
85
+ tasks = await extractTasksFromPlan(content);
86
+ }
87
+ catch (err) {
88
+ const msg = err instanceof Error ? err.message : String(err);
89
+ return { success: false, planId: plan.id, message: `LLM extraction failed: ${msg}` };
90
+ }
91
+ // Reconcile tasks with DB
92
+ const result = reconcileTasks(db, plan.id, tasks);
93
+ const summary = [
94
+ isNew ? 'Created plan' : 'Updated plan',
95
+ `: ${title}`,
96
+ ` (${result.created.length} new,`,
97
+ ` ${result.updated.length} updated,`,
98
+ ` ${result.removed.length} removed,`,
99
+ ` ${result.unchanged} unchanged)`,
100
+ ].join('');
101
+ if (result.created.length > 0 || result.updated.length > 0 || result.removed.length > 0) {
102
+ emit(db, {
103
+ type: 'plan:sync',
104
+ content: summary,
105
+ plan_id: plan.id,
106
+ });
107
+ }
108
+ return {
109
+ success: true,
110
+ planId: plan.id,
111
+ message: summary,
112
+ };
113
+ }
114
+ /**
115
+ * Initial sync - scan existing plan files
116
+ */
117
+ export async function initialPlanSync(project) {
118
+ if (!existsSync(CLAUDE_PLANS_DIR)) {
119
+ return;
120
+ }
121
+ const files = readdirSync(CLAUDE_PLANS_DIR).filter((f) => f.endsWith('.md'));
122
+ for (const file of files) {
123
+ try {
124
+ const result = await syncPlanFile(project, file);
125
+ if (result.success && result.message !== 'Debounced') {
126
+ console.log(`[plan-watcher] ${result.message}`);
127
+ }
128
+ }
129
+ catch (err) {
130
+ console.error(`[plan-watcher] Error syncing ${file}:`, err);
131
+ }
132
+ }
133
+ }
134
+ /**
135
+ * Start watching the plans directory
136
+ */
137
+ export function startPlanWatcher(options) {
138
+ const { project, verbose = false } = options;
139
+ if (!existsSync(CLAUDE_PLANS_DIR)) {
140
+ console.log(`[plan-watcher] Plans directory not found: ${CLAUDE_PLANS_DIR}`);
141
+ return { stop: () => { } };
142
+ }
143
+ console.log(`[plan-watcher] Watching ${CLAUDE_PLANS_DIR}`);
144
+ // Initial sync (async, don't block)
145
+ initialPlanSync(project).catch((err) => {
146
+ console.error('[plan-watcher] Initial sync error:', err);
147
+ });
148
+ // Start watching
149
+ const watcher = watch(CLAUDE_PLANS_DIR, (event, filename) => {
150
+ if (!filename || !filename.endsWith('.md'))
151
+ return;
152
+ // Small delay for file to finish writing, then async sync
153
+ setTimeout(async () => {
154
+ try {
155
+ const result = await syncPlanFile(project, filename);
156
+ if (verbose || (result.success && result.message !== 'Debounced')) {
157
+ console.log(`[plan-watcher] ${result.message}`);
158
+ }
159
+ }
160
+ catch (err) {
161
+ console.error(`[plan-watcher] Error syncing ${filename}:`, err);
162
+ }
163
+ }, 500); // Longer delay for LLM call
164
+ });
165
+ return {
166
+ stop: () => {
167
+ watcher.close();
168
+ console.log('[plan-watcher] Stopped');
169
+ },
170
+ };
171
+ }
@@ -0,0 +1,6 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ import type { WorktreeRecord } from './types';
3
+ /**
4
+ * Get all registered worktrees
5
+ */
6
+ export declare function getAllWorktrees(db: Database): WorktreeRecord[];
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Get all registered worktrees
3
+ */
4
+ export function getAllWorktrees(db) {
5
+ const stmt = db.prepare(`
6
+ SELECT * FROM worktrees
7
+ ORDER BY is_main DESC, last_seen DESC
8
+ `);
9
+ return stmt.all();
10
+ }
@@ -0,0 +1,6 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ import type { WorktreeRecord } from './types';
3
+ /**
4
+ * Get a worktree by ID
5
+ */
6
+ export declare function getWorktreeById(db: Database, id: string): WorktreeRecord | null;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Get a worktree by ID
3
+ */
4
+ export function getWorktreeById(db, id) {
5
+ const stmt = db.prepare('SELECT * FROM worktrees WHERE id = ?');
6
+ return stmt.get(id) ?? null;
7
+ }
@@ -0,0 +1,6 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ import type { WorktreeRecord } from './types';
3
+ /**
4
+ * Get a worktree by its filesystem path
5
+ */
6
+ export declare function getWorktreeByPath(db: Database, path: string): WorktreeRecord | null;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Get a worktree by its filesystem path
3
+ */
4
+ export function getWorktreeByPath(db, path) {
5
+ const stmt = db.prepare('SELECT * FROM worktrees WHERE path = ?');
6
+ return stmt.get(path) ?? null;
7
+ }
@@ -0,0 +1,9 @@
1
+ export type { WorktreeStatus, WorktreeRecord, RegisterWorktreeInput } from './types';
2
+ export { registerWorktree } from './registerWorktree';
3
+ export { getWorktreeById } from './getWorktreeById';
4
+ export { getWorktreeByPath } from './getWorktreeByPath';
5
+ export { getAllWorktrees } from './getAllWorktrees';
6
+ export { updateWorktreeSeen } from './updateWorktreeSeen';
7
+ export { updateWorktreeCommit } from './updateWorktreeCommit';
8
+ export { removeWorktree } from './removeWorktree';
9
+ export { syncWorktreesFromGit } from './syncWorktreesFromGit';
@@ -0,0 +1,13 @@
1
+ // Registration
2
+ export { registerWorktree } from './registerWorktree';
3
+ // Query
4
+ export { getWorktreeById } from './getWorktreeById';
5
+ export { getWorktreeByPath } from './getWorktreeByPath';
6
+ export { getAllWorktrees } from './getAllWorktrees';
7
+ // Updates
8
+ export { updateWorktreeSeen } from './updateWorktreeSeen';
9
+ export { updateWorktreeCommit } from './updateWorktreeCommit';
10
+ // Removal
11
+ export { removeWorktree } from './removeWorktree';
12
+ // Sync
13
+ export { syncWorktreesFromGit } from './syncWorktreesFromGit';
@@ -0,0 +1,6 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ import type { WorktreeRecord, RegisterWorktreeInput } from './types';
3
+ /**
4
+ * Register a worktree in the hivemind
5
+ */
6
+ export declare function registerWorktree(db: Database, input: RegisterWorktreeInput): WorktreeRecord;
@@ -0,0 +1,28 @@
1
+ import { makeWorktreeId } from '../ids/makeWorktreeId';
2
+ import { parseId } from '../ids/parseId';
3
+ import { now } from '../datetime/now';
4
+ /**
5
+ * Register a worktree in the hivemind
6
+ */
7
+ export function registerWorktree(db, input) {
8
+ const id = makeWorktreeId(input.label);
9
+ const parsed = parseId(id);
10
+ const timestamp = now();
11
+ const stmt = db.prepare(`
12
+ INSERT INTO worktrees (id, hex, label, path, branch, commit_hash, is_main, status, created_at, last_seen)
13
+ VALUES (?, ?, ?, ?, ?, ?, ?, 'active', ?, ?)
14
+ `);
15
+ stmt.run(id, parsed.hex, parsed.label ?? null, input.path, input.branch ?? null, input.commit_hash ?? null, input.is_main ? 1 : 0, timestamp, timestamp);
16
+ return {
17
+ id,
18
+ hex: parsed.hex,
19
+ label: parsed.label ?? null,
20
+ path: input.path,
21
+ branch: input.branch ?? null,
22
+ commit_hash: input.commit_hash ?? null,
23
+ is_main: input.is_main ? 1 : 0,
24
+ status: 'active',
25
+ created_at: timestamp,
26
+ last_seen: timestamp,
27
+ };
28
+ }
@@ -0,0 +1,6 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ /**
3
+ * Remove a worktree from the database
4
+ * Does NOT delete the actual git worktree - just the DB record
5
+ */
6
+ export declare function removeWorktree(db: Database, worktreeId: string): boolean;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Remove a worktree from the database
3
+ * Does NOT delete the actual git worktree - just the DB record
4
+ */
5
+ export function removeWorktree(db, worktreeId) {
6
+ const stmt = db.prepare('DELETE FROM worktrees WHERE id = ?');
7
+ const result = stmt.run(worktreeId);
8
+ return result.changes > 0;
9
+ }
@@ -0,0 +1,7 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ import type { WorktreeRecord } from './types';
3
+ /**
4
+ * Sync worktrees from git into the database
5
+ * Returns list of all worktrees after sync
6
+ */
7
+ export declare function syncWorktreesFromGit(db: Database): WorktreeRecord[];
@@ -0,0 +1,51 @@
1
+ import { getWorktrees as getGitWorktrees } from '../git/getWorktrees';
2
+ import { getWorktreeByPath } from './getWorktreeByPath';
3
+ import { registerWorktree } from './registerWorktree';
4
+ import { updateWorktreeSeen } from './updateWorktreeSeen';
5
+ import { updateWorktreeCommit } from './updateWorktreeCommit';
6
+ import { emit } from '../events/emit';
7
+ /**
8
+ * Sync worktrees from git into the database
9
+ * Returns list of all worktrees after sync
10
+ */
11
+ export function syncWorktreesFromGit(db) {
12
+ const gitWorktrees = getGitWorktrees();
13
+ const results = [];
14
+ for (const wt of gitWorktrees) {
15
+ const existing = getWorktreeByPath(db, wt.path);
16
+ if (existing) {
17
+ // Update existing worktree
18
+ updateWorktreeSeen(db, existing.id);
19
+ if (wt.commit && wt.commit !== existing.commit_hash) {
20
+ updateWorktreeCommit(db, existing.id, wt.commit);
21
+ }
22
+ results.push({
23
+ ...existing,
24
+ commit_hash: wt.commit ?? existing.commit_hash,
25
+ });
26
+ }
27
+ else {
28
+ // Register new worktree
29
+ const record = registerWorktree(db, {
30
+ path: wt.path,
31
+ branch: wt.branch ?? undefined,
32
+ commit_hash: wt.commit,
33
+ is_main: wt.isMain,
34
+ });
35
+ // Emit worktree:register system event
36
+ emit(db, {
37
+ type: 'worktree:register',
38
+ worktree_id: record.id,
39
+ branch: wt.branch ?? undefined,
40
+ content: `Worktree registered: ${wt.path}${wt.branch ? ` (${wt.branch})` : ''}`,
41
+ metadata: {
42
+ path: wt.path,
43
+ commit_hash: wt.commit,
44
+ is_main: wt.isMain,
45
+ },
46
+ });
47
+ results.push(record);
48
+ }
49
+ }
50
+ return results;
51
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Worktree status values
3
+ */
4
+ export type WorktreeStatus = 'active' | 'stale';
5
+ /**
6
+ * Worktree record as stored in DB
7
+ */
8
+ export type WorktreeRecord = {
9
+ id: string;
10
+ hex: string;
11
+ label: string | null;
12
+ path: string;
13
+ branch: string | null;
14
+ commit_hash: string | null;
15
+ is_main: number;
16
+ status: WorktreeStatus;
17
+ created_at: string;
18
+ last_seen: string | null;
19
+ };
20
+ /**
21
+ * Input for registering a worktree
22
+ */
23
+ export type RegisterWorktreeInput = {
24
+ path: string;
25
+ branch?: string;
26
+ commit_hash?: string;
27
+ is_main?: boolean;
28
+ label?: string;
29
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ /**
3
+ * Update a worktree's current commit
4
+ */
5
+ export declare function updateWorktreeCommit(db: Database, id: string, commitHash: string): boolean;
@@ -0,0 +1,13 @@
1
+ import { now } from '../datetime/now';
2
+ /**
3
+ * Update a worktree's current commit
4
+ */
5
+ export function updateWorktreeCommit(db, id, commitHash) {
6
+ const stmt = db.prepare(`
7
+ UPDATE worktrees
8
+ SET commit_hash = ?, last_seen = ?
9
+ WHERE id = ?
10
+ `);
11
+ const result = stmt.run(commitHash, now(), id);
12
+ return result.changes > 0;
13
+ }
@@ -0,0 +1,5 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ /**
3
+ * Update a worktree's last_seen timestamp
4
+ */
5
+ export declare function updateWorktreeSeen(db: Database, id: string): boolean;
@@ -0,0 +1,13 @@
1
+ import { now } from '../datetime/now';
2
+ /**
3
+ * Update a worktree's last_seen timestamp
4
+ */
5
+ export function updateWorktreeSeen(db, id) {
6
+ const stmt = db.prepare(`
7
+ UPDATE worktrees
8
+ SET last_seen = ?, status = 'active'
9
+ WHERE id = ?
10
+ `);
11
+ const result = stmt.run(now(), id);
12
+ return result.changes > 0;
13
+ }
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@inixiative/hivemind",
3
+ "version": "0.1.0",
4
+ "description": "Multi-agent coordination system for Claude Code - shared event log, plans, and tasks across multiple Claude sessions",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "hivemind": "./dist/cli.js",
9
+ "hivemind-mcp": "./dist/mcp/server.js"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/inixiative/hivemind.git"
14
+ },
15
+ "homepage": "https://github.com/inixiative/hivemind#readme",
16
+ "bugs": {
17
+ "url": "https://github.com/inixiative/hivemind/issues"
18
+ },
19
+ "license": "MIT",
20
+ "author": {
21
+ "name": "Aron Greenspan",
22
+ "url": "https://github.com/inixiative"
23
+ },
24
+ "keywords": [
25
+ "mcp",
26
+ "model-context-protocol",
27
+ "claude",
28
+ "claude-code",
29
+ "multi-agent",
30
+ "coordination",
31
+ "ai-agents"
32
+ ],
33
+ "files": [
34
+ "dist",
35
+ "README.md",
36
+ "LICENSE"
37
+ ],
38
+ "engines": {
39
+ "node": ">=18.0.0"
40
+ },
41
+ "scripts": {
42
+ "build": "tsc",
43
+ "dev": "tsc --watch",
44
+ "test": "bun test",
45
+ "mcp": "bun run src/mcp/server.ts",
46
+ "cli": "bun run src/cli.ts"
47
+ },
48
+ "dependencies": {
49
+ "@modelcontextprotocol/sdk": "^1.0.0",
50
+ "better-sqlite3": "^11.0.0",
51
+ "commander": "^12.0.0"
52
+ },
53
+ "devDependencies": {
54
+ "@types/better-sqlite3": "^7.6.9",
55
+ "@types/bun": "latest",
56
+ "typescript": "^5.3.0"
57
+ }
58
+ }