@cardor/agent-harness-kit 1.7.5 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -83,7 +83,8 @@ var MCP_CLAUDE_PERMISSIONS_LEAD = [
83
83
  "mcp__agent-harness-kit__tasks_archive",
84
84
  "mcp__agent-harness-kit__tasks_unarchive",
85
85
  "mcp__agent-harness-kit__tasks_acceptance_get",
86
- "mcp__agent-harness-kit__docs_search"
86
+ "mcp__agent-harness-kit__docs_search",
87
+ "mcp__agent-harness-kit__ahk_doctor"
87
88
  ];
88
89
  var MCP_CLAUDE_PERMISSIONS_EXPLORER = [
89
90
  "mcp__agent-harness-kit__actions_start",
@@ -95,7 +96,8 @@ var MCP_CLAUDE_PERMISSIONS_EXPLORER = [
95
96
  "mcp__agent-harness-kit__tasks_get",
96
97
  "mcp__agent-harness-kit__tasks_claim",
97
98
  "mcp__agent-harness-kit__tasks_acceptance_get",
98
- "mcp__agent-harness-kit__docs_search"
99
+ "mcp__agent-harness-kit__docs_search",
100
+ "mcp__agent-harness-kit__ahk_doctor"
99
101
  ];
100
102
  var MCP_CLAUDE_PERMISSIONS_BUILDER = [
101
103
  "mcp__agent-harness-kit__actions_start",
@@ -112,7 +114,8 @@ var MCP_CLAUDE_PERMISSIONS_BUILDER = [
112
114
  "mcp__agent-harness-kit__tasks_archive",
113
115
  "mcp__agent-harness-kit__tasks_unarchive",
114
116
  "mcp__agent-harness-kit__tasks_acceptance_get",
115
- "mcp__agent-harness-kit__docs_search"
117
+ "mcp__agent-harness-kit__docs_search",
118
+ "mcp__agent-harness-kit__ahk_doctor"
116
119
  ];
117
120
  var MCP_CLAUDE_PERMISSIONS_REVIEWER = [
118
121
  "mcp__agent-harness-kit__actions_start",
@@ -130,7 +133,8 @@ var MCP_CLAUDE_PERMISSIONS_REVIEWER = [
130
133
  "mcp__agent-harness-kit__tasks_unarchive",
131
134
  "mcp__agent-harness-kit__tasks_acceptance_update",
132
135
  "mcp__agent-harness-kit__tasks_acceptance_get",
133
- "mcp__agent-harness-kit__docs_search"
136
+ "mcp__agent-harness-kit__docs_search",
137
+ "mcp__agent-harness-kit__ahk_doctor"
134
138
  ];
135
139
  var MCP_CLAUDE_PERMISSIONS_CONSULTANT = [
136
140
  "mcp__agent-harness-kit__actions_start",
@@ -144,7 +148,8 @@ var MCP_CLAUDE_PERMISSIONS_CONSULTANT = [
144
148
  "mcp__agent-harness-kit__tasks_acceptance_get",
145
149
  "mcp__agent-harness-kit__deps_snapshot",
146
150
  "mcp__agent-harness-kit__deps_check",
147
- "mcp__agent-harness-kit__docs_search"
151
+ "mcp__agent-harness-kit__docs_search",
152
+ "mcp__agent-harness-kit__ahk_doctor"
148
153
  ];
149
154
  var MCP_CLAUDE_PERMISSIONS = [
150
155
  .../* @__PURE__ */ new Set([
@@ -249,7 +254,8 @@ function mergeCodexConfigToml(filePath, port) {
249
254
 
250
255
  // src/core/materializer/scaffold-utils.ts
251
256
  import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
252
- import { join as join3, resolve as resolve2 } from "path";
257
+ import { dirname as dirname3, join as join3, resolve as resolve2 } from "path";
258
+ import { fileURLToPath as fileURLToPath2 } from "url";
253
259
 
254
260
  // src/core/materializer/templates.ts
255
261
  import { readFileSync as readFileSync2 } from "fs";
@@ -655,6 +661,7 @@ var GITIGNORE_ENTRIES = `
655
661
  `;
656
662
 
657
663
  // src/core/materializer/scaffold-utils.ts
664
+ var __dirname2 = dirname3(fileURLToPath2(import.meta.url));
658
665
  function writeAgentFile(cwd2, relPath, content) {
659
666
  const abs = join3(cwd2, relPath);
660
667
  if (existsSync2(abs)) return;
@@ -672,6 +679,16 @@ function appendGitignore(cwd2) {
672
679
  function slugify(title) {
673
680
  return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
674
681
  }
682
+ function writeSkills(cwd2, skillsDir) {
683
+ const skillNames = ["ahk-ask", "ahk-consultant", "ahk-triage"];
684
+ for (const skillName of skillNames) {
685
+ const src = join3(__dirname2, "skills", skillName, "SKILL.md");
686
+ const destDir = join3(cwd2, skillsDir, skillName);
687
+ const dest = join3(destDir, "SKILL.md");
688
+ mkdirSync3(destDir, { recursive: true });
689
+ writeFileSync3(dest, readFileSync3(src, "utf8"), "utf8");
690
+ }
691
+ }
675
692
 
676
693
  // src/core/materializer/claude-code.ts
677
694
  var ClaudeCodeMaterializer = class {
@@ -709,6 +726,7 @@ No tasks in progress.
709
726
  mergeClaudeSettingsJson(join4(cwd2, ".claude/settings.json"));
710
727
  mergeClaudeSettingsLocalJson(join4(cwd2, ".claude/settings.local.json"));
711
728
  appendGitignore(cwd2);
729
+ writeSkills(cwd2, ".claude/skills");
712
730
  }
713
731
  async build(config, cwd2) {
714
732
  const write2 = (relPath, content) => {
@@ -721,14 +739,15 @@ No tasks in progress.
721
739
  const projectName = config.project.name;
722
740
  const allowedPaths = (config.agents.explorer.allowedPaths ?? []).join(", ");
723
741
  const writablePaths = (config.agents.builder.writablePaths ?? []).join(", ");
724
- writeAgentFile(cwd2, ".claude/agents/lead.md", translateFrontmatterForClaudeCode(agentLead({ projectName }), "lead"));
725
- writeAgentFile(cwd2, ".claude/agents/explorer.md", translateFrontmatterForClaudeCode(agentExplorer({ projectName, allowedPaths }), "explorer"));
726
- writeAgentFile(cwd2, ".claude/agents/consultant.md", translateFrontmatterForClaudeCode(agentConsultant({ projectName }), "consultant"));
727
- writeAgentFile(cwd2, ".claude/agents/builder.md", translateFrontmatterForClaudeCode(agentBuilder({ projectName, writablePaths }), "builder"));
728
- writeAgentFile(cwd2, ".claude/agents/reviewer.md", translateFrontmatterForClaudeCode(agentReviewer({ projectName }), "reviewer"));
742
+ write2(".claude/agents/lead.md", translateFrontmatterForClaudeCode(agentLead({ projectName }), "lead"));
743
+ write2(".claude/agents/explorer.md", translateFrontmatterForClaudeCode(agentExplorer({ projectName, allowedPaths }), "explorer"));
744
+ write2(".claude/agents/consultant.md", translateFrontmatterForClaudeCode(agentConsultant({ projectName }), "consultant"));
745
+ write2(".claude/agents/builder.md", translateFrontmatterForClaudeCode(agentBuilder({ projectName, writablePaths }), "builder"));
746
+ write2(".claude/agents/reviewer.md", translateFrontmatterForClaudeCode(agentReviewer({ projectName }), "reviewer"));
729
747
  mergeClaudeMcpJson(join4(cwd2, ".mcp.json"), config.tools.mcp.port);
730
748
  mergeClaudeSettingsJson(join4(cwd2, ".claude/settings.json"));
731
749
  mergeClaudeSettingsLocalJson(join4(cwd2, ".claude/settings.local.json"));
750
+ writeSkills(cwd2, ".claude/skills");
732
751
  }
733
752
  async migrate(config, _to, _cwd) {
734
753
  void config;
@@ -807,6 +826,7 @@ No tasks in progress.
807
826
  writeAgentFile(cwd2, ".codex/agents/default.toml", agentLeadAsDefaultToml({ projectName }));
808
827
  mergeCodexConfigToml(join5(cwd2, ".codex/config.toml"), config.tools.mcp.port);
809
828
  appendGitignore(cwd2);
829
+ writeSkills(cwd2, ".agents/skills");
810
830
  }
811
831
  async build(config, cwd2) {
812
832
  const write2 = (relPath, content) => {
@@ -825,6 +845,7 @@ No tasks in progress.
825
845
  writeAgentFile(cwd2, ".codex/agents/reviewer.toml", agentReviewerToml({ projectName }));
826
846
  writeAgentFile(cwd2, ".codex/agents/default.toml", agentLeadAsDefaultToml({ projectName }));
827
847
  mergeCodexConfigToml(join5(cwd2, ".codex/config.toml"), config.tools.mcp.port);
848
+ writeSkills(cwd2, ".agents/skills");
828
849
  }
829
850
  async migrate(config, _to, _cwd) {
830
851
  void config;
@@ -873,6 +894,7 @@ No tasks in progress.
873
894
  writeAgentFile(cwd2, ".opencode/agents/reviewer.md", translateFrontmatterForOpenCode(agentReviewer({ projectName })));
874
895
  mergeOpencodeJson(join6(cwd2, "opencode.json"), config.tools.mcp.port);
875
896
  appendGitignore(cwd2);
897
+ writeSkills(cwd2, ".opencode/skills");
876
898
  }
877
899
  async build(config, cwd2) {
878
900
  const write2 = (relPath, content) => {
@@ -890,6 +912,7 @@ No tasks in progress.
890
912
  writeAgentFile(cwd2, ".opencode/agents/builder.md", translateFrontmatterForOpenCode(agentBuilder({ projectName, writablePaths })));
891
913
  writeAgentFile(cwd2, ".opencode/agents/reviewer.md", translateFrontmatterForOpenCode(agentReviewer({ projectName })));
892
914
  mergeOpencodeJson(join6(cwd2, "opencode.json"), config.tools.mcp.port);
915
+ writeSkills(cwd2, ".opencode/skills");
893
916
  }
894
917
  async migrate(config, _to, _cwd) {
895
918
  void config;
@@ -954,8 +977,8 @@ async function buildOnce(cwd2) {
954
977
  }
955
978
 
956
979
  // src/commands/dashboard.ts
957
- import { dirname as dirname4, join as join9, resolve as resolve7 } from "path";
958
- import { fileURLToPath as fileURLToPath2 } from "url";
980
+ import { dirname as dirname5, join as join9, resolve as resolve7 } from "path";
981
+ import { fileURLToPath as fileURLToPath3 } from "url";
959
982
  import pc2 from "picocolors";
960
983
 
961
984
  // src/core/dashboard-server.ts
@@ -1165,7 +1188,7 @@ async function startDashboardServer(db, dbPath, staticPath, port) {
1165
1188
  // src/core/db.ts
1166
1189
  import { randomUUID } from "crypto";
1167
1190
  import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync7 } from "fs";
1168
- import { dirname as dirname3, join as join8, resolve as resolve6 } from "path";
1191
+ import { dirname as dirname4, join as join8, resolve as resolve6 } from "path";
1169
1192
 
1170
1193
  // src/core/repositories/ActionRepository.ts
1171
1194
  var ActionRepository = class {
@@ -1648,7 +1671,7 @@ var HarnessDB = class {
1648
1671
  async regenerateCurrentMd() {
1649
1672
  if (!this.config.storage.markdownFallback.enabled) return;
1650
1673
  const mdPath = resolve6(this.config.storage.markdownFallback.path);
1651
- mkdirSync7(dirname3(mdPath), { recursive: true });
1674
+ mkdirSync7(dirname4(mdPath), { recursive: true });
1652
1675
  const inProgress = await this.tasks.getAll("in_progress");
1653
1676
  const now = (/* @__PURE__ */ new Date()).toISOString();
1654
1677
  let md = `<!-- AUTO-GENERATED by agent-harness-kit \u2014 DO NOT EDIT MANUALLY -->
@@ -1761,7 +1784,7 @@ var HarnessDB = class {
1761
1784
  }))
1762
1785
  );
1763
1786
  const path = join8(resolve6(cwd2), this.config.storage.dir, "feature_list.json");
1764
- mkdirSync7(dirname3(path), { recursive: true });
1787
+ mkdirSync7(dirname4(path), { recursive: true });
1765
1788
  writeFileSync7(path, JSON.stringify(list, null, 2) + "\n", "utf8");
1766
1789
  }
1767
1790
  };
@@ -1786,12 +1809,12 @@ async function openDB(config, cwd2) {
1786
1809
  }
1787
1810
 
1788
1811
  // src/commands/dashboard.ts
1789
- var __dirname2 = dirname4(fileURLToPath2(import.meta.url));
1812
+ var __dirname3 = dirname5(fileURLToPath3(import.meta.url));
1790
1813
  async function runDashboard(cwd2, opts) {
1791
1814
  const config = await loadConfig(cwd2);
1792
1815
  const db = await openDB(config, cwd2);
1793
1816
  const dbPath = config.database.type === "sqlite" ? resolve7(cwd2, config.database.path) : null;
1794
- const staticPath = join9(__dirname2, "dashboard-dist");
1817
+ const staticPath = join9(__dirname3, "dashboard-dist");
1795
1818
  const { url } = await startDashboardServer(db, dbPath, staticPath, opts.port);
1796
1819
  console.log(pc2.green(`\u2713`) + ` Dashboard running at ${pc2.bold(pc2.cyan(url))}`);
1797
1820
  console.log(pc2.dim(` WebSocket live updates enabled`));
@@ -1807,12 +1830,255 @@ async function runDashboard(cwd2, opts) {
1807
1830
  });
1808
1831
  }
1809
1832
 
1833
+ // src/commands/doctor.ts
1834
+ import pc3 from "picocolors";
1835
+
1836
+ // src/core/doctor.ts
1837
+ import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
1838
+ import { dirname as dirname7, join as join11 } from "path";
1839
+ import { fileURLToPath as fileURLToPath5 } from "url";
1840
+
1841
+ // src/core/package-data.ts
1842
+ import { createRequire } from "module";
1843
+ import { dirname as dirname6, join as join10 } from "path";
1844
+ import { fileURLToPath as fileURLToPath4 } from "url";
1845
+ var require2 = createRequire(import.meta.url);
1846
+ var pkgPath = join10(dirname6(fileURLToPath4(import.meta.url)), "..", "package.json");
1847
+ var pkg = require2(pkgPath);
1848
+
1849
+ // src/core/doctor.ts
1850
+ var REGISTRY_URL = `https://registry.npmjs.org/${pkg.name}/latest`;
1851
+ var TIMEOUT_MS = 2e3;
1852
+ var AGENT_NAMES = ["lead", "explorer", "consultant", "builder", "reviewer"];
1853
+ var SKILL_NAMES = ["ahk-ask", "ahk-consultant", "ahk-triage"];
1854
+ var __dirname4 = dirname7(fileURLToPath5(import.meta.url));
1855
+ async function checkLibVersion() {
1856
+ const current = pkg.version;
1857
+ try {
1858
+ const controller = new AbortController();
1859
+ const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
1860
+ const res = await fetch(REGISTRY_URL, { signal: controller.signal });
1861
+ clearTimeout(timer);
1862
+ const data = await res.json();
1863
+ const latest = data.version;
1864
+ const outdated = isNewer(latest, current);
1865
+ return { current, latest, outdated };
1866
+ } catch {
1867
+ return { current, latest: null, outdated: false };
1868
+ }
1869
+ }
1870
+ function isNewer(latest, current) {
1871
+ const toNum = (v4) => v4.split(".").map(Number);
1872
+ const [lMaj, lMin, lPat] = toNum(latest);
1873
+ const [cMaj, cMin, cPat] = toNum(current);
1874
+ if (lMaj !== cMaj) return lMaj > cMaj;
1875
+ if (lMin !== cMin) return lMin > cMin;
1876
+ return lPat > cPat;
1877
+ }
1878
+ function getProviderAgentInfo(provider) {
1879
+ switch (provider) {
1880
+ case "claude-code":
1881
+ return { agentsDir: ".claude/agents", ext: ".md" };
1882
+ case "opencode":
1883
+ return { agentsDir: ".opencode/agents", ext: ".md" };
1884
+ case "codex-cli":
1885
+ return { agentsDir: ".codex/agents", ext: ".toml" };
1886
+ default:
1887
+ return { agentsDir: ".claude/agents", ext: ".md" };
1888
+ }
1889
+ }
1890
+ function generateExpectedAgentContent(agentName, provider, vars) {
1891
+ const { projectName, allowedPaths, writablePaths } = vars;
1892
+ if (provider === "claude-code") {
1893
+ const templateFns = {
1894
+ lead: () => agentLead({ projectName }),
1895
+ explorer: () => agentExplorer({ projectName, allowedPaths }),
1896
+ consultant: () => agentConsultant({ projectName }),
1897
+ builder: () => agentBuilder({ projectName, writablePaths }),
1898
+ reviewer: () => agentReviewer({ projectName })
1899
+ };
1900
+ return translateFrontmatterForClaudeCode(templateFns[agentName](), agentName);
1901
+ }
1902
+ if (provider === "opencode") {
1903
+ const templateFns = {
1904
+ lead: () => agentLead({ projectName }),
1905
+ explorer: () => agentExplorer({ projectName, allowedPaths }),
1906
+ consultant: () => agentConsultant({ projectName }),
1907
+ builder: () => agentBuilder({ projectName, writablePaths }),
1908
+ reviewer: () => agentReviewer({ projectName })
1909
+ };
1910
+ return translateFrontmatterForOpenCode(templateFns[agentName]());
1911
+ }
1912
+ const tomlFns = {
1913
+ lead: () => agentLeadToml({ projectName }),
1914
+ explorer: () => agentExplorerToml({ projectName, allowedPaths }),
1915
+ consultant: () => agentConsultantToml({ projectName }),
1916
+ builder: () => agentBuilderToml({ projectName, writablePaths }),
1917
+ reviewer: () => agentReviewerToml({ projectName })
1918
+ };
1919
+ return tomlFns[agentName]();
1920
+ }
1921
+ function checkAgentFiles(cwd2, provider, projectName, allowedPaths, writablePaths) {
1922
+ const { agentsDir, ext } = getProviderAgentInfo(provider);
1923
+ return AGENT_NAMES.map((name) => {
1924
+ const filePath = join11(cwd2, agentsDir, `${name}${ext}`);
1925
+ if (!existsSync7(filePath)) {
1926
+ return { name, status: "missing" };
1927
+ }
1928
+ try {
1929
+ const live = readFileSync6(filePath, "utf8");
1930
+ const expected = generateExpectedAgentContent(name, provider, {
1931
+ projectName,
1932
+ allowedPaths,
1933
+ writablePaths
1934
+ });
1935
+ return { name, status: live === expected ? "ok" : "outdated" };
1936
+ } catch {
1937
+ return { name, status: "outdated" };
1938
+ }
1939
+ });
1940
+ }
1941
+ function getProviderSkillsDir(provider) {
1942
+ switch (provider) {
1943
+ case "claude-code":
1944
+ return ".claude/skills";
1945
+ case "opencode":
1946
+ return ".opencode/skills";
1947
+ case "codex-cli":
1948
+ return ".agents/skills";
1949
+ default:
1950
+ return ".claude/skills";
1951
+ }
1952
+ }
1953
+ function checkSkills(cwd2, provider) {
1954
+ const skillsDir = getProviderSkillsDir(provider);
1955
+ const skillSourceBase = join11(__dirname4, "skills");
1956
+ return SKILL_NAMES.map((name) => {
1957
+ const livePath = join11(cwd2, skillsDir, name, "SKILL.md");
1958
+ const sourcePath = join11(skillSourceBase, name, "SKILL.md");
1959
+ if (!existsSync7(livePath)) {
1960
+ return { name, status: "missing" };
1961
+ }
1962
+ try {
1963
+ const live = readFileSync6(livePath, "utf8");
1964
+ const source = readFileSync6(sourcePath, "utf8");
1965
+ return { name, status: live === source ? "ok" : "outdated" };
1966
+ } catch {
1967
+ return { name, status: "outdated" };
1968
+ }
1969
+ });
1970
+ }
1971
+ async function getDoctorStatus(cwd2) {
1972
+ const lib = await checkLibVersion();
1973
+ let config;
1974
+ try {
1975
+ config = await loadConfig(cwd2);
1976
+ } catch {
1977
+ return {
1978
+ lib,
1979
+ agents: [],
1980
+ skills: []
1981
+ };
1982
+ }
1983
+ const provider = config.provider;
1984
+ const projectName = config.project.name;
1985
+ const allowedPaths = (config.agents.explorer.allowedPaths ?? []).join(", ");
1986
+ const writablePaths = (config.agents.builder.writablePaths ?? []).join(", ");
1987
+ const agents = checkAgentFiles(cwd2, provider, projectName, allowedPaths, writablePaths);
1988
+ const skills = checkSkills(cwd2, provider);
1989
+ return { lib, agents, skills };
1990
+ }
1991
+
1992
+ // src/commands/doctor.ts
1993
+ function ok(label, detail) {
1994
+ console.log(` ${pc3.cyan(label.padEnd(16))}${pc3.green("[\u2713]")} ${detail}`);
1995
+ }
1996
+ function warn(label, detail, hint) {
1997
+ console.log(` ${pc3.cyan(label.padEnd(16))}${pc3.yellow("[!]")} ${detail}`);
1998
+ if (hint) {
1999
+ console.log(` ${"".padEnd(16)} ${pc3.dim(hint)}`);
2000
+ }
2001
+ }
2002
+ function neutral(label, detail) {
2003
+ console.log(` ${pc3.cyan(label.padEnd(16))}${pc3.dim("[~]")} ${detail}`);
2004
+ }
2005
+ function printLibSection(lib) {
2006
+ if (lib.latest === null) {
2007
+ neutral("lib version", `${lib.current} (unknown \u2014 could not reach npm registry)`);
2008
+ } else if (lib.outdated) {
2009
+ warn(
2010
+ "lib version",
2011
+ `${lib.current} \u2192 ${lib.latest} available`,
2012
+ `run: npm i @cardor/agent-harness-kit@latest && ahk build`
2013
+ );
2014
+ } else {
2015
+ ok("lib version", `${lib.current} (up to date)`);
2016
+ }
2017
+ }
2018
+ function printAgentsSection(agents) {
2019
+ if (agents.length === 0) {
2020
+ warn("agent files", "no config found \u2014 run: ahk init");
2021
+ return;
2022
+ }
2023
+ const missing = agents.filter((a) => a.status === "missing");
2024
+ const outdated = agents.filter((a) => a.status === "outdated");
2025
+ if (missing.length === 0 && outdated.length === 0) {
2026
+ ok("agent files", "all up to date");
2027
+ return;
2028
+ }
2029
+ for (const agent of missing) {
2030
+ warn("agent files", `${agent.name} missing`, "run: ahk build");
2031
+ }
2032
+ for (const agent of outdated) {
2033
+ warn("agent files", `${agent.name} outdated`, "run: ahk build");
2034
+ }
2035
+ }
2036
+ function printSkillsSection(skills) {
2037
+ if (skills.length === 0) {
2038
+ warn("harness skills", "no config found \u2014 run: ahk init");
2039
+ return;
2040
+ }
2041
+ const issues = skills.filter((s) => s.status !== "ok");
2042
+ if (issues.length === 0) {
2043
+ ok("harness skills", "all present and up to date");
2044
+ return;
2045
+ }
2046
+ for (const skill of issues) {
2047
+ const detail = skill.status === "missing" ? `${skill.name} missing` : `${skill.name} outdated`;
2048
+ warn("harness skills", detail, "run: ahk build");
2049
+ }
2050
+ }
2051
+ async function runDoctor(cwd2) {
2052
+ let configFound = true;
2053
+ try {
2054
+ await loadConfig(cwd2);
2055
+ } catch {
2056
+ configFound = false;
2057
+ }
2058
+ console.log("");
2059
+ console.log(pc3.bold(`\u25CF ahk doctor ` + "\u2500".repeat(44)));
2060
+ console.log("");
2061
+ const status = await getDoctorStatus(cwd2);
2062
+ printLibSection(status.lib);
2063
+ if (!configFound) {
2064
+ console.log("");
2065
+ warn("config", "no agent-harness-kit.config found", "run: ahk init");
2066
+ console.log("");
2067
+ return;
2068
+ }
2069
+ console.log("");
2070
+ printAgentsSection(status.agents);
2071
+ console.log("");
2072
+ printSkillsSection(status.skills);
2073
+ console.log("");
2074
+ }
2075
+
1810
2076
  // src/commands/export.ts
1811
2077
  import { writeFileSync as writeFileSync8 } from "fs";
1812
- import pc3 from "picocolors";
2078
+ import pc4 from "picocolors";
1813
2079
  async function runExport(cwd2, opts) {
1814
2080
  if (!opts.sql && !opts.json) {
1815
- console.error(pc3.red("Specify --sql or --json"));
2081
+ console.error(pc4.red("Specify --sql or --json"));
1816
2082
  process.exit(1);
1817
2083
  }
1818
2084
  const config = await loadConfig(cwd2);
@@ -1823,13 +2089,13 @@ async function runExport(cwd2, opts) {
1823
2089
  const out = JSON.stringify(data, null, 2) + "\n";
1824
2090
  if (opts.output) {
1825
2091
  writeFileSync8(opts.output, out, "utf8");
1826
- console.log(pc3.green(`\u2713 Exported JSON \u2192 ${opts.output}`));
2092
+ console.log(pc4.green(`\u2713 Exported JSON \u2192 ${opts.output}`));
1827
2093
  } else {
1828
2094
  process.stdout.write(out);
1829
2095
  }
1830
2096
  }
1831
2097
  if (opts.sql) {
1832
- console.error(pc3.dim("SQL dump requires direct SQLite access \u2014 use: sqlite3 .harness/harness.db .dump"));
2098
+ console.error(pc4.dim("SQL dump requires direct SQLite access \u2014 use: sqlite3 .harness/harness.db .dump"));
1833
2099
  process.exit(1);
1834
2100
  }
1835
2101
  } finally {
@@ -1839,27 +2105,27 @@ async function runExport(cwd2, opts) {
1839
2105
 
1840
2106
  // src/commands/health.ts
1841
2107
  import { spawnSync } from "child_process";
1842
- import { existsSync as existsSync7 } from "fs";
1843
- import { join as join10, resolve as resolve8 } from "path";
1844
- import pc4 from "picocolors";
1845
- function checkLine(label, ok2, message, indent = 0) {
1846
- const prefix = label ? pc4.cyan(`[${label}] `) : " ".repeat(indent);
1847
- const icon = ok2 ? pc4.green("\u2713") : pc4.red("\u2717");
1848
- console.log(prefix + icon + " " + (ok2 ? pc4.green(message) : pc4.red(message)));
2108
+ import { existsSync as existsSync8 } from "fs";
2109
+ import { join as join12, resolve as resolve8 } from "path";
2110
+ import pc5 from "picocolors";
2111
+ function checkLine(label, ok3, message, indent = 0) {
2112
+ const prefix = label ? pc5.cyan(`[${label}] `) : " ".repeat(indent);
2113
+ const icon = ok3 ? pc5.green("\u2713") : pc5.red("\u2717");
2114
+ console.log(prefix + icon + " " + (ok3 ? pc5.green(message) : pc5.red(message)));
1849
2115
  }
1850
2116
  async function runHealth(cwd2) {
1851
2117
  let config;
1852
2118
  try {
1853
2119
  config = await loadConfig(cwd2);
1854
2120
  } catch {
1855
- console.error(pc4.red("\u2717 No config found. Run: ahk init"));
2121
+ console.error(pc5.red("\u2717 No config found. Run: ahk init"));
1856
2122
  process.exit(1);
1857
2123
  }
1858
2124
  let allOk = true;
1859
2125
  let dbOk;
1860
2126
  if (config.database.type === "sqlite") {
1861
2127
  const dbPath = resolve8(cwd2, config.database.path);
1862
- dbOk = existsSync7(dbPath);
2128
+ dbOk = existsSync8(dbPath);
1863
2129
  checkLine("checking DB", dbOk, `${config.database.path} reachable`);
1864
2130
  } else {
1865
2131
  dbOk = true;
@@ -1872,31 +2138,31 @@ async function runHealth(cwd2) {
1872
2138
  const agentsLabelWidth = "[checking agents] ".length;
1873
2139
  for (let i = 0; i < agentNames.length; i++) {
1874
2140
  const name = agentNames[i];
1875
- const agentPath = join10(cwd2, agentsDir, `${name}${providerFiles.agentExtension}`);
1876
- const ok2 = existsSync7(agentPath);
2141
+ const agentPath = join12(cwd2, agentsDir, `${name}${providerFiles.agentExtension}`);
2142
+ const ok3 = existsSync8(agentPath);
1877
2143
  checkLine(
1878
2144
  i === 0 ? "checking agents" : null,
1879
- ok2,
2145
+ ok3,
1880
2146
  `${name}${providerFiles.agentExtension} present`,
1881
2147
  agentsLabelWidth
1882
2148
  );
1883
- if (!ok2) allOk = false;
2149
+ if (!ok3) allOk = false;
1884
2150
  }
1885
2151
  if (config.tools.mcp.enabled) {
1886
2152
  const mcpFile = providerFiles.mcpFile;
1887
2153
  const mcpPath = resolve8(cwd2, mcpFile);
1888
- const mcpOk = existsSync7(mcpPath);
2154
+ const mcpOk = existsSync8(mcpPath);
1889
2155
  checkLine("checking MCP", mcpOk, `${mcpFile} valid`);
1890
2156
  if (!mcpOk) allOk = false;
1891
2157
  }
1892
2158
  if (!allOk) {
1893
2159
  console.log("");
1894
- console.error(pc4.red("\u2717 Harness checks failed \u2014 fix the above before running health.sh"));
2160
+ console.error(pc5.red("\u2717 Harness checks failed \u2014 fix the above before running health.sh"));
1895
2161
  process.exit(1);
1896
2162
  }
1897
2163
  const scriptPath = resolve8(cwd2, config.health.scriptPath);
1898
- if (!existsSync7(scriptPath)) {
1899
- console.error(pc4.red(`\u2717 health.sh not found: ${scriptPath}`));
2164
+ if (!existsSync8(scriptPath)) {
2165
+ console.error(pc5.red(`\u2717 health.sh not found: ${scriptPath}`));
1900
2166
  console.error(" Run ahk init first.");
1901
2167
  process.exit(1);
1902
2168
  }
@@ -1906,14 +2172,14 @@ async function runHealth(cwd2) {
1906
2172
  encoding: "utf8"
1907
2173
  });
1908
2174
  if (result.error) {
1909
- console.error(pc4.red(`\u2717 Failed to run health.sh: ${result.error.message}`));
2175
+ console.error(pc5.red(`\u2717 Failed to run health.sh: ${result.error.message}`));
1910
2176
  process.exit(1);
1911
2177
  }
1912
2178
  if (result.status === 0) {
1913
- console.log(pc4.green("\u2713 Health check passed"));
2179
+ console.log(pc5.green("\u2713 Health check passed"));
1914
2180
  process.exit(0);
1915
2181
  } else {
1916
- console.error(pc4.red(`\u2717 Health check failed (exit ${result.status ?? "unknown"})`));
2182
+ console.error(pc5.red(`\u2717 Health check failed (exit ${result.status ?? "unknown"})`));
1917
2183
  process.exit(result.status ?? 1);
1918
2184
  }
1919
2185
  }
@@ -1932,9 +2198,9 @@ function getProviderHealthFiles(provider) {
1932
2198
 
1933
2199
  // src/commands/init.ts
1934
2200
  import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync9 } from "fs";
1935
- import { join as join12 } from "path";
2201
+ import { join as join14 } from "path";
1936
2202
  import * as p3 from "@clack/prompts";
1937
- import pc6 from "picocolors";
2203
+ import pc7 from "picocolors";
1938
2204
 
1939
2205
  // src/schema/init.ts
1940
2206
  import * as v from "valibot";
@@ -1987,14 +2253,14 @@ var cliFormWithRetry = async (formFn, schema) => {
1987
2253
  };
1988
2254
 
1989
2255
  // src/commands/init-helpers.ts
1990
- import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
1991
- import { join as join11 } from "path";
1992
- import pc5 from "picocolors";
2256
+ import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
2257
+ import { join as join13 } from "path";
2258
+ import pc6 from "picocolors";
1993
2259
  function readProjectNameFromPackageJson(cwd2) {
1994
2260
  try {
1995
- const pkgPath2 = join11(cwd2, "package.json");
1996
- if (!existsSync8(pkgPath2)) return null;
1997
- const content = readFileSync6(pkgPath2, "utf8");
2261
+ const pkgPath2 = join13(cwd2, "package.json");
2262
+ if (!existsSync9(pkgPath2)) return null;
2263
+ const content = readFileSync7(pkgPath2, "utf8");
1998
2264
  const pkg2 = JSON.parse(content);
1999
2265
  const name = pkg2?.name;
2000
2266
  if (typeof name === "string" && name.trim()) return name.trim();
@@ -2005,10 +2271,10 @@ function readProjectNameFromPackageJson(cwd2) {
2005
2271
  }
2006
2272
  function detectConfigExtension(cwd2) {
2007
2273
  try {
2008
- if (existsSync8(join11(cwd2, "tsconfig.json"))) return "ts";
2009
- const pkgPath2 = join11(cwd2, "package.json");
2010
- if (!existsSync8(pkgPath2)) return "mjs";
2011
- const pkg2 = JSON.parse(readFileSync6(pkgPath2, "utf8"));
2274
+ if (existsSync9(join13(cwd2, "tsconfig.json"))) return "ts";
2275
+ const pkgPath2 = join13(cwd2, "package.json");
2276
+ if (!existsSync9(pkgPath2)) return "mjs";
2277
+ const pkg2 = JSON.parse(readFileSync7(pkgPath2, "utf8"));
2012
2278
  if (pkg2?.type === "module") return "mjs";
2013
2279
  } catch {
2014
2280
  }
@@ -2059,27 +2325,27 @@ function stripAnsi(str2) {
2059
2325
  function drawBox(lines) {
2060
2326
  const width = Math.max(...lines.map((l) => stripAnsi(l).length));
2061
2327
  const border = "\u2500".repeat(width);
2062
- console.log(pc5.yellow(`\u250C${border}\u2510`));
2328
+ console.log(pc6.yellow(`\u250C${border}\u2510`));
2063
2329
  for (const line of lines) {
2064
2330
  const pad = width - stripAnsi(line).length;
2065
2331
  const padStr = pad > 0 ? " ".repeat(pad) : "";
2066
- console.log(pc5.yellow("\u2502") + line + padStr + pc5.yellow("\u2502"));
2332
+ console.log(pc6.yellow("\u2502") + line + padStr + pc6.yellow("\u2502"));
2067
2333
  }
2068
- console.log(pc5.yellow(`\u2514${border}\u2518`));
2334
+ console.log(pc6.yellow(`\u2514${border}\u2518`));
2069
2335
  }
2070
2336
  function printWelcomeMessage(projectName) {
2071
2337
  const sep = "\u2500".repeat(38);
2072
2338
  const lines = [
2073
- ` ${pc5.bold(pc5.white("agent-harness-kit"))} `,
2074
- ` ${pc5.gray("\u2014")} harness scaffolding ${pc5.gray("\u2014")} `,
2075
- ` ${pc5.gray(sep)} `,
2076
- ` ${pc5.bold("Project:")} ${projectName || "\u2014"} `,
2077
- ` ${pc5.bold("Status:")} ${pc5.green("ready to configure")} `,
2078
- ` ${pc5.gray(sep)} `,
2079
- ` ${pc5.gray("Next steps:")} `,
2080
- ` ${pc5.gray("\u2192")} ${pc5.gray("Set up your AI provider config")} `,
2081
- ` ${pc5.gray("\u2192")} ${pc5.gray("Run your health check to verify")} `,
2082
- ` ${pc5.gray("\u2192")} ${pc5.gray("Start adding tasks for your agents")} `
2339
+ ` ${pc6.bold(pc6.white("agent-harness-kit"))} `,
2340
+ ` ${pc6.gray("\u2014")} harness scaffolding ${pc6.gray("\u2014")} `,
2341
+ ` ${pc6.gray(sep)} `,
2342
+ ` ${pc6.bold("Project:")} ${projectName || "\u2014"} `,
2343
+ ` ${pc6.bold("Status:")} ${pc6.green("ready to configure")} `,
2344
+ ` ${pc6.gray(sep)} `,
2345
+ ` ${pc6.gray("Next steps:")} `,
2346
+ ` ${pc6.gray("\u2192")} ${pc6.gray("Set up your AI provider config")} `,
2347
+ ` ${pc6.gray("\u2192")} ${pc6.gray("Run your health check to verify")} `,
2348
+ ` ${pc6.gray("\u2192")} ${pc6.gray("Start adding tasks for your agents")} `
2083
2349
  ];
2084
2350
  console.log();
2085
2351
  drawBox(lines);
@@ -2091,18 +2357,18 @@ async function runInit(cwd2, flags) {
2091
2357
  const existingConfig = findConfigFile(cwd2);
2092
2358
  if (existingConfig) {
2093
2359
  console.log(
2094
- pc6.yellow("\u26A0") + " " + pc6.bold("Project already initialized.") + pc6.dim(` (${existingConfig})`)
2360
+ pc7.yellow("\u26A0") + " " + pc7.bold("Project already initialized.") + pc7.dim(` (${existingConfig})`)
2095
2361
  );
2096
2362
  console.log();
2097
- console.log(pc6.dim("Suggested next steps:"));
2363
+ console.log(pc7.dim("Suggested next steps:"));
2098
2364
  console.log(
2099
- " " + pc6.cyan("ahk build") + pc6.dim(" \u2014 re-sync agent files after updating the library")
2365
+ " " + pc7.cyan("ahk build") + pc7.dim(" \u2014 re-sync agent files after updating the library")
2100
2366
  );
2101
- console.log(" " + pc6.cyan("ahk build --sync") + pc6.dim(" \u2014 also sync agent permissions"));
2367
+ console.log(" " + pc7.cyan("ahk build --sync") + pc7.dim(" \u2014 also sync agent permissions"));
2102
2368
  console.log(
2103
- " " + pc6.cyan("ahk reset") + pc6.dim(" \u2014 wipe and re-initialize from scratch")
2369
+ " " + pc7.cyan("ahk reset") + pc7.dim(" \u2014 wipe and re-initialize from scratch")
2104
2370
  );
2105
- console.log(" " + pc6.cyan("ahk dashboard") + pc6.dim(" \u2014 open the harness dashboard"));
2371
+ console.log(" " + pc7.cyan("ahk dashboard") + pc7.dim(" \u2014 open the harness dashboard"));
2106
2372
  process.exit(0);
2107
2373
  }
2108
2374
  const detectedName = flags.name ?? readProjectNameFromPackageJson(cwd2);
@@ -2241,8 +2507,8 @@ async function runInit(cwd2, flags) {
2241
2507
  tasksAdapter,
2242
2508
  port: config.tools.mcp.port
2243
2509
  });
2244
- writeFileSync9(join12(installDir, configFileName), configContent, "utf8");
2245
- mkdirSync8(join12(installDir, config.storage.dir), { recursive: true });
2510
+ writeFileSync9(join14(installDir, configFileName), configContent, "utf8");
2511
+ mkdirSync8(join14(installDir, config.storage.dir), { recursive: true });
2246
2512
  const db = await openDB(config, installDir);
2247
2513
  await materializer.scaffold(config, { cwd: installDir, firstTask });
2248
2514
  if (firstTask) {
@@ -2261,30 +2527,30 @@ async function runInit(cwd2, flags) {
2261
2527
  p3.log.error(err instanceof Error ? err.message : String(err));
2262
2528
  throw err;
2263
2529
  }
2264
- console.log(pc6.green("\u2713 Scaffolded harness in current directory"));
2530
+ console.log(pc7.green("\u2713 Scaffolded harness in current directory"));
2265
2531
  const agentsDir = provider === "claude-code" ? ".claude/agents/" : ".opencode/agents/";
2266
2532
  const mcpFile = provider === "claude-code" ? ".claude/mcp.json" : "./opencode.json";
2267
2533
  console.log("");
2268
- console.log(pc6.green(`\u2713 agent-harness-kit.config.${configExt}`));
2269
- console.log(pc6.green("\u2713 AGENTS.md"));
2270
- console.log(pc6.green("\u2713 health.sh"));
2271
- console.log(pc6.green("\u2713 .harness/harness.db"));
2272
- console.log(pc6.green("\u2713 .harness/current.md"));
2273
- console.log(pc6.green(`\u2713 ${agentsDir}lead.md`));
2274
- console.log(pc6.green(`\u2713 ${agentsDir}explorer.md`));
2275
- console.log(pc6.green(`\u2713 ${agentsDir}builder.md`));
2276
- console.log(pc6.green(`\u2713 ${agentsDir}reviewer.md`));
2277
- console.log(pc6.green(`\u2713 ${mcpFile}`));
2278
- console.log(pc6.green("\u2713 .gitignore entries added"));
2534
+ console.log(pc7.green(`\u2713 agent-harness-kit.config.${configExt}`));
2535
+ console.log(pc7.green("\u2713 AGENTS.md"));
2536
+ console.log(pc7.green("\u2713 health.sh"));
2537
+ console.log(pc7.green("\u2713 .harness/harness.db"));
2538
+ console.log(pc7.green("\u2713 .harness/current.md"));
2539
+ console.log(pc7.green(`\u2713 ${agentsDir}lead.md`));
2540
+ console.log(pc7.green(`\u2713 ${agentsDir}explorer.md`));
2541
+ console.log(pc7.green(`\u2713 ${agentsDir}builder.md`));
2542
+ console.log(pc7.green(`\u2713 ${agentsDir}reviewer.md`));
2543
+ console.log(pc7.green(`\u2713 ${mcpFile}`));
2544
+ console.log(pc7.green("\u2713 .gitignore entries added"));
2279
2545
  console.log("");
2280
- console.log(pc6.cyan("\u2192") + ` Edit ${pc6.cyan("health.sh")} with your project checks`);
2281
- console.log(pc6.cyan("\u2192") + ` ${pc6.cyan("ahk task add")} to queue work for agents`);
2546
+ console.log(pc7.cyan("\u2192") + ` Edit ${pc7.cyan("health.sh")} with your project checks`);
2547
+ console.log(pc7.cyan("\u2192") + ` ${pc7.cyan("ahk task add")} to queue work for agents`);
2282
2548
  console.log(
2283
- pc6.cyan("\u2192") + ` Enrich your docs with knowledge graphs: ${pc6.cyan("https://github.com/safishamsi/graphify")}`
2549
+ pc7.cyan("\u2192") + ` Enrich your docs with knowledge graphs: ${pc7.cyan("https://github.com/safishamsi/graphify")}`
2284
2550
  );
2285
2551
  const recommendations = [
2286
2552
  ` Give a try to Heimdall MCP: Transparent proxy that traces every MCP tool call with OpenTelemetry. `,
2287
- ` Learn more: ${pc6.cyan("https://github.com/enmanuelmag/heimdall-mcp")} `
2553
+ ` Learn more: ${pc7.cyan("https://github.com/enmanuelmag/heimdall-mcp")} `
2288
2554
  ];
2289
2555
  console.log("");
2290
2556
  drawBox(recommendations);
@@ -2292,7 +2558,7 @@ async function runInit(cwd2, flags) {
2292
2558
 
2293
2559
  // src/commands/migrate.ts
2294
2560
  import * as p4 from "@clack/prompts";
2295
- import pc7 from "picocolors";
2561
+ import pc8 from "picocolors";
2296
2562
  async function runMigrate(cwd2, opts) {
2297
2563
  const config = await loadConfig(cwd2);
2298
2564
  let target;
@@ -2314,7 +2580,7 @@ async function runMigrate(cwd2, opts) {
2314
2580
  target = val;
2315
2581
  }
2316
2582
  if (target === config.provider) {
2317
- console.log(pc7.dim(`Already on ${target} \u2014 nothing to migrate.`));
2583
+ console.log(pc8.dim(`Already on ${target} \u2014 nothing to migrate.`));
2318
2584
  return;
2319
2585
  }
2320
2586
  const spinner6 = p4.spinner();
@@ -2322,27 +2588,27 @@ async function runMigrate(cwd2, opts) {
2322
2588
  try {
2323
2589
  const targetMaterializer = getMaterializer(target);
2324
2590
  await targetMaterializer.build(config, cwd2);
2325
- spinner6.stop(pc7.green(`Migrated to ${target}`));
2591
+ spinner6.stop(pc8.green(`Migrated to ${target}`));
2326
2592
  p4.log.warn(`Update agent-harness-kit.config.ts: set provider: '${target}'`);
2327
2593
  p4.log.warn(`Then run: ahk build`);
2328
2594
  } catch (err) {
2329
- spinner6.stop(pc7.red("Migration failed"));
2595
+ spinner6.stop(pc8.red("Migration failed"));
2330
2596
  p4.log.error(err instanceof Error ? err.message : String(err));
2331
2597
  process.exit(1);
2332
2598
  }
2333
2599
  }
2334
2600
 
2335
2601
  // src/commands/reset.ts
2336
- import { existsSync as existsSync9, readdirSync, rmSync } from "fs";
2337
- import { join as join13, resolve as resolve9 } from "path";
2602
+ import { existsSync as existsSync10, readdirSync, rmSync } from "fs";
2603
+ import { join as join15, resolve as resolve9 } from "path";
2338
2604
  import * as p5 from "@clack/prompts";
2339
- import pc8 from "picocolors";
2605
+ import pc9 from "picocolors";
2340
2606
  var AGENT_MD_FILES = ["lead", "explorer", "consultant", "builder", "reviewer"];
2341
2607
  async function resetAgentMds(cwd2, provider) {
2342
2608
  const agentDir = provider === "claude-code" ? ".claude/agents" : ".opencode/agents";
2343
2609
  const agentDirPath = resolve9(cwd2, agentDir);
2344
- if (!existsSync9(agentDirPath)) {
2345
- console.log(pc8.yellow(` Skipping agent files \u2014 directory not found: ${agentDirPath}`));
2610
+ if (!existsSync10(agentDirPath)) {
2611
+ console.log(pc9.yellow(` Skipping agent files \u2014 directory not found: ${agentDirPath}`));
2346
2612
  return;
2347
2613
  }
2348
2614
  const existingFiles = [];
@@ -2354,11 +2620,11 @@ async function resetAgentMds(cwd2, provider) {
2354
2620
  }
2355
2621
  }
2356
2622
  } catch {
2357
- console.log(pc8.yellow(` Skipping agent files \u2014 ${agentDirPath} is not readable`));
2623
+ console.log(pc9.yellow(` Skipping agent files \u2014 ${agentDirPath} is not readable`));
2358
2624
  return;
2359
2625
  }
2360
2626
  if (existingFiles.length === 0) {
2361
- console.log(pc8.yellow(` No agent MD files found in ${agentDir}/`));
2627
+ console.log(pc9.yellow(` No agent MD files found in ${agentDir}/`));
2362
2628
  return;
2363
2629
  }
2364
2630
  for (const file of existingFiles) {
@@ -2367,19 +2633,19 @@ async function resetAgentMds(cwd2, provider) {
2367
2633
  initialValue: true
2368
2634
  });
2369
2635
  if (p5.isCancel(confirm3)) {
2370
- console.log(pc8.red(" Cancelled by user."));
2636
+ console.log(pc9.red(" Cancelled by user."));
2371
2637
  return;
2372
2638
  }
2373
2639
  if (confirm3) {
2374
2640
  try {
2375
- const filePath = join13(agentDirPath, file);
2641
+ const filePath = join15(agentDirPath, file);
2376
2642
  rmSync(filePath, { force: true });
2377
- console.log(pc8.green(` Removed ${file}`));
2643
+ console.log(pc9.green(` Removed ${file}`));
2378
2644
  } catch {
2379
- console.error(pc8.red(` Failed to remove ${file}`));
2645
+ console.error(pc9.red(` Failed to remove ${file}`));
2380
2646
  }
2381
2647
  } else {
2382
- console.log(pc8.cyan(` Skipped ${file}`));
2648
+ console.log(pc9.cyan(` Skipped ${file}`));
2383
2649
  }
2384
2650
  }
2385
2651
  }
@@ -2388,7 +2654,7 @@ async function runReset(cwd2, opts) {
2388
2654
  try {
2389
2655
  config = await loadConfig(cwd2);
2390
2656
  } catch {
2391
- console.error(pc8.red("\u2717 No agent-harness-kit.config found. Run: ahk init"));
2657
+ console.error(pc9.red("\u2717 No agent-harness-kit.config found. Run: ahk init"));
2392
2658
  process.exit(1);
2393
2659
  }
2394
2660
  const storageDir = config.storage.dir || ".harness";
@@ -2397,12 +2663,12 @@ async function runReset(cwd2, opts) {
2397
2663
  let resetDb = false;
2398
2664
  let resetFeatureList = false;
2399
2665
  let resetAgentMdsFlag = false;
2400
- if (dbPath && existsSync9(dbPath)) {
2666
+ if (dbPath && existsSync10(dbPath)) {
2401
2667
  if (opts.force) {
2402
2668
  resetDb = true;
2403
2669
  } else {
2404
2670
  if (config.database.type !== "sqlite") {
2405
- console.log(pc8.yellow(` Skipping DB reset \u2014 database type "${config.database.type}" is not managed by this command.`));
2671
+ console.log(pc9.yellow(` Skipping DB reset \u2014 database type "${config.database.type}" is not managed by this command.`));
2406
2672
  resetDb = false;
2407
2673
  } else {
2408
2674
  const confirm3 = await p5.confirm({
@@ -2410,16 +2676,16 @@ async function runReset(cwd2, opts) {
2410
2676
  initialValue: true
2411
2677
  });
2412
2678
  if (p5.isCancel(confirm3)) {
2413
- console.log(pc8.red(" Cancelled by user."));
2679
+ console.log(pc9.red(" Cancelled by user."));
2414
2680
  return;
2415
2681
  }
2416
2682
  resetDb = confirm3;
2417
2683
  }
2418
2684
  }
2419
2685
  } else if (!dbPath) {
2420
- console.log(pc8.dim(` Skipping DB reset \u2014 remote ${config.database.type} database is not managed by this command.`));
2686
+ console.log(pc9.dim(` Skipping DB reset \u2014 remote ${config.database.type} database is not managed by this command.`));
2421
2687
  }
2422
- if (existsSync9(featureListPath)) {
2688
+ if (existsSync10(featureListPath)) {
2423
2689
  if (opts.force) {
2424
2690
  resetFeatureList = true;
2425
2691
  } else {
@@ -2428,7 +2694,7 @@ async function runReset(cwd2, opts) {
2428
2694
  initialValue: true
2429
2695
  });
2430
2696
  if (p5.isCancel(confirm3)) {
2431
- console.log(pc8.red(" Cancelled by user."));
2697
+ console.log(pc9.red(" Cancelled by user."));
2432
2698
  return;
2433
2699
  }
2434
2700
  resetFeatureList = confirm3;
@@ -2442,17 +2708,17 @@ async function runReset(cwd2, opts) {
2442
2708
  rmSync(dbPath, { force: true });
2443
2709
  rmSync(`${dbPath}-wal`, { force: true });
2444
2710
  rmSync(`${dbPath}-shm`, { force: true });
2445
- console.log(pc8.green(` \u2713 Removed ${dbPath}`));
2711
+ console.log(pc9.green(` \u2713 Removed ${dbPath}`));
2446
2712
  } catch {
2447
- console.error(pc8.red(` \u2717 Failed to remove ${dbPath}`));
2713
+ console.error(pc9.red(` \u2717 Failed to remove ${dbPath}`));
2448
2714
  }
2449
2715
  }
2450
2716
  if (resetFeatureList) {
2451
2717
  try {
2452
2718
  rmSync(featureListPath, { force: true });
2453
- console.log(pc8.green(` \u2713 Removed ${storageDir}/feature_list.json`));
2719
+ console.log(pc9.green(` \u2713 Removed ${storageDir}/feature_list.json`));
2454
2720
  } catch {
2455
- console.error(pc8.red(` \u2717 Failed to remove ${featureListPath}`));
2721
+ console.error(pc9.red(` \u2717 Failed to remove ${featureListPath}`));
2456
2722
  }
2457
2723
  }
2458
2724
  if (resetAgentMdsFlag) {
@@ -2460,16 +2726,16 @@ async function runReset(cwd2, opts) {
2460
2726
  await resetAgentMds(cwd2, opts.provider || "claude-code");
2461
2727
  }
2462
2728
  if (!resetDb && !resetFeatureList && !resetAgentMdsFlag) {
2463
- console.log(pc8.yellow(" Nothing to reset (all items missing or skipped)."));
2729
+ console.log(pc9.yellow(" Nothing to reset (all items missing or skipped)."));
2464
2730
  return;
2465
2731
  }
2466
2732
  console.log("");
2467
- console.log(pc8.green('\u2713 Reset complete. Run "ahk init" to scaffold a fresh harness.'));
2733
+ console.log(pc9.green('\u2713 Reset complete. Run "ahk init" to scaffold a fresh harness.'));
2468
2734
  }
2469
2735
 
2470
2736
  // src/core/mcp-server.ts
2471
- import { existsSync as existsSync11, mkdirSync as mkdirSync9, readdirSync as readdirSync2, readFileSync as readFileSync8, statSync, writeFileSync as writeFileSync10 } from "fs";
2472
- import { join as join15, resolve as resolve10 } from "path";
2737
+ import { existsSync as existsSync12, mkdirSync as mkdirSync9, readdirSync as readdirSync2, readFileSync as readFileSync9, statSync, writeFileSync as writeFileSync10 } from "fs";
2738
+ import { join as join17, resolve as resolve10 } from "path";
2473
2739
  import { Server } from "@modelcontextprotocol/sdk/server";
2474
2740
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
2475
2741
  import {
@@ -2478,8 +2744,8 @@ import {
2478
2744
  } from "@modelcontextprotocol/sdk/types.js";
2479
2745
 
2480
2746
  // src/core/permissions-check.ts
2481
- import { existsSync as existsSync10, readFileSync as readFileSync7 } from "fs";
2482
- import { join as join14 } from "path";
2747
+ import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
2748
+ import { join as join16 } from "path";
2483
2749
  var CANONICAL = {
2484
2750
  lead: [...MCP_CLAUDE_PERMISSIONS_LEAD],
2485
2751
  explorer: [...MCP_CLAUDE_PERMISSIONS_EXPLORER],
@@ -2502,21 +2768,21 @@ function checkPermissionsSync(cwd2, config) {
2502
2768
  const agents = {};
2503
2769
  let in_sync = true;
2504
2770
  for (const agent of ["lead", "explorer", "consultant", "builder", "reviewer"]) {
2505
- const filePath = join14(cwd2, ".claude", "agents", `${agent}.md`);
2506
- if (!existsSync10(filePath)) {
2771
+ const filePath = join16(cwd2, ".claude", "agents", `${agent}.md`);
2772
+ if (!existsSync11(filePath)) {
2507
2773
  const missing2 = CANONICAL[agent];
2508
2774
  agents[agent] = { ok: false, missing: missing2, extra: [] };
2509
2775
  in_sync = false;
2510
2776
  continue;
2511
2777
  }
2512
- const content = readFileSync7(filePath, "utf-8");
2778
+ const content = readFileSync8(filePath, "utf-8");
2513
2779
  const installed = parseToolsFromFrontmatter(content);
2514
2780
  const canonical = CANONICAL[agent];
2515
2781
  const missing = canonical.filter((t) => !installed.includes(t));
2516
2782
  const extra = installed.filter((t) => !canonical.includes(t));
2517
- const ok2 = missing.length === 0 && extra.length === 0;
2518
- if (!ok2) in_sync = false;
2519
- agents[agent] = { ok: ok2, missing, extra };
2783
+ const ok3 = missing.length === 0 && extra.length === 0;
2784
+ if (!ok3) in_sync = false;
2785
+ agents[agent] = { ok: ok3, missing, extra };
2520
2786
  }
2521
2787
  return { in_sync, agents };
2522
2788
  }
@@ -2772,6 +3038,11 @@ var TOOLS = [
2772
3038
  name: "deps.check",
2773
3039
  description: "Compare current package.json against .harness/deps-lock.json and report changes",
2774
3040
  inputSchema: { type: "object", properties: {}, required: [] }
3041
+ },
3042
+ {
3043
+ name: "ahk.doctor",
3044
+ description: "Check lib version, agent files, and harness skills sync status. Returns structured JSON.",
3045
+ inputSchema: { type: "object", properties: {}, required: [] }
2775
3046
  }
2776
3047
  ];
2777
3048
  async function startMcpServer(config, cwd2) {
@@ -2789,7 +3060,7 @@ async function startMcpServer(config, cwd2) {
2789
3060
  const result = await dispatch(name, a, db, docsPath, cwd2, config);
2790
3061
  return result;
2791
3062
  } catch (err) {
2792
- return ok(`Error: ${err instanceof Error ? err.message : String(err)}`, true);
3063
+ return ok2(`Error: ${err instanceof Error ? err.message : String(err)}`, true);
2793
3064
  }
2794
3065
  });
2795
3066
  const transport = new StdioServerTransport();
@@ -2801,20 +3072,20 @@ async function dispatch(name, args, db, docsPath, cwd2, config) {
2801
3072
  const taskId = num(args, "taskId");
2802
3073
  const agent = str(args, "agent");
2803
3074
  const action = await db.startAction(taskId, agent);
2804
- return ok(JSON.stringify({ actionId: action.id, taskId, agent, status: "in_progress" }));
3075
+ return ok2(JSON.stringify({ actionId: action.id, taskId, agent, status: "in_progress" }));
2805
3076
  }
2806
3077
  case "actions.write": {
2807
3078
  const actionId = str(args, "actionId");
2808
3079
  const sectionType = str(args, "sectionType");
2809
3080
  const content = str(args, "content");
2810
3081
  await db.writeSection(actionId, sectionType, content);
2811
- return ok(JSON.stringify({ actionId, sectionType, recorded: true }));
3082
+ return ok2(JSON.stringify({ actionId, sectionType, recorded: true }));
2812
3083
  }
2813
3084
  case "actions.complete": {
2814
3085
  const actionId = str(args, "actionId");
2815
3086
  const summary = str(args, "summary");
2816
3087
  const action = await db.completeAction(actionId, summary);
2817
- return ok(
3088
+ return ok2(
2818
3089
  JSON.stringify({ actionId, status: action.status, completedAt: action.completed_at })
2819
3090
  );
2820
3091
  }
@@ -2827,22 +3098,22 @@ async function dispatch(name, args, db, docsPath, cwd2, config) {
2827
3098
  sections: await db.getActionSections(a.id)
2828
3099
  }))
2829
3100
  );
2830
- return ok(JSON.stringify(full, null, 2));
3101
+ return ok2(JSON.stringify(full, null, 2));
2831
3102
  }
2832
3103
  case "tasks.get": {
2833
3104
  const status = args["status"];
2834
3105
  const includeArchived = args["includeArchived"];
2835
3106
  const tasks = status ? await db.getTasks(status, includeArchived ?? false) : await db.getTasks(void 0, includeArchived ?? false);
2836
- return ok(JSON.stringify(tasks, null, 2));
3107
+ return ok2(JSON.stringify(tasks, null, 2));
2837
3108
  }
2838
3109
  case "tasks.claim": {
2839
3110
  const id = num(args, "id");
2840
3111
  const agent = str(args, "agent");
2841
3112
  const task2 = await db.claimTask(id, agent);
2842
3113
  if (!task2) {
2843
- return ok(JSON.stringify({ error: "task_already_claimed", taskId: id }));
3114
+ return ok2(JSON.stringify({ error: "task_already_claimed", taskId: id }));
2844
3115
  }
2845
- return ok(JSON.stringify(task2));
3116
+ return ok2(JSON.stringify(task2));
2846
3117
  }
2847
3118
  case "tasks.add": {
2848
3119
  const title = str(args, "title");
@@ -2850,7 +3121,7 @@ async function dispatch(name, args, db, docsPath, cwd2, config) {
2850
3121
  const description = args["description"];
2851
3122
  const acceptance = args["acceptance"];
2852
3123
  const task2 = await db.addTask({ slug, title, description, acceptance });
2853
- return ok(JSON.stringify(task2));
3124
+ return ok2(JSON.stringify(task2));
2854
3125
  }
2855
3126
  case "tasks.update": {
2856
3127
  const id = num(args, "id");
@@ -2859,12 +3130,12 @@ async function dispatch(name, args, db, docsPath, cwd2, config) {
2859
3130
  await db.closeOrphanedActions(id);
2860
3131
  }
2861
3132
  const task2 = await db.updateTaskStatus(id, status);
2862
- return ok(JSON.stringify(task2));
3133
+ return ok2(JSON.stringify(task2));
2863
3134
  }
2864
3135
  case "docs.search": {
2865
3136
  const query = str(args, "query");
2866
3137
  const results = searchDocs(docsPath, query);
2867
- return ok(JSON.stringify(results, null, 2));
3138
+ return ok2(JSON.stringify(results, null, 2));
2868
3139
  }
2869
3140
  case "actions.record_file": {
2870
3141
  const actionId = str(args, "actionId");
@@ -2872,17 +3143,17 @@ async function dispatch(name, args, db, docsPath, cwd2, config) {
2872
3143
  const operation = str(args, "operation");
2873
3144
  const notes = args["notes"];
2874
3145
  await db.recordFile(actionId, filePath, operation, notes);
2875
- return ok(JSON.stringify({ actionId, filePath, operation, recorded: true }));
3146
+ return ok2(JSON.stringify({ actionId, filePath, operation, recorded: true }));
2876
3147
  }
2877
3148
  case "tasks.acceptance.update": {
2878
3149
  const criterionId = num(args, "criterionId");
2879
3150
  await db.markAcceptanceMet(criterionId);
2880
- return ok(JSON.stringify({ criterionId, met: true }));
3151
+ return ok2(JSON.stringify({ criterionId, met: true }));
2881
3152
  }
2882
3153
  case "tasks.acceptance.get": {
2883
3154
  const taskId = num(args, "taskId");
2884
3155
  const criteria = await db.getTaskAcceptance(taskId);
2885
- return ok(JSON.stringify(criteria, null, 2));
3156
+ return ok2(JSON.stringify(criteria, null, 2));
2886
3157
  }
2887
3158
  case "actions.record_tool": {
2888
3159
  const actionId = str(args, "actionId");
@@ -2890,7 +3161,7 @@ async function dispatch(name, args, db, docsPath, cwd2, config) {
2890
3161
  const argsJson = args["argsJson"];
2891
3162
  const resultSummary = args["resultSummary"];
2892
3163
  await db.recordTool(actionId, toolName, argsJson, resultSummary);
2893
- return ok(JSON.stringify({ actionId, toolName, recorded: true }));
3164
+ return ok2(JSON.stringify({ actionId, toolName, recorded: true }));
2894
3165
  }
2895
3166
  case "tasks.edit": {
2896
3167
  const id = num(args, "id");
@@ -2898,7 +3169,7 @@ async function dispatch(name, args, db, docsPath, cwd2, config) {
2898
3169
  const description = args["description"];
2899
3170
  const acceptance = args["acceptance"];
2900
3171
  const task2 = await db.getTaskById(id);
2901
- if (!task2) return ok(JSON.stringify({ error: "Task not found", taskId: id }), true);
3172
+ if (!task2) return ok2(JSON.stringify({ error: "Task not found", taskId: id }), true);
2902
3173
  await db.updateTask(id, {
2903
3174
  title,
2904
3175
  description: description !== void 0 ? description : void 0
@@ -2907,37 +3178,37 @@ async function dispatch(name, args, db, docsPath, cwd2, config) {
2907
3178
  await db.updateTaskAcceptance(id, acceptance);
2908
3179
  }
2909
3180
  const updated = await db.getTaskById(id);
2910
- return ok(JSON.stringify(updated));
3181
+ return ok2(JSON.stringify(updated));
2911
3182
  }
2912
3183
  case "tasks.archive": {
2913
3184
  const id = num(args, "id");
2914
3185
  const task2 = await db.archiveTask(id);
2915
- return ok(JSON.stringify(task2));
3186
+ return ok2(JSON.stringify(task2));
2916
3187
  }
2917
3188
  case "tasks.unarchive": {
2918
3189
  const id = num(args, "id");
2919
3190
  const task2 = await db.unarchiveTask(id);
2920
- return ok(JSON.stringify(task2));
3191
+ return ok2(JSON.stringify(task2));
2921
3192
  }
2922
3193
  case "permissions.check": {
2923
3194
  const result = checkPermissionsSync(cwd2, config);
2924
- return ok(JSON.stringify(result, null, 2));
3195
+ return ok2(JSON.stringify(result, null, 2));
2925
3196
  }
2926
3197
  case "deps.snapshot": {
2927
- const pkgPath2 = join15(cwd2, "package.json");
2928
- if (!existsSync11(pkgPath2)) {
2929
- return ok("package.json not found in project root", true);
3198
+ const pkgPath2 = join17(cwd2, "package.json");
3199
+ if (!existsSync12(pkgPath2)) {
3200
+ return ok2("package.json not found in project root", true);
2930
3201
  }
2931
- const pkg2 = JSON.parse(readFileSync8(pkgPath2, "utf8"));
3202
+ const pkg2 = JSON.parse(readFileSync9(pkgPath2, "utf8"));
2932
3203
  const snapshot = {
2933
3204
  capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
2934
3205
  dependencies: pkg2.dependencies ?? {},
2935
3206
  devDependencies: pkg2.devDependencies ?? {}
2936
3207
  };
2937
- const harnessDir = join15(cwd2, ".harness");
3208
+ const harnessDir = join17(cwd2, ".harness");
2938
3209
  mkdirSync9(harnessDir, { recursive: true });
2939
- writeFileSync10(join15(harnessDir, "deps-lock.json"), JSON.stringify(snapshot, null, 2), "utf8");
2940
- return ok(
3210
+ writeFileSync10(join17(harnessDir, "deps-lock.json"), JSON.stringify(snapshot, null, 2), "utf8");
3211
+ return ok2(
2941
3212
  JSON.stringify({
2942
3213
  message: "Snapshot saved to .harness/deps-lock.json",
2943
3214
  capturedAt: snapshot.capturedAt
@@ -2945,21 +3216,21 @@ async function dispatch(name, args, db, docsPath, cwd2, config) {
2945
3216
  );
2946
3217
  }
2947
3218
  case "deps.check": {
2948
- const pkgPath2 = join15(cwd2, "package.json");
2949
- const lockPath = join15(cwd2, ".harness", "deps-lock.json");
2950
- if (!existsSync11(pkgPath2)) {
2951
- return ok("package.json not found in project root", true);
3219
+ const pkgPath2 = join17(cwd2, "package.json");
3220
+ const lockPath = join17(cwd2, ".harness", "deps-lock.json");
3221
+ if (!existsSync12(pkgPath2)) {
3222
+ return ok2("package.json not found in project root", true);
2952
3223
  }
2953
- if (!existsSync11(lockPath)) {
2954
- return ok(
3224
+ if (!existsSync12(lockPath)) {
3225
+ return ok2(
2955
3226
  JSON.stringify({
2956
3227
  status: "no-snapshot",
2957
3228
  message: "No deps-lock.json found. Run deps.snapshot first to establish a baseline."
2958
3229
  })
2959
3230
  );
2960
3231
  }
2961
- const pkg2 = JSON.parse(readFileSync8(pkgPath2, "utf8"));
2962
- const lock = JSON.parse(readFileSync8(lockPath, "utf8"));
3232
+ const pkg2 = JSON.parse(readFileSync9(pkgPath2, "utf8"));
3233
+ const lock = JSON.parse(readFileSync9(lockPath, "utf8"));
2963
3234
  const current = { ...pkg2.dependencies ?? {}, ...pkg2.devDependencies ?? {} };
2964
3235
  const previous = { ...lock.dependencies ?? {}, ...lock.devDependencies ?? {} };
2965
3236
  const added = [];
@@ -2986,7 +3257,7 @@ async function dispatch(name, args, db, docsPath, cwd2, config) {
2986
3257
  }
2987
3258
  const significant = added.length > 0 || removed.length > 0 || majorBumps.length > 0;
2988
3259
  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.";
2989
- return ok(
3260
+ return ok2(
2990
3261
  JSON.stringify({
2991
3262
  significant,
2992
3263
  added,
@@ -2997,8 +3268,25 @@ async function dispatch(name, args, db, docsPath, cwd2, config) {
2997
3268
  })
2998
3269
  );
2999
3270
  }
3271
+ case "ahk.doctor": {
3272
+ const status = await getDoctorStatus(cwd2);
3273
+ const result = {
3274
+ lib: { current: status.lib.current, latest: status.lib.latest, outdated: status.lib.outdated },
3275
+ agents: {
3276
+ missing: status.agents.filter((a) => a.status === "missing").map((a) => a.name),
3277
+ outdated: status.agents.filter((a) => a.status === "outdated").map((a) => a.name),
3278
+ ok: status.agents.filter((a) => a.status === "ok").map((a) => a.name)
3279
+ },
3280
+ skills: {
3281
+ missing: status.skills.filter((s) => s.status === "missing").map((s) => s.name),
3282
+ outdated: status.skills.filter((s) => s.status === "outdated").map((s) => s.name),
3283
+ ok: status.skills.filter((s) => s.status === "ok").map((s) => s.name)
3284
+ }
3285
+ };
3286
+ return ok2(JSON.stringify(result, null, 2));
3287
+ }
3000
3288
  default:
3001
- return ok(`Unknown tool: ${name}`, true);
3289
+ return ok2(`Unknown tool: ${name}`, true);
3002
3290
  }
3003
3291
  }
3004
3292
  function searchDocs(docsPath, query, maxResults = 10) {
@@ -3009,7 +3297,7 @@ function searchDocs(docsPath, query, maxResults = 10) {
3009
3297
  for (const file of files) {
3010
3298
  if (results.length >= maxResults) break;
3011
3299
  try {
3012
- const content = readFileSync8(file, "utf8");
3300
+ const content = readFileSync9(file, "utf8");
3013
3301
  const lines = content.split("\n");
3014
3302
  for (let i = 0; i < lines.length; i++) {
3015
3303
  const lower = lines[i].toLowerCase();
@@ -3034,7 +3322,7 @@ function collectMarkdownFiles(dir) {
3034
3322
  const files = [];
3035
3323
  try {
3036
3324
  for (const entry of readdirSync2(dir)) {
3037
- const full = join15(dir, entry);
3325
+ const full = join17(dir, entry);
3038
3326
  const stat = statSync(full);
3039
3327
  if (stat.isDirectory()) {
3040
3328
  files.push(...collectMarkdownFiles(full));
@@ -3046,7 +3334,7 @@ function collectMarkdownFiles(dir) {
3046
3334
  }
3047
3335
  return files;
3048
3336
  }
3049
- function ok(text4, isError = false) {
3337
+ function ok2(text4, isError = false) {
3050
3338
  return { content: [{ type: "text", text: text4 }], isError };
3051
3339
  }
3052
3340
  function str(args, key) {
@@ -3085,12 +3373,12 @@ async function runServe(cwd2, opts) {
3085
3373
 
3086
3374
  // src/commands/status.ts
3087
3375
  import Table from "cli-table3";
3088
- import pc9 from "picocolors";
3376
+ import pc10 from "picocolors";
3089
3377
  var STATUS_COLOR = {
3090
- pending: (s) => pc9.dim(s),
3091
- in_progress: (s) => pc9.cyan(s),
3092
- done: (s) => pc9.green(s),
3093
- blocked: (s) => pc9.red(s)
3378
+ pending: (s) => pc10.dim(s),
3379
+ in_progress: (s) => pc10.cyan(s),
3380
+ done: (s) => pc10.green(s),
3381
+ blocked: (s) => pc10.red(s)
3094
3382
  };
3095
3383
  async function runStatus(cwd2, opts) {
3096
3384
  const config = await loadConfig(cwd2);
@@ -3111,11 +3399,11 @@ async function runStatus(cwd2, opts) {
3111
3399
  return;
3112
3400
  }
3113
3401
  if (tasks.length === 0) {
3114
- console.log(pc9.dim("No tasks yet. Run: ahk task add"));
3402
+ console.log(pc10.dim("No tasks yet. Run: ahk task add"));
3115
3403
  return;
3116
3404
  }
3117
3405
  const table = new Table({
3118
- head: ["ID", "Slug", "Title", "Status", "Assigned", "Started"].map((h) => pc9.bold(h)),
3406
+ head: ["ID", "Slug", "Title", "Status", "Assigned", "Started"].map((h) => pc10.bold(h)),
3119
3407
  style: { head: [], border: [] }
3120
3408
  });
3121
3409
  for (const t of tasks) {
@@ -3133,12 +3421,12 @@ async function runStatus(cwd2, opts) {
3133
3421
  const inProgress = tasks.filter((t) => t.status === "in_progress");
3134
3422
  if (inProgress.length > 0) {
3135
3423
  console.log("");
3136
- console.log(pc9.bold("Active actions:"));
3424
+ console.log(pc10.bold("Active actions:"));
3137
3425
  for (const t of inProgress) {
3138
3426
  const actions = await db.getActionsForTask(t.id);
3139
3427
  const active = actions.filter((a) => a.status === "in_progress");
3140
3428
  for (const a of active) {
3141
- console.log(` ${pc9.cyan(a.agent.padEnd(10))} \u2192 task #${t.id} ${t.slug}`);
3429
+ console.log(` ${pc10.cyan(a.agent.padEnd(10))} \u2192 task #${t.id} ${t.slug}`);
3142
3430
  }
3143
3431
  }
3144
3432
  }
@@ -3147,10 +3435,10 @@ async function runStatus(cwd2, opts) {
3147
3435
  const fn = STATUS_COLOR[s.status] ?? ((x) => x);
3148
3436
  return `${fn(s.status)}: ${s.total}`;
3149
3437
  });
3150
- console.log(pc9.dim("Tasks \u2014 ") + parts.join(pc9.dim(" | ")));
3438
+ console.log(pc10.dim("Tasks \u2014 ") + parts.join(pc10.dim(" | ")));
3151
3439
  const archivedTasks = await db.getArchivedTasks();
3152
3440
  if (archivedTasks.length > 0) {
3153
- console.log(pc9.dim(`${archivedTasks.length} archived (use \`ahk task list --archived\` to view)`));
3441
+ console.log(pc10.dim(`${archivedTasks.length} archived (use \`ahk task list --archived\` to view)`));
3154
3442
  }
3155
3443
  } finally {
3156
3444
  await db.close();
@@ -3158,13 +3446,13 @@ async function runStatus(cwd2, opts) {
3158
3446
  }
3159
3447
 
3160
3448
  // src/commands/sync.ts
3161
- import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
3162
- import { join as join16, resolve as resolve11 } from "path";
3163
- import pc10 from "picocolors";
3449
+ import { existsSync as existsSync13, readFileSync as readFileSync10 } from "fs";
3450
+ import { join as join18, resolve as resolve11 } from "path";
3451
+ import pc11 from "picocolors";
3164
3452
  async function runSync(cwd2, opts) {
3165
3453
  const config = await loadConfig(cwd2);
3166
3454
  const direction = opts.direction ?? "both";
3167
- const featureListPath = resolve11(join16(cwd2, config.storage.dir, "feature_list.json"));
3455
+ const featureListPath = resolve11(join18(cwd2, config.storage.dir, "feature_list.json"));
3168
3456
  const db = await openDB(config, cwd2);
3169
3457
  try {
3170
3458
  if (direction === "in" || direction === "both") {
@@ -3178,44 +3466,44 @@ async function runSync(cwd2, opts) {
3178
3466
  }
3179
3467
  }
3180
3468
  async function syncIn(featureListPath, db, dryRun) {
3181
- if (!existsSync12(featureListPath)) {
3182
- console.log(pc10.dim(`feature_list.json not found at ${featureListPath} \u2014 skipping in-sync`));
3469
+ if (!existsSync13(featureListPath)) {
3470
+ console.log(pc11.dim(`feature_list.json not found at ${featureListPath} \u2014 skipping in-sync`));
3183
3471
  return;
3184
3472
  }
3185
3473
  let seeds;
3186
3474
  try {
3187
- seeds = JSON.parse(readFileSync9(featureListPath, "utf8"));
3475
+ seeds = JSON.parse(readFileSync10(featureListPath, "utf8"));
3188
3476
  } catch (err) {
3189
- console.error(pc10.red(`Failed to parse feature_list.json: ${err}`));
3477
+ console.error(pc11.red(`Failed to parse feature_list.json: ${err}`));
3190
3478
  process.exit(1);
3191
3479
  }
3192
3480
  if (dryRun) {
3193
- console.log(pc10.bold("Dry run \u2014 in-sync (feature_list.json \u2192 SQLite):"));
3481
+ console.log(pc11.bold("Dry run \u2014 in-sync (feature_list.json \u2192 SQLite):"));
3194
3482
  for (const t of seeds) {
3195
3483
  const existing = await db.getTaskBySlug(t.slug);
3196
- console.log(` ${existing ? pc10.dim("skip") : pc10.green("add ")} ${t.slug}`);
3484
+ console.log(` ${existing ? pc11.dim("skip") : pc11.green("add ")} ${t.slug}`);
3197
3485
  }
3198
3486
  return;
3199
3487
  }
3200
3488
  const result = await db.syncFromFeatureList(seeds);
3201
- console.log(pc10.green(`\u2713 In-sync: ${result.added} added, ${result.skipped} already existed`));
3489
+ console.log(pc11.green(`\u2713 In-sync: ${result.added} added, ${result.skipped} already existed`));
3202
3490
  }
3203
3491
  async function syncOut(db, cwd2, dryRun) {
3204
3492
  if (dryRun) {
3205
3493
  const tasks = await db.getTasks();
3206
- console.log(pc10.bold("Dry run \u2014 out-sync (SQLite \u2192 feature_list.json):"));
3494
+ console.log(pc11.bold("Dry run \u2014 out-sync (SQLite \u2192 feature_list.json):"));
3207
3495
  console.log(` ${tasks.length} tasks would be written`);
3208
3496
  return;
3209
3497
  }
3210
3498
  await db.writeFeatureList(cwd2);
3211
- console.log(pc10.green("\u2713 Out-sync: feature_list.json updated"));
3499
+ console.log(pc11.green("\u2713 Out-sync: feature_list.json updated"));
3212
3500
  }
3213
3501
 
3214
3502
  // src/commands/task/add.ts
3215
3503
  import * as p6 from "@clack/prompts";
3216
- import pc11 from "picocolors";
3504
+ import pc12 from "picocolors";
3217
3505
  async function runTaskAdd(cwd2) {
3218
- p6.intro(pc11.bold("agent-harness-kit \u2014 add task"));
3506
+ p6.intro(pc12.bold("agent-harness-kit \u2014 add task"));
3219
3507
  const title = await cliFormWithRetry(
3220
3508
  async () => {
3221
3509
  const val = await p6.text({ message: "Task title" });
@@ -3258,10 +3546,10 @@ async function runTaskAdd(cwd2) {
3258
3546
  await db.writeFeatureList(cwd2);
3259
3547
  await db.close();
3260
3548
  spinner6.stop("");
3261
- console.log(pc11.green(`\u2713 Task #${task2.id} added \u2014 ${task2.slug} (pending)`));
3262
- console.log(pc11.cyan("\u2192") + " " + pc11.cyan("ahk status") + " to see all tasks");
3549
+ console.log(pc12.green(`\u2713 Task #${task2.id} added \u2014 ${task2.slug} (pending)`));
3550
+ console.log(pc12.cyan("\u2192") + " " + pc12.cyan("ahk status") + " to see all tasks");
3263
3551
  } catch (err) {
3264
- spinner6.stop(pc11.red("Failed"));
3552
+ spinner6.stop(pc12.red("Failed"));
3265
3553
  p6.log.error(err instanceof Error ? err.message : String(err));
3266
3554
  process.exit(1);
3267
3555
  }
@@ -3269,17 +3557,17 @@ async function runTaskAdd(cwd2) {
3269
3557
 
3270
3558
  // src/commands/task/done.ts
3271
3559
  import { spawnSync as spawnSync2 } from "child_process";
3272
- import { existsSync as existsSync13 } from "fs";
3560
+ import { existsSync as existsSync14 } from "fs";
3273
3561
  import { resolve as resolve12 } from "path";
3274
- import pc12 from "picocolors";
3562
+ import pc13 from "picocolors";
3275
3563
  async function runTaskDone(cwd2, idOrSlug) {
3276
3564
  const config = await loadConfig(cwd2);
3277
3565
  if (config.health.required) {
3278
3566
  const scriptPath = resolve12(cwd2, config.health.scriptPath);
3279
- if (existsSync13(scriptPath)) {
3567
+ if (existsSync14(scriptPath)) {
3280
3568
  const result = spawnSync2("bash", [scriptPath], { cwd: cwd2, stdio: "pipe", encoding: "utf8" });
3281
3569
  if (result.status !== 0) {
3282
- console.error(pc12.red("\u2717 Health check failed \u2014 cannot mark task as done."));
3570
+ console.error(pc13.red("\u2717 Health check failed \u2014 cannot mark task as done."));
3283
3571
  if (result.stdout) console.error(result.stdout);
3284
3572
  if (result.stderr) console.error(result.stderr);
3285
3573
  process.exit(1);
@@ -3292,16 +3580,16 @@ async function runTaskDone(cwd2, idOrSlug) {
3292
3580
  const isId = !isNaN(parsed);
3293
3581
  const task2 = isId ? await db.getTaskById(parsed) : await db.getTaskBySlug(idOrSlug);
3294
3582
  if (!task2) {
3295
- console.error(pc12.red(`Task not found: ${idOrSlug}`));
3583
+ console.error(pc13.red(`Task not found: ${idOrSlug}`));
3296
3584
  process.exit(1);
3297
3585
  }
3298
3586
  if (task2.status === "done") {
3299
- console.log(pc12.dim(`Task #${task2.id} is already done.`));
3587
+ console.log(pc13.dim(`Task #${task2.id} is already done.`));
3300
3588
  return;
3301
3589
  }
3302
3590
  await db.updateTaskStatus(task2.id, "done");
3303
3591
  await db.writeFeatureList(cwd2);
3304
- console.log(pc12.green(`\u2713 Task #${task2.id} \u2014 ${task2.slug} marked as done`));
3592
+ console.log(pc13.green(`\u2713 Task #${task2.id} \u2014 ${task2.slug} marked as done`));
3305
3593
  } finally {
3306
3594
  await db.close();
3307
3595
  }
@@ -3309,9 +3597,9 @@ async function runTaskDone(cwd2, idOrSlug) {
3309
3597
 
3310
3598
  // src/commands/task/edit.ts
3311
3599
  import * as p7 from "@clack/prompts";
3312
- import pc13 from "picocolors";
3600
+ import pc14 from "picocolors";
3313
3601
  async function runTaskEdit(cwd2) {
3314
- p7.intro(pc13.bold("agent-harness-kit \u2014 edit task"));
3602
+ p7.intro(pc14.bold("agent-harness-kit \u2014 edit task"));
3315
3603
  const config = await loadConfig(cwd2);
3316
3604
  const db = await openDB(config, cwd2);
3317
3605
  try {
@@ -3389,9 +3677,9 @@ async function runTaskEdit(cwd2) {
3389
3677
  await db.updateTaskAcceptance(task2.id, newAcceptance);
3390
3678
  await db.writeFeatureList(cwd2);
3391
3679
  spinner6.stop("");
3392
- console.log(pc13.green(`\u2713 Task #${task2.id} updated \u2014 ${newSlug}`));
3680
+ console.log(pc14.green(`\u2713 Task #${task2.id} updated \u2014 ${newSlug}`));
3393
3681
  } catch (err) {
3394
- spinner6.stop(pc13.red("Failed"));
3682
+ spinner6.stop(pc14.red("Failed"));
3395
3683
  p7.log.error(err instanceof Error ? err.message : String(err));
3396
3684
  process.exit(1);
3397
3685
  }
@@ -3402,12 +3690,12 @@ async function runTaskEdit(cwd2) {
3402
3690
 
3403
3691
  // src/commands/task/list.ts
3404
3692
  import Table2 from "cli-table3";
3405
- import pc14 from "picocolors";
3693
+ import pc15 from "picocolors";
3406
3694
  var STATUS_COLOR2 = {
3407
- pending: (s) => pc14.dim(s),
3408
- in_progress: (s) => pc14.cyan(s),
3409
- done: (s) => pc14.green(s),
3410
- blocked: (s) => pc14.red(s)
3695
+ pending: (s) => pc15.dim(s),
3696
+ in_progress: (s) => pc15.cyan(s),
3697
+ done: (s) => pc15.green(s),
3698
+ blocked: (s) => pc15.red(s)
3411
3699
  };
3412
3700
  async function runTaskList(cwd2, opts) {
3413
3701
  const config = await loadConfig(cwd2);
@@ -3424,11 +3712,11 @@ async function runTaskList(cwd2, opts) {
3424
3712
  let msg = "No tasks";
3425
3713
  if (filterStatus) msg += ` with status: ${filterStatus}`;
3426
3714
  if (opts.archived) msg += " (archived)";
3427
- console.log(pc14.dim(msg + "."));
3715
+ console.log(pc15.dim(msg + "."));
3428
3716
  return;
3429
3717
  }
3430
3718
  const table = new Table2({
3431
- head: ["ID", "Slug", "Title", "Status"].map((h) => pc14.bold(h)),
3719
+ head: ["ID", "Slug", "Title", "Status"].map((h) => pc15.bold(h)),
3432
3720
  style: { head: [], border: [] }
3433
3721
  });
3434
3722
  for (const t of tasks) {
@@ -3439,7 +3727,7 @@ async function runTaskList(cwd2, opts) {
3439
3727
  if (!opts.archived && !opts.includeArchived) {
3440
3728
  const archivedTasks = await db.getArchivedTasks();
3441
3729
  if (archivedTasks.length > 0) {
3442
- console.log(pc14.dim(`${archivedTasks.length} archived task${archivedTasks.length !== 1 ? "s" : ""} (use --archived to view)`));
3730
+ console.log(pc15.dim(`${archivedTasks.length} archived task${archivedTasks.length !== 1 ? "s" : ""} (use --archived to view)`));
3443
3731
  }
3444
3732
  }
3445
3733
  } finally {
@@ -3447,25 +3735,17 @@ async function runTaskList(cwd2, opts) {
3447
3735
  }
3448
3736
  }
3449
3737
 
3450
- // src/core/package-data.ts
3451
- import { createRequire } from "module";
3452
- import { dirname as dirname5, join as join17 } from "path";
3453
- import { fileURLToPath as fileURLToPath3 } from "url";
3454
- var require2 = createRequire(import.meta.url);
3455
- var pkgPath = join17(dirname5(fileURLToPath3(import.meta.url)), "..", "package.json");
3456
- var pkg = require2(pkgPath);
3457
-
3458
3738
  // src/core/update-check.ts
3459
- import pc15 from "picocolors";
3460
- var REGISTRY_URL = `https://registry.npmjs.org/${pkg.name}/latest`;
3461
- var TIMEOUT_MS = 2500;
3739
+ import pc16 from "picocolors";
3740
+ var REGISTRY_URL2 = `https://registry.npmjs.org/${pkg.name}/latest`;
3741
+ var TIMEOUT_MS2 = 2500;
3462
3742
  function checkForUpdate(currentVersion) {
3463
3743
  return new Promise((resolve13) => {
3464
- const timer = setTimeout(() => resolve13(null), TIMEOUT_MS);
3465
- fetch(REGISTRY_URL).then((res) => res.json()).then((data) => {
3744
+ const timer = setTimeout(() => resolve13(null), TIMEOUT_MS2);
3745
+ fetch(REGISTRY_URL2).then((res) => res.json()).then((data) => {
3466
3746
  clearTimeout(timer);
3467
3747
  const latest = data.version;
3468
- resolve13(isNewer(latest, currentVersion) ? { current: currentVersion, latest } : null);
3748
+ resolve13(isNewer2(latest, currentVersion) ? { current: currentVersion, latest } : null);
3469
3749
  }).catch(() => {
3470
3750
  clearTimeout(timer);
3471
3751
  resolve13(null);
@@ -3474,12 +3754,12 @@ function checkForUpdate(currentVersion) {
3474
3754
  }
3475
3755
  function printUpdateMessage({ current, latest }) {
3476
3756
  const lines = [
3477
- ` Update available ${pc15.dim(current)} \u2192 ${pc15.green(latest)} `,
3478
- ` Run: ${pc15.cyan(`pnpm i ${pkg.name}@${latest}`)} `
3757
+ ` Update available ${pc16.dim(current)} \u2192 ${pc16.green(latest)} `,
3758
+ ` Run: ${pc16.cyan(`pnpm i ${pkg.name}@${latest}`)} `
3479
3759
  ];
3480
3760
  drawBox(lines);
3481
3761
  }
3482
- function isNewer(latest, current) {
3762
+ function isNewer2(latest, current) {
3483
3763
  const toNum = (v4) => v4.split(".").map(Number);
3484
3764
  const [lMaj, lMin, lPat] = toNum(latest);
3485
3765
  const [cMaj, cMin, cPat] = toNum(current);
@@ -3536,6 +3816,9 @@ program.command("export").description("Export the database").option("--sql", "SQ
3536
3816
  program.command("reset").description("Reset/clear harness data (DB, feature list, agent files)").option("--force", "Skip confirmation prompts").option("--provider <claude-code|opencode>", "Reset agent MD files for specified provider").action(async (opts) => {
3537
3817
  await runReset(cwd, opts);
3538
3818
  });
3819
+ program.command("doctor").description("Check lib version, agent files, and harness skills sync status").action(async () => {
3820
+ await runDoctor(cwd);
3821
+ });
3539
3822
  program.hook("postAction", async () => {
3540
3823
  const update = await updateCheck;
3541
3824
  if (update) printUpdateMessage(update);