@cardor/agent-harness-kit 1.2.4 → 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 +352 -51
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/{mysql-IMDWH2CU.js → mysql-NXLYFD2H.js} +11 -2
- package/dist/mysql-NXLYFD2H.js.map +1 -0
- package/dist/{postgres-TYINLEAT.js → postgres-6BXN7ZH4.js} +11 -2
- package/dist/postgres-6BXN7ZH4.js.map +1 -0
- package/dist/{sqlite-5R6LB3RX.js → sqlite-M65L55DA.js} +15 -2
- package/dist/sqlite-M65L55DA.js.map +1 -0
- package/package.json +1 -1
- package/dist/mysql-IMDWH2CU.js.map +0 -1
- package/dist/postgres-TYINLEAT.js.map +0 -1
- package/dist/sqlite-5R6LB3RX.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
|
}
|
|
@@ -745,6 +745,7 @@ function startDashboardServer(db, dbPath, staticPath, port) {
|
|
|
745
745
|
c.res.headers.set("Access-Control-Allow-Origin", "*");
|
|
746
746
|
});
|
|
747
747
|
app.get("/api/stats", async (c) => {
|
|
748
|
+
await db.reconnect();
|
|
748
749
|
const summary = await tasks.getStatusSummary();
|
|
749
750
|
const byStatus = { pending: 0, in_progress: 0, done: 0, blocked: 0 };
|
|
750
751
|
for (const { status, total } of summary) byStatus[status] = total;
|
|
@@ -755,9 +756,12 @@ function startDashboardServer(db, dbPath, staticPath, port) {
|
|
|
755
756
|
return c.json({ ok: true });
|
|
756
757
|
});
|
|
757
758
|
app.get("/api/tasks", async (c) => {
|
|
758
|
-
|
|
759
|
+
await db.reconnect();
|
|
760
|
+
const includeArchived = c.req.query("includeArchived") === "true";
|
|
761
|
+
return c.json(await tasks.getAllWithAcceptanceCounts(includeArchived));
|
|
759
762
|
});
|
|
760
763
|
app.get("/api/tasks/:id", async (c) => {
|
|
764
|
+
await db.reconnect();
|
|
761
765
|
const id = parseInt(c.req.param("id"));
|
|
762
766
|
const task2 = await tasks.getById(id);
|
|
763
767
|
if (!task2) return c.json({ error: "Not found" }, 404);
|
|
@@ -765,26 +769,65 @@ function startDashboardServer(db, dbPath, staticPath, port) {
|
|
|
765
769
|
const taskActions = await actions.getWithDetails(id);
|
|
766
770
|
return c.json({ ...task2, acceptance, actions: taskActions });
|
|
767
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
|
+
});
|
|
768
805
|
app.get("/api/tools/top", async (c) => {
|
|
806
|
+
await db.reconnect();
|
|
769
807
|
const limit = parseInt(c.req.query("limit") ?? "20");
|
|
770
808
|
return c.json(await actions.getTopTools(limit));
|
|
771
809
|
});
|
|
772
810
|
app.get("/api/tools/recent", async (c) => {
|
|
811
|
+
await db.reconnect();
|
|
773
812
|
const limit = parseInt(c.req.query("limit") ?? "50");
|
|
774
813
|
return c.json(await stats.getRecentTools(limit));
|
|
775
814
|
});
|
|
776
815
|
app.get("/api/files/top", async (c) => {
|
|
816
|
+
await db.reconnect();
|
|
777
817
|
const limit = parseInt(c.req.query("limit") ?? "20");
|
|
778
818
|
return c.json(await stats.getTopFiles(limit));
|
|
779
819
|
});
|
|
780
820
|
app.get("/api/files/recent", async (c) => {
|
|
821
|
+
await db.reconnect();
|
|
781
822
|
const limit = parseInt(c.req.query("limit") ?? "50");
|
|
782
823
|
return c.json(await stats.getRecentFiles(limit));
|
|
783
824
|
});
|
|
784
825
|
app.get("/api/agents/stats", async (c) => {
|
|
826
|
+
await db.reconnect();
|
|
785
827
|
return c.json(await stats.getAgentStats());
|
|
786
828
|
});
|
|
787
829
|
app.get("/api/timeline", async (c) => {
|
|
830
|
+
await db.reconnect();
|
|
788
831
|
const limit = parseInt(c.req.query("limit") ?? "50");
|
|
789
832
|
return c.json(await stats.getTimeline(limit));
|
|
790
833
|
});
|
|
@@ -1065,22 +1108,36 @@ var TaskRepository = class {
|
|
|
1065
1108
|
);
|
|
1066
1109
|
}
|
|
1067
1110
|
}
|
|
1068
|
-
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
|
+
}
|
|
1069
1118
|
if (status) {
|
|
1070
|
-
|
|
1119
|
+
conditions.push(`status = ?`);
|
|
1120
|
+
params.push(status);
|
|
1121
|
+
}
|
|
1122
|
+
if (conditions.length > 0) {
|
|
1123
|
+
sql += ` WHERE ${conditions.join(" AND ")}`;
|
|
1071
1124
|
}
|
|
1072
|
-
|
|
1125
|
+
sql += ` ORDER BY id`;
|
|
1126
|
+
return this.driver.query(sql, params);
|
|
1073
1127
|
}
|
|
1074
|
-
async getAllWithAcceptanceCounts() {
|
|
1075
|
-
|
|
1128
|
+
async getAllWithAcceptanceCounts(includeArchived = false) {
|
|
1129
|
+
let sql = `
|
|
1076
1130
|
SELECT t.*,
|
|
1077
1131
|
COUNT(ta.id) as acceptance_total,
|
|
1078
1132
|
COALESCE(SUM(ta.met), 0) as acceptance_met
|
|
1079
1133
|
FROM tasks t
|
|
1080
1134
|
LEFT JOIN task_acceptance ta ON ta.task_id = t.id
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
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);
|
|
1084
1141
|
}
|
|
1085
1142
|
async getById(id) {
|
|
1086
1143
|
return this.driver.queryOne(`SELECT * FROM tasks WHERE id = ?`, [id]);
|
|
@@ -1109,6 +1166,46 @@ var TaskRepository = class {
|
|
|
1109
1166
|
await this.driver.exec(`UPDATE tasks SET status = ? WHERE id = ?`, [status, id]);
|
|
1110
1167
|
}
|
|
1111
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
|
+
}
|
|
1112
1209
|
async claim(id, agent, now) {
|
|
1113
1210
|
return this.driver.exec(
|
|
1114
1211
|
`UPDATE tasks SET status = 'in_progress', assigned_to = ?, started_at = ? WHERE id = ? AND status = 'pending'`,
|
|
@@ -1120,7 +1217,7 @@ var TaskRepository = class {
|
|
|
1120
1217
|
}
|
|
1121
1218
|
async getStatusSummary() {
|
|
1122
1219
|
return this.driver.query(
|
|
1123
|
-
`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`
|
|
1124
1221
|
);
|
|
1125
1222
|
}
|
|
1126
1223
|
};
|
|
@@ -1152,8 +1249,8 @@ var HarnessDB = class {
|
|
|
1152
1249
|
await this.regenerateCurrentMd();
|
|
1153
1250
|
return await this.tasks.getById(taskId);
|
|
1154
1251
|
}
|
|
1155
|
-
async getTasks(status) {
|
|
1156
|
-
return this.tasks.getAll(status);
|
|
1252
|
+
async getTasks(status, includeArchived = false) {
|
|
1253
|
+
return this.tasks.getAll(status, includeArchived);
|
|
1157
1254
|
}
|
|
1158
1255
|
async getTaskById(id) {
|
|
1159
1256
|
return this.tasks.getById(id);
|
|
@@ -1193,6 +1290,28 @@ var HarnessDB = class {
|
|
|
1193
1290
|
async markAcceptanceMet(criterionId) {
|
|
1194
1291
|
return this.tasks.markAcceptanceMet(criterionId);
|
|
1195
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
|
+
}
|
|
1196
1315
|
async getStatusSummary() {
|
|
1197
1316
|
return this.tasks.getStatusSummary();
|
|
1198
1317
|
}
|
|
@@ -1321,11 +1440,14 @@ var HarnessDB = class {
|
|
|
1321
1440
|
// ─── Export helpers ───────────────────────────────────────────────────────
|
|
1322
1441
|
async exportJson() {
|
|
1323
1442
|
return {
|
|
1324
|
-
tasks: await this.tasks.getAll(),
|
|
1443
|
+
tasks: await this.tasks.getAll(void 0, true),
|
|
1325
1444
|
actions: await this.actions.getAll(),
|
|
1326
1445
|
sections: await this.actions.getAllSections()
|
|
1327
1446
|
};
|
|
1328
1447
|
}
|
|
1448
|
+
async reconnect() {
|
|
1449
|
+
await this.driver.reconnect();
|
|
1450
|
+
}
|
|
1329
1451
|
async close() {
|
|
1330
1452
|
await this.driver.close();
|
|
1331
1453
|
}
|
|
@@ -1344,7 +1466,7 @@ var HarnessDB = class {
|
|
|
1344
1466
|
return { added, skipped };
|
|
1345
1467
|
}
|
|
1346
1468
|
async writeFeatureList(cwd2) {
|
|
1347
|
-
const allTasks = await this.tasks.getAll();
|
|
1469
|
+
const allTasks = await this.tasks.getAll(void 0, true);
|
|
1348
1470
|
const list = await Promise.all(
|
|
1349
1471
|
allTasks.map(async (t) => ({
|
|
1350
1472
|
slug: t.slug,
|
|
@@ -1363,13 +1485,13 @@ async function openDB(config, cwd2) {
|
|
|
1363
1485
|
const dbConfig = config.database;
|
|
1364
1486
|
let driver;
|
|
1365
1487
|
if (dbConfig.type === "postgres") {
|
|
1366
|
-
const { PostgresDriver } = await import("./postgres-
|
|
1488
|
+
const { PostgresDriver } = await import("./postgres-6BXN7ZH4.js");
|
|
1367
1489
|
driver = new PostgresDriver(dbConfig);
|
|
1368
1490
|
} else if (dbConfig.type === "mysql") {
|
|
1369
|
-
const { MySQLDriver } = await import("./mysql-
|
|
1491
|
+
const { MySQLDriver } = await import("./mysql-NXLYFD2H.js");
|
|
1370
1492
|
driver = new MySQLDriver(dbConfig);
|
|
1371
1493
|
} else {
|
|
1372
|
-
const { SQLiteDriver } = await import("./sqlite-
|
|
1494
|
+
const { SQLiteDriver } = await import("./sqlite-M65L55DA.js");
|
|
1373
1495
|
if (dbConfig.type !== "sqlite") {
|
|
1374
1496
|
throw new Error("Invalid database type");
|
|
1375
1497
|
}
|
|
@@ -1802,8 +1924,8 @@ async function runInit(cwd2, flags) {
|
|
|
1802
1924
|
}
|
|
1803
1925
|
firstTask = { title: taskTitle, description: taskDesc, acceptance };
|
|
1804
1926
|
}
|
|
1805
|
-
const
|
|
1806
|
-
|
|
1927
|
+
const spinner6 = p3.spinner();
|
|
1928
|
+
spinner6.start("Scaffolding...");
|
|
1807
1929
|
try {
|
|
1808
1930
|
const config = applyConfigDefaults({ name, description, provider, docsPath, tasksAdapter });
|
|
1809
1931
|
const materializer = getMaterializer(provider);
|
|
@@ -1837,9 +1959,9 @@ async function runInit(cwd2, flags) {
|
|
|
1837
1959
|
});
|
|
1838
1960
|
}
|
|
1839
1961
|
await db.close();
|
|
1840
|
-
|
|
1962
|
+
spinner6.stop("");
|
|
1841
1963
|
} catch (err) {
|
|
1842
|
-
|
|
1964
|
+
spinner6.stop("Failed");
|
|
1843
1965
|
p3.log.error(err instanceof Error ? err.message : String(err));
|
|
1844
1966
|
throw err;
|
|
1845
1967
|
}
|
|
@@ -1900,16 +2022,16 @@ async function runMigrate(cwd2, opts) {
|
|
|
1900
2022
|
console.log(pc7.dim(`Already on ${target} \u2014 nothing to migrate.`));
|
|
1901
2023
|
return;
|
|
1902
2024
|
}
|
|
1903
|
-
const
|
|
1904
|
-
|
|
2025
|
+
const spinner6 = p4.spinner();
|
|
2026
|
+
spinner6.start(`Migrating from ${config.provider} to ${target}...`);
|
|
1905
2027
|
try {
|
|
1906
2028
|
const targetMaterializer = getMaterializer(target);
|
|
1907
2029
|
await targetMaterializer.build(config, cwd2);
|
|
1908
|
-
|
|
2030
|
+
spinner6.stop(pc7.green(`Migrated to ${target}`));
|
|
1909
2031
|
p4.log.warn(`Update agent-harness-kit.config.ts: set provider: '${target}'`);
|
|
1910
2032
|
p4.log.warn(`Then run: ahk build`);
|
|
1911
2033
|
} catch (err) {
|
|
1912
|
-
|
|
2034
|
+
spinner6.stop(pc7.red("Migration failed"));
|
|
1913
2035
|
p4.log.error(err instanceof Error ? err.message : String(err));
|
|
1914
2036
|
process.exit(1);
|
|
1915
2037
|
}
|
|
@@ -2117,7 +2239,7 @@ var TOOLS = [
|
|
|
2117
2239
|
},
|
|
2118
2240
|
{
|
|
2119
2241
|
name: "tasks.get",
|
|
2120
|
-
description: "List tasks, optionally filtered by status.",
|
|
2242
|
+
description: "List tasks, optionally filtered by status. Excludes archived tasks by default.",
|
|
2121
2243
|
inputSchema: {
|
|
2122
2244
|
type: "object",
|
|
2123
2245
|
properties: {
|
|
@@ -2125,6 +2247,10 @@ var TOOLS = [
|
|
|
2125
2247
|
type: "string",
|
|
2126
2248
|
enum: ["pending", "in_progress", "done", "blocked"],
|
|
2127
2249
|
description: "Filter by status (omit for all tasks)"
|
|
2250
|
+
},
|
|
2251
|
+
includeArchived: {
|
|
2252
|
+
type: "boolean",
|
|
2253
|
+
description: "If true, include archived tasks in results"
|
|
2128
2254
|
}
|
|
2129
2255
|
}
|
|
2130
2256
|
}
|
|
@@ -2227,6 +2353,46 @@ var TOOLS = [
|
|
|
2227
2353
|
},
|
|
2228
2354
|
required: ["actionId", "toolName"]
|
|
2229
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
|
+
}
|
|
2230
2396
|
}
|
|
2231
2397
|
];
|
|
2232
2398
|
async function startMcpServer(config, cwd2) {
|
|
@@ -2284,7 +2450,8 @@ async function dispatch(name, args, db, docsPath) {
|
|
|
2284
2450
|
}
|
|
2285
2451
|
case "tasks.get": {
|
|
2286
2452
|
const status = args["status"];
|
|
2287
|
-
const
|
|
2453
|
+
const includeArchived = args["includeArchived"];
|
|
2454
|
+
const tasks = status ? await db.getTasks(status, includeArchived ?? false) : await db.getTasks(void 0, includeArchived ?? false);
|
|
2288
2455
|
return ok(JSON.stringify(tasks, null, 2));
|
|
2289
2456
|
}
|
|
2290
2457
|
case "tasks.claim": {
|
|
@@ -2339,6 +2506,30 @@ async function dispatch(name, args, db, docsPath) {
|
|
|
2339
2506
|
await db.recordTool(actionId, toolName, argsJson, resultSummary);
|
|
2340
2507
|
return ok(JSON.stringify({ actionId, toolName, recorded: true }));
|
|
2341
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
|
+
}
|
|
2342
2533
|
default:
|
|
2343
2534
|
return ok(`Unknown tool: ${name}`, true);
|
|
2344
2535
|
}
|
|
@@ -2384,8 +2575,8 @@ function collectMarkdownFiles(dir) {
|
|
|
2384
2575
|
}
|
|
2385
2576
|
return files;
|
|
2386
2577
|
}
|
|
2387
|
-
function ok(
|
|
2388
|
-
return { content: [{ type: "text", text:
|
|
2578
|
+
function ok(text4, isError = false) {
|
|
2579
|
+
return { content: [{ type: "text", text: text4 }], isError };
|
|
2389
2580
|
}
|
|
2390
2581
|
function str(args, key) {
|
|
2391
2582
|
const v4 = args[key];
|
|
@@ -2432,7 +2623,8 @@ async function runStatus(cwd2, opts) {
|
|
|
2432
2623
|
acceptance: await db.getTaskAcceptance(t.id)
|
|
2433
2624
|
}))
|
|
2434
2625
|
);
|
|
2435
|
-
|
|
2626
|
+
const archivedCount = (await db.getArchivedTasks()).length;
|
|
2627
|
+
console.log(JSON.stringify({ tasks: actions, summary, archivedCount }, null, 2));
|
|
2436
2628
|
return;
|
|
2437
2629
|
}
|
|
2438
2630
|
if (tasks.length === 0) {
|
|
@@ -2473,6 +2665,10 @@ async function runStatus(cwd2, opts) {
|
|
|
2473
2665
|
return `${fn(s.status)}: ${s.total}`;
|
|
2474
2666
|
});
|
|
2475
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
|
+
}
|
|
2476
2672
|
} finally {
|
|
2477
2673
|
await db.close();
|
|
2478
2674
|
}
|
|
@@ -2569,8 +2765,8 @@ async function runTaskAdd(cwd2) {
|
|
|
2569
2765
|
if (p6.isCancel(val) || !val || !val.trim()) break;
|
|
2570
2766
|
acceptance.push(val.trim());
|
|
2571
2767
|
}
|
|
2572
|
-
const
|
|
2573
|
-
|
|
2768
|
+
const spinner6 = p6.spinner();
|
|
2769
|
+
spinner6.start("Saving...");
|
|
2574
2770
|
try {
|
|
2575
2771
|
const config = await loadConfig(cwd2);
|
|
2576
2772
|
const db = await openDB(config, cwd2);
|
|
@@ -2578,11 +2774,11 @@ async function runTaskAdd(cwd2) {
|
|
|
2578
2774
|
const task2 = await db.addTask({ slug, title, description: description || void 0, acceptance });
|
|
2579
2775
|
await db.writeFeatureList(cwd2);
|
|
2580
2776
|
await db.close();
|
|
2581
|
-
|
|
2777
|
+
spinner6.stop("");
|
|
2582
2778
|
console.log(pc11.green(`\u2713 Task #${task2.id} added \u2014 ${task2.slug} (pending)`));
|
|
2583
2779
|
console.log(pc11.cyan("\u2192") + " " + pc11.cyan("ahk status") + " to see all tasks");
|
|
2584
2780
|
} catch (err) {
|
|
2585
|
-
|
|
2781
|
+
spinner6.stop(pc11.red("Failed"));
|
|
2586
2782
|
p6.log.error(err instanceof Error ? err.message : String(err));
|
|
2587
2783
|
process.exit(1);
|
|
2588
2784
|
}
|
|
@@ -2628,14 +2824,107 @@ async function runTaskDone(cwd2, idOrSlug) {
|
|
|
2628
2824
|
}
|
|
2629
2825
|
}
|
|
2630
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
|
+
|
|
2631
2920
|
// src/commands/task/list.ts
|
|
2632
2921
|
import Table2 from "cli-table3";
|
|
2633
|
-
import
|
|
2922
|
+
import pc14 from "picocolors";
|
|
2634
2923
|
var STATUS_COLOR2 = {
|
|
2635
|
-
pending: (s) =>
|
|
2636
|
-
in_progress: (s) =>
|
|
2637
|
-
done: (s) =>
|
|
2638
|
-
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)
|
|
2639
2928
|
};
|
|
2640
2929
|
async function runTaskList(cwd2, opts) {
|
|
2641
2930
|
const config = await loadConfig(cwd2);
|
|
@@ -2643,17 +2932,20 @@ async function runTaskList(cwd2, opts) {
|
|
|
2643
2932
|
try {
|
|
2644
2933
|
const validStatuses = ["pending", "in_progress", "done", "blocked"];
|
|
2645
2934
|
const filterStatus = opts.status && validStatuses.includes(opts.status) ? opts.status : void 0;
|
|
2646
|
-
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);
|
|
2647
2936
|
if (opts.json) {
|
|
2648
2937
|
console.log(JSON.stringify(tasks, null, 2));
|
|
2649
2938
|
return;
|
|
2650
2939
|
}
|
|
2651
2940
|
if (tasks.length === 0) {
|
|
2652
|
-
|
|
2941
|
+
let msg = "No tasks";
|
|
2942
|
+
if (filterStatus) msg += ` with status: ${filterStatus}`;
|
|
2943
|
+
if (opts.archived) msg += " (archived)";
|
|
2944
|
+
console.log(pc14.dim(msg + "."));
|
|
2653
2945
|
return;
|
|
2654
2946
|
}
|
|
2655
2947
|
const table = new Table2({
|
|
2656
|
-
head: ["ID", "Slug", "Title", "Status"].map((h) =>
|
|
2948
|
+
head: ["ID", "Slug", "Title", "Status"].map((h) => pc14.bold(h)),
|
|
2657
2949
|
style: { head: [], border: [] }
|
|
2658
2950
|
});
|
|
2659
2951
|
for (const t of tasks) {
|
|
@@ -2661,6 +2953,12 @@ async function runTaskList(cwd2, opts) {
|
|
|
2661
2953
|
table.push([String(t.id), t.slug, t.title.slice(0, 50), colorFn(t.status)]);
|
|
2662
2954
|
}
|
|
2663
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
|
+
}
|
|
2664
2962
|
} finally {
|
|
2665
2963
|
await db.close();
|
|
2666
2964
|
}
|
|
@@ -2675,7 +2973,7 @@ var pkgPath = join16(dirname5(fileURLToPath3(import.meta.url)), "..", "package.j
|
|
|
2675
2973
|
var pkg = require2(pkgPath);
|
|
2676
2974
|
|
|
2677
2975
|
// src/core/update-check.ts
|
|
2678
|
-
import
|
|
2976
|
+
import pc15 from "picocolors";
|
|
2679
2977
|
var REGISTRY_URL = `https://registry.npmjs.org/${pkg.name}/latest`;
|
|
2680
2978
|
var TIMEOUT_MS = 2500;
|
|
2681
2979
|
function checkForUpdate(currentVersion) {
|
|
@@ -2693,8 +2991,8 @@ function checkForUpdate(currentVersion) {
|
|
|
2693
2991
|
}
|
|
2694
2992
|
function printUpdateMessage({ current, latest }) {
|
|
2695
2993
|
const lines = [
|
|
2696
|
-
` Update available ${
|
|
2697
|
-
` Run: ${
|
|
2994
|
+
` Update available ${pc15.dim(current)} \u2192 ${pc15.green(latest)} `,
|
|
2995
|
+
` Run: ${pc15.cyan(`pnpm i ${pkg.name}@${latest}`)} `
|
|
2698
2996
|
];
|
|
2699
2997
|
drawBox(lines);
|
|
2700
2998
|
}
|
|
@@ -2734,12 +3032,15 @@ var task = program.command("task").description("Manage tasks");
|
|
|
2734
3032
|
task.command("add").description("Add a task interactively").action(async () => {
|
|
2735
3033
|
await runTaskAdd(cwd);
|
|
2736
3034
|
});
|
|
2737
|
-
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) => {
|
|
2738
3036
|
await runTaskList(cwd, opts);
|
|
2739
3037
|
});
|
|
2740
3038
|
task.command("done <id|slug>").description("Mark a task as done").action(async (idOrSlug) => {
|
|
2741
3039
|
await runTaskDone(cwd, idOrSlug);
|
|
2742
3040
|
});
|
|
3041
|
+
task.command("edit").description("Edit a task interactively").action(async () => {
|
|
3042
|
+
await runTaskEdit(cwd);
|
|
3043
|
+
});
|
|
2743
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) => {
|
|
2744
3045
|
await runDashboard(cwd, { port: parseInt(opts.port), open: opts.open });
|
|
2745
3046
|
});
|