@cardor/agent-harness-kit 2.1.1 → 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
@@ -53,7 +53,7 @@ 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((p9) => existsSync(p9)) ?? 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
@@ -373,6 +373,17 @@ function mergeTomlSection(content, sectionName, sectionBody) {
373
373
  ];
374
374
  return newLines.join("\n");
375
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
+ }
376
387
  function mergeCodexConfigToml(filePath, port, cwd2, pm = "npm") {
377
388
  mkdirSync2(dirname2(filePath), { recursive: true });
378
389
  let content = "";
@@ -383,8 +394,10 @@ function mergeCodexConfigToml(filePath, port, cwd2, pm = "npm") {
383
394
  const sectionBody = [
384
395
  `command = ${JSON.stringify(command)}`,
385
396
  `args = ${JSON.stringify(args)}`,
386
- 'default_tools_approval_mode = "auto"'
397
+ 'default_tools_approval_mode = "approve"'
387
398
  ].join("\n");
399
+ content = ensureTomlTopLevelKey(content, "model", "gpt-5.6-terra");
400
+ content = ensureTomlTopLevelKey(content, "model_reasoning_effort", "medium");
388
401
  content = mergeTomlSection(content, "mcp_servers.agent-harness-kit", sectionBody);
389
402
  writeFileSync2(filePath, content, "utf8");
390
403
  }
@@ -428,14 +441,16 @@ function claudeDisallowedTools(agentName) {
428
441
  function opencodePermissions(agentName) {
429
442
  return restrictionFor(agentName) === "no-write" ? { edit: "deny" } : {};
430
443
  }
431
- function codexSandboxMode(agentName) {
432
- return restrictionFor(agentName) === "no-write" ? "read-only" : "workspace-write";
444
+ function codexSandboxMode(_agentName) {
445
+ return "danger-full-access";
433
446
  }
434
- 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)
435
448
 
436
- 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\`, ...).
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.
437
450
 
438
- 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.`;
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.
452
+
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.`;
439
454
  function codexRestrictionNotice(agentName) {
440
455
  return restrictionFor(agentName) === "no-write" ? CODEX_READ_ONLY_NOTICE : "";
441
456
  }
@@ -783,7 +798,7 @@ function stripFrontmatter(md) {
783
798
  }
784
799
  return { description, body };
785
800
  }
786
- function toCodexToml(tomlName, agentName, description, body) {
801
+ function toCodexToml(tomlName, agentName, description, body, opts) {
787
802
  const safe = (s) => s.replace(/"""/g, '""\\u0022');
788
803
  const sandboxMode = codexSandboxMode(agentName);
789
804
  const notice = codexRestrictionNotice(agentName);
@@ -792,9 +807,14 @@ function toCodexToml(tomlName, agentName, description, body) {
792
807
  ---
793
808
 
794
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
+ ` : "";
795
815
  return `name = "${tomlName}"
796
816
  sandbox_mode = "${sandboxMode}"
797
-
817
+ ${modelBlock}
798
818
  description = """
799
819
  ${safe(description)}
800
820
  """
@@ -804,29 +824,29 @@ ${safe(instructions)}
804
824
  """
805
825
  `;
806
826
  }
807
- function agentLeadToml(vars) {
827
+ function agentLeadToml(vars, opts) {
808
828
  const { description, body } = stripFrontmatter(loadAgentTemplate("lead", vars));
809
- return toCodexToml("lead", "lead", description, body);
829
+ return toCodexToml("lead", "lead", description, body, opts);
810
830
  }
811
- function agentLeadAsDefaultToml(vars) {
831
+ function agentLeadAsDefaultToml(vars, opts) {
812
832
  const { description, body } = stripFrontmatter(loadAgentTemplate("lead", vars));
813
- return toCodexToml("default", "lead", description, body);
833
+ return toCodexToml("default", "lead", description, body, opts);
814
834
  }
815
- function agentExplorerToml(vars) {
835
+ function agentExplorerToml(vars, opts) {
816
836
  const { description, body } = stripFrontmatter(loadAgentTemplate("explorer", vars));
817
- return toCodexToml("explorer", "explorer", description, body);
837
+ return toCodexToml("explorer", "explorer", description, body, opts);
818
838
  }
819
- function agentBuilderToml(vars) {
839
+ function agentBuilderToml(vars, opts) {
820
840
  const { description, body } = stripFrontmatter(loadAgentTemplate("builder", vars));
821
- return toCodexToml("builder", "builder", description, body);
841
+ return toCodexToml("builder", "builder", description, body, opts);
822
842
  }
823
- function agentReviewerToml(vars) {
843
+ function agentReviewerToml(vars, opts) {
824
844
  const { description, body } = stripFrontmatter(loadAgentTemplate("reviewer", vars));
825
- return toCodexToml("reviewer", "reviewer", description, body);
845
+ return toCodexToml("reviewer", "reviewer", description, body, opts);
826
846
  }
827
- function agentConsultantToml(vars) {
847
+ function agentConsultantToml(vars, opts) {
828
848
  const { description, body } = stripFrontmatter(loadAgentTemplate("consultant", vars));
829
- return toCodexToml("consultant", "consultant", description, body);
849
+ return toCodexToml("consultant", "consultant", description, body, opts);
830
850
  }
831
851
  function stripFrontmatterBlockSequence(md, key) {
832
852
  const re = new RegExp(`^${key}:\\n(?: - [^\\n]+\\n)+`, "m");
@@ -1128,20 +1148,20 @@ No tasks in progress.
1128
1148
  // src/core/materializer/codex-cli.ts
1129
1149
  import { existsSync as existsSync7, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
1130
1150
  import { join as join8, resolve as resolve3 } from "path";
1131
- function codexAgentFiles(config) {
1151
+ function codexAgentFiles(config, modelsByRole) {
1132
1152
  const projectName = config.project.name;
1133
1153
  return [
1134
- { relPath: ".codex/agents/lead.toml", content: agentLeadToml({ projectName }) },
1135
- { relPath: ".codex/agents/explorer.toml", content: agentExplorerToml({ projectName }) },
1136
- { relPath: ".codex/agents/consultant.toml", content: agentConsultantToml({ projectName }) },
1137
- { relPath: ".codex/agents/builder.toml", content: agentBuilderToml({ projectName }) },
1138
- { relPath: ".codex/agents/reviewer.toml", content: agentReviewerToml({ projectName }) },
1139
- { 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) }
1140
1160
  ];
1141
1161
  }
1142
1162
  var CodexCliMaterializer = class {
1143
1163
  async scaffold(config, opts) {
1144
- const { cwd: cwd2 } = opts;
1164
+ const { cwd: cwd2, codexAgentModels } = opts;
1145
1165
  const write2 = (relPath, content, mode) => {
1146
1166
  const abs = join8(cwd2, relPath);
1147
1167
  mkdirSync4(resolve3(abs, ".."), { recursive: true });
@@ -1163,7 +1183,7 @@ No tasks in progress.
1163
1183
  `
1164
1184
  );
1165
1185
  }
1166
- writeAgentFiles(cwd2, codexAgentFiles(config));
1186
+ writeAgentFiles(cwd2, codexAgentFiles(config, codexAgentModels));
1167
1187
  mergeCodexConfigToml(join8(cwd2, ".codex/config.toml"), config.tools.mcp.port, cwd2, detectPackageManager(cwd2));
1168
1188
  appendGitignore(cwd2);
1169
1189
  writeSkills(cwd2, ".agents/skills");
@@ -1174,7 +1194,7 @@ No tasks in progress.
1174
1194
  [{ relPath: "AGENTS.md", content: agentsMd(config) }],
1175
1195
  { force: opts.force, backupRoot: join8(cwd2, config.storage.dir, "backups") }
1176
1196
  );
1177
- const agents = writeAgentFiles(cwd2, codexAgentFiles(config), {
1197
+ const agents = writeAgentFiles(cwd2, codexAgentFiles(config, opts.codexAgentModels), {
1178
1198
  force: opts.force,
1179
1199
  backupRoot: join8(cwd2, config.storage.dir, "backups")
1180
1200
  });
@@ -2077,7 +2097,7 @@ function getProviderHealthFiles(provider) {
2077
2097
  // src/commands/init.ts
2078
2098
  import { existsSync as existsSync14, mkdirSync as mkdirSync7, readFileSync as readFileSync9, writeFileSync as writeFileSync8 } from "fs";
2079
2099
  import { join as join16 } from "path";
2080
- import * as p4 from "@clack/prompts";
2100
+ import * as p5 from "@clack/prompts";
2081
2101
  import pc8 from "picocolors";
2082
2102
 
2083
2103
  // src/schema/init.ts
@@ -2129,6 +2149,55 @@ var cliFormWithRetry = async (formFn, schema) => {
2129
2149
  }
2130
2150
  };
2131
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
+
2132
2201
  // src/commands/init-helpers.ts
2133
2202
  import { randomUUID } from "crypto";
2134
2203
  import { existsSync as existsSync13, readFileSync as readFileSync8 } from "fs";
@@ -2286,25 +2355,25 @@ async function runInit(cwd2, flags) {
2286
2355
  name = flags.name;
2287
2356
  } else {
2288
2357
  name = await cliFormWithRetry(async () => {
2289
- const val = await p4.text({
2358
+ const val = await p5.text({
2290
2359
  message: "Project name",
2291
2360
  placeholder: "my-app",
2292
2361
  ...detectedName && { initialValue: detectedName }
2293
2362
  });
2294
- if (p4.isCancel(val)) {
2295
- p4.cancel("Cancelled.");
2363
+ if (p5.isCancel(val)) {
2364
+ p5.cancel("Cancelled.");
2296
2365
  process.exit(0);
2297
2366
  }
2298
2367
  return val;
2299
2368
  }, initNameSchema);
2300
2369
  }
2301
2370
  const description = await cliFormWithRetry(async () => {
2302
- const val = await p4.text({
2371
+ const val = await p5.text({
2303
2372
  message: "Short description (shown to agents as context)",
2304
2373
  placeholder: "A REST API for managing notes"
2305
2374
  });
2306
- if (p4.isCancel(val)) {
2307
- p4.cancel("Cancelled.");
2375
+ if (p5.isCancel(val)) {
2376
+ p5.cancel("Cancelled.");
2308
2377
  process.exit(0);
2309
2378
  }
2310
2379
  return val;
@@ -2313,7 +2382,7 @@ async function runInit(cwd2, flags) {
2313
2382
  if (flags.provider && ["claude-code", "opencode", "codex-cli", "grok-cli"].includes(flags.provider)) {
2314
2383
  provider = flags.provider;
2315
2384
  } else {
2316
- const val = await p4.select({
2385
+ const val = await p5.select({
2317
2386
  message: "AI provider",
2318
2387
  options: [
2319
2388
  { value: "opencode", label: "OpenCode" },
@@ -2322,24 +2391,25 @@ async function runInit(cwd2, flags) {
2322
2391
  { value: "grok-cli", label: "Grok CLI" }
2323
2392
  ]
2324
2393
  });
2325
- if (p4.isCancel(val)) {
2326
- p4.cancel("Cancelled.");
2394
+ if (p5.isCancel(val)) {
2395
+ p5.cancel("Cancelled.");
2327
2396
  process.exit(0);
2328
2397
  }
2329
2398
  provider = val;
2330
2399
  }
2331
2400
  const claudeAgentModels = await promptClaudeAgentModels(provider);
2401
+ const codexAgentModels = await promptCodexAgentModels(provider);
2332
2402
  let docsPath;
2333
2403
  if (flags.docs) {
2334
2404
  docsPath = flags.docs;
2335
2405
  } else {
2336
2406
  docsPath = await cliFormWithRetry(async () => {
2337
- const val = await p4.text({
2407
+ const val = await p5.text({
2338
2408
  message: "Docs folder path (agents will search here)",
2339
2409
  initialValue: "./docs"
2340
2410
  });
2341
- if (p4.isCancel(val)) {
2342
- p4.cancel("Cancelled.");
2411
+ if (p5.isCancel(val)) {
2412
+ p5.cancel("Cancelled.");
2343
2413
  process.exit(0);
2344
2414
  }
2345
2415
  return val;
@@ -2349,7 +2419,7 @@ async function runInit(cwd2, flags) {
2349
2419
  if (flags.storageScope && ["local", "global"].includes(flags.storageScope)) {
2350
2420
  storageScope = flags.storageScope;
2351
2421
  } else {
2352
- const val = await p4.select({
2422
+ const val = await p5.select({
2353
2423
  message: "Storage scope",
2354
2424
  options: [
2355
2425
  { value: "local", label: "Local \u2014 .harness/harness.db lives in this project" },
@@ -2360,8 +2430,8 @@ async function runInit(cwd2, flags) {
2360
2430
  ],
2361
2431
  initialValue: "local"
2362
2432
  });
2363
- if (p4.isCancel(val)) {
2364
- p4.cancel("Cancelled.");
2433
+ if (p5.isCancel(val)) {
2434
+ p5.cancel("Cancelled.");
2365
2435
  process.exit(0);
2366
2436
  }
2367
2437
  storageScope = val;
@@ -2370,7 +2440,7 @@ async function runInit(cwd2, flags) {
2370
2440
  if (flags.tasks && ["local", "jira", "linear"].includes(flags.tasks)) {
2371
2441
  tasksAdapter = flags.tasks;
2372
2442
  } else {
2373
- const val = await p4.select({
2443
+ const val = await p5.select({
2374
2444
  message: "Task adapter",
2375
2445
  options: [
2376
2446
  { value: "local", label: "Local (feature_list.json)" },
@@ -2378,50 +2448,50 @@ async function runInit(cwd2, flags) {
2378
2448
  { value: "linear", label: "Linear (coming soon)" }
2379
2449
  ]
2380
2450
  });
2381
- if (p4.isCancel(val)) {
2382
- p4.cancel("Cancelled");
2451
+ if (p5.isCancel(val)) {
2452
+ p5.cancel("Cancelled");
2383
2453
  process.exit(0);
2384
2454
  }
2385
2455
  tasksAdapter = val;
2386
2456
  }
2387
- const addFirstTask = await p4.confirm({ message: "Add your first task now?", initialValue: false });
2388
- if (p4.isCancel(addFirstTask)) {
2389
- p4.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");
2390
2460
  process.exit(0);
2391
2461
  }
2392
2462
  let firstTask;
2393
2463
  if (addFirstTask) {
2394
2464
  const taskTitle = await cliFormWithRetry(async () => {
2395
- const val = await p4.text({ message: "Task title" });
2396
- if (p4.isCancel(val)) {
2397
- p4.cancel("Cancelled");
2465
+ const val = await p5.text({ message: "Task title" });
2466
+ if (p5.isCancel(val)) {
2467
+ p5.cancel("Cancelled");
2398
2468
  process.exit(0);
2399
2469
  }
2400
2470
  return val.trim();
2401
2471
  }, taskTitleSchema);
2402
2472
  const taskDesc = await cliFormWithRetry(async () => {
2403
- const val = await p4.text({ message: "Task description", placeholder: "What and why" });
2404
- if (p4.isCancel(val)) {
2405
- p4.cancel("Cancelled");
2473
+ const val = await p5.text({ message: "Task description", placeholder: "What and why" });
2474
+ if (p5.isCancel(val)) {
2475
+ p5.cancel("Cancelled");
2406
2476
  process.exit(0);
2407
2477
  }
2408
2478
  return val.trim();
2409
2479
  }, taskDescriptionSchema);
2410
2480
  const acceptance = [];
2411
- p4.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");
2412
2482
  while (true) {
2413
- const criterionVal = await p4.text({
2483
+ const criterionVal = await p5.text({
2414
2484
  message: ">",
2415
2485
  placeholder: "Criterion (or press Enter to finish)"
2416
2486
  });
2417
- if (p4.isCancel(criterionVal) || !criterionVal || !criterionVal.trim()) break;
2487
+ if (p5.isCancel(criterionVal) || !criterionVal || !criterionVal.trim()) break;
2418
2488
  acceptance.push(criterionVal.trim());
2419
2489
  }
2420
2490
  firstTask = { title: taskTitle, description: taskDesc, acceptance };
2421
2491
  }
2422
2492
  let configExt = "ts";
2423
2493
  let featureListParseFailedPath = null;
2424
- const spinner6 = p4.spinner();
2494
+ const spinner6 = p5.spinner();
2425
2495
  spinner6.start("Scaffolding...");
2426
2496
  try {
2427
2497
  const config = applyConfigDefaults({
@@ -2451,7 +2521,7 @@ async function runInit(cwd2, flags) {
2451
2521
  mkdirSync7(join16(installDir, config.storage.dir), { recursive: true });
2452
2522
  const db = await openDB(config, installDir);
2453
2523
  await db.writeStorageState(installDir);
2454
- await materializer.scaffold(config, { cwd: installDir, firstTask, claudeAgentModels });
2524
+ await materializer.scaffold(config, { cwd: installDir, firstTask, claudeAgentModels, codexAgentModels });
2455
2525
  const { parseFailed } = await reconcileFeatureList(db, installDir, config.storage.dir, firstTask);
2456
2526
  if (parseFailed) {
2457
2527
  featureListParseFailedPath = join16(config.storage.dir, "feature_list.json");
@@ -2460,7 +2530,7 @@ async function runInit(cwd2, flags) {
2460
2530
  spinner6.stop("");
2461
2531
  } catch (err) {
2462
2532
  spinner6.stop("Failed");
2463
- p4.log.error(err instanceof Error ? err.message : String(err));
2533
+ p5.log.error(err instanceof Error ? err.message : String(err));
2464
2534
  throw err;
2465
2535
  }
2466
2536
  if (featureListParseFailedPath) {
@@ -2512,7 +2582,7 @@ async function runInit(cwd2, flags) {
2512
2582
  }
2513
2583
 
2514
2584
  // src/commands/migrate.ts
2515
- import * as p5 from "@clack/prompts";
2585
+ import * as p6 from "@clack/prompts";
2516
2586
  import pc9 from "picocolors";
2517
2587
  async function runMigrate(cwd2, opts) {
2518
2588
  const config = await loadConfig(cwd2);
@@ -2520,7 +2590,7 @@ async function runMigrate(cwd2, opts) {
2520
2590
  if (opts.to && ["claude-code", "opencode", "codex-cli", "grok-cli"].includes(opts.to)) {
2521
2591
  target = opts.to;
2522
2592
  } else {
2523
- const val = await p5.select({
2593
+ const val = await p6.select({
2524
2594
  message: "Migrate to provider",
2525
2595
  options: [
2526
2596
  { value: "claude-code", label: "Claude Code" },
@@ -2529,8 +2599,8 @@ async function runMigrate(cwd2, opts) {
2529
2599
  { value: "grok-cli", label: "Grok CLI" }
2530
2600
  ]
2531
2601
  });
2532
- if (p5.isCancel(val)) {
2533
- p5.cancel("Cancelled.");
2602
+ if (p6.isCancel(val)) {
2603
+ p6.cancel("Cancelled.");
2534
2604
  process.exit(0);
2535
2605
  }
2536
2606
  target = val;
@@ -2539,17 +2609,17 @@ async function runMigrate(cwd2, opts) {
2539
2609
  console.log(pc9.dim(`Already on ${target} \u2014 nothing to migrate.`));
2540
2610
  return;
2541
2611
  }
2542
- const spinner6 = p5.spinner();
2612
+ const spinner6 = p6.spinner();
2543
2613
  spinner6.start(`Migrating from ${config.provider} to ${target}...`);
2544
2614
  try {
2545
2615
  const targetMaterializer = getMaterializer(target);
2546
2616
  await targetMaterializer.build(config, cwd2);
2547
2617
  spinner6.stop(pc9.green(`Migrated to ${target}`));
2548
- p5.log.warn(`Update agent-harness-kit.config.ts: set provider: '${target}'`);
2549
- p5.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`);
2550
2620
  } catch (err) {
2551
2621
  spinner6.stop(pc9.red("Migration failed"));
2552
- p5.log.error(err instanceof Error ? err.message : String(err));
2622
+ p6.log.error(err instanceof Error ? err.message : String(err));
2553
2623
  process.exit(1);
2554
2624
  }
2555
2625
  }
@@ -2851,7 +2921,7 @@ async function runModels(cwd2) {
2851
2921
  import { existsSync as existsSync16, readdirSync, rmSync as rmSync2 } from "fs";
2852
2922
  import { homedir as homedir4 } from "os";
2853
2923
  import { join as join19, resolve as resolve8 } from "path";
2854
- import * as p6 from "@clack/prompts";
2924
+ import * as p7 from "@clack/prompts";
2855
2925
  import pc12 from "picocolors";
2856
2926
  var AGENT_MD_FILES = ["lead", "explorer", "consultant", "builder", "reviewer"];
2857
2927
  var PROVIDER_AGENT_DIRS = {
@@ -2891,11 +2961,11 @@ async function resetAgentMds(cwd2, provider) {
2891
2961
  return;
2892
2962
  }
2893
2963
  for (const file of existingFiles) {
2894
- const confirm3 = await p6.confirm({
2964
+ const confirm3 = await p7.confirm({
2895
2965
  message: `Remove ${file}?`,
2896
2966
  initialValue: true
2897
2967
  });
2898
- if (p6.isCancel(confirm3)) {
2968
+ if (p7.isCancel(confirm3)) {
2899
2969
  console.log(pc12.red(" Cancelled by user."));
2900
2970
  return;
2901
2971
  }
@@ -2934,11 +3004,11 @@ async function runReset(cwd2, opts) {
2934
3004
  console.log(pc12.yellow(` Skipping DB reset \u2014 database type "${config.database.type}" is not managed by this command.`));
2935
3005
  resetDb = false;
2936
3006
  } else {
2937
- const confirm3 = await p6.confirm({
3007
+ const confirm3 = await p7.confirm({
2938
3008
  message: `Delete database (${dbPath})?`,
2939
3009
  initialValue: true
2940
3010
  });
2941
- if (p6.isCancel(confirm3)) {
3011
+ if (p7.isCancel(confirm3)) {
2942
3012
  console.log(pc12.red(" Cancelled by user."));
2943
3013
  return;
2944
3014
  }
@@ -2952,11 +3022,11 @@ async function runReset(cwd2, opts) {
2952
3022
  if (opts.force) {
2953
3023
  resetFeatureList = true;
2954
3024
  } else {
2955
- const confirm3 = await p6.confirm({
3025
+ const confirm3 = await p7.confirm({
2956
3026
  message: `Delete feature list (${storageDir}/feature_list.json)?`,
2957
3027
  initialValue: true
2958
3028
  });
2959
- if (p6.isCancel(confirm3)) {
3029
+ if (p7.isCancel(confirm3)) {
2960
3030
  console.log(pc12.red(" Cancelled by user."));
2961
3031
  return;
2962
3032
  }
@@ -3791,15 +3861,15 @@ async function syncOut(db, cwd2, dryRun) {
3791
3861
  }
3792
3862
 
3793
3863
  // src/commands/task/add.ts
3794
- import * as p7 from "@clack/prompts";
3864
+ import * as p8 from "@clack/prompts";
3795
3865
  import pc15 from "picocolors";
3796
3866
  async function runTaskAdd(cwd2) {
3797
- p7.intro(pc15.bold("agent-harness-kit \u2014 add task"));
3867
+ p8.intro(pc15.bold("agent-harness-kit \u2014 add task"));
3798
3868
  const title = await cliFormWithRetry(
3799
3869
  async () => {
3800
- const val = await p7.text({ message: "Task title" });
3801
- if (p7.isCancel(val)) {
3802
- p7.cancel("Cancelled.");
3870
+ const val = await p8.text({ message: "Task title" });
3871
+ if (p8.isCancel(val)) {
3872
+ p8.cancel("Cancelled.");
3803
3873
  process.exit(0);
3804
3874
  }
3805
3875
  return val.trim();
@@ -3808,12 +3878,12 @@ async function runTaskAdd(cwd2) {
3808
3878
  );
3809
3879
  const description = await cliFormWithRetry(
3810
3880
  async () => {
3811
- const val = await p7.text({
3881
+ const val = await p8.text({
3812
3882
  message: "Description (what and why)",
3813
3883
  placeholder: "Describe the task in more detail, including any relevant context or instructions for the agents."
3814
3884
  });
3815
- if (p7.isCancel(val)) {
3816
- p7.cancel("Cancelled.");
3885
+ if (p8.isCancel(val)) {
3886
+ p8.cancel("Cancelled.");
3817
3887
  process.exit(0);
3818
3888
  }
3819
3889
  return val.trim();
@@ -3821,13 +3891,13 @@ async function runTaskAdd(cwd2) {
3821
3891
  taskDescriptionSchema
3822
3892
  );
3823
3893
  const acceptance = [];
3824
- p7.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");
3825
3895
  while (true) {
3826
- const val = await p7.text({ message: ">", placeholder: "Criterion (or press Enter to finish)" });
3827
- if (p7.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;
3828
3898
  acceptance.push(val.trim());
3829
3899
  }
3830
- const spinner6 = p7.spinner();
3900
+ const spinner6 = p8.spinner();
3831
3901
  spinner6.start("Saving...");
3832
3902
  try {
3833
3903
  const config = await loadConfig(cwd2);
@@ -3841,7 +3911,7 @@ async function runTaskAdd(cwd2) {
3841
3911
  console.log(pc15.cyan("\u2192") + " " + pc15.cyan("ahk status") + " to see all tasks");
3842
3912
  } catch (err) {
3843
3913
  spinner6.stop(pc15.red("Failed"));
3844
- p7.log.error(err instanceof Error ? err.message : String(err));
3914
+ p8.log.error(err instanceof Error ? err.message : String(err));
3845
3915
  process.exit(1);
3846
3916
  }
3847
3917
  }
@@ -3887,63 +3957,63 @@ async function runTaskDone(cwd2, idOrSlug) {
3887
3957
  }
3888
3958
 
3889
3959
  // src/commands/task/edit.ts
3890
- import * as p8 from "@clack/prompts";
3960
+ import * as p9 from "@clack/prompts";
3891
3961
  import pc17 from "picocolors";
3892
3962
  async function runTaskEdit(cwd2) {
3893
- p8.intro(pc17.bold("agent-harness-kit \u2014 edit task"));
3963
+ p9.intro(pc17.bold("agent-harness-kit \u2014 edit task"));
3894
3964
  const config = await loadConfig(cwd2);
3895
3965
  const db = await openDB(config, cwd2);
3896
3966
  try {
3897
3967
  const allTasks = await db.getTasks();
3898
3968
  const activeTasks = allTasks.filter((t) => t.status !== "done");
3899
3969
  if (activeTasks.length === 0) {
3900
- p8.log.error("No active tasks to edit.");
3970
+ p9.log.error("No active tasks to edit.");
3901
3971
  return;
3902
3972
  }
3903
- const taskId = await p8.select({
3973
+ const taskId = await p9.select({
3904
3974
  message: "Select a task to edit",
3905
3975
  options: activeTasks.map((t) => ({
3906
3976
  label: `#${t.id} \u2014 ${t.title} (${t.slug})`,
3907
3977
  value: t.id
3908
3978
  }))
3909
3979
  });
3910
- if (p8.isCancel(taskId)) {
3911
- p8.cancel("Cancelled.");
3980
+ if (p9.isCancel(taskId)) {
3981
+ p9.cancel("Cancelled.");
3912
3982
  process.exit(0);
3913
3983
  }
3914
3984
  const task2 = await db.getTaskById(taskId);
3915
3985
  if (!task2) {
3916
- p8.log.error("Task not found");
3986
+ p9.log.error("Task not found");
3917
3987
  process.exit(1);
3918
3988
  }
3919
- const title = await p8.text({
3989
+ const title = await p9.text({
3920
3990
  message: "Title",
3921
3991
  initialValue: task2.title
3922
3992
  });
3923
- if (p8.isCancel(title)) {
3924
- p8.cancel("Cancelled.");
3993
+ if (p9.isCancel(title)) {
3994
+ p9.cancel("Cancelled.");
3925
3995
  process.exit(0);
3926
3996
  }
3927
- const description = await p8.text({
3997
+ const description = await p9.text({
3928
3998
  message: "Description (what and why)",
3929
3999
  initialValue: task2.description ?? ""
3930
4000
  });
3931
- if (p8.isCancel(description)) {
3932
- p8.cancel("Cancelled.");
4001
+ if (p9.isCancel(description)) {
4002
+ p9.cancel("Cancelled.");
3933
4003
  process.exit(0);
3934
4004
  }
3935
4005
  const currentAcceptance = await db.getTaskAcceptance(task2.id);
3936
4006
  const newAcceptance = [];
3937
- p8.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.");
3938
4008
  for (let i = 0; i < currentAcceptance.length; i++) {
3939
4009
  const ac = currentAcceptance[i];
3940
- const val = await p8.text({
4010
+ const val = await p9.text({
3941
4011
  message: `#${i + 1}/${currentAcceptance.length}`,
3942
4012
  initialValue: ac.criterion,
3943
4013
  defaultValue: ""
3944
4014
  });
3945
- if (p8.isCancel(val)) {
3946
- p8.cancel("Cancelled.");
4015
+ if (p9.isCancel(val)) {
4016
+ p9.cancel("Cancelled.");
3947
4017
  process.exit(0);
3948
4018
  }
3949
4019
  const trimmed = val.trim();
@@ -3952,11 +4022,11 @@ async function runTaskEdit(cwd2) {
3952
4022
  }
3953
4023
  }
3954
4024
  while (true) {
3955
- const val = await p8.text({ message: "New acceptance criterion", placeholder: "(press Enter to finish)" });
3956
- if (p8.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;
3957
4027
  newAcceptance.push(val.trim());
3958
4028
  }
3959
- const spinner6 = p8.spinner();
4029
+ const spinner6 = p9.spinner();
3960
4030
  spinner6.start("Saving...");
3961
4031
  try {
3962
4032
  const newSlug = slugify(title);
@@ -3971,7 +4041,7 @@ async function runTaskEdit(cwd2) {
3971
4041
  console.log(pc17.green(`\u2713 Task #${task2.id} updated \u2014 ${newSlug}`));
3972
4042
  } catch (err) {
3973
4043
  spinner6.stop(pc17.red("Failed"));
3974
- p8.log.error(err instanceof Error ? err.message : String(err));
4044
+ p9.log.error(err instanceof Error ? err.message : String(err));
3975
4045
  process.exit(1);
3976
4046
  }
3977
4047
  } finally {