@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.
- package/LICENSE +21 -0
- package/README.md +115 -0
- package/dist/agents/agents.test.d.ts +1 -0
- package/dist/agents/agents.test.js +167 -0
- package/dist/agents/getActiveAgents.d.ts +6 -0
- package/dist/agents/getActiveAgents.js +11 -0
- package/dist/agents/getAgent.d.ts +6 -0
- package/dist/agents/getAgent.js +7 -0
- package/dist/agents/getAgentBySessionId.d.ts +10 -0
- package/dist/agents/getAgentBySessionId.js +17 -0
- package/dist/agents/index.d.ts +10 -0
- package/dist/agents/index.js +12 -0
- package/dist/agents/markAgentDead.d.ts +6 -0
- package/dist/agents/markAgentDead.js +26 -0
- package/dist/agents/markAgentIdle.d.ts +5 -0
- package/dist/agents/markAgentIdle.js +12 -0
- package/dist/agents/registerAgent.d.ts +6 -0
- package/dist/agents/registerAgent.js +29 -0
- package/dist/agents/types.d.ts +30 -0
- package/dist/agents/types.js +1 -0
- package/dist/agents/unregisterAgent.d.ts +5 -0
- package/dist/agents/unregisterAgent.js +8 -0
- package/dist/agents/updateAgentContext.d.ts +5 -0
- package/dist/agents/updateAgentContext.js +12 -0
- package/dist/agents/updateAgentTask.d.ts +5 -0
- package/dist/agents/updateAgentTask.js +12 -0
- package/dist/agents/updateAgentWorktree.d.ts +5 -0
- package/dist/agents/updateAgentWorktree.js +12 -0
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.js +8 -0
- package/dist/cli/init.d.ts +14 -0
- package/dist/cli/init.js +71 -0
- package/dist/cli/install.d.ts +8 -0
- package/dist/cli/install.js +47 -0
- package/dist/cli/join.d.ts +9 -0
- package/dist/cli/join.js +38 -0
- package/dist/cli/registerMcp.d.ts +28 -0
- package/dist/cli/registerMcp.js +138 -0
- package/dist/cli/status.d.ts +8 -0
- package/dist/cli/status.js +82 -0
- package/dist/cli/watch.d.ts +6 -0
- package/dist/cli/watch.js +68 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.js +49 -0
- package/dist/coordinator/coordinator.test.d.ts +1 -0
- package/dist/coordinator/coordinator.test.js +171 -0
- package/dist/coordinator/index.d.ts +16 -0
- package/dist/coordinator/index.js +166 -0
- package/dist/coordinator/spawn.d.ts +22 -0
- package/dist/coordinator/spawn.js +66 -0
- package/dist/datetime/datetime.test.d.ts +1 -0
- package/dist/datetime/datetime.test.js +63 -0
- package/dist/datetime/formatDate.d.ts +6 -0
- package/dist/datetime/formatDate.js +11 -0
- package/dist/datetime/formatDatetime.d.ts +6 -0
- package/dist/datetime/formatDatetime.js +12 -0
- package/dist/datetime/formatTime.d.ts +6 -0
- package/dist/datetime/formatTime.js +11 -0
- package/dist/datetime/index.d.ts +4 -0
- package/dist/datetime/index.js +7 -0
- package/dist/datetime/isStale.d.ts +10 -0
- package/dist/datetime/isStale.js +18 -0
- package/dist/datetime/now.d.ts +6 -0
- package/dist/datetime/now.js +9 -0
- package/dist/datetime/parseDatetime.d.ts +7 -0
- package/dist/datetime/parseDatetime.js +28 -0
- package/dist/db/constants.d.ts +4 -0
- package/dist/db/constants.js +6 -0
- package/dist/db/db.test.d.ts +1 -0
- package/dist/db/db.test.js +141 -0
- package/dist/db/ensureProjectDirs.d.ts +4 -0
- package/dist/db/ensureProjectDirs.js +12 -0
- package/dist/db/getConnection.d.ts +19 -0
- package/dist/db/getConnection.js +51 -0
- package/dist/db/getCurrentProject.d.ts +8 -0
- package/dist/db/getCurrentProject.js +14 -0
- package/dist/db/getProjectPaths.d.ts +21 -0
- package/dist/db/getProjectPaths.js +26 -0
- package/dist/db/index.d.ts +10 -0
- package/dist/db/index.js +13 -0
- package/dist/db/initializeDb.d.ts +7 -0
- package/dist/db/initializeDb.js +23 -0
- package/dist/db/nextEventSeq.d.ts +5 -0
- package/dist/db/nextEventSeq.js +13 -0
- package/dist/db/nextSubtaskSeq.d.ts +5 -0
- package/dist/db/nextSubtaskSeq.js +12 -0
- package/dist/db/nextTaskSeq.d.ts +5 -0
- package/dist/db/nextTaskSeq.js +13 -0
- package/dist/db/resetDb.d.ts +10 -0
- package/dist/db/resetDb.js +36 -0
- package/dist/events/emit.d.ts +6 -0
- package/dist/events/emit.js +31 -0
- package/dist/events/events.test.d.ts +1 -0
- package/dist/events/events.test.js +145 -0
- package/dist/events/getEventsByAgent.d.ts +6 -0
- package/dist/events/getEventsByAgent.js +14 -0
- package/dist/events/getEventsByBranch.d.ts +6 -0
- package/dist/events/getEventsByBranch.js +12 -0
- package/dist/events/getEventsByPlan.d.ts +6 -0
- package/dist/events/getEventsByPlan.js +14 -0
- package/dist/events/getEventsByWorktree.d.ts +6 -0
- package/dist/events/getEventsByWorktree.js +12 -0
- package/dist/events/getEventsSince.d.ts +12 -0
- package/dist/events/getEventsSince.js +47 -0
- package/dist/events/getRecentEvents.d.ts +6 -0
- package/dist/events/getRecentEvents.js +12 -0
- package/dist/events/index.d.ts +8 -0
- package/dist/events/index.js +9 -0
- package/dist/events/types.d.ts +34 -0
- package/dist/events/types.js +1 -0
- package/dist/git/getBranch.d.ts +4 -0
- package/dist/git/getBranch.js +14 -0
- package/dist/git/getCurrentWorktree.d.ts +5 -0
- package/dist/git/getCurrentWorktree.js +15 -0
- package/dist/git/getGitInfo.d.ts +10 -0
- package/dist/git/getGitInfo.js +23 -0
- package/dist/git/getRepoName.d.ts +4 -0
- package/dist/git/getRepoName.js +32 -0
- package/dist/git/getRepoRoot.d.ts +4 -0
- package/dist/git/getRepoRoot.js +14 -0
- package/dist/git/getWorktrees.d.ts +10 -0
- package/dist/git/getWorktrees.js +39 -0
- package/dist/git/index.d.ts +9 -0
- package/dist/git/index.js +7 -0
- package/dist/git/isGitRepo.d.ts +4 -0
- package/dist/git/isGitRepo.js +13 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/sessionStart.d.ts +21 -0
- package/dist/hooks/sessionStart.js +93 -0
- package/dist/ids/generateHex.d.ts +4 -0
- package/dist/ids/generateHex.js +7 -0
- package/dist/ids/getParentTaskId.d.ts +7 -0
- package/dist/ids/getParentTaskId.js +15 -0
- package/dist/ids/getPlanHexFromTaskId.d.ts +6 -0
- package/dist/ids/getPlanHexFromTaskId.js +9 -0
- package/dist/ids/ids.test.d.ts +1 -0
- package/dist/ids/ids.test.js +215 -0
- package/dist/ids/index.d.ts +16 -0
- package/dist/ids/index.js +17 -0
- package/dist/ids/isSubtask.d.ts +7 -0
- package/dist/ids/isSubtask.js +11 -0
- package/dist/ids/isValidId.d.ts +9 -0
- package/dist/ids/isValidId.js +22 -0
- package/dist/ids/makeAgentId.d.ts +8 -0
- package/dist/ids/makeAgentId.js +15 -0
- package/dist/ids/makeEventId.d.ts +11 -0
- package/dist/ids/makeEventId.js +12 -0
- package/dist/ids/makePlanId.d.ts +11 -0
- package/dist/ids/makePlanId.js +15 -0
- package/dist/ids/makeSubtaskId.d.ts +8 -0
- package/dist/ids/makeSubtaskId.js +15 -0
- package/dist/ids/makeTaskId.d.ts +8 -0
- package/dist/ids/makeTaskId.js +14 -0
- package/dist/ids/makeWorktreeId.d.ts +5 -0
- package/dist/ids/makeWorktreeId.js +12 -0
- package/dist/ids/parseId.d.ts +11 -0
- package/dist/ids/parseId.js +26 -0
- package/dist/ids/sanitizeLabel.d.ts +7 -0
- package/dist/ids/sanitizeLabel.js +12 -0
- package/dist/ids/typedIds.d.ts +34 -0
- package/dist/ids/typedIds.js +22 -0
- package/dist/ids/types.d.ts +14 -0
- package/dist/ids/types.js +1 -0
- package/dist/init/claudeConfig.d.ts +39 -0
- package/dist/init/claudeConfig.js +161 -0
- package/dist/llm/extractTasks.d.ts +28 -0
- package/dist/llm/extractTasks.js +108 -0
- package/dist/llm/index.d.ts +2 -0
- package/dist/llm/index.js +2 -0
- package/dist/llm/reconcileTasks.d.ts +21 -0
- package/dist/llm/reconcileTasks.js +82 -0
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.js +100 -0
- package/dist/mcp/tools/emitEvent.d.ts +62 -0
- package/dist/mcp/tools/emitEvent.js +84 -0
- package/dist/mcp/tools/events.d.ts +55 -0
- package/dist/mcp/tools/events.js +56 -0
- package/dist/mcp/tools/index.d.ts +18 -0
- package/dist/mcp/tools/index.js +13 -0
- package/dist/mcp/tools/query.d.ts +54 -0
- package/dist/mcp/tools/query.js +70 -0
- package/dist/mcp/tools/register.d.ts +47 -0
- package/dist/mcp/tools/register.js +79 -0
- package/dist/mcp/tools/reset.d.ts +38 -0
- package/dist/mcp/tools/reset.js +56 -0
- package/dist/mcp/tools/setup.d.ts +42 -0
- package/dist/mcp/tools/setup.js +75 -0
- package/dist/mcp/tools/status.d.ts +44 -0
- package/dist/mcp/tools/status.js +74 -0
- package/dist/mcp/tools/tasks.d.ts +116 -0
- package/dist/mcp/tools/tasks.js +143 -0
- package/dist/mcp/tools/worktreeCleanup.d.ts +38 -0
- package/dist/mcp/tools/worktreeCleanup.js +67 -0
- package/dist/plans/createPlan.d.ts +6 -0
- package/dist/plans/createPlan.js +29 -0
- package/dist/plans/getActivePlans.d.ts +6 -0
- package/dist/plans/getActivePlans.js +11 -0
- package/dist/plans/getPlan.d.ts +6 -0
- package/dist/plans/getPlan.js +7 -0
- package/dist/plans/index.d.ts +5 -0
- package/dist/plans/index.js +5 -0
- package/dist/plans/plans.test.d.ts +1 -0
- package/dist/plans/plans.test.js +107 -0
- package/dist/plans/types.d.ts +32 -0
- package/dist/plans/types.js +1 -0
- package/dist/plans/updatePlanStatus.d.ts +6 -0
- package/dist/plans/updatePlanStatus.js +8 -0
- package/dist/tasks/assignTask.d.ts +7 -0
- package/dist/tasks/assignTask.js +20 -0
- package/dist/tasks/blockTask.d.ts +5 -0
- package/dist/tasks/blockTask.js +12 -0
- package/dist/tasks/claimTask.d.ts +7 -0
- package/dist/tasks/claimTask.js +21 -0
- package/dist/tasks/completeTask.d.ts +6 -0
- package/dist/tasks/completeTask.js +18 -0
- package/dist/tasks/createTask.d.ts +6 -0
- package/dist/tasks/createTask.js +36 -0
- package/dist/tasks/getPendingTasks.d.ts +6 -0
- package/dist/tasks/getPendingTasks.js +19 -0
- package/dist/tasks/getTask.d.ts +6 -0
- package/dist/tasks/getTask.js +7 -0
- package/dist/tasks/getTasksByPlan.d.ts +6 -0
- package/dist/tasks/getTasksByPlan.js +11 -0
- package/dist/tasks/index.d.ts +11 -0
- package/dist/tasks/index.js +12 -0
- package/dist/tasks/startTask.d.ts +5 -0
- package/dist/tasks/startTask.js +12 -0
- package/dist/tasks/tasks.test.d.ts +1 -0
- package/dist/tasks/tasks.test.js +209 -0
- package/dist/tasks/types.d.ts +36 -0
- package/dist/tasks/types.js +1 -0
- package/dist/tasks/unclaimTask.d.ts +5 -0
- package/dist/tasks/unclaimTask.js +12 -0
- package/dist/test/factories/agentFactory.d.ts +13 -0
- package/dist/test/factories/agentFactory.js +5 -0
- package/dist/test/factories/eventFactory.d.ts +20 -0
- package/dist/test/factories/eventFactory.js +16 -0
- package/dist/test/factories/factories.test.d.ts +1 -0
- package/dist/test/factories/factories.test.js +101 -0
- package/dist/test/factories/index.d.ts +4 -0
- package/dist/test/factories/index.js +4 -0
- package/dist/test/factories/planFactory.d.ts +15 -0
- package/dist/test/factories/planFactory.js +14 -0
- package/dist/test/factories/taskFactory.d.ts +22 -0
- package/dist/test/factories/taskFactory.js +44 -0
- package/dist/test/multiAgentDemo.d.ts +16 -0
- package/dist/test/multiAgentDemo.js +20 -0
- package/dist/test/multiAgentDemo.test.d.ts +1 -0
- package/dist/test/multiAgentDemo.test.js +14 -0
- package/dist/test/setup.d.ts +9 -0
- package/dist/test/setup.js +50 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.js +5 -0
- package/dist/utils/math.d.ts +6 -0
- package/dist/utils/math.js +10 -0
- package/dist/utils/math.test.d.ts +1 -0
- package/dist/utils/math.test.js +26 -0
- package/dist/utils/string.d.ts +11 -0
- package/dist/utils/string.js +17 -0
- package/dist/utils/string.test.d.ts +1 -0
- package/dist/utils/string.test.js +30 -0
- package/dist/watcher/index.d.ts +1 -0
- package/dist/watcher/index.js +1 -0
- package/dist/watcher/planWatcher.d.ts +31 -0
- package/dist/watcher/planWatcher.js +171 -0
- package/dist/worktrees/getAllWorktrees.d.ts +6 -0
- package/dist/worktrees/getAllWorktrees.js +10 -0
- package/dist/worktrees/getWorktreeById.d.ts +6 -0
- package/dist/worktrees/getWorktreeById.js +7 -0
- package/dist/worktrees/getWorktreeByPath.d.ts +6 -0
- package/dist/worktrees/getWorktreeByPath.js +7 -0
- package/dist/worktrees/index.d.ts +9 -0
- package/dist/worktrees/index.js +13 -0
- package/dist/worktrees/registerWorktree.d.ts +6 -0
- package/dist/worktrees/registerWorktree.js +28 -0
- package/dist/worktrees/removeWorktree.d.ts +6 -0
- package/dist/worktrees/removeWorktree.js +9 -0
- package/dist/worktrees/syncWorktreesFromGit.d.ts +7 -0
- package/dist/worktrees/syncWorktreesFromGit.js +51 -0
- package/dist/worktrees/types.d.ts +29 -0
- package/dist/worktrees/types.js +1 -0
- package/dist/worktrees/updateWorktreeCommit.d.ts +5 -0
- package/dist/worktrees/updateWorktreeCommit.js +13 -0
- package/dist/worktrees/updateWorktreeSeen.d.ts +5 -0
- package/dist/worktrees/updateWorktreeSeen.js +13 -0
- 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,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,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,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,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
|
+
}
|