From 0cc88603e1d43c14269bb58db9e9cd818d208c10 Mon Sep 17 00:00:00 2001 From: chahinebrini Date: Wed, 24 Jun 2026 23:42:22 +0200 Subject: [PATCH] feat(cli): add handoff create/read/list commands --- src/cli/commands/handoff.ts | 69 +++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 src/cli/commands/handoff.ts diff --git a/src/cli/commands/handoff.ts b/src/cli/commands/handoff.ts new file mode 100644 index 0000000..4d68bb3 --- /dev/null +++ b/src/cli/commands/handoff.ts @@ -0,0 +1,69 @@ +import { input, select } from '@inquirer/prompts'; +import { join } from 'path'; +import { getEntityDir } from '../../core/paths.js'; +import { getNextId } from '../../core/counter.js'; +import { readEntity, writeEntity, listEntities } from '../../core/files.js'; +import { HandoffSchema, type Handoff } from '../../core/schema.js'; +import { Index } from '../../core/index.js'; + +const roles = ['architect', 'implementer', 'reviewer', 'tester']; + +export async function handoffCreate(cwd: string, options: Partial = {}): Promise { + const fromRole = options.fromRole ?? await select({ message: 'From role:', choices: roles.map((r) => ({ name: r, value: r })) }); + const toRole = options.toRole ?? await select({ message: 'To role:', choices: roles.map((r) => ({ name: r, value: r })) }); + const taskId = options.taskId ?? await input({ message: 'Related task id (optional):' }); + const summary = options.summary ?? await input({ message: 'Summary:' }); + const context = options.context ?? await input({ message: 'Context:' }); + + const now = new Date().toISOString(); + const handoff: Handoff = HandoffSchema.parse({ + id: getNextId(cwd, 'handoff'), + fromRole, + toRole, + taskId: taskId || undefined, + summary, + context, + createdAt: now, + }); + + const filePath = join(getEntityDir(cwd, 'handoffs'), `${handoff.id}.md`); + writeEntity(filePath, handoff, `# ${handoff.summary}\n\n${handoff.context}`); + + const index = new Index(cwd); + index.upsert({ + id: handoff.id, + type: 'handoff', + title: handoff.summary, + content: handoff.context, + filePath, + createdAt: handoff.createdAt, + updatedAt: handoff.createdAt, + }); + index.close(); + + console.log(`Handoff created: ${handoff.id}`); +} + +export function handoffRead(cwd: string, id: string): void { + const filePath = join(getEntityDir(cwd, 'handoffs'), `${id}.md`); + const { frontmatter, body } = readEntity(filePath); + console.log(`# ${frontmatter.summary}`); + console.log(`From: ${frontmatter.fromRole} → ${frontmatter.toRole}`); + if (frontmatter.taskId) console.log(`Task: ${frontmatter.taskId}`); + console.log('\n' + body); +} + +export function handoffList(cwd: string): void { + const index = new Index(cwd); + const handoffs = index.list('handoff'); + index.close(); + + if (handoffs.length === 0) { + console.log('No handoffs found.'); + return; + } + + for (const h of handoffs) { + console.log(`${h.id}: ${h.title}`); + } +}