@cardor/agent-harness-kit 2.0.0 → 2.1.1

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
@@ -16,11 +16,11 @@ import {
16
16
 
17
17
  // src/cli.ts
18
18
  import { Command, InvalidArgumentError } from "commander";
19
- import pc20 from "picocolors";
19
+ import pc21 from "picocolors";
20
20
 
21
21
  // src/commands/build.ts
22
22
  import { watch } from "fs";
23
- import * as p from "@clack/prompts";
23
+ import * as p2 from "@clack/prompts";
24
24
  import pc2 from "picocolors";
25
25
 
26
26
  // src/core/materializer/claude-code.ts
@@ -53,31 +53,39 @@ import { fileURLToPath } from "url";
53
53
  var require2 = createRequire(import.meta.url);
54
54
  var here = dirname(fileURLToPath(import.meta.url));
55
55
  var candidates = [join2(here, "..", "..", "package.json"), join2(here, "..", "package.json")];
56
- var pkgPath = candidates.find((p8) => existsSync(p8)) ?? candidates[0];
56
+ var pkgPath = candidates.find((p9) => existsSync(p9)) ?? candidates[0];
57
57
  var pkg = require2(pkgPath);
58
58
 
59
59
  // src/core/local-install-guard.ts
60
60
  function isLocalInstallSatisfied(cwd2) {
61
61
  const selfPkgPath = join3(cwd2, "package.json");
62
- let projectPkg = null;
63
62
  if (existsSync2(selfPkgPath)) {
64
63
  try {
65
64
  const selfPkg = JSON.parse(readFileSync(selfPkgPath, "utf8"));
66
65
  if (selfPkg?.name === pkg.name) return true;
67
- projectPkg = selfPkg;
68
66
  } catch {
69
67
  }
70
68
  }
69
+ return hasRealLocalInstall(cwd2);
70
+ }
71
+ function hasRealLocalInstall(cwd2) {
71
72
  const [scope, name] = pkg.name.split("/");
72
73
  const localPath = pkg.name.startsWith("@") ? join3(cwd2, "node_modules", scope, name) : join3(cwd2, "node_modules", pkg.name);
73
74
  if (existsSync2(localPath)) return true;
74
75
  const isPnp = existsSync2(join3(cwd2, ".pnp.cjs")) || existsSync2(join3(cwd2, ".pnp.loader.mjs"));
75
- if (isPnp && projectPkg) {
76
- const deps = {
77
- ...projectPkg.dependencies ?? {},
78
- ...projectPkg.devDependencies ?? {}
79
- };
80
- if (Object.prototype.hasOwnProperty.call(deps, pkg.name)) return true;
76
+ if (isPnp) {
77
+ const pkgPath2 = join3(cwd2, "package.json");
78
+ if (existsSync2(pkgPath2)) {
79
+ try {
80
+ const projectPkg = JSON.parse(readFileSync(pkgPath2, "utf8"));
81
+ const deps = {
82
+ ...projectPkg?.dependencies ?? {},
83
+ ...projectPkg?.devDependencies ?? {}
84
+ };
85
+ if (Object.prototype.hasOwnProperty.call(deps, pkg.name)) return true;
86
+ } catch {
87
+ }
88
+ }
81
89
  }
82
90
  return false;
83
91
  }
@@ -132,7 +140,7 @@ function detectFromPackageManagerField(cwd2) {
132
140
  }
133
141
  function getMcpCommandParts(pm, port, cwd2) {
134
142
  const portStr = String(port);
135
- if (!isLocalInstallSatisfied(cwd2)) {
143
+ if (!hasRealLocalInstall(cwd2)) {
136
144
  return ["ahk", "serve", "--port", portStr];
137
145
  }
138
146
  switch (pm) {
@@ -380,6 +388,17 @@ function mergeCodexConfigToml(filePath, port, cwd2, pm = "npm") {
380
388
  content = mergeTomlSection(content, "mcp_servers.agent-harness-kit", sectionBody);
381
389
  writeFileSync2(filePath, content, "utf8");
382
390
  }
391
+ function mergeGrokConfigToml(filePath, port, cwd2, pm = "npm") {
392
+ mkdirSync2(dirname2(filePath), { recursive: true });
393
+ let content = "";
394
+ if (existsSync4(filePath)) {
395
+ content = readFileSync3(filePath, "utf8");
396
+ }
397
+ const [command, ...args] = getMcpCommandParts(pm, port, cwd2);
398
+ const sectionBody = [`command = ${JSON.stringify(command)}`, `args = ${JSON.stringify(args)}`].join("\n");
399
+ content = mergeTomlSection(content, "mcp_servers.agent-harness-kit", sectionBody);
400
+ writeFileSync2(filePath, content, "utf8");
401
+ }
383
402
 
384
403
  // src/core/materializer/scaffold-utils.ts
385
404
  import { createHash } from "crypto";
@@ -420,6 +439,9 @@ These tools may still appear available to you. The sandbox will reject the call.
420
439
  function codexRestrictionNotice(agentName) {
421
440
  return restrictionFor(agentName) === "no-write" ? CODEX_READ_ONLY_NOTICE : "";
422
441
  }
442
+ function grokToolsAllowlist(agentName) {
443
+ return restrictionFor(agentName) === "no-write" ? ["Bash", "Read", "NotebookRead", "Grep", "Glob", "WebFetch", "WebSearch", "search_tool", "use_tool"] : [];
444
+ }
423
445
 
424
446
  // src/core/materializer/templates.ts
425
447
  var __dirname = dirname3(fileURLToPath2(import.meta.url));
@@ -849,6 +871,11 @@ function translateFrontmatterForOpenCode(md, agentName) {
849
871
  result = stripFrontmatterBlockSequence(result, "disallowedTools");
850
872
  return appendFrontmatterMapping(result, "permission", opencodePermissions(agentName));
851
873
  }
874
+ function translateFrontmatterForGrok(md, agentName) {
875
+ let result = stripFrontmatterBlockSequence(md, "tools");
876
+ result = stripFrontmatterBlockSequence(result, "disallowedTools");
877
+ return appendFrontmatterBlockSequence(result, "tools", grokToolsAllowlist(agentName));
878
+ }
852
879
  var GITIGNORE_ENTRIES = `
853
880
  # agent-harness-kit
854
881
  .harness/harness.db
@@ -1064,7 +1091,7 @@ No tasks in progress.
1064
1091
  ],
1065
1092
  { force: opts.force, backupRoot: join7(cwd2, config.storage.dir, "backups") }
1066
1093
  );
1067
- const agents = writeAgentFiles(cwd2, claudeAgentFiles(config), {
1094
+ const agents = writeAgentFiles(cwd2, claudeAgentFiles(config, opts.claudeAgentModels), {
1068
1095
  force: opts.force,
1069
1096
  backupRoot: join7(cwd2, config.storage.dir, "backups")
1070
1097
  });
@@ -1163,9 +1190,73 @@ No tasks in progress.
1163
1190
  }
1164
1191
  };
1165
1192
 
1166
- // src/core/materializer/opencode.ts
1193
+ // src/core/materializer/grok.ts
1167
1194
  import { existsSync as existsSync8, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "fs";
1168
1195
  import { join as join9, resolve as resolve4 } from "path";
1196
+ function grokAgentFiles(config) {
1197
+ const projectName = config.project.name;
1198
+ return [
1199
+ { relPath: ".grok/agents/lead.md", content: translateFrontmatterForGrok(agentLead({ projectName }), "lead") },
1200
+ { relPath: ".grok/agents/explorer.md", content: translateFrontmatterForGrok(agentExplorer({ projectName }), "explorer") },
1201
+ { relPath: ".grok/agents/consultant.md", content: translateFrontmatterForGrok(agentConsultant({ projectName }), "consultant") },
1202
+ { relPath: ".grok/agents/builder.md", content: translateFrontmatterForGrok(agentBuilder({ projectName }), "builder") },
1203
+ { relPath: ".grok/agents/reviewer.md", content: translateFrontmatterForGrok(agentReviewer({ projectName }), "reviewer") }
1204
+ ];
1205
+ }
1206
+ var GrokMaterializer = class {
1207
+ async scaffold(config, opts) {
1208
+ const { cwd: cwd2 } = opts;
1209
+ const write2 = (relPath, content, mode) => {
1210
+ const abs = join9(cwd2, relPath);
1211
+ mkdirSync5(resolve4(abs, ".."), { recursive: true });
1212
+ writeFileSync5(abs, content, { encoding: "utf8", mode });
1213
+ };
1214
+ write2("AGENTS.md", stampGenerated(agentsMd(config)));
1215
+ if (!existsSync8(join9(cwd2, "health.sh"))) {
1216
+ write2("health.sh", HEALTH_SH, 493);
1217
+ }
1218
+ if (config.storage.scope === "local" && !existsSync8(join9(cwd2, config.storage.markdownFallback.path))) {
1219
+ write2(
1220
+ config.storage.markdownFallback.path,
1221
+ `<!-- AUTO-GENERATED by agent-harness-kit \u2014 DO NOT EDIT MANUALLY -->
1222
+ <!-- Run ahk status to refresh -->
1223
+
1224
+ # Current Session
1225
+
1226
+ No tasks in progress.
1227
+ `
1228
+ );
1229
+ }
1230
+ writeAgentFiles(cwd2, grokAgentFiles(config));
1231
+ mergeGrokConfigToml(join9(cwd2, ".grok/config.toml"), config.tools.mcp.port, cwd2, detectPackageManager(cwd2));
1232
+ appendGitignore(cwd2);
1233
+ writeSkills(cwd2, ".grok/skills");
1234
+ }
1235
+ async build(config, cwd2, opts = {}) {
1236
+ const derived = reconcileGeneratedFiles(
1237
+ cwd2,
1238
+ [{ relPath: "AGENTS.md", content: agentsMd(config) }],
1239
+ { force: opts.force, backupRoot: join9(cwd2, config.storage.dir, "backups") }
1240
+ );
1241
+ const agents = writeAgentFiles(cwd2, grokAgentFiles(config), {
1242
+ force: opts.force,
1243
+ backupRoot: join9(cwd2, config.storage.dir, "backups")
1244
+ });
1245
+ mergeGrokConfigToml(join9(cwd2, ".grok/config.toml"), config.tools.mcp.port, cwd2, detectPackageManager(cwd2));
1246
+ writeSkills(cwd2, ".grok/skills");
1247
+ return { agents, derived };
1248
+ }
1249
+ async migrate(config, _to, _cwd) {
1250
+ void config;
1251
+ }
1252
+ async syncPermissions(_cwd) {
1253
+ console.log(" Permissions sync not needed for grok-cli \u2014 skipping");
1254
+ }
1255
+ };
1256
+
1257
+ // src/core/materializer/opencode.ts
1258
+ import { existsSync as existsSync9, mkdirSync as mkdirSync6, writeFileSync as writeFileSync6 } from "fs";
1259
+ import { join as join10, resolve as resolve5 } from "path";
1169
1260
  function opencodeAgentFiles(config) {
1170
1261
  const projectName = config.project.name;
1171
1262
  return [
@@ -1180,15 +1271,15 @@ var OpenCodeMaterializer = class {
1180
1271
  async scaffold(config, opts) {
1181
1272
  const { cwd: cwd2 } = opts;
1182
1273
  const write2 = (relPath, content, mode) => {
1183
- const abs = join9(cwd2, relPath);
1184
- mkdirSync5(resolve4(abs, ".."), { recursive: true });
1185
- writeFileSync5(abs, content, { encoding: "utf8", mode });
1274
+ const abs = join10(cwd2, relPath);
1275
+ mkdirSync6(resolve5(abs, ".."), { recursive: true });
1276
+ writeFileSync6(abs, content, { encoding: "utf8", mode });
1186
1277
  };
1187
1278
  write2("AGENTS.md", stampGenerated(agentsMd(config)));
1188
- if (!existsSync8(join9(cwd2, "health.sh"))) {
1279
+ if (!existsSync9(join10(cwd2, "health.sh"))) {
1189
1280
  write2("health.sh", HEALTH_SH, 493);
1190
1281
  }
1191
- if (config.storage.scope === "local" && !existsSync8(join9(cwd2, config.storage.markdownFallback.path))) {
1282
+ if (config.storage.scope === "local" && !existsSync9(join10(cwd2, config.storage.markdownFallback.path))) {
1192
1283
  write2(
1193
1284
  config.storage.markdownFallback.path,
1194
1285
  `<!-- AUTO-GENERATED by agent-harness-kit \u2014 DO NOT EDIT MANUALLY -->
@@ -1201,7 +1292,7 @@ No tasks in progress.
1201
1292
  );
1202
1293
  }
1203
1294
  writeAgentFiles(cwd2, opencodeAgentFiles(config));
1204
- mergeOpencodeJson(join9(cwd2, "opencode.json"), config.tools.mcp.port, cwd2, detectPackageManager(cwd2));
1295
+ mergeOpencodeJson(join10(cwd2, "opencode.json"), config.tools.mcp.port, cwd2, detectPackageManager(cwd2));
1205
1296
  appendGitignore(cwd2);
1206
1297
  writeSkills(cwd2, ".opencode/skills");
1207
1298
  }
@@ -1209,13 +1300,13 @@ No tasks in progress.
1209
1300
  const derived = reconcileGeneratedFiles(
1210
1301
  cwd2,
1211
1302
  [{ relPath: "AGENTS.md", content: agentsMd(config) }],
1212
- { force: opts.force, backupRoot: join9(cwd2, config.storage.dir, "backups") }
1303
+ { force: opts.force, backupRoot: join10(cwd2, config.storage.dir, "backups") }
1213
1304
  );
1214
1305
  const agents = writeAgentFiles(cwd2, opencodeAgentFiles(config), {
1215
1306
  force: opts.force,
1216
- backupRoot: join9(cwd2, config.storage.dir, "backups")
1307
+ backupRoot: join10(cwd2, config.storage.dir, "backups")
1217
1308
  });
1218
- mergeOpencodeJson(join9(cwd2, "opencode.json"), config.tools.mcp.port, cwd2, detectPackageManager(cwd2));
1309
+ mergeOpencodeJson(join10(cwd2, "opencode.json"), config.tools.mcp.port, cwd2, detectPackageManager(cwd2));
1219
1310
  writeSkills(cwd2, ".opencode/skills");
1220
1311
  return { agents, derived };
1221
1312
  }
@@ -1236,25 +1327,60 @@ function getMaterializer(provider) {
1236
1327
  return new OpenCodeMaterializer();
1237
1328
  case "codex-cli":
1238
1329
  return new CodexCliMaterializer();
1330
+ case "grok-cli":
1331
+ return new GrokMaterializer();
1239
1332
  default:
1240
1333
  throw new Error(`Unknown provider: ${provider}`);
1241
1334
  }
1242
1335
  }
