/** * Tests for `agenthub start` — one-command onboarding that announces and * auto-claims the task addressed to the agent (local path; the server path * uses the same logic via remoteClient). */ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { mkdtempSync, rmSync } from 'fs'; import { tmpdir } from 'os'; import { join } from 'path'; import { init } from '../src/cli/commands/init.js'; import { startAgent } from '../src/cli/commands/start.js'; import { createTask, getTask, assignTask } from '../src/core/services/taskService.js'; describe('agenthub start — onboarding auto-claim', () => { let cwd: string; beforeEach(() => { cwd = mkdtempSync(join(tmpdir(), 'ah-start-')); init(cwd, { projectName: 'start-test', yes: true }); }); afterEach(() => { rmSync(cwd, { recursive: true, force: true }); }); it('claims the open implementer task addressed to the agent (title prefix)', async () => { const mine = createTask(cwd, { title: 'kimi: backend tests', role: 'implementer' }); createTask(cwd, { title: 'codex: magic audit', role: 'implementer' }); // addressed to someone else await startAgent({ projectCwd: cwd, agent: 'kimi', role: 'implementer' }); const { task } = getTask(cwd, mine.id); expect(task.status).toBe('in_progress'); expect(task.assignedTo).toBe('kimi'); }); it('does NOT claim a task addressed to a different agent', async () => { const other = createTask(cwd, { title: 'codex: magic audit', role: 'implementer' }); await startAgent({ projectCwd: cwd, agent: 'kimi', role: 'implementer' }); const { task } = getTask(cwd, other.id); expect(task.status).toBe('open'); expect(task.assignedTo).toBeUndefined(); }); it('claims nothing when there are no open tasks (just announces)', async () => { // Should not throw; nothing to claim. await expect(startAgent({ projectCwd: cwd, agent: 'kimi', role: 'implementer' })).resolves.toBeUndefined(); }); it('assignTask addresses an open task; start then claims it via assignedTo', async () => { // A generically-titled task, NOT name-addressed to the agent. const t = createTask(cwd, { title: 'Win L2: generic bypass task', role: 'implementer' }); assignTask(cwd, t.id, 'windows-claude'); const afterAssign = getTask(cwd, t.id).task; expect(afterAssign.status).toBe('open'); // assign does NOT claim expect(afterAssign.assignedTo).toBe('windows-claude'); // Now the agent's start/work finds it via the assignedTo match and claims it. await startAgent({ projectCwd: cwd, agent: 'windows-claude', role: 'implementer' }); expect(getTask(cwd, t.id).task.status).toBe('in_progress'); expect(getTask(cwd, t.id).task.assignedTo).toBe('windows-claude'); }); });