feat(KB-034): add task archive/unarchive functionality
- Add 'archived' column to task store with archiveTask and unarchiveTask methods - Add CLI commands: kb task archive <id> and kb task unarchive <id> - Add pi extension tools for archive and unarchive operations - Add dashboard API endpoints POST /api/tasks/:id/archive and /unarchive - Add Archived column to board UI with archive/unarchive buttons - Prevent drag-drop into archived column, add visual distinction - Include duplicateTask from concurrent branch in merge resolution
This commit is contained in:
@@ -84,6 +84,8 @@ describe("kb pi extension", () => {
|
||||
"kb_task_import_github",
|
||||
"kb_task_import_github_issue",
|
||||
"kb_task_browse_github_issues",
|
||||
"kb_task_archive",
|
||||
"kb_task_unarchive",
|
||||
];
|
||||
|
||||
for (const name of expected) {
|
||||
|
||||
@@ -39,7 +39,7 @@ if (isBunBinary) {
|
||||
|
||||
// Dynamic imports so the pi-coding-agent config module sees PI_PACKAGE_DIR
|
||||
const { runDashboard } = await import("./commands/dashboard.js");
|
||||
const { runTaskCreate, runTaskList, runTaskMove, runTaskMerge, runTaskUpdate, runTaskLog, runTaskShow, runTaskAttach, runTaskPause, runTaskUnpause, runTaskImportFromGitHub, runTaskDuplicate } = await import("./commands/task.js");
|
||||
const { runTaskCreate, runTaskList, runTaskMove, runTaskMerge, runTaskUpdate, runTaskLog, runTaskShow, runTaskAttach, runTaskPause, runTaskUnpause, runTaskImportFromGitHub, runTaskDuplicate, runTaskArchive, runTaskUnarchive } = await import("./commands/task.js");
|
||||
|
||||
const HELP = `
|
||||
kb — AI-orchestrated task board
|
||||
@@ -56,6 +56,8 @@ Usage:
|
||||
kb task log <id> <message> Add a log entry
|
||||
kb task merge <id> Merge an in-review task and close it
|
||||
kb task duplicate <id> Duplicate a task (creates copy in triage)
|
||||
kb task archive <id> Archive a done task
|
||||
kb task unarchive <id> Unarchive an archived task
|
||||
kb task attach <id> <file> Attach a file to a task
|
||||
kb task pause <id> Pause a task (stops all automation)
|
||||
kb task unpause <id> Unpause a task (resumes automation)
|
||||
@@ -72,7 +74,7 @@ Options:
|
||||
--interactive, -i Interactive mode for issue selection
|
||||
--help, -h Show this help
|
||||
|
||||
Columns: triage, todo, in-progress, in-review, done
|
||||
Columns: triage, todo, in-progress, in-review, done, archived
|
||||
Supported file types: png, jpg, gif, webp, txt, log, json, yaml, yml, toml, csv, xml
|
||||
|
||||
The AI engine uses pi (github.com/badlogic/pi-mono) for agent sessions.
|
||||
@@ -174,6 +176,18 @@ async function main() {
|
||||
await runTaskDuplicate(id);
|
||||
break;
|
||||
}
|
||||
case "archive": {
|
||||
const id = args[2];
|
||||
if (!id) { console.error("Usage: kb task archive <id>"); process.exit(1); }
|
||||
await runTaskArchive(id);
|
||||
break;
|
||||
}
|
||||
case "unarchive": {
|
||||
const id = args[2];
|
||||
if (!id) { console.error("Usage: kb task unarchive <id>"); process.exit(1); }
|
||||
await runTaskUnarchive(id);
|
||||
break;
|
||||
}
|
||||
case "attach": {
|
||||
const id = args[2], file = args[3];
|
||||
if (!id || !file) {
|
||||
|
||||
@@ -296,6 +296,24 @@ export async function runTaskDuplicate(id: string) {
|
||||
console.log();
|
||||
}
|
||||
|
||||
export async function runTaskArchive(id: string) {
|
||||
const store = await getStore();
|
||||
const task = await store.archiveTask(id);
|
||||
|
||||
console.log();
|
||||
console.log(` ✓ Archived ${task.id} → ${COLUMN_LABELS[task.column]}`);
|
||||
console.log();
|
||||
}
|
||||
|
||||
export async function runTaskUnarchive(id: string) {
|
||||
const store = await getStore();
|
||||
const task = await store.unarchiveTask(id);
|
||||
|
||||
console.log();
|
||||
console.log(` ✓ Unarchived ${task.id} → ${COLUMN_LABELS[task.column]}`);
|
||||
console.log();
|
||||
}
|
||||
|
||||
export async function runTaskImportGitHubInteractive(
|
||||
ownerRepo: string,
|
||||
options: TaskImportOptions = {}
|
||||
|
||||
@@ -371,6 +371,63 @@ export default function kbExtension(pi: ExtensionAPI) {
|
||||
},
|
||||
});
|
||||
|
||||
// ── kb_task_archive ───────────────────────────────────────────────
|
||||
|
||||
pi.registerTool({
|
||||
name: "kb_task_archive",
|
||||
label: "KB: Archive Task",
|
||||
description:
|
||||
"Archive a done task (move from done → archived). " +
|
||||
"Archived tasks are preserved for historical reference but moved out of the main board view.",
|
||||
promptSnippet: "Archive a done kb task (moves to archived column)",
|
||||
promptGuidelines: [
|
||||
"Use to clean up old completed tasks from the done column",
|
||||
"Only tasks in the 'done' column can be archived",
|
||||
"Archived tasks can be unarchived later if needed",
|
||||
],
|
||||
parameters: Type.Object({
|
||||
id: Type.String({ description: "Task ID to archive (e.g. KB-001). Must be in 'done' column." }),
|
||||
}),
|
||||
|
||||
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
||||
const store = await getStore(ctx.cwd);
|
||||
const task = await store.archiveTask(params.id);
|
||||
|
||||
return {
|
||||
content: [{ type: "text", text: `Archived ${task.id} → ${COLUMN_LABELS[task.column]}` }],
|
||||
details: { taskId: task.id, column: task.column },
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
// ── kb_task_unarchive ─────────────────────────────────────────────
|
||||
|
||||
pi.registerTool({
|
||||
name: "kb_task_unarchive",
|
||||
label: "KB: Unarchive Task",
|
||||
description:
|
||||
"Unarchive an archived task (move from archived → done). " +
|
||||
"Restores the task to the done column.",
|
||||
promptSnippet: "Unarchive a kb task (restores to done column)",
|
||||
promptGuidelines: [
|
||||
"Use to restore an archived task back to the done column",
|
||||
"Only tasks in the 'archived' column can be unarchived",
|
||||
],
|
||||
parameters: Type.Object({
|
||||
id: Type.String({ description: "Task ID to unarchive (e.g. KB-001). Must be in 'archived' column." }),
|
||||
}),
|
||||
|
||||
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
||||
const store = await getStore(ctx.cwd);
|
||||
const task = await store.unarchiveTask(params.id);
|
||||
|
||||
return {
|
||||
content: [{ type: "text", text: `Unarchived ${task.id} → ${COLUMN_LABELS[task.column]}` }],
|
||||
details: { taskId: task.id, column: task.column },
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
// ── kb_task_import_github ─────────────────────────────────────────
|
||||
|
||||
pi.registerTool({
|
||||
|
||||
Reference in New Issue
Block a user