1243
1336
 
1337
+ // src/commands/claude-model-prompt.ts
1338
+ import * as p from "@clack/prompts";
1339
+ var AGENT_LABELS = [
1340
+ { key: "lead", label: "Lead" },
1341
+ { key: "explorer", label: "Explorer" },
1342
+ { key: "consultant", label: "Consultant" },
1343
+ { key: "builder", label: "Builder" },
1344
+ { key: "reviewer", label: "Reviewer" }
1345
+ ];
1346
+ async function promptClaudeAgentModels(provider) {
1347
+ const claudeAgentModels = {};
1348
+ if (provider !== "claude-code") return claudeAgentModels;
1349
+ for (const agent of AGENT_LABELS) {
1350
+ const val = await p.select({
1351
+ message: `Model for ${agent.label}`,
1352
+ options: [
1353
+ { value: "inherit", label: "inherit (default)" },
1354
+ { value: "haiku", label: "haiku" },
1355
+ { value: "sonnet", label: "sonnet" },
1356
+ { value: "opus", label: "opus" },
1357
+ { value: "fable", label: "fable" }
1358
+ ],
1359
+ initialValue: "inherit"
1360
+ });
1361
+ if (p.isCancel(val)) {
1362
+ p.cancel("Cancelled.");
1363
+ process.exit(0);
1364
+ }
1365
+ claudeAgentModels[agent.key] = val;
1366
+ }
1367
+ return claudeAgentModels;
1368
+ }
1369
+
1244
1370
  // src/commands/build.ts
