@cardor/agent-harness-kit 1.8.1 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -456,7 +456,11 @@ If orchestrating: Agent definition files in .claude/agents/
456
456
  \`\`\`
457
457
  `;
458
458
  }
459
+ function modelField(model) {
460
+ return model ? `, model: ${JSON.stringify(model)}` : "";
461
+ }
459
462
  function configTs(params) {
463
+ const models = params.models ?? {};
460
464
  return `import { defineHarness } from '@cardor/agent-harness-kit'
461
465
 
462
466
  export default defineHarness({
@@ -469,11 +473,12 @@ export default defineHarness({
469
473
  provider: '${params.provider}',
470
474
 
471
475
  agents: {
472
- lead: { instructionsPath: null },
473
- explorer: { instructionsPath: null, allowedPaths: ['${params.docsPath}', './src'] },
474
- builder: { instructionsPath: null, writablePaths: ['./src', './tests'] },
475
- reviewer: { instructionsPath: null },
476
- custom: [],
476
+ lead: { instructionsPath: null${modelField(models.lead)} },
477
+ explorer: { instructionsPath: null, allowedPaths: ['${params.docsPath}', './src']${modelField(models.explorer)} },
478
+ builder: { instructionsPath: null, writablePaths: ['./src', './tests']${modelField(models.builder)} },
479
+ reviewer: { instructionsPath: null${modelField(models.reviewer)} },
480
+ ${models.consultant ? `consultant: { instructionsPath: null${modelField(models.consultant)} },
481
+ ` : ""}custom: [],
477
482
  },
478
483
 
479
484
  // SQLite (default). Switch to postgres/mysql by changing database.type.
@@ -508,6 +513,7 @@ export default defineHarness({
508
513
  }
509
514
  var configMjs = configTs;
510
515
  function configCjs(params) {
516
+ const models = params.models ?? {};
511
517
  return `const { defineHarness } = require('@cardor/agent-harness-kit')
512
518
 
513
519
  module.exports = defineHarness({
@@ -520,11 +526,12 @@ module.exports = defineHarness({
520
526
  provider: '${params.provider}',
521
527
 
522
528
  agents: {
523
- lead: { instructionsPath: null },
524
- explorer: { instructionsPath: null, allowedPaths: ['${params.docsPath}', './src'] },
525
- builder: { instructionsPath: null, writablePaths: ['./src', './tests'] },
526
- reviewer: { instructionsPath: null },
527
- custom: [],
529
+ lead: { instructionsPath: null${modelField(models.lead)} },
530
+ explorer: { instructionsPath: null, allowedPaths: ['${params.docsPath}', './src']${modelField(models.explorer)} },
531
+ builder: { instructionsPath: null, writablePaths: ['./src', './tests']${modelField(models.builder)} },
532
+ reviewer: { instructionsPath: null${modelField(models.reviewer)} },
533
+ ${models.consultant ? `consultant: { instructionsPath: null${modelField(models.consultant)} },
534
+ ` : ""}custom: [],
528
535
  },
529
536
 
530
537
  // SQLite (default). Switch to postgres/mysql by changing database.type.
@@ -590,11 +597,14 @@ function stripFrontmatter(md) {
590
597
  }
591
598
  return { description, body };
592
599
  }
593
- function toCodexToml(name, description, body, sandboxMode) {
600
+ function toCodexToml(name, description, body, sandboxMode, model) {
594
601
  const safe = (s) => s.replace(/"""/g, '""\\u0022');
602
+ const trimmedModel = model?.trim() ?? "";
603
+ const modelLine = trimmedModel.length >= 3 ? `model = "${trimmedModel}"
604
+ ` : "";
595
605
  return `name = "${name}"
596
606
  sandbox_mode = "${sandboxMode}"
597
-
607
+ ${modelLine}
598
608
  description = """
599
609
  ${safe(description)}
600
610
  """
@@ -606,29 +616,29 @@ ${safe(body.trimEnd())}
606
616
  }
607
617
  function agentLeadToml(vars) {
608
618
  const { description, body } = stripFrontmatter(loadAgentTemplate("lead", vars));
609
- return toCodexToml("lead", description, body, "read-only");
619
+ return toCodexToml("lead", description, body, "read-only", vars.model);
610
620
  }
611
621
  function agentLeadAsDefaultToml(vars) {
612
622
  const { description, body } = stripFrontmatter(loadAgentTemplate("lead", vars));
613
- return toCodexToml("default", description, body, "read-only");
623
+ return toCodexToml("default", description, body, "read-only", vars.model);
614
624
  }
615
625
  function agentExplorerToml(vars) {
616
626
  const { description, body } = stripFrontmatter(loadAgentTemplate("explorer", vars));
617
- return toCodexToml("explorer", description, body, "read-only");
627
+ return toCodexToml("explorer", description, body, "read-only", vars.model);
618
628
  }
619
629
  function agentBuilderToml(vars) {
620
630
  const { description, body } = stripFrontmatter(loadAgentTemplate("builder", vars));
621
- return toCodexToml("builder", description, body, "workspace-write");
631
+ return toCodexToml("builder", description, body, "workspace-write", vars.model);
622
632
  }
623
633
  function agentReviewerToml(vars) {
624
634
  const { description, body } = stripFrontmatter(loadAgentTemplate("reviewer", vars));
625
- return toCodexToml("reviewer", description, body, "read-only");
635
+ return toCodexToml("reviewer", description, body, "read-only", vars.model);
626
636
  }
627
637
  function agentConsultantToml(vars) {
628
638
  const { description, body } = stripFrontmatter(loadAgentTemplate("consultant", vars));
629
- return toCodexToml("consultant", description, body, "read-only");
639
+ return toCodexToml("consultant", description, body, "read-only", vars.model);
630
640
  }
631
- function translateFrontmatterForClaudeCode(md, agentName) {
641
+ function translateFrontmatterForClaudeCode(md, agentName, model) {
632
642
  const permissionsMap = {
633
643
  lead: [...MCP_CLAUDE_PERMISSIONS_LEAD],
634
644
  explorer: [...MCP_CLAUDE_PERMISSIONS_EXPLORER],
@@ -638,13 +648,24 @@ function translateFrontmatterForClaudeCode(md, agentName) {
638
648
  };
639
649
  const permissions = permissionsMap[agentName] ?? MCP_CLAUDE_PERMISSIONS;
640
650
  const mcpLines = permissions.map((t) => ` - ${t}`).join("\n");
641
- return md.replace(/(tools:\n(?: - (?!mcp__)[^\n]+\n)+)/, (match) => {
651
+ let result = md.replace(/(tools:\n(?: - (?!mcp__)[^\n]+\n)+)/, (match) => {
642
652
  const trimmed = match.trimEnd();
643
653
  return `${trimmed}
644
654
  - Task
645
655
  ${mcpLines}
646
656
  `;
647
657
  });
658
+ if (model) {
659
+ result = injectModelFrontmatterLine(result, model);
660
+ }
661
+ return result;
662
+ }
663
+ function injectModelFrontmatterLine(md, model) {
664
+ if (/^model:\s*.*$/m.test(md)) {
665
+ return md.replace(/^model:\s*.*$/m, `model: ${model}`);
666
+ }
667
+ return md.replace(/^(name:.*)$/m, `$1
668
+ model: ${model}`);
648
669
  }
649
670
  function translateFrontmatterForOpenCode(md) {
650
671
  return md.replace(/(tools:\n(?: - [^\n]+\n)+)/, (match) => {
@@ -680,7 +701,7 @@ function slugify(title) {
680
701
  return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
681
702
  }
682
703
  function writeSkills(cwd2, skillsDir) {
683
- const skillNames = ["ahk-ask", "ahk-consultant", "ahk-triage"];
704
+ const skillNames = ["ahk-ask", "ahk-consultant", "ahk-triage", "ahk-review"];
684
705
  for (const skillName of skillNames) {
685
706
  const src = join3(__dirname2, "skills", skillName, "SKILL.md");
686
707
  const destDir = join3(cwd2, skillsDir, skillName);
@@ -717,11 +738,16 @@ No tasks in progress.
717
738
  const projectName = config.project.name;
718
739
  const allowedPaths = (config.agents.explorer.allowedPaths ?? []).join(", ");
719
740
  const writablePaths = (config.agents.builder.writablePaths ?? []).join(", ");
720
- writeAgentFile(cwd2, ".claude/agents/lead.md", translateFrontmatterForClaudeCode(agentLead({ projectName }), "lead"));
721
- writeAgentFile(cwd2, ".claude/agents/explorer.md", translateFrontmatterForClaudeCode(agentExplorer({ projectName, allowedPaths }), "explorer"));
722
- writeAgentFile(cwd2, ".claude/agents/consultant.md", translateFrontmatterForClaudeCode(agentConsultant({ projectName }), "consultant"));
723
- writeAgentFile(cwd2, ".claude/agents/builder.md", translateFrontmatterForClaudeCode(agentBuilder({ projectName, writablePaths }), "builder"));
724
- writeAgentFile(cwd2, ".claude/agents/reviewer.md", translateFrontmatterForClaudeCode(agentReviewer({ projectName }), "reviewer"));
741
+ const leadModel = config.agents.lead.model;
742
+ const explorerModel = config.agents.explorer.model;
743
+ const consultantModel = config.agents.consultant?.model;
744
+ const builderModel = config.agents.builder.model;
745
+ const reviewerModel = config.agents.reviewer.model;
746
+ writeAgentFile(cwd2, ".claude/agents/lead.md", translateFrontmatterForClaudeCode(agentLead({ projectName }), "lead", leadModel));
747
+ writeAgentFile(cwd2, ".claude/agents/explorer.md", translateFrontmatterForClaudeCode(agentExplorer({ projectName, allowedPaths }), "explorer", explorerModel));
748
+ writeAgentFile(cwd2, ".claude/agents/consultant.md", translateFrontmatterForClaudeCode(agentConsultant({ projectName }), "consultant", consultantModel));
749
+ writeAgentFile(cwd2, ".claude/agents/builder.md", translateFrontmatterForClaudeCode(agentBuilder({ projectName, writablePaths }), "builder", builderModel));
750
+ writeAgentFile(cwd2, ".claude/agents/reviewer.md", translateFrontmatterForClaudeCode(agentReviewer({ projectName }), "reviewer", reviewerModel));
725
751
  mergeClaudeMcpJson(join4(cwd2, ".mcp.json"), config.tools.mcp.port);
726
752
  mergeClaudeSettingsJson(join4(cwd2, ".claude/settings.json"));
727
753
  mergeClaudeSettingsLocalJson(join4(cwd2, ".claude/settings.local.json"));
@@ -739,11 +765,16 @@ No tasks in progress.
739
765
  const projectName = config.project.name;
740
766
  const allowedPaths = (config.agents.explorer.allowedPaths ?? []).join(", ");
741
767
  const writablePaths = (config.agents.builder.writablePaths ?? []).join(", ");
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"));
768
+ const leadModel = config.agents.lead.model;
769
+ const explorerModel = config.agents.explorer.model;
770
+ const consultantModel = config.agents.consultant?.model;
771
+ const builderModel = config.agents.builder.model;
772
+ const reviewerModel = config.agents.reviewer.model;
773
+ write2(".claude/agents/lead.md", translateFrontmatterForClaudeCode(agentLead({ projectName }), "lead", leadModel));
774
+ write2(".claude/agents/explorer.md", translateFrontmatterForClaudeCode(agentExplorer({ projectName, allowedPaths }), "explorer", explorerModel));
775
+ write2(".claude/agents/consultant.md", translateFrontmatterForClaudeCode(agentConsultant({ projectName }), "consultant", consultantModel));
776
+ write2(".claude/agents/builder.md", translateFrontmatterForClaudeCode(agentBuilder({ projectName, writablePaths }), "builder", builderModel));
777
+ write2(".claude/agents/reviewer.md", translateFrontmatterForClaudeCode(agentReviewer({ projectName }), "reviewer", reviewerModel));
747
778
  mergeClaudeMcpJson(join4(cwd2, ".mcp.json"), config.tools.mcp.port);
748
779
  mergeClaudeSettingsJson(join4(cwd2, ".claude/settings.json"));
749
780
  mergeClaudeSettingsLocalJson(join4(cwd2, ".claude/settings.local.json"));
@@ -818,12 +849,17 @@ No tasks in progress.
818
849
  const projectName = config.project.name;
819
850
  const allowedPaths = (config.agents.explorer.allowedPaths ?? []).join(", ");
820
851
  const writablePaths = (config.agents.builder.writablePaths ?? []).join(", ");
821
- writeAgentFile(cwd2, ".codex/agents/lead.toml", agentLeadToml({ projectName }));
822
- writeAgentFile(cwd2, ".codex/agents/explorer.toml", agentExplorerToml({ projectName, allowedPaths }));
823
- writeAgentFile(cwd2, ".codex/agents/consultant.toml", agentConsultantToml({ projectName }));
824
- writeAgentFile(cwd2, ".codex/agents/builder.toml", agentBuilderToml({ projectName, writablePaths }));
825
- writeAgentFile(cwd2, ".codex/agents/reviewer.toml", agentReviewerToml({ projectName }));
826
- writeAgentFile(cwd2, ".codex/agents/default.toml", agentLeadAsDefaultToml({ projectName }));
852
+ const leadModel = config.agents.lead.model;
853
+ const explorerModel = config.agents.explorer.model;
854
+ const consultantModel = config.agents.consultant?.model;
855
+ const builderModel = config.agents.builder.model;
856
+ const reviewerModel = config.agents.reviewer.model;
857
+ writeAgentFile(cwd2, ".codex/agents/lead.toml", agentLeadToml({ projectName, model: leadModel }));
858
+ writeAgentFile(cwd2, ".codex/agents/explorer.toml", agentExplorerToml({ projectName, allowedPaths, model: explorerModel }));
859
+ writeAgentFile(cwd2, ".codex/agents/consultant.toml", agentConsultantToml({ projectName, model: consultantModel }));
860
+ writeAgentFile(cwd2, ".codex/agents/builder.toml", agentBuilderToml({ projectName, writablePaths, model: builderModel }));
861
+ writeAgentFile(cwd2, ".codex/agents/reviewer.toml", agentReviewerToml({ projectName, model: reviewerModel }));
862
+ writeAgentFile(cwd2, ".codex/agents/default.toml", agentLeadAsDefaultToml({ projectName, model: leadModel }));
827
863
  mergeCodexConfigToml(join5(cwd2, ".codex/config.toml"), config.tools.mcp.port);
828
864
  appendGitignore(cwd2);
829
865
  writeSkills(cwd2, ".agents/skills");
@@ -838,12 +874,17 @@ No tasks in progress.
838
874
  const projectName = config.project.name;
839
875
  const allowedPaths = (config.agents.explorer.allowedPaths ?? []).join(", ");
840
876
  const writablePaths = (config.agents.builder.writablePaths ?? []).join(", ");
841
- writeAgentFile(cwd2, ".codex/agents/lead.toml", agentLeadToml({ projectName }));
842
- writeAgentFile(cwd2, ".codex/agents/explorer.toml", agentExplorerToml({ projectName, allowedPaths }));
843
- writeAgentFile(cwd2, ".codex/agents/consultant.toml", agentConsultantToml({ projectName }));
844
- writeAgentFile(cwd2, ".codex/agents/builder.toml", agentBuilderToml({ projectName, writablePaths }));
845
- writeAgentFile(cwd2, ".codex/agents/reviewer.toml", agentReviewerToml({ projectName }));
846
- writeAgentFile(cwd2, ".codex/agents/default.toml", agentLeadAsDefaultToml({ projectName }));
877
+ const leadModel = config.agents.lead.model;
878
+ const explorerModel = config.agents.explorer.model;
879
+ const consultantModel = config.agents.consultant?.model;
880
+ const builderModel = config.agents.builder.model;
881
+ const reviewerModel = config.agents.reviewer.model;
882
+ writeAgentFile(cwd2, ".codex/agents/lead.toml", agentLeadToml({ projectName, model: leadModel }));
883
+ writeAgentFile(cwd2, ".codex/agents/explorer.toml", agentExplorerToml({ projectName, allowedPaths, model: explorerModel }));
884
+ writeAgentFile(cwd2, ".codex/agents/consultant.toml", agentConsultantToml({ projectName, model: consultantModel }));
885
+ writeAgentFile(cwd2, ".codex/agents/builder.toml", agentBuilderToml({ projectName, writablePaths, model: builderModel }));
886
+ writeAgentFile(cwd2, ".codex/agents/reviewer.toml", agentReviewerToml({ projectName, model: reviewerModel }));
887
+ writeAgentFile(cwd2, ".codex/agents/default.toml", agentLeadAsDefaultToml({ projectName, model: leadModel }));
847
888
  mergeCodexConfigToml(join5(cwd2, ".codex/config.toml"), config.tools.mcp.port);
848
889
  writeSkills(cwd2, ".agents/skills");
849
890
  }
@@ -1834,23 +1875,26 @@ async function runDashboard(cwd2, opts) {
1834
1875
  import pc3 from "picocolors";
1835
1876
 
1836
1877
  // src/core/doctor.ts
1837
- import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
1878
+ import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
1838
1879
  import { dirname as dirname7, join as join11 } from "path";
1839
1880
  import { fileURLToPath as fileURLToPath5 } from "url";
1840
1881
 
1841
1882
  // src/core/package-data.ts
1883
+ import { existsSync as existsSync7 } from "fs";
1842
1884
  import { createRequire } from "module";
1843
1885
  import { dirname as dirname6, join as join10 } from "path";
1844
1886
  import { fileURLToPath as fileURLToPath4 } from "url";
1845
1887
  var require2 = createRequire(import.meta.url);
1846
- var pkgPath = join10(dirname6(fileURLToPath4(import.meta.url)), "..", "package.json");
1888
+ var here = dirname6(fileURLToPath4(import.meta.url));
1889
+ var candidates = [join10(here, "..", "..", "package.json"), join10(here, "..", "package.json")];
1890
+ var pkgPath = candidates.find((p8) => existsSync7(p8)) ?? candidates[0];
1847
1891
  var pkg = require2(pkgPath);
1848
1892
 
1849
1893
  // src/core/doctor.ts
1850
1894
  var REGISTRY_URL = `https://registry.npmjs.org/${pkg.name}/latest`;
1851
1895
  var TIMEOUT_MS = 2e3;
1852
1896
  var AGENT_NAMES = ["lead", "explorer", "consultant", "builder", "reviewer"];
1853
- var SKILL_NAMES = ["ahk-ask", "ahk-consultant", "ahk-triage"];
1897
+ var SKILL_NAMES = ["ahk-ask", "ahk-consultant", "ahk-triage", "ahk-review"];
1854
1898
  var __dirname4 = dirname7(fileURLToPath5(import.meta.url));
1855
1899
  async function checkLibVersion() {
1856
1900
  const current = pkg.version;
@@ -1888,7 +1932,7 @@ function getProviderAgentInfo(provider) {
1888
1932
  }
1889
1933
  }
1890
1934
  function generateExpectedAgentContent(agentName, provider, vars) {
1891
- const { projectName, allowedPaths, writablePaths } = vars;
1935
+ const { projectName, allowedPaths, writablePaths, model } = vars;
1892
1936
  if (provider === "claude-code") {
1893
1937
  const templateFns = {
1894
1938
  lead: () => agentLead({ projectName }),
@@ -1897,7 +1941,7 @@ function generateExpectedAgentContent(agentName, provider, vars) {
1897
1941
  builder: () => agentBuilder({ projectName, writablePaths }),
1898
1942
  reviewer: () => agentReviewer({ projectName })
1899
1943
  };
1900
- return translateFrontmatterForClaudeCode(templateFns[agentName](), agentName);
1944
+ return translateFrontmatterForClaudeCode(templateFns[agentName](), agentName, model);
1901
1945
  }
1902
1946
  if (provider === "opencode") {
1903
1947
  const templateFns = {
@@ -1910,19 +1954,19 @@ function generateExpectedAgentContent(agentName, provider, vars) {
1910
1954
  return translateFrontmatterForOpenCode(templateFns[agentName]());
1911
1955
  }
1912
1956
  const tomlFns = {
1913
- lead: () => agentLeadToml({ projectName }),
1914
- explorer: () => agentExplorerToml({ projectName, allowedPaths }),
1915
- consultant: () => agentConsultantToml({ projectName }),
1916
- builder: () => agentBuilderToml({ projectName, writablePaths }),
1917
- reviewer: () => agentReviewerToml({ projectName })
1957
+ lead: () => agentLeadToml({ projectName, model }),
1958
+ explorer: () => agentExplorerToml({ projectName, allowedPaths, model }),
1959
+ consultant: () => agentConsultantToml({ projectName, model }),
1960
+ builder: () => agentBuilderToml({ projectName, writablePaths, model }),
1961
+ reviewer: () => agentReviewerToml({ projectName, model })
1918
1962
  };
1919
1963
  return tomlFns[agentName]();
1920
1964
  }
1921
- function checkAgentFiles(cwd2, provider, projectName, allowedPaths, writablePaths) {
1965
+ function checkAgentFiles(cwd2, provider, projectName, allowedPaths, writablePaths, models) {
1922
1966
  const { agentsDir, ext } = getProviderAgentInfo(provider);
1923
1967
  return AGENT_NAMES.map((name) => {
1924
1968
  const filePath = join11(cwd2, agentsDir, `${name}${ext}`);
1925
- if (!existsSync7(filePath)) {
1969
+ if (!existsSync8(filePath)) {
1926
1970
  return { name, status: "missing" };
1927
1971
  }
1928
1972
  try {
@@ -1930,7 +1974,8 @@ function checkAgentFiles(cwd2, provider, projectName, allowedPaths, writablePath
1930
1974
  const expected = generateExpectedAgentContent(name, provider, {
1931
1975
  projectName,
1932
1976
  allowedPaths,
1933
- writablePaths
1977
+ writablePaths,
1978
+ model: models[name]
1934
1979
  });
1935
1980
  return { name, status: live === expected ? "ok" : "outdated" };
1936
1981
  } catch {
@@ -1956,7 +2001,7 @@ function checkSkills(cwd2, provider) {
1956
2001
  return SKILL_NAMES.map((name) => {
1957
2002
  const livePath = join11(cwd2, skillsDir, name, "SKILL.md");
1958
2003
  const sourcePath = join11(skillSourceBase, name, "SKILL.md");
1959
- if (!existsSync7(livePath)) {
2004
+ if (!existsSync8(livePath)) {
1960
2005
  return { name, status: "missing" };
1961
2006
  }
1962
2007
  try {
@@ -1984,7 +2029,14 @@ async function getDoctorStatus(cwd2) {
1984
2029
  const projectName = config.project.name;
1985
2030
  const allowedPaths = (config.agents.explorer.allowedPaths ?? []).join(", ");
1986
2031
  const writablePaths = (config.agents.builder.writablePaths ?? []).join(", ");
1987
- const agents = checkAgentFiles(cwd2, provider, projectName, allowedPaths, writablePaths);
2032
+ const models = {
2033
+ lead: config.agents.lead.model,
2034
+ explorer: config.agents.explorer.model,
2035
+ consultant: config.agents.consultant?.model,
2036
+ builder: config.agents.builder.model,
2037
+ reviewer: config.agents.reviewer.model
2038
+ };
2039
+ const agents = checkAgentFiles(cwd2, provider, projectName, allowedPaths, writablePaths, models);
1988
2040
  const skills = checkSkills(cwd2, provider);
1989
2041
  return { lib, agents, skills };
1990
2042
  }
@@ -2105,7 +2157,7 @@ async function runExport(cwd2, opts) {
2105
2157
 
2106
2158
  // src/commands/health.ts
2107
2159
  import { spawnSync } from "child_process";
2108
- import { existsSync as existsSync8 } from "fs";
2160
+ import { existsSync as existsSync9 } from "fs";
2109
2161
  import { join as join12, resolve as resolve8 } from "path";
2110
2162
  import pc5 from "picocolors";
2111
2163
  function checkLine(label, ok3, message, indent = 0) {
@@ -2125,7 +2177,7 @@ async function runHealth(cwd2) {
2125
2177
  let dbOk;
2126
2178
  if (config.database.type === "sqlite") {
2127
2179
  const dbPath = resolve8(cwd2, config.database.path);
2128
- dbOk = existsSync8(dbPath);
2180
+ dbOk = existsSync9(dbPath);
2129
2181
  checkLine("checking DB", dbOk, `${config.database.path} reachable`);
2130
2182
  } else {
2131
2183
  dbOk = true;
@@ -2139,7 +2191,7 @@ async function runHealth(cwd2) {
2139
2191
  for (let i = 0; i < agentNames.length; i++) {
2140
2192
  const name = agentNames[i];
2141
2193
  const agentPath = join12(cwd2, agentsDir, `${name}${providerFiles.agentExtension}`);
2142
- const ok3 = existsSync8(agentPath);
2194
+ const ok3 = existsSync9(agentPath);
2143
2195
  checkLine(
2144
2196
  i === 0 ? "checking agents" : null,
2145
2197
  ok3,
@@ -2151,7 +2203,7 @@ async function runHealth(cwd2) {
2151
2203
  if (config.tools.mcp.enabled) {
2152
2204
  const mcpFile = providerFiles.mcpFile;
2153
2205
  const mcpPath = resolve8(cwd2, mcpFile);
2154
- const mcpOk = existsSync8(mcpPath);
2206
+ const mcpOk = existsSync9(mcpPath);
2155
2207
  checkLine("checking MCP", mcpOk, `${mcpFile} valid`);
2156
2208
  if (!mcpOk) allOk = false;
2157
2209
  }
@@ -2161,7 +2213,7 @@ async function runHealth(cwd2) {
2161
2213
  process.exit(1);
2162
2214
  }
2163
2215
  const scriptPath = resolve8(cwd2, config.health.scriptPath);
2164
- if (!existsSync8(scriptPath)) {
2216
+ if (!existsSync9(scriptPath)) {
2165
2217
  console.error(pc5.red(`\u2717 health.sh not found: ${scriptPath}`));
2166
2218
  console.error(" Run ahk init first.");
2167
2219
  process.exit(1);
@@ -2253,13 +2305,13 @@ var cliFormWithRetry = async (formFn, schema) => {
2253
2305
  };
2254
2306
 
2255
2307
  // src/commands/init-helpers.ts
2256
- import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
2308
+ import { existsSync as existsSync10, readFileSync as readFileSync7 } from "fs";
2257
2309
  import { join as join13 } from "path";
2258
2310
  import pc6 from "picocolors";
2259
2311
  function readProjectNameFromPackageJson(cwd2) {
2260
2312
  try {
2261
2313
  const pkgPath2 = join13(cwd2, "package.json");
2262
- if (!existsSync9(pkgPath2)) return null;
2314
+ if (!existsSync10(pkgPath2)) return null;
2263
2315
  const content = readFileSync7(pkgPath2, "utf8");
2264
2316
  const pkg2 = JSON.parse(content);
2265
2317
  const name = pkg2?.name;
@@ -2271,9 +2323,9 @@ function readProjectNameFromPackageJson(cwd2) {
2271
2323
  }
2272
2324
  function detectConfigExtension(cwd2) {
2273
2325
  try {
2274
- if (existsSync9(join13(cwd2, "tsconfig.json"))) return "ts";
2326
+ if (existsSync10(join13(cwd2, "tsconfig.json"))) return "ts";
2275
2327
  const pkgPath2 = join13(cwd2, "package.json");
2276
- if (!existsSync9(pkgPath2)) return "mjs";
2328
+ if (!existsSync10(pkgPath2)) return "mjs";
2277
2329
  const pkg2 = JSON.parse(readFileSync7(pkgPath2, "utf8"));
2278
2330
  if (pkg2?.type === "module") return "mjs";
2279
2331
  } catch {
@@ -2281,6 +2333,7 @@ function detectConfigExtension(cwd2) {
2281
2333
  return "mjs";
2282
2334
  }
2283
2335
  function applyConfigDefaults(params) {
2336
+ const models = params.models ?? {};
2284
2337
  return {
2285
2338
  provider: params.provider,
2286
2339
  project: {
@@ -2290,10 +2343,19 @@ function applyConfigDefaults(params) {
2290
2343
  agentsMd: "./AGENTS.md"
2291
2344
  },
2292
2345
  agents: {
2293
- lead: { instructionsPath: null },
2294
- explorer: { instructionsPath: null, allowedPaths: [params.docsPath, "./src"] },
2295
- builder: { instructionsPath: null, writablePaths: ["./src", "./tests"] },
2296
- reviewer: { instructionsPath: null },
2346
+ lead: { instructionsPath: null, ...models.lead && { model: models.lead } },
2347
+ explorer: {
2348
+ instructionsPath: null,
2349
+ allowedPaths: [params.docsPath, "./src"],
2350
+ ...models.explorer && { model: models.explorer }
2351
+ },
2352
+ builder: {
2353
+ instructionsPath: null,
2354
+ writablePaths: ["./src", "./tests"],
2355
+ ...models.builder && { model: models.builder }
2356
+ },
2357
+ reviewer: { instructionsPath: null, ...models.reviewer && { model: models.reviewer } },
2358
+ ...models.consultant && { consultant: { instructionsPath: null, model: models.consultant } },
2297
2359
  custom: []
2298
2360
  },
2299
2361
  database: { type: "sqlite", path: ".harness/harness.db" },
@@ -2420,6 +2482,61 @@ async function runInit(cwd2, flags) {
2420
2482
  }
2421
2483
  provider = val;
2422
2484
  }
2485
+ const AGENT_LABELS = [
2486
+ { key: "lead", label: "Lead" },
2487
+ { key: "explorer", label: "Explorer" },
2488
+ { key: "consultant", label: "Consultant" },
2489
+ { key: "builder", label: "Builder" },
2490
+ { key: "reviewer", label: "Reviewer" }
2491
+ ];
2492
+ const modelOverrides = {};
2493
+ if (provider === "claude-code" || provider === "codex-cli") {
2494
+ const wantsModelCustomization = await p3.confirm({
2495
+ message: "\xBFPersonalizar el modelo por agente?",
2496
+ initialValue: false
2497
+ });
2498
+ if (p3.isCancel(wantsModelCustomization)) {
2499
+ p3.cancel("Cancelled.");
2500
+ process.exit(0);
2501
+ }
2502
+ if (wantsModelCustomization) {
2503
+ if (provider === "claude-code") {
2504
+ for (const agent of AGENT_LABELS) {
2505
+ const val = await p3.select({
2506
+ message: `Modelo para ${agent.label}`,
2507
+ options: [
2508
+ { value: "inherit", label: "inherit (default)" },
2509
+ { value: "haiku", label: "haiku" },
2510
+ { value: "sonnet", label: "sonnet" },
2511
+ { value: "opus", label: "opus" },
2512
+ { value: "fable", label: "fable" }
2513
+ ],
2514
+ initialValue: "inherit"
2515
+ });
2516
+ if (p3.isCancel(val)) {
2517
+ p3.cancel("Cancelled.");
2518
+ process.exit(0);
2519
+ }
2520
+ modelOverrides[agent.key] = val;
2521
+ }
2522
+ } else {
2523
+ for (const agent of AGENT_LABELS) {
2524
+ const val = await p3.text({
2525
+ message: `Modelo para ${agent.label} (Codex no valida este valor)`,
2526
+ placeholder: "ej. gpt-5 (vac\xEDo o <3 caracteres = sin override)"
2527
+ });
2528
+ if (p3.isCancel(val)) {
2529
+ p3.cancel("Cancelled.");
2530
+ process.exit(0);
2531
+ }
2532
+ const trimmed = val.trim();
2533
+ if (trimmed.length >= 3) {
2534
+ modelOverrides[agent.key] = trimmed;
2535
+ }
2536
+ }
2537
+ }
2538
+ }
2539
+ }
2423
2540
  let docsPath;
2424
2541
  if (flags.docs) {
2425
2542
  docsPath = flags.docs;
@@ -2493,7 +2610,7 @@ async function runInit(cwd2, flags) {
2493
2610
  const spinner6 = p3.spinner();
2494
2611
  spinner6.start("Scaffolding...");
2495
2612
  try {
2496
- const config = applyConfigDefaults({ name, description, provider, docsPath, tasksAdapter });
2613
+ const config = applyConfigDefaults({ name, description, provider, docsPath, tasksAdapter, models: modelOverrides });
2497
2614
  const materializer = getMaterializer(provider);
2498
2615
  const installDir = cwd2;
2499
2616
  configExt = detectConfigExtension(cwd2);
@@ -2505,7 +2622,8 @@ async function runInit(cwd2, flags) {
2505
2622
  provider,
2506
2623
  docsPath,
2507
2624
  tasksAdapter,
2508
- port: config.tools.mcp.port
2625
+ port: config.tools.mcp.port,
2626
+ models: modelOverrides
2509
2627
  });
2510
2628
  writeFileSync9(join14(installDir, configFileName), configContent, "utf8");
2511
2629
  mkdirSync8(join14(installDir, config.storage.dir), { recursive: true });
@@ -2599,7 +2717,7 @@ async function runMigrate(cwd2, opts) {
2599
2717
  }
2600
2718
 
2601
2719
  // src/commands/reset.ts
2602
- import { existsSync as existsSync10, readdirSync, rmSync } from "fs";
2720
+ import { existsSync as existsSync11, readdirSync, rmSync } from "fs";
2603
2721
  import { join as join15, resolve as resolve9 } from "path";
2604
2722
  import * as p5 from "@clack/prompts";
2605
2723
  import pc9 from "picocolors";
@@ -2607,7 +2725,7 @@ var AGENT_MD_FILES = ["lead", "explorer", "consultant", "builder", "reviewer"];
2607
2725
  async function resetAgentMds(cwd2, provider) {
2608
2726
  const agentDir = provider === "claude-code" ? ".claude/agents" : ".opencode/agents";
2609
2727
  const agentDirPath = resolve9(cwd2, agentDir);
2610
- if (!existsSync10(agentDirPath)) {
2728
+ if (!existsSync11(agentDirPath)) {
2611
2729
  console.log(pc9.yellow(` Skipping agent files \u2014 directory not found: ${agentDirPath}`));
2612
2730
  return;
2613
2731
  }
@@ -2663,7 +2781,7 @@ async function runReset(cwd2, opts) {
2663
2781
  let resetDb = false;
2664
2782
  let resetFeatureList = false;
2665
2783
  let resetAgentMdsFlag = false;
2666
- if (dbPath && existsSync10(dbPath)) {
2784
+ if (dbPath && existsSync11(dbPath)) {
2667
2785
  if (opts.force) {
2668
2786
  resetDb = true;
2669
2787
  } else {
@@ -2685,7 +2803,7 @@ async function runReset(cwd2, opts) {
2685
2803
  } else if (!dbPath) {
2686
2804
  console.log(pc9.dim(` Skipping DB reset \u2014 remote ${config.database.type} database is not managed by this command.`));
2687
2805
  }
2688
- if (existsSync10(featureListPath)) {
2806
+ if (existsSync11(featureListPath)) {
2689
2807
  if (opts.force) {
2690
2808
  resetFeatureList = true;
2691
2809
  } else {
@@ -2734,7 +2852,7 @@ async function runReset(cwd2, opts) {
2734
2852
  }
2735
2853
 
2736
2854
  // src/core/mcp-server.ts
2737
- import { existsSync as existsSync12, mkdirSync as mkdirSync9, readdirSync as readdirSync2, readFileSync as readFileSync9, statSync, writeFileSync as writeFileSync10 } from "fs";
2855
+ import { existsSync as existsSync13, mkdirSync as mkdirSync9, readdirSync as readdirSync2, readFileSync as readFileSync9, statSync, writeFileSync as writeFileSync10 } from "fs";
2738
2856
  import { join as join17, resolve as resolve10 } from "path";
2739
2857
  import { Server } from "@modelcontextprotocol/sdk/server";
2740
2858
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -2744,7 +2862,7 @@ import {
2744
2862
  } from "@modelcontextprotocol/sdk/types.js";
2745
2863
 
2746
2864
  // src/core/permissions-check.ts
2747
- import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
2865
+ import { existsSync as existsSync12, readFileSync as readFileSync8 } from "fs";
2748
2866
  import { join as join16 } from "path";
2749
2867
  var CANONICAL = {
2750
2868
  lead: [...MCP_CLAUDE_PERMISSIONS_LEAD],
@@ -2769,7 +2887,7 @@ function checkPermissionsSync(cwd2, config) {
2769
2887
  let in_sync = true;
2770
2888
  for (const agent of ["lead", "explorer", "consultant", "builder", "reviewer"]) {
2771
2889
  const filePath = join16(cwd2, ".claude", "agents", `${agent}.md`);
2772
- if (!existsSync11(filePath)) {
2890
+ if (!existsSync12(filePath)) {
2773
2891
  const missing2 = CANONICAL[agent];
2774
2892
  agents[agent] = { ok: false, missing: missing2, extra: [] };
2775
2893
  in_sync = false;
@@ -3196,7 +3314,7 @@ async function dispatch(name, args, db, docsPath, cwd2, config) {
3196
3314
  }
3197
3315
  case "deps.snapshot": {
3198
3316
  const pkgPath2 = join17(cwd2, "package.json");
3199
- if (!existsSync12(pkgPath2)) {
3317
+ if (!existsSync13(pkgPath2)) {
3200
3318
  return ok2("package.json not found in project root", true);
3201
3319
  }
3202
3320
  const pkg2 = JSON.parse(readFileSync9(pkgPath2, "utf8"));
@@ -3218,10 +3336,10 @@ async function dispatch(name, args, db, docsPath, cwd2, config) {
3218
3336
  case "deps.check": {
3219
3337
  const pkgPath2 = join17(cwd2, "package.json");
3220
3338
  const lockPath = join17(cwd2, ".harness", "deps-lock.json");
3221
- if (!existsSync12(pkgPath2)) {
3339
+ if (!existsSync13(pkgPath2)) {
3222
3340
  return ok2("package.json not found in project root", true);
3223
3341
  }
3224
- if (!existsSync12(lockPath)) {
3342
+ if (!existsSync13(lockPath)) {
3225
3343
  return ok2(
3226
3344
  JSON.stringify({
3227
3345
  status: "no-snapshot",
@@ -3446,7 +3564,7 @@ async function runStatus(cwd2, opts) {
3446
3564
  }
3447
3565
 
3448
3566
  // src/commands/sync.ts
3449
- import { existsSync as existsSync13, readFileSync as readFileSync10 } from "fs";
3567
+ import { existsSync as existsSync14, readFileSync as readFileSync10 } from "fs";
3450
3568
  import { join as join18, resolve as resolve11 } from "path";
3451
3569
  import pc11 from "picocolors";
3452
3570
  async function runSync(cwd2, opts) {
@@ -3466,7 +3584,7 @@ async function runSync(cwd2, opts) {
3466
3584
  }
3467
3585
  }
3468
3586
  async function syncIn(featureListPath, db, dryRun) {
3469
- if (!existsSync13(featureListPath)) {
3587
+ if (!existsSync14(featureListPath)) {
3470
3588
  console.log(pc11.dim(`feature_list.json not found at ${featureListPath} \u2014 skipping in-sync`));
3471
3589
  return;
3472
3590
  }
@@ -3557,14 +3675,14 @@ async function runTaskAdd(cwd2) {
3557
3675
 
3558
3676
  // src/commands/task/done.ts
3559
3677
  import { spawnSync as spawnSync2 } from "child_process";
3560
- import { existsSync as existsSync14 } from "fs";
3678
+ import { existsSync as existsSync15 } from "fs";
3561
3679
  import { resolve as resolve12 } from "path";
3562
3680
  import pc13 from "picocolors";
3563
3681
  async function runTaskDone(cwd2, idOrSlug) {
3564
3682
  const config = await loadConfig(cwd2);
3565
3683
  if (config.health.required) {
3566
3684
  const scriptPath = resolve12(cwd2, config.health.scriptPath);
3567
- if (existsSync14(scriptPath)) {
3685
+ if (existsSync15(scriptPath)) {
3568
3686
  const result = spawnSync2("bash", [scriptPath], { cwd: cwd2, stdio: "pipe", encoding: "utf8" });
3569
3687
  if (result.status !== 0) {
3570
3688
  console.error(pc13.red("\u2717 Health check failed \u2014 cannot mark task as done."));