@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,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task status values
|
|
3
|
+
*/
|
|
4
|
+
export type TaskStatus = 'pending' | 'claimed' | 'in_progress' | 'blocked' | 'done';
|
|
5
|
+
/**
|
|
6
|
+
* Task record as stored in DB
|
|
7
|
+
*/
|
|
8
|
+
export type Task = {
|
|
9
|
+
id: string;
|
|
10
|
+
plan_hex: string;
|
|
11
|
+
seq: string;
|
|
12
|
+
label: string | null;
|
|
13
|
+
plan_id: string;
|
|
14
|
+
title: string;
|
|
15
|
+
description: string | null;
|
|
16
|
+
status: TaskStatus;
|
|
17
|
+
branch: string | null;
|
|
18
|
+
worktree_id: string | null;
|
|
19
|
+
claimed_by: string | null;
|
|
20
|
+
claimed_at: string | null;
|
|
21
|
+
completed_at: string | null;
|
|
22
|
+
outcome: string | null;
|
|
23
|
+
parent_task_id: string | null;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Input for creating a task
|
|
27
|
+
*/
|
|
28
|
+
export type CreateTaskInput = {
|
|
29
|
+
plan_id: string;
|
|
30
|
+
title: string;
|
|
31
|
+
description?: string;
|
|
32
|
+
branch?: string;
|
|
33
|
+
worktree_id?: string;
|
|
34
|
+
parent_task_id?: string;
|
|
35
|
+
label?: string;
|
|
36
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unclaim a task (release it back to pending)
|
|
3
|
+
*/
|
|
4
|
+
export function unclaimTask(db, taskId) {
|
|
5
|
+
const stmt = db.prepare(`
|
|
6
|
+
UPDATE tasks
|
|
7
|
+
SET status = 'pending', claimed_by = NULL, claimed_at = NULL
|
|
8
|
+
WHERE id = ? AND status IN ('claimed', 'in_progress', 'blocked')
|
|
9
|
+
`);
|
|
10
|
+
const result = stmt.run(taskId);
|
|
11
|
+
return result.changes > 0;
|
|
12
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Database } from 'bun:sqlite';
|
|
2
|
+
import type { Agent } from '../../agents/types';
|
|
3
|
+
export type AgentOverrides = {
|
|
4
|
+
label?: string;
|
|
5
|
+
context_summary?: string;
|
|
6
|
+
pid?: number;
|
|
7
|
+
session_id?: string;
|
|
8
|
+
worktree_id?: string;
|
|
9
|
+
};
|
|
10
|
+
export type BuildAgentResult = {
|
|
11
|
+
agent: Agent;
|
|
12
|
+
};
|
|
13
|
+
export declare function buildAgent(db: Database, overrides?: AgentOverrides): BuildAgentResult;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Database } from 'bun:sqlite';
|
|
2
|
+
import type { Event, EventType } from '../../events/types';
|
|
3
|
+
import type { Agent } from '../../agents/types';
|
|
4
|
+
import type { Plan } from '../../plans/types';
|
|
5
|
+
import type { Task } from '../../tasks/types';
|
|
6
|
+
export type EventOverrides = {
|
|
7
|
+
type?: EventType;
|
|
8
|
+
content?: string;
|
|
9
|
+
agent?: Agent;
|
|
10
|
+
plan?: Plan;
|
|
11
|
+
task?: Task;
|
|
12
|
+
branch?: string;
|
|
13
|
+
worktree_id?: string;
|
|
14
|
+
metadata?: Record<string, unknown>;
|
|
15
|
+
};
|
|
16
|
+
export type BuildEventResult = {
|
|
17
|
+
event: Event;
|
|
18
|
+
agent: Agent;
|
|
19
|
+
};
|
|
20
|
+
export declare function buildEvent(db: Database, overrides?: EventOverrides): BuildEventResult;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { emit } from '../../events/emit';
|
|
2
|
+
import { buildAgent } from './agentFactory';
|
|
3
|
+
export function buildEvent(db, overrides = {}) {
|
|
4
|
+
const agent = overrides.agent ?? buildAgent(db).agent;
|
|
5
|
+
const event = emit(db, {
|
|
6
|
+
agent_id: agent.id,
|
|
7
|
+
type: overrides.type ?? 'note',
|
|
8
|
+
content: overrides.content ?? 'Test event',
|
|
9
|
+
plan_id: overrides.plan?.id,
|
|
10
|
+
task_id: overrides.task?.id,
|
|
11
|
+
worktree_id: overrides.worktree_id,
|
|
12
|
+
branch: overrides.branch,
|
|
13
|
+
metadata: overrides.metadata,
|
|
14
|
+
});
|
|
15
|
+
return { event, agent };
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, afterAll } from 'bun:test';
|
|
2
|
+
import { createTestDb, cleanupAllTestDbs } from '../setup';
|
|
3
|
+
import { buildAgent } from './agentFactory';
|
|
4
|
+
import { buildPlan } from './planFactory';
|
|
5
|
+
import { buildTask } from './taskFactory';
|
|
6
|
+
import { buildEvent } from './eventFactory';
|
|
7
|
+
describe('Test Factories', () => {
|
|
8
|
+
let testDb;
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
testDb = createTestDb();
|
|
11
|
+
});
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
testDb.cleanup();
|
|
14
|
+
});
|
|
15
|
+
afterAll(() => {
|
|
16
|
+
cleanupAllTestDbs();
|
|
17
|
+
});
|
|
18
|
+
describe('buildAgent', () => {
|
|
19
|
+
it('creates agent with defaults', () => {
|
|
20
|
+
const { agent } = buildAgent(testDb.db);
|
|
21
|
+
expect(agent.id).toMatch(/^agt_[a-f0-9]{6}$/);
|
|
22
|
+
expect(agent.status).toBe('active');
|
|
23
|
+
});
|
|
24
|
+
it('creates agent with label', () => {
|
|
25
|
+
const { agent } = buildAgent(testDb.db, { label: 'worker' });
|
|
26
|
+
expect(agent.id).toMatch(/^agt_[a-f0-9]{6}_worker$/);
|
|
27
|
+
expect(agent.label).toBe('worker');
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
describe('buildPlan', () => {
|
|
31
|
+
it('creates plan and agent', () => {
|
|
32
|
+
const { plan, agent } = buildPlan(testDb.db);
|
|
33
|
+
expect(plan.id).toMatch(/^pln_[a-f0-9]{6}$/);
|
|
34
|
+
expect(plan.title).toBe('Test Plan');
|
|
35
|
+
expect(plan.created_by).toBe(agent.id);
|
|
36
|
+
});
|
|
37
|
+
it('uses existing agent', () => {
|
|
38
|
+
const { agent: existingAgent } = buildAgent(testDb.db, { label: 'lead' });
|
|
39
|
+
const { plan, agent } = buildPlan(testDb.db, { agent: existingAgent });
|
|
40
|
+
expect(plan.created_by).toBe(existingAgent.id);
|
|
41
|
+
expect(agent.id).toBe(existingAgent.id);
|
|
42
|
+
});
|
|
43
|
+
it('accepts custom title', () => {
|
|
44
|
+
const { plan } = buildPlan(testDb.db, { title: 'Auth Feature' });
|
|
45
|
+
expect(plan.title).toBe('Auth Feature');
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
describe('buildTask', () => {
|
|
49
|
+
it('creates task with plan and agent', () => {
|
|
50
|
+
const { task, plan, agent } = buildTask(testDb.db);
|
|
51
|
+
expect(task.id).toMatch(/^tsk_[a-f0-9]{6}_001$/);
|
|
52
|
+
expect(task.plan_id).toBe(plan.id);
|
|
53
|
+
expect(task.status).toBe('pending');
|
|
54
|
+
expect(plan.created_by).toBe(agent.id);
|
|
55
|
+
});
|
|
56
|
+
it('uses existing plan', () => {
|
|
57
|
+
const { plan: existingPlan, agent: existingAgent } = buildPlan(testDb.db, { title: 'My Plan' });
|
|
58
|
+
const { task, plan, agent } = buildTask(testDb.db, { plan: existingPlan });
|
|
59
|
+
expect(task.plan_id).toBe(existingPlan.id);
|
|
60
|
+
expect(plan.id).toBe(existingPlan.id);
|
|
61
|
+
expect(agent.id).toBe(existingAgent.id);
|
|
62
|
+
});
|
|
63
|
+
it('creates claimed task', () => {
|
|
64
|
+
const { agent: claimer } = buildAgent(testDb.db, { label: 'claimer' });
|
|
65
|
+
const { task } = buildTask(testDb.db, {
|
|
66
|
+
status: 'claimed',
|
|
67
|
+
claimed_by: claimer,
|
|
68
|
+
});
|
|
69
|
+
expect(task.status).toBe('claimed');
|
|
70
|
+
expect(task.claimed_by).toBe(claimer.id);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
describe('buildEvent', () => {
|
|
74
|
+
it('creates event with agent', () => {
|
|
75
|
+
const { event, agent } = buildEvent(testDb.db);
|
|
76
|
+
expect(event.id).toMatch(/^evt_[a-f0-9]{6}_\d+$/);
|
|
77
|
+
expect(event.agent_id).toBe(agent.id);
|
|
78
|
+
expect(event.event_type).toBe('note');
|
|
79
|
+
expect(event.content).toBe('Test event');
|
|
80
|
+
});
|
|
81
|
+
it('uses existing agent', () => {
|
|
82
|
+
const { agent: existingAgent } = buildAgent(testDb.db, { label: 'emitter' });
|
|
83
|
+
const { event, agent } = buildEvent(testDb.db, { agent: existingAgent });
|
|
84
|
+
expect(event.agent_id).toBe(existingAgent.id);
|
|
85
|
+
expect(agent.id).toBe(existingAgent.id);
|
|
86
|
+
});
|
|
87
|
+
it('creates event with plan and task', () => {
|
|
88
|
+
const { task, plan, agent } = buildTask(testDb.db);
|
|
89
|
+
const { event } = buildEvent(testDb.db, {
|
|
90
|
+
agent,
|
|
91
|
+
plan,
|
|
92
|
+
task,
|
|
93
|
+
type: 'task:start',
|
|
94
|
+
content: 'Starting work',
|
|
95
|
+
});
|
|
96
|
+
expect(event.plan_id).toBe(plan.id);
|
|
97
|
+
expect(event.task_id).toBe(task.id);
|
|
98
|
+
expect(event.event_type).toBe('task:start');
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { buildAgent, type AgentOverrides, type BuildAgentResult } from './agentFactory';
|
|
2
|
+
export { buildPlan, type PlanOverrides, type BuildPlanResult } from './planFactory';
|
|
3
|
+
export { buildTask, type TaskOverrides, type BuildTaskResult } from './taskFactory';
|
|
4
|
+
export { buildEvent, type EventOverrides, type BuildEventResult } from './eventFactory';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Database } from 'bun:sqlite';
|
|
2
|
+
import type { Plan } from '../../plans/types';
|
|
3
|
+
import type { Agent } from '../../agents/types';
|
|
4
|
+
export type PlanOverrides = {
|
|
5
|
+
title?: string;
|
|
6
|
+
label?: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
branch?: string;
|
|
9
|
+
agent?: Agent;
|
|
10
|
+
};
|
|
11
|
+
export type BuildPlanResult = {
|
|
12
|
+
plan: Plan;
|
|
13
|
+
agent: Agent;
|
|
14
|
+
};
|
|
15
|
+
export declare function buildPlan(db: Database, overrides?: PlanOverrides): BuildPlanResult;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createPlan } from '../../plans/createPlan';
|
|
2
|
+
import { buildAgent } from './agentFactory';
|
|
3
|
+
export function buildPlan(db, overrides = {}) {
|
|
4
|
+
const { agent: existingAgent, ...planOverrides } = overrides;
|
|
5
|
+
const agent = existingAgent ?? buildAgent(db).agent;
|
|
6
|
+
const plan = createPlan(db, {
|
|
7
|
+
title: planOverrides.title ?? 'Test Plan',
|
|
8
|
+
label: planOverrides.label,
|
|
9
|
+
description: planOverrides.description,
|
|
10
|
+
branch: planOverrides.branch,
|
|
11
|
+
created_by: agent.id,
|
|
12
|
+
});
|
|
13
|
+
return { plan, agent };
|
|
14
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Database } from 'bun:sqlite';
|
|
2
|
+
import type { Task, TaskStatus } from '../../tasks/types';
|
|
3
|
+
import type { Plan } from '../../plans/types';
|
|
4
|
+
import type { Agent } from '../../agents/types';
|
|
5
|
+
export type TaskOverrides = {
|
|
6
|
+
title?: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
status?: TaskStatus;
|
|
9
|
+
claimed_by?: Agent;
|
|
10
|
+
plan?: Plan;
|
|
11
|
+
agent?: Agent;
|
|
12
|
+
branch?: string;
|
|
13
|
+
worktree_id?: string;
|
|
14
|
+
parent_task_id?: string;
|
|
15
|
+
label?: string;
|
|
16
|
+
};
|
|
17
|
+
export type BuildTaskResult = {
|
|
18
|
+
task: Task;
|
|
19
|
+
plan: Plan;
|
|
20
|
+
agent: Agent;
|
|
21
|
+
};
|
|
22
|
+
export declare function buildTask(db: Database, overrides?: TaskOverrides): BuildTaskResult;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { createTask } from '../../tasks/createTask';
|
|
2
|
+
import { buildPlan } from './planFactory';
|
|
3
|
+
export function buildTask(db, overrides = {}) {
|
|
4
|
+
let plan;
|
|
5
|
+
let agent;
|
|
6
|
+
if (overrides.plan) {
|
|
7
|
+
plan = overrides.plan;
|
|
8
|
+
const agentRow = db.prepare(`SELECT * FROM agents WHERE id = ?`).get(plan.created_by);
|
|
9
|
+
agent = overrides.agent ?? agentRow ?? buildPlan(db).agent;
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
const result = buildPlan(db, { agent: overrides.agent });
|
|
13
|
+
plan = result.plan;
|
|
14
|
+
agent = result.agent;
|
|
15
|
+
}
|
|
16
|
+
const task = createTask(db, {
|
|
17
|
+
plan_id: plan.id,
|
|
18
|
+
title: overrides.title ?? 'Test Task',
|
|
19
|
+
description: overrides.description,
|
|
20
|
+
branch: overrides.branch,
|
|
21
|
+
worktree_id: overrides.worktree_id,
|
|
22
|
+
parent_task_id: overrides.parent_task_id,
|
|
23
|
+
label: overrides.label,
|
|
24
|
+
});
|
|
25
|
+
// Handle status and claimed_by if provided
|
|
26
|
+
if (overrides.status || overrides.claimed_by) {
|
|
27
|
+
const updates = [];
|
|
28
|
+
const values = [];
|
|
29
|
+
if (overrides.status) {
|
|
30
|
+
updates.push('status = ?');
|
|
31
|
+
values.push(overrides.status);
|
|
32
|
+
}
|
|
33
|
+
if (overrides.claimed_by) {
|
|
34
|
+
updates.push('claimed_by = ?', 'claimed_at = ?');
|
|
35
|
+
values.push(overrides.claimed_by.id, new Date().toISOString());
|
|
36
|
+
}
|
|
37
|
+
if (updates.length > 0) {
|
|
38
|
+
values.push(task.id);
|
|
39
|
+
db.prepare(`UPDATE tasks SET ${updates.join(', ')} WHERE id = ?`).run(...values);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const finalTask = db.prepare(`SELECT * FROM tasks WHERE id = ?`).get(task.id);
|
|
43
|
+
return { task: finalTask, plan, agent };
|
|
44
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-agent coordination demo
|
|
3
|
+
*
|
|
4
|
+
* Task 001 (agt_c88d5b): greeting function
|
|
5
|
+
* Task 002 (agt_4564e7): farewell function
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Returns a greeting message from Agent 1
|
|
9
|
+
* @returns A friendly hello message
|
|
10
|
+
*/
|
|
11
|
+
export declare function greeting(): string;
|
|
12
|
+
/**
|
|
13
|
+
* Returns a farewell message from Agent 2
|
|
14
|
+
* @returns A friendly goodbye message
|
|
15
|
+
*/
|
|
16
|
+
export declare function farewell(): string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-agent coordination demo
|
|
3
|
+
*
|
|
4
|
+
* Task 001 (agt_c88d5b): greeting function
|
|
5
|
+
* Task 002 (agt_4564e7): farewell function
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Returns a greeting message from Agent 1
|
|
9
|
+
* @returns A friendly hello message
|
|
10
|
+
*/
|
|
11
|
+
export function greeting() {
|
|
12
|
+
return "Hello from Agent 1";
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Returns a farewell message from Agent 2
|
|
16
|
+
* @returns A friendly goodbye message
|
|
17
|
+
*/
|
|
18
|
+
export function farewell() {
|
|
19
|
+
return "Goodbye from Agent 2";
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { describe, it, expect } from 'bun:test';
|
|
2
|
+
import { greeting, farewell } from './multiAgentDemo';
|
|
3
|
+
describe('Multi-Agent Demo', () => {
|
|
4
|
+
it('greeting returns correct message', () => {
|
|
5
|
+
expect(greeting()).toBe('Hello from Agent 1');
|
|
6
|
+
});
|
|
7
|
+
it('farewell returns correct message', () => {
|
|
8
|
+
expect(farewell()).toBe('Goodbye from Agent 2');
|
|
9
|
+
});
|
|
10
|
+
it('both functions work together', () => {
|
|
11
|
+
const conversation = `${greeting()} ... ${farewell()}`;
|
|
12
|
+
expect(conversation).toBe('Hello from Agent 1 ... Goodbye from Agent 2');
|
|
13
|
+
});
|
|
14
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Database } from 'bun:sqlite';
|
|
2
|
+
import { readFileSync } from 'fs';
|
|
3
|
+
import { join, dirname } from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { mkdirSync, rmSync, existsSync } from 'fs';
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const TEST_DB_DIR = join(__dirname, '../../.test-db');
|
|
8
|
+
let testDbCounter = 0;
|
|
9
|
+
export function createTestDb() {
|
|
10
|
+
testDbCounter++;
|
|
11
|
+
const dbPath = join(TEST_DB_DIR, `test-${Date.now()}-${testDbCounter}.sqlite`);
|
|
12
|
+
if (!existsSync(TEST_DB_DIR)) {
|
|
13
|
+
mkdirSync(TEST_DB_DIR, { recursive: true });
|
|
14
|
+
}
|
|
15
|
+
const db = new Database(dbPath);
|
|
16
|
+
db.exec('PRAGMA journal_mode = WAL');
|
|
17
|
+
db.exec('PRAGMA busy_timeout = 5000');
|
|
18
|
+
db.exec('PRAGMA foreign_keys = ON');
|
|
19
|
+
const schemaPath = join(__dirname, '../db/schema.sql');
|
|
20
|
+
const schema = readFileSync(schemaPath, 'utf-8');
|
|
21
|
+
db.exec(schema);
|
|
22
|
+
const clearAllTables = () => {
|
|
23
|
+
// Order matters due to FK constraints - delete children first
|
|
24
|
+
db.exec('DELETE FROM events');
|
|
25
|
+
db.exec('DELETE FROM tasks');
|
|
26
|
+
db.exec('DELETE FROM plans');
|
|
27
|
+
db.exec('DELETE FROM agents');
|
|
28
|
+
db.exec('DELETE FROM worktrees');
|
|
29
|
+
// Reset sequence counter
|
|
30
|
+
db.exec("UPDATE sequences SET value = 0 WHERE name = 'events'");
|
|
31
|
+
};
|
|
32
|
+
const cleanup = () => {
|
|
33
|
+
db.close();
|
|
34
|
+
if (existsSync(dbPath)) {
|
|
35
|
+
rmSync(dbPath, { force: true });
|
|
36
|
+
}
|
|
37
|
+
if (existsSync(`${dbPath}-wal`)) {
|
|
38
|
+
rmSync(`${dbPath}-wal`, { force: true });
|
|
39
|
+
}
|
|
40
|
+
if (existsSync(`${dbPath}-shm`)) {
|
|
41
|
+
rmSync(`${dbPath}-shm`, { force: true });
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
return { db, path: dbPath, cleanup, clearAllTables };
|
|
45
|
+
}
|
|
46
|
+
export function cleanupAllTestDbs() {
|
|
47
|
+
if (existsSync(TEST_DB_DIR)) {
|
|
48
|
+
rmSync(TEST_DB_DIR, { recursive: true, force: true });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { add, subtract } from "./math";
|
|
3
|
+
describe("math utilities", () => {
|
|
4
|
+
describe("add", () => {
|
|
5
|
+
test("adds two positive numbers", () => {
|
|
6
|
+
expect(add(2, 3)).toBe(5);
|
|
7
|
+
});
|
|
8
|
+
test("adds negative numbers", () => {
|
|
9
|
+
expect(add(-1, -2)).toBe(-3);
|
|
10
|
+
});
|
|
11
|
+
test("adds zero", () => {
|
|
12
|
+
expect(add(5, 0)).toBe(5);
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
describe("subtract", () => {
|
|
16
|
+
test("subtracts two positive numbers", () => {
|
|
17
|
+
expect(subtract(5, 3)).toBe(2);
|
|
18
|
+
});
|
|
19
|
+
test("subtracts resulting in negative", () => {
|
|
20
|
+
expect(subtract(3, 5)).toBe(-2);
|
|
21
|
+
});
|
|
22
|
+
test("subtracts zero", () => {
|
|
23
|
+
expect(subtract(5, 0)).toBe(5);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* String utility functions
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Capitalizes the first letter of a string
|
|
6
|
+
*/
|
|
7
|
+
export function capitalize(str) {
|
|
8
|
+
if (!str)
|
|
9
|
+
return str;
|
|
10
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Reverses a string
|
|
14
|
+
*/
|
|
15
|
+
export function reverse(str) {
|
|
16
|
+
return str.split("").reverse().join("");
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import { capitalize, reverse } from "./string";
|
|
3
|
+
describe("capitalize", () => {
|
|
4
|
+
test("capitalizes first letter", () => {
|
|
5
|
+
expect(capitalize("hello")).toBe("Hello");
|
|
6
|
+
});
|
|
7
|
+
test("handles already capitalized", () => {
|
|
8
|
+
expect(capitalize("Hello")).toBe("Hello");
|
|
9
|
+
});
|
|
10
|
+
test("handles empty string", () => {
|
|
11
|
+
expect(capitalize("")).toBe("");
|
|
12
|
+
});
|
|
13
|
+
test("handles single character", () => {
|
|
14
|
+
expect(capitalize("a")).toBe("A");
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
describe("reverse", () => {
|
|
18
|
+
test("reverses a string", () => {
|
|
19
|
+
expect(reverse("hello")).toBe("olleh");
|
|
20
|
+
});
|
|
21
|
+
test("handles empty string", () => {
|
|
22
|
+
expect(reverse("")).toBe("");
|
|
23
|
+
});
|
|
24
|
+
test("handles palindrome", () => {
|
|
25
|
+
expect(reverse("radar")).toBe("radar");
|
|
26
|
+
});
|
|
27
|
+
test("handles single character", () => {
|
|
28
|
+
expect(reverse("x")).toBe("x");
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { startPlanWatcher, syncPlanFile, initialPlanSync } from './planWatcher';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { startPlanWatcher, syncPlanFile, initialPlanSync } from './planWatcher';
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|
+
/**
|
|
11
|
+
* Sync a single plan file to hivemind
|
|
12
|
+
* Uses LLM to extract tasks, then reconciles with DB
|
|
13
|
+
*/
|
|
14
|
+
export declare function syncPlanFile(project: string, filename: string): Promise<{
|
|
15
|
+
success: boolean;
|
|
16
|
+
planId?: string;
|
|
17
|
+
message: string;
|
|
18
|
+
}>;
|
|
19
|
+
/**
|
|
20
|
+
* Initial sync - scan existing plan files
|
|
21
|
+
*/
|
|
22
|
+
export declare function initialPlanSync(project: string): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Start watching the plans directory
|
|
25
|
+
*/
|
|
26
|
+
export declare function startPlanWatcher(options: {
|
|
27
|
+
project: string;
|
|
28
|
+
verbose?: boolean;
|
|
29
|
+
}): {
|
|
30
|
+
stop: () => void;
|
|
31
|
+
};
|