@cardor/agent-harness-kit 1.2.5 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +340 -51
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/{mysql-LQXFOAQE.js → mysql-NXLYFD2H.js} +7 -2
- package/dist/mysql-NXLYFD2H.js.map +1 -0
- package/dist/{postgres-KQMJNRS2.js → postgres-6BXN7ZH4.js} +7 -2
- package/dist/postgres-6BXN7ZH4.js.map +1 -0
- package/dist/{sqlite-NXFRDHK6.js → sqlite-M65L55DA.js} +7 -2
- package/dist/sqlite-M65L55DA.js.map +1 -0
- package/package.json +1 -1
- package/dist/mysql-LQXFOAQE.js.map +0 -1
- package/dist/postgres-KQMJNRS2.js.map +0 -1
- package/dist/sqlite-NXFRDHK6.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -687,19 +687,19 @@ async function runBuild(cwd2, opts) {
|
|
|
687
687
|
}
|
|
688
688
|
}
|
|
689
689
|
async function buildOnce(cwd2) {
|
|
690
|
-
const
|
|
691
|
-
|
|
690
|
+
const spinner6 = p.spinner();
|
|
691
|
+
spinner6.start("Loading config...");
|
|
692
692
|
try {
|
|
693
693
|
const config = await loadConfig(cwd2);
|
|
694
|
-
|
|
694
|
+
spinner6.message("Rebuilding files...");
|
|
695
695
|
const materializer = getMaterializer(config.provider);
|
|
696
696
|
await materializer.build(config, cwd2);
|
|
697
|
-
|
|
697
|
+
spinner6.stop(pc.green("Build complete"));
|
|
698
698
|
p.log.success("AGENTS.md");
|
|
699
699
|
p.log.success(`Agent definitions (${config.provider})`);
|
|
700
700
|
p.log.success("MCP config");
|
|
701
701
|
} catch (err) {
|
|
702
|
-
|
|
702
|
+
spinner6.stop(pc.red("Build failed"));
|
|
703
703
|
p.log.error(err instanceof Error ? err.message : String(err));
|
|
704
704
|
process.exit(1);
|
|
705
705
|
}
|
|
@@ -757,7 +757,8 @@ function startDashboardServer(db, dbPath, staticPath, port) {
|
|
|
757
757
|
});
|
|
758
758
|
app.get("/api/tasks", async (c) => {
|
|
759
759
|
await db.reconnect();
|
|
760
|
-
|
|
760
|
+
const includeArchived = c.req.query("includeArchived") === "true";
|
|
761
|
+
return c.json(await tasks.getAllWithAcceptanceCounts(includeArchived));
|
|
761
762
|
});
|
|
762
763
|
app.get("/api/tasks/:id", async (c) => {
|
|
763
764
|
await db.reconnect();
|
|
@@ -768,6 +769,39 @@ function startDashboardServer(db, dbPath, staticPath, port) {
|
|
|
768
769
|
const taskActions = await actions.getWithDetails(id);
|
|
769
770
|
return c.json({ ...task2, acceptance, actions: taskActions });
|
|
770
771
|
});
|
|
772
|
+
app.patch("/api/tasks/:id", async (c) => {
|
|
773
|
+
const id = parseInt(c.req.param("id"));
|
|
774
|
+
const task2 = await tasks.getById(id);
|
|
775
|
+
if (!task2) return c.json({ error: "Not found" }, 404);
|
|
776
|
+
const body = await c.req.json();
|
|
777
|
+
const updateParams = {};
|
|
778
|
+
if (body.title !== void 0) updateParams.title = body.title;
|
|
779
|
+
if (body.description !== void 0) updateParams.description = body.description;
|
|
780
|
+
await db.updateTask(id, updateParams);
|
|
781
|
+
if (body.acceptance !== void 0 && Array.isArray(body.acceptance)) {
|
|
782
|
+
await db.updateTaskAcceptance(id, body.acceptance.map((a) => a.trim()).filter(Boolean));
|
|
783
|
+
}
|
|
784
|
+
const updated = await tasks.getById(id);
|
|
785
|
+
const acceptance = await tasks.getAcceptance(id);
|
|
786
|
+
const taskActions = await actions.getWithDetails(id);
|
|
787
|
+
return c.json({ ...updated, acceptance, actions: taskActions });
|
|
788
|
+
});
|
|
789
|
+
app.patch("/api/tasks/:id/archive", async (c) => {
|
|
790
|
+
await db.reconnect();
|
|
791
|
+
const id = parseInt(c.req.param("id"));
|
|
792
|
+
const task2 = await tasks.getById(id);
|
|
793
|
+
if (!task2) return c.json({ error: "Not found" }, 404);
|
|
794
|
+
const updated = await db.archiveTask(id);
|
|
795
|
+
return c.json(updated);
|
|
796
|
+
});
|
|
797
|
+
app.patch("/api/tasks/:id/unarchive", async (c) => {
|
|
798
|
+
await db.reconnect();
|
|
799
|
+
const id = parseInt(c.req.param("id"));
|
|
800
|
+
const task2 = await tasks.getById(id);
|
|
801
|
+
if (!task2) return c.json({ error: "Not found" }, 404);
|
|
802
|
+
const updated = await db.unarchiveTask(id);
|
|
803
|
+
return c.json(updated);
|
|
804
|
+
});
|
|
771
805
|
app.get("/api/tools/top", async (c) => {
|
|
772
806
|
await db.reconnect();
|
|
773
807
|
const limit = parseInt(c.req.query("limit") ?? "20");
|
|
@@ -1074,22 +1108,36 @@ var TaskRepository = class {
|
|
|
1074
1108
|
);
|
|
1075
1109
|
}
|
|
1076
1110
|
}
|
|
1077
|
-
async getAll(status) {
|
|
1111
|
+
async getAll(status, includeArchived = false) {
|
|
1112
|
+
let sql = `SELECT * FROM tasks`;
|
|
1113
|
+
const params = [];
|
|
1114
|
+
const conditions = [];
|
|
1115
|
+
if (!includeArchived) {
|
|
1116
|
+
conditions.push(`archived_at IS NULL`);
|
|
1117
|
+
}
|
|
1078
1118
|
if (status) {
|
|
1079
|
-
|
|
1119
|
+
conditions.push(`status = ?`);
|
|
1120
|
+
params.push(status);
|
|
1121
|
+
}
|
|
1122
|
+
if (conditions.length > 0) {
|
|
1123
|
+
sql += ` WHERE ${conditions.join(" AND ")}`;
|
|
1080
1124
|
}
|
|
1081
|
-
|
|
1125
|
+
sql += ` ORDER BY id`;
|
|
1126
|
+
return this.driver.query(sql, params);
|
|
1082
1127
|
}
|
|
1083
|
-
async getAllWithAcceptanceCounts() {
|
|
1084
|
-
|
|
1128
|
+
async getAllWithAcceptanceCounts(includeArchived = false) {
|
|
1129
|
+
let sql = `
|
|
1085
1130
|
SELECT t.*,
|
|
1086
1131
|
COUNT(ta.id) as acceptance_total,
|
|
1087
1132
|
COALESCE(SUM(ta.met), 0) as acceptance_met
|
|
1088
1133
|
FROM tasks t
|
|
1089
1134
|
LEFT JOIN task_acceptance ta ON ta.task_id = t.id
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1135
|
+
`;
|
|
1136
|
+
if (!includeArchived) {
|
|
1137
|
+
sql += ` WHERE t.archived_at IS NULL`;
|
|
1138
|
+
}
|
|
1139
|
+
sql += ` GROUP BY t.id ORDER BY t.id`;
|
|
1140
|
+
return this.driver.query(sql);
|
|
1093
1141
|
}
|
|
1094
1142
|
async getById(id) {
|
|
1095
1143
|
return this.driver.queryOne(`SELECT * FROM tasks WHERE id = ?`, [id]);
|
|
@@ -1118,6 +1166,46 @@ var TaskRepository = class {
|
|
|
1118
1166
|
await this.driver.exec(`UPDATE tasks SET status = ? WHERE id = ?`, [status, id]);
|
|
1119
1167
|
}
|
|
1120
1168
|
}
|
|
1169
|
+
async update(id, params) {
|
|
1170
|
+
const sets = [];
|
|
1171
|
+
const vals = [];
|
|
1172
|
+
if (params.title !== void 0) {
|
|
1173
|
+
sets.push("title = ?");
|
|
1174
|
+
vals.push(params.title);
|
|
1175
|
+
}
|
|
1176
|
+
if (params.description !== void 0) {
|
|
1177
|
+
sets.push("description = ?");
|
|
1178
|
+
vals.push(params.description);
|
|
1179
|
+
}
|
|
1180
|
+
if (params.slug !== void 0) {
|
|
1181
|
+
sets.push("slug = ?");
|
|
1182
|
+
vals.push(params.slug);
|
|
1183
|
+
}
|
|
1184
|
+
if (sets.length === 0) return;
|
|
1185
|
+
vals.push(id);
|
|
1186
|
+
await this.driver.exec(`UPDATE tasks SET ${sets.join(", ")} WHERE id = ?`, vals);
|
|
1187
|
+
}
|
|
1188
|
+
async replaceAcceptance(taskId, criteria) {
|
|
1189
|
+
await this.driver.exec(`DELETE FROM task_acceptance WHERE task_id = ?`, [taskId]);
|
|
1190
|
+
for (const criterion of criteria) {
|
|
1191
|
+
await this.driver.exec(
|
|
1192
|
+
`INSERT INTO task_acceptance (task_id, criterion) VALUES (?, ?)`,
|
|
1193
|
+
[taskId, criterion]
|
|
1194
|
+
);
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
async archive(id) {
|
|
1198
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1199
|
+
await this.driver.exec(`UPDATE tasks SET archived_at = ? WHERE id = ?`, [now, id]);
|
|
1200
|
+
}
|
|
1201
|
+
async unarchive(id) {
|
|
1202
|
+
await this.driver.exec(`UPDATE tasks SET archived_at = NULL WHERE id = ?`, [id]);
|
|
1203
|
+
}
|
|
1204
|
+
async getArchived() {
|
|
1205
|
+
return this.driver.query(
|
|
1206
|
+
`SELECT * FROM tasks WHERE archived_at IS NOT NULL ORDER BY archived_at DESC`
|
|
1207
|
+
);
|
|
1208
|
+
}
|
|
1121
1209
|
async claim(id, agent, now) {
|
|
1122
1210
|
return this.driver.exec(
|
|
1123
1211
|
`UPDATE tasks SET status = 'in_progress', assigned_to = ?, started_at = ? WHERE id = ? AND status = 'pending'`,
|
|
@@ -1129,7 +1217,7 @@ var TaskRepository = class {
|
|
|
1129
1217
|
}
|
|
1130
1218
|
async getStatusSummary() {
|
|
1131
1219
|
return this.driver.query(
|
|
1132
|
-
`SELECT status, COUNT(*) as total FROM tasks GROUP BY status`
|
|
1220
|
+
`SELECT status, COUNT(*) as total FROM tasks WHERE archived_at IS NULL GROUP BY status`
|
|
1133
1221
|
);
|
|
1134
1222
|
}
|
|
1135
1223
|
};
|
|
@@ -1161,8 +1249,8 @@ var HarnessDB = class {
|
|
|
1161
1249
|
await this.regenerateCurrentMd();
|
|
1162
1250
|
return await this.tasks.getById(taskId);
|
|
1163
1251
|
}
|
|
1164
|
-
async getTasks(status) {
|
|
1165
|
-
return this.tasks.getAll(status);
|
|
1252
|
+
async getTasks(status, includeArchived = false) {
|
|
1253
|
+
return this.tasks.getAll(status, includeArchived);
|
|
1166
1254
|
}
|
|
1167
1255
|
async getTaskById(id) {
|
|
1168
1256
|
return this.tasks.getById(id);
|
|
@@ -1202,6 +1290,28 @@ var HarnessDB = class {
|
|
|
1202
1290
|
async markAcceptanceMet(criterionId) {
|
|
1203
1291
|
return this.tasks.markAcceptanceMet(criterionId);
|
|
1204
1292
|
}
|
|
1293
|
+
async updateTask(id, params) {
|
|
1294
|
+
await this.tasks.update(id, params);
|
|
1295
|
+
await this.regenerateCurrentMd();
|
|
1296
|
+
return await this.tasks.getById(id);
|
|
1297
|
+
}
|
|
1298
|
+
async updateTaskAcceptance(taskId, criteria) {
|
|
1299
|
+
await this.tasks.replaceAcceptance(taskId, criteria);
|
|
1300
|
+
await this.regenerateCurrentMd();
|
|
1301
|
+
}
|
|
1302
|
+
async archiveTask(id) {
|
|
1303
|
+
await this.tasks.archive(id);
|
|
1304
|
+
await this.regenerateCurrentMd();
|
|
1305
|
+
return await this.tasks.getById(id);
|
|
1306
|
+
}
|
|
1307
|
+
async unarchiveTask(id) {
|
|
1308
|
+
await this.tasks.unarchive(id);
|
|
1309
|
+
await this.regenerateCurrentMd();
|
|
1310
|
+
return await this.tasks.getById(id);
|
|
1311
|
+
}
|
|
1312
|
+
async getArchivedTasks() {
|
|
1313
|
+
return this.tasks.getArchived();
|
|
1314
|
+
}
|
|
1205
1315
|
async getStatusSummary() {
|
|
1206
1316
|
return this.tasks.getStatusSummary();
|
|
1207
1317
|
}
|
|
@@ -1330,7 +1440,7 @@ var HarnessDB = class {
|
|
|
1330
1440
|
// ─── Export helpers ───────────────────────────────────────────────────────
|
|
1331
1441
|
async exportJson() {
|
|
1332
1442
|
return {
|
|
1333
|
-
tasks: await this.tasks.getAll(),
|
|
1443
|
+
tasks: await this.tasks.getAll(void 0, true),
|
|
1334
1444
|
actions: await this.actions.getAll(),
|
|
1335
1445
|
sections: await this.actions.getAllSections()
|
|
1336
1446
|
};
|
|
@@ -1356,7 +1466,7 @@ var HarnessDB = class {
|
|
|
1356
1466
|
return { added, skipped };
|
|
1357
1467
|
}
|
|
1358
1468
|
async writeFeatureList(cwd2) {
|
|
1359
|
-
const allTasks = await this.tasks.getAll();
|
|
1469
|
+
const allTasks = await this.tasks.getAll(void 0, true);
|
|
1360
1470
|
const list = await Promise.all(
|
|
1361
1471
|
allTasks.map(async (t) => ({
|
|
1362
1472
|
slug: t.slug,
|
|
@@ -1375,13 +1485,13 @@ async function openDB(config, cwd2) {
|
|
|
1375
1485
|
const dbConfig = config.database;
|
|
1376
1486
|
let driver;
|
|
1377
1487
|
if (dbConfig.type === "postgres") {
|
|
1378
|
-
const { PostgresDriver } = await import("./postgres-
|
|
1488
|
+
const { PostgresDriver } = await import("./postgres-6BXN7ZH4.js");
|
|
1379
1489
|
driver = new PostgresDriver(dbConfig);
|
|
1380
1490
|
} else if (dbConfig.type === "mysql") {
|
|
1381
|
-
const { MySQLDriver } = await import("./mysql-
|
|
1491
|
+
const { MySQLDriver } = await import("./mysql-NXLYFD2H.js");
|
|
1382
1492
|
driver = new MySQLDriver(dbConfig);
|
|
1383
1493
|
} else {
|
|
1384
|
-
const { SQLiteDriver } = await import("./sqlite-
|
|
1494
|
+
const { SQLiteDriver } = await import("./sqlite-M65L55DA.js");
|
|
1385
1495
|
if (dbConfig.type !== "sqlite") {
|
|
1386
1496
|
throw new Error("Invalid database type");
|
|
1387
1497
|
}
|
|
@@ -1814,8 +1924,8 @@ async function runInit(cwd2, flags) {
|
|
|
1814
1924
|
}
|
|
1815
1925
|
firstTask = { title: taskTitle, description: taskDesc, acceptance };
|
|
1816
1926
|
}
|
|
1817
|
-
const
|
|
1818
|
-
|
|
1927
|
+
const spinner6 = p3.spinner();
|
|
1928
|
+
spinner6.start("Scaffolding...");
|
|
1819
1929
|
try {
|
|
1820
1930
|
const config = applyConfigDefaults({ name, description, provider, docsPath, tasksAdapter });
|
|
1821
1931
|
const materializer = getMaterializer(provider);
|
|
@@ -1849,9 +1959,9 @@ async function runInit(cwd2, flags) {
|
|
|
1849
1959
|
});
|
|
1850
1960
|
}
|
|
1851
1961
|
await db.close();
|
|
1852
|
-
|
|
1962
|
+
spinner6.stop("");
|
|
1853
1963
|
} catch (err) {
|
|
1854
|
-
|
|
1964
|
+
spinner6.stop("Failed");
|
|
1855
1965
|
p3.log.error(err instanceof Error ? err.message : String(err));
|
|
1856
1966
|
throw err;
|
|
1857
1967
|
}
|
|
@@ -1912,16 +2022,16 @@ async function runMigrate(cwd2, opts) {
|
|
|
1912
2022
|
console.log(pc7.dim(`Already on ${target} \u2014 nothing to migrate.`));
|
|
1913
2023
|
return;
|
|
1914
2024
|
}
|
|
1915
|
-
const
|
|
1916
|
-
|
|
2025
|
+
const spinner6 = p4.spinner();
|
|
2026
|
+
spinner6.start(`Migrating from ${config.provider} to ${target}...`);
|
|
1917
2027
|
try {
|
|
1918
2028
|
const targetMaterializer = getMaterializer(target);
|
|
1919
2029
|
await targetMaterializer.build(config, cwd2);
|
|
1920
|
-
|
|
2030
|
+
spinner6.stop(pc7.green(`Migrated to ${target}`));
|
|
1921
2031
|
p4.log.warn(`Update agent-harness-kit.config.ts: set provider: '${target}'`);
|
|
1922
2032
|
p4.log.warn(`Then run: ahk build`);
|
|
1923
2033
|
} catch (err) {
|
|
1924
|
-
|
|
2034
|
+
spinner6.stop(pc7.red("Migration failed"));
|
|
1925
2035
|
p4.log.error(err instanceof Error ? err.message : String(err));
|
|
1926
2036
|
process.exit(1);
|
|
1927
2037
|
}
|
|
@@ -2129,7 +2239,7 @@ var TOOLS = [
|
|
|
2129
2239
|
},
|
|
2130
2240
|
{
|
|
2131
2241
|
name: "tasks.get",
|
|
2132
|
-
description: "List tasks, optionally filtered by status.",
|
|
2242
|
+
description: "List tasks, optionally filtered by status. Excludes archived tasks by default.",
|
|
2133
2243
|
inputSchema: {
|
|
2134
2244
|
type: "object",
|
|
2135
2245
|
properties: {
|
|
@@ -2137,6 +2247,10 @@ var TOOLS = [
|
|
|
2137
2247
|
type: "string",
|
|
2138
2248
|
enum: ["pending", "in_progress", "done", "blocked"],
|
|
2139
2249
|
description: "Filter by status (omit for all tasks)"
|
|
2250
|
+
},
|
|
2251
|
+
includeArchived: {
|
|
2252
|
+
type: "boolean",
|
|
2253
|
+
description: "If true, include archived tasks in results"
|
|
2140
2254
|
}
|
|
2141
2255
|
}
|
|
2142
2256
|
}
|
|
@@ -2239,6 +2353,46 @@ var TOOLS = [
|
|
|
2239
2353
|
},
|
|
2240
2354
|
required: ["actionId", "toolName"]
|
|
2241
2355
|
}
|
|
2356
|
+
},
|
|
2357
|
+
{
|
|
2358
|
+
name: "tasks.edit",
|
|
2359
|
+
description: "Edit an existing task (title, description, acceptance criteria). Omitted fields keep their current values.",
|
|
2360
|
+
inputSchema: {
|
|
2361
|
+
type: "object",
|
|
2362
|
+
properties: {
|
|
2363
|
+
id: { type: "number", description: "Task ID to edit" },
|
|
2364
|
+
title: { type: "string", description: "New title (optional)" },
|
|
2365
|
+
description: { type: "string", description: "New description (optional, null to clear)" },
|
|
2366
|
+
acceptance: {
|
|
2367
|
+
type: "array",
|
|
2368
|
+
items: { type: "string" },
|
|
2369
|
+
description: "New acceptance criteria list (optional, null to keep existing)"
|
|
2370
|
+
}
|
|
2371
|
+
},
|
|
2372
|
+
required: ["id"]
|
|
2373
|
+
}
|
|
2374
|
+
},
|
|
2375
|
+
{
|
|
2376
|
+
name: "tasks.archive",
|
|
2377
|
+
description: "Archive a task. Archived tasks are hidden from default views (CLI and dashboard).",
|
|
2378
|
+
inputSchema: {
|
|
2379
|
+
type: "object",
|
|
2380
|
+
properties: {
|
|
2381
|
+
id: { type: "number", description: "Task ID to archive" }
|
|
2382
|
+
},
|
|
2383
|
+
required: ["id"]
|
|
2384
|
+
}
|
|
2385
|
+
},
|
|
2386
|
+
{
|
|
2387
|
+
name: "tasks.unarchive",
|
|
2388
|
+
description: "Unarchive a previously archived task, restoring it to default views.",
|
|
2389
|
+
inputSchema: {
|
|
2390
|
+
type: "object",
|
|
2391
|
+
properties: {
|
|
2392
|
+
id: { type: "number", description: "Task ID to unarchive" }
|
|
2393
|
+
},
|
|
2394
|
+
required: ["id"]
|
|
2395
|
+
}
|
|
2242
2396
|
}
|
|
2243
2397
|
];
|
|
2244
2398
|
async function startMcpServer(config, cwd2) {
|
|
@@ -2296,7 +2450,8 @@ async function dispatch(name, args, db, docsPath) {
|
|
|
2296
2450
|
}
|
|
2297
2451
|
case "tasks.get": {
|
|
2298
2452
|
const status = args["status"];
|
|
2299
|
-
const
|
|
2453
|
+
const includeArchived = args["includeArchived"];
|
|
2454
|
+
const tasks = status ? await db.getTasks(status, includeArchived ?? false) : await db.getTasks(void 0, includeArchived ?? false);
|
|
2300
2455
|
return ok(JSON.stringify(tasks, null, 2));
|
|
2301
2456
|
}
|
|
2302
2457
|
case "tasks.claim": {
|
|
@@ -2351,6 +2506,30 @@ async function dispatch(name, args, db, docsPath) {
|
|
|
2351
2506
|
await db.recordTool(actionId, toolName, argsJson, resultSummary);
|
|
2352
2507
|
return ok(JSON.stringify({ actionId, toolName, recorded: true }));
|
|
2353
2508
|
}
|
|
2509
|
+
case "tasks.edit": {
|
|
2510
|
+
const id = num(args, "id");
|
|
2511
|
+
const title = args["title"];
|
|
2512
|
+
const description = args["description"];
|
|
2513
|
+
const acceptance = args["acceptance"];
|
|
2514
|
+
const task2 = await db.getTaskById(id);
|
|
2515
|
+
if (!task2) return ok(JSON.stringify({ error: "Task not found", taskId: id }), true);
|
|
2516
|
+
await db.updateTask(id, { title, description: description !== void 0 ? description : void 0 });
|
|
2517
|
+
if (acceptance !== void 0 && acceptance !== null) {
|
|
2518
|
+
await db.updateTaskAcceptance(id, acceptance);
|
|
2519
|
+
}
|
|
2520
|
+
const updated = await db.getTaskById(id);
|
|
2521
|
+
return ok(JSON.stringify(updated));
|
|
2522
|
+
}
|
|
2523
|
+
case "tasks.archive": {
|
|
2524
|
+
const id = num(args, "id");
|
|
2525
|
+
const task2 = await db.archiveTask(id);
|
|
2526
|
+
return ok(JSON.stringify(task2));
|
|
2527
|
+
}
|
|
2528
|
+
case "tasks.unarchive": {
|
|
2529
|
+
const id = num(args, "id");
|
|
2530
|
+
const task2 = await db.unarchiveTask(id);
|
|
2531
|
+
return ok(JSON.stringify(task2));
|
|
2532
|
+
}
|
|
2354
2533
|
default:
|
|
2355
2534
|
return ok(`Unknown tool: ${name}`, true);
|
|
2356
2535
|
}
|
|
@@ -2396,8 +2575,8 @@ function collectMarkdownFiles(dir) {
|
|
|
2396
2575
|
}
|
|
2397
2576
|
return files;
|
|
2398
2577
|
}
|
|
2399
|
-
function ok(
|
|
2400
|
-
return { content: [{ type: "text", text:
|
|
2578
|
+
function ok(text4, isError = false) {
|
|
2579
|
+
return { content: [{ type: "text", text: text4 }], isError };
|
|
2401
2580
|
}
|
|
2402
2581
|
function str(args, key) {
|
|
2403
2582
|
const v4 = args[key];
|
|
@@ -2444,7 +2623,8 @@ async function runStatus(cwd2, opts) {
|
|
|
2444
2623
|
acceptance: await db.getTaskAcceptance(t.id)
|
|
2445
2624
|
}))
|
|
2446
2625
|
);
|
|
2447
|
-
|
|
2626
|
+
const archivedCount = (await db.getArchivedTasks()).length;
|
|
2627
|
+
console.log(JSON.stringify({ tasks: actions, summary, archivedCount }, null, 2));
|
|
2448
2628
|
return;
|
|
2449
2629
|
}
|
|
2450
2630
|
if (tasks.length === 0) {
|
|
@@ -2485,6 +2665,10 @@ async function runStatus(cwd2, opts) {
|
|
|
2485
2665
|
return `${fn(s.status)}: ${s.total}`;
|
|
2486
2666
|
});
|
|
2487
2667
|
console.log(pc9.dim("Tasks \u2014 ") + parts.join(pc9.dim(" | ")));
|
|
2668
|
+
const archivedTasks = await db.getArchivedTasks();
|
|
2669
|
+
if (archivedTasks.length > 0) {
|
|
2670
|
+
console.log(pc9.dim(`${archivedTasks.length} archived (use \`ahk task list --archived\` to view)`));
|
|
2671
|
+
}
|
|
2488
2672
|
} finally {
|
|
2489
2673
|
await db.close();
|
|
2490
2674
|
}
|
|
@@ -2581,8 +2765,8 @@ async function runTaskAdd(cwd2) {
|
|
|
2581
2765
|
if (p6.isCancel(val) || !val || !val.trim()) break;
|
|
2582
2766
|
acceptance.push(val.trim());
|
|
2583
2767
|
}
|
|
2584
|
-
const
|
|
2585
|
-
|
|
2768
|
+
const spinner6 = p6.spinner();
|
|
2769
|
+
spinner6.start("Saving...");
|
|
2586
2770
|
try {
|
|
2587
2771
|
const config = await loadConfig(cwd2);
|
|
2588
2772
|
const db = await openDB(config, cwd2);
|
|
@@ -2590,11 +2774,11 @@ async function runTaskAdd(cwd2) {
|
|
|
2590
2774
|
const task2 = await db.addTask({ slug, title, description: description || void 0, acceptance });
|
|
2591
2775
|
await db.writeFeatureList(cwd2);
|
|
2592
2776
|
await db.close();
|
|
2593
|
-
|
|
2777
|
+
spinner6.stop("");
|
|
2594
2778
|
console.log(pc11.green(`\u2713 Task #${task2.id} added \u2014 ${task2.slug} (pending)`));
|
|
2595
2779
|
console.log(pc11.cyan("\u2192") + " " + pc11.cyan("ahk status") + " to see all tasks");
|
|
2596
2780
|
} catch (err) {
|
|
2597
|
-
|
|
2781
|
+
spinner6.stop(pc11.red("Failed"));
|
|
2598
2782
|
p6.log.error(err instanceof Error ? err.message : String(err));
|
|
2599
2783
|
process.exit(1);
|
|
2600
2784
|
}
|
|
@@ -2640,14 +2824,107 @@ async function runTaskDone(cwd2, idOrSlug) {
|
|
|
2640
2824
|
}
|
|
2641
2825
|
}
|
|
2642
2826
|
|
|
2827
|
+
// src/commands/task/edit.ts
|
|
2828
|
+
import * as p7 from "@clack/prompts";
|
|
2829
|
+
import pc13 from "picocolors";
|
|
2830
|
+
async function runTaskEdit(cwd2) {
|
|
2831
|
+
p7.intro(pc13.bold("agent-harness-kit \u2014 edit task"));
|
|
2832
|
+
const config = await loadConfig(cwd2);
|
|
2833
|
+
const db = await openDB(config, cwd2);
|
|
2834
|
+
try {
|
|
2835
|
+
const allTasks = await db.getTasks();
|
|
2836
|
+
const activeTasks = allTasks.filter((t) => t.status !== "done");
|
|
2837
|
+
if (activeTasks.length === 0) {
|
|
2838
|
+
p7.log.error("No active tasks to edit.");
|
|
2839
|
+
return;
|
|
2840
|
+
}
|
|
2841
|
+
const taskId = await p7.select({
|
|
2842
|
+
message: "Select a task to edit",
|
|
2843
|
+
options: activeTasks.map((t) => ({
|
|
2844
|
+
label: `#${t.id} \u2014 ${t.title} (${t.slug})`,
|
|
2845
|
+
value: t.id
|
|
2846
|
+
}))
|
|
2847
|
+
});
|
|
2848
|
+
if (p7.isCancel(taskId)) {
|
|
2849
|
+
p7.cancel("Cancelled.");
|
|
2850
|
+
process.exit(0);
|
|
2851
|
+
}
|
|
2852
|
+
const task2 = await db.getTaskById(taskId);
|
|
2853
|
+
if (!task2) {
|
|
2854
|
+
p7.log.error("Task not found");
|
|
2855
|
+
process.exit(1);
|
|
2856
|
+
}
|
|
2857
|
+
const title = await p7.text({
|
|
2858
|
+
message: "Title",
|
|
2859
|
+
initialValue: task2.title
|
|
2860
|
+
});
|
|
2861
|
+
if (p7.isCancel(title)) {
|
|
2862
|
+
p7.cancel("Cancelled.");
|
|
2863
|
+
process.exit(0);
|
|
2864
|
+
}
|
|
2865
|
+
const description = await p7.text({
|
|
2866
|
+
message: "Description (what and why)",
|
|
2867
|
+
initialValue: task2.description ?? ""
|
|
2868
|
+
});
|
|
2869
|
+
if (p7.isCancel(description)) {
|
|
2870
|
+
p7.cancel("Cancelled.");
|
|
2871
|
+
process.exit(0);
|
|
2872
|
+
}
|
|
2873
|
+
const currentAcceptance = await db.getTaskAcceptance(task2.id);
|
|
2874
|
+
const newAcceptance = [];
|
|
2875
|
+
p7.log.info("Acceptance criteria \u2014 edit each, empty to delete. Add new ones at the end.");
|
|
2876
|
+
for (let i = 0; i < currentAcceptance.length; i++) {
|
|
2877
|
+
const ac = currentAcceptance[i];
|
|
2878
|
+
const val = await p7.text({
|
|
2879
|
+
message: `#${i + 1}/${currentAcceptance.length}`,
|
|
2880
|
+
initialValue: ac.criterion,
|
|
2881
|
+
defaultValue: ""
|
|
2882
|
+
});
|
|
2883
|
+
if (p7.isCancel(val)) {
|
|
2884
|
+
p7.cancel("Cancelled.");
|
|
2885
|
+
process.exit(0);
|
|
2886
|
+
}
|
|
2887
|
+
const trimmed = val.trim();
|
|
2888
|
+
if (trimmed !== "") {
|
|
2889
|
+
newAcceptance.push(trimmed);
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
while (true) {
|
|
2893
|
+
const val = await p7.text({ message: "New acceptance criterion", placeholder: "(press Enter to finish)" });
|
|
2894
|
+
if (p7.isCancel(val) || !val || !val.trim()) break;
|
|
2895
|
+
newAcceptance.push(val.trim());
|
|
2896
|
+
}
|
|
2897
|
+
const spinner6 = p7.spinner();
|
|
2898
|
+
spinner6.start("Saving...");
|
|
2899
|
+
try {
|
|
2900
|
+
const newSlug = slugify(title);
|
|
2901
|
+
await db.updateTask(task2.id, {
|
|
2902
|
+
title,
|
|
2903
|
+
description: description.trim() || void 0,
|
|
2904
|
+
slug: newSlug
|
|
2905
|
+
});
|
|
2906
|
+
await db.updateTaskAcceptance(task2.id, newAcceptance);
|
|
2907
|
+
await db.writeFeatureList(cwd2);
|
|
2908
|
+
spinner6.stop("");
|
|
2909
|
+
console.log(pc13.green(`\u2713 Task #${task2.id} updated \u2014 ${newSlug}`));
|
|
2910
|
+
} catch (err) {
|
|
2911
|
+
spinner6.stop(pc13.red("Failed"));
|
|
2912
|
+
p7.log.error(err instanceof Error ? err.message : String(err));
|
|
2913
|
+
process.exit(1);
|
|
2914
|
+
}
|
|
2915
|
+
} finally {
|
|
2916
|
+
await db.close();
|
|
2917
|
+
}
|
|
2918
|
+
}
|
|
2919
|
+
|
|
2643
2920
|
// src/commands/task/list.ts
|
|
2644
2921
|
import Table2 from "cli-table3";
|
|
2645
|
-
import
|
|
2922
|
+
import pc14 from "picocolors";
|
|
2646
2923
|
var STATUS_COLOR2 = {
|
|
2647
|
-
pending: (s) =>
|
|
2648
|
-
in_progress: (s) =>
|
|
2649
|
-
done: (s) =>
|
|
2650
|
-
blocked: (s) =>
|
|
2924
|
+
pending: (s) => pc14.dim(s),
|
|
2925
|
+
in_progress: (s) => pc14.cyan(s),
|
|
2926
|
+
done: (s) => pc14.green(s),
|
|
2927
|
+
blocked: (s) => pc14.red(s)
|
|
2651
2928
|
};
|
|
2652
2929
|
async function runTaskList(cwd2, opts) {
|
|
2653
2930
|
const config = await loadConfig(cwd2);
|
|
@@ -2655,17 +2932,20 @@ async function runTaskList(cwd2, opts) {
|
|
|
2655
2932
|
try {
|
|
2656
2933
|
const validStatuses = ["pending", "in_progress", "done", "blocked"];
|
|
2657
2934
|
const filterStatus = opts.status && validStatuses.includes(opts.status) ? opts.status : void 0;
|
|
2658
|
-
const tasks = filterStatus ? await db.getTasks(filterStatus) : await db.getTasks();
|
|
2935
|
+
const tasks = opts.archived ? await db.getArchivedTasks() : filterStatus ? await db.getTasks(filterStatus, opts.includeArchived ?? false) : await db.getTasks(void 0, opts.includeArchived ?? false);
|
|
2659
2936
|
if (opts.json) {
|
|
2660
2937
|
console.log(JSON.stringify(tasks, null, 2));
|
|
2661
2938
|
return;
|
|
2662
2939
|
}
|
|
2663
2940
|
if (tasks.length === 0) {
|
|
2664
|
-
|
|
2941
|
+
let msg = "No tasks";
|
|
2942
|
+
if (filterStatus) msg += ` with status: ${filterStatus}`;
|
|
2943
|
+
if (opts.archived) msg += " (archived)";
|
|
2944
|
+
console.log(pc14.dim(msg + "."));
|
|
2665
2945
|
return;
|
|
2666
2946
|
}
|
|
2667
2947
|
const table = new Table2({
|
|
2668
|
-
head: ["ID", "Slug", "Title", "Status"].map((h) =>
|
|
2948
|
+
head: ["ID", "Slug", "Title", "Status"].map((h) => pc14.bold(h)),
|
|
2669
2949
|
style: { head: [], border: [] }
|
|
2670
2950
|
});
|
|
2671
2951
|
for (const t of tasks) {
|
|
@@ -2673,6 +2953,12 @@ async function runTaskList(cwd2, opts) {
|
|
|
2673
2953
|
table.push([String(t.id), t.slug, t.title.slice(0, 50), colorFn(t.status)]);
|
|
2674
2954
|
}
|
|
2675
2955
|
console.log(table.toString());
|
|
2956
|
+
if (!opts.archived && !opts.includeArchived) {
|
|
2957
|
+
const archivedTasks = await db.getArchivedTasks();
|
|
2958
|
+
if (archivedTasks.length > 0) {
|
|
2959
|
+
console.log(pc14.dim(`${archivedTasks.length} archived task${archivedTasks.length !== 1 ? "s" : ""} (use --archived to view)`));
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2676
2962
|
} finally {
|
|
2677
2963
|
await db.close();
|
|
2678
2964
|
}
|
|
@@ -2687,7 +2973,7 @@ var pkgPath = join16(dirname5(fileURLToPath3(import.meta.url)), "..", "package.j
|
|
|
2687
2973
|
var pkg = require2(pkgPath);
|
|
2688
2974
|
|
|
2689
2975
|
// src/core/update-check.ts
|
|
2690
|
-
import
|
|
2976
|
+
import pc15 from "picocolors";
|
|
2691
2977
|
var REGISTRY_URL = `https://registry.npmjs.org/${pkg.name}/latest`;
|
|
2692
2978
|
var TIMEOUT_MS = 2500;
|
|
2693
2979
|
function checkForUpdate(currentVersion) {
|
|
@@ -2705,8 +2991,8 @@ function checkForUpdate(currentVersion) {
|
|
|
2705
2991
|
}
|
|
2706
2992
|
function printUpdateMessage({ current, latest }) {
|
|
2707
2993
|
const lines = [
|
|
2708
|
-
` Update available ${
|
|
2709
|
-
` Run: ${
|
|
2994
|
+
` Update available ${pc15.dim(current)} \u2192 ${pc15.green(latest)} `,
|
|
2995
|
+
` Run: ${pc15.cyan(`pnpm i ${pkg.name}@${latest}`)} `
|
|
2710
2996
|
];
|
|
2711
2997
|
drawBox(lines);
|
|
2712
2998
|
}
|
|
@@ -2746,12 +3032,15 @@ var task = program.command("task").description("Manage tasks");
|
|
|
2746
3032
|
task.command("add").description("Add a task interactively").action(async () => {
|
|
2747
3033
|
await runTaskAdd(cwd);
|
|
2748
3034
|
});
|
|
2749
|
-
task.command("list").description("List tasks").option("--status <status>", "Filter by status: pending | in_progress | done | blocked").option("--json", "Output as JSON").action(async (opts) => {
|
|
3035
|
+
task.command("list").description("List tasks").option("--status <status>", "Filter by status: pending | in_progress | done | blocked").option("--archived", "Show only archived tasks").option("--include-archived", "Include archived tasks in the list").option("--json", "Output as JSON").action(async (opts) => {
|
|
2750
3036
|
await runTaskList(cwd, opts);
|
|
2751
3037
|
});
|
|
2752
3038
|
task.command("done <id|slug>").description("Mark a task as done").action(async (idOrSlug) => {
|
|
2753
3039
|
await runTaskDone(cwd, idOrSlug);
|
|
2754
3040
|
});
|
|
3041
|
+
task.command("edit").description("Edit a task interactively").action(async () => {
|
|
3042
|
+
await runTaskEdit(cwd);
|
|
3043
|
+
});
|
|
2755
3044
|
program.command("dashboard").description("Open web dashboard to visualize harness data").option("-p, --port <port>", "Port to listen on", "4242").option("--no-open", "Do not open browser automatically").action(async (opts) => {
|
|
2756
3045
|
await runDashboard(cwd, { port: parseInt(opts.port), open: opts.open });
|
|
2757
3046
|
});
|