@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/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 pc5 from "picocolors";
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
- p2.intro(pc5.bold("agent-harness-kit \u2014 harness scaffolding"));
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(pc5.green(`\u2713 Scaffolded harness in ${agentHarnessKitDir}`));
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(pc5.green("\u2713 agent-harness-kit.config.ts"));
1373
- console.log(pc5.green("\u2713 AGENTS.md"));
1374
- console.log(pc5.green("\u2713 health.sh"));
1375
- console.log(pc5.green("\u2713 .harness/harness.db"));
1376
- console.log(pc5.green("\u2713 .harness/current.md"));
1377
- console.log(pc5.green(`\u2713 ${agentsDir}lead.md`));
1378
- console.log(pc5.green(`\u2713 ${agentsDir}explorer.md`));
1379
- console.log(pc5.green(`\u2713 ${agentsDir}builder.md`));
1380
- console.log(pc5.green(`\u2713 ${agentsDir}reviewer.md`));
1381
- console.log(pc5.green(`\u2713 ${mcpFile}`));
1382
- console.log(pc5.green("\u2713 .gitignore entries added"));
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(pc5.cyan("\u2192") + ` Edit ${pc5.cyan("health.sh")} with your project checks`);
1385
- console.log(pc5.cyan("\u2192") + ` ${pc5.cyan("ahk task add")} to queue work for agents`);
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 pc6 from "picocolors";
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(pc6.dim(`Already on ${target} \u2014 nothing to migrate.`));
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(pc6.green(`Migrated to ${target}`));
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(pc6.red("Migration failed"));
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 pc7 from "picocolors";
1804
+ import pc8 from "picocolors";
1738
1805
  var STATUS_COLOR = {
1739
- pending: (s) => pc7.dim(s),
1740
- in_progress: (s) => pc7.cyan(s),
1741
- done: (s) => pc7.green(s),
1742
- blocked: (s) => pc7.red(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(pc7.dim("No tasks yet. Run: ahk task add"));
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) => pc7.bold(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(pc7.bold("Active actions:"));
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(` ${pc7.cyan(a.agent.padEnd(10))} \u2192 task #${t.id} ${t.slug}`);
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(pc7.dim("Tasks \u2014 ") + parts.join(pc7.dim(" | ")));
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 pc8 from "picocolors";
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(pc8.dim(`feature_list.json not found at ${featureListPath} \u2014 skipping in-sync`));
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(pc8.red(`Failed to parse feature_list.json: ${err}`));
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(pc8.bold("Dry run \u2014 in-sync (feature_list.json \u2192 SQLite):"));
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 ? pc8.dim("skip") : pc8.green("add ")} ${t.slug}`);
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(pc8.green(`\u2713 In-sync: ${result.added} added, ${result.skipped} already existed`));
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(pc8.bold("Dry run \u2014 out-sync (SQLite \u2192 feature_list.json):"));
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(pc8.green("\u2713 Out-sync: feature_list.json updated"));
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 pc9 from "picocolors";
1925
+ import pc10 from "picocolors";
1859
1926
  async function runTaskAdd(cwd2) {
1860
- p4.intro(pc9.bold("agent-harness-kit \u2014 add task"));
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(pc9.green(`\u2713 Task #${task2.id} added \u2014 ${task2.slug} (pending)`));
1897
- console.log(pc9.cyan("\u2192") + " " + pc9.cyan("ahk status") + " to see all tasks");
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(pc9.red("Failed"));
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 pc10 from "picocolors";
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(pc10.red("\u2717 Health check failed \u2014 cannot mark task as done."));
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(pc10.red(`Task not found: ${idOrSlug}`));
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(pc10.dim(`Task #${task2.id} is already done.`));
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(pc10.green(`\u2713 Task #${task2.id} \u2014 ${task2.slug} marked as done`));
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 pc11 from "picocolors";
2014
+ import pc12 from "picocolors";
1948
2015
  var STATUS_COLOR2 = {
1949
- pending: (s) => pc11.dim(s),
1950
- in_progress: (s) => pc11.cyan(s),
1951
- done: (s) => pc11.green(s),
1952
- blocked: (s) => pc11.red(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(pc11.dim("No tasks" + (filterStatus ? ` with status: ${filterStatus}` : "") + "."));
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) => pc11.bold(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 pc12 from "picocolors";
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((resolve10) => {
1997
- const timer = setTimeout(() => resolve10(null), TIMEOUT_MS);
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
- resolve10(isNewer(latest, currentVersion) ? { current: currentVersion, latest } : null);
2068
+ resolve11(isNewer(latest, currentVersion) ? { current: currentVersion, latest } : null);
2002
2069
  }).catch(() => {
2003
2070
  clearTimeout(timer);
2004
- resolve10(null);
2071
+ resolve11(null);
2005
2072
  });
2006
2073
  });
2007
2074
  }
2008
2075
  function printUpdateMessage({ current, latest }) {
2009
2076
  const lines = [
2010
- ` Update available ${pc12.dim(current)} \u2192 ${pc12.green(latest)} `,
2011
- ` Run: ${pc12.cyan(`npm i ${pkg.name}@${latest}`)} `
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) => stripAnsi(l).length));
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(pc12.yellow(`\u250C${border}\u2510`));
2083
+ console.log(pc13.yellow(`\u250C${border}\u2510`));
2017
2084
  for (const line of lines) {
2018
- const pad = width - stripAnsi(line).length;
2019
- console.log(pc12.yellow("\u2502") + line + " ".repeat(pad) + pc12.yellow("\u2502"));
2085
+ const pad = width - stripAnsi2(line).length;
2086
+ console.log(pc13.yellow("\u2502") + line + " ".repeat(pad) + pc13.yellow("\u2502"));
2020
2087
  }
2021
- console.log(pc12.yellow(`\u2514${border}\u2518`));
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 stripAnsi(str2) {
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);