chahinebrini 78bbebf435 feat(task): agenthub task assign — architect addresses an open task to an agent
Closes the addressing gap that left a waiting `agenthub work` blocked: the
remaining Win tasks were titled "Win L2: …" / unassigned, so they matched
no agent and the daemon waited forever.

`agenthub task assign <id> --agent <name>` sets assignedTo WITHOUT claiming
(status stays open) and fires task/updated — so an agent's blocked `work`
re-checks, matches via assignedTo, and auto-claims it. The architect can now
route a specific open task to a specific agent and have its daemon pick it up.

- taskService.assignTask; PATCH /tasks/:id handles assignedTo-without-status;
  remoteClient.assignTask; CLI `task assign`.
- test: assign keeps status open + sets assignedTo; start/work then claims it
  via the assignedTo match. 122/122 green.

Bump 0.5.0 -> 0.6.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 00:12:50 +02:00

72 lines
2.4 KiB
TypeScript

import { input, select } from '@inquirer/prompts';
import { createTask, listTasks, getTask, claimTask, doneTask, reviewTask, reopenTask, assignTask } from '../../core/services/taskService.js';
import type { Task } from '../../core/schema.js';
export async function taskCreate(cwd: string, options: Partial<Task> = {}): Promise<void> {
const title = options.title ?? await input({ message: 'Task title:' });
const role = options.role ?? await select({
message: 'Role:',
choices: [
{ name: 'architect', value: 'architect' },
{ name: 'implementer', value: 'implementer' },
{ name: 'reviewer', value: 'reviewer' },
{ name: 'tester', value: 'tester' },
],
});
const priority = options.priority ?? 'medium';
const task = createTask(cwd, { title, role, priority });
console.log(`AgentHub: Task created ${task.id} ${task.title}`);
}
export function taskList(cwd: string, filters?: { status?: string; role?: string }): void {
const tasks = listTasks(cwd, filters);
if (tasks.length === 0) {
console.log('No tasks found.');
return;
}
for (const t of tasks) {
console.log(`${t.id} [${t.status}] (${t.role ?? 'unassigned'}) ${t.title}`);
}
}
export function taskShow(cwd: string, id: string): void {
const { task, body } = getTask(cwd, id);
console.log(`# ${task.title}`);
console.log(`Status: ${task.status} | Role: ${task.role ?? '-'} | Priority: ${task.priority}`);
console.log('\n' + body);
}
export function taskClaim(cwd: string, id: string, agentName: string): void {
claimTask(cwd, id, agentName);
console.log(`AgentHub: Task claimed ${id} by ${agentName}`);
}
export function taskDone(
cwd: string,
id: string,
meta?: { tokens?: number; duration?: number; by?: string },
): void {
doneTask(cwd, id, {
doneBy: meta?.by,
doneTokens: meta?.tokens,
doneDuration: meta?.duration,
});
console.log(`AgentHub: Task done ${id}`);
}
export function taskReview(cwd: string, id: string): void {
reviewTask(cwd, id);
console.log(`AgentHub: Task review ${id} (awaiting architect review)`);
}
export function taskReopen(cwd: string, id: string): void {
reopenTask(cwd, id);
console.log(`AgentHub: Task reopened ${id}`);
}
export function taskAssign(cwd: string, id: string, agentName: string): void {
assignTask(cwd, id, agentName);
console.log(`AgentHub: Task assigned ${id}${agentName}`);
}