@cardor/agent-harness-kit 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js 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((p10) => existsSync(p10)) ?? 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) {
@@ -365,6 +373,17 @@ function mergeTomlSection(content, sectionName, sectionBody) {
365
373
  ];
366
374
  return newLines.join("\n");
367
375
  }
376
+ function ensureTomlTopLevelKey(content, key, value) {
377
+ const lines = content.length > 0 ? content.split("\n") : [];
378
+ const firstSectionIdx = lines.findIndex((l) => /^\s*\[/.test(l));
379
+ const preambleEnd = firstSectionIdx === -1 ? lines.length : firstSectionIdx;
380
+ const keyRe = new RegExp(`^\\s*${key}\\s*=`);
381
+ const alreadyPresent = lines.slice(0, preambleEnd).some((l) => keyRe.test(l));
382
+ if (alreadyPresent) return content;
383
+ const newLines = [...lines];
384
+ newLines.splice(preambleEnd, 0, `${key} = ${JSON.stringify(value)}`);
385
+ return newLines.join("\n");
386
+ }
368
387
  function mergeCodexConfigToml(filePath, port, cwd2, pm = "npm") {
369
388
  mkdirSync2(dirname2(filePath), { recursive: true });
370
389
  let content = "";
@@ -375,8 +394,10 @@ function mergeCodexConfigToml(filePath, port, cwd2, pm = "npm") {
375
394
  const sectionBody = [
376
395
  `command = ${JSON.stringify(command)}`,
377
396
  `args = ${JSON.stringify(args)}`,
378
- 'default_tools_approval_mode = "auto"'
397
+ 'default_tools_approval_mode = "approve"'
379
398
  ].join("\n");
399
+ content = ensureTomlTopLevelKey(content, "model", "gpt-5.6-terra");
400
+ content = ensureTomlTopLevelKey(content, "model_reasoning_effort", "medium");
380
401
  content = mergeTomlSection(content, "mcp_servers.agent-harness-kit", sectionBody);
381
402
  writeFileSync2(filePath, content, "utf8");
382
403
  }
@@ -420,14 +441,16 @@ function claudeDisallowedTools(agentName) {
420
441
  function opencodePermissions(agentName) {
421
442
  return restrictionFor(agentName) === "no-write" ? { edit: "deny" } : {};
422
443
  }
423
- function codexSandboxMode(agentName) {
424
- return restrictionFor(agentName) === "no-write" ? "read-only" : "workspace-write";
444
+ function codexSandboxMode(_agentName) {
445
+ return "danger-full-access";
425
446
  }
426
- var CODEX_READ_ONLY_NOTICE = `## Tool restrictions (enforced by the sandbox)
447
+ var CODEX_READ_ONLY_NOTICE = `## Tool restrictions (enforced by instruction only \u2014 NOT by the sandbox)
448
+
449
+ This agent runs UNSANDBOXED: \`sandbox_mode = "danger-full-access"\`. There is no OS-level write protection. This is a deliberate project configuration choice, not an oversight.
427
450
 
428
- This agent runs with \`sandbox_mode = "read-only"\`. You MUST NOT create, modify, or delete any file: no \`Write\`, no \`Edit\`, no \`apply_patch\`, and no shell command that writes to disk (\`>\`, \`tee\`, \`sed -i\`, \`mv\`, \`rm\`, ...).
451
+ You MUST NOT create, modify, or delete any file: no \`Write\`, no \`Edit\`, no \`apply_patch\`, and no shell command that writes to disk (\`>\`, \`tee\`, \`sed -i\`, \`mv\`, \`rm\`, ...). This restriction is enforced ONLY by you following this instruction \u2014 nothing will technically block or reject the call.
429
452
 
430
- These tools may still appear available to you. The sandbox will reject the call. Do not retry a rejected write \u2014 report it as a blocker instead.`;
453
+ If you find yourself about to perform a write, STOP. Do not perform it. Report it as a blocker instead. Treat this as a hard rule: breaking it will not fail loudly, it will silently break the harness's audit trail and workflow guarantees.`;
431
454
  function codexRestrictionNotice(agentName) {
432
455
  return restrictionFor(agentName) === "no-write" ? CODEX_READ_ONLY_NOTICE : "";
433
456
  }
@@ -775,7 +798,7 @@ function stripFrontmatter(md) {
775
798
  }
776
799
  return { description, body };
777
800
  }
778
- function toCodexToml(tomlName, agentName, description, body) {
801
+ function toCodexToml(tomlName, agentName, description, body, opts) {
779
802
  const safe = (s) => s.replace(/"""/g, '""\\u0022');
780
803
  const sandboxMode = codexSandboxMode(agentName);
781
804
  const notice = codexRestrictionNotice(agentName);
@@ -784,9 +807,14 @@ function toCodexToml(tomlName, agentName, description, body) {
784
807
  ---
785
808
 
786
809
  ${notice}` : body.trimEnd();
810
+ const modelLines = [];
811
+ if (opts?.model) modelLines.push(`model = "${opts.model}"`);
812
+ if (opts?.effort) modelLines.push(`model_reasoning_effort = "${opts.effort}"`);
813
+ const modelBlock = modelLines.length > 0 ? `${modelLines.join("\n")}
814
+ ` : "";
787
815
  return `name = "${tomlName}"
788
816
  sandbox_mode = "${sandboxMode}"
789
-
817
+ ${modelBlock}
790
818
  description = """
791
819
  ${safe(description)}
792
820
  """
@@ -796,29 +824,29 @@ ${safe(instructions)}
796
824
  """
797
825
  `;
798
826
  }
799
- function agentLeadToml(vars) {
827
+ function agentLeadToml(vars, opts) {
800
828
  const { description, body } = stripFrontmatter(loadAgentTemplate("lead", vars));
801
- return toCodexToml("lead", "lead", description, body);
829
+ return toCodexToml("lead", "lead", description, body, opts);
802
830
  }
803
- function agentLeadAsDefaultToml(vars) {
831
+ function agentLeadAsDefaultToml(vars, opts) {
804
832
  const { description, body } = stripFrontmatter(loadAgentTemplate("lead", vars));
805
- return toCodexToml("default", "lead", description, body);
833
+ return toCodexToml("default", "lead", description, body, opts);
806
834
  }
807
- function agentExplorerToml(vars) {
835
+ function agentExplorerToml(vars, opts) {
808
836
  const { description, body } = stripFrontmatter(loadAgentTemplate("explorer", vars));
809
- return toCodexToml("explorer", "explorer", description, body);
837
+ return toCodexToml("explorer", "explorer", description, body, opts);
810
838
  }
811
- function agentBuilderToml(vars) {
839
+ function agentBuilderToml(vars, opts) {
812
840
  const { description, body } = stripFrontmatter(loadAgentTemplate("builder", vars));
813
- return toCodexToml("builder", "builder", description, body);
841
+ return toCodexToml("builder", "builder", description, body, opts);
814
842
  }
815
- function agentReviewerToml(vars) {
843
+ function agentReviewerToml(vars, opts) {
816
844
  const { description, body } = stripFrontmatter(loadAgentTemplate("reviewer", vars));
817
- return toCodexToml("reviewer", "reviewer", description, body);
845
+ return toCodexToml("reviewer", "reviewer", description, body, opts);
818
846
  }
819
- function agentConsultantToml(vars) {
847
+ function agentConsultantToml(vars, opts) {
820
848
  const { description, body } = stripFrontmatter(loadAgentTemplate("consultant", vars));
821
- return toCodexToml("consultant", "consultant", description, body);
849
+ return toCodexToml("consultant", "consultant", description, body, opts);
822
850
  }
823
851
  function stripFrontmatterBlockSequence(md, key) {
824
852
  const re = new RegExp(`^${key}:\\n(?: - [^\\n]+\\n)+`, "m");
@@ -1083,7 +1111,7 @@ No tasks in progress.
1083
1111
  ],
1084
1112
  { force: opts.force, backupRoot: join7(cwd2, config.storage.dir, "backups") }
1085
1113
  );
1086
- const agents = writeAgentFiles(cwd2, claudeAgentFiles(config), {
1114
+ const agents = writeAgentFiles(cwd2, claudeAgentFiles(config, opts.claudeAgentModels), {
1087
1115
  force: opts.force,
1088
1116
  backupRoot: join7(cwd2, config.storage.dir, "backups")
1089
1117
  });
@@ -1120,20 +1148,20 @@ No tasks in progress.
1120
1148
  // src/core/materializer/codex-cli.ts
1121
1149
  import { existsSync as existsSync7, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
1122
1150
  import { join as join8, resolve as resolve3 } from "path";
1123
- function codexAgentFiles(config) {
1151
+ function codexAgentFiles(config, modelsByRole) {
1124
1152
  const projectName = config.project.name;
1125
1153
  return [
1126
- { relPath: ".codex/agents/lead.toml", content: agentLeadToml({ projectName }) },
1127
- { relPath: ".codex/agents/explorer.toml", content: agentExplorerToml({ projectName }) },
1128
- { relPath: ".codex/agents/consultant.toml", content: agentConsultantToml({ projectName }) },
1129
- { relPath: ".codex/agents/builder.toml", content: agentBuilderToml({ projectName }) },
1130
- { relPath: ".codex/agents/reviewer.toml", content: agentReviewerToml({ projectName }) },
1131
- { relPath: ".codex/agents/default.toml", content: agentLeadAsDefaultToml({ projectName }) }
1154
+ { relPath: ".codex/agents/lead.toml", content: agentLeadToml({ projectName }, modelsByRole?.lead) },
1155
+ { relPath: ".codex/agents/explorer.toml", content: agentExplorerToml({ projectName }, modelsByRole?.explorer) },
1156
+ { relPath: ".codex/agents/consultant.toml", content: agentConsultantToml({ projectName }, modelsByRole?.consultant) },
1157
+ { relPath: ".codex/agents/builder.toml", content: agentBuilderToml({ projectName }, modelsByRole?.builder) },
1158
+ { relPath: ".codex/agents/reviewer.toml", content: agentReviewerToml({ projectName }, modelsByRole?.reviewer) },
1159
+ { relPath: ".codex/agents/default.toml", content: agentLeadAsDefaultToml({ projectName }, modelsByRole?.lead) }
1132
1160
  ];
1133
1161
  }
1134
1162
  var CodexCliMaterializer = class {
1135
1163
  async scaffold(config, opts) {
1136
- const { cwd: cwd2 } = opts;
1164
+ const { cwd: cwd2, codexAgentModels } = opts;
1137
1165
  const write2 = (relPath, content, mode) => {
1138
1166
  const abs = join8(cwd2, relPath);
1139
1167
  mkdirSync4(resolve3(abs, ".."), { recursive: true });
@@ -1155,7 +1183,7 @@ No tasks in progress.
1155
1183
  `
1156
1184
  );
1157
1185
  }
1158
- writeAgentFiles(cwd2, codexAgentFiles(config));
1186
+ writeAgentFiles(cwd2, codexAgentFiles(config, codexAgentModels));
1159
1187
  mergeCodexConfigToml(join8(cwd2, ".codex/config.toml"), config.tools.mcp.port, cwd2, detectPackageManager(cwd2));
1160
1188
  appendGitignore(cwd2);
1161
1189
  writeSkills(cwd2, ".agents/skills");
@@ -1166,7 +1194,7 @@ No tasks in progress.
1166
1194
  [{ relPath: "AGENTS.md", content: agentsMd(config) }],
1167
1195
  { force: opts.force, backupRoot: join8(cwd2, config.storage.dir, "backups") }
1168
1196
  );
1169
- const agents = writeAgentFiles(cwd2, codexAgentFiles(config), {
1197
+ const agents = writeAgentFiles(cwd2, codexAgentFiles(config, opts.codexAgentModels), {
1170
1198
  force: opts.force,
1171
1199
  backupRoot: join8(cwd2, config.storage.dir, "backups")
1172
1200
  });
@@ -1326,20 +1354,53 @@ function getMaterializer(provider) {
1326
1354
  }
1327
1355
  }
1328
1356
 
1357
+ // src/commands/claude-model-prompt.ts
1358
+ import * as p from "@clack/prompts";
1359
+ var AGENT_LABELS = [
1360
+ { key: "lead", label: "Lead" },
1361
+ { key: "explorer", label: "Explorer" },
1362
+ { key: "consultant", label: "Consultant" },
1363
+ { key: "builder", label: "Builder" },
1364
+ { key: "reviewer", label: "Reviewer" }
1365
+ ];
1366
+ async function promptClaudeAgentModels(provider) {
1367
+ const claudeAgentModels = {};
1368
+ if (provider !== "claude-code") return claudeAgentModels;
1369
+ for (const agent of AGENT_LABELS) {
1370
+ const val = await p.select({
1371
+ message: `Model for ${agent.label}`,
1372
+ options: [
1373
+ { value: "inherit", label: "inherit (default)" },
1374
+ { value: "haiku", label: "haiku" },
1375
+ { value: "sonnet", label: "sonnet" },
1376
+ { value: "opus", label: "opus" },
1377
+ { value: "fable", label: "fable" }
1378
+ ],
1379
+ initialValue: "inherit"
1380
+ });
1381
+ if (p.isCancel(val)) {
1382
+ p.cancel("Cancelled.");
1383
+ process.exit(0);
1384
+ }
1385
+ claudeAgentModels[agent.key] = val;
1386
+ }
1387
+ return claudeAgentModels;
1388
+ }
1389
+
1329
1390
  // src/commands/build.ts
1330
1391
  async function runBuild(cwd2, opts) {
1331
1392
  await buildOnce(cwd2, opts.force);
1332
1393
  if (opts.sync) {
1333
- p.log.step("Syncing agent permissions...");
1394
+ p2.log.step("Syncing agent permissions...");
1334
1395
  const config = await loadConfig(cwd2);
1335
1396
  const materializer = getMaterializer(config.provider);
1336
1397
  await materializer.syncPermissions(cwd2);
1337
1398
  }
1338
1399
  if (opts.watch) {
1339
- p.log.info(`Watching agent-harness-kit.config.ts for changes...`);
1400
+ p2.log.info(`Watching agent-harness-kit.config.ts for changes...`);
1340
1401
  watch(cwd2, { recursive: false }, async (_, filename) => {
1341
1402
  if (filename?.startsWith("agent-harness-kit.config")) {
1342
- p.log.step("Config changed \u2014 rebuilding...");
1403
+ p2.log.step("Config changed \u2014 rebuilding...");
1343
1404
  await buildOnce(cwd2, false);
1344
1405
  }
1345
1406
  });
@@ -1348,36 +1409,45 @@ async function runBuild(cwd2, opts) {
1348
1409
  }
1349
1410
  }
1350
1411
  async function buildOnce(cwd2, force) {
1351
- const spinner6 = p.spinner();
1352
- spinner6.start("Loading config...");
1412
+ let config;
1413
+ try {
1414
+ config = await loadConfig(cwd2);
1415
+ } catch (err) {
1416
+ p2.log.error(err instanceof Error ? err.message : String(err));
1417
+ process.exit(1);
1418
+ }
1419
+ let claudeAgentModels;
1420
+ if (force && config.provider === "claude-code") {
1421
+ claudeAgentModels = await promptClaudeAgentModels(config.provider);
1422
+ }
1423
+ const spinner6 = p2.spinner();
1424
+ spinner6.start("Rebuilding files...");
1353
1425
  try {
1354
- const config = await loadConfig(cwd2);
1355
- spinner6.message("Rebuilding files...");
1356
1426
  const materializer = getMaterializer(config.provider);
1357
- const report = await materializer.build(config, cwd2, { force });
1427
+ const report = await materializer.build(config, cwd2, { force, claudeAgentModels });
1358
1428
  spinner6.stop(pc2.green("Build complete"));
1359
1429
  const d = report.derived;
1360
1430
  const upToDate = [...d.created, ...d.current, ...d.propagated];
1361
1431
  if (upToDate.length > 0) {
1362
- p.log.success(upToDate.join(", "));
1432
+ p2.log.success(upToDate.join(", "));
1363
1433
  }
1364
1434
  if (d.propagated.length > 0) {
1365
- p.log.info(`Propagated config changes to ${d.propagated.length} generated file(s):
1435
+ p2.log.info(`Propagated config changes to ${d.propagated.length} generated file(s):
1366
1436
  ${d.propagated.join("\n ")}`);
1367
1437
  }
1368
1438
  if (d.overwritten.length > 0) {
1369
- p.log.warn(
1439
+ p2.log.warn(
1370
1440
  pc2.yellow(
1371
1441
  `--force REGENERATED ${d.overwritten.length} hand-edited generated file(s), discarding your edits:
1372
1442
  ` + d.overwritten.join("\n ")
1373
1443
  )
1374
1444
  );
1375
1445
  if (d.backupDir) {
1376
- p.log.info(pc2.yellow(` Previous content backed up \u2192 ${d.backupDir}`));
1446
+ p2.log.info(pc2.yellow(` Previous content backed up \u2192 ${d.backupDir}`));
1377
1447
  }
1378
1448
  }
1379
1449
  if (d.preserved.length > 0) {
1380
- p.log.warn(
1450
+ p2.log.warn(
1381
1451
  pc2.yellow(
1382
1452
  `Left ${d.preserved.length} hand-edited generated file(s) UNTOUCHED \u2014 your edits are safe:
1383
1453
  ` + d.preserved.join("\n ") + `
@@ -1386,26 +1456,26 @@ async function buildOnce(cwd2, force) {
1386
1456
  )
1387
1457
  );
1388
1458
  }
1389
- p.log.success(`Agent definitions (${config.provider})`);
1390
- p.log.success("MCP config");
1459
+ p2.log.success(`Agent definitions (${config.provider})`);
1460
+ p2.log.success("MCP config");
1391
1461
  const { created, overwritten, preserved, backupDir } = report.agents;
1392
1462
  if (created.length > 0) {
1393
- p.log.info(`Created ${created.length} missing agent file(s):
1463
+ p2.log.info(`Created ${created.length} missing agent file(s):
1394
1464
  ${created.join("\n ")}`);
1395
1465
  }
1396
1466
  if (overwritten.length > 0) {
1397
- p.log.warn(
1467
+ p2.log.warn(
1398
1468
  pc2.yellow(
1399
1469
  `--force REGENERATED ${overwritten.length} existing agent file(s), discarding any customizations:
1400
1470
  ` + overwritten.join("\n ")
1401
1471
  )
1402
1472
  );
1403
1473
  if (backupDir) {
1404
- p.log.info(pc2.yellow(` Previous content backed up \u2192 ${backupDir}`));
1474
+ p2.log.info(pc2.yellow(` Previous content backed up \u2192 ${backupDir}`));
1405
1475
  }
1406
1476
  }
1407
1477
  if (preserved.length > 0) {
1408
- p.log.info(
1478
+ p2.log.info(
1409
1479
  `Left ${preserved.length} existing agent file(s) untouched \u2014 agent files are yours to edit.
1410
1480
  Re-run with --force to regenerate them from the packaged templates (this DESTROYS your edits;
1411
1481
  a backup is written first).`
@@ -1413,7 +1483,7 @@ async function buildOnce(cwd2, force) {
1413
1483
  }
1414
1484
  } catch (err) {
1415
1485
  spinner6.stop(pc2.red("Build failed"));
1416
- p.log.error(err instanceof Error ? err.message : String(err));
1486
+ p2.log.error(err instanceof Error ? err.message : String(err));
1417
1487
  process.exit(1);
1418
1488
  }
1419
1489
  }
@@ -2027,7 +2097,7 @@ function getProviderHealthFiles(provider) {
2027
2097
  // src/commands/init.ts
2028
2098
  import { existsSync as existsSync14, mkdirSync as mkdirSync7, readFileSync as readFileSync9, writeFileSync as writeFileSync8 } from "fs";
2029
2099
  import { join as join16 } from "path";
2030
- import * as p3 from "@clack/prompts";
2100
+ import * as p5 from "@clack/prompts";
2031
2101
  import pc8 from "picocolors";
2032
2102
 
2033
2103
  // src/schema/init.ts
@@ -2066,7 +2136,7 @@ var taskDescriptionSchema = v2.pipe(
2066
2136
  );
2067
2137
 
2068
2138
  // src/utils/form.ts
2069
- import * as p2 from "@clack/prompts";
2139
+ import * as p3 from "@clack/prompts";
2070
2140
  import * as v3 from "valibot";
2071
2141
  var cliFormWithRetry = async (formFn, schema) => {
2072
2142
  while (true) {
@@ -2074,11 +2144,60 @@ var cliFormWithRetry = async (formFn, schema) => {
2074
2144
  const result = v3.safeParse(schema, res);
2075
2145
  if (result.success) return result.output;
2076
2146
  const messages = result.issues.map((i) => i.message).join(", ");
2077
- p2.log.error(messages);
2078
- p2.log.info("Please try again.\n");
2147
+ p3.log.error(messages);
2148
+ p3.log.info("Please try again.\n");
2079
2149
  }
2080
2150
  };
2081
2151
 
2152
+ // src/commands/codex-model-prompt.ts
2153
+ import * as p4 from "@clack/prompts";
2154
+ var AGENT_LABELS2 = [
2155
+ { key: "lead", label: "Lead" },
2156
+ { key: "explorer", label: "Explorer" },
2157
+ { key: "consultant", label: "Consultant" },
2158
+ { key: "builder", label: "Builder" },
2159
+ { key: "reviewer", label: "Reviewer" }
2160
+ ];
2161
+ var CODEX_MODEL_CHOICES = [
2162
+ "gpt-5.6-sol",
2163
+ "gpt-5.6-terra",
2164
+ "gpt-5.6-luna",
2165
+ "gpt-5.5",
2166
+ "gpt-5.4",
2167
+ "gpt-5.4-mini",
2168
+ "gpt-5.3-codex-spark"
2169
+ ];
2170
+ var CODEX_EFFORT_CHOICES = ["minimal", "low", "medium", "high", "xhigh"];
2171
+ async function promptCodexAgentModels(provider) {
2172
+ const codexAgentModels = {};
2173
+ if (provider !== "codex-cli") return codexAgentModels;
2174
+ for (const agent of AGENT_LABELS2) {
2175
+ const modelVal = await p4.select({
2176
+ message: `Model for ${agent.label}`,
2177
+ options: CODEX_MODEL_CHOICES.map((value) => ({ value, label: value })),
2178
+ initialValue: "gpt-5.6-terra"
2179
+ });
2180
+ if (p4.isCancel(modelVal)) {
2181
+ p4.cancel("Cancelled.");
2182
+ process.exit(0);
2183
+ }
2184
+ const effortVal = await p4.select({
2185
+ message: `Reasoning effort for ${agent.label}`,
2186
+ options: CODEX_EFFORT_CHOICES.map((value) => ({ value, label: value })),
2187
+ initialValue: "medium"
2188
+ });
2189
+ if (p4.isCancel(effortVal)) {
2190
+ p4.cancel("Cancelled.");
2191
+ process.exit(0);
2192
+ }
2193
+ codexAgentModels[agent.key] = {
2194
+ model: modelVal,
2195
+ effort: effortVal
2196
+ };
2197
+ }
2198
+ return codexAgentModels;
2199
+ }
2200
+
2082
2201
  // src/commands/init-helpers.ts
2083
2202
  import { randomUUID } from "crypto";
2084
2203
  import { existsSync as existsSync13, readFileSync as readFileSync8 } from "fs";
@@ -2236,25 +2355,25 @@ async function runInit(cwd2, flags) {
2236
2355
  name = flags.name;
2237
2356
  } else {
2238
2357
  name = await cliFormWithRetry(async () => {
2239
- const val = await p3.text({
2358
+ const val = await p5.text({
2240
2359
  message: "Project name",
2241
2360
  placeholder: "my-app",
2242
2361
  ...detectedName && { initialValue: detectedName }
2243
2362
  });
2244
- if (p3.isCancel(val)) {
2245
- p3.cancel("Cancelled.");
2363
+ if (p5.isCancel(val)) {
2364
+ p5.cancel("Cancelled.");
2246
2365
  process.exit(0);
2247
2366
  }
2248
2367
  return val;
2249
2368
  }, initNameSchema);
2250
2369
  }
2251
2370
  const description = await cliFormWithRetry(async () => {
2252
- const val = await p3.text({
2371
+ const val = await p5.text({
2253
2372
  message: "Short description (shown to agents as context)",
2254
2373
  placeholder: "A REST API for managing notes"
2255
2374
  });
2256
- if (p3.isCancel(val)) {
2257
- p3.cancel("Cancelled.");
2375
+ if (p5.isCancel(val)) {
2376
+ p5.cancel("Cancelled.");
2258
2377
  process.exit(0);
2259
2378
  }
2260
2379
  return val;
@@ -2263,7 +2382,7 @@ async function runInit(cwd2, flags) {
2263
2382
  if (flags.provider && ["claude-code", "opencode", "codex-cli", "grok-cli"].includes(flags.provider)) {
2264
2383
  provider = flags.provider;
2265
2384
  } else {
2266
- const val = await p3.select({
2385
+ const val = await p5.select({
2267
2386
  message: "AI provider",
2268
2387
  options: [
2269
2388
  { value: "opencode", label: "OpenCode" },
@@ -2272,51 +2391,25 @@ async function runInit(cwd2, flags) {
2272
2391
  { value: "grok-cli", label: "Grok CLI" }
2273
2392
  ]
2274
2393
  });
2275
- if (p3.isCancel(val)) {
2276
- p3.cancel("Cancelled.");
2394
+ if (p5.isCancel(val)) {
2395
+ p5.cancel("Cancelled.");
2277
2396
  process.exit(0);
2278
2397
  }
2279
2398
  provider = val;
2280
2399
  }
2281
- const AGENT_LABELS = [
2282
- { key: "lead", label: "Lead" },
2283
- { key: "explorer", label: "Explorer" },
2284
- { key: "consultant", label: "Consultant" },
2285
- { key: "builder", label: "Builder" },
2286
- { key: "reviewer", label: "Reviewer" }
2287
- ];
2288
- const claudeAgentModels = {};
2289
- if (provider === "claude-code") {
2290
- for (const agent of AGENT_LABELS) {
2291
- const val = await p3.select({
2292
- message: `Model for ${agent.label}`,
2293
- options: [
2294
- { value: "inherit", label: "inherit (default)" },
2295
- { value: "haiku", label: "haiku" },
2296
- { value: "sonnet", label: "sonnet" },
2297
- { value: "opus", label: "opus" },
2298
- { value: "fable", label: "fable" }
2299
- ],
2300
- initialValue: "inherit"
2301
- });
2302
- if (p3.isCancel(val)) {
2303
- p3.cancel("Cancelled.");
2304
- process.exit(0);
2305
- }
2306
- claudeAgentModels[agent.key] = val;
2307
- }
2308
- }
2400
+ const claudeAgentModels = await promptClaudeAgentModels(provider);
2401
+ const codexAgentModels = await promptCodexAgentModels(provider);
2309
2402
  let docsPath;
2310
2403
  if (flags.docs) {
2311
2404
  docsPath = flags.docs;
2312
2405
  } else {
2313
2406
  docsPath = await cliFormWithRetry(async () => {
2314
- const val = await p3.text({
2407
+ const val = await p5.text({
2315
2408
  message: "Docs folder path (agents will search here)",
2316
2409
  initialValue: "./docs"
2317
2410
  });
2318
- if (p3.isCancel(val)) {
2319
- p3.cancel("Cancelled.");
2411
+ if (p5.isCancel(val)) {
2412
+ p5.cancel("Cancelled.");
2320
2413
  process.exit(0);
2321
2414
  }
2322
2415
  return val;
@@ -2326,7 +2419,7 @@ async function runInit(cwd2, flags) {
2326
2419
  if (flags.storageScope && ["local", "global"].includes(flags.storageScope)) {
2327
2420
  storageScope = flags.storageScope;
2328
2421
  } else {
2329
- const val = await p3.select({
2422
+ const val = await p5.select({
2330
2423
  message: "Storage scope",
2331
2424
  options: [
2332
2425
  { value: "local", label: "Local \u2014 .harness/harness.db lives in this project" },
@@ -2337,8 +2430,8 @@ async function runInit(cwd2, flags) {
2337
2430
  ],
2338
2431
  initialValue: "local"
2339
2432
  });
2340
- if (p3.isCancel(val)) {
2341
- p3.cancel("Cancelled.");
2433
+ if (p5.isCancel(val)) {
2434
+ p5.cancel("Cancelled.");
2342
2435
  process.exit(0);
2343
2436
  }
2344
2437
  storageScope = val;
@@ -2347,7 +2440,7 @@ async function runInit(cwd2, flags) {
2347
2440
  if (flags.tasks && ["local", "jira", "linear"].includes(flags.tasks)) {
2348
2441
  tasksAdapter = flags.tasks;
2349
2442
  } else {
2350
- const val = await p3.select({
2443
+ const val = await p5.select({
2351
2444
  message: "Task adapter",
2352
2445
  options: [
2353
2446
  { value: "local", label: "Local (feature_list.json)" },
@@ -2355,50 +2448,50 @@ async function runInit(cwd2, flags) {
2355
2448
  { value: "linear", label: "Linear (coming soon)" }
2356
2449
  ]
2357
2450
  });
2358
- if (p3.isCancel(val)) {
2359
- p3.cancel("Cancelled");
2451
+ if (p5.isCancel(val)) {
2452
+ p5.cancel("Cancelled");
2360
2453
  process.exit(0);
2361
2454
  }
2362
2455
  tasksAdapter = val;
2363
2456
  }
2364
- const addFirstTask = await p3.confirm({ message: "Add your first task now?", initialValue: false });
2365
- if (p3.isCancel(addFirstTask)) {
2366
- p3.cancel("Cancelled");
2457
+ const addFirstTask = await p5.confirm({ message: "Add your first task now?", initialValue: false });
2458
+ if (p5.isCancel(addFirstTask)) {
2459
+ p5.cancel("Cancelled");
2367
2460
  process.exit(0);
2368
2461
  }
2369
2462
  let firstTask;
2370
2463
  if (addFirstTask) {
2371
2464
  const taskTitle = await cliFormWithRetry(async () => {
2372
- const val = await p3.text({ message: "Task title" });
2373
- if (p3.isCancel(val)) {
2374
- p3.cancel("Cancelled");
2465
+ const val = await p5.text({ message: "Task title" });
2466
+ if (p5.isCancel(val)) {
2467
+ p5.cancel("Cancelled");
2375
2468
  process.exit(0);
2376
2469
  }
2377
2470
  return val.trim();
2378
2471
  }, taskTitleSchema);
2379
2472
  const taskDesc = await cliFormWithRetry(async () => {
2380
- const val = await p3.text({ message: "Task description", placeholder: "What and why" });
2381
- if (p3.isCancel(val)) {
2382
- p3.cancel("Cancelled");
2473
+ const val = await p5.text({ message: "Task description", placeholder: "What and why" });
2474
+ if (p5.isCancel(val)) {
2475
+ p5.cancel("Cancelled");
2383
2476
  process.exit(0);
2384
2477
  }
2385
2478
  return val.trim();
2386
2479
  }, taskDescriptionSchema);
2387
2480
  const acceptance = [];
2388
- p3.log.info("Acceptance criteria \u2014 one per line, empty line to finish");
2481
+ p5.log.info("Acceptance criteria \u2014 one per line, empty line to finish");
2389
2482
  while (true) {
2390
- const criterionVal = await p3.text({
2483
+ const criterionVal = await p5.text({
2391
2484
  message: ">",
2392
2485
  placeholder: "Criterion (or press Enter to finish)"
2393
2486
  });
2394
- if (p3.isCancel(criterionVal) || !criterionVal || !criterionVal.trim()) break;
2487
+ if (p5.isCancel(criterionVal) || !criterionVal || !criterionVal.trim()) break;
2395
2488
  acceptance.push(criterionVal.trim());
2396
2489
  }
2397
2490
  firstTask = { title: taskTitle, description: taskDesc, acceptance };
2398
2491
  }
2399
2492
  let configExt = "ts";
2400
2493
  let featureListParseFailedPath = null;
2401
- const spinner6 = p3.spinner();
2494
+ const spinner6 = p5.spinner();
2402
2495
  spinner6.start("Scaffolding...");
2403
2496
  try {
2404
2497
  const config = applyConfigDefaults({
@@ -2428,7 +2521,7 @@ async function runInit(cwd2, flags) {
2428
2521
  mkdirSync7(join16(installDir, config.storage.dir), { recursive: true });
2429
2522
  const db = await openDB(config, installDir);
2430
2523
  await db.writeStorageState(installDir);
2431
- await materializer.scaffold(config, { cwd: installDir, firstTask, claudeAgentModels });
2524
+ await materializer.scaffold(config, { cwd: installDir, firstTask, claudeAgentModels, codexAgentModels });
2432
2525
  const { parseFailed } = await reconcileFeatureList(db, installDir, config.storage.dir, firstTask);
2433
2526
  if (parseFailed) {
2434
2527
  featureListParseFailedPath = join16(config.storage.dir, "feature_list.json");
@@ -2437,7 +2530,7 @@ async function runInit(cwd2, flags) {
2437
2530
  spinner6.stop("");
2438
2531
  } catch (err) {
2439
2532
  spinner6.stop("Failed");
2440
- p3.log.error(err instanceof Error ? err.message : String(err));
2533
+ p5.log.error(err instanceof Error ? err.message : String(err));
2441
2534
  throw err;
2442
2535
  }
2443
2536
  if (featureListParseFailedPath) {
@@ -2489,7 +2582,7 @@ async function runInit(cwd2, flags) {
2489
2582
  }
2490
2583
 
2491
2584
  // src/commands/migrate.ts
2492
- import * as p4 from "@clack/prompts";
2585
+ import * as p6 from "@clack/prompts";
2493
2586
  import pc9 from "picocolors";
2494
2587
  async function runMigrate(cwd2, opts) {
2495
2588
  const config = await loadConfig(cwd2);
@@ -2497,7 +2590,7 @@ async function runMigrate(cwd2, opts) {
2497
2590
  if (opts.to && ["claude-code", "opencode", "codex-cli", "grok-cli"].includes(opts.to)) {
2498
2591
  target = opts.to;
2499
2592
  } else {
2500
- const val = await p4.select({
2593
+ const val = await p6.select({
2501
2594
  message: "Migrate to provider",
2502
2595
  options: [
2503
2596
  { value: "claude-code", label: "Claude Code" },
@@ -2506,8 +2599,8 @@ async function runMigrate(cwd2, opts) {
2506
2599
  { value: "grok-cli", label: "Grok CLI" }
2507
2600
  ]
2508
2601
  });
2509
- if (p4.isCancel(val)) {
2510
- p4.cancel("Cancelled.");
2602
+ if (p6.isCancel(val)) {
2603
+ p6.cancel("Cancelled.");
2511
2604
  process.exit(0);
2512
2605
  }
2513
2606
  target = val;
@@ -2516,17 +2609,17 @@ async function runMigrate(cwd2, opts) {
2516
2609
  console.log(pc9.dim(`Already on ${target} \u2014 nothing to migrate.`));
2517
2610
  return;
2518
2611
  }
2519
- const spinner6 = p4.spinner();
2612
+ const spinner6 = p6.spinner();
2520
2613
  spinner6.start(`Migrating from ${config.provider} to ${target}...`);
2521
2614
  try {
2522
2615
  const targetMaterializer = getMaterializer(target);
2523
2616
  await targetMaterializer.build(config, cwd2);
2524
2617
  spinner6.stop(pc9.green(`Migrated to ${target}`));
2525
- p4.log.warn(`Update agent-harness-kit.config.ts: set provider: '${target}'`);
2526
- p4.log.warn(`Then run: ahk build`);
2618
+ p6.log.warn(`Update agent-harness-kit.config.ts: set provider: '${target}'`);
2619
+ p6.log.warn(`Then run: ahk build`);
2527
2620
  } catch (err) {
2528
2621
  spinner6.stop(pc9.red("Migration failed"));
2529
- p4.log.error(err instanceof Error ? err.message : String(err));
2622
+ p6.log.error(err instanceof Error ? err.message : String(err));
2530
2623
  process.exit(1);
2531
2624
  }
2532
2625
  }
@@ -2771,12 +2864,65 @@ async function migrateAcrossDbType(cwd2, config, homeDir, sourceScope, opts) {
2771
2864
  }
2772
2865
  }
2773
2866
 
2867
+ // src/commands/models.ts
2868
+ import { join as join18 } from "path";
2869
+ import pc11 from "picocolors";
2870
+ async function resolveModelsContext(cwd2) {
2871
+ let config;
2872
+ try {
2873
+ config = await loadConfig(cwd2);
2874
+ } catch {
2875
+ return { ok: false, reason: "no-config" };
2876
+ }
2877
+ if (config.provider !== "claude-code") {
2878
+ return { ok: false, reason: "not-claude-code", provider: config.provider };
2879
+ }
2880
+ return { ok: true, config };
2881
+ }
2882
+ async function runModels(cwd2) {
2883
+ const ctx = await resolveModelsContext(cwd2);
2884
+ if (!ctx.ok) {
2885
+ if (ctx.reason === "no-config") {
2886
+ console.log("");
2887
+ console.log(` ${pc11.cyan("config".padEnd(16))}${pc11.yellow("[!]")} no agent-harness-kit.config found`);
2888
+ console.log(` ${"".padEnd(16)} ${pc11.dim("run: ahk init")}`);
2889
+ console.log("");
2890
+ return;
2891
+ }
2892
+ console.log(
2893
+ pc11.dim(
2894
+ `ahk models only applies to Claude Code projects (this project uses '${ctx.provider}') \u2014 nothing to do.`
2895
+ )
2896
+ );
2897
+ return;
2898
+ }
2899
+ const { config } = ctx;
2900
+ const claudeAgentModels = await promptClaudeAgentModels(config.provider);
2901
+ const agents = writeAgentFiles(cwd2, claudeAgentFiles(config, claudeAgentModels), {
2902
+ force: true,
2903
+ backupRoot: join18(cwd2, config.storage.dir, "backups")
2904
+ });
2905
+ console.log("");
2906
+ if (agents.overwritten.length > 0) {
2907
+ console.log(pc11.green(`\u2713 Regenerated ${agents.overwritten.length} agent file(s) with updated models:`));
2908
+ for (const file of agents.overwritten) console.log(pc11.green(` \u2713 ${file}`));
2909
+ if (agents.backupDir) {
2910
+ console.log(pc11.dim(` Previous content backed up \u2192 ${agents.backupDir}`));
2911
+ }
2912
+ }
2913
+ if (agents.created.length > 0) {
2914
+ console.log(pc11.green(`\u2713 Created ${agents.created.length} missing agent file(s):`));
2915
+ for (const file of agents.created) console.log(pc11.green(` \u2713 ${file}`));
2916
+ }
2917
+ console.log("");
2918
+ }
2919
+
2774
2920
  // src/commands/reset.ts
2775
2921
  import { existsSync as existsSync16, readdirSync, rmSync as rmSync2 } from "fs";
2776
2922
  import { homedir as homedir4 } from "os";
2777
- import { join as join18, resolve as resolve8 } from "path";
2778
- import * as p5 from "@clack/prompts";
2779
- import pc11 from "picocolors";
2923
+ import { join as join19, resolve as resolve8 } from "path";
2924
+ import * as p7 from "@clack/prompts";
2925
+ import pc12 from "picocolors";
2780
2926
  var AGENT_MD_FILES = ["lead", "explorer", "consultant", "builder", "reviewer"];
2781
2927
  var PROVIDER_AGENT_DIRS = {
2782
2928
  "claude-code": ".claude/agents",
@@ -2795,7 +2941,7 @@ async function resetAgentMds(cwd2, provider) {
2795
2941
  const agentDirPath = resolve8(cwd2, agentDir);
2796
2942
  const agentExt = PROVIDER_AGENT_EXT[provider];
2797
2943
  if (!existsSync16(agentDirPath)) {
2798
- console.log(pc11.yellow(` Skipping agent files \u2014 directory not found: ${agentDirPath}`));
2944
+ console.log(pc12.yellow(` Skipping agent files \u2014 directory not found: ${agentDirPath}`));
2799
2945
  return;
2800
2946
  }
2801
2947
  const existingFiles = [];
@@ -2807,32 +2953,32 @@ async function resetAgentMds(cwd2, provider) {
2807
2953
  }
2808
2954
  }
2809
2955
  } catch {
2810
- console.log(pc11.yellow(` Skipping agent files \u2014 ${agentDirPath} is not readable`));
2956
+ console.log(pc12.yellow(` Skipping agent files \u2014 ${agentDirPath} is not readable`));
2811
2957
  return;
2812
2958
  }
2813
2959
  if (existingFiles.length === 0) {
2814
- console.log(pc11.yellow(` No agent MD files found in ${agentDir}/`));
2960
+ console.log(pc12.yellow(` No agent MD files found in ${agentDir}/`));
2815
2961
  return;
2816
2962
  }
2817
2963
  for (const file of existingFiles) {
2818
- const confirm3 = await p5.confirm({
2964
+ const confirm3 = await p7.confirm({
2819
2965
  message: `Remove ${file}?`,
2820
2966
  initialValue: true
2821
2967
  });
2822
- if (p5.isCancel(confirm3)) {
2823
- console.log(pc11.red(" Cancelled by user."));
2968
+ if (p7.isCancel(confirm3)) {
2969
+ console.log(pc12.red(" Cancelled by user."));
2824
2970
  return;
2825
2971
  }
2826
2972
  if (confirm3) {
2827
2973
  try {
2828
- const filePath = join18(agentDirPath, file);
2974
+ const filePath = join19(agentDirPath, file);
2829
2975
  rmSync2(filePath, { force: true });
2830
- console.log(pc11.green(` Removed ${file}`));
2976
+ console.log(pc12.green(` Removed ${file}`));
2831
2977
  } catch {
2832
- console.error(pc11.red(` Failed to remove ${file}`));
2978
+ console.error(pc12.red(` Failed to remove ${file}`));
2833
2979
  }
2834
2980
  } else {
2835
- console.log(pc11.cyan(` Skipped ${file}`));
2981
+ console.log(pc12.cyan(` Skipped ${file}`));
2836
2982
  }
2837
2983
  }
2838
2984
  }
@@ -2841,7 +2987,7 @@ async function runReset(cwd2, opts) {
2841
2987
  try {
2842
2988
  config = await loadConfig(cwd2);
2843
2989
  } catch {
2844
- console.error(pc11.red("\u2717 No agent-harness-kit.config found. Run: ahk init"));
2990
+ console.error(pc12.red("\u2717 No agent-harness-kit.config found. Run: ahk init"));
2845
2991
  process.exit(1);
2846
2992
  }
2847
2993
  const storageDir = config.storage.dir || ".harness";
@@ -2855,33 +3001,33 @@ async function runReset(cwd2, opts) {
2855
3001
  resetDb = true;
2856
3002
  } else {
2857
3003
  if (config.database.type !== "sqlite") {
2858
- console.log(pc11.yellow(` Skipping DB reset \u2014 database type "${config.database.type}" is not managed by this command.`));
3004
+ console.log(pc12.yellow(` Skipping DB reset \u2014 database type "${config.database.type}" is not managed by this command.`));
2859
3005
  resetDb = false;
2860
3006
  } else {
2861
- const confirm3 = await p5.confirm({
3007
+ const confirm3 = await p7.confirm({
2862
3008
  message: `Delete database (${dbPath})?`,
2863
3009
  initialValue: true
2864
3010
  });
2865
- if (p5.isCancel(confirm3)) {
2866
- console.log(pc11.red(" Cancelled by user."));
3011
+ if (p7.isCancel(confirm3)) {
3012
+ console.log(pc12.red(" Cancelled by user."));
2867
3013
  return;
2868
3014
  }
2869
3015
  resetDb = confirm3;
2870
3016
  }
2871
3017
  }
2872
3018
  } else if (!dbPath) {
2873
- console.log(pc11.dim(` Skipping DB reset \u2014 remote ${config.database.type} database is not managed by this command.`));
3019
+ console.log(pc12.dim(` Skipping DB reset \u2014 remote ${config.database.type} database is not managed by this command.`));
2874
3020
  }
2875
3021
  if (existsSync16(featureListPath)) {
2876
3022
  if (opts.force) {
2877
3023
  resetFeatureList = true;
2878
3024
  } else {
2879
- const confirm3 = await p5.confirm({
3025
+ const confirm3 = await p7.confirm({
2880
3026
  message: `Delete feature list (${storageDir}/feature_list.json)?`,
2881
3027
  initialValue: true
2882
3028
  });
2883
- if (p5.isCancel(confirm3)) {
2884
- console.log(pc11.red(" Cancelled by user."));
3029
+ if (p7.isCancel(confirm3)) {
3030
+ console.log(pc12.red(" Cancelled by user."));
2885
3031
  return;
2886
3032
  }
2887
3033
  resetFeatureList = confirm3;
@@ -2895,17 +3041,17 @@ async function runReset(cwd2, opts) {
2895
3041
  rmSync2(dbPath, { force: true });
2896
3042
  rmSync2(`${dbPath}-wal`, { force: true });
2897
3043
  rmSync2(`${dbPath}-shm`, { force: true });
2898
- console.log(pc11.green(` \u2713 Removed ${dbPath}`));
3044
+ console.log(pc12.green(` \u2713 Removed ${dbPath}`));
2899
3045
  } catch {
2900
- console.error(pc11.red(` \u2717 Failed to remove ${dbPath}`));
3046
+ console.error(pc12.red(` \u2717 Failed to remove ${dbPath}`));
2901
3047
  }
2902
3048
  }
2903
3049
  if (resetFeatureList) {
2904
3050
  try {
2905
3051
  rmSync2(featureListPath, { force: true });
2906
- console.log(pc11.green(` \u2713 Removed ${storageDir}/feature_list.json`));
3052
+ console.log(pc12.green(` \u2713 Removed ${storageDir}/feature_list.json`));
2907
3053
  } catch {
2908
- console.error(pc11.red(` \u2717 Failed to remove ${featureListPath}`));
3054
+ console.error(pc12.red(` \u2717 Failed to remove ${featureListPath}`));
2909
3055
  }
2910
3056
  }
2911
3057
  if (resetAgentMdsFlag) {
@@ -2913,16 +3059,16 @@ async function runReset(cwd2, opts) {
2913
3059
  await resetAgentMds(cwd2, opts.provider || "claude-code");
2914
3060
  }
2915
3061
  if (!resetDb && !resetFeatureList && !resetAgentMdsFlag) {
2916
- console.log(pc11.yellow(" Nothing to reset (all items missing or skipped)."));
3062
+ console.log(pc12.yellow(" Nothing to reset (all items missing or skipped)."));
2917
3063
  return;
2918
3064
  }
2919
3065
  console.log("");
2920
- console.log(pc11.green('\u2713 Reset complete. Run "ahk init" to scaffold a fresh harness.'));
3066
+ console.log(pc12.green('\u2713 Reset complete. Run "ahk init" to scaffold a fresh harness.'));
2921
3067
  }
2922
3068
 
2923
3069
  // src/core/mcp-server.ts
2924
3070
  import { existsSync as existsSync18, mkdirSync as mkdirSync9, readdirSync as readdirSync2, readFileSync as readFileSync10, statSync, writeFileSync as writeFileSync10 } from "fs";
2925
- import { join as join20, resolve as resolve9 } from "path";
3071
+ import { join as join21, resolve as resolve9 } from "path";
2926
3072
  import { Server } from "@modelcontextprotocol/sdk/server";
2927
3073
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
2928
3074
  import {
@@ -2932,7 +3078,7 @@ import {
2932
3078
 
2933
3079
  // src/core/permissions-check.ts
2934
3080
  import { existsSync as existsSync17 } from "fs";
2935
- import { join as join19 } from "path";
3081
+ import { join as join20 } from "path";
2936
3082
  var AGENTS = ["lead", "explorer", "consultant", "builder", "reviewer"];
2937
3083
  function checkPermissionsSync(cwd2, config) {
2938
3084
  if (config.provider !== "claude-code") {
@@ -2941,7 +3087,7 @@ function checkPermissionsSync(cwd2, config) {
2941
3087
  const agents = {};
2942
3088
  let in_sync = true;
2943
3089
  for (const agent of AGENTS) {
2944
- const filePath = join19(cwd2, ".claude", "agents", `${agent}.md`);
3090
+ const filePath = join20(cwd2, ".claude", "agents", `${agent}.md`);
2945
3091
  const exists = existsSync17(filePath);
2946
3092
  if (!exists) in_sync = false;
2947
3093
  agents[agent] = exists ? { ok: true } : { ok: false, reason: "missing_file" };
@@ -3403,7 +3549,7 @@ async function dispatch(name, args, db, docsPath, cwd2, config) {
3403
3549
  return ok2(JSON.stringify(result));
3404
3550
  }
3405
3551
  case "deps.snapshot": {
3406
- const pkgPath2 = join20(cwd2, "package.json");
3552
+ const pkgPath2 = join21(cwd2, "package.json");
3407
3553
  if (!existsSync18(pkgPath2)) {
3408
3554
  return ok2("package.json not found in project root", true);
3409
3555
  }
@@ -3413,9 +3559,9 @@ async function dispatch(name, args, db, docsPath, cwd2, config) {
3413
3559
  dependencies: pkg2.dependencies ?? {},
3414
3560
  devDependencies: pkg2.devDependencies ?? {}
3415
3561
  };
3416
- const harnessDir = join20(cwd2, ".harness");
3562
+ const harnessDir = join21(cwd2, ".harness");
3417
3563
  mkdirSync9(harnessDir, { recursive: true });
3418
- writeFileSync10(join20(harnessDir, "deps-lock.json"), JSON.stringify(snapshot, null, 2), "utf8");
3564
+ writeFileSync10(join21(harnessDir, "deps-lock.json"), JSON.stringify(snapshot, null, 2), "utf8");
3419
3565
  return ok2(
3420
3566
  JSON.stringify({
3421
3567
  message: "Snapshot saved to .harness/deps-lock.json",
@@ -3424,8 +3570,8 @@ async function dispatch(name, args, db, docsPath, cwd2, config) {
3424
3570
  );
3425
3571
  }
3426
3572
  case "deps.check": {
3427
- const pkgPath2 = join20(cwd2, "package.json");
3428
- const lockPath = join20(cwd2, ".harness", "deps-lock.json");
3573
+ const pkgPath2 = join21(cwd2, "package.json");
3574
+ const lockPath = join21(cwd2, ".harness", "deps-lock.json");
3429
3575
  if (!existsSync18(pkgPath2)) {
3430
3576
  return ok2("package.json not found in project root", true);
3431
3577
  }
@@ -3531,7 +3677,7 @@ function collectMarkdownFiles(dir) {
3531
3677
  const files = [];
3532
3678
  try {
3533
3679
  for (const entry of readdirSync2(dir)) {
3534
- const full = join20(dir, entry);
3680
+ const full = join21(dir, entry);
3535
3681
  const stat = statSync(full);
3536
3682
  if (stat.isDirectory()) {
3537
3683
  files.push(...collectMarkdownFiles(full));
@@ -3588,12 +3734,12 @@ async function runServe(cwd2, opts) {
3588
3734
 
3589
3735
  // src/commands/status.ts
3590
3736
  import Table from "cli-table3";
3591
- import pc12 from "picocolors";
3737
+ import pc13 from "picocolors";
3592
3738
  var STATUS_COLOR = {
3593
- pending: (s) => pc12.dim(s),
3594
- in_progress: (s) => pc12.cyan(s),
3595
- done: (s) => pc12.green(s),
3596
- blocked: (s) => pc12.red(s)
3739
+ pending: (s) => pc13.dim(s),
3740
+ in_progress: (s) => pc13.cyan(s),
3741
+ done: (s) => pc13.green(s),
3742
+ blocked: (s) => pc13.red(s)
3597
3743
  };
3598
3744
  async function runStatus(cwd2, opts) {
3599
3745
  const config = await loadConfig(cwd2);
@@ -3614,11 +3760,11 @@ async function runStatus(cwd2, opts) {
3614
3760
  return;
3615
3761
  }
3616
3762
  if (tasks.length === 0) {
3617
- console.log(pc12.dim("No tasks yet. Run: ahk task add"));
3763
+ console.log(pc13.dim("No tasks yet. Run: ahk task add"));
3618
3764
  return;
3619
3765
  }
3620
3766
  const table = new Table({
3621
- head: ["ID", "Slug", "Title", "Status", "Assigned", "Started"].map((h) => pc12.bold(h)),
3767
+ head: ["ID", "Slug", "Title", "Status", "Assigned", "Started"].map((h) => pc13.bold(h)),
3622
3768
  style: { head: [], border: [] }
3623
3769
  });
3624
3770
  for (const t of tasks) {
@@ -3636,12 +3782,12 @@ async function runStatus(cwd2, opts) {
3636
3782
  const inProgress = tasks.filter((t) => t.status === "in_progress");
3637
3783
  if (inProgress.length > 0) {
3638
3784
  console.log("");
3639
- console.log(pc12.bold("Active actions:"));
3785
+ console.log(pc13.bold("Active actions:"));
3640
3786
  for (const t of inProgress) {
3641
3787
  const actions = await db.getActionsForTask(t.id);
3642
3788
  const active = actions.filter((a) => a.status === "in_progress");
3643
3789
  for (const a of active) {
3644
- console.log(` ${pc12.cyan(a.agent.padEnd(10))} \u2192 task #${t.id} ${t.slug}`);
3790
+ console.log(` ${pc13.cyan(a.agent.padEnd(10))} \u2192 task #${t.id} ${t.slug}`);
3645
3791
  }
3646
3792
  }
3647
3793
  }
@@ -3650,10 +3796,10 @@ async function runStatus(cwd2, opts) {
3650
3796
  const fn = STATUS_COLOR[s.status] ?? ((x) => x);
3651
3797
  return `${fn(s.status)}: ${s.total}`;
3652
3798
  });
3653
- console.log(pc12.dim("Tasks \u2014 ") + parts.join(pc12.dim(" | ")));
3799
+ console.log(pc13.dim("Tasks \u2014 ") + parts.join(pc13.dim(" | ")));
3654
3800
  const archivedTasks = await db.getArchivedTasks();
3655
3801
  if (archivedTasks.length > 0) {
3656
- console.log(pc12.dim(`${archivedTasks.length} archived (use \`ahk task list --archived\` to view)`));
3802
+ console.log(pc13.dim(`${archivedTasks.length} archived (use \`ahk task list --archived\` to view)`));
3657
3803
  }
3658
3804
  } finally {
3659
3805
  await db.close();
@@ -3662,12 +3808,12 @@ async function runStatus(cwd2, opts) {
3662
3808
 
3663
3809
  // src/commands/sync.ts
3664
3810
  import { existsSync as existsSync19, readFileSync as readFileSync11 } from "fs";
3665
- import { join as join21, resolve as resolve10 } from "path";
3666
- import pc13 from "picocolors";
3811
+ import { join as join22, resolve as resolve10 } from "path";
3812
+ import pc14 from "picocolors";
3667
3813
  async function runSync(cwd2, opts) {
3668
3814
  const config = await loadConfig(cwd2);
3669
3815
  const direction = opts.direction ?? "both";
3670
- const featureListPath = resolve10(join21(cwd2, config.storage.dir, "feature_list.json"));
3816
+ const featureListPath = resolve10(join22(cwd2, config.storage.dir, "feature_list.json"));
3671
3817
  const db = await openDB(config, cwd2);
3672
3818
  try {
3673
3819
  if (direction === "in" || direction === "both") {
@@ -3682,48 +3828,48 @@ async function runSync(cwd2, opts) {
3682
3828
  }
3683
3829
  async function syncIn(featureListPath, db, dryRun) {
3684
3830
  if (!existsSync19(featureListPath)) {
3685
- console.log(pc13.dim(`feature_list.json not found at ${featureListPath} \u2014 skipping in-sync`));
3831
+ console.log(pc14.dim(`feature_list.json not found at ${featureListPath} \u2014 skipping in-sync`));
3686
3832
  return;
3687
3833
  }
3688
3834
  let seeds;
3689
3835
  try {
3690
3836
  seeds = JSON.parse(readFileSync11(featureListPath, "utf8"));
3691
3837
  } catch (err) {
3692
- console.error(pc13.red(`Failed to parse feature_list.json: ${err}`));
3838
+ console.error(pc14.red(`Failed to parse feature_list.json: ${err}`));
3693
3839
  process.exit(1);
3694
3840
  }
3695
3841
  if (dryRun) {
3696
- console.log(pc13.bold("Dry run \u2014 in-sync (feature_list.json \u2192 SQLite):"));
3842
+ console.log(pc14.bold("Dry run \u2014 in-sync (feature_list.json \u2192 SQLite):"));
3697
3843
  for (const t of seeds) {
3698
3844
  const existing = await db.getTaskBySlug(t.slug);
3699
- console.log(` ${existing ? pc13.dim("skip") : pc13.green("add ")} ${t.slug}`);
3845
+ console.log(` ${existing ? pc14.dim("skip") : pc14.green("add ")} ${t.slug}`);
3700
3846
  }
3701
3847
  return;
3702
3848
  }
3703
3849
  const result = await db.syncFromFeatureList(seeds);
3704
- console.log(pc13.green(`\u2713 In-sync: ${result.added} added, ${result.skipped} already existed`));
3850
+ console.log(pc14.green(`\u2713 In-sync: ${result.added} added, ${result.skipped} already existed`));
3705
3851
  }
3706
3852
  async function syncOut(db, cwd2, dryRun) {
3707
3853
  if (dryRun) {
3708
3854
  const tasks = await db.getTasks();
3709
- console.log(pc13.bold("Dry run \u2014 out-sync (SQLite \u2192 feature_list.json):"));
3855
+ console.log(pc14.bold("Dry run \u2014 out-sync (SQLite \u2192 feature_list.json):"));
3710
3856
  console.log(` ${tasks.length} tasks would be written`);
3711
3857
  return;
3712
3858
  }
3713
3859
  await db.writeFeatureList(cwd2);
3714
- console.log(pc13.green("\u2713 Out-sync: feature_list.json updated"));
3860
+ console.log(pc14.green("\u2713 Out-sync: feature_list.json updated"));
3715
3861
  }
3716
3862
 
3717
3863
  // src/commands/task/add.ts
3718
- import * as p6 from "@clack/prompts";
3719
- import pc14 from "picocolors";
3864
+ import * as p8 from "@clack/prompts";
3865
+ import pc15 from "picocolors";
3720
3866
  async function runTaskAdd(cwd2) {
3721
- p6.intro(pc14.bold("agent-harness-kit \u2014 add task"));
3867
+ p8.intro(pc15.bold("agent-harness-kit \u2014 add task"));
3722
3868
  const title = await cliFormWithRetry(
3723
3869
  async () => {
3724
- const val = await p6.text({ message: "Task title" });
3725
- if (p6.isCancel(val)) {
3726
- p6.cancel("Cancelled.");
3870
+ const val = await p8.text({ message: "Task title" });
3871
+ if (p8.isCancel(val)) {
3872
+ p8.cancel("Cancelled.");
3727
3873
  process.exit(0);
3728
3874
  }
3729
3875
  return val.trim();
@@ -3732,12 +3878,12 @@ async function runTaskAdd(cwd2) {
3732
3878
  );
3733
3879
  const description = await cliFormWithRetry(
3734
3880
  async () => {
3735
- const val = await p6.text({
3881
+ const val = await p8.text({
3736
3882
  message: "Description (what and why)",
3737
3883
  placeholder: "Describe the task in more detail, including any relevant context or instructions for the agents."
3738
3884
  });
3739
- if (p6.isCancel(val)) {
3740
- p6.cancel("Cancelled.");
3885
+ if (p8.isCancel(val)) {
3886
+ p8.cancel("Cancelled.");
3741
3887
  process.exit(0);
3742
3888
  }
3743
3889
  return val.trim();
@@ -3745,13 +3891,13 @@ async function runTaskAdd(cwd2) {
3745
3891
  taskDescriptionSchema
3746
3892
  );
3747
3893
  const acceptance = [];
3748
- p6.log.info("Acceptance criteria \u2014 one per line, empty line to finish");
3894
+ p8.log.info("Acceptance criteria \u2014 one per line, empty line to finish");
3749
3895
  while (true) {
3750
- const val = await p6.text({ message: ">", placeholder: "Criterion (or press Enter to finish)" });
3751
- if (p6.isCancel(val) || !val || !val.trim()) break;
3896
+ const val = await p8.text({ message: ">", placeholder: "Criterion (or press Enter to finish)" });
3897
+ if (p8.isCancel(val) || !val || !val.trim()) break;
3752
3898
  acceptance.push(val.trim());
3753
3899
  }
3754
- const spinner6 = p6.spinner();
3900
+ const spinner6 = p8.spinner();
3755
3901
  spinner6.start("Saving...");
3756
3902
  try {
3757
3903
  const config = await loadConfig(cwd2);
@@ -3761,11 +3907,11 @@ async function runTaskAdd(cwd2) {
3761
3907
  await db.writeFeatureList(cwd2);
3762
3908
  await db.close();
3763
3909
  spinner6.stop("");
3764
- console.log(pc14.green(`\u2713 Task #${task2.id} added \u2014 ${task2.slug} (pending)`));
3765
- console.log(pc14.cyan("\u2192") + " " + pc14.cyan("ahk status") + " to see all tasks");
3910
+ console.log(pc15.green(`\u2713 Task #${task2.id} added \u2014 ${task2.slug} (pending)`));
3911
+ console.log(pc15.cyan("\u2192") + " " + pc15.cyan("ahk status") + " to see all tasks");
3766
3912
  } catch (err) {
3767
- spinner6.stop(pc14.red("Failed"));
3768
- p6.log.error(err instanceof Error ? err.message : String(err));
3913
+ spinner6.stop(pc15.red("Failed"));
3914
+ p8.log.error(err instanceof Error ? err.message : String(err));
3769
3915
  process.exit(1);
3770
3916
  }
3771
3917
  }
@@ -3774,7 +3920,7 @@ async function runTaskAdd(cwd2) {
3774
3920
  import { spawnSync as spawnSync2 } from "child_process";
3775
3921
  import { existsSync as existsSync20 } from "fs";
3776
3922
  import { resolve as resolve11 } from "path";
3777
- import pc15 from "picocolors";
3923
+ import pc16 from "picocolors";
3778
3924
  async function runTaskDone(cwd2, idOrSlug) {
3779
3925
  const config = await loadConfig(cwd2);
3780
3926
  if (config.health.required) {
@@ -3782,7 +3928,7 @@ async function runTaskDone(cwd2, idOrSlug) {
3782
3928
  if (existsSync20(scriptPath)) {
3783
3929
  const result = spawnSync2("bash", [scriptPath], { cwd: cwd2, stdio: "pipe", encoding: "utf8" });
3784
3930
  if (result.status !== 0) {
3785
- console.error(pc15.red("\u2717 Health check failed \u2014 cannot mark task as done."));
3931
+ console.error(pc16.red("\u2717 Health check failed \u2014 cannot mark task as done."));
3786
3932
  if (result.stdout) console.error(result.stdout);
3787
3933
  if (result.stderr) console.error(result.stderr);
3788
3934
  process.exit(1);
@@ -3795,79 +3941,79 @@ async function runTaskDone(cwd2, idOrSlug) {
3795
3941
  const isId = !isNaN(parsed);
3796
3942
  const task2 = isId ? await db.getTaskById(parsed) : await db.getTaskBySlug(idOrSlug);
3797
3943
  if (!task2) {
3798
- console.error(pc15.red(`Task not found: ${idOrSlug}`));
3944
+ console.error(pc16.red(`Task not found: ${idOrSlug}`));
3799
3945
  process.exit(1);
3800
3946
  }
3801
3947
  if (task2.status === "done") {
3802
- console.log(pc15.dim(`Task #${task2.id} is already done.`));
3948
+ console.log(pc16.dim(`Task #${task2.id} is already done.`));
3803
3949
  return;
3804
3950
  }
3805
3951
  await db.updateTaskStatus(task2.id, "done");
3806
3952
  await db.writeFeatureList(cwd2);
3807
- console.log(pc15.green(`\u2713 Task #${task2.id} \u2014 ${task2.slug} marked as done`));
3953
+ console.log(pc16.green(`\u2713 Task #${task2.id} \u2014 ${task2.slug} marked as done`));
3808
3954
  } finally {
3809
3955
  await db.close();
3810
3956
  }
3811
3957
  }
3812
3958
 
3813
3959
  // src/commands/task/edit.ts
3814
- import * as p7 from "@clack/prompts";
3815
- import pc16 from "picocolors";
3960
+ import * as p9 from "@clack/prompts";
3961
+ import pc17 from "picocolors";
3816
3962
  async function runTaskEdit(cwd2) {
3817
- p7.intro(pc16.bold("agent-harness-kit \u2014 edit task"));
3963
+ p9.intro(pc17.bold("agent-harness-kit \u2014 edit task"));
3818
3964
  const config = await loadConfig(cwd2);
3819
3965
  const db = await openDB(config, cwd2);
3820
3966
  try {
3821
3967
  const allTasks = await db.getTasks();
3822
3968
  const activeTasks = allTasks.filter((t) => t.status !== "done");
3823
3969
  if (activeTasks.length === 0) {
3824
- p7.log.error("No active tasks to edit.");
3970
+ p9.log.error("No active tasks to edit.");
3825
3971
  return;
3826
3972
  }
3827
- const taskId = await p7.select({
3973
+ const taskId = await p9.select({
3828
3974
  message: "Select a task to edit",
3829
3975
  options: activeTasks.map((t) => ({
3830
3976
  label: `#${t.id} \u2014 ${t.title} (${t.slug})`,
3831
3977
  value: t.id
3832
3978
  }))
3833
3979
  });
3834
- if (p7.isCancel(taskId)) {
3835
- p7.cancel("Cancelled.");
3980
+ if (p9.isCancel(taskId)) {
3981
+ p9.cancel("Cancelled.");
3836
3982
  process.exit(0);
3837
3983
  }
3838
3984
  const task2 = await db.getTaskById(taskId);
3839
3985
  if (!task2) {
3840
- p7.log.error("Task not found");
3986
+ p9.log.error("Task not found");
3841
3987
  process.exit(1);
3842
3988
  }
3843
- const title = await p7.text({
3989
+ const title = await p9.text({
3844
3990
  message: "Title",
3845
3991
  initialValue: task2.title
3846
3992
  });
3847
- if (p7.isCancel(title)) {
3848
- p7.cancel("Cancelled.");
3993
+ if (p9.isCancel(title)) {
3994
+ p9.cancel("Cancelled.");
3849
3995
  process.exit(0);
3850
3996
  }
3851
- const description = await p7.text({
3997
+ const description = await p9.text({
3852
3998
  message: "Description (what and why)",
3853
3999
  initialValue: task2.description ?? ""
3854
4000
  });
3855
- if (p7.isCancel(description)) {
3856
- p7.cancel("Cancelled.");
4001
+ if (p9.isCancel(description)) {
4002
+ p9.cancel("Cancelled.");
3857
4003
  process.exit(0);
3858
4004
  }
3859
4005
  const currentAcceptance = await db.getTaskAcceptance(task2.id);
3860
4006
  const newAcceptance = [];
3861
- p7.log.info("Acceptance criteria \u2014 edit each, empty to delete. Add new ones at the end.");
4007
+ p9.log.info("Acceptance criteria \u2014 edit each, empty to delete. Add new ones at the end.");
3862
4008
  for (let i = 0; i < currentAcceptance.length; i++) {
3863
4009
  const ac = currentAcceptance[i];
3864
- const val = await p7.text({
4010
+ const val = await p9.text({
3865
4011
  message: `#${i + 1}/${currentAcceptance.length}`,
3866
4012
  initialValue: ac.criterion,
3867
4013
  defaultValue: ""
3868
4014
  });
3869
- if (p7.isCancel(val)) {
3870
- p7.cancel("Cancelled.");
4015
+ if (p9.isCancel(val)) {
4016
+ p9.cancel("Cancelled.");
3871
4017
  process.exit(0);
3872
4018
  }
3873
4019
  const trimmed = val.trim();
@@ -3876,11 +4022,11 @@ async function runTaskEdit(cwd2) {
3876
4022
  }
3877
4023
  }
3878
4024
  while (true) {
3879
- const val = await p7.text({ message: "New acceptance criterion", placeholder: "(press Enter to finish)" });
3880
- if (p7.isCancel(val) || !val || !val.trim()) break;
4025
+ const val = await p9.text({ message: "New acceptance criterion", placeholder: "(press Enter to finish)" });
4026
+ if (p9.isCancel(val) || !val || !val.trim()) break;
3881
4027
  newAcceptance.push(val.trim());
3882
4028
  }
3883
- const spinner6 = p7.spinner();
4029
+ const spinner6 = p9.spinner();
3884
4030
  spinner6.start("Saving...");
3885
4031
  try {
3886
4032
  const newSlug = slugify(title);
@@ -3892,10 +4038,10 @@ async function runTaskEdit(cwd2) {
3892
4038
  await db.updateTaskAcceptance(task2.id, newAcceptance);
3893
4039
  await db.writeFeatureList(cwd2);
3894
4040
  spinner6.stop("");
3895
- console.log(pc16.green(`\u2713 Task #${task2.id} updated \u2014 ${newSlug}`));
4041
+ console.log(pc17.green(`\u2713 Task #${task2.id} updated \u2014 ${newSlug}`));
3896
4042
  } catch (err) {
3897
- spinner6.stop(pc16.red("Failed"));
3898
- p7.log.error(err instanceof Error ? err.message : String(err));
4043
+ spinner6.stop(pc17.red("Failed"));
4044
+ p9.log.error(err instanceof Error ? err.message : String(err));
3899
4045
  process.exit(1);
3900
4046
  }
3901
4047
  } finally {
@@ -3905,12 +4051,12 @@ async function runTaskEdit(cwd2) {
3905
4051
 
3906
4052
  // src/commands/task/list.ts
3907
4053
  import Table2 from "cli-table3";
3908
- import pc17 from "picocolors";
4054
+ import pc18 from "picocolors";
3909
4055
  var STATUS_COLOR2 = {
3910
- pending: (s) => pc17.dim(s),
3911
- in_progress: (s) => pc17.cyan(s),
3912
- done: (s) => pc17.green(s),
3913
- blocked: (s) => pc17.red(s)
4056
+ pending: (s) => pc18.dim(s),
4057
+ in_progress: (s) => pc18.cyan(s),
4058
+ done: (s) => pc18.green(s),
4059
+ blocked: (s) => pc18.red(s)
3914
4060
  };
3915
4061
  async function runTaskList(cwd2, opts) {
3916
4062
  const config = await loadConfig(cwd2);
@@ -3927,11 +4073,11 @@ async function runTaskList(cwd2, opts) {
3927
4073
  let msg = "No tasks";
3928
4074
  if (filterStatus) msg += ` with status: ${filterStatus}`;
3929
4075
  if (opts.archived) msg += " (archived)";
3930
- console.log(pc17.dim(msg + "."));
4076
+ console.log(pc18.dim(msg + "."));
3931
4077
  return;
3932
4078
  }
3933
4079
  const table = new Table2({
3934
- head: ["ID", "Slug", "Title", "Status"].map((h) => pc17.bold(h)),
4080
+ head: ["ID", "Slug", "Title", "Status"].map((h) => pc18.bold(h)),
3935
4081
  style: { head: [], border: [] }
3936
4082
  });
3937
4083
  for (const t of tasks) {
@@ -3942,7 +4088,7 @@ async function runTaskList(cwd2, opts) {
3942
4088
  if (!opts.archived && !opts.includeArchived) {
3943
4089
  const archivedTasks = await db.getArchivedTasks();
3944
4090
  if (archivedTasks.length > 0) {
3945
- console.log(pc17.dim(`${archivedTasks.length} archived task${archivedTasks.length !== 1 ? "s" : ""} (use --archived to view)`));
4091
+ console.log(pc18.dim(`${archivedTasks.length} archived task${archivedTasks.length !== 1 ? "s" : ""} (use --archived to view)`));
3946
4092
  }
3947
4093
  }
3948
4094
  } finally {
@@ -3952,8 +4098,8 @@ async function runTaskList(cwd2, opts) {
3952
4098
 
3953
4099
  // src/core/path-probe.ts
3954
4100
  import { accessSync, constants, readdirSync as readdirSync3 } from "fs";
3955
- import { join as join22 } from "path";
3956
- import pc18 from "picocolors";
4101
+ import { join as join23 } from "path";
4102
+ import pc19 from "picocolors";
3957
4103
  var DEFAULT_PATHEXT = [".COM", ".EXE", ".BAT", ".CMD"];
3958
4104
  function defaultIsExecutable(filePath) {
3959
4105
  try {
@@ -3997,7 +4143,7 @@ function resolveOnPath(name, options = {}) {
3997
4143
  }
3998
4144
  for (const dir of dirs) {
3999
4145
  try {
4000
- if (isExecutable(join22(dir, name))) return true;
4146
+ if (isExecutable(join23(dir, name))) return true;
4001
4147
  } catch {
4002
4148
  continue;
4003
4149
  }
@@ -4008,16 +4154,16 @@ function isExecutableOnPath(name) {
4008
4154
  return resolveOnPath(name);
4009
4155
  }
4010
4156
  function printMissingGlobalBinaryWarning() {
4011
- console.error(pc18.yellow("\u26A0 `ahk` was not found on your PATH."));
4012
- console.error(pc18.dim(" Your project has no local install, so the generated MCP config launches"));
4013
- console.error(pc18.dim(" `ahk serve` directly. Without `ahk` on your PATH, starting the MCP server"));
4014
- console.error(pc18.dim(" from that config will fail. This is only a warning \u2014 the command continues."));
4015
- console.error(pc18.dim(` Run: npm i -g ${pkg.name} (install globally)`));
4016
- console.error(pc18.dim(` or: npm install --save-dev ${pkg.name} (install locally in this project)`));
4157
+ console.error(pc19.yellow("\u26A0 `ahk` was not found on your PATH."));
4158
+ console.error(pc19.dim(" Your project has no local install, so the generated MCP config launches"));
4159
+ console.error(pc19.dim(" `ahk serve` directly. Without `ahk` on your PATH, starting the MCP server"));
4160
+ console.error(pc19.dim(" from that config will fail. This is only a warning \u2014 the command continues."));
4161
+ console.error(pc19.dim(` Run: npm i -g ${pkg.name} (install globally)`));
4162
+ console.error(pc19.dim(` or: npm install --save-dev ${pkg.name} (install locally in this project)`));
4017
4163
  }
4018
4164
 
4019
4165
  // src/core/update-check.ts
4020
- import pc19 from "picocolors";
4166
+ import pc20 from "picocolors";
4021
4167
  var REGISTRY_URL2 = `https://registry.npmjs.org/${pkg.name}/latest`;
4022
4168
  var TIMEOUT_MS2 = 2500;
4023
4169
  function checkForUpdate(currentVersion) {
@@ -4035,8 +4181,8 @@ function checkForUpdate(currentVersion) {
4035
4181
  }
4036
4182
  function printUpdateMessage({ current, latest }) {
4037
4183
  const lines = [
4038
- ` Update available ${pc19.dim(current)} \u2192 ${pc19.green(latest)} `,
4039
- ` Run: ${pc19.cyan(`pnpm i ${pkg.name}@${latest}`)} `
4184
+ ` Update available ${pc20.dim(current)} \u2192 ${pc20.green(latest)} `,
4185
+ ` Run: ${pc20.cyan(`pnpm i ${pkg.name}@${latest}`)} `
4040
4186
  ];
4041
4187
  drawBox(lines);
4042
4188
  }
@@ -4113,7 +4259,7 @@ migrate.command("storage").description(
4113
4259
  try {
4114
4260
  await runMigrateStorage(cwd, { force: opts.force, dryRun: opts["dry-run"] });
4115
4261
  } catch (err) {
4116
- console.error(pc20.red(`\u2717 ${err instanceof Error ? err.message : String(err)}`));
4262
+ console.error(pc21.red(`\u2717 ${err instanceof Error ? err.message : String(err)}`));
4117
4263
  process.exit(1);
4118
4264
  }
4119
4265
  });
@@ -4126,6 +4272,9 @@ program.command("reset").description("Reset/clear harness data (DB, feature list
4126
4272
  program.command("doctor").description("Check lib version, agent files, and harness skills sync status").action(async () => {
4127
4273
  await runDoctor(cwd);
4128
4274
  });
4275
+ program.command("models").description("Re-prompt per-role Claude Code models and regenerate .claude/agents/*.md (claude-code projects only)").action(async () => {
4276
+ await runModels(cwd);
4277
+ });
4129
4278
  program.hook("preAction", () => {
4130
4279
  if (!isLocalInstallSatisfied(cwd)) {
4131
4280
  printLocalInstallWarning();