@cardor/agent-harness-kit 1.6.0 → 1.6.3

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
@@ -131,12 +131,24 @@ var MCP_CLAUDE_PERMISSIONS_REVIEWER = [
131
131
  "mcp__agent-harness-kit__tasks_acceptance_get",
132
132
  "mcp__agent-harness-kit__docs_search"
133
133
  ];
134
+ var MCP_CLAUDE_PERMISSIONS_CONSULTANT = [
135
+ "mcp__agent-harness-kit__actions_start",
136
+ "mcp__agent-harness-kit__actions_write",
137
+ "mcp__agent-harness-kit__actions_complete",
138
+ "mcp__agent-harness-kit__actions_get",
139
+ "mcp__agent-harness-kit__actions_record_file",
140
+ "mcp__agent-harness-kit__actions_record_tool",
141
+ "mcp__agent-harness-kit__tasks_get",
142
+ "mcp__agent-harness-kit__deps_snapshot",
143
+ "mcp__agent-harness-kit__deps_check"
144
+ ];
134
145
  var MCP_CLAUDE_PERMISSIONS = [
135
146
  .../* @__PURE__ */ new Set([
136
147
  ...MCP_CLAUDE_PERMISSIONS_LEAD,
137
148
  ...MCP_CLAUDE_PERMISSIONS_EXPLORER,
138
149
  ...MCP_CLAUDE_PERMISSIONS_BUILDER,
139
- ...MCP_CLAUDE_PERMISSIONS_REVIEWER
150
+ ...MCP_CLAUDE_PERMISSIONS_REVIEWER,
151
+ ...MCP_CLAUDE_PERMISSIONS_CONSULTANT
140
152
  ])
141
153
  ];
