@cardor/agent-harness-kit 0.16.5 → 0.16.10
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/agent-templates/builder.md +37 -7
- package/dist/agent-templates/explorer.md +25 -6
- package/dist/agent-templates/lead.md +23 -0
- package/dist/agent-templates/reviewer.md +35 -6
- package/dist/cli.js +270 -72
- package/dist/cli.js.map +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -860,6 +860,14 @@ var HarnessDB = class {
|
|
|
860
860
|
this.regenerateCurrentMd();
|
|
861
861
|
return this.getAction(actionId);
|
|
862
862
|
}
|
|
863
|
+
closeOrphanedActions(taskId) {
|
|
864
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
865
|
+
const result = this.db.prepare(
|
|
866
|
+
`UPDATE actions SET status = 'completed', completed_at = ?, summary = 'Auto-closed: task marked done'
|
|
867
|
+
WHERE task_id = ? AND status = 'in_progress'`
|
|
868
|
+
).run(now, taskId);
|
|
869
|
+
return result.changes;
|
|
870
|
+
}
|
|
863
871
|
getAction(actionId) {
|
|
864
872
|
return this.db.prepare(`SELECT * FROM actions WHERE id = ?`).get(actionId) ?? null;
|
|
865
873
|
}
|
|
@@ -1155,9 +1163,10 @@ import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
|
1155
1163
|
import { homedir } from "os";
|
|
1156
1164
|
import { join as join9 } from "path";
|
|
1157
1165
|
import * as p2 from "@clack/prompts";
|
|
1158
|
-
import
|
|
1166
|
+
import pc6 from "picocolors";
|
|
1159
1167
|
|
|
1160
1168
|
// src/commands/init-helpers.ts
|
|
1169
|
+
import pc5 from "picocolors";
|
|
1161
1170
|
function applyConfigDefaults(params) {
|
|
1162
1171
|
return {
|
|
1163
1172
|
provider: params.provider,
|
|
@@ -1197,10 +1206,43 @@ function applyConfigDefaults(params) {
|
|
|
1197
1206
|
}
|
|
1198
1207
|
};
|
|
1199
1208
|
}
|
|
1209
|
+
function stripAnsi(str2) {
|
|
1210
|
+
return str2.replace(/\x1B\[[0-9;]*m/g, "");
|
|
1211
|
+
}
|
|
1212
|
+
function drawBox(lines) {
|
|
1213
|
+
const width = Math.max(...lines.map((l) => stripAnsi(l).length));
|
|
1214
|
+
const border = "\u2500".repeat(width);
|
|
1215
|
+
console.log(pc5.yellow(`\u250C${border}\u2510`));
|
|
1216
|
+
for (const line of lines) {
|
|
1217
|
+
const pad = width - stripAnsi(line).length;
|
|
1218
|
+
const padStr = pad > 0 ? " ".repeat(pad) : "";
|
|
1219
|
+
console.log(pc5.yellow("\u2502") + line + padStr + pc5.yellow("\u2502"));
|
|
1220
|
+
}
|
|
1221
|
+
console.log(pc5.yellow(`\u2514${border}\u2518`));
|
|
1222
|
+
}
|
|
1223
|
+
function printWelcomeMessage(projectName) {
|
|
1224
|
+
const sep = "\u2500".repeat(38);
|
|
1225
|
+
const lines = [
|
|
1226
|
+
` ${pc5.bold(pc5.white("agent-harness-kit"))} `,
|
|
1227
|
+
` ${pc5.gray("\u2014")} harness scaffolding ${pc5.gray("\u2014")} `,
|
|
1228
|
+
` ${pc5.gray(sep)} `,
|
|
1229
|
+
` ${pc5.bold("Project:")} ${projectName || "\u2014"} `,
|
|
1230
|
+
` ${pc5.bold("Status:")} ${pc5.green("ready to configure")} `,
|
|
1231
|
+
` ${pc5.gray(sep)} `,
|
|
1232
|
+
` ${pc5.gray("Next steps:")} `,
|
|
1233
|
+
` ${pc5.gray("\u2192")} ${pc5.gray("Set up your AI provider config")} `,
|
|
1234
|
+
` ${pc5.gray("\u2192")} ${pc5.gray("Run your health check to verify")} `,
|
|
1235
|
+
` ${pc5.gray("\u2192")} ${pc5.gray("Start adding tasks for your agents")} `
|
|
1236
|
+
];
|
|
1237
|
+
console.log();
|
|
1238
|
+
drawBox(lines);
|
|
1239
|
+
console.log();
|
|
1240
|
+
}
|
|
1200
1241
|
|
|
1201
1242
|
// src/commands/init.ts
|
|
1202
1243
|
async function runInit(cwd2, flags) {
|
|
1203
|
-
|
|
1244
|
+
const projectName = flags.name || "my-project";
|
|
1245
|
+
printWelcomeMessage(projectName);
|
|
1204
1246
|
let name;
|
|
1205
1247
|
if (flags.name) {
|
|
1206
1248
|
name = flags.name;
|
|
@@ -1281,14 +1323,14 @@ async function runInit(cwd2, flags) {
|
|
|
1281
1323
|
]
|
|
1282
1324
|
});
|
|
1283
1325
|
if (p2.isCancel(val)) {
|
|
1284
|
-
p2.cancel("Cancelled
|
|
1326
|
+
p2.cancel("Cancelled");
|
|
1285
1327
|
process.exit(0);
|
|
1286
1328
|
}
|
|
1287
1329
|
tasksAdapter = val;
|
|
1288
1330
|
}
|
|
1289
1331
|
const addFirstTask = await p2.confirm({ message: "Add your first task now?", initialValue: true });
|
|
1290
1332
|
if (p2.isCancel(addFirstTask)) {
|
|
1291
|
-
p2.cancel("Cancelled
|
|
1333
|
+
p2.cancel("Cancelled");
|
|
1292
1334
|
process.exit(0);
|
|
1293
1335
|
}
|
|
1294
1336
|
let firstTask;
|
|
@@ -1298,7 +1340,7 @@ async function runInit(cwd2, flags) {
|
|
|
1298
1340
|
validate: (v) => v.trim() ? void 0 : "Title is required"
|
|
1299
1341
|
});
|
|
1300
1342
|
if (p2.isCancel(titleVal)) {
|
|
1301
|
-
p2.cancel("Cancelled
|
|
1343
|
+
p2.cancel("Cancelled");
|
|
1302
1344
|
process.exit(0);
|
|
1303
1345
|
}
|
|
1304
1346
|
const taskTitle = titleVal.trim();
|
|
@@ -1307,7 +1349,7 @@ async function runInit(cwd2, flags) {
|
|
|
1307
1349
|
placeholder: "What and why"
|
|
1308
1350
|
});
|
|
1309
1351
|
if (p2.isCancel(taskDescVal)) {
|
|
1310
|
-
p2.cancel("Cancelled
|
|
1352
|
+
p2.cancel("Cancelled");
|
|
1311
1353
|
process.exit(0);
|
|
1312
1354
|
}
|
|
1313
1355
|
const taskDesc = taskDescVal.trim();
|
|
@@ -1365,29 +1407,29 @@ async function runInit(cwd2, flags) {
|
|
|
1365
1407
|
process.exit(1);
|
|
1366
1408
|
}
|
|
1367
1409
|
const agentHarnessKitDir = globalInstallation ? "home directory" : "current directory";
|
|
1368
|
-
console.log(
|
|
1410
|
+
console.log(pc6.green(`\u2713 Scaffolded harness in ${agentHarnessKitDir}`));
|
|
1369
1411
|
const agentsDir = provider === "claude-code" ? ".claude/agents/" : ".opencode/agents/";
|
|
1370
1412
|
const mcpFile = provider === "claude-code" ? ".claude/mcp.json" : "./opencode.json";
|
|
1371
1413
|
console.log("");
|
|
1372
|
-
console.log(
|
|
1373
|
-
console.log(
|
|
1374
|
-
console.log(
|
|
1375
|
-
console.log(
|
|
1376
|
-
console.log(
|
|
1377
|
-
console.log(
|
|
1378
|
-
console.log(
|
|
1379
|
-
console.log(
|
|
1380
|
-
console.log(
|
|
1381
|
-
console.log(
|
|
1382
|
-
console.log(
|
|
1414
|
+
console.log(pc6.green("\u2713 agent-harness-kit.config.ts"));
|
|
1415
|
+
console.log(pc6.green("\u2713 AGENTS.md"));
|
|
1416
|
+
console.log(pc6.green("\u2713 health.sh"));
|
|
1417
|
+
console.log(pc6.green("\u2713 .harness/harness.db"));
|
|
1418
|
+
console.log(pc6.green("\u2713 .harness/current.md"));
|
|
1419
|
+
console.log(pc6.green(`\u2713 ${agentsDir}lead.md`));
|
|
1420
|
+
console.log(pc6.green(`\u2713 ${agentsDir}explorer.md`));
|
|
1421
|
+
console.log(pc6.green(`\u2713 ${agentsDir}builder.md`));
|
|
1422
|
+
console.log(pc6.green(`\u2713 ${agentsDir}reviewer.md`));
|
|
1423
|
+
console.log(pc6.green(`\u2713 ${mcpFile}`));
|
|
1424
|
+
console.log(pc6.green("\u2713 .gitignore entries added"));
|
|
1383
1425
|
console.log("");
|
|
1384
|
-
console.log(
|
|
1385
|
-
console.log(
|
|
1426
|
+
console.log(pc6.cyan("\u2192") + ` Edit ${pc6.cyan("health.sh")} with your project checks`);
|
|
1427
|
+
console.log(pc6.cyan("\u2192") + ` ${pc6.cyan("ahk task add")} to queue work for agents`);
|
|
1386
1428
|
}
|
|
1387
1429
|
|
|
1388
1430
|
// src/commands/migrate.ts
|
|
1389
1431
|
import * as p3 from "@clack/prompts";
|
|
1390
|
-
import
|
|
1432
|
+
import pc7 from "picocolors";
|
|
1391
1433
|
async function runMigrate(cwd2, opts) {
|
|
1392
1434
|
const config = await loadConfig(cwd2);
|
|
1393
1435
|
let target;
|
|
@@ -1408,7 +1450,7 @@ async function runMigrate(cwd2, opts) {
|
|
|
1408
1450
|
target = val;
|
|
1409
1451
|
}
|
|
1410
1452
|
if (target === config.provider) {
|
|
1411
|
-
console.log(
|
|
1453
|
+
console.log(pc7.dim(`Already on ${target} \u2014 nothing to migrate.`));
|
|
1412
1454
|
return;
|
|
1413
1455
|
}
|
|
1414
1456
|
const spinner5 = p3.spinner();
|
|
@@ -1416,11 +1458,11 @@ async function runMigrate(cwd2, opts) {
|
|
|
1416
1458
|
try {
|
|
1417
1459
|
const targetMaterializer = getMaterializer(target);
|
|
1418
1460
|
await targetMaterializer.build(config, cwd2);
|
|
1419
|
-
spinner5.stop(
|
|
1461
|
+
spinner5.stop(pc7.green(`Migrated to ${target}`));
|
|
1420
1462
|
p3.log.warn(`Update agent-harness-kit.config.ts: set provider: '${target}'`);
|
|
1421
1463
|
p3.log.warn(`Then run: ahk build`);
|
|
1422
1464
|
} catch (err) {
|
|
1423
|
-
spinner5.stop(
|
|
1465
|
+
spinner5.stop(pc7.red("Migration failed"));
|
|
1424
1466
|
p3.log.error(err instanceof Error ? err.message : String(err));
|
|
1425
1467
|
process.exit(1);
|
|
1426
1468
|
}
|
|
@@ -1571,6 +1613,20 @@ var TOOLS = [
|
|
|
1571
1613
|
},
|
|
1572
1614
|
required: ["criterionId"]
|
|
1573
1615
|
}
|
|
1616
|
+
},
|
|
1617
|
+
{
|
|
1618
|
+
name: "actions.record_tool",
|
|
1619
|
+
description: "Record a tool call made during an action. This is the only way to populate the Tools dashboard. Call once per tool invocation.",
|
|
1620
|
+
inputSchema: {
|
|
1621
|
+
type: "object",
|
|
1622
|
+
properties: {
|
|
1623
|
+
actionId: { type: "string", description: "UUID returned by actions.start" },
|
|
1624
|
+
toolName: { type: "string", description: "Name of the tool that was called (e.g. Read, Bash, Edit)" },
|
|
1625
|
+
argsJson: { type: "string", description: "Optional JSON string of the arguments passed to the tool" },
|
|
1626
|
+
resultSummary: { type: "string", description: "Optional short summary of the tool result" }
|
|
1627
|
+
},
|
|
1628
|
+
required: ["actionId", "toolName"]
|
|
1629
|
+
}
|
|
1574
1630
|
}
|
|
1575
1631
|
];
|
|
1576
1632
|
async function startMcpServer(config, cwd2) {
|
|
@@ -1641,6 +1697,9 @@ async function dispatch(name, args, db, docsPath) {
|
|
|
1641
1697
|
case "tasks.update": {
|
|
1642
1698
|
const id = num(args, "id");
|
|
1643
1699
|
const status = str(args, "status");
|
|
1700
|
+
if (status === "done") {
|
|
1701
|
+
db.closeOrphanedActions(id);
|
|
1702
|
+
}
|
|
1644
1703
|
const task2 = db.updateTaskStatus(id, status);
|
|
1645
1704
|
return ok(JSON.stringify(task2));
|
|
1646
1705
|
}
|
|
@@ -1662,6 +1721,14 @@ async function dispatch(name, args, db, docsPath) {
|
|
|
1662
1721
|
db.markAcceptanceMet(criterionId);
|
|
1663
1722
|
return ok(JSON.stringify({ criterionId, met: true }));
|
|
1664
1723
|
}
|
|
1724
|
+
case "actions.record_tool": {
|
|
1725
|
+
const actionId = str(args, "actionId");
|
|
1726
|
+
const toolName = str(args, "toolName");
|
|
1727
|
+
const argsJson = args["argsJson"];
|
|
1728
|
+
const resultSummary = args["resultSummary"];
|
|
1729
|
+
db.recordTool(actionId, toolName, argsJson, resultSummary);
|
|
1730
|
+
return ok(JSON.stringify({ actionId, toolName, recorded: true }));
|
|
1731
|
+
}
|
|
1665
1732
|
default:
|
|
1666
1733
|
return ok(`Unknown tool: ${name}`, true);
|
|
1667
1734
|
}
|
|
@@ -1734,12 +1801,12 @@ async function runServe(cwd2, opts) {
|
|
|
1734
1801
|
|
|
1735
1802
|
// src/commands/status.ts
|
|
1736
1803
|
import Table from "cli-table3";
|
|
1737
|
-
import
|
|
1804
|
+
import pc8 from "picocolors";
|
|
1738
1805
|
var STATUS_COLOR = {
|
|
1739
|
-
pending: (s) =>
|
|
1740
|
-
in_progress: (s) =>
|
|
1741
|
-
done: (s) =>
|
|
1742
|
-
blocked: (s) =>
|
|
1806
|
+
pending: (s) => pc8.dim(s),
|
|
1807
|
+
in_progress: (s) => pc8.cyan(s),
|
|
1808
|
+
done: (s) => pc8.green(s),
|
|
1809
|
+
blocked: (s) => pc8.red(s)
|
|
1743
1810
|
};
|
|
1744
1811
|
async function runStatus(cwd2, opts) {
|
|
1745
1812
|
const config = await loadConfig(cwd2);
|
|
@@ -1757,11 +1824,11 @@ async function runStatus(cwd2, opts) {
|
|
|
1757
1824
|
return;
|
|
1758
1825
|
}
|
|
1759
1826
|
if (tasks.length === 0) {
|
|
1760
|
-
console.log(
|
|
1827
|
+
console.log(pc8.dim("No tasks yet. Run: ahk task add"));
|
|
1761
1828
|
return;
|
|
1762
1829
|
}
|
|
1763
1830
|
const table = new Table({
|
|
1764
|
-
head: ["ID", "Slug", "Title", "Status", "Assigned", "Started"].map((h) =>
|
|
1831
|
+
head: ["ID", "Slug", "Title", "Status", "Assigned", "Started"].map((h) => pc8.bold(h)),
|
|
1765
1832
|
style: { head: [], border: [] }
|
|
1766
1833
|
});
|
|
1767
1834
|
for (const t of tasks) {
|
|
@@ -1779,12 +1846,12 @@ async function runStatus(cwd2, opts) {
|
|
|
1779
1846
|
const inProgress = tasks.filter((t) => t.status === "in_progress");
|
|
1780
1847
|
if (inProgress.length > 0) {
|
|
1781
1848
|
console.log("");
|
|
1782
|
-
console.log(
|
|
1849
|
+
console.log(pc8.bold("Active actions:"));
|
|
1783
1850
|
for (const t of inProgress) {
|
|
1784
1851
|
const actions = db.getActionsForTask(t.id);
|
|
1785
1852
|
const active = actions.filter((a) => a.status === "in_progress");
|
|
1786
1853
|
for (const a of active) {
|
|
1787
|
-
console.log(` ${
|
|
1854
|
+
console.log(` ${pc8.cyan(a.agent.padEnd(10))} \u2192 task #${t.id} ${t.slug}`);
|
|
1788
1855
|
}
|
|
1789
1856
|
}
|
|
1790
1857
|
}
|
|
@@ -1793,7 +1860,7 @@ async function runStatus(cwd2, opts) {
|
|
|
1793
1860
|
const fn = STATUS_COLOR[s.status] ?? ((x) => x);
|
|
1794
1861
|
return `${fn(s.status)}: ${s.total}`;
|
|
1795
1862
|
});
|
|
1796
|
-
console.log(
|
|
1863
|
+
console.log(pc8.dim("Tasks \u2014 ") + parts.join(pc8.dim(" | ")));
|
|
1797
1864
|
} finally {
|
|
1798
1865
|
db.close();
|
|
1799
1866
|
}
|
|
@@ -1802,7 +1869,7 @@ async function runStatus(cwd2, opts) {
|
|
|
1802
1869
|
// src/commands/sync.ts
|
|
1803
1870
|
import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
|
|
1804
1871
|
import { join as join11, resolve as resolve8 } from "path";
|
|
1805
|
-
import
|
|
1872
|
+
import pc9 from "picocolors";
|
|
1806
1873
|
async function runSync(cwd2, opts) {
|
|
1807
1874
|
const config = await loadConfig(cwd2);
|
|
1808
1875
|
const direction = opts.direction ?? "both";
|
|
@@ -1821,43 +1888,43 @@ async function runSync(cwd2, opts) {
|
|
|
1821
1888
|
}
|
|
1822
1889
|
async function syncIn(featureListPath, db, dryRun) {
|
|
1823
1890
|
if (!existsSync7(featureListPath)) {
|
|
1824
|
-
console.log(
|
|
1891
|
+
console.log(pc9.dim(`feature_list.json not found at ${featureListPath} \u2014 skipping in-sync`));
|
|
1825
1892
|
return;
|
|
1826
1893
|
}
|
|
1827
1894
|
let seeds;
|
|
1828
1895
|
try {
|
|
1829
1896
|
seeds = JSON.parse(readFileSync6(featureListPath, "utf8"));
|
|
1830
1897
|
} catch (err) {
|
|
1831
|
-
console.error(
|
|
1898
|
+
console.error(pc9.red(`Failed to parse feature_list.json: ${err}`));
|
|
1832
1899
|
process.exit(1);
|
|
1833
1900
|
}
|
|
1834
1901
|
if (dryRun) {
|
|
1835
|
-
console.log(
|
|
1902
|
+
console.log(pc9.bold("Dry run \u2014 in-sync (feature_list.json \u2192 SQLite):"));
|
|
1836
1903
|
for (const t of seeds) {
|
|
1837
1904
|
const existing = db.getTaskBySlug(t.slug);
|
|
1838
|
-
console.log(` ${existing ?
|
|
1905
|
+
console.log(` ${existing ? pc9.dim("skip") : pc9.green("add ")} ${t.slug}`);
|
|
1839
1906
|
}
|
|
1840
1907
|
return;
|
|
1841
1908
|
}
|
|
1842
1909
|
const result = db.syncFromFeatureList(seeds);
|
|
1843
|
-
console.log(
|
|
1910
|
+
console.log(pc9.green(`\u2713 In-sync: ${result.added} added, ${result.skipped} already existed`));
|
|
1844
1911
|
}
|
|
1845
1912
|
function syncOut(db, cwd2, dryRun) {
|
|
1846
1913
|
if (dryRun) {
|
|
1847
1914
|
const tasks = db.getTasks();
|
|
1848
|
-
console.log(
|
|
1915
|
+
console.log(pc9.bold("Dry run \u2014 out-sync (SQLite \u2192 feature_list.json):"));
|
|
1849
1916
|
console.log(` ${tasks.length} tasks would be written`);
|
|
1850
1917
|
return;
|
|
1851
1918
|
}
|
|
1852
1919
|
db.writeFeatureList(cwd2);
|
|
1853
|
-
console.log(
|
|
1920
|
+
console.log(pc9.green("\u2713 Out-sync: feature_list.json updated"));
|
|
1854
1921
|
}
|
|
1855
1922
|
|
|
1856
1923
|
// src/commands/task/add.ts
|
|
1857
1924
|
import * as p4 from "@clack/prompts";
|
|
1858
|
-
import
|
|
1925
|
+
import pc10 from "picocolors";
|
|
1859
1926
|
async function runTaskAdd(cwd2) {
|
|
1860
|
-
p4.intro(
|
|
1927
|
+
p4.intro(pc10.bold("agent-harness-kit \u2014 add task"));
|
|
1861
1928
|
const titleVal = await p4.text({
|
|
1862
1929
|
message: "Task title",
|
|
1863
1930
|
validate: (v) => v.trim() ? void 0 : "Title is required"
|
|
@@ -1893,10 +1960,10 @@ async function runTaskAdd(cwd2) {
|
|
|
1893
1960
|
db.writeFeatureList(cwd2);
|
|
1894
1961
|
db.close();
|
|
1895
1962
|
spinner5.stop("");
|
|
1896
|
-
console.log(
|
|
1897
|
-
console.log(
|
|
1963
|
+
console.log(pc10.green(`\u2713 Task #${task2.id} added \u2014 ${task2.slug} (pending)`));
|
|
1964
|
+
console.log(pc10.cyan("\u2192") + " " + pc10.cyan("ahk status") + " to see all tasks");
|
|
1898
1965
|
} catch (err) {
|
|
1899
|
-
spinner5.stop(
|
|
1966
|
+
spinner5.stop(pc10.red("Failed"));
|
|
1900
1967
|
p4.log.error(err instanceof Error ? err.message : String(err));
|
|
1901
1968
|
process.exit(1);
|
|
1902
1969
|
}
|
|
@@ -1906,7 +1973,7 @@ async function runTaskAdd(cwd2) {
|
|
|
1906
1973
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
1907
1974
|
import { existsSync as existsSync8 } from "fs";
|
|
1908
1975
|
import { resolve as resolve9 } from "path";
|
|
1909
|
-
import
|
|
1976
|
+
import pc11 from "picocolors";
|
|
1910
1977
|
async function runTaskDone(cwd2, idOrSlug) {
|
|
1911
1978
|
const config = await loadConfig(cwd2);
|
|
1912
1979
|
if (config.health.required) {
|
|
@@ -1914,7 +1981,7 @@ async function runTaskDone(cwd2, idOrSlug) {
|
|
|
1914
1981
|
if (existsSync8(scriptPath)) {
|
|
1915
1982
|
const result = spawnSync2("bash", [scriptPath], { cwd: cwd2, stdio: "pipe", encoding: "utf8" });
|
|
1916
1983
|
if (result.status !== 0) {
|
|
1917
|
-
console.error(
|
|
1984
|
+
console.error(pc11.red("\u2717 Health check failed \u2014 cannot mark task as done."));
|
|
1918
1985
|
if (result.stdout) console.error(result.stdout);
|
|
1919
1986
|
if (result.stderr) console.error(result.stderr);
|
|
1920
1987
|
process.exit(1);
|
|
@@ -1927,16 +1994,16 @@ async function runTaskDone(cwd2, idOrSlug) {
|
|
|
1927
1994
|
const isId = !isNaN(parsed);
|
|
1928
1995
|
const task2 = isId ? db.getTaskById(parsed) : db.getTaskBySlug(idOrSlug);
|
|
1929
1996
|
if (!task2) {
|
|
1930
|
-
console.error(
|
|
1997
|
+
console.error(pc11.red(`Task not found: ${idOrSlug}`));
|
|
1931
1998
|
process.exit(1);
|
|
1932
1999
|
}
|
|
1933
2000
|
if (task2.status === "done") {
|
|
1934
|
-
console.log(
|
|
2001
|
+
console.log(pc11.dim(`Task #${task2.id} is already done.`));
|
|
1935
2002
|
return;
|
|
1936
2003
|
}
|
|
1937
2004
|
db.updateTaskStatus(task2.id, "done");
|
|
1938
2005
|
db.writeFeatureList(cwd2);
|
|
1939
|
-
console.log(
|
|
2006
|
+
console.log(pc11.green(`\u2713 Task #${task2.id} \u2014 ${task2.slug} marked as done`));
|
|
1940
2007
|
} finally {
|
|
1941
2008
|
db.close();
|
|
1942
2009
|
}
|
|
@@ -1944,12 +2011,12 @@ async function runTaskDone(cwd2, idOrSlug) {
|
|
|
1944
2011
|
|
|
1945
2012
|
// src/commands/task/list.ts
|
|
1946
2013
|
import Table2 from "cli-table3";
|
|
1947
|
-
import
|
|
2014
|
+
import pc12 from "picocolors";
|
|
1948
2015
|
var STATUS_COLOR2 = {
|
|
1949
|
-
pending: (s) =>
|
|
1950
|
-
in_progress: (s) =>
|
|
1951
|
-
done: (s) =>
|
|
1952
|
-
blocked: (s) =>
|
|
2016
|
+
pending: (s) => pc12.dim(s),
|
|
2017
|
+
in_progress: (s) => pc12.cyan(s),
|
|
2018
|
+
done: (s) => pc12.green(s),
|
|
2019
|
+
blocked: (s) => pc12.red(s)
|
|
1953
2020
|
};
|
|
1954
2021
|
async function runTaskList(cwd2, opts) {
|
|
1955
2022
|
const config = await loadConfig(cwd2);
|
|
@@ -1963,11 +2030,11 @@ async function runTaskList(cwd2, opts) {
|
|
|
1963
2030
|
return;
|
|
1964
2031
|
}
|
|
1965
2032
|
if (tasks.length === 0) {
|
|
1966
|
-
console.log(
|
|
2033
|
+
console.log(pc12.dim("No tasks" + (filterStatus ? ` with status: ${filterStatus}` : "") + "."));
|
|
1967
2034
|
return;
|
|
1968
2035
|
}
|
|
1969
2036
|
const table = new Table2({
|
|
1970
|
-
head: ["ID", "Slug", "Title", "Status"].map((h) =>
|
|
2037
|
+
head: ["ID", "Slug", "Title", "Status"].map((h) => pc12.bold(h)),
|
|
1971
2038
|
style: { head: [], border: [] }
|
|
1972
2039
|
});
|
|
1973
2040
|
for (const t of tasks) {
|
|
@@ -1989,36 +2056,36 @@ var pkgPath = join12(dirname5(fileURLToPath3(import.meta.url)), "..", "package.j
|
|
|
1989
2056
|
var pkg = require2(pkgPath);
|
|
1990
2057
|
|
|
1991
2058
|
// src/core/update-check.ts
|
|
1992
|
-
import
|
|
2059
|
+
import pc13 from "picocolors";
|
|
1993
2060
|
var REGISTRY_URL = `https://registry.npmjs.org/${pkg.name}/latest`;
|
|
1994
2061
|
var TIMEOUT_MS = 2500;
|
|
1995
2062
|
function checkForUpdate(currentVersion) {
|
|
1996
|
-
return new Promise((
|
|
1997
|
-
const timer = setTimeout(() =>
|
|
2063
|
+
return new Promise((resolve11) => {
|
|
2064
|
+
const timer = setTimeout(() => resolve11(null), TIMEOUT_MS);
|
|
1998
2065
|
fetch(REGISTRY_URL).then((res) => res.json()).then((data) => {
|
|
1999
2066
|
clearTimeout(timer);
|
|
2000
2067
|
const latest = data.version;
|
|
2001
|
-
|
|
2068
|
+
resolve11(isNewer(latest, currentVersion) ? { current: currentVersion, latest } : null);
|
|
2002
2069
|
}).catch(() => {
|
|
2003
2070
|
clearTimeout(timer);
|
|
2004
|
-
|
|
2071
|
+
resolve11(null);
|
|
2005
2072
|
});
|
|
2006
2073
|
});
|
|
2007
2074
|
}
|
|
2008
2075
|
function printUpdateMessage({ current, latest }) {
|
|
2009
2076
|
const lines = [
|
|
2010
|
-
` Update available ${
|
|
2011
|
-
` Run: ${
|
|
2077
|
+
` Update available ${pc13.dim(current)} \u2192 ${pc13.green(latest)} `,
|
|
2078
|
+
` Run: ${pc13.cyan(`npm i ${pkg.name}@${latest}`)} `
|
|
2012
2079
|
];
|
|
2013
|
-
const width = Math.max(...lines.map((l) =>
|
|
2080
|
+
const width = Math.max(...lines.map((l) => stripAnsi2(l).length));
|
|
2014
2081
|
const border = "\u2500".repeat(width);
|
|
2015
2082
|
console.log();
|
|
2016
|
-
console.log(
|
|
2083
|
+
console.log(pc13.yellow(`\u250C${border}\u2510`));
|
|
2017
2084
|
for (const line of lines) {
|
|
2018
|
-
const pad = width -
|
|
2019
|
-
console.log(
|
|
2085
|
+
const pad = width - stripAnsi2(line).length;
|
|
2086
|
+
console.log(pc13.yellow("\u2502") + line + " ".repeat(pad) + pc13.yellow("\u2502"));
|
|
2020
2087
|
}
|
|
2021
|
-
console.log(
|
|
2088
|
+
console.log(pc13.yellow(`\u2514${border}\u2518`));
|
|
2022
2089
|
console.log();
|
|
2023
2090
|
}
|
|
2024
2091
|
function isNewer(latest, current) {
|
|
@@ -2029,10 +2096,138 @@ function isNewer(latest, current) {
|
|
|
2029
2096
|
if (lMin !== cMin) return lMin > cMin;
|
|
2030
2097
|
return lPat > cPat;
|
|
2031
2098
|
}
|
|
2032
|
-
function
|
|
2099
|
+
function stripAnsi2(str2) {
|
|
2033
2100
|
return str2.replace(/\x1B\[[0-9;]*m/g, "");
|
|
2034
2101
|
}
|
|
2035
2102
|
|
|
2103
|
+
// src/commands/reset.ts
|
|
2104
|
+
import { existsSync as existsSync9, readdirSync as readdirSync2, rmSync } from "fs";
|
|
2105
|
+
import { join as join13, resolve as resolve10 } from "path";
|
|
2106
|
+
import * as p5 from "@clack/prompts";
|
|
2107
|
+
import pc14 from "picocolors";
|
|
2108
|
+
async function resetAgentMds(cwd2, provider) {
|
|
2109
|
+
const agentDir = provider === "claude-code" ? ".claude/agents" : ".opencode/agents";
|
|
2110
|
+
const agentDirPath = resolve10(cwd2, agentDir);
|
|
2111
|
+
if (!existsSync9(agentDirPath)) {
|
|
2112
|
+
console.log(pc14.yellow(` Skipping agent files \u2014 directory not found: ${agentDirPath}`));
|
|
2113
|
+
return;
|
|
2114
|
+
}
|
|
2115
|
+
const existingFiles = [];
|
|
2116
|
+
try {
|
|
2117
|
+
const files = readdirSync2(agentDirPath);
|
|
2118
|
+
for (const f of files) {
|
|
2119
|
+
if (f.endsWith(".md")) {
|
|
2120
|
+
existingFiles.push(f);
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
} catch {
|
|
2124
|
+
console.log(pc14.yellow(` Skipping agent files \u2014 ${agentDirPath} is not readable`));
|
|
2125
|
+
return;
|
|
2126
|
+
}
|
|
2127
|
+
if (existingFiles.length === 0) {
|
|
2128
|
+
console.log(pc14.yellow(` No agent MD files found in ${agentDir}/`));
|
|
2129
|
+
return;
|
|
2130
|
+
}
|
|
2131
|
+
for (const file of existingFiles) {
|
|
2132
|
+
const confirm3 = await p5.confirm({
|
|
2133
|
+
message: `Remove ${file}?`,
|
|
2134
|
+
initialValue: true
|
|
2135
|
+
});
|
|
2136
|
+
if (p5.isCancel(confirm3)) {
|
|
2137
|
+
console.log(pc14.red(" Cancelled by user."));
|
|
2138
|
+
return;
|
|
2139
|
+
}
|
|
2140
|
+
if (confirm3) {
|
|
2141
|
+
try {
|
|
2142
|
+
const filePath = join13(agentDirPath, file);
|
|
2143
|
+
rmSync(filePath, { force: true });
|
|
2144
|
+
console.log(pc14.green(` Removed ${file}`));
|
|
2145
|
+
} catch {
|
|
2146
|
+
console.error(pc14.red(` Failed to remove ${file}`));
|
|
2147
|
+
}
|
|
2148
|
+
} else {
|
|
2149
|
+
console.log(pc14.cyan(` Skipped ${file}`));
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
async function runReset(cwd2, opts) {
|
|
2154
|
+
let config;
|
|
2155
|
+
try {
|
|
2156
|
+
config = await loadConfig(cwd2);
|
|
2157
|
+
} catch {
|
|
2158
|
+
console.error(pc14.red("\u2717 No agent-harness-kit.config found. Run: ahk init"));
|
|
2159
|
+
process.exit(1);
|
|
2160
|
+
}
|
|
2161
|
+
const storageDir = config.storage.dir || ".harness";
|
|
2162
|
+
const dbPath = resolve10(cwd2, storageDir, "harness.db");
|
|
2163
|
+
const featureListPath = resolve10(cwd2, storageDir, "feature_list.json");
|
|
2164
|
+
let resetDb = false;
|
|
2165
|
+
let resetFeatureList = false;
|
|
2166
|
+
let resetAgentMdsFlag = false;
|
|
2167
|
+
if (existsSync9(dbPath)) {
|
|
2168
|
+
if (opts.force) {
|
|
2169
|
+
resetDb = true;
|
|
2170
|
+
} else {
|
|
2171
|
+
const confirm3 = await p5.confirm({
|
|
2172
|
+
message: `Delete database (${storageDir}/harness.db)?`,
|
|
2173
|
+
initialValue: true
|
|
2174
|
+
});
|
|
2175
|
+
if (p5.isCancel(confirm3)) {
|
|
2176
|
+
console.log(pc14.red(" Cancelled by user."));
|
|
2177
|
+
return;
|
|
2178
|
+
}
|
|
2179
|
+
resetDb = confirm3;
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
if (existsSync9(featureListPath)) {
|
|
2183
|
+
if (opts.force) {
|
|
2184
|
+
resetFeatureList = true;
|
|
2185
|
+
} else {
|
|
2186
|
+
const confirm3 = await p5.confirm({
|
|
2187
|
+
message: `Delete feature list (${storageDir}/feature_list.json)?`,
|
|
2188
|
+
initialValue: true
|
|
2189
|
+
});
|
|
2190
|
+
if (p5.isCancel(confirm3)) {
|
|
2191
|
+
console.log(pc14.red(" Cancelled by user."));
|
|
2192
|
+
return;
|
|
2193
|
+
}
|
|
2194
|
+
resetFeatureList = confirm3;
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
if (opts.provider) {
|
|
2198
|
+
resetAgentMdsFlag = true;
|
|
2199
|
+
}
|
|
2200
|
+
let changed = false;
|
|
2201
|
+
if (resetDb) {
|
|
2202
|
+
try {
|
|
2203
|
+
rmSync(dbPath, { force: true });
|
|
2204
|
+
console.log(pc14.green(` \u2713 Removed ${storageDir}/harness.db`));
|
|
2205
|
+
changed = true;
|
|
2206
|
+
} catch {
|
|
2207
|
+
console.error(pc14.red(` \u2717 Failed to remove ${dbPath}`));
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
if (resetFeatureList) {
|
|
2211
|
+
try {
|
|
2212
|
+
rmSync(featureListPath, { force: true });
|
|
2213
|
+
console.log(pc14.green(` \u2713 Removed ${storageDir}/feature_list.json`));
|
|
2214
|
+
changed = true;
|
|
2215
|
+
} catch {
|
|
2216
|
+
console.error(pc14.red(` \u2717 Failed to remove ${featureListPath}`));
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
if (resetAgentMdsFlag) {
|
|
2220
|
+
console.log("");
|
|
2221
|
+
await resetAgentMds(cwd2, opts.provider || "claude-code");
|
|
2222
|
+
}
|
|
2223
|
+
if (!resetDb && !resetFeatureList && !resetAgentMdsFlag) {
|
|
2224
|
+
console.log(pc14.yellow(" Nothing to reset (all items missing or skipped)."));
|
|
2225
|
+
return;
|
|
2226
|
+
}
|
|
2227
|
+
console.log("");
|
|
2228
|
+
console.log(pc14.green('\u2713 Reset complete. Run "ahk init" to scaffold a fresh harness.'));
|
|
2229
|
+
}
|
|
2230
|
+
|
|
2036
2231
|
// src/cli.ts
|
|
2037
2232
|
var cwd = process.cwd();
|
|
2038
2233
|
var updateCheck = checkForUpdate(pkg.version);
|
|
@@ -2075,6 +2270,9 @@ program.command("migrate").description("Migrate provider-specific files to a dif
|
|
|
2075
2270
|
program.command("export").description("Export the database").option("--sql", "SQL dump").option("--json", "JSON export of tasks and actions").option("--output <path>", "Output file path (default: stdout)").action(async (opts) => {
|
|
2076
2271
|
await runExport(cwd, opts);
|
|
2077
2272
|
});
|
|
2273
|
+
program.command("reset").description("Reset/clear harness data (DB, feature list, agent files)").option("--force", "Skip confirmation prompts").option("--provider <claude-code|opencode>", "Reset agent MD files for specified provider").action(async (opts) => {
|
|
2274
|
+
await runReset(cwd, opts);
|
|
2275
|
+
});
|
|
2078
2276
|
program.hook("postAction", async () => {
|
|
2079
2277
|
const update = await updateCheck;
|
|
2080
2278
|
if (update) printUpdateMessage(update);
|