agenthub/tests/start.test.ts
chahinebrini 7723360054 feat: agenthub start — one-command onboarding (no copy-paste prompts)
An agent runs ONE command and is working: it announces presence, claims
the open task addressed to it, and prints the task + its handoff + the
next step. Removes the per-agent kickoff copy-paste.

- src/cli/commands/start.ts: `agenthub start --agent <name> --role <role>`.
  "Addressed to <agent>" = task title starts with "<agent>:" OR a handoff
  has toAgent=<agent>. Claims it, prints task body + handoff + the review
  gate reminder. No addressed task → lists open role tasks to pick.
  Works on local + --server paths.
- templates: implementer guides (AGENTS/CODEX/KIMI.md) now lead with
  "On your first turn, RUN `agenthub start …` — do not just summarize",
  so a fresh session self-onboards from one trigger word.
- tests: start auto-claims the addressed task, ignores others, no-ops on
  empty queue; templates test updated for the new commands. 116/116 green.

Bump 0.2.2 -> 0.3.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 17:15:25 +02:00

52 lines
1.9 KiB
TypeScript

/**
* 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 } 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();
});
});