142
154
  function mergeClaudeSettingsLocalJson(filePath) {
@@ -491,6 +503,9 @@ function agentExplorer(vars) {
491
503
  function agentBuilder(vars) {
492
504
  return loadAgentTemplate("builder", vars);
493
505
  }
506
+ function agentConsultant(vars) {
507
+ return loadAgentTemplate("consultant", vars);
508
+ }
494
509
  function agentReviewer(vars) {
495
510
  return loadAgentTemplate("reviewer", vars);
496
511
  }
@@ -548,10 +563,11 @@ function agentReviewerToml(vars) {
548
563
  }
549
564
  function translateFrontmatterForClaudeCode(md, agentName) {
550
565
  const permissionsMap = {
551
- lead: MCP_CLAUDE_PERMISSIONS_LEAD,
552
- explorer: MCP_CLAUDE_PERMISSIONS_EXPLORER,
553
- builder: MCP_CLAUDE_PERMISSIONS_BUILDER,
554
- reviewer: MCP_CLAUDE_PERMISSIONS_REVIEWER
566
+ lead: [...MCP_CLAUDE_PERMISSIONS_LEAD],
567
+ explorer: [...MCP_CLAUDE_PERMISSIONS_EXPLORER],
568
+ consultant: [...MCP_CLAUDE_PERMISSIONS_CONSULTANT],
569
+ builder: [...MCP_CLAUDE_PERMISSIONS_BUILDER],
570
+ reviewer: [...MCP_CLAUDE_PERMISSIONS_REVIEWER]
555
571
  };
556
572
  const permissions = permissionsMap[agentName] ?? MCP_CLAUDE_PERMISSIONS;
557
573
  const mcpLines = permissions.map((t) => ` - ${t}`).join("\n");
@@ -589,6 +605,33 @@ function appendGitignore(cwd2) {
589
605
  function slugify(title) {
590
606
  return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
591
607
  }
608
+ var AGENT_TOOLS = {
609
+ lead: [...MCP_CLAUDE_PERMISSIONS_LEAD],
610
+ explorer: [...MCP_CLAUDE_PERMISSIONS_EXPLORER],
611
+ consultant: [...MCP_CLAUDE_PERMISSIONS_CONSULTANT],
612
+ builder: [...MCP_CLAUDE_PERMISSIONS_BUILDER],
613
+ reviewer: [...MCP_CLAUDE_PERMISSIONS_REVIEWER]
614
+ };
615
+ async function syncAgentPermissions(cwd2) {
616
+ for (const [agent, tools] of Object.entries(AGENT_TOOLS)) {
617
+ const filePath = join3(cwd2, ".claude", "agents", `${agent}.md`);
618
+ if (!existsSync2(filePath)) {
619
+ console.log(` ${agent}.md not found \u2014 skipping`);
620
+ continue;
621
+ }
622
+ const content = readFileSync3(filePath, "utf-8");
623
+ const toolsBlock = `tools:
624
+ ${tools.map((t) => ` - ${t}`).join("\n")}
625
+ `;
626
+ const updated = content.replace(/tools:\n(?: - [^\n]+\n)*/m, toolsBlock);
627
+ if (updated === content) {
628
+ console.log(` ${agent}.md already in sync`);
629
+ } else {
630
+ writeFileSync3(filePath, updated, "utf-8");
631
+ console.log(` ${agent}.md updated`);
632
+ }
633
+ }
634
+ }
592
635
 
593
636
  // src/core/materializer/claude-code.ts
594
637
  var ClaudeCodeMaterializer = class {
@@ -619,6 +662,7 @@ No tasks in progress.
619
662
  const writablePaths = (config.agents.builder.writablePaths ?? []).join(", ");
620
663
  writeAgentFile(cwd2, ".claude/agents/lead.md", translateFrontmatterForClaudeCode(agentLead({ projectName }), "lead"));
621
664
  writeAgentFile(cwd2, ".claude/agents/explorer.md", translateFrontmatterForClaudeCode(agentExplorer({ projectName, allowedPaths }), "explorer"));
665
+ writeAgentFile(cwd2, ".claude/agents/consultant.md", translateFrontmatterForClaudeCode(agentConsultant({ projectName }), "consultant"));
622
666
  writeAgentFile(cwd2, ".claude/agents/builder.md", translateFrontmatterForClaudeCode(agentBuilder({ projectName, writablePaths }), "builder"));
623
667
  writeAgentFile(cwd2, ".claude/agents/reviewer.md", translateFrontmatterForClaudeCode(agentReviewer({ projectName }), "reviewer"));
624
668
  mergeClaudeMcpJson(join4(cwd2, ".mcp.json"), config.tools.mcp.port);
@@ -639,6 +683,7 @@ No tasks in progress.
639
683
  const writablePaths = (config.agents.builder.writablePaths ?? []).join(", ");
640
684
  writeAgentFile(cwd2, ".claude/agents/lead.md", translateFrontmatterForClaudeCode(agentLead({ projectName }), "lead"));
641
685
  writeAgentFile(cwd2, ".claude/agents/explorer.md", translateFrontmatterForClaudeCode(agentExplorer({ projectName, allowedPaths }), "explorer"));
686
+ writeAgentFile(cwd2, ".claude/agents/consultant.md", translateFrontmatterForClaudeCode(agentConsultant({ projectName }), "consultant"));
642
687
  writeAgentFile(cwd2, ".claude/agents/builder.md", translateFrontmatterForClaudeCode(agentBuilder({ projectName, writablePaths }), "builder"));
643
688
  writeAgentFile(cwd2, ".claude/agents/reviewer.md", translateFrontmatterForClaudeCode(agentReviewer({ projectName }), "reviewer"));
644
689
  mergeClaudeMcpJson(join4(cwd2, ".mcp.json"), config.tools.mcp.port);
@@ -789,6 +834,9 @@ function getMaterializer(provider) {
789
834
  // src/commands/build.ts
790
835
  async function runBuild(cwd2, opts) {
791
836
  await buildOnce(cwd2);
837
+ if (opts.sync) {
838
+ await syncAgentPermissions(cwd2);
839
+ }
792
840
  if (opts.watch) {
793
841
  p.log.info(`Watching agent-harness-kit.config.ts for changes...`);
794
842
  watch(cwd2, { recursive: false }, async (_, filename) => {
@@ -1642,7 +1690,7 @@ async function openDB(config, cwd2) {
1642
1690
  const { MySQLDriver } = await import("./mysql-THKQOXIS.js");
1643
1691
  driver = new MySQLDriver(dbConfig);
1644
1692
  } else {
1645
- const { SQLiteDriver } = await import("./sqlite-XBEJJ5T2.js");
1693
+ const { SQLiteDriver } = await import("./sqlite-KWYK4IJW.js");
1646
1694
  if (dbConfig.type !== "sqlite") {
1647
1695
  throw new Error("Invalid database type");
1648
1696
  }
@@ -2324,14 +2372,57 @@ async function runReset(cwd2, opts) {
2324
2372
  }
2325
2373
 
2326
2374
  // src/core/mcp-server.ts
2327
- import { readdirSync as readdirSync2, readFileSync as readFileSync6, statSync } from "fs";
2328
- import { join as join14, resolve as resolve10 } from "path";
2375
+ import { existsSync as existsSync11, mkdirSync as mkdirSync9, readdirSync as readdirSync2, readFileSync as readFileSync7, statSync, writeFileSync as writeFileSync10 } from "fs";
2376
+ import { join as join15, resolve as resolve10 } from "path";
2329
2377
  import { Server } from "@modelcontextprotocol/sdk/server";
2330
2378
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
2331
2379
  import {
2332
2380
  CallToolRequestSchema,
2333
2381
  ListToolsRequestSchema
2334
2382
  } from "@modelcontextprotocol/sdk/types.js";
2383
+
2384
+ // src/core/permissions-check.ts
2385
+ import { existsSync as existsSync10, readFileSync as readFileSync6 } from "fs";
2386
+ import { join as join14 } from "path";
2387
+ var CANONICAL = {
2388
+ lead: [...MCP_CLAUDE_PERMISSIONS_LEAD],
2389
+ explorer: [...MCP_CLAUDE_PERMISSIONS_EXPLORER],
2390
+ consultant: [...MCP_CLAUDE_PERMISSIONS_CONSULTANT],
2391
+ builder: [...MCP_CLAUDE_PERMISSIONS_BUILDER],
2392
+ reviewer: [...MCP_CLAUDE_PERMISSIONS_REVIEWER]
2393
+ };
2394
+ function parseToolsFromFrontmatter(content) {
2395
+ const match = content.match(/^---\n([\s\S]*?)\n---/m);
2396
+ if (!match) return [];
2397
+ const fm = match[1];
2398
+ const toolsMatch = fm.match(/^tools:\n((?: - [^\n]+\n?)*)/m);
2399
+ if (!toolsMatch) return [];
2400
+ return toolsMatch[1].split("\n").map((l) => l.trim().replace(/^- /, "")).filter((l) => l.startsWith("mcp__"));
2401
+ }
2402
+ function checkPermissionsSync(cwd2) {
2403
+ const agents = {};
2404
+ let in_sync = true;
2405
+ for (const agent of ["lead", "explorer", "consultant", "builder", "reviewer"]) {
2406
+ const filePath = join14(cwd2, ".claude", "agents", `${agent}.md`);
2407
+ if (!existsSync10(filePath)) {
2408
+ const missing2 = CANONICAL[agent];
2409
+ agents[agent] = { ok: false, missing: missing2, extra: [] };
2410
+ in_sync = false;
2411
+ continue;
2412
+ }
2413
+ const content = readFileSync6(filePath, "utf-8");
2414
+ const installed = parseToolsFromFrontmatter(content);
2415
+ const canonical = CANONICAL[agent];
2416
+ const missing = canonical.filter((t) => !installed.includes(t));
2417
+ const extra = installed.filter((t) => !canonical.includes(t));
2418
+ const ok2 = missing.length === 0 && extra.length === 0;
2419
+ if (!ok2) in_sync = false;
2420
+ agents[agent] = { ok: ok2, missing, extra };
2421
+ }
2422
+ return { in_sync, agents };
2423
+ }
2424
+
2425
+ // src/core/mcp-server.ts
2335
2426
  var VERSION = "0.1.0";
2336
2427
  var TOOLS = [
2337
2428
  {
@@ -2555,6 +2646,21 @@ var TOOLS = [
2555
2646
  },
2556
2647
  required: ["id"]
2557
2648
  }
2649
+ },
2650
+ {
2651
+ name: "permissions.check",
2652
+ description: "Check whether the .claude/agents/*.md tool permission lists are in sync with the current canonical permission constants. Returns per-agent diff with missing and extra tools. Call this at session start to detect outdated agent files after an ahk upgrade.",
2653
+ inputSchema: { type: "object", properties: {}, required: [] }
2654
+ },
2655
+ {
2656
+ name: "deps.snapshot",
2657
+ description: "Snapshot current package.json dependencies to .harness/deps-lock.json",
2658
+ inputSchema: { type: "object", properties: {}, required: [] }
2659
+ },
2660
+ {
2661
+ name: "deps.check",
2662
+ description: "Compare current package.json against .harness/deps-lock.json and report changes",
2663
+ inputSchema: { type: "object", properties: {}, required: [] }
2558
2664
  }
2559
2665
  ];
2560
2666
  async function startMcpServer(config, cwd2) {
@@ -2569,7 +2675,7 @@ async function startMcpServer(config, cwd2) {
2569
2675
  const { name, arguments: args } = request.params;
2570
2676
  const a = args ?? {};
2571
2677
  try {
2572
- const result = await dispatch(name, a, db, docsPath);
2678
+ const result = await dispatch(name, a, db, docsPath, cwd2);
2573
2679
  return result;
2574
2680
  } catch (err) {
2575
2681
  return ok(`Error: ${err instanceof Error ? err.message : String(err)}`, true);
@@ -2578,7 +2684,7 @@ async function startMcpServer(config, cwd2) {
2578
2684
  const transport = new StdioServerTransport();
2579
2685
  await server.connect(transport);
2580
2686
  }
2581
- async function dispatch(name, args, db, docsPath) {
2687
+ async function dispatch(name, args, db, docsPath, cwd2) {
2582
2688
  switch (name) {
2583
2689
  case "actions.start": {
2584
2690
  const taskId = num(args, "taskId");
@@ -2697,6 +2803,62 @@ async function dispatch(name, args, db, docsPath) {
2697
2803
  const task2 = await db.unarchiveTask(id);
2698
2804
  return ok(JSON.stringify(task2));
2699
2805
  }
2806
+ case "permissions.check": {
2807
+ const result = checkPermissionsSync(cwd2);
2808
+ return ok(JSON.stringify(result, null, 2));
2809
+ }
2810
+ case "deps.snapshot": {
2811
+ const pkgPath2 = join15(cwd2, "package.json");
2812
+ if (!existsSync11(pkgPath2)) {
2813
+ return ok("package.json not found in project root", true);
2814
+ }
2815
+ const pkg2 = JSON.parse(readFileSync7(pkgPath2, "utf8"));
2816
+ const snapshot = {
2817
+ capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
2818
+ dependencies: pkg2.dependencies ?? {},
2819
+ devDependencies: pkg2.devDependencies ?? {}
2820
+ };
2821
+ const harnessDir = join15(cwd2, ".harness");
2822
+ mkdirSync9(harnessDir, { recursive: true });
2823
+ writeFileSync10(join15(harnessDir, "deps-lock.json"), JSON.stringify(snapshot, null, 2), "utf8");
2824
+ return ok(JSON.stringify({ message: "Snapshot saved to .harness/deps-lock.json", capturedAt: snapshot.capturedAt }));
2825
+ }
2826
+ case "deps.check": {
2827
+ const pkgPath2 = join15(cwd2, "package.json");
2828
+ const lockPath = join15(cwd2, ".harness", "deps-lock.json");
2829
+ if (!existsSync11(pkgPath2)) {
2830
+ return ok("package.json not found in project root", true);
2831
+ }
2832
+ if (!existsSync11(lockPath)) {
2833
+ return ok(JSON.stringify({ status: "no-snapshot", message: "No deps-lock.json found. Run deps.snapshot first to establish a baseline." }));
2834
+ }
2835
+ const pkg2 = JSON.parse(readFileSync7(pkgPath2, "utf8"));
2836
+ const lock = JSON.parse(readFileSync7(lockPath, "utf8"));
2837
+ const current = { ...pkg2.dependencies ?? {}, ...pkg2.devDependencies ?? {} };
2838
+ const previous = { ...lock.dependencies ?? {}, ...lock.devDependencies ?? {} };
2839
+ const added = [];
2840
+ const removed = [];
2841
+ const majorBumps = [];
2842
+ for (const [name2, version] of Object.entries(current)) {
2843
+ if (!(name2 in previous)) {
2844
+ added.push(`${name2}@${version}`);
2845
+ } else {
2846
+ const prevMajor = parseInt(previous[name2].replace(/^[\^~>=v]/, "").split(".")[0] ?? "0", 10);
2847
+ const curMajor = parseInt(version.replace(/^[\^~>=v]/, "").split(".")[0] ?? "0", 10);
2848
+ if (!isNaN(prevMajor) && !isNaN(curMajor) && curMajor > prevMajor) {
2849
+ majorBumps.push({ name: name2, from: previous[name2], to: version });
2850
+ }
2851
+ }
2852
+ }
2853
+ for (const depName of Object.keys(previous)) {
2854
+ if (!(depName in current)) {
2855
+ removed.push(depName);
2856
+ }
2857
+ }
2858
+ const significant = added.length > 0 || removed.length > 0 || majorBumps.length > 0;
2859
+ const advisory = significant ? "Significant dependency changes detected. Consider running `pnpx autoskills` (or `npx autoskills` if pnpm is unavailable) to refresh agent skills. Clearing stale skills before re-running is recommended." : "No significant dependency changes detected.";
2860
+ return ok(JSON.stringify({ significant, added, removed, majorBumps, advisory, snapshotDate: lock.capturedAt }));
2861
+ }
2700
2862
  default:
2701
2863
  return ok(`Unknown tool: ${name}`, true);
2702
2864
  }
@@ -2709,7 +2871,7 @@ function searchDocs(docsPath, query, maxResults = 10) {
2709
2871
  for (const file of files) {
2710
2872
  if (results.length >= maxResults) break;
2711
2873
  try {
2712
- const content = readFileSync6(file, "utf8");
2874
+ const content = readFileSync7(file, "utf8");
2713
2875
  const lines = content.split("\n");
2714
2876
  for (let i = 0; i < lines.length; i++) {
2715
2877
  const lower = lines[i].toLowerCase();
@@ -2730,7 +2892,7 @@ function collectMarkdownFiles(dir) {
2730
2892
  const files = [];
2731
2893
  try {
2732
2894
  for (const entry of readdirSync2(dir)) {
2733
- const full = join14(dir, entry);
2895
+ const full = join15(dir, entry);
2734
2896
  const stat = statSync(full);
2735
2897
  if (stat.isDirectory()) {
2736
2898
  files.push(...collectMarkdownFiles(full));
@@ -2764,6 +2926,18 @@ async function runServe(cwd2, opts) {
2764
2926
  }
2765
2927
  process.stderr.write(`[agent-harness-kit] MCP server starting (stdio)
2766
2928
  `);
2929
+ const syncResult = checkPermissionsSync(cwd2);
2930
+ if (!syncResult.in_sync) {
2931
+ const affected = Object.entries(syncResult.agents).filter(([, r]) => !r.ok).map(([name, r]) => {
2932
+ const parts = [];
2933
+ if (r.missing.length) parts.push(`missing: ${r.missing.map((t) => t.replace("mcp__agent-harness-kit__", "")).join(", ")}`);
2934
+ if (r.extra.length) parts.push(`extra: ${r.extra.map((t) => t.replace("mcp__agent-harness-kit__", "")).join(", ")}`);
2935
+ return `${name} (${parts.join("; ")})`;
2936
+ }).join("\n ");
2937
+ process.stderr.write(`[agent-harness-kit] Agent permissions out of sync. Run: ahk build --sync
2938
+ ${affected}
2939
+ `);
2940
+ }
2767
2941
  await startMcpServer(config, cwd2);
2768
2942
  }
2769
2943
 
@@ -2842,13 +3016,13 @@ async function runStatus(cwd2, opts) {
2842
3016
  }
2843
3017
 
2844
3018
  // src/commands/sync.ts
2845
- import { existsSync as existsSync10, readFileSync as readFileSync7 } from "fs";
2846
- import { join as join15, resolve as resolve11 } from "path";
3019
+ import { existsSync as existsSync12, readFileSync as readFileSync8 } from "fs";
3020
+ import { join as join16, resolve as resolve11 } from "path";
2847
3021
  import pc10 from "picocolors";
2848
3022
  async function runSync(cwd2, opts) {
2849
3023
  const config = await loadConfig(cwd2);
2850
3024
  const direction = opts.direction ?? "both";
2851
- const featureListPath = resolve11(join15(cwd2, config.storage.dir, "feature_list.json"));
3025
+ const featureListPath = resolve11(join16(cwd2, config.storage.dir, "feature_list.json"));
2852
3026
  const db = await openDB(config, cwd2);
2853
3027
  try {
2854
3028
  if (direction === "in" || direction === "both") {
@@ -2862,13 +3036,13 @@ async function runSync(cwd2, opts) {
2862
3036
  }
2863
3037
  }
2864
3038
  async function syncIn(featureListPath, db, dryRun) {
2865
- if (!existsSync10(featureListPath)) {
3039
+ if (!existsSync12(featureListPath)) {
2866
3040
  console.log(pc10.dim(`feature_list.json not found at ${featureListPath} \u2014 skipping in-sync`));
2867
3041
  return;
2868
3042
  }
2869
3043
  let seeds;
2870
3044
  try {
2871
- seeds = JSON.parse(readFileSync7(featureListPath, "utf8"));
3045
+ seeds = JSON.parse(readFileSync8(featureListPath, "utf8"));
2872
3046
  } catch (err) {
2873
3047
  console.error(pc10.red(`Failed to parse feature_list.json: ${err}`));
2874
3048
  process.exit(1);
@@ -2953,14 +3127,14 @@ async function runTaskAdd(cwd2) {
2953
3127
 
2954
3128
  // src/commands/task/done.ts
2955
3129
  import { spawnSync as spawnSync2 } from "child_process";
2956
- import { existsSync as existsSync11 } from "fs";
3130
+ import { existsSync as existsSync13 } from "fs";
2957
3131
  import { resolve as resolve12 } from "path";
2958
3132
  import pc12 from "picocolors";
2959
3133
  async function runTaskDone(cwd2, idOrSlug) {
2960
3134
  const config = await loadConfig(cwd2);
2961
3135
  if (config.health.required) {
2962
3136
  const scriptPath = resolve12(cwd2, config.health.scriptPath);
2963
- if (existsSync11(scriptPath)) {
3137
+ if (existsSync13(scriptPath)) {
2964
3138
  const result = spawnSync2("bash", [scriptPath], { cwd: cwd2, stdio: "pipe", encoding: "utf8" });
2965
3139
  if (result.status !== 0) {
2966
3140
  console.error(pc12.red("\u2717 Health check failed \u2014 cannot mark task as done."));
@@ -3133,10 +3307,10 @@ async function runTaskList(cwd2, opts) {
3133
3307
 
3134
3308
  // src/core/package-data.ts
3135
3309
  import { createRequire } from "module";
3136
- import { dirname as dirname5, join as join16 } from "path";
3310
+ import { dirname as dirname5, join as join17 } from "path";
3137
3311
  import { fileURLToPath as fileURLToPath3 } from "url";
3138
3312
  var require2 = createRequire(import.meta.url);
3139
- var pkgPath = join16(dirname5(fileURLToPath3(import.meta.url)), "..", "package.json");
3313
+ var pkgPath = join17(dirname5(fileURLToPath3(import.meta.url)), "..", "package.json");
3140
3314
  var pkg = require2(pkgPath);
3141
3315
 
3142
3316
  // src/core/update-check.ts
@@ -3180,7 +3354,7 @@ program.name("ahk").description("agent-harness-kit \u2014 CLI scaffolding for mu
3180
3354
  program.command("init").description("Scaffold a harness interactively in the current directory").option("--name <name>", "Project name (skip prompt)").option("--provider <provider>", "AI provider: claude-code | opencode (skip prompt)").option("--docs <path>", "Docs folder path (skip prompt)").option("--tasks <adapter>", "Task adapter: local | jira | linear (skip prompt)").action(async (opts) => {
3181
3355
  await runInit(cwd, opts);
3182
3356
  });
3183
- program.command("build").description("Regenerate AGENTS.md and provider files from agent-harness-kit.config.ts").option("--watch", "Rebuild on config changes").action(async (opts) => {
3357
+ program.command("build").description("Regenerate AGENTS.md and provider files from agent-harness-kit.config.ts").option("--watch", "Rebuild on config changes").option("--sync", "Sync tools: frontmatter in existing .claude/agents/*.md to match current permission constants").action(async (opts) => {
3184
3358
  await runBuild(cwd, opts);
3185
3359
  });
3186
3360
  program.command("health").description("Run health.sh and report result").action(async () => {