@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,12 @@
1
+ /**
2
+ * Sanitize a label for use in IDs
3
+ * - lowercase
4
+ * - remove special chars (keep alphanumeric only)
5
+ * - max 20 chars
6
+ */
7
+ export function sanitizeLabel(label) {
8
+ return label
9
+ .toLowerCase()
10
+ .replace(/[^a-z0-9]/g, '')
11
+ .slice(0, 20);
12
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Typed ID utility for compile-time type safety
3
+ *
4
+ * Creates a "typed" string by adding a phantom type property.
5
+ * The __model property only exists at compile-time and has zero runtime cost.
6
+ */
7
+ export type Id<Model> = string & {
8
+ readonly __model: Model;
9
+ };
10
+ export declare const HivemindIdType: {
11
+ readonly Agent: "AgentId";
12
+ readonly Plan: "PlanId";
13
+ readonly Task: "TaskId";
14
+ readonly Event: "EventId";
15
+ readonly Worktree: "WorktreeId";
16
+ };
17
+ export type HivemindIdType = (typeof HivemindIdType)[keyof typeof HivemindIdType];
18
+ export type AgentId = Id<typeof HivemindIdType.Agent>;
19
+ export type PlanId = Id<typeof HivemindIdType.Plan>;
20
+ export type TaskId = Id<typeof HivemindIdType.Task>;
21
+ export type EventId = Id<typeof HivemindIdType.Event>;
22
+ export type WorktreeId = Id<typeof HivemindIdType.Worktree>;
23
+ export declare const createId: <M extends HivemindIdType>(raw: string) => Id<M>;
24
+ export declare const agentId: (id: string) => AgentId;
25
+ export declare const planId: (id: string) => PlanId;
26
+ export declare const taskId: (id: string) => TaskId;
27
+ export declare const eventId: (id: string) => EventId;
28
+ export declare const worktreeId: (id: string) => WorktreeId;
29
+ export declare const isAgentId: (id: string) => id is AgentId;
30
+ export declare const isPlanId: (id: string) => id is PlanId;
31
+ export declare const isTaskId: (id: string) => id is TaskId;
32
+ export declare const isEventId: (id: string) => id is EventId;
33
+ export declare const isWorktreeId: (id: string) => id is WorktreeId;
34
+ export type ExtractIdType<T> = T extends AgentId ? typeof HivemindIdType.Agent : T extends PlanId ? typeof HivemindIdType.Plan : T extends TaskId ? typeof HivemindIdType.Task : T extends EventId ? typeof HivemindIdType.Event : T extends WorktreeId ? typeof HivemindIdType.Worktree : never;
@@ -0,0 +1,22 @@
1
+ // Define all hivemind entity ID types
2
+ export const HivemindIdType = {
3
+ Agent: 'AgentId',
4
+ Plan: 'PlanId',
5
+ Task: 'TaskId',
6
+ Event: 'EventId',
7
+ Worktree: 'WorktreeId',
8
+ };
9
+ // Core helper functions
10
+ export const createId = (raw) => raw;
11
+ // Convenience creator functions (for casting existing strings)
12
+ export const agentId = (id) => id;
13
+ export const planId = (id) => id;
14
+ export const taskId = (id) => id;
15
+ export const eventId = (id) => id;
16
+ export const worktreeId = (id) => id;
17
+ // Type guards for runtime validation
18
+ export const isAgentId = (id) => id.startsWith('agt_');
19
+ export const isPlanId = (id) => id.startsWith('pln_');
20
+ export const isTaskId = (id) => id.startsWith('tsk_');
21
+ export const isEventId = (id) => id.startsWith('evt_');
22
+ export const isWorktreeId = (id) => id.startsWith('wkt_');
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Valid ID type prefixes
3
+ */
4
+ export type IdType = 'agt' | 'pln' | 'tsk' | 'evt';
5
+ /**
6
+ * Parsed ID components
7
+ */
8
+ export type ParsedId = {
9
+ type: IdType;
10
+ hex: string;
11
+ seq?: string;
12
+ label?: string;
13
+ raw: string;
14
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Claude Code configuration for hivemind
3
+ *
4
+ * When `hivemind init` runs, it sets up:
5
+ * 1. .claude/settings.json - SessionStart hook for auto-registration
6
+ * 2. .claude/CLAUDE.md - Behavioral instructions for agents
7
+ *
8
+ * Projects can customize agent behavior by adding a `## Hivemind Events`
9
+ * section to their root CLAUDE.md - it gets extracted and injected.
10
+ */
11
+ /**
12
+ * Extract a markdown section by heading from content
13
+ * Returns the section content (including heading) or null if not found
14
+ */
15
+ export declare function extractMarkdownSection(content: string, heading: string): string | null;
16
+ /**
17
+ * Build the CLAUDE.md content, optionally with custom events section
18
+ */
19
+ export declare function buildHivemindClaudeMd(customEventsSection?: string): string;
20
+ /**
21
+ * Settings.json hooks for auto-registration
22
+ */
23
+ export declare function getHivemindHooks(hivemindRoot: string): {
24
+ hooks: {
25
+ SessionStart: {
26
+ hooks: {
27
+ type: string;
28
+ command: string;
29
+ }[];
30
+ }[];
31
+ };
32
+ };
33
+ /**
34
+ * Initialize Claude Code configuration for hivemind
35
+ */
36
+ export declare function initClaudeConfig(projectRoot: string, _project: string, hivemindRoot: string): {
37
+ created: string[];
38
+ updated: string[];
39
+ };
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Claude Code configuration for hivemind
3
+ *
4
+ * When `hivemind init` runs, it sets up:
5
+ * 1. .claude/settings.json - SessionStart hook for auto-registration
6
+ * 2. .claude/CLAUDE.md - Behavioral instructions for agents
7
+ *
8
+ * Projects can customize agent behavior by adding a `## Hivemind Events`
9
+ * section to their root CLAUDE.md - it gets extracted and injected.
10
+ */
11
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
12
+ import { join } from 'path';
13
+ /**
14
+ * Default events instructions (used if project doesn't define custom ones)
15
+ */
16
+ const DEFAULT_EVENTS_SECTION = `## Hivemind Events
17
+
18
+ Emit concise events to coordinate. **Under 80 chars.** Map, not territory.
19
+
20
+ \`\`\`
21
+ hivemind_emit type=decision content="Redis for cache, Postgres for persistence"
22
+ hivemind_emit type=context content="API rate limit: 100/min per key"
23
+ hivemind_emit type=note content="003 done, starting 004"
24
+ \`\`\`
25
+
26
+ **Emit for:** architectural decisions, discoveries others need, blockers, questions.
27
+
28
+ **Skip:** routine progress, obvious steps, anything verbose.`;
29
+ /**
30
+ * Extract a markdown section by heading from content
31
+ * Returns the section content (including heading) or null if not found
32
+ */
33
+ export function extractMarkdownSection(content, heading) {
34
+ // Match ## Heading through end of section (next ## or EOF)
35
+ // Split into lines and find section boundaries
36
+ const lines = content.split('\n');
37
+ const startPattern = new RegExp(`^## ${heading}\\s*$`, 'i');
38
+ let startIdx = -1;
39
+ let endIdx = lines.length;
40
+ for (let i = 0; i < lines.length; i++) {
41
+ if (startIdx === -1 && startPattern.test(lines[i])) {
42
+ startIdx = i;
43
+ }
44
+ else if (startIdx !== -1 && /^## /.test(lines[i])) {
45
+ endIdx = i;
46
+ break;
47
+ }
48
+ }
49
+ if (startIdx === -1)
50
+ return null;
51
+ return lines.slice(startIdx, endIdx).join('\n').trim();
52
+ }
53
+ /**
54
+ * Build the CLAUDE.md content, optionally with custom events section
55
+ */
56
+ export function buildHivemindClaudeMd(customEventsSection) {
57
+ const eventsSection = customEventsSection || DEFAULT_EVENTS_SECTION;
58
+ return `# Hivemind Agent Instructions
59
+
60
+ You are part of a **hivemind** - a coordinated group of Claude agents working together.
61
+
62
+ ## Automatic Behaviors
63
+
64
+ **Your todos are automatically synced to hivemind.** When you use TodoWrite, the hivemind watcher detects changes and syncs them to a shared plan. Other agents can see your progress in real-time.
65
+
66
+ Your lifecycle is tracked by PID - no heartbeats needed. When you exit, hivemind automatically marks you as inactive.
67
+
68
+ ## MCP Tools
69
+
70
+ - \`hivemind_status\` - View active agents, plans, and recent activity
71
+ - \`hivemind_query\` - See what other agents are doing
72
+ - \`hivemind_emit\` - Share notes, decisions, questions with other agents
73
+
74
+ ${eventsSection}
75
+
76
+ ## Coordination Protocol
77
+
78
+ 1. **Before starting work**: Check \`hivemind_status\` to see what others are doing
79
+ 2. **When making decisions**: Use \`hivemind_emit\` with type "decision" to share
80
+ 3. **When blocked**: Use \`hivemind_emit\` with type "question" to ask for help
81
+ 4. **Just work**: Your todos sync automatically - focus on the task!
82
+ `;
83
+ }
84
+ /**
85
+ * Settings.json hooks for auto-registration
86
+ */
87
+ export function getHivemindHooks(hivemindRoot) {
88
+ return {
89
+ hooks: {
90
+ SessionStart: [
91
+ {
92
+ hooks: [
93
+ {
94
+ type: 'command',
95
+ command: `bun run ${hivemindRoot}/src/hooks/sessionStart.ts`,
96
+ },
97
+ ],
98
+ },
99
+ ],
100
+ },
101
+ };
102
+ }
103
+ /**
104
+ * Initialize Claude Code configuration for hivemind
105
+ */
106
+ export function initClaudeConfig(projectRoot, _project, hivemindRoot) {
107
+ const claudeDir = join(projectRoot, '.claude');
108
+ const settingsPath = join(claudeDir, 'settings.json');
109
+ const claudeMdPath = join(claudeDir, 'CLAUDE.md');
110
+ const projectClaudeMdPath = join(projectRoot, 'CLAUDE.md');
111
+ const created = [];
112
+ const updated = [];
113
+ // Ensure .claude directory exists
114
+ if (!existsSync(claudeDir)) {
115
+ mkdirSync(claudeDir, { recursive: true });
116
+ created.push('.claude/');
117
+ }
118
+ // Create or update settings.json
119
+ let settings = {};
120
+ if (existsSync(settingsPath)) {
121
+ try {
122
+ settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
123
+ }
124
+ catch {
125
+ // Invalid JSON, start fresh
126
+ }
127
+ }
128
+ const hooks = getHivemindHooks(hivemindRoot);
129
+ const existingHooks = settings.hooks;
130
+ if (!existingHooks?.SessionStart) {
131
+ settings.hooks = { ...existingHooks, ...hooks.hooks };
132
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
133
+ updated.push('.claude/settings.json');
134
+ }
135
+ // Extract custom events section from project's root CLAUDE.md (if exists)
136
+ let customEventsSection;
137
+ if (existsSync(projectClaudeMdPath)) {
138
+ const projectClaudeMd = readFileSync(projectClaudeMdPath, 'utf-8');
139
+ const extracted = extractMarkdownSection(projectClaudeMd, 'Hivemind Events');
140
+ if (extracted) {
141
+ customEventsSection = extracted;
142
+ }
143
+ }
144
+ // Build the hivemind instructions (with custom or default events section)
145
+ const hivemindClaudeMd = buildHivemindClaudeMd(customEventsSection);
146
+ // Create or update .claude/CLAUDE.md
147
+ if (!existsSync(claudeMdPath)) {
148
+ writeFileSync(claudeMdPath, hivemindClaudeMd);
149
+ created.push('.claude/CLAUDE.md');
150
+ }
151
+ else {
152
+ // Check if hivemind instructions are already there
153
+ const existing = readFileSync(claudeMdPath, 'utf-8');
154
+ if (!existing.includes('Hivemind Agent Instructions')) {
155
+ // Append hivemind instructions
156
+ writeFileSync(claudeMdPath, existing + '\n\n' + hivemindClaudeMd);
157
+ updated.push('.claude/CLAUDE.md');
158
+ }
159
+ }
160
+ return { created, updated };
161
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Task extraction from plan markdown
3
+ *
4
+ * Two modes:
5
+ * 1. Fast regex parsing (default) - handles common formats instantly
6
+ * 2. LLM extraction (optional) - for complex plans, user-triggered
7
+ */
8
+ export type ExtractedTask = {
9
+ title: string;
10
+ description?: string;
11
+ status: 'pending' | 'in_progress' | 'done';
12
+ };
13
+ /**
14
+ * Fast regex-based task extraction
15
+ * Handles common markdown patterns:
16
+ * - [ ] pending task
17
+ * - [x] completed task
18
+ * - [ ] ~in progress~ (strikethrough = in progress)
19
+ * ### 1. Numbered section headers
20
+ * 1. Numbered list items
21
+ */
22
+ export declare function extractTasksWithRegex(markdown: string): ExtractedTask[];
23
+ /**
24
+ * LLM-based task extraction (slower, more accurate for complex plans)
25
+ * Use extractTasksWithRegex() for real-time sync
26
+ */
27
+ export declare function extractTasksWithLLM(markdown: string): Promise<ExtractedTask[]>;
28
+ export declare const extractTasksFromPlan: typeof extractTasksWithRegex;
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Task extraction from plan markdown
3
+ *
4
+ * Two modes:
5
+ * 1. Fast regex parsing (default) - handles common formats instantly
6
+ * 2. LLM extraction (optional) - for complex plans, user-triggered
7
+ */
8
+ import { spawn } from 'child_process';
9
+ /**
10
+ * Fast regex-based task extraction
11
+ * Handles common markdown patterns:
12
+ * - [ ] pending task
13
+ * - [x] completed task
14
+ * - [ ] ~in progress~ (strikethrough = in progress)
15
+ * ### 1. Numbered section headers
16
+ * 1. Numbered list items
17
+ */
18
+ export function extractTasksWithRegex(markdown) {
19
+ const tasks = [];
20
+ const lines = markdown.split('\n');
21
+ for (const line of lines) {
22
+ // Checkbox tasks: - [ ] task or - [x] task
23
+ const checkboxMatch = line.match(/^[\s]*[-*]\s*\[([ xX])\]\s*(.+)$/);
24
+ if (checkboxMatch) {
25
+ const isDone = checkboxMatch[1].toLowerCase() === 'x';
26
+ const title = checkboxMatch[2].trim();
27
+ tasks.push({
28
+ title: title.slice(0, 100),
29
+ status: isDone ? 'done' : 'pending',
30
+ });
31
+ continue;
32
+ }
33
+ // Numbered section headers: ### 1. Task name or ## 1. Task name
34
+ const sectionMatch = line.match(/^#{2,4}\s*\d+\.\s*(.+)$/);
35
+ if (sectionMatch) {
36
+ const title = sectionMatch[1].trim();
37
+ // Skip if it looks like a file path or code
38
+ if (!title.includes('/') && !title.includes('`')) {
39
+ tasks.push({
40
+ title: title.slice(0, 100),
41
+ status: 'pending',
42
+ });
43
+ }
44
+ continue;
45
+ }
46
+ // Top-level numbered list: 1. Task item (only at start of line)
47
+ const numberedMatch = line.match(/^\d+\.\s+([A-Z][^.]+)$/);
48
+ if (numberedMatch) {
49
+ const title = numberedMatch[1].trim();
50
+ tasks.push({
51
+ title: title.slice(0, 100),
52
+ status: 'pending',
53
+ });
54
+ continue;
55
+ }
56
+ }
57
+ return tasks;
58
+ }
59
+ /**
60
+ * LLM-based task extraction (slower, more accurate for complex plans)
61
+ * Use extractTasksWithRegex() for real-time sync
62
+ */
63
+ export async function extractTasksWithLLM(markdown) {
64
+ const MAX_LENGTH = 4000;
65
+ const truncated = markdown.length > MAX_LENGTH
66
+ ? markdown.slice(0, MAX_LENGTH) + '\n\n[... truncated]'
67
+ : markdown;
68
+ const prompt = `I'm building a multi-agent coordination system. Extract implementation tasks from this plan as JSON array:
69
+ [{"title": "Task name", "status": "pending"}]
70
+
71
+ Plan:
72
+ ${truncated}`;
73
+ return new Promise((resolve, reject) => {
74
+ const proc = spawn('claude', ['--print', '--output-format', 'text', prompt], {
75
+ stdio: ['pipe', 'pipe', 'pipe'],
76
+ });
77
+ let stdout = '';
78
+ let stderr = '';
79
+ proc.stdout.on('data', (d) => { stdout += d.toString(); });
80
+ proc.stderr.on('data', (d) => { stderr += d.toString(); });
81
+ proc.on('error', (err) => reject(new Error(`spawn failed: ${err.message}`)));
82
+ proc.on('close', (code) => {
83
+ if (code !== 0) {
84
+ reject(new Error(`claude exited ${code}: ${stderr || stdout}`));
85
+ return;
86
+ }
87
+ try {
88
+ const match = stdout.match(/```(?:json)?\s*([\s\S]*?)```/) || stdout.match(/\[[\s\S]*\]/);
89
+ const jsonStr = match ? (match[1] || match[0]).trim() : null;
90
+ if (!jsonStr) {
91
+ resolve([]);
92
+ return;
93
+ }
94
+ const tasks = JSON.parse(jsonStr);
95
+ resolve(tasks.map((t) => ({
96
+ title: String(t.title || '').slice(0, 100),
97
+ description: t.description,
98
+ status: ['pending', 'in_progress', 'done'].includes(t.status) ? t.status : 'pending',
99
+ })).filter((t) => t.title));
100
+ }
101
+ catch (err) {
102
+ reject(new Error(`JSON parse failed: ${err}`));
103
+ }
104
+ });
105
+ });
106
+ }
107
+ // Default export uses fast regex
108
+ export const extractTasksFromPlan = extractTasksWithRegex;
@@ -0,0 +1,2 @@
1
+ export { extractTasksFromPlan, type ExtractedTask } from './extractTasks';
2
+ export { reconcileTasks, type ReconcileResult } from './reconcileTasks';
@@ -0,0 +1,2 @@
1
+ export { extractTasksFromPlan } from './extractTasks';
2
+ export { reconcileTasks } from './reconcileTasks';
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Reconcile extracted tasks with database
3
+ * Plan file is source of truth - DB reflects what's in the file
4
+ */
5
+ import type { Database } from 'bun:sqlite';
6
+ import type { Task } from '../tasks/types';
7
+ import type { ExtractedTask } from './extractTasks';
8
+ export type ReconcileResult = {
9
+ created: Task[];
10
+ updated: Task[];
11
+ removed: Task[];
12
+ unchanged: number;
13
+ };
14
+ /**
15
+ * Reconcile extracted tasks with existing tasks in a plan
16
+ *
17
+ * - Tasks in file but not DB → create
18
+ * - Tasks in DB but not file → mark done (removed)
19
+ * - Tasks in both → update status if changed
20
+ */
21
+ export declare function reconcileTasks(db: Database, planId: string, extracted: ExtractedTask[]): ReconcileResult;
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Reconcile extracted tasks with database
3
+ * Plan file is source of truth - DB reflects what's in the file
4
+ */
5
+ import { createTask } from '../tasks/createTask';
6
+ import { getTasksByPlan } from '../tasks/getTasksByPlan';
7
+ import { completeTask } from '../tasks/completeTask';
8
+ import { startTask } from '../tasks/startTask';
9
+ import { emit } from '../events/emit';
10
+ /**
11
+ * Reconcile extracted tasks with existing tasks in a plan
12
+ *
13
+ * - Tasks in file but not DB → create
14
+ * - Tasks in DB but not file → mark done (removed)
15
+ * - Tasks in both → update status if changed
16
+ */
17
+ export function reconcileTasks(db, planId, extracted) {
18
+ const existing = getTasksByPlan(db, planId);
19
+ const existingByTitle = new Map(existing.map((t) => [t.title.toLowerCase(), t]));
20
+ const extractedTitles = new Set(extracted.map((t) => t.title.toLowerCase()));
21
+ const created = [];
22
+ const updated = [];
23
+ const removed = [];
24
+ let unchanged = 0;
25
+ // Process extracted tasks
26
+ for (const ext of extracted) {
27
+ const titleKey = ext.title.toLowerCase();
28
+ const existingTask = existingByTitle.get(titleKey);
29
+ if (existingTask) {
30
+ // Task exists - check if status changed
31
+ const dbStatus = existingTask.status;
32
+ const extStatus = ext.status;
33
+ // Map extracted status to DB status
34
+ let needsUpdate = false;
35
+ if (extStatus === 'done' && dbStatus !== 'done') {
36
+ completeTask(db, existingTask.id);
37
+ updated.push({ ...existingTask, status: 'done' });
38
+ needsUpdate = true;
39
+ }
40
+ else if (extStatus === 'in_progress' && dbStatus !== 'in_progress') {
41
+ startTask(db, existingTask.id);
42
+ updated.push({ ...existingTask, status: 'in_progress' });
43
+ needsUpdate = true;
44
+ }
45
+ if (!needsUpdate) {
46
+ unchanged++;
47
+ }
48
+ }
49
+ else {
50
+ // New task - create it
51
+ const task = createTask(db, {
52
+ plan_id: planId,
53
+ title: ext.title,
54
+ description: ext.description,
55
+ });
56
+ // Emit task:create system event
57
+ emit(db, {
58
+ type: 'task:create',
59
+ task_id: task.id,
60
+ plan_id: planId,
61
+ content: `Task created: ${task.title}`,
62
+ });
63
+ // Set initial status if not pending
64
+ if (ext.status === 'in_progress') {
65
+ startTask(db, task.id);
66
+ }
67
+ else if (ext.status === 'done') {
68
+ completeTask(db, task.id);
69
+ }
70
+ created.push(task);
71
+ }
72
+ }
73
+ // Tasks in DB but not in file → mark as done (removed from plan)
74
+ for (const task of existing) {
75
+ const titleKey = task.title.toLowerCase();
76
+ if (!extractedTitles.has(titleKey) && task.status !== 'done') {
77
+ completeTask(db, task.id);
78
+ removed.push({ ...task, status: 'done' });
79
+ }
80
+ }
81
+ return { created, updated, removed, unchanged };
82
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
5
+ import { setupTool, executeSetup, registerTool, executeRegister, emitEventTool, executeEmitEvent, queryTool, executeQuery, statusTool, executeStatus, resetTool, executeReset, claimTaskTool, executeClaimTask, startTaskTool, executeStartTask, completeTaskTool, executeCompleteTask, eventsTool, executeEvents, worktreeCleanupTool, executeWorktreeCleanup, } from './tools/index';
6
+ const server = new Server({
7
+ name: 'hivemind',
8
+ version: '0.1.0',
9
+ }, {
10
+ capabilities: {
11
+ tools: {},
12
+ },
13
+ });
14
+ // List available tools
15
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
16
+ return {
17
+ tools: [
18
+ setupTool,
19
+ registerTool,
20
+ statusTool,
21
+ eventsTool,
22
+ emitEventTool,
23
+ queryTool,
24
+ claimTaskTool,
25
+ startTaskTool,
26
+ completeTaskTool,
27
+ worktreeCleanupTool,
28
+ resetTool,
29
+ ],
30
+ };
31
+ });
32
+ // Handle tool calls
33
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
34
+ const { name, arguments: args } = request.params;
35
+ try {
36
+ let result;
37
+ switch (name) {
38
+ case 'hivemind_setup':
39
+ result = executeSetup(args);
40
+ break;
41
+ case 'hivemind_register':
42
+ result = executeRegister(args);
43
+ break;
44
+ case 'hivemind_emit':
45
+ result = executeEmitEvent(args);
46
+ break;
47
+ case 'hivemind_query':
48
+ result = executeQuery(args);
49
+ break;
50
+ case 'hivemind_status':
51
+ result = executeStatus(args);
52
+ break;
53
+ case 'hivemind_reset':
54
+ result = executeReset(args);
55
+ break;
56
+ case 'hivemind_claim_task':
57
+ result = executeClaimTask(args);
58
+ break;
59
+ case 'hivemind_start_task':
60
+ result = executeStartTask(args);
61
+ break;
62
+ case 'hivemind_complete_task':
63
+ result = executeCompleteTask(args);
64
+ break;
65
+ case 'hivemind_events':
66
+ result = executeEvents(args);
67
+ break;
68
+ case 'hivemind_worktree_cleanup':
69
+ result = executeWorktreeCleanup(args);
70
+ break;
71
+ default:
72
+ throw new Error(`Unknown tool: ${name}`);
73
+ }
74
+ return {
75
+ content: [
76
+ {
77
+ type: 'text',
78
+ text: JSON.stringify(result, null, 2),
79
+ },
80
+ ],
81
+ };
82
+ }
83
+ catch (error) {
84
+ const message = error instanceof Error ? error.message : String(error);
85
+ return {
86
+ content: [
87
+ {
88
+ type: 'text',
89
+ text: JSON.stringify({ error: message }),
90
+ },
91
+ ],
92
+ isError: true,
93
+ };
94
+ }
95
+ });
96
+ async function main() {
97
+ const transport = new StdioServerTransport();
98
+ await server.connect(transport);
99
+ }
100
+ main().catch(console.error);