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