1245
1371
  async function runBuild(cwd2, opts) {
1246
1372
  await buildOnce(cwd2, opts.force);
1247
1373
  if (opts.sync) {
1248
- p.log.step("Syncing agent permissions...");
1374
+ p2.log.step("Syncing agent permissions...");
1249
1375
  const config = await loadConfig(cwd2);
1250
1376
  const materializer = getMaterializer(config.provider);
1251
1377
  await materializer.syncPermissions(cwd2);
1252
1378
  }
1253
1379
  if (opts.watch) {
1254
- p.log.info(`Watching agent-harness-kit.config.ts for changes...`);
1380
+ p2.log.info(`Watching agent-harness-kit.config.ts for changes...`);
1255
1381
  watch(cwd2, { recursive: false }, async (_, filename) => {
1256
1382
  if (filename?.startsWith("agent-harness-kit.config")) {
1257
- p.log.step("Config changed \u2014 rebuilding...");
1383
+ p2.log.step("Config changed \u2014 rebuilding...");
1258
1384
  await buildOnce(cwd2, false);
1259
1385
  }
1260
1386
  });
@@ -1263,36 +1389,45 @@ async function runBuild(cwd2, opts) {
1263
1389
  }
1264
1390
  }
1265
1391
  async function buildOnce(cwd2, force) {
1266
- const spinner6 = p.spinner();
1267
- spinner6.start("Loading config...");
1392
+ let config;
1393
+ try {
1394
+ config = await loadConfig(cwd2);
1395
+ } catch (err) {
1396
+ p2.log.error(err instanceof Error ? err.message : String(err));
1397
+ process.exit(1);
1398
+ }
1399
+ let claudeAgentModels;
1400
+ if (force && config.provider === "claude-code") {
1401
+ claudeAgentModels = await promptClaudeAgentModels(config.provider);
1402
+ }
1403
+ const spinner6 = p2.spinner();
1404
+ spinner6.start("Rebuilding files...");
1268
1405
  try {
1269
- const config = await loadConfig(cwd2);
1270
- spinner6.message("Rebuilding files...");
1271
1406
  const materializer = getMaterializer(config.provider);
1272
- const report = await materializer.build(config, cwd2, { force });
1407
+ const report = await materializer.build(config, cwd2, { force, claudeAgentModels });
1273
1408
  spinner6.stop(pc2.green("Build complete"));
1274
1409
  const d = report.derived;
1275
1410
  const upToDate = [...d.created, ...d.current, ...d.propagated];
1276
1411
  if (upToDate.length > 0) {
1277
- p.log.success(upToDate.join(", "));
1412
+ p2.log.success(upToDate.join(", "));
1278
1413
  }
1279
1414
  if (d.propagated.length > 0) {
1280
- p.log.info(`Propagated config changes to ${d.propagated.length} generated file(s):
1415
+ p2.log.info(`Propagated config changes to ${d.propagated.length} generated file(s):
1281
1416
  ${d.propagated.join("\n ")}`);
1282
1417
  }
1283
1418
  if (d.overwritten.length > 0) {
1284
- p.log.warn(
1419
+ p2.log.warn(
1285
1420
  pc2.yellow(
1286
1421
  `--force REGENERATED ${d.overwritten.length} hand-edited generated file(s), discarding your edits:
1287
1422
  ` + d.overwritten.join("\n ")
1288
1423
  )
1289
1424
  );
1290
1425
  if (d.backupDir) {
1291
- p.log.info(pc2.yellow(` Previous content backed up \u2192 ${d.backupDir}`));
1426
+ p2.log.info(pc2.yellow(` Previous content backed up \u2192 ${d.backupDir}`));
1292
1427
  }
1293
1428
  }
1294
1429
  if (d.preserved.length > 0) {
1295
- p.log.warn(
1430
+ p2.log.warn(
1296
1431
  pc2.yellow(
1297
1432
  `Left ${d.preserved.length} hand-edited generated file(s) UNTOUCHED \u2014 your edits are safe:
1298
1433
  ` + d.preserved.join("\n ") + `
@@ -1301,26 +1436,26 @@ async function buildOnce(cwd2, force) {
1301
1436
  )
1302
1437
  );
1303
1438
  }
1304
- p.log.success(`Agent definitions (${config.provider})`);
1305
- p.log.success("MCP config");
1439
+ p2.log.success(`Agent definitions (${config.provider})`);
1440
+ p2.log.success("MCP config");
1306
1441
  const { created, overwritten, preserved, backupDir } = report.agents;
1307
1442
  if (created.length > 0) {
1308
- p.log.info(`Created ${created.length} missing agent file(s):
1443
+ p2.log.info(`Created ${created.length} missing agent file(s):
1309
1444
  ${created.join("\n ")}`);
1310
1445
  }
1311
1446
  if (overwritten.length > 0) {
1312
- p.log.warn(
1447
+ p2.log.warn(
1313
1448
  pc2.yellow(
1314
1449
  `--force REGENERATED ${overwritten.length} existing agent file(s), discarding any customizations:
1315
1450
  ` + overwritten.join("\n ")
1316
1451
  )
1317
1452
  );
1318
1453
  if (backupDir) {
1319
- p.log.info(pc2.yellow(` Previous content backed up \u2192 ${backupDir}`));
1454
+ p2.log.info(pc2.yellow(` Previous content backed up \u2192 ${backupDir}`));
1320
1455
  }
1321
1456
  }
1322
1457
  if (preserved.length > 0) {
1323
- p.log.info(
1458
+ p2.log.info(
1324
1459
  `Left ${preserved.length} existing agent file(s) untouched \u2014 agent files are yours to edit.
1325
1460
  Re-run with --force to regenerate them from the packaged templates (this DESTROYS your edits;
1326
1461
  a backup is written first).`
@@ -1328,21 +1463,21 @@ async function buildOnce(cwd2, force) {
1328
1463
  }
1329
1464
  } catch (err) {
1330
1465
  spinner6.stop(pc2.red("Build failed"));
1331
- p.log.error(err instanceof Error ? err.message : String(err));
1466
+ p2.log.error(err instanceof Error ? err.message : String(err));
1332
1467
  process.exit(1);
1333
1468
  }
1334
1469
  }
1335
1470
 
1336
1471
  // src/commands/dashboard.ts
1337
1472
  import { homedir } from "os";
1338
- import { dirname as dirname5, join as join11 } from "path";
1473
+ import { dirname as dirname5, join as join12 } from "path";
1339
1474
  import { fileURLToPath as fileURLToPath4 } from "url";
1340
1475
  import pc3 from "picocolors";
1341
1476
 
1342
1477
  // src/core/dashboard-server.ts
1343
1478
  import { watch as watch2 } from "fs";
1344
- import { existsSync as existsSync9, readFileSync as readFileSync6 } from "fs";
1345
- import { extname, join as join10 } from "path";
1479
+ import { existsSync as existsSync10, readFileSync as readFileSync6 } from "fs";
1480
+ import { extname, join as join11 } from "path";
1346
1481
  import { serve } from "@hono/node-server";
1347
1482
  import { Hono } from "hono";
1348
1483
  import { WebSocketServer } from "ws";
@@ -1351,11 +1486,11 @@ import { WebSocketServer } from "ws";
1351
1486
  import { createServer } from "net";
1352
1487
  var DASHBOARD_BIND_HOST = void 0;
1353
1488
  function isPortFree(port, host = DASHBOARD_BIND_HOST) {
1354
- return new Promise((resolve11) => {
1489
+ return new Promise((resolve12) => {
1355
1490
  const server = createServer();
1356
- server.once("error", () => resolve11(false));
1491
+ server.once("error", () => resolve12(false));
1357
1492
  server.once("listening", () => {
1358
- server.close(() => resolve11(true));
1493
+ server.close(() => resolve12(true));
1359
1494
  });
1360
1495
  server.listen(port, host);
1361
1496
  });
@@ -1398,7 +1533,7 @@ function fileResponse(filePath) {
1398
1533
  });
1399
1534
  }
1400
1535
  function awaitServerListening(server, port) {
1401
- return new Promise((resolve11, reject) => {
1536
+ return new Promise((resolve12, reject) => {
1402
1537
  const closeQuietly = () => {
1403
1538
  try {
1404
1539
  server.close(() => {
@@ -1421,7 +1556,7 @@ function awaitServerListening(server, port) {
1421
1556
  };
1422
1557
  const onListening = () => {
1423
1558
  server.off("error", onError);
1424
- resolve11();
1559
+ resolve12();
1425
1560
  };
1426
1561
  server.once("error", onError);
1427
1562
  server.once("listening", onListening);
@@ -1527,15 +1662,15 @@ async function startDashboardServer(db, dbPath, staticPath, port) {
1527
1662
  app.get("/*", (c) => {
1528
1663
  const urlPath = c.req.path;
1529
1664
  if (urlPath !== "/") {
1530
- const candidate = join10(staticPath, urlPath);
1531
- if (existsSync9(candidate)) {
1665
+ const candidate = join11(staticPath, urlPath);
1666
+ if (existsSync10(candidate)) {
1532
1667
  try {
1533
1668
  return fileResponse(candidate);
1534
1669
  } catch {
1535
1670
  }
1536
1671
  }
1537
1672
  }
1538
- return fileResponse(join10(staticPath, "index.html"));
1673
+ return fileResponse(join11(staticPath, "index.html"));
1539
1674
  });
1540
1675
  const resolvedPort = await findFreePort(port, { host: DASHBOARD_BIND_HOST });
1541
1676
  if (resolvedPort !== port) {
@@ -1574,7 +1709,7 @@ async function startDashboardServer(db, dbPath, staticPath, port) {
1574
1709
  let watcher = null;
1575
1710
  if (dbPath) {
1576
1711
  const walPath = `${dbPath}-wal`;
1577
- const watchTarget = existsSync9(walPath) ? walPath : dbPath;
1712
+ const watchTarget = existsSync10(walPath) ? walPath : dbPath;
1578
1713
  watcher = watch2(watchTarget, broadcast);
1579
1714
  }
1580
1715
  return {
@@ -1594,7 +1729,7 @@ async function runDashboard(cwd2, opts) {
1594
1729
  const config = await loadConfig(cwd2);
1595
1730
  const db = await openDB(config, cwd2);
1596
1731
  const dbPath = config.database.type === "sqlite" ? resolveSqlitePath(config, cwd2, homedir()) : null;
1597
- const staticPath = join11(__dirname3, "dashboard-dist");
1732
+ const staticPath = join12(__dirname3, "dashboard-dist");
1598
1733
  const { url } = await startDashboardServer(db, dbPath, staticPath, opts.port);
1599
1734
  console.log(pc3.green(`\u2713`) + ` Dashboard running at ${pc3.bold(pc3.cyan(url))}`);
1600
1735
  console.log(pc3.dim(` WebSocket live updates enabled`));
@@ -1614,8 +1749,8 @@ async function runDashboard(cwd2, opts) {
1614
1749
  import pc4 from "picocolors";
1615
1750
 
1616
1751
  // src/core/doctor.ts
1617
- import { existsSync as existsSync10, readFileSync as readFileSync7 } from "fs";
1618
- import { dirname as dirname6, join as join12 } from "path";
1752
+ import { existsSync as existsSync11, readFileSync as readFileSync7 } from "fs";
1753
+ import { dirname as dirname6, join as join13 } from "path";
1619
1754
  import { fileURLToPath as fileURLToPath5 } from "url";
1620
1755
  var REGISTRY_URL = `https://registry.npmjs.org/${pkg.name}/latest`;
1621
1756
  var TIMEOUT_MS = 2e3;
@@ -1664,19 +1799,21 @@ function getProviderAgentInfo(provider) {
1664
1799
  return { agentsDir: ".opencode/agents", ext: ".md" };
1665
1800
  case "codex-cli":
1666
1801
  return { agentsDir: ".codex/agents", ext: ".toml" };
1802
+ case "grok-cli":
1803
+ return { agentsDir: ".grok/agents", ext: ".md" };
1667
1804
  default:
1668
1805
  return { agentsDir: ".claude/agents", ext: ".md" };
1669
1806
  }
1670
1807
  }
1671
1808
  function checkAgentFilesAtRoot(agentsRoot, ext) {
1672
1809
  return AGENT_NAMES.map((name) => {
1673
- const filePath = join12(agentsRoot, `${name}${ext}`);
1674
- return { name, status: existsSync10(filePath) ? "ok" : "missing" };
1810
+ const filePath = join13(agentsRoot, `${name}${ext}`);
1811
+ return { name, status: existsSync11(filePath) ? "ok" : "missing" };
1675
1812
  });
1676
1813
  }
1677
1814
  function checkAgentFiles(cwd2, provider) {
1678
1815
  const { agentsDir, ext } = getProviderAgentInfo(provider);
1679
- return checkAgentFilesAtRoot(join12(cwd2, agentsDir), ext);
1816
+ return checkAgentFilesAtRoot(join13(cwd2, agentsDir), ext);
1680
1817
  }
1681
1818
  function getProviderSkillsDir(provider) {
1682
1819
  switch (provider) {
@@ -1686,16 +1823,18 @@ function getProviderSkillsDir(provider) {
1686
1823
  return ".opencode/skills";
1687
1824
  case "codex-cli":
1688
1825
  return ".agents/skills";
1826
+ case "grok-cli":
1827
+ return ".grok/skills";
1689
1828
  default:
1690
1829
  return ".claude/skills";
1691
1830
  }
1692
1831
  }
1693
1832
  function checkSkillsAtRoot(skillsRoot) {
1694
- const skillSourceBase = join12(__dirname4, "skills");
1833
+ const skillSourceBase = join13(__dirname4, "skills");
1695
1834
  return SKILL_NAMES.map((name) => {
1696
- const livePath = join12(skillsRoot, name, "SKILL.md");
1697
- const sourcePath = join12(skillSourceBase, name, "SKILL.md");
1698
- if (!existsSync10(livePath)) {
1835
+ const livePath = join13(skillsRoot, name, "SKILL.md");
1836
+ const sourcePath = join13(skillSourceBase, name, "SKILL.md");
1837
+ if (!existsSync11(livePath)) {
1699
1838
  return { name, status: "missing" };
1700
1839
  }
1701
1840
  try {
@@ -1709,7 +1848,7 @@ function checkSkillsAtRoot(skillsRoot) {
1709
1848
  }
1710
1849
  function checkSkills(cwd2, provider) {
1711
1850
  const skillsDir = getProviderSkillsDir(provider);
1712
- return checkSkillsAtRoot(join12(cwd2, skillsDir));
1851
+ return checkSkillsAtRoot(join13(cwd2, skillsDir));
1713
1852
  }
1714
1853
  async function getDoctorStatus(cwd2) {
1715
1854
  const lib = await checkLibVersion();
@@ -1810,7 +1949,7 @@ async function runDoctor(cwd2) {
1810
1949
  }
1811
1950
 
1812
1951
  // src/commands/export.ts
1813
- import { writeFileSync as writeFileSync6 } from "fs";
1952
+ import { writeFileSync as writeFileSync7 } from "fs";
1814
1953
  import pc5 from "picocolors";
1815
1954
  async function runExport(cwd2, opts) {
1816
1955
  if (!opts.sql && !opts.json) {
@@ -1824,7 +1963,7 @@ async function runExport(cwd2, opts) {
1824
1963
  const data = await db.exportJson();
1825
1964
  const out = JSON.stringify(data, null, 2) + "\n";
1826
1965
  if (opts.output) {
1827
- writeFileSync6(opts.output, out, "utf8");
1966
+ writeFileSync7(opts.output, out, "utf8");
1828
1967
  console.log(pc5.green(`\u2713 Exported JSON \u2192 ${opts.output}`));
1829
1968
  } else {
1830
1969
  process.stdout.write(out);
@@ -1841,9 +1980,9 @@ async function runExport(cwd2, opts) {
1841
1980
 
1842
1981
  // src/commands/health.ts
1843
1982
  import { spawnSync } from "child_process";
1844
- import { existsSync as existsSync11 } from "fs";
1983
+ import { existsSync as existsSync12 } from "fs";
1845
1984
  import { homedir as homedir2 } from "os";
1846
- import { join as join13, resolve as resolve5 } from "path";
1985
+ import { join as join14, resolve as resolve6 } from "path";
1847
1986
  import pc6 from "picocolors";
1848
1987
  function checkLine(label, ok3, message, indent = 0) {
1849
1988
  const prefix = label ? pc6.cyan(`[${label}] `) : " ".repeat(indent);
@@ -1862,7 +2001,7 @@ async function runHealth(cwd2) {
1862
2001
  let dbOk;
1863
2002
  if (config.database.type === "sqlite") {
1864
2003
  const dbPath = resolveSqlitePath(config, cwd2, homedir2());
1865
- dbOk = existsSync11(dbPath);
2004
+ dbOk = existsSync12(dbPath);
1866
2005
  checkLine("checking DB", dbOk, `${dbPath} reachable`);
1867
2006
  } else {
1868
2007
  dbOk = true;
@@ -1875,8 +2014,8 @@ async function runHealth(cwd2) {
1875
2014
  const agentsLabelWidth = "[checking agents] ".length;
1876
2015
  for (let i = 0; i < agentNames.length; i++) {
1877
2016
  const name = agentNames[i];
1878
- const agentPath = join13(cwd2, agentsDir, `${name}${providerFiles.agentExtension}`);
1879
- const ok3 = existsSync11(agentPath);
2017
+ const agentPath = join14(cwd2, agentsDir, `${name}${providerFiles.agentExtension}`);
2018
+ const ok3 = existsSync12(agentPath);
1880
2019
  checkLine(
1881
2020
  i === 0 ? "checking agents" : null,
1882
2021
  ok3,
@@ -1887,8 +2026,8 @@ async function runHealth(cwd2) {
1887
2026
  }
1888
2027
  if (config.tools.mcp.enabled) {
1889
2028
  const mcpFile = providerFiles.mcpFile;
1890
- const mcpPath = resolve5(cwd2, mcpFile);
1891
- const mcpOk = existsSync11(mcpPath);
2029
+ const mcpPath = resolve6(cwd2, mcpFile);
2030
+ const mcpOk = existsSync12(mcpPath);
1892
2031
  checkLine("checking MCP", mcpOk, `${mcpFile} valid`);
1893
2032
  if (!mcpOk) allOk = false;
1894
2033
  }
@@ -1897,8 +2036,8 @@ async function runHealth(cwd2) {
1897
2036
  console.error(pc6.red("\u2717 Harness checks failed \u2014 fix the above before running health.sh"));
1898
2037
  process.exit(1);
1899
2038
  }
1900
- const scriptPath = resolve5(cwd2, config.health.scriptPath);
1901
- if (!existsSync11(scriptPath)) {
2039
+ const scriptPath = resolve6(cwd2, config.health.scriptPath);
2040
+ if (!existsSync12(scriptPath)) {
1902
2041
  console.error(pc6.red(`\u2717 health.sh not found: ${scriptPath}`));
1903
2042
  console.error(" Run ahk init first.");
1904
2043
  process.exit(1);
@@ -1928,15 +2067,17 @@ function getProviderHealthFiles(provider) {
1928
2067
  return { agentsDir: ".opencode/agents", agentExtension: ".md", mcpFile: "opencode.json" };
1929
2068
  case "codex-cli":
1930
2069
  return { agentsDir: ".codex/agents", agentExtension: ".toml", mcpFile: ".codex/config.toml" };
2070
+ case "grok-cli":
2071
+ return { agentsDir: ".grok/agents", agentExtension: ".md", mcpFile: ".grok/config.toml" };
1931
2072
  default:
1932
2073
  throw new Error(`Unknown provider: ${provider}`);
1933
2074
  }
1934
2075
  }
1935
2076
 
1936
2077
  // src/commands/init.ts
1937
- import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
1938
- import { join as join15 } from "path";
1939
- import * as p3 from "@clack/prompts";
2078
+ import { existsSync as existsSync14, mkdirSync as mkdirSync7, readFileSync as readFileSync9, writeFileSync as writeFileSync8 } from "fs";
2079
+ import { join as join16 } from "path";
2080
+ import * as p4 from "@clack/prompts";
1940
2081
  import pc8 from "picocolors";
1941
2082
 
1942
2083
  // src/schema/init.ts
@@ -1975,7 +2116,7 @@ var taskDescriptionSchema = v2.pipe(
1975
2116
  );
1976
2117
 
1977
2118
  // src/utils/form.ts
1978
- import * as p2 from "@clack/prompts";
2119
+ import * as p3 from "@clack/prompts";
1979
2120
  import * as v3 from "valibot";
1980
2121
  var cliFormWithRetry = async (formFn, schema) => {
1981
2122
  while (true) {
@@ -1983,20 +2124,20 @@ var cliFormWithRetry = async (formFn, schema) => {
1983
2124
  const result = v3.safeParse(schema, res);
1984
2125
  if (result.success) return result.output;
1985
2126
  const messages = result.issues.map((i) => i.message).join(", ");
1986
- p2.log.error(messages);
1987
- p2.log.info("Please try again.\n");
2127
+ p3.log.error(messages);
2128
+ p3.log.info("Please try again.\n");
1988
2129
  }
1989
2130
  };
1990
2131
 
1991
2132
  // src/commands/init-helpers.ts
1992
2133
  import { randomUUID } from "crypto";
1993
- import { existsSync as existsSync12, readFileSync as readFileSync8 } from "fs";
1994
- import { join as join14 } from "path";
2134
+ import { existsSync as existsSync13, readFileSync as readFileSync8 } from "fs";
2135
+ import { join as join15 } from "path";
1995
2136
  import pc7 from "picocolors";
1996
2137
  function readProjectNameFromPackageJson(cwd2) {
1997
2138
  try {
1998
- const pkgPath2 = join14(cwd2, "package.json");
1999
- if (!existsSync12(pkgPath2)) return null;
2139
+ const pkgPath2 = join15(cwd2, "package.json");
2140
+ if (!existsSync13(pkgPath2)) return null;
2000
2141
  const content = readFileSync8(pkgPath2, "utf8");
2001
2142
  const pkg2 = JSON.parse(content);
2002
2143
  const name = pkg2?.name;
@@ -2009,9 +2150,9 @@ function readProjectNameFromPackageJson(cwd2) {
2009
2150
  function detectConfigExtension(cwd2) {
2010
2151
  if (!isLocalInstallSatisfied(cwd2)) return "json";
2011
2152
  try {
2012
- if (existsSync12(join14(cwd2, "tsconfig.json"))) return "ts";
2013
- const pkgPath2 = join14(cwd2, "package.json");
2014
- if (!existsSync12(pkgPath2)) return "mjs";
2153
+ if (existsSync13(join15(cwd2, "tsconfig.json"))) return "ts";
2154
+ const pkgPath2 = join15(cwd2, "package.json");
2155
+ if (!existsSync13(pkgPath2)) return "mjs";
2015
2156
  const pkg2 = JSON.parse(readFileSync8(pkgPath2, "utf8"));
2016
2157
  if (pkg2?.type === "module") return "mjs";
2017
2158
  } catch {
@@ -2092,10 +2233,10 @@ function printWelcomeMessage(projectName) {
2092
2233
 
2093
2234
  // src/commands/init.ts
2094
2235
  async function reconcileFeatureList(db, installDir, storageDir, firstTask) {
2095
- const featureListPath = join15(installDir, storageDir, "feature_list.json");
2236
+ const featureListPath = join16(installDir, storageDir, "feature_list.json");
2096
2237
  let existingSeeds = [];
2097
2238
  let parseFailed = false;
2098
- if (existsSync13(featureListPath)) {
2239
+ if (existsSync14(featureListPath)) {
2099
2240
  try {
2100
2241
  const parsed = JSON.parse(readFileSync9(featureListPath, "utf8"));
2101
2242
  if (!Array.isArray(parsed)) throw new Error("feature_list.json is not a JSON array");
@@ -2145,86 +2286,60 @@ async function runInit(cwd2, flags) {
2145
2286
  name = flags.name;
2146
2287
  } else {
2147
2288
  name = await cliFormWithRetry(async () => {
2148
- const val = await p3.text({
2289
+ const val = await p4.text({
2149
2290
  message: "Project name",
2150
2291
  placeholder: "my-app",
2151
2292
  ...detectedName && { initialValue: detectedName }
2152
2293
  });
2153
- if (p3.isCancel(val)) {
2154
- p3.cancel("Cancelled.");
2294
+ if (p4.isCancel(val)) {
2295
+ p4.cancel("Cancelled.");
2155
2296
  process.exit(0);
2156
2297
  }
2157
2298
  return val;
2158
2299
  }, initNameSchema);
2159
2300
  }
2160
2301
  const description = await cliFormWithRetry(async () => {
2161
- const val = await p3.text({
2302
+ const val = await p4.text({
2162
2303
  message: "Short description (shown to agents as context)",
2163
2304
  placeholder: "A REST API for managing notes"
2164
2305
  });
2165
- if (p3.isCancel(val)) {
2166
- p3.cancel("Cancelled.");
2306
+ if (p4.isCancel(val)) {
2307
+ p4.cancel("Cancelled.");
2167
2308
  process.exit(0);
2168
2309
  }
2169
2310
  return val;
2170
2311
  }, initDescriptionSchema);
2171
2312
  let provider;
2172
- if (flags.provider && ["claude-code", "opencode"].includes(flags.provider)) {
2313
+ if (flags.provider && ["claude-code", "opencode", "codex-cli", "grok-cli"].includes(flags.provider)) {
2173
2314
  provider = flags.provider;
2174
2315
  } else {
2175
- const val = await p3.select({
2316
+ const val = await p4.select({
2176
2317
  message: "AI provider",
2177
2318
  options: [
2178
2319
  { value: "opencode", label: "OpenCode" },
2179
2320
  { value: "claude-code", label: "Claude Code" },
2180
- { value: "codex-cli", label: "Codex CLI" }
2321
+ { value: "codex-cli", label: "Codex CLI" },
2322
+ { value: "grok-cli", label: "Grok CLI" }
2181
2323
  ]
2182
2324
  });
2183
- if (p3.isCancel(val)) {
2184
- p3.cancel("Cancelled.");
2325
+ if (p4.isCancel(val)) {
2326
+ p4.cancel("Cancelled.");
2185
2327
  process.exit(0);
2186
2328
  }
2187
2329
  provider = val;
2188
2330
  }
2189
- const AGENT_LABELS = [
2190
- { key: "lead", label: "Lead" },
2191
- { key: "explorer", label: "Explorer" },
2192
- { key: "consultant", label: "Consultant" },
2193
- { key: "builder", label: "Builder" },
2194
- { key: "reviewer", label: "Reviewer" }
2195
- ];
2196
- const claudeAgentModels = {};
2197
- if (provider === "claude-code") {
2198
- for (const agent of AGENT_LABELS) {
2199
- const val = await p3.select({
2200
- message: `Model for ${agent.label}`,
2201
- options: [
2202
- { value: "inherit", label: "inherit (default)" },
2203
- { value: "haiku", label: "haiku" },
2204
- { value: "sonnet", label: "sonnet" },
2205
- { value: "opus", label: "opus" },
2206
- { value: "fable", label: "fable" }
2207
- ],
2208
- initialValue: "inherit"
2209
- });
2210
- if (p3.isCancel(val)) {
2211
- p3.cancel("Cancelled.");
2212
- process.exit(0);
2213
- }
2214
- claudeAgentModels[agent.key] = val;
2215
- }
2216
- }
2331
+ const claudeAgentModels = await promptClaudeAgentModels(provider);
2217
2332
  let docsPath;
2218
2333
  if (flags.docs) {
2219
2334
  docsPath = flags.docs;
2220
2335
  } else {
2221
2336
  docsPath = await cliFormWithRetry(async () => {
2222
- const val = await p3.text({
2337
+ const val = await p4.text({
2223
2338
  message: "Docs folder path (agents will search here)",
2224
2339
  initialValue: "./docs"
2225
2340
  });
2226
- if (p3.isCancel(val)) {
2227
- p3.cancel("Cancelled.");
2341
+ if (p4.isCancel(val)) {
2342
+ p4.cancel("Cancelled.");
2228
2343
  process.exit(0);
2229
2344
  }
2230
2345
  return val;
@@ -2234,7 +2349,7 @@ async function runInit(cwd2, flags) {
2234
2349
  if (flags.storageScope && ["local", "global"].includes(flags.storageScope)) {
2235
2350
  storageScope = flags.storageScope;
2236
2351
  } else {
2237
- const val = await p3.select({
2352
+ const val = await p4.select({
2238
2353
  message: "Storage scope",
2239
2354
  options: [
2240
2355
  { value: "local", label: "Local \u2014 .harness/harness.db lives in this project" },
@@ -2245,8 +2360,8 @@ async function runInit(cwd2, flags) {
2245
2360
  ],
2246
2361
  initialValue: "local"
2247
2362
  });
2248
- if (p3.isCancel(val)) {
2249
- p3.cancel("Cancelled.");
2363
+ if (p4.isCancel(val)) {
2364
+ p4.cancel("Cancelled.");
2250
2365
  process.exit(0);
2251
2366
  }
2252
2367
  storageScope = val;
@@ -2255,7 +2370,7 @@ async function runInit(cwd2, flags) {
2255
2370
  if (flags.tasks && ["local", "jira", "linear"].includes(flags.tasks)) {
2256
2371
  tasksAdapter = flags.tasks;
2257
2372
  } else {
2258
- const val = await p3.select({
2373
+ const val = await p4.select({
2259
2374
  message: "Task adapter",
2260
2375
  options: [
2261
2376
  { value: "local", label: "Local (feature_list.json)" },
@@ -2263,50 +2378,50 @@ async function runInit(cwd2, flags) {
2263
2378
  { value: "linear", label: "Linear (coming soon)" }
2264
2379
  ]
2265
2380
  });
2266
- if (p3.isCancel(val)) {
2267
- p3.cancel("Cancelled");
2381
+ if (p4.isCancel(val)) {
2382
+ p4.cancel("Cancelled");
2268
2383
  process.exit(0);
2269
2384
  }
2270
2385
  tasksAdapter = val;
2271
2386
  }
2272
- const addFirstTask = await p3.confirm({ message: "Add your first task now?", initialValue: false });
2273
- if (p3.isCancel(addFirstTask)) {
2274
- p3.cancel("Cancelled");
2387
+ const addFirstTask = await p4.confirm({ message: "Add your first task now?", initialValue: false });
2388
+ if (p4.isCancel(addFirstTask)) {
2389
+ p4.cancel("Cancelled");
2275
2390
  process.exit(0);
2276
2391
  }
2277
2392
  let firstTask;
2278
2393
  if (addFirstTask) {
2279
2394
  const taskTitle = await cliFormWithRetry(async () => {
2280
- const val = await p3.text({ message: "Task title" });
2281
- if (p3.isCancel(val)) {
2282
- p3.cancel("Cancelled");
2395
+ const val = await p4.text({ message: "Task title" });
2396
+ if (p4.isCancel(val)) {
2397
+ p4.cancel("Cancelled");
2283
2398
  process.exit(0);
2284
2399
  }
2285
2400
  return val.trim();
2286
2401
  }, taskTitleSchema);
2287
2402
  const taskDesc = await cliFormWithRetry(async () => {
2288
- const val = await p3.text({ message: "Task description", placeholder: "What and why" });
2289
- if (p3.isCancel(val)) {
2290
- p3.cancel("Cancelled");
2403
+ const val = await p4.text({ message: "Task description", placeholder: "What and why" });
2404
+ if (p4.isCancel(val)) {
2405
+ p4.cancel("Cancelled");
2291
2406
  process.exit(0);
2292
2407
  }
2293
2408
  return val.trim();
2294
2409
  }, taskDescriptionSchema);
2295
2410
  const acceptance = [];
2296
- p3.log.info("Acceptance criteria \u2014 one per line, empty line to finish");
2411
+ p4.log.info("Acceptance criteria \u2014 one per line, empty line to finish");
2297
2412
  while (true) {
2298
- const criterionVal = await p3.text({
2413
+ const criterionVal = await p4.text({
2299
2414
  message: ">",
2300
2415
  placeholder: "Criterion (or press Enter to finish)"
2301
2416
  });
2302
- if (p3.isCancel(criterionVal) || !criterionVal || !criterionVal.trim()) break;
2417
+ if (p4.isCancel(criterionVal) || !criterionVal || !criterionVal.trim()) break;
2303
2418
  acceptance.push(criterionVal.trim());
2304
2419
  }
2305
2420
  firstTask = { title: taskTitle, description: taskDesc, acceptance };
2306
2421
  }
2307
2422
  let configExt = "ts";
2308
2423
  let featureListParseFailedPath = null;
2309
- const spinner6 = p3.spinner();
2424
+ const spinner6 = p4.spinner();
2310
2425
  spinner6.start("Scaffolding...");
2311
2426
  try {
2312
2427
  const config = applyConfigDefaults({
@@ -2332,20 +2447,20 @@ async function runInit(cwd2, flags) {
2332
2447
  scope: config.storage.scope,
2333
2448
  projectId: config.storage.projectId
2334
2449
  });
2335
- writeFileSync7(join15(installDir, configFileName), configContent, "utf8");
2336
- mkdirSync6(join15(installDir, config.storage.dir), { recursive: true });
2450
+ writeFileSync8(join16(installDir, configFileName), configContent, "utf8");
2451
+ mkdirSync7(join16(installDir, config.storage.dir), { recursive: true });
2337
2452
  const db = await openDB(config, installDir);
2338
2453
  await db.writeStorageState(installDir);
2339
2454
  await materializer.scaffold(config, { cwd: installDir, firstTask, claudeAgentModels });
2340
2455
  const { parseFailed } = await reconcileFeatureList(db, installDir, config.storage.dir, firstTask);
2341
2456
  if (parseFailed) {
2342
- featureListParseFailedPath = join15(config.storage.dir, "feature_list.json");
2457
+ featureListParseFailedPath = join16(config.storage.dir, "feature_list.json");
2343
2458
  }
2344
2459
  await db.close();
2345
2460
  spinner6.stop("");
2346
2461
  } catch (err) {
2347
2462
  spinner6.stop("Failed");
2348
- p3.log.error(err instanceof Error ? err.message : String(err));
2463
+ p4.log.error(err instanceof Error ? err.message : String(err));
2349
2464
  throw err;
2350
2465
  }
2351
2466
  if (featureListParseFailedPath) {
@@ -2354,8 +2469,13 @@ async function runInit(cwd2, flags) {
2354
2469
  );
2355
2470
  }
2356
2471
  console.log(pc8.green("\u2713 Scaffolded harness in current directory"));
2357
- const agentsDir = provider === "claude-code" ? ".claude/agents/" : ".opencode/agents/";
2358
- const mcpFile = provider === "claude-code" ? ".claude/mcp.json" : "./opencode.json";
2472
+ const PROVIDER_SUMMARY_INFO = {
2473
+ "claude-code": { agentsDir: ".claude/agents/", mcpFile: ".mcp.json" },
2474
+ opencode: { agentsDir: ".opencode/agents/", mcpFile: "./opencode.json" },
2475
+ "codex-cli": { agentsDir: ".codex/agents/", mcpFile: ".codex/config.toml" },
2476
+ "grok-cli": { agentsDir: ".grok/agents/", mcpFile: ".grok/config.toml" }
2477
+ };
2478
+ const { agentsDir, mcpFile } = PROVIDER_SUMMARY_INFO[provider];
2359
2479
  console.log("");
2360
2480
  console.log(pc8.green(`\u2713 agent-harness-kit.config.${configExt}`));
2361
2481
  console.log(pc8.green("\u2713 AGENTS.md"));
@@ -2392,24 +2512,25 @@ async function runInit(cwd2, flags) {
2392
2512
  }
2393
2513
 
2394
2514
  // src/commands/migrate.ts
2395
- import * as p4 from "@clack/prompts";
2515
+ import * as p5 from "@clack/prompts";
2396
2516
  import pc9 from "picocolors";
2397
2517
  async function runMigrate(cwd2, opts) {
2398
2518
  const config = await loadConfig(cwd2);
2399
2519
  let target;
2400
- if (opts.to && ["claude-code", "opencode", "codex-cli"].includes(opts.to)) {
2520
+ if (opts.to && ["claude-code", "opencode", "codex-cli", "grok-cli"].includes(opts.to)) {
2401
2521
  target = opts.to;
2402
2522
  } else {
2403
- const val = await p4.select({
2523
+ const val = await p5.select({
2404
2524
  message: "Migrate to provider",
2405
2525
  options: [
2406
2526
  { value: "claude-code", label: "Claude Code" },
2407
2527
  { value: "opencode", label: "OpenCode" },
2408
- { value: "codex-cli", label: "Codex CLI" }
2528
+ { value: "codex-cli", label: "Codex CLI" },
2529
+ { value: "grok-cli", label: "Grok CLI" }
2409
2530
  ]
2410
2531
  });
2411
- if (p4.isCancel(val)) {
2412
- p4.cancel("Cancelled.");
2532
+ if (p5.isCancel(val)) {
2533
+ p5.cancel("Cancelled.");
2413
2534
  process.exit(0);
2414
2535
  }
2415
2536
  target = val;
@@ -2418,25 +2539,25 @@ async function runMigrate(cwd2, opts) {
2418
2539
  console.log(pc9.dim(`Already on ${target} \u2014 nothing to migrate.`));
2419
2540
  return;
2420
2541
  }
2421
- const spinner6 = p4.spinner();
2542
+ const spinner6 = p5.spinner();
2422
2543
  spinner6.start(`Migrating from ${config.provider} to ${target}...`);
2423
2544
  try {
2424
2545
  const targetMaterializer = getMaterializer(target);
2425
2546
  await targetMaterializer.build(config, cwd2);
2426
2547
  spinner6.stop(pc9.green(`Migrated to ${target}`));
2427
- p4.log.warn(`Update agent-harness-kit.config.ts: set provider: '${target}'`);
2428
- p4.log.warn(`Then run: ahk build`);
2548
+ p5.log.warn(`Update agent-harness-kit.config.ts: set provider: '${target}'`);
2549
+ p5.log.warn(`Then run: ahk build`);
2429
2550
  } catch (err) {
2430
2551
  spinner6.stop(pc9.red("Migration failed"));
2431
- p4.log.error(err instanceof Error ? err.message : String(err));
2552
+ p5.log.error(err instanceof Error ? err.message : String(err));
2432
2553
  process.exit(1);
2433
2554
  }
2434
2555
  }
2435
2556
 
2436
2557
  // src/commands/migrate-storage.ts
2437
- import { copyFileSync, existsSync as existsSync14, mkdirSync as mkdirSync7, rmSync, writeFileSync as writeFileSync8 } from "fs";
2558
+ import { copyFileSync, existsSync as existsSync15, mkdirSync as mkdirSync8, rmSync, writeFileSync as writeFileSync9 } from "fs";
2438
2559
  import { homedir as homedir3 } from "os";
2439
- import { dirname as dirname7, join as join16, resolve as resolve6 } from "path";
2560
+ import { dirname as dirname7, join as join17, resolve as resolve7 } from "path";
2440
2561
  import pc10 from "picocolors";
2441
2562
  function log5(msg) {
2442
2563
  console.log(msg);
@@ -2448,17 +2569,17 @@ function defaultMarkdownPathForConfig(config) {
2448
2569
  return config.storage.scope === "local" ? config.storage.markdownFallback.path : DEFAULT_MARKDOWN_PATH;
2449
2570
  }
2450
2571
  function currentMdPathForScope(scope, config, cwd2, homeDir) {
2451
- return scope === "global" ? join16(resolveGlobalStorageDir(config, homeDir), "current.md") : resolve6(cwd2, defaultMarkdownPathForConfig(config));
2572
+ return scope === "global" ? join17(resolveGlobalStorageDir(config, homeDir), "current.md") : resolve7(cwd2, defaultMarkdownPathForConfig(config));
2452
2573
  }
2453
2574
  function defaultSqlitePathForConfig(config) {
2454
2575
  return config.storage.scope === "local" && config.database.type === "sqlite" ? config.storage.sqlitePath ?? DEFAULT_SQLITE_PATH : DEFAULT_SQLITE_PATH;
2455
2576
  }
2456
2577
  async function backupDestination(cwd2, storageDir, data) {
2457
- const backupsDir = resolve6(cwd2, storageDir, "backups");
2458
- const path = join16(backupsDir, `pre-migrate-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.json`);
2578
+ const backupsDir = resolve7(cwd2, storageDir, "backups");
2579
+ const path = join17(backupsDir, `pre-migrate-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.json`);
2459
2580
  try {
2460
- mkdirSync7(backupsDir, { recursive: true });
2461
- writeFileSync8(path, JSON.stringify(data, null, 2) + "\n", "utf8");
2581
+ mkdirSync8(backupsDir, { recursive: true });
2582
+ writeFileSync9(path, JSON.stringify(data, null, 2) + "\n", "utf8");
2462
2583
  } catch (err) {
2463
2584
  throw new Error(
2464
2585
  `Could not write destination backup to ${path} (${err instanceof Error ? err.message : String(err)}). Aborting migration WITHOUT touching the destination \u2014 nothing was overwritten.`
@@ -2467,14 +2588,14 @@ async function backupDestination(cwd2, storageDir, data) {
2467
2588
  return path;
2468
2589
  }
2469
2590
  function copySqliteFile(srcPath, destPath) {
2470
- mkdirSync7(dirname7(destPath), { recursive: true });
2591
+ mkdirSync8(dirname7(destPath), { recursive: true });
2471
2592
  copyFileSync(srcPath, destPath);
2472
2593
  for (const suffix of ["-wal", "-shm"]) {
2473
- if (existsSync14(`${srcPath}${suffix}`)) {
2594
+ if (existsSync15(`${srcPath}${suffix}`)) {
2474
2595
  copyFileSync(`${srcPath}${suffix}`, `${destPath}${suffix}`);
2475
2596
  }
2476
2597
  }
2477
- if (!existsSync14(destPath)) {
2598
+ if (!existsSync15(destPath)) {
2478
2599
  throw new Error(`Copy verification failed: ${destPath} does not exist after copy.`);
2479
2600
  }
2480
2601
  }
@@ -2533,7 +2654,7 @@ async function runMigrateStorage(cwd2, opts, homeDir = homedir3()) {
2533
2654
  return migrateAcrossDbType(cwd2, config, homeDir, realScope, opts);
2534
2655
  }
2535
2656
  async function probeTaskCount(dbPath) {
2536
- if (!existsSync14(dbPath)) return 0;
2657
+ if (!existsSync15(dbPath)) return 0;
2537
2658
  const { SQLiteDriver } = await import("./sqlite-5OWKTUUZ.js");
2538
2659
  const driver = new SQLiteDriver(dbPath);
2539
2660
  try {
@@ -2550,10 +2671,10 @@ async function migrateScopeOnly(cwd2, config, homeDir, fromScope, toScope, opts)
2550
2671
  const destDb = resolveSqlitePathForScope(toScope, sqlitePath, cwd2, config, homeDir);
2551
2672
  const srcMd = currentMdPathForScope(fromScope, config, cwd2, homeDir);
2552
2673
  const destMd = currentMdPathForScope(toScope, config, cwd2, homeDir);
2553
- if (!existsSync14(srcDb)) {
2674
+ if (!existsSync15(srcDb)) {
2554
2675
  fail(`Source database not found at ${srcDb} (expected ${fromScope} scope) \u2014 nothing to move.`);
2555
2676
  }
2556
- const destExists = existsSync14(destDb);
2677
+ const destExists = existsSync15(destDb);
2557
2678
  if (destExists) {
2558
2679
  const { SQLiteDriver } = await import("./sqlite-5OWKTUUZ.js");
2559
2680
  const destDriver = new SQLiteDriver(destDb);
@@ -2591,15 +2712,15 @@ async function migrateScopeOnly(cwd2, config, homeDir, fromScope, toScope, opts)
2591
2712
  }
2592
2713
  copySqliteFile(srcDb, destDb);
2593
2714
  log5(pc10.green(`\u2713 Copied database ${srcDb} \u2192 ${destDb}`));
2594
- if (existsSync14(srcMd)) {
2595
- mkdirSync7(dirname7(destMd), { recursive: true });
2715
+ if (existsSync15(srcMd)) {
2716
+ mkdirSync8(dirname7(destMd), { recursive: true });
2596
2717
  copyFileSync(srcMd, destMd);
2597
2718
  log5(pc10.green(`\u2713 Copied current.md ${srcMd} \u2192 ${destMd}`));
2598
2719
  }
2599
2720
  rmSync(srcDb, { force: true });
2600
2721
  rmSync(`${srcDb}-wal`, { force: true });
2601
2722
  rmSync(`${srcDb}-shm`, { force: true });
2602
- if (existsSync14(srcMd) && srcMd !== destMd) rmSync(srcMd, { force: true });
2723
+ if (existsSync15(srcMd) && srcMd !== destMd) rmSync(srcMd, { force: true });
2603
2724
  const db = await openDB(config, cwd2, homeDir);
2604
2725
  try {
2605
2726
  await db.writeStorageState(cwd2);
@@ -2611,7 +2732,7 @@ async function migrateScopeOnly(cwd2, config, homeDir, fromScope, toScope, opts)
2611
2732
  async function migrateAcrossDbType(cwd2, config, homeDir, sourceScope, opts) {
2612
2733
  const sqlitePath = defaultSqlitePathForConfig(config);
2613
2734
  const srcPath = resolveSqlitePathForScope(sourceScope, sqlitePath, cwd2, config, homeDir);
2614
- if (!existsSync14(srcPath)) {
2735
+ if (!existsSync15(srcPath)) {
2615
2736
  fail(`Source sqlite database not found at ${srcPath} (expected ${sourceScope} scope) \u2014 nothing to migrate.`);
2616
2737
  }
2617
2738
  const { SQLiteDriver } = await import("./sqlite-5OWKTUUZ.js");
@@ -2673,55 +2794,121 @@ async function migrateAcrossDbType(cwd2, config, homeDir, sourceScope, opts) {
2673
2794
  }
2674
2795
  }
2675
2796
 
2797
+ // src/commands/models.ts
2798
+ import { join as join18 } from "path";
2799
+ import pc11 from "picocolors";
2800
+ async function resolveModelsContext(cwd2) {
2801
+ let config;
2802
+ try {
2803
+ config = await loadConfig(cwd2);
2804
+ } catch {
2805
+ return { ok: false, reason: "no-config" };
2806
+ }
2807
+ if (config.provider !== "claude-code") {
2808
+ return { ok: false, reason: "not-claude-code", provider: config.provider };
2809
+ }
2810
+ return { ok: true, config };
2811
+ }
2812
+ async function runModels(cwd2) {
2813
+ const ctx = await resolveModelsContext(cwd2);
2814
+ if (!ctx.ok) {
2815
+ if (ctx.reason === "no-config") {
2816
+ console.log("");
2817
+ console.log(` ${pc11.cyan("config".padEnd(16))}${pc11.yellow("[!]")} no agent-harness-kit.config found`);
2818
+ console.log(` ${"".padEnd(16)} ${pc11.dim("run: ahk init")}`);
2819
+ console.log("");
2820
+ return;
2821
+ }
2822
+ console.log(
2823
+ pc11.dim(
2824
+ `ahk models only applies to Claude Code projects (this project uses '${ctx.provider}') \u2014 nothing to do.`
2825
+ )
2826
+ );
2827
+ return;
2828
+ }
2829
+ const { config } = ctx;
2830
+ const claudeAgentModels = await promptClaudeAgentModels(config.provider);
2831
+ const agents = writeAgentFiles(cwd2, claudeAgentFiles(config, claudeAgentModels), {
2832
+ force: true,
2833
+ backupRoot: join18(cwd2, config.storage.dir, "backups")
2834
+ });
2835
+ console.log("");
2836
+ if (agents.overwritten.length > 0) {
2837
+ console.log(pc11.green(`\u2713 Regenerated ${agents.overwritten.length} agent file(s) with updated models:`));
2838
+ for (const file of agents.overwritten) console.log(pc11.green(` \u2713 ${file}`));
2839
+ if (agents.backupDir) {
2840
+ console.log(pc11.dim(` Previous content backed up \u2192 ${agents.backupDir}`));
2841
+ }
2842
+ }
2843
+ if (agents.created.length > 0) {
2844
+ console.log(pc11.green(`\u2713 Created ${agents.created.length} missing agent file(s):`));
2845
+ for (const file of agents.created) console.log(pc11.green(` \u2713 ${file}`));
2846
+ }
2847
+ console.log("");
2848
+ }
2849
+
2676
2850
  // src/commands/reset.ts
2677
- import { existsSync as existsSync15, readdirSync, rmSync as rmSync2 } from "fs";
2851
+ import { existsSync as existsSync16, readdirSync, rmSync as rmSync2 } from "fs";
2678
2852
  import { homedir as homedir4 } from "os";
2679
- import { join as join17, resolve as resolve7 } from "path";
2680
- import * as p5 from "@clack/prompts";
2681
- import pc11 from "picocolors";
2853
+ import { join as join19, resolve as resolve8 } from "path";
2854
+ import * as p6 from "@clack/prompts";
2855
+ import pc12 from "picocolors";
2682
2856
  var AGENT_MD_FILES = ["lead", "explorer", "consultant", "builder", "reviewer"];
2857
+ var PROVIDER_AGENT_DIRS = {
2858
+ "claude-code": ".claude/agents",
2859
+ opencode: ".opencode/agents",
2860
+ "codex-cli": ".codex/agents",
2861
+ "grok-cli": ".grok/agents"
2862
+ };
2863
+ var PROVIDER_AGENT_EXT = {
2864
+ "claude-code": ".md",
2865
+ opencode: ".md",
2866
+ "codex-cli": ".toml",
2867
+ "grok-cli": ".md"
2868
+ };
2683
2869
  async function resetAgentMds(cwd2, provider) {
2684
- const agentDir = provider === "claude-code" ? ".claude/agents" : ".opencode/agents";
2685
- const agentDirPath = resolve7(cwd2, agentDir);
2686
- if (!existsSync15(agentDirPath)) {
2687
- console.log(pc11.yellow(` Skipping agent files \u2014 directory not found: ${agentDirPath}`));
2870
+ const agentDir = PROVIDER_AGENT_DIRS[provider];
2871
+ const agentDirPath = resolve8(cwd2, agentDir);
2872
+ const agentExt = PROVIDER_AGENT_EXT[provider];
2873
+ if (!existsSync16(agentDirPath)) {
2874
+ console.log(pc12.yellow(` Skipping agent files \u2014 directory not found: ${agentDirPath}`));
2688
2875
  return;
2689
2876
  }
2690
2877
  const existingFiles = [];
2691
2878
  try {
2692
2879
  const files = readdirSync(agentDirPath);
2693
2880
  for (const f of files) {
2694
- if (f.endsWith(".md") && AGENT_MD_FILES.includes(f.replace(".md", ""))) {
2881
+ if (f.endsWith(agentExt) && AGENT_MD_FILES.includes(f.replace(agentExt, ""))) {
2695
2882
  existingFiles.push(f);
2696
2883
  }
2697
2884
  }
2698
2885
  } catch {
2699
- console.log(pc11.yellow(` Skipping agent files \u2014 ${agentDirPath} is not readable`));
2886
+ console.log(pc12.yellow(` Skipping agent files \u2014 ${agentDirPath} is not readable`));
2700
2887
  return;
2701
2888
  }
2702
2889
  if (existingFiles.length === 0) {
2703
- console.log(pc11.yellow(` No agent MD files found in ${agentDir}/`));
2890
+ console.log(pc12.yellow(` No agent MD files found in ${agentDir}/`));
2704
2891
  return;
2705
2892
  }
2706
2893
  for (const file of existingFiles) {
2707
- const confirm3 = await p5.confirm({
2894
+ const confirm3 = await p6.confirm({
2708
2895
  message: `Remove ${file}?`,
2709
2896
  initialValue: true
2710
2897
  });
2711
- if (p5.isCancel(confirm3)) {
2712
- console.log(pc11.red(" Cancelled by user."));
2898
+ if (p6.isCancel(confirm3)) {
2899
+ console.log(pc12.red(" Cancelled by user."));
2713
2900
  return;
2714
2901
  }
2715
2902
  if (confirm3) {
2716
2903
  try {
2717
- const filePath = join17(agentDirPath, file);
2904
+ const filePath = join19(agentDirPath, file);
2718
2905
  rmSync2(filePath, { force: true });
2719
- console.log(pc11.green(` Removed ${file}`));
2906
+ console.log(pc12.green(` Removed ${file}`));
2720
2907
  } catch {
2721
- console.error(pc11.red(` Failed to remove ${file}`));
2908
+ console.error(pc12.red(` Failed to remove ${file}`));
2722
2909
  }
2723
2910
  } else {
2724
- console.log(pc11.cyan(` Skipped ${file}`));
2911
+ console.log(pc12.cyan(` Skipped ${file}`));
2725
2912
  }
2726
2913
  }
2727
2914
  }
@@ -2730,47 +2917,47 @@ async function runReset(cwd2, opts) {
2730
2917
  try {
2731
2918
  config = await loadConfig(cwd2);
2732
2919
  } catch {
2733
- console.error(pc11.red("\u2717 No agent-harness-kit.config found. Run: ahk init"));
2920
+ console.error(pc12.red("\u2717 No agent-harness-kit.config found. Run: ahk init"));
2734
2921
  process.exit(1);
2735
2922
  }
2736
2923
  const storageDir = config.storage.dir || ".harness";
2737
2924
  const dbPath = config.database.type === "sqlite" ? resolveSqlitePath(config, cwd2, homedir4()) : null;
2738
- const featureListPath = resolve7(cwd2, storageDir, "feature_list.json");
2925
+ const featureListPath = resolve8(cwd2, storageDir, "feature_list.json");
2739
2926
  let resetDb = false;
2740
2927
  let resetFeatureList = false;
2741
2928
  let resetAgentMdsFlag = false;
2742
- if (dbPath && existsSync15(dbPath)) {
2929
+ if (dbPath && existsSync16(dbPath)) {
2743
2930
  if (opts.force) {
2744
2931
  resetDb = true;
2745
2932
  } else {
2746
2933
  if (config.database.type !== "sqlite") {
2747
- console.log(pc11.yellow(` Skipping DB reset \u2014 database type "${config.database.type}" is not managed by this command.`));
2934
+ console.log(pc12.yellow(` Skipping DB reset \u2014 database type "${config.database.type}" is not managed by this command.`));
2748
2935
  resetDb = false;
2749
2936
  } else {
2750
- const confirm3 = await p5.confirm({
2937
+ const confirm3 = await p6.confirm({
2751
2938
  message: `Delete database (${dbPath})?`,
2752
2939
  initialValue: true
2753
2940
  });
2754
- if (p5.isCancel(confirm3)) {
2755
- console.log(pc11.red(" Cancelled by user."));
2941
+ if (p6.isCancel(confirm3)) {
2942
+ console.log(pc12.red(" Cancelled by user."));
2756
2943
  return;
2757
2944
  }
2758
2945
  resetDb = confirm3;
2759
2946
  }
2760
2947
  }
2761
2948
  } else if (!dbPath) {
2762
- console.log(pc11.dim(` Skipping DB reset \u2014 remote ${config.database.type} database is not managed by this command.`));
2949
+ console.log(pc12.dim(` Skipping DB reset \u2014 remote ${config.database.type} database is not managed by this command.`));
2763
2950
  }
2764
- if (existsSync15(featureListPath)) {
2951
+ if (existsSync16(featureListPath)) {
2765
2952
  if (opts.force) {
2766
2953
  resetFeatureList = true;
2767
2954
  } else {
2768
- const confirm3 = await p5.confirm({
2955
+ const confirm3 = await p6.confirm({
2769
2956
  message: `Delete feature list (${storageDir}/feature_list.json)?`,
2770
2957
  initialValue: true
2771
2958
  });
2772
- if (p5.isCancel(confirm3)) {
2773
- console.log(pc11.red(" Cancelled by user."));
2959
+ if (p6.isCancel(confirm3)) {
2960
+ console.log(pc12.red(" Cancelled by user."));
2774
2961
  return;
2775
2962
  }
2776
2963
  resetFeatureList = confirm3;
@@ -2784,17 +2971,17 @@ async function runReset(cwd2, opts) {
2784
2971
  rmSync2(dbPath, { force: true });
2785
2972
  rmSync2(`${dbPath}-wal`, { force: true });
2786
2973
  rmSync2(`${dbPath}-shm`, { force: true });
2787
- console.log(pc11.green(` \u2713 Removed ${dbPath}`));
2974
+ console.log(pc12.green(` \u2713 Removed ${dbPath}`));
2788
2975
  } catch {
2789
- console.error(pc11.red(` \u2717 Failed to remove ${dbPath}`));
2976
+ console.error(pc12.red(` \u2717 Failed to remove ${dbPath}`));
2790
2977
  }
2791
2978
  }
2792
2979
  if (resetFeatureList) {
2793
2980
  try {
2794
2981
  rmSync2(featureListPath, { force: true });
2795
- console.log(pc11.green(` \u2713 Removed ${storageDir}/feature_list.json`));
2982
+ console.log(pc12.green(` \u2713 Removed ${storageDir}/feature_list.json`));
2796
2983
  } catch {
2797
- console.error(pc11.red(` \u2717 Failed to remove ${featureListPath}`));
2984
+ console.error(pc12.red(` \u2717 Failed to remove ${featureListPath}`));
2798
2985
  }
2799
2986
  }
2800
2987
  if (resetAgentMdsFlag) {
@@ -2802,16 +2989,16 @@ async function runReset(cwd2, opts) {
2802
2989
  await resetAgentMds(cwd2, opts.provider || "claude-code");
2803
2990
  }
2804
2991
  if (!resetDb && !resetFeatureList && !resetAgentMdsFlag) {
2805
- console.log(pc11.yellow(" Nothing to reset (all items missing or skipped)."));
2992
+ console.log(pc12.yellow(" Nothing to reset (all items missing or skipped)."));
2806
2993
  return;
2807
2994
  }
2808
2995
  console.log("");
2809
- console.log(pc11.green('\u2713 Reset complete. Run "ahk init" to scaffold a fresh harness.'));
2996
+ console.log(pc12.green('\u2713 Reset complete. Run "ahk init" to scaffold a fresh harness.'));
2810
2997
  }
2811
2998
 
2812
2999
  // src/core/mcp-server.ts
2813
- import { existsSync as existsSync17, mkdirSync as mkdirSync8, readdirSync as readdirSync2, readFileSync as readFileSync10, statSync, writeFileSync as writeFileSync9 } from "fs";
2814
- import { join as join19, resolve as resolve8 } from "path";
3000
+ import { existsSync as existsSync18, mkdirSync as mkdirSync9, readdirSync as readdirSync2, readFileSync as readFileSync10, statSync, writeFileSync as writeFileSync10 } from "fs";
3001
+ import { join as join21, resolve as resolve9 } from "path";
2815
3002
  import { Server } from "@modelcontextprotocol/sdk/server";
2816
3003
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
2817
3004
  import {
@@ -2820,8 +3007,8 @@ import {
2820
3007
  } from "@modelcontextprotocol/sdk/types.js";
2821
3008
 
2822
3009
  // src/core/permissions-check.ts
2823
- import { existsSync as existsSync16 } from "fs";
2824
- import { join as join18 } from "path";
3010
+ import { existsSync as existsSync17 } from "fs";
3011
+ import { join as join20 } from "path";
2825
3012
  var AGENTS = ["lead", "explorer", "consultant", "builder", "reviewer"];
2826
3013
  function checkPermissionsSync(cwd2, config) {
2827
3014
  if (config.provider !== "claude-code") {
@@ -2830,8 +3017,8 @@ function checkPermissionsSync(cwd2, config) {
2830
3017
  const agents = {};
2831
3018
  let in_sync = true;
2832
3019
  for (const agent of AGENTS) {
2833
- const filePath = join18(cwd2, ".claude", "agents", `${agent}.md`);
2834
- const exists = existsSync16(filePath);
3020
+ const filePath = join20(cwd2, ".claude", "agents", `${agent}.md`);
3021
+ const exists = existsSync17(filePath);
2835
3022
  if (!exists) in_sync = false;
2836
3023
  agents[agent] = exists ? { ok: true } : { ok: false, reason: "missing_file" };
2837
3024
  }
@@ -3129,7 +3316,7 @@ var TOOLS = [
3129
3316
  ];
3130
3317
  async function startMcpServer(config, cwd2) {
3131
3318
  const db = await openDB(config, cwd2);
3132
- const docsPath = resolve8(cwd2, config.project.docsPath);
3319
+ const docsPath = resolve9(cwd2, config.project.docsPath);
3133
3320
  const server = new Server(
3134
3321
  { name: "agent-harness-kit", version: VERSION },
3135
3322
  { capabilities: { tools: {} } }
@@ -3292,8 +3479,8 @@ async function dispatch(name, args, db, docsPath, cwd2, config) {
3292
3479
  return ok2(JSON.stringify(result));
3293
3480
  }
3294
3481
  case "deps.snapshot": {
3295
- const pkgPath2 = join19(cwd2, "package.json");
3296
- if (!existsSync17(pkgPath2)) {
3482
+ const pkgPath2 = join21(cwd2, "package.json");
3483
+ if (!existsSync18(pkgPath2)) {
3297
3484
  return ok2("package.json not found in project root", true);
3298
3485
  }
3299
3486
  const pkg2 = JSON.parse(readFileSync10(pkgPath2, "utf8"));
@@ -3302,9 +3489,9 @@ async function dispatch(name, args, db, docsPath, cwd2, config) {
3302
3489
  dependencies: pkg2.dependencies ?? {},
3303
3490
  devDependencies: pkg2.devDependencies ?? {}
3304
3491
  };
3305
- const harnessDir = join19(cwd2, ".harness");
3306
- mkdirSync8(harnessDir, { recursive: true });
3307
- writeFileSync9(join19(harnessDir, "deps-lock.json"), JSON.stringify(snapshot, null, 2), "utf8");
3492
+ const harnessDir = join21(cwd2, ".harness");
3493
+ mkdirSync9(harnessDir, { recursive: true });
3494
+ writeFileSync10(join21(harnessDir, "deps-lock.json"), JSON.stringify(snapshot, null, 2), "utf8");
3308
3495
  return ok2(
3309
3496
  JSON.stringify({
3310
3497
  message: "Snapshot saved to .harness/deps-lock.json",
@@ -3313,12 +3500,12 @@ async function dispatch(name, args, db, docsPath, cwd2, config) {
3313
3500
  );
3314
3501
  }
3315
3502
  case "deps.check": {
3316
- const pkgPath2 = join19(cwd2, "package.json");
3317
- const lockPath = join19(cwd2, ".harness", "deps-lock.json");
3318
- if (!existsSync17(pkgPath2)) {
3503
+ const pkgPath2 = join21(cwd2, "package.json");
3504
+ const lockPath = join21(cwd2, ".harness", "deps-lock.json");
3505
+ if (!existsSync18(pkgPath2)) {
3319
3506
  return ok2("package.json not found in project root", true);
3320
3507
  }
3321
- if (!existsSync17(lockPath)) {
3508
+ if (!existsSync18(lockPath)) {
3322
3509
  return ok2(
3323
3510
  JSON.stringify({
3324
3511
  status: "no-snapshot",
@@ -3420,7 +3607,7 @@ function collectMarkdownFiles(dir) {
3420
3607
  const files = [];
3421
3608
  try {
3422
3609
  for (const entry of readdirSync2(dir)) {
3423
- const full = join19(dir, entry);
3610
+ const full = join21(dir, entry);
3424
3611
  const stat = statSync(full);
3425
3612
  if (stat.isDirectory()) {
3426
3613
  files.push(...collectMarkdownFiles(full));
@@ -3477,12 +3664,12 @@ async function runServe(cwd2, opts) {
3477
3664
 
3478
3665
  // src/commands/status.ts
3479
3666
  import Table from "cli-table3";
3480
- import pc12 from "picocolors";
3667
+ import pc13 from "picocolors";
3481
3668
  var STATUS_COLOR = {
3482
- pending: (s) => pc12.dim(s),
3483
- in_progress: (s) => pc12.cyan(s),
3484
- done: (s) => pc12.green(s),
3485
- blocked: (s) => pc12.red(s)
3669
+ pending: (s) => pc13.dim(s),
3670
+ in_progress: (s) => pc13.cyan(s),
3671
+ done: (s) => pc13.green(s),
3672
+ blocked: (s) => pc13.red(s)
3486
3673
  };
3487
3674
  async function runStatus(cwd2, opts) {
3488
3675
  const config = await loadConfig(cwd2);
@@ -3503,11 +3690,11 @@ async function runStatus(cwd2, opts) {
3503
3690
  return;
3504
3691
  }
3505
3692
  if (tasks.length === 0) {
3506
- console.log(pc12.dim("No tasks yet. Run: ahk task add"));
3693
+ console.log(pc13.dim("No tasks yet. Run: ahk task add"));
3507
3694
  return;
3508
3695
  }
3509
3696
  const table = new Table({
3510
- head: ["ID", "Slug", "Title", "Status", "Assigned", "Started"].map((h) => pc12.bold(h)),
3697
+ head: ["ID", "Slug", "Title", "Status", "Assigned", "Started"].map((h) => pc13.bold(h)),
3511
3698
  style: { head: [], border: [] }
3512
3699
  });
3513
3700
  for (const t of tasks) {
@@ -3525,12 +3712,12 @@ async function runStatus(cwd2, opts) {
3525
3712
  const inProgress = tasks.filter((t) => t.status === "in_progress");
3526
3713
  if (inProgress.length > 0) {
3527
3714
  console.log("");
3528
- console.log(pc12.bold("Active actions:"));
3715
+ console.log(pc13.bold("Active actions:"));
3529
3716
  for (const t of inProgress) {
3530
3717
  const actions = await db.getActionsForTask(t.id);
3531
3718
  const active = actions.filter((a) => a.status === "in_progress");
3532
3719
  for (const a of active) {
3533
- console.log(` ${pc12.cyan(a.agent.padEnd(10))} \u2192 task #${t.id} ${t.slug}`);
3720
+ console.log(` ${pc13.cyan(a.agent.padEnd(10))} \u2192 task #${t.id} ${t.slug}`);
3534
3721
  }
3535
3722
  }
3536
3723
  }
@@ -3539,10 +3726,10 @@ async function runStatus(cwd2, opts) {
3539
3726
  const fn = STATUS_COLOR[s.status] ?? ((x) => x);
3540
3727
  return `${fn(s.status)}: ${s.total}`;
3541
3728
  });
3542
- console.log(pc12.dim("Tasks \u2014 ") + parts.join(pc12.dim(" | ")));
3729
+ console.log(pc13.dim("Tasks \u2014 ") + parts.join(pc13.dim(" | ")));
3543
3730
  const archivedTasks = await db.getArchivedTasks();
3544
3731
  if (archivedTasks.length > 0) {
3545
- console.log(pc12.dim(`${archivedTasks.length} archived (use \`ahk task list --archived\` to view)`));
3732
+ console.log(pc13.dim(`${archivedTasks.length} archived (use \`ahk task list --archived\` to view)`));
3546
3733
  }
3547
3734
  } finally {
3548
3735
  await db.close();
@@ -3550,13 +3737,13 @@ async function runStatus(cwd2, opts) {
3550
3737
  }
3551
3738
 
3552
3739
  // src/commands/sync.ts
3553
- import { existsSync as existsSync18, readFileSync as readFileSync11 } from "fs";
3554
- import { join as join20, resolve as resolve9 } from "path";
3555
- import pc13 from "picocolors";
3740
+ import { existsSync as existsSync19, readFileSync as readFileSync11 } from "fs";
3741
+ import { join as join22, resolve as resolve10 } from "path";
3742
+ import pc14 from "picocolors";
3556
3743
  async function runSync(cwd2, opts) {
3557
3744
  const config = await loadConfig(cwd2);
3558
3745
  const direction = opts.direction ?? "both";
3559
- const featureListPath = resolve9(join20(cwd2, config.storage.dir, "feature_list.json"));
3746
+ const featureListPath = resolve10(join22(cwd2, config.storage.dir, "feature_list.json"));
3560
3747
  const db = await openDB(config, cwd2);
3561
3748
  try {
3562
3749
  if (direction === "in" || direction === "both") {
@@ -3570,49 +3757,49 @@ async function runSync(cwd2, opts) {
3570
3757
  }
3571
3758
  }
3572
3759
  async function syncIn(featureListPath, db, dryRun) {
3573
- if (!existsSync18(featureListPath)) {
3574
- console.log(pc13.dim(`feature_list.json not found at ${featureListPath} \u2014 skipping in-sync`));
3760
+ if (!existsSync19(featureListPath)) {
3761
+ console.log(pc14.dim(`feature_list.json not found at ${featureListPath} \u2014 skipping in-sync`));
3575
3762
  return;
3576
3763
  }
3577
3764
  let seeds;
3578
3765
  try {
3579
3766
  seeds = JSON.parse(readFileSync11(featureListPath, "utf8"));
3580
3767
  } catch (err) {
3581
- console.error(pc13.red(`Failed to parse feature_list.json: ${err}`));
3768
+ console.error(pc14.red(`Failed to parse feature_list.json: ${err}`));
3582
3769
  process.exit(1);
3583
3770
  }
3584
3771
  if (dryRun) {
3585
- console.log(pc13.bold("Dry run \u2014 in-sync (feature_list.json \u2192 SQLite):"));
3772
+ console.log(pc14.bold("Dry run \u2014 in-sync (feature_list.json \u2192 SQLite):"));
3586
3773
  for (const t of seeds) {
3587
3774
  const existing = await db.getTaskBySlug(t.slug);
3588
- console.log(` ${existing ? pc13.dim("skip") : pc13.green("add ")} ${t.slug}`);
3775
+ console.log(` ${existing ? pc14.dim("skip") : pc14.green("add ")} ${t.slug}`);
3589
3776
  }
3590
3777
  return;
3591
3778
  }
3592
3779
  const result = await db.syncFromFeatureList(seeds);
3593
- console.log(pc13.green(`\u2713 In-sync: ${result.added} added, ${result.skipped} already existed`));
3780
+ console.log(pc14.green(`\u2713 In-sync: ${result.added} added, ${result.skipped} already existed`));
3594
3781
  }
3595
3782
  async function syncOut(db, cwd2, dryRun) {
3596
3783
  if (dryRun) {
3597
3784
  const tasks = await db.getTasks();
3598
- console.log(pc13.bold("Dry run \u2014 out-sync (SQLite \u2192 feature_list.json):"));
3785
+ console.log(pc14.bold("Dry run \u2014 out-sync (SQLite \u2192 feature_list.json):"));
3599
3786
  console.log(` ${tasks.length} tasks would be written`);
3600
3787
  return;
3601
3788
  }
3602
3789
  await db.writeFeatureList(cwd2);
3603
- console.log(pc13.green("\u2713 Out-sync: feature_list.json updated"));
3790
+ console.log(pc14.green("\u2713 Out-sync: feature_list.json updated"));
3604
3791
  }
3605
3792
 
3606
3793
  // src/commands/task/add.ts
3607
- import * as p6 from "@clack/prompts";
3608
- import pc14 from "picocolors";
3794
+ import * as p7 from "@clack/prompts";
3795
+ import pc15 from "picocolors";
3609
3796
  async function runTaskAdd(cwd2) {
3610
- p6.intro(pc14.bold("agent-harness-kit \u2014 add task"));
3797
+ p7.intro(pc15.bold("agent-harness-kit \u2014 add task"));
3611
3798
  const title = await cliFormWithRetry(
3612
3799
  async () => {
3613
- const val = await p6.text({ message: "Task title" });
3614
- if (p6.isCancel(val)) {
3615
- p6.cancel("Cancelled.");
3800
+ const val = await p7.text({ message: "Task title" });
3801
+ if (p7.isCancel(val)) {
3802
+ p7.cancel("Cancelled.");
3616
3803
  process.exit(0);
3617
3804
  }
3618
3805
  return val.trim();
@@ -3621,12 +3808,12 @@ async function runTaskAdd(cwd2) {
3621
3808
  );
3622
3809
  const description = await cliFormWithRetry(
3623
3810
  async () => {
3624
- const val = await p6.text({
3811
+ const val = await p7.text({
3625
3812
  message: "Description (what and why)",
3626
3813
  placeholder: "Describe the task in more detail, including any relevant context or instructions for the agents."
3627
3814
  });
3628
- if (p6.isCancel(val)) {
3629
- p6.cancel("Cancelled.");
3815
+ if (p7.isCancel(val)) {
3816
+ p7.cancel("Cancelled.");
3630
3817
  process.exit(0);
3631
3818
  }
3632
3819
  return val.trim();
@@ -3634,13 +3821,13 @@ async function runTaskAdd(cwd2) {
3634
3821
  taskDescriptionSchema
3635
3822
  );
3636
3823
  const acceptance = [];
3637
- p6.log.info("Acceptance criteria \u2014 one per line, empty line to finish");
3824
+ p7.log.info("Acceptance criteria \u2014 one per line, empty line to finish");
3638
3825
  while (true) {
3639
- const val = await p6.text({ message: ">", placeholder: "Criterion (or press Enter to finish)" });
3640
- if (p6.isCancel(val) || !val || !val.trim()) break;
3826
+ const val = await p7.text({ message: ">", placeholder: "Criterion (or press Enter to finish)" });
3827
+ if (p7.isCancel(val) || !val || !val.trim()) break;
3641
3828
  acceptance.push(val.trim());
3642
3829
  }
3643
- const spinner6 = p6.spinner();
3830
+ const spinner6 = p7.spinner();
3644
3831
  spinner6.start("Saving...");
3645
3832
  try {
3646
3833
  const config = await loadConfig(cwd2);
@@ -3650,28 +3837,28 @@ async function runTaskAdd(cwd2) {
3650
3837
  await db.writeFeatureList(cwd2);
3651
3838
  await db.close();
3652
3839
  spinner6.stop("");
3653
- console.log(pc14.green(`\u2713 Task #${task2.id} added \u2014 ${task2.slug} (pending)`));
3654
- console.log(pc14.cyan("\u2192") + " " + pc14.cyan("ahk status") + " to see all tasks");
3840
+ console.log(pc15.green(`\u2713 Task #${task2.id} added \u2014 ${task2.slug} (pending)`));
3841
+ console.log(pc15.cyan("\u2192") + " " + pc15.cyan("ahk status") + " to see all tasks");
3655
3842
  } catch (err) {
3656
- spinner6.stop(pc14.red("Failed"));
3657
- p6.log.error(err instanceof Error ? err.message : String(err));
3843
+ spinner6.stop(pc15.red("Failed"));
3844
+ p7.log.error(err instanceof Error ? err.message : String(err));
3658
3845
  process.exit(1);
3659
3846
  }
3660
3847
  }
3661
3848
 
3662
3849
  // src/commands/task/done.ts
3663
3850
  import { spawnSync as spawnSync2 } from "child_process";
3664
- import { existsSync as existsSync19 } from "fs";
3665
- import { resolve as resolve10 } from "path";
3666
- import pc15 from "picocolors";
3851
+ import { existsSync as existsSync20 } from "fs";
3852
+ import { resolve as resolve11 } from "path";
3853
+ import pc16 from "picocolors";
3667
3854
  async function runTaskDone(cwd2, idOrSlug) {
3668
3855
  const config = await loadConfig(cwd2);
3669
3856
  if (config.health.required) {
3670
- const scriptPath = resolve10(cwd2, config.health.scriptPath);
3671
- if (existsSync19(scriptPath)) {
3857
+ const scriptPath = resolve11(cwd2, config.health.scriptPath);
3858
+ if (existsSync20(scriptPath)) {
3672
3859
  const result = spawnSync2("bash", [scriptPath], { cwd: cwd2, stdio: "pipe", encoding: "utf8" });
3673
3860
  if (result.status !== 0) {
3674
- console.error(pc15.red("\u2717 Health check failed \u2014 cannot mark task as done."));
3861
+ console.error(pc16.red("\u2717 Health check failed \u2014 cannot mark task as done."));
3675
3862
  if (result.stdout) console.error(result.stdout);
3676
3863
  if (result.stderr) console.error(result.stderr);
3677
3864
  process.exit(1);
@@ -3684,79 +3871,79 @@ async function runTaskDone(cwd2, idOrSlug) {
3684
3871
  const isId = !isNaN(parsed);
3685
3872
  const task2 = isId ? await db.getTaskById(parsed) : await db.getTaskBySlug(idOrSlug);
3686
3873
  if (!task2) {
3687
- console.error(pc15.red(`Task not found: ${idOrSlug}`));
3874
+ console.error(pc16.red(`Task not found: ${idOrSlug}`));
3688
3875
  process.exit(1);
3689
3876
  }
3690
3877
  if (task2.status === "done") {
3691
- console.log(pc15.dim(`Task #${task2.id} is already done.`));
3878
+ console.log(pc16.dim(`Task #${task2.id} is already done.`));
3692
3879
  return;
3693
3880
  }
3694
3881
  await db.updateTaskStatus(task2.id, "done");
3695
3882
  await db.writeFeatureList(cwd2);
3696
- console.log(pc15.green(`\u2713 Task #${task2.id} \u2014 ${task2.slug} marked as done`));
3883
+ console.log(pc16.green(`\u2713 Task #${task2.id} \u2014 ${task2.slug} marked as done`));
3697
3884
  } finally {
3698
3885
  await db.close();
3699
3886
  }
3700
3887
  }
3701
3888
 
3702
3889
  // src/commands/task/edit.ts
3703
- import * as p7 from "@clack/prompts";
3704
- import pc16 from "picocolors";
3890
+ import * as p8 from "@clack/prompts";
3891
+ import pc17 from "picocolors";
3705
3892
  async function runTaskEdit(cwd2) {
3706
- p7.intro(pc16.bold("agent-harness-kit \u2014 edit task"));
3893
+ p8.intro(pc17.bold("agent-harness-kit \u2014 edit task"));
3707
3894
  const config = await loadConfig(cwd2);
3708
3895
  const db = await openDB(config, cwd2);
3709
3896
  try {
3710
3897
  const allTasks = await db.getTasks();
3711
3898
  const activeTasks = allTasks.filter((t) => t.status !== "done");
3712
3899
  if (activeTasks.length === 0) {
3713
- p7.log.error("No active tasks to edit.");
3900
+ p8.log.error("No active tasks to edit.");
3714
3901
  return;
3715
3902
  }
3716
- const taskId = await p7.select({
3903
+ const taskId = await p8.select({
3717
3904
  message: "Select a task to edit",
3718
3905
  options: activeTasks.map((t) => ({
3719
3906
  label: `#${t.id} \u2014 ${t.title} (${t.slug})`,
3720
3907
  value: t.id
3721
3908
  }))
3722
3909
  });
3723
- if (p7.isCancel(taskId)) {
3724
- p7.cancel("Cancelled.");
3910
+ if (p8.isCancel(taskId)) {
3911
+ p8.cancel("Cancelled.");
3725
3912
  process.exit(0);
3726
3913
  }
3727
3914
  const task2 = await db.getTaskById(taskId);
3728
3915
  if (!task2) {
3729
- p7.log.error("Task not found");
3916
+ p8.log.error("Task not found");
3730
3917
  process.exit(1);
3731
3918
  }
3732
- const title = await p7.text({
3919
+ const title = await p8.text({
3733
3920
  message: "Title",
3734
3921
  initialValue: task2.title
3735
3922
  });
3736
- if (p7.isCancel(title)) {
3737
- p7.cancel("Cancelled.");
3923
+ if (p8.isCancel(title)) {
3924
+ p8.cancel("Cancelled.");
3738
3925
  process.exit(0);
3739
3926
  }
3740
- const description = await p7.text({
3927
+ const description = await p8.text({
3741
3928
  message: "Description (what and why)",
3742
3929
  initialValue: task2.description ?? ""
3743
3930
  });
3744
- if (p7.isCancel(description)) {
3745
- p7.cancel("Cancelled.");
3931
+ if (p8.isCancel(description)) {
3932
+ p8.cancel("Cancelled.");
3746
3933
  process.exit(0);
3747
3934
  }
3748
3935
  const currentAcceptance = await db.getTaskAcceptance(task2.id);
3749
3936
  const newAcceptance = [];
3750
- p7.log.info("Acceptance criteria \u2014 edit each, empty to delete. Add new ones at the end.");
3937
+ p8.log.info("Acceptance criteria \u2014 edit each, empty to delete. Add new ones at the end.");
3751
3938
  for (let i = 0; i < currentAcceptance.length; i++) {
3752
3939
  const ac = currentAcceptance[i];
3753
- const val = await p7.text({
3940
+ const val = await p8.text({
3754
3941
  message: `#${i + 1}/${currentAcceptance.length}`,
3755
3942
  initialValue: ac.criterion,
3756
3943
  defaultValue: ""
3757
3944
  });
3758
- if (p7.isCancel(val)) {
3759
- p7.cancel("Cancelled.");
3945
+ if (p8.isCancel(val)) {
3946
+ p8.cancel("Cancelled.");
3760
3947
  process.exit(0);
3761
3948
  }
3762
3949
  const trimmed = val.trim();
@@ -3765,11 +3952,11 @@ async function runTaskEdit(cwd2) {
3765
3952
  }
3766
3953
  }
3767
3954
  while (true) {
3768
- const val = await p7.text({ message: "New acceptance criterion", placeholder: "(press Enter to finish)" });
3769
- if (p7.isCancel(val) || !val || !val.trim()) break;
3955
+ const val = await p8.text({ message: "New acceptance criterion", placeholder: "(press Enter to finish)" });
3956
+ if (p8.isCancel(val) || !val || !val.trim()) break;
3770
3957
  newAcceptance.push(val.trim());
3771
3958
  }
3772
- const spinner6 = p7.spinner();
3959
+ const spinner6 = p8.spinner();
3773
3960
  spinner6.start("Saving...");
3774
3961
  try {
3775
3962
  const newSlug = slugify(title);
@@ -3781,10 +3968,10 @@ async function runTaskEdit(cwd2) {
3781
3968
  await db.updateTaskAcceptance(task2.id, newAcceptance);
3782
3969
  await db.writeFeatureList(cwd2);
3783
3970
  spinner6.stop("");
3784
- console.log(pc16.green(`\u2713 Task #${task2.id} updated \u2014 ${newSlug}`));
3971
+ console.log(pc17.green(`\u2713 Task #${task2.id} updated \u2014 ${newSlug}`));
3785
3972
  } catch (err) {
3786
- spinner6.stop(pc16.red("Failed"));
3787
- p7.log.error(err instanceof Error ? err.message : String(err));
3973
+ spinner6.stop(pc17.red("Failed"));
3974
+ p8.log.error(err instanceof Error ? err.message : String(err));
3788
3975
  process.exit(1);
3789
3976
  }
3790
3977
  } finally {
@@ -3794,12 +3981,12 @@ async function runTaskEdit(cwd2) {
3794
3981
 
3795
3982
  // src/commands/task/list.ts
3796
3983
  import Table2 from "cli-table3";
3797
- import pc17 from "picocolors";
3984
+ import pc18 from "picocolors";
3798
3985
  var STATUS_COLOR2 = {
3799
- pending: (s) => pc17.dim(s),
3800
- in_progress: (s) => pc17.cyan(s),
3801
- done: (s) => pc17.green(s),
3802
- blocked: (s) => pc17.red(s)
3986
+ pending: (s) => pc18.dim(s),
3987
+ in_progress: (s) => pc18.cyan(s),
3988
+ done: (s) => pc18.green(s),
3989
+ blocked: (s) => pc18.red(s)
3803
3990
  };
3804
3991
  async function runTaskList(cwd2, opts) {
3805
3992
  const config = await loadConfig(cwd2);
@@ -3816,11 +4003,11 @@ async function runTaskList(cwd2, opts) {
3816
4003
  let msg = "No tasks";
3817
4004
  if (filterStatus) msg += ` with status: ${filterStatus}`;
3818
4005
  if (opts.archived) msg += " (archived)";
3819
- console.log(pc17.dim(msg + "."));
4006
+ console.log(pc18.dim(msg + "."));
3820
4007
  return;
3821
4008
  }
3822
4009
  const table = new Table2({
3823
- head: ["ID", "Slug", "Title", "Status"].map((h) => pc17.bold(h)),
4010
+ head: ["ID", "Slug", "Title", "Status"].map((h) => pc18.bold(h)),
3824
4011
  style: { head: [], border: [] }
3825
4012
  });
3826
4013
  for (const t of tasks) {
@@ -3831,7 +4018,7 @@ async function runTaskList(cwd2, opts) {
3831
4018
  if (!opts.archived && !opts.includeArchived) {
3832
4019
  const archivedTasks = await db.getArchivedTasks();
3833
4020
  if (archivedTasks.length > 0) {
3834
- console.log(pc17.dim(`${archivedTasks.length} archived task${archivedTasks.length !== 1 ? "s" : ""} (use --archived to view)`));
4021
+ console.log(pc18.dim(`${archivedTasks.length} archived task${archivedTasks.length !== 1 ? "s" : ""} (use --archived to view)`));
3835
4022
  }
3836
4023
  }
3837
4024
  } finally {
@@ -3841,8 +4028,8 @@ async function runTaskList(cwd2, opts) {
3841
4028
 
3842
4029
  // src/core/path-probe.ts
3843
4030
  import { accessSync, constants, readdirSync as readdirSync3 } from "fs";
3844
- import { join as join21 } from "path";
3845
- import pc18 from "picocolors";
4031
+ import { join as join23 } from "path";
4032
+ import pc19 from "picocolors";
3846
4033
  var DEFAULT_PATHEXT = [".COM", ".EXE", ".BAT", ".CMD"];
3847
4034
  function defaultIsExecutable(filePath) {
3848
4035
  try {
@@ -3886,7 +4073,7 @@ function resolveOnPath(name, options = {}) {
3886
4073
  }
3887
4074
  for (const dir of dirs) {
3888
4075
  try {
3889
- if (isExecutable(join21(dir, name))) return true;
4076
+ if (isExecutable(join23(dir, name))) return true;
3890
4077
  } catch {
3891
4078
  continue;
3892
4079
  }
@@ -3897,35 +4084,35 @@ function isExecutableOnPath(name) {
3897
4084
  return resolveOnPath(name);
3898
4085
  }
3899
4086
  function printMissingGlobalBinaryWarning() {
3900
- console.error(pc18.yellow("\u26A0 `ahk` was not found on your PATH."));
3901
- console.error(pc18.dim(" Your project has no local install, so the generated MCP config launches"));
3902
- console.error(pc18.dim(" `ahk serve` directly. Without `ahk` on your PATH, starting the MCP server"));
3903
- console.error(pc18.dim(" from that config will fail. This is only a warning \u2014 the command continues."));
3904
- console.error(pc18.dim(` Run: npm i -g ${pkg.name} (install globally)`));
3905
- console.error(pc18.dim(` or: npm install --save-dev ${pkg.name} (install locally in this project)`));
4087
+ console.error(pc19.yellow("\u26A0 `ahk` was not found on your PATH."));
4088
+ console.error(pc19.dim(" Your project has no local install, so the generated MCP config launches"));
4089
+ console.error(pc19.dim(" `ahk serve` directly. Without `ahk` on your PATH, starting the MCP server"));
4090
+ console.error(pc19.dim(" from that config will fail. This is only a warning \u2014 the command continues."));
4091
+ console.error(pc19.dim(` Run: npm i -g ${pkg.name} (install globally)`));
4092
+ console.error(pc19.dim(` or: npm install --save-dev ${pkg.name} (install locally in this project)`));
3906
4093
  }
3907
4094
 
3908
4095
  // src/core/update-check.ts
3909
- import pc19 from "picocolors";
4096
+ import pc20 from "picocolors";
3910
4097
  var REGISTRY_URL2 = `https://registry.npmjs.org/${pkg.name}/latest`;
3911
4098
  var TIMEOUT_MS2 = 2500;
3912
4099
  function checkForUpdate(currentVersion) {
3913
- return new Promise((resolve11) => {
3914
- const timer = setTimeout(() => resolve11(null), TIMEOUT_MS2);
4100
+ return new Promise((resolve12) => {
4101
+ const timer = setTimeout(() => resolve12(null), TIMEOUT_MS2);
3915
4102
  fetch(REGISTRY_URL2).then((res) => res.json()).then((data) => {
3916
4103
  clearTimeout(timer);
3917
4104
  const latest = data.version;
3918
- resolve11(isNewer2(latest, currentVersion) ? { current: currentVersion, latest } : null);
4105
+ resolve12(isNewer2(latest, currentVersion) ? { current: currentVersion, latest } : null);
3919
4106
  }).catch(() => {
3920
4107
  clearTimeout(timer);
3921
- resolve11(null);
4108
+ resolve12(null);
3922
4109
  });
3923
4110
  });
3924
4111
  }
3925
4112
  function printUpdateMessage({ current, latest }) {
3926
4113
  const lines = [
3927
- ` Update available ${pc19.dim(current)} \u2192 ${pc19.green(latest)} `,
3928
- ` Run: ${pc19.cyan(`pnpm i ${pkg.name}@${latest}`)} `
4114
+ ` Update available ${pc20.dim(current)} \u2192 ${pc20.green(latest)} `,
4115
+ ` Run: ${pc20.cyan(`pnpm i ${pkg.name}@${latest}`)} `
3929
4116
  ];
3930
4117
  drawBox(lines);
3931
4118
  }
@@ -3955,7 +4142,7 @@ function parsePort(raw) {
3955
4142
  var updateCheck = checkForUpdate(pkg.version);
3956
4143
  var program = new Command();
3957
4144
  program.name("ahk").description("agent-harness-kit \u2014 CLI scaffolding for multi-agent harness systems").version(pkg.version, "-v, --version");
3958
- program.command("init").description("Scaffold a harness interactively in the current directory").option("--name <name>", "Project name (skip prompt)").option("--provider <provider>", "AI provider: claude-code | opencode (skip prompt)").option("--docs <path>", "Docs folder path (skip prompt)").option("--tasks <adapter>", "Task adapter: local | jira | linear (skip prompt)").option("--storage-scope <scope>", "Storage scope: local | global (skip prompt)").action(async (opts) => {
4145
+ program.command("init").description("Scaffold a harness interactively in the current directory").option("--name <name>", "Project name (skip prompt)").option("--provider <provider>", "AI provider: claude-code | opencode | codex-cli | grok-cli (skip prompt)").option("--docs <path>", "Docs folder path (skip prompt)").option("--tasks <adapter>", "Task adapter: local | jira | linear (skip prompt)").option("--storage-scope <scope>", "Storage scope: local | global (skip prompt)").action(async (opts) => {
3959
4146
  await runInit(cwd, opts);
3960
4147
  });
3961
4148
  program.command("build").description("Regenerate AGENTS.md and provider files from agent-harness-kit.config.ts").option("--watch", "Rebuild on config changes").option("--sync", "Sync tools: frontmatter in existing .claude/agents/*.md to match current permission constants").option(
@@ -3993,7 +4180,7 @@ program.command("dashboard").description("Open web dashboard to visualize harnes
3993
4180
  await runDashboard(cwd, { port: opts.port, open: opts.open });
3994
4181
  });
3995
4182
  var migrate = program.command("migrate").description("Migrate provider files to a different provider, or migrate harness storage (see subcommands)");
3996
- migrate.command("provider").description("Migrate provider-specific files to a different provider").option("--to <provider>", "Target provider: claude-code | opencode | codex-cli").action(async (opts) => {
4183
+ migrate.command("provider").description("Migrate provider-specific files to a different provider").option("--to <provider>", "Target provider: claude-code | opencode | codex-cli | grok-cli").action(async (opts) => {
3997
4184
  await runMigrate(cwd, opts);
3998
4185
  });
3999
4186
  migrate.command("storage").description(
@@ -4002,19 +4189,22 @@ migrate.command("storage").description(
4002
4189
  try {
4003
4190
  await runMigrateStorage(cwd, { force: opts.force, dryRun: opts["dry-run"] });
4004
4191
  } catch (err) {
4005
- console.error(pc20.red(`\u2717 ${err instanceof Error ? err.message : String(err)}`));
4192
+ console.error(pc21.red(`\u2717 ${err instanceof Error ? err.message : String(err)}`));
4006
4193
  process.exit(1);
4007
4194
  }
4008
4195
  });
4009
4196
  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) => {
4010
4197
  await runExport(cwd, opts);
4011
4198
  });
4012
- 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) => {
4199
+ program.command("reset").description("Reset/clear harness data (DB, feature list, agent files)").option("--force", "Skip confirmation prompts").option("--provider <claude-code|opencode|codex-cli|grok-cli>", "Reset agent MD files for specified provider").action(async (opts) => {
4013
4200
  await runReset(cwd, opts);
4014
4201
  });
4015
4202
  program.command("doctor").description("Check lib version, agent files, and harness skills sync status").action(async () => {
4016
4203
  await runDoctor(cwd);
4017
4204
  });
4205
+ program.command("models").description("Re-prompt per-role Claude Code models and regenerate .claude/agents/*.md (claude-code projects only)").action(async () => {
4206
+ await runModels(cwd);
4207
+ });
4018
4208
  program.hook("preAction", () => {
4019
4209
  if (!isLocalInstallSatisfied(cwd)) {
4020
4210
  printLocalInstallWarning();