@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/LICENSE +80 -58
- package/README.md +26 -20
- package/dist/cli.js +205 -87
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/skills/ahk-review/SKILL.md +45 -0
- package/package.json +2 -2
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
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
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
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
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
writeAgentFile(cwd2, ".codex/agents/
|
|
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
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
writeAgentFile(cwd2, ".codex/agents/
|
|
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
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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 (
|
|
2326
|
+
if (existsSync10(join13(cwd2, "tsconfig.json"))) return "ts";
|
|
2275
2327
|
const pkgPath2 = join13(cwd2, "package.json");
|
|
2276
|
-
if (!
|
|
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: {
|
|
2295
|
-
|
|
2296
|
-
|
|
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
|
|
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 (!
|
|
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 &&
|
|
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 (
|
|
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
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
3339
|
+
if (!existsSync13(pkgPath2)) {
|
|
3222
3340
|
return ok2("package.json not found in project root", true);
|
|
3223
3341
|
}
|
|
3224
|
-
if (!
|
|
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
|
|
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 (!
|
|
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
|
|
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 (
|
|
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."));
|