feat(HAI-023): complete Step 4 — CLI attach command
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { runDashboard } from "./commands/dashboard.js";
|
||||
import { runTaskCreate, runTaskList, runTaskMove, runTaskMerge, runTaskUpdate, runTaskLog, runTaskShow } from "./commands/task.js";
|
||||
import { runTaskCreate, runTaskList, runTaskMove, runTaskMerge, runTaskUpdate, runTaskLog, runTaskShow, runTaskAttach } from "./commands/task.js";
|
||||
|
||||
const HELP = `
|
||||
hai — AI-orchestrated task board
|
||||
@@ -15,6 +15,7 @@ Usage:
|
||||
hai task update <id> <step> <status> Update step status (pending|in-progress|done|skipped)
|
||||
hai task log <id> <message> Add a log entry
|
||||
hai task merge <id> Merge an in-review task and close it
|
||||
hai task attach <id> <file> Attach a screenshot image to a task
|
||||
|
||||
Options:
|
||||
--port, -p <port> Dashboard port (default: 4040)
|
||||
@@ -100,6 +101,15 @@ async function main() {
|
||||
await runTaskMerge(id);
|
||||
break;
|
||||
}
|
||||
case "attach": {
|
||||
const id = args[2], file = args[3];
|
||||
if (!id || !file) {
|
||||
console.error("Usage: hai task attach <id> <file>");
|
||||
process.exit(1);
|
||||
}
|
||||
await runTaskAttach(id, file);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
console.error(`Unknown subcommand: task ${subcommand || ""}`);
|
||||
console.log("Try: hai task create | list | move");
|
||||
|
||||
@@ -169,6 +169,49 @@ export async function runTaskMerge(id: string) {
|
||||
}
|
||||
}
|
||||
|
||||
const MIME_TYPES: Record<string, string> = {
|
||||
".png": "image/png",
|
||||
".jpg": "image/jpeg",
|
||||
".jpeg": "image/jpeg",
|
||||
".gif": "image/gif",
|
||||
".webp": "image/webp",
|
||||
};
|
||||
|
||||
export async function runTaskAttach(id: string, filePath: string) {
|
||||
const { readFile } = await import("node:fs/promises");
|
||||
const { basename, extname } = await import("node:path");
|
||||
const { resolve } = await import("node:path");
|
||||
|
||||
const resolvedPath = resolve(filePath);
|
||||
const filename = basename(resolvedPath);
|
||||
const ext = extname(filename).toLowerCase();
|
||||
const mimeType = MIME_TYPES[ext];
|
||||
|
||||
if (!mimeType) {
|
||||
console.error(`Unsupported file type: ${ext}`);
|
||||
console.error(`Supported: ${Object.keys(MIME_TYPES).join(", ")}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let content: Buffer;
|
||||
try {
|
||||
content = await readFile(resolvedPath);
|
||||
} catch {
|
||||
console.error(`Cannot read file: ${filePath}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const store = await getStore();
|
||||
const attachment = await store.addAttachment(id, filename, content, mimeType);
|
||||
|
||||
const sizeKB = (attachment.size / 1024).toFixed(1);
|
||||
console.log();
|
||||
console.log(` ✓ Attached to ${id}: ${attachment.originalName}`);
|
||||
console.log(` File: ${attachment.filename} (${sizeKB} KB)`);
|
||||
console.log(` Path: .hai/tasks/${id}/attachments/${attachment.filename}`);
|
||||
console.log();
|
||||
}
|
||||
|
||||
export async function runTaskMove(id: string, column: string) {
|
||||
if (!COLUMNS.includes(column as Column)) {
|
||||
console.error(`Invalid column: ${column}`);
|
||||
|
||||
Reference in New Issue
Block a user