@harness-engineering/cli 4.1.0 → 4.3.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/agents/skills/claude-code/harness-autopilot/SKILL.md +31 -0
- package/dist/agents/skills/claude-code/harness-catalog-retrospective/SKILL.md +124 -0
- package/dist/agents/skills/claude-code/harness-catalog-retrospective/skill.yaml +48 -0
- package/dist/agents/skills/claude-code/harness-execution/SKILL.md +13 -0
- package/dist/agents/skills/claude-code/harness-parallel-agents/SKILL.md +3 -0
- package/dist/agents/skills/claude-code/harness-planning/SKILL.md +3 -1
- package/dist/agents/skills/codex/harness-autopilot/SKILL.md +31 -0
- package/dist/agents/skills/codex/harness-catalog-retrospective/SKILL.md +124 -0
- package/dist/agents/skills/codex/harness-catalog-retrospective/skill.yaml +48 -0
- package/dist/agents/skills/codex/harness-execution/SKILL.md +13 -0
- package/dist/agents/skills/codex/harness-parallel-agents/SKILL.md +3 -0
- package/dist/agents/skills/codex/harness-planning/SKILL.md +3 -1
- package/dist/agents/skills/cursor/harness-autopilot/SKILL.md +31 -0
- package/dist/agents/skills/cursor/harness-catalog-retrospective/SKILL.md +124 -0
- package/dist/agents/skills/cursor/harness-catalog-retrospective/skill.yaml +48 -0
- package/dist/agents/skills/cursor/harness-execution/SKILL.md +13 -0
- package/dist/agents/skills/cursor/harness-parallel-agents/SKILL.md +3 -0
- package/dist/agents/skills/cursor/harness-planning/SKILL.md +3 -1
- package/dist/agents/skills/gemini-cli/harness-autopilot/SKILL.md +31 -0
- package/dist/agents/skills/gemini-cli/harness-catalog-retrospective/SKILL.md +124 -0
- package/dist/agents/skills/gemini-cli/harness-catalog-retrospective/skill.yaml +48 -0
- package/dist/agents/skills/gemini-cli/harness-execution/SKILL.md +13 -0
- package/dist/agents/skills/gemini-cli/harness-parallel-agents/SKILL.md +3 -0
- package/dist/agents/skills/gemini-cli/harness-planning/SKILL.md +3 -1
- package/dist/{agents-md-7L4PKKOM.js → agents-md-N4ULMUAK.js} +4 -4
- package/dist/{analyzer-XRCV63KC-HLTEIKAJ.js → analyzer-XRCV63KC-WGRHW2ZM.js} +2 -2
- package/dist/{architecture-RCHFDI7E.js → architecture-OIMIBDJC.js} +5 -5
- package/dist/{assess-project-YUDPNRS6.js → assess-project-ITSB6W4L.js} +1 -1
- package/dist/bin/harness-mcp.js +15 -15
- package/dist/bin/harness.js +26 -26
- package/dist/{check-phase-gate-56GS3A72.js → check-phase-gate-GN4X7HT6.js} +5 -5
- package/dist/{chunk-N4AI2WFH.js → chunk-574JCBYH.js} +2 -2
- package/dist/{chunk-RY2U77OY.js → chunk-5BUHPOCP.js} +7 -7
- package/dist/{chunk-IGWJRAJT.js → chunk-6NEJPS7W.js} +2 -2
- package/dist/{chunk-NCMJXBEO.js → chunk-73QZ43DF.js} +2 -2
- package/dist/{chunk-YJYUJSWK.js → chunk-7RH2NTLA.js} +1 -1
- package/dist/{chunk-XZNCONFG.js → chunk-CMU4DNLY.js} +1 -1
- package/dist/{chunk-UACYZCW4.js → chunk-CRHQYBDA.js} +396 -13
- package/dist/{chunk-4SKWGVYD.js → chunk-D7A5JR7M.js} +2 -2
- package/dist/{chunk-P4NA5OQX.js → chunk-GTHL3TRZ.js} +2 -2
- package/dist/{chunk-ZU6T7W3U.js → chunk-H5GN7R43.js} +2 -2
- package/dist/{chunk-YXOPQEFZ.js → chunk-HJYAXAYQ.js} +2 -2
- package/dist/{chunk-Z4XWWNYX.js → chunk-HSYU3RUW.js} +203 -92
- package/dist/{chunk-Y33P35U3.js → chunk-JZL2O3RC.js} +5 -5
- package/dist/{chunk-XW5XVVMI.js → chunk-LP764MGR.js} +3 -3
- package/dist/{chunk-Y5HPJMTM.js → chunk-LTYVAKWK.js} +2 -2
- package/dist/{chunk-DGVT4DBI.js → chunk-MEDRUIRO.js} +3 -3
- package/dist/{chunk-GSYBGOY3.js → chunk-MIZQY35D.js} +2 -2
- package/dist/{chunk-YWRPQZL7.js → chunk-NEMB36N4.js} +2 -2
- package/dist/{chunk-GLHLXTKC.js → chunk-PT65VY4S.js} +6 -6
- package/dist/{chunk-AU4YPEIE.js → chunk-T46G6YNI.js} +3 -3
- package/dist/{chunk-ABRYIEWJ.js → chunk-TDPQZAEE.js} +1069 -853
- package/dist/{chunk-VG4G3R2V.js → chunk-TKPAOB2T.js} +2 -2
- package/dist/{chunk-RZQZXWEL.js → chunk-U2NOOATF.js} +6 -6
- package/dist/{chunk-BDGTZRZ6.js → chunk-VJDSB7CI.js} +159 -85
- package/dist/{chunk-OZSKLJQW.js → chunk-XKILFNRK.js} +2 -2
- package/dist/{ci-workflow-TFAA5BK7.js → ci-workflow-PA3WL6WK.js} +4 -4
- package/dist/{dist-MCTEX5QH.js → dist-BWFFVKEW.js} +34 -4
- package/dist/{dist-ICW4QUEW.js → dist-ISDDHDOR.js} +19 -3
- package/dist/{docs-BBMAN3CY.js → docs-HOTWQJQF.js} +5 -5
- package/dist/{engine-R2YH74AL.js → engine-MVG2SPGD.js} +4 -4
- package/dist/{entropy-5UP5N6AF.js → entropy-3B4U2BTF.js} +5 -5
- package/dist/{feedback-6MDCCFZL.js → feedback-WIWWDVUC.js} +1 -1
- package/dist/{generate-agent-definitions-KKAN5DB5.js → generate-agent-definitions-KPRHY6X3.js} +5 -5
- package/dist/index.js +25 -25
- package/dist/{loader-2WT2ZE2T.js → loader-SPJ2ZGYW.js} +4 -4
- package/dist/{mcp-5V4LPMB3.js → mcp-RIPEYDMU.js} +15 -15
- package/dist/{performance-2N3CFXLR.js → performance-JYFS27B3.js} +5 -5
- package/dist/{review-pipeline-XZIGBU3Q.js → review-pipeline-BDT6Y6QV.js} +4 -4
- package/dist/{runtime-POME3GTR.js → runtime-ZEPHPPQJ.js} +4 -4
- package/dist/{security-QLJLMSIW.js → security-FWSDTTEW.js} +1 -1
- package/dist/{state-events-YY5KBZTC.js → state-events-PBDNQPXT.js} +4 -4
- package/dist/{validate-VZORVK43.js → validate-XIMNCIKR.js} +5 -5
- package/dist/{validate-cross-check-OYKEDCST.js → validate-cross-check-H2OUHTID.js} +4 -4
- package/package.json +8 -8
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import {
|
|
2
2
|
generateRuntime
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-574JCBYH.js";
|
|
4
4
|
import {
|
|
5
5
|
generateAgentsMd
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-TKPAOB2T.js";
|
|
7
7
|
import {
|
|
8
8
|
generateCIWorkflow
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-GTHL3TRZ.js";
|
|
10
10
|
import {
|
|
11
11
|
readHarnessState
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-NEMB36N4.js";
|
|
13
13
|
import {
|
|
14
14
|
runCrossCheck
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-MIZQY35D.js";
|
|
16
16
|
import {
|
|
17
17
|
createGenerateAgentDefinitionsCommand,
|
|
18
18
|
generateAgentDefinitions
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-CMU4DNLY.js";
|
|
20
20
|
import {
|
|
21
21
|
toKebabCase
|
|
22
22
|
} from "./chunk-KET4QQZB.js";
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
} from "./chunk-YYFSFSB3.js";
|
|
27
27
|
import {
|
|
28
28
|
TemplateEngine
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-6NEJPS7W.js";
|
|
30
30
|
import {
|
|
31
31
|
generate,
|
|
32
32
|
validate
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
import {
|
|
38
38
|
listPersonas,
|
|
39
39
|
loadPersona
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-73QZ43DF.js";
|
|
41
41
|
import {
|
|
42
42
|
runPersona
|
|
43
43
|
} from "./chunk-GISMXMVL.js";
|
|
@@ -51,7 +51,7 @@ import {
|
|
|
51
51
|
OutputFormatter,
|
|
52
52
|
OutputMode,
|
|
53
53
|
createCheckPhaseGateCommand
|
|
54
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-LP764MGR.js";
|
|
55
55
|
import {
|
|
56
56
|
appendFrameworkAgents,
|
|
57
57
|
captureHealthSnapshot,
|
|
@@ -79,7 +79,7 @@ import {
|
|
|
79
79
|
runSecurityCraft,
|
|
80
80
|
runSpecCraft,
|
|
81
81
|
runTestCraft
|
|
82
|
-
} from "./chunk-
|
|
82
|
+
} from "./chunk-HSYU3RUW.js";
|
|
83
83
|
import {
|
|
84
84
|
VALID_PLATFORMS
|
|
85
85
|
} from "./chunk-3ISINLYT.js";
|
|
@@ -108,7 +108,7 @@ import {
|
|
|
108
108
|
BranchingConfigSchema,
|
|
109
109
|
findConfigFile,
|
|
110
110
|
resolveConfig
|
|
111
|
-
} from "./chunk-
|
|
111
|
+
} from "./chunk-LTYVAKWK.js";
|
|
112
112
|
import {
|
|
113
113
|
logger
|
|
114
114
|
} from "./chunk-EBJQ6N4M.js";
|
|
@@ -225,7 +225,7 @@ import {
|
|
|
225
225
|
writeConfig,
|
|
226
226
|
writeLockfile,
|
|
227
227
|
writeRegeneratedRoadmap
|
|
228
|
-
} from "./chunk-
|
|
228
|
+
} from "./chunk-CRHQYBDA.js";
|
|
229
229
|
import {
|
|
230
230
|
CLI_VERSION
|
|
231
231
|
} from "./chunk-BM3PWGXQ.js";
|
|
@@ -238,14 +238,14 @@ import {
|
|
|
238
238
|
detectDocDrift,
|
|
239
239
|
generateSuggestions,
|
|
240
240
|
validateDependencies
|
|
241
|
-
} from "./chunk-
|
|
241
|
+
} from "./chunk-7RH2NTLA.js";
|
|
242
242
|
import {
|
|
243
243
|
Err,
|
|
244
244
|
INDEXED_FILE_KINDS,
|
|
245
245
|
INSIGHTS_KEYS,
|
|
246
246
|
Ok,
|
|
247
247
|
init_dist
|
|
248
|
-
} from "./chunk-
|
|
248
|
+
} from "./chunk-VJDSB7CI.js";
|
|
249
249
|
import {
|
|
250
250
|
DEFAULT_SKIP_DIRS,
|
|
251
251
|
DesignConstraintAdapter,
|
|
@@ -434,6 +434,8 @@ function createAddCommand() {
|
|
|
434
434
|
}
|
|
435
435
|
|
|
436
436
|
// src/commands/adoption.ts
|
|
437
|
+
import * as fs2 from "fs";
|
|
438
|
+
import * as path2 from "path";
|
|
437
439
|
import { Command as Command2 } from "commander";
|
|
438
440
|
function formatDuration(ms) {
|
|
439
441
|
if (ms < 1e3) return `${ms}ms`;
|
|
@@ -451,7 +453,7 @@ function registerSkillsCommand(adoption) {
|
|
|
451
453
|
const globalOpts = cmd.optsWithGlobals();
|
|
452
454
|
const limit = Math.max(parseInt(opts.limit, 10) || 20, 1);
|
|
453
455
|
const cwd = process.cwd();
|
|
454
|
-
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-
|
|
456
|
+
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-BWFFVKEW.js");
|
|
455
457
|
const records = readAdoptionRecords(cwd);
|
|
456
458
|
if (records.length === 0) {
|
|
457
459
|
if (globalOpts.json) {
|
|
@@ -487,7 +489,7 @@ function registerRecentCommand(adoption) {
|
|
|
487
489
|
const globalOpts = cmd.optsWithGlobals();
|
|
488
490
|
const limit = Math.max(parseInt(opts.limit, 10) || 20, 1);
|
|
489
491
|
const cwd = process.cwd();
|
|
490
|
-
const { readAdoptionRecords } = await import("./dist-
|
|
492
|
+
const { readAdoptionRecords } = await import("./dist-BWFFVKEW.js");
|
|
491
493
|
const records = readAdoptionRecords(cwd);
|
|
492
494
|
if (records.length === 0) {
|
|
493
495
|
if (globalOpts.json) {
|
|
@@ -565,7 +567,7 @@ function registerSkillCommand(adoption) {
|
|
|
565
567
|
adoption.command("skill <name>").description("Show detail for a specific skill").action(async (name, _opts, cmd) => {
|
|
566
568
|
const globalOpts = cmd.optsWithGlobals();
|
|
567
569
|
const cwd = process.cwd();
|
|
568
|
-
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-
|
|
570
|
+
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-BWFFVKEW.js");
|
|
569
571
|
const records = readAdoptionRecords(cwd);
|
|
570
572
|
const skillRecords = records.filter((r) => r.skill === name);
|
|
571
573
|
if (skillRecords.length === 0) {
|
|
@@ -584,11 +586,55 @@ function registerSkillCommand(adoption) {
|
|
|
584
586
|
printSkillDetail(name, summary, phaseRates, skillRecords);
|
|
585
587
|
});
|
|
586
588
|
}
|
|
589
|
+
function discoverCatalogSkills(projectRoot3) {
|
|
590
|
+
const skillsDir = path2.join(projectRoot3, "agents", "skills", "claude-code");
|
|
591
|
+
let entries;
|
|
592
|
+
try {
|
|
593
|
+
entries = fs2.readdirSync(skillsDir, { withFileTypes: true });
|
|
594
|
+
} catch {
|
|
595
|
+
return void 0;
|
|
596
|
+
}
|
|
597
|
+
const names = entries.filter((e) => e.isDirectory()).map((e) => e.name).filter((name) => fs2.existsSync(path2.join(skillsDir, name, "skill.yaml")));
|
|
598
|
+
return names.length > 0 ? names : void 0;
|
|
599
|
+
}
|
|
600
|
+
function registerRetrospectiveCommand(adoption) {
|
|
601
|
+
adoption.command("retrospective").description("Generate a catalog retrospective from adoption telemetry").option("--inactive-days <n>", "Inactivity threshold in days (default: 90)", "90").option("--top <n>", "Rows per ranked section (default: 10)", "10").option("--out <path>", "Output file path (default: docs/retrospectives/YYYY-MM-DD.md)").option("--no-write", "Print the report to stdout instead of writing a file").action(async (opts, cmd) => {
|
|
602
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
603
|
+
const cwd = process.cwd();
|
|
604
|
+
const { readAdoptionRecords, getCatalogRetrospectiveReport, renderRetrospectiveMarkdown } = await import("./dist-BWFFVKEW.js");
|
|
605
|
+
const records = readAdoptionRecords(cwd);
|
|
606
|
+
const catalogSkills = discoverCatalogSkills(cwd);
|
|
607
|
+
const report = getCatalogRetrospectiveReport(records, {
|
|
608
|
+
inactiveDays: Math.max(parseInt(opts.inactiveDays, 10) || 90, 1),
|
|
609
|
+
topN: Math.max(parseInt(opts.top, 10) || 10, 1),
|
|
610
|
+
...catalogSkills ? { catalogSkills } : {}
|
|
611
|
+
});
|
|
612
|
+
if (globalOpts.json) {
|
|
613
|
+
console.log(JSON.stringify(report, null, 2));
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
const markdown = renderRetrospectiveMarkdown(report);
|
|
617
|
+
if (opts.write === false) {
|
|
618
|
+
console.log(markdown);
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
const date = report.generatedAt.slice(0, 10);
|
|
622
|
+
const outPath = opts.out ? path2.resolve(cwd, opts.out) : path2.join(cwd, "docs", "retrospectives", `${date}.md`);
|
|
623
|
+
fs2.mkdirSync(path2.dirname(outPath), { recursive: true });
|
|
624
|
+
fs2.writeFileSync(outPath, markdown, "utf-8");
|
|
625
|
+
const relPath = path2.relative(cwd, outPath).replaceAll("\\", "/");
|
|
626
|
+
logger.info(`Catalog retrospective written to ${relPath}`);
|
|
627
|
+
logger.info(
|
|
628
|
+
`${report.totalRecords} records \xB7 ${report.distinctSkills} skills \xB7 window ${report.windowDays}d`
|
|
629
|
+
);
|
|
630
|
+
});
|
|
631
|
+
}
|
|
587
632
|
function createAdoptionCommand() {
|
|
588
633
|
const adoption = new Command2("adoption").description("View skill adoption telemetry").option("--json", "Output in JSON format");
|
|
589
634
|
registerSkillsCommand(adoption);
|
|
590
635
|
registerRecentCommand(adoption);
|
|
591
636
|
registerSkillCommand(adoption);
|
|
637
|
+
registerRetrospectiveCommand(adoption);
|
|
592
638
|
return adoption;
|
|
593
639
|
}
|
|
594
640
|
|
|
@@ -599,7 +645,7 @@ import { Command as Command5 } from "commander";
|
|
|
599
645
|
init_dist3();
|
|
600
646
|
init_dist3();
|
|
601
647
|
import { Command as Command3 } from "commander";
|
|
602
|
-
import * as
|
|
648
|
+
import * as path3 from "path";
|
|
603
649
|
import * as childProcess from "child_process";
|
|
604
650
|
async function runAgentTask(task, options) {
|
|
605
651
|
const configResult = resolveConfig(options.configPath);
|
|
@@ -671,7 +717,7 @@ function createCommandExecutor() {
|
|
|
671
717
|
}
|
|
672
718
|
async function runPersonaMode(opts, quiet) {
|
|
673
719
|
const personasDir = resolvePersonasDir();
|
|
674
|
-
const filePath =
|
|
720
|
+
const filePath = path3.join(personasDir, `${opts.persona}.yaml`);
|
|
675
721
|
const personaResult = loadPersona(filePath);
|
|
676
722
|
if (!personaResult.ok) {
|
|
677
723
|
logger.error(personaResult.error.message);
|
|
@@ -982,8 +1028,8 @@ function outcomeIcon(kind) {
|
|
|
982
1028
|
// src/commands/audit-protected.ts
|
|
983
1029
|
init_dist3();
|
|
984
1030
|
import { Command as Command7 } from "commander";
|
|
985
|
-
import * as
|
|
986
|
-
import * as
|
|
1031
|
+
import * as path4 from "path";
|
|
1032
|
+
import * as fs3 from "fs";
|
|
987
1033
|
import { glob } from "glob";
|
|
988
1034
|
async function runAuditProtected(options) {
|
|
989
1035
|
const cwd = options.cwd ?? process.cwd();
|
|
@@ -992,7 +1038,7 @@ async function runAuditProtected(options) {
|
|
|
992
1038
|
return Err(configResult.error);
|
|
993
1039
|
}
|
|
994
1040
|
const config = configResult.value;
|
|
995
|
-
const rootDir =
|
|
1041
|
+
const rootDir = path4.resolve(cwd, config.rootDir);
|
|
996
1042
|
const excludePatterns = config.entropy?.excludePatterns ?? ["**/node_modules/**", "**/*.test.ts"];
|
|
997
1043
|
let files;
|
|
998
1044
|
try {
|
|
@@ -1006,10 +1052,10 @@ async function runAuditProtected(options) {
|
|
|
1006
1052
|
const allRegions = [];
|
|
1007
1053
|
const allIssues = [];
|
|
1008
1054
|
for (const relativePath of files) {
|
|
1009
|
-
const absolutePath =
|
|
1055
|
+
const absolutePath = path4.resolve(rootDir, relativePath);
|
|
1010
1056
|
let content;
|
|
1011
1057
|
try {
|
|
1012
|
-
content =
|
|
1058
|
+
content = fs3.readFileSync(absolutePath, "utf-8");
|
|
1013
1059
|
} catch {
|
|
1014
1060
|
continue;
|
|
1015
1061
|
}
|
|
@@ -1079,41 +1125,41 @@ function createAuditProtectedCommand() {
|
|
|
1079
1125
|
|
|
1080
1126
|
// src/commands/backfill-skill-provenance.ts
|
|
1081
1127
|
import { Command as Command8 } from "commander";
|
|
1082
|
-
import * as
|
|
1083
|
-
import * as
|
|
1128
|
+
import * as fs4 from "fs";
|
|
1129
|
+
import * as path5 from "path";
|
|
1084
1130
|
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
1085
1131
|
function listSkillYamlFiles(skillsRoot) {
|
|
1086
1132
|
const out = [];
|
|
1087
1133
|
let hosts;
|
|
1088
1134
|
try {
|
|
1089
|
-
hosts =
|
|
1135
|
+
hosts = fs4.readdirSync(skillsRoot);
|
|
1090
1136
|
} catch {
|
|
1091
1137
|
return out;
|
|
1092
1138
|
}
|
|
1093
1139
|
for (const host of hosts) {
|
|
1094
|
-
const hostDir =
|
|
1140
|
+
const hostDir = path5.join(skillsRoot, host);
|
|
1095
1141
|
let stat;
|
|
1096
1142
|
try {
|
|
1097
|
-
stat =
|
|
1143
|
+
stat = fs4.statSync(hostDir);
|
|
1098
1144
|
} catch {
|
|
1099
1145
|
continue;
|
|
1100
1146
|
}
|
|
1101
1147
|
if (!stat.isDirectory()) continue;
|
|
1102
1148
|
let skills;
|
|
1103
1149
|
try {
|
|
1104
|
-
skills =
|
|
1150
|
+
skills = fs4.readdirSync(hostDir);
|
|
1105
1151
|
} catch {
|
|
1106
1152
|
continue;
|
|
1107
1153
|
}
|
|
1108
1154
|
for (const skill of skills) {
|
|
1109
|
-
const skillDir =
|
|
1155
|
+
const skillDir = path5.join(hostDir, skill);
|
|
1110
1156
|
try {
|
|
1111
|
-
if (!
|
|
1157
|
+
if (!fs4.statSync(skillDir).isDirectory()) continue;
|
|
1112
1158
|
} catch {
|
|
1113
1159
|
continue;
|
|
1114
1160
|
}
|
|
1115
|
-
const yamlFile =
|
|
1116
|
-
if (
|
|
1161
|
+
const yamlFile = path5.join(skillDir, "skill.yaml");
|
|
1162
|
+
if (fs4.existsSync(yamlFile)) out.push(yamlFile);
|
|
1117
1163
|
}
|
|
1118
1164
|
}
|
|
1119
1165
|
return out;
|
|
@@ -1121,7 +1167,7 @@ function listSkillYamlFiles(skillsRoot) {
|
|
|
1121
1167
|
function backfillFile(yamlFile) {
|
|
1122
1168
|
let raw;
|
|
1123
1169
|
try {
|
|
1124
|
-
raw =
|
|
1170
|
+
raw = fs4.readFileSync(yamlFile, "utf-8");
|
|
1125
1171
|
} catch (err) {
|
|
1126
1172
|
return { error: err instanceof Error ? err.message : String(err) };
|
|
1127
1173
|
}
|
|
@@ -1139,11 +1185,11 @@ function backfillFile(yamlFile) {
|
|
|
1139
1185
|
return "skipped";
|
|
1140
1186
|
}
|
|
1141
1187
|
obj["provenance"] = "user-authored";
|
|
1142
|
-
|
|
1188
|
+
fs4.writeFileSync(yamlFile, stringifyYaml(obj));
|
|
1143
1189
|
return "updated";
|
|
1144
1190
|
}
|
|
1145
|
-
function runBackfillSkillProvenance(
|
|
1146
|
-
const skillsRoot =
|
|
1191
|
+
function runBackfillSkillProvenance(projectRoot3) {
|
|
1192
|
+
const skillsRoot = path5.join(projectRoot3, "agents", "skills");
|
|
1147
1193
|
const files = listSkillYamlFiles(skillsRoot);
|
|
1148
1194
|
const result = {
|
|
1149
1195
|
scanned: files.length,
|
|
@@ -1179,19 +1225,19 @@ function createBackfillSkillProvenanceCommand() {
|
|
|
1179
1225
|
// src/commands/blueprint.ts
|
|
1180
1226
|
init_dist3();
|
|
1181
1227
|
import { Command as Command9 } from "commander";
|
|
1182
|
-
import * as
|
|
1228
|
+
import * as path6 from "path";
|
|
1183
1229
|
function createBlueprintCommand() {
|
|
1184
1230
|
return new Command9("blueprint").description("Generate a self-contained, interactive blueprint of the codebase").argument("[path]", "Path to the project root", ".").option("-o, --output <dir>", "Output directory", "docs/blueprint").action(async (projectPath, options) => {
|
|
1185
1231
|
try {
|
|
1186
|
-
const rootDir =
|
|
1187
|
-
const outputDir =
|
|
1232
|
+
const rootDir = path6.resolve(projectPath);
|
|
1233
|
+
const outputDir = path6.resolve(options.output);
|
|
1188
1234
|
logger.info(`Scanning project at ${rootDir}...`);
|
|
1189
1235
|
const scanner = new ProjectScanner(rootDir);
|
|
1190
1236
|
const data = await scanner.scan();
|
|
1191
1237
|
logger.info(`Generating blueprint to ${outputDir}...`);
|
|
1192
1238
|
const generator = new BlueprintGenerator();
|
|
1193
1239
|
await generator.generate(data, { outputDir });
|
|
1194
|
-
logger.success(`Blueprint generated successfully at ${
|
|
1240
|
+
logger.success(`Blueprint generated successfully at ${path6.join(outputDir, "index.html")}`);
|
|
1195
1241
|
} catch (error) {
|
|
1196
1242
|
logger.error(
|
|
1197
1243
|
`Failed to generate blueprint: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -1373,7 +1419,7 @@ function createCheckArchCommand() {
|
|
|
1373
1419
|
init_dist3();
|
|
1374
1420
|
init_dist3();
|
|
1375
1421
|
import { Command as Command11 } from "commander";
|
|
1376
|
-
import * as
|
|
1422
|
+
import * as path7 from "path";
|
|
1377
1423
|
async function runCheckDeps(options) {
|
|
1378
1424
|
const cwd = options.cwd ?? process.cwd();
|
|
1379
1425
|
const configResult = resolveConfig(options.configPath);
|
|
@@ -1389,7 +1435,7 @@ async function runCheckDeps(options) {
|
|
|
1389
1435
|
if (!config.layers || config.layers.length === 0) {
|
|
1390
1436
|
return Ok(result);
|
|
1391
1437
|
}
|
|
1392
|
-
const rootDir =
|
|
1438
|
+
const rootDir = path7.resolve(cwd, config.rootDir);
|
|
1393
1439
|
const parser = new TypeScriptParser();
|
|
1394
1440
|
const layers = config.layers.map((l) => defineLayer(l.name, [l.pattern], l.allowedDependencies));
|
|
1395
1441
|
const layerConfig = {
|
|
@@ -1703,9 +1749,9 @@ ${name} (${count} finding${count === 1 ? "" : "s"})`);
|
|
|
1703
1749
|
}
|
|
1704
1750
|
function printAnatomyFindings(findings, verbose) {
|
|
1705
1751
|
const byFile = groupByFile(findings, (f) => f.file);
|
|
1706
|
-
for (const [file,
|
|
1752
|
+
for (const [file, fs51] of byFile) {
|
|
1707
1753
|
console.log(` ${file}`);
|
|
1708
|
-
for (const f of
|
|
1754
|
+
for (const f of fs51) {
|
|
1709
1755
|
const line = f.line !== null && f.line !== void 0 ? `:${f.line}` : "";
|
|
1710
1756
|
console.log(` ${f.code} [${f.severity}] line${line}: ${f.message}`);
|
|
1711
1757
|
if (verbose) {
|
|
@@ -1716,9 +1762,9 @@ function printAnatomyFindings(findings, verbose) {
|
|
|
1716
1762
|
}
|
|
1717
1763
|
function printCraftFindings(findings, verbose) {
|
|
1718
1764
|
const byFile = groupByFile(findings, (f) => f.target.file);
|
|
1719
|
-
for (const [file,
|
|
1765
|
+
for (const [file, fs51] of byFile) {
|
|
1720
1766
|
console.log(` ${file}`);
|
|
1721
|
-
for (const f of
|
|
1767
|
+
for (const f of fs51) {
|
|
1722
1768
|
const line = f.target.line !== void 0 ? `:${f.target.line}` : "";
|
|
1723
1769
|
const sev = craftTierToSeverity(f.tier);
|
|
1724
1770
|
console.log(
|
|
@@ -1732,9 +1778,9 @@ function printCraftFindings(findings, verbose) {
|
|
|
1732
1778
|
}
|
|
1733
1779
|
function printDriftFindings(findings, verbose) {
|
|
1734
1780
|
const byFile = groupByFile(findings, (f) => f.file);
|
|
1735
|
-
for (const [file,
|
|
1781
|
+
for (const [file, fs51] of byFile) {
|
|
1736
1782
|
console.log(` ${file}`);
|
|
1737
|
-
for (const f of
|
|
1783
|
+
for (const f of fs51) {
|
|
1738
1784
|
const line = f.line !== null && f.line !== void 0 ? `:${f.line}` : "";
|
|
1739
1785
|
console.log(` ${f.code} [${f.severity}] line${line}: ${f.message}`);
|
|
1740
1786
|
if (verbose) {
|
|
@@ -1745,9 +1791,9 @@ function printDriftFindings(findings, verbose) {
|
|
|
1745
1791
|
}
|
|
1746
1792
|
function printBrandFindings(findings, verbose) {
|
|
1747
1793
|
const byFile = groupByFile(findings, (f) => f.file);
|
|
1748
|
-
for (const [file,
|
|
1794
|
+
for (const [file, fs51] of byFile) {
|
|
1749
1795
|
console.log(` ${file}`);
|
|
1750
|
-
for (const f of
|
|
1796
|
+
for (const f of fs51) {
|
|
1751
1797
|
const line = f.line !== null && f.line !== void 0 ? `:${f.line}` : "";
|
|
1752
1798
|
console.log(` ${f.code} [${f.severity}] line${line}: ${f.message}`);
|
|
1753
1799
|
if (verbose) {
|
|
@@ -1810,7 +1856,7 @@ function createCheckDesignCommand() {
|
|
|
1810
1856
|
init_dist3();
|
|
1811
1857
|
init_dist3();
|
|
1812
1858
|
import { Command as Command13 } from "commander";
|
|
1813
|
-
import * as
|
|
1859
|
+
import * as path8 from "path";
|
|
1814
1860
|
async function runCheckDocs(options) {
|
|
1815
1861
|
const cwd = options.cwd ?? process.cwd();
|
|
1816
1862
|
const minCoverage = options.minCoverage ?? 80;
|
|
@@ -1819,8 +1865,8 @@ async function runCheckDocs(options) {
|
|
|
1819
1865
|
return configResult;
|
|
1820
1866
|
}
|
|
1821
1867
|
const config = configResult.value;
|
|
1822
|
-
const docsDir =
|
|
1823
|
-
const sourceDir =
|
|
1868
|
+
const docsDir = path8.resolve(cwd, config.docsDir);
|
|
1869
|
+
const sourceDir = path8.resolve(cwd, config.rootDir);
|
|
1824
1870
|
const coverageResult = await checkDocCoverage("project", {
|
|
1825
1871
|
docsDir,
|
|
1826
1872
|
sourceDir,
|
|
@@ -1919,12 +1965,12 @@ function createCheckDocsCommand() {
|
|
|
1919
1965
|
// src/commands/check-harness-strength.ts
|
|
1920
1966
|
init_dist3();
|
|
1921
1967
|
import { Command as Command14 } from "commander";
|
|
1922
|
-
import * as
|
|
1968
|
+
import * as path9 from "path";
|
|
1923
1969
|
var SEVERITY_RANK = { error: 3, warning: 2, info: 1 };
|
|
1924
1970
|
function runCheckHarnessStrength(cwd, options) {
|
|
1925
|
-
const
|
|
1971
|
+
const projectRoot3 = path9.resolve(cwd);
|
|
1926
1972
|
const auditor = new HarnessStrengthAuditor();
|
|
1927
|
-
const result = auditor.audit(
|
|
1973
|
+
const result = auditor.audit(projectRoot3, options.mode ? { mode: options.mode } : {});
|
|
1928
1974
|
if (!result.ok) return result;
|
|
1929
1975
|
const audit = result.value;
|
|
1930
1976
|
const threshold = options.severity ?? "warning";
|
|
@@ -1995,7 +2041,7 @@ function createCheckHarnessStrengthCommand() {
|
|
|
1995
2041
|
// src/commands/check-perf.ts
|
|
1996
2042
|
init_dist3();
|
|
1997
2043
|
import { Command as Command15 } from "commander";
|
|
1998
|
-
import * as
|
|
2044
|
+
import * as path10 from "path";
|
|
1999
2045
|
function resolveConfiguredEntryPoints(configPath) {
|
|
2000
2046
|
const configResult = resolveConfig(configPath);
|
|
2001
2047
|
if (!configResult.ok) return void 0;
|
|
@@ -2012,7 +2058,7 @@ async function runCheckPerf(cwd, options) {
|
|
|
2012
2058
|
const runAll2 = !options.structural && !options.size && !options.coupling;
|
|
2013
2059
|
const entryPoints = resolveConfiguredEntryPoints(options.configPath);
|
|
2014
2060
|
const analyzer = new EntropyAnalyzer({
|
|
2015
|
-
rootDir:
|
|
2061
|
+
rootDir: path10.resolve(cwd),
|
|
2016
2062
|
...entryPoints && { entryPoints },
|
|
2017
2063
|
analyze: {
|
|
2018
2064
|
complexity: runAll2 || !!options.structural,
|
|
@@ -2134,7 +2180,7 @@ function createCheckPerfCommand() {
|
|
|
2134
2180
|
// src/commands/check-security.ts
|
|
2135
2181
|
init_dist3();
|
|
2136
2182
|
import { Command as Command16 } from "commander";
|
|
2137
|
-
import * as
|
|
2183
|
+
import * as path11 from "path";
|
|
2138
2184
|
import { execSync as execSync3 } from "child_process";
|
|
2139
2185
|
var SEVERITY_RANK2 = {
|
|
2140
2186
|
error: 3,
|
|
@@ -2147,19 +2193,19 @@ function getChangedFiles(cwd) {
|
|
|
2147
2193
|
cwd,
|
|
2148
2194
|
encoding: "utf-8"
|
|
2149
2195
|
});
|
|
2150
|
-
return output.trim().split("\n").filter((f) => f.length > 0).map((f) =>
|
|
2196
|
+
return output.trim().split("\n").filter((f) => f.length > 0).map((f) => path11.resolve(cwd, f));
|
|
2151
2197
|
} catch {
|
|
2152
2198
|
return [];
|
|
2153
2199
|
}
|
|
2154
2200
|
}
|
|
2155
2201
|
async function runCheckSecurity(cwd, options) {
|
|
2156
|
-
const
|
|
2202
|
+
const projectRoot3 = path11.resolve(cwd);
|
|
2157
2203
|
let configData = {};
|
|
2158
2204
|
try {
|
|
2159
|
-
const
|
|
2160
|
-
const configPath =
|
|
2161
|
-
if (
|
|
2162
|
-
const raw =
|
|
2205
|
+
const fs51 = await import("fs");
|
|
2206
|
+
const configPath = path11.join(projectRoot3, "harness.config.json");
|
|
2207
|
+
if (fs51.existsSync(configPath)) {
|
|
2208
|
+
const raw = fs51.readFileSync(configPath, "utf-8");
|
|
2163
2209
|
const parsed = JSON.parse(raw);
|
|
2164
2210
|
configData = parsed.security ?? {};
|
|
2165
2211
|
}
|
|
@@ -2167,10 +2213,10 @@ async function runCheckSecurity(cwd, options) {
|
|
|
2167
2213
|
}
|
|
2168
2214
|
const securityConfig = parseSecurityConfig(configData);
|
|
2169
2215
|
const scanner = new SecurityScanner(securityConfig);
|
|
2170
|
-
scanner.configureForProject(
|
|
2216
|
+
scanner.configureForProject(projectRoot3);
|
|
2171
2217
|
let filesToScan;
|
|
2172
2218
|
if (options.changedOnly) {
|
|
2173
|
-
filesToScan = getChangedFiles(
|
|
2219
|
+
filesToScan = getChangedFiles(projectRoot3);
|
|
2174
2220
|
} else {
|
|
2175
2221
|
const { glob: glob2 } = await import("glob");
|
|
2176
2222
|
const pattern = "**/*.{ts,tsx,js,jsx,go,py,java,rb}";
|
|
@@ -2180,15 +2226,15 @@ async function runCheckSecurity(cwd, options) {
|
|
|
2180
2226
|
"**/*.test.ts",
|
|
2181
2227
|
"**/fixtures/**"
|
|
2182
2228
|
];
|
|
2183
|
-
filesToScan = await glob2(pattern, { cwd:
|
|
2229
|
+
filesToScan = await glob2(pattern, { cwd: projectRoot3, absolute: true, ignore });
|
|
2184
2230
|
}
|
|
2185
2231
|
const result = await scanner.scanFiles(filesToScan);
|
|
2186
2232
|
try {
|
|
2187
2233
|
const commitHash = execSync3("git rev-parse HEAD", {
|
|
2188
|
-
cwd:
|
|
2234
|
+
cwd: projectRoot3,
|
|
2189
2235
|
encoding: "utf-8"
|
|
2190
2236
|
}).trim();
|
|
2191
|
-
const timelineManager = new SecurityTimelineManager(
|
|
2237
|
+
const timelineManager = new SecurityTimelineManager(projectRoot3);
|
|
2192
2238
|
timelineManager.capture(result, commitHash);
|
|
2193
2239
|
timelineManager.updateLifecycles(result.findings, commitHash);
|
|
2194
2240
|
} catch {
|
|
@@ -2353,8 +2399,8 @@ function createCheckCommand() {
|
|
|
2353
2399
|
// src/commands/ci/init.ts
|
|
2354
2400
|
init_dist3();
|
|
2355
2401
|
import { Command as Command18 } from "commander";
|
|
2356
|
-
import * as
|
|
2357
|
-
import * as
|
|
2402
|
+
import * as fs5 from "fs";
|
|
2403
|
+
import * as path12 from "path";
|
|
2358
2404
|
var ALL_CHECKS = [
|
|
2359
2405
|
"validate",
|
|
2360
2406
|
"deps",
|
|
@@ -2525,17 +2571,17 @@ function generateCIConfig(options) {
|
|
|
2525
2571
|
});
|
|
2526
2572
|
}
|
|
2527
2573
|
function detectPlatform() {
|
|
2528
|
-
if (
|
|
2529
|
-
if (
|
|
2574
|
+
if (fs5.existsSync(".github")) return "github";
|
|
2575
|
+
if (fs5.existsSync(".gitlab-ci.yml")) return "gitlab";
|
|
2530
2576
|
return null;
|
|
2531
2577
|
}
|
|
2532
2578
|
function writeGeneratedConfig(filename, content, platform) {
|
|
2533
|
-
const targetPath =
|
|
2534
|
-
const dir =
|
|
2535
|
-
|
|
2536
|
-
|
|
2579
|
+
const targetPath = path12.resolve(filename);
|
|
2580
|
+
const dir = path12.dirname(targetPath);
|
|
2581
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
2582
|
+
fs5.writeFileSync(targetPath, content);
|
|
2537
2583
|
if (platform === "generic" && process.platform !== "win32") {
|
|
2538
|
-
|
|
2584
|
+
fs5.chmodSync(targetPath, "755");
|
|
2539
2585
|
}
|
|
2540
2586
|
}
|
|
2541
2587
|
async function runInitAction(opts, globalOpts) {
|
|
@@ -2567,10 +2613,10 @@ function createInitCommand() {
|
|
|
2567
2613
|
// src/commands/ci/notify.ts
|
|
2568
2614
|
init_dist3();
|
|
2569
2615
|
import { Command as Command19 } from "commander";
|
|
2570
|
-
import * as
|
|
2616
|
+
import * as fs6 from "fs";
|
|
2571
2617
|
function loadReport(reportPath) {
|
|
2572
2618
|
try {
|
|
2573
|
-
const raw =
|
|
2619
|
+
const raw = fs6.readFileSync(reportPath, "utf-8");
|
|
2574
2620
|
return JSON.parse(raw);
|
|
2575
2621
|
} catch {
|
|
2576
2622
|
return null;
|
|
@@ -2680,7 +2726,7 @@ function createCICommand() {
|
|
|
2680
2726
|
// src/commands/cleanup.ts
|
|
2681
2727
|
init_dist3();
|
|
2682
2728
|
import { Command as Command21 } from "commander";
|
|
2683
|
-
import * as
|
|
2729
|
+
import * as path13 from "path";
|
|
2684
2730
|
async function runCleanup(options) {
|
|
2685
2731
|
const cwd = options.cwd ?? process.cwd();
|
|
2686
2732
|
const type = options.type ?? "all";
|
|
@@ -2695,14 +2741,14 @@ async function runCleanup(options) {
|
|
|
2695
2741
|
patternViolations: [],
|
|
2696
2742
|
totalIssues: 0
|
|
2697
2743
|
};
|
|
2698
|
-
const rootDir =
|
|
2699
|
-
const docsDir =
|
|
2744
|
+
const rootDir = path13.resolve(cwd, config.rootDir);
|
|
2745
|
+
const docsDir = path13.resolve(cwd, config.docsDir);
|
|
2700
2746
|
const driftEnabled = type === "all" || type === "drift";
|
|
2701
2747
|
const driftConfig = config.entropy?.drift;
|
|
2702
2748
|
const entropyConfig = {
|
|
2703
2749
|
rootDir,
|
|
2704
2750
|
...config.entropy?.entryPoints && { entryPoints: config.entropy.entryPoints },
|
|
2705
|
-
docPaths: [
|
|
2751
|
+
docPaths: [path13.join(docsDir, "**/*.md")],
|
|
2706
2752
|
analyze: {
|
|
2707
2753
|
drift: driftEnabled ? driftConfig ?? true : false,
|
|
2708
2754
|
deadCode: type === "all" || type === "dead-code",
|
|
@@ -2802,8 +2848,8 @@ function createCleanupCommand() {
|
|
|
2802
2848
|
// src/commands/cleanup-sessions.ts
|
|
2803
2849
|
init_dist3();
|
|
2804
2850
|
import { Command as Command22 } from "commander";
|
|
2805
|
-
import * as
|
|
2806
|
-
import * as
|
|
2851
|
+
import * as fs7 from "fs";
|
|
2852
|
+
import * as path14 from "path";
|
|
2807
2853
|
var STALE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
2808
2854
|
var ONE_HOUR_MS = 60 * 60 * 1e3;
|
|
2809
2855
|
var ONE_DAY_MS = 24 * ONE_HOUR_MS;
|
|
@@ -2818,13 +2864,13 @@ var CLEANUP_TARGETS = [
|
|
|
2818
2864
|
function getMostRecentMtime(dirPath) {
|
|
2819
2865
|
let latest = 0;
|
|
2820
2866
|
try {
|
|
2821
|
-
const entries =
|
|
2867
|
+
const entries = fs7.readdirSync(dirPath, { withFileTypes: true });
|
|
2822
2868
|
for (const entry of entries) {
|
|
2823
|
-
const fullPath =
|
|
2824
|
-
const stat =
|
|
2869
|
+
const fullPath = path14.join(dirPath, entry.name);
|
|
2870
|
+
const stat = fs7.statSync(fullPath);
|
|
2825
2871
|
if (stat.mtimeMs > latest) latest = stat.mtimeMs;
|
|
2826
2872
|
}
|
|
2827
|
-
const dirStat =
|
|
2873
|
+
const dirStat = fs7.statSync(dirPath);
|
|
2828
2874
|
if (dirStat.mtimeMs > latest) latest = dirStat.mtimeMs;
|
|
2829
2875
|
} catch {
|
|
2830
2876
|
}
|
|
@@ -2833,14 +2879,14 @@ function getMostRecentMtime(dirPath) {
|
|
|
2833
2879
|
async function runCleanupSessions(options) {
|
|
2834
2880
|
const cwd = options.cwd ?? process.cwd();
|
|
2835
2881
|
const dryRun = options.dryRun ?? false;
|
|
2836
|
-
const sessionsDir =
|
|
2882
|
+
const sessionsDir = path14.join(cwd, ".harness", "sessions");
|
|
2837
2883
|
const result = { removed: [], kept: [] };
|
|
2838
|
-
if (!
|
|
2884
|
+
if (!fs7.existsSync(sessionsDir)) {
|
|
2839
2885
|
return Ok(result);
|
|
2840
2886
|
}
|
|
2841
2887
|
let entries;
|
|
2842
2888
|
try {
|
|
2843
|
-
entries =
|
|
2889
|
+
entries = fs7.readdirSync(sessionsDir, { withFileTypes: true });
|
|
2844
2890
|
} catch (err) {
|
|
2845
2891
|
return Err(
|
|
2846
2892
|
new CLIError(
|
|
@@ -2852,14 +2898,14 @@ async function runCleanupSessions(options) {
|
|
|
2852
2898
|
const now = Date.now();
|
|
2853
2899
|
for (const entry of entries) {
|
|
2854
2900
|
if (!entry.isDirectory()) continue;
|
|
2855
|
-
const sessionPath =
|
|
2901
|
+
const sessionPath = path14.join(sessionsDir, entry.name);
|
|
2856
2902
|
const mostRecent = getMostRecentMtime(sessionPath);
|
|
2857
2903
|
const ageMs = now - mostRecent;
|
|
2858
2904
|
if (ageMs > STALE_TTL_MS) {
|
|
2859
2905
|
result.removed.push(entry.name);
|
|
2860
2906
|
if (!dryRun) {
|
|
2861
2907
|
try {
|
|
2862
|
-
|
|
2908
|
+
fs7.rmSync(sessionPath, { recursive: true, force: true });
|
|
2863
2909
|
} catch (err) {
|
|
2864
2910
|
return Err(
|
|
2865
2911
|
new CLIError(
|
|
@@ -2916,7 +2962,7 @@ async function runCleanupAll(opts) {
|
|
|
2916
2962
|
const results = [];
|
|
2917
2963
|
for (const target of targets) {
|
|
2918
2964
|
const ttlMs = (ttlOverrides[target.name] ?? 0) > 0 ? ttlOverrides[target.name] * ONE_HOUR_MS : target.defaultTtlMs;
|
|
2919
|
-
const dir =
|
|
2965
|
+
const dir = path14.join(cwd, ".harness", target.relativeDir);
|
|
2920
2966
|
const sweepResult = await sweepDirectory(dir, ttlMs, dryRun);
|
|
2921
2967
|
if (!sweepResult.ok) return Err(sweepResult.error);
|
|
2922
2968
|
results.push({ target: target.name, ...sweepResult.value });
|
|
@@ -2926,10 +2972,10 @@ async function runCleanupAll(opts) {
|
|
|
2926
2972
|
async function sweepDirectory(dir, ttlMs, dryRun) {
|
|
2927
2973
|
const removed = [];
|
|
2928
2974
|
const kept = [];
|
|
2929
|
-
if (!
|
|
2975
|
+
if (!fs7.existsSync(dir)) return Ok({ removed, kept });
|
|
2930
2976
|
let entries;
|
|
2931
2977
|
try {
|
|
2932
|
-
entries =
|
|
2978
|
+
entries = fs7.readdirSync(dir, { withFileTypes: true });
|
|
2933
2979
|
} catch (err) {
|
|
2934
2980
|
return Err(
|
|
2935
2981
|
new CLIError(
|
|
@@ -2940,13 +2986,13 @@ async function sweepDirectory(dir, ttlMs, dryRun) {
|
|
|
2940
2986
|
}
|
|
2941
2987
|
const now = Date.now();
|
|
2942
2988
|
for (const entry of entries) {
|
|
2943
|
-
const entryPath =
|
|
2989
|
+
const entryPath = path14.join(dir, entry.name);
|
|
2944
2990
|
const ageMs = now - mtimeOf(entryPath);
|
|
2945
2991
|
if (ageMs > ttlMs) {
|
|
2946
2992
|
removed.push(entry.name);
|
|
2947
2993
|
if (!dryRun) {
|
|
2948
2994
|
try {
|
|
2949
|
-
|
|
2995
|
+
fs7.rmSync(entryPath, { recursive: true, force: true });
|
|
2950
2996
|
} catch (err) {
|
|
2951
2997
|
return Err(
|
|
2952
2998
|
new CLIError(
|
|
@@ -2964,7 +3010,7 @@ async function sweepDirectory(dir, ttlMs, dryRun) {
|
|
|
2964
3010
|
}
|
|
2965
3011
|
function mtimeOf(target) {
|
|
2966
3012
|
try {
|
|
2967
|
-
const stat =
|
|
3013
|
+
const stat = fs7.statSync(target);
|
|
2968
3014
|
if (stat.isDirectory()) return getMostRecentMtime(target);
|
|
2969
3015
|
return stat.mtimeMs;
|
|
2970
3016
|
} catch {
|
|
@@ -3004,7 +3050,7 @@ function createCleanupSessionsCommand() {
|
|
|
3004
3050
|
"Comma-separated target names (mutually-exclusive with --exclude/--all)"
|
|
3005
3051
|
).option("--exclude <list>", "Comma-separated target names to skip").action(async (opts, cmd) => {
|
|
3006
3052
|
const globalOpts = cmd.optsWithGlobals();
|
|
3007
|
-
const cwd =
|
|
3053
|
+
const cwd = path14.resolve(opts.path);
|
|
3008
3054
|
const asJson = Boolean(globalOpts.json);
|
|
3009
3055
|
if (opts.all || opts.include || opts.exclude) {
|
|
3010
3056
|
await runExtendedSweep(cwd, opts, asJson);
|
|
@@ -3050,8 +3096,8 @@ import { Command as Command24 } from "commander";
|
|
|
3050
3096
|
|
|
3051
3097
|
// src/commands/compound/scan-candidates.ts
|
|
3052
3098
|
init_dist3();
|
|
3053
|
-
import { mkdirSync as
|
|
3054
|
-
import { dirname as
|
|
3099
|
+
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
3100
|
+
import { dirname as dirname3, join as join9, resolve as resolve13 } from "path";
|
|
3055
3101
|
import { Command as Command23 } from "commander";
|
|
3056
3102
|
var HOTSPOT_THRESHOLD = 7;
|
|
3057
3103
|
var DEFAULT_LOOKBACK = "7d";
|
|
@@ -3078,9 +3124,9 @@ async function runCompoundScanCandidatesCommand(opts) {
|
|
|
3078
3124
|
isoWeek: week,
|
|
3079
3125
|
lookback
|
|
3080
3126
|
});
|
|
3081
|
-
const outputPath = opts.outputPath ??
|
|
3082
|
-
|
|
3083
|
-
|
|
3127
|
+
const outputPath = opts.outputPath ?? join9(cwd, "docs/solutions/.candidates", `${formatIsoWeek(week)}.md`);
|
|
3128
|
+
mkdirSync4(dirname3(outputPath), { recursive: true });
|
|
3129
|
+
writeFileSync5(outputPath, report);
|
|
3084
3130
|
const candidatesFound = undocumented.length + hotspots.length;
|
|
3085
3131
|
const status = {
|
|
3086
3132
|
status: candidatesFound === 0 ? "no-issues" : "success",
|
|
@@ -3120,9 +3166,9 @@ function createScanCandidatesCommand() {
|
|
|
3120
3166
|
const nonInteractive = options.nonInteractive === true || !process.stdout.isTTY;
|
|
3121
3167
|
const status = await runCompoundScanCandidatesCommand({
|
|
3122
3168
|
lookback: options.lookback,
|
|
3123
|
-
configPath:
|
|
3124
|
-
outputPath: options.outputPath ?
|
|
3125
|
-
solutionsDir:
|
|
3169
|
+
configPath: resolve13(process.cwd(), options.config),
|
|
3170
|
+
outputPath: options.outputPath ? resolve13(process.cwd(), options.outputPath) : void 0,
|
|
3171
|
+
solutionsDir: resolve13(process.cwd(), options.solutionsDir),
|
|
3126
3172
|
nonInteractive
|
|
3127
3173
|
});
|
|
3128
3174
|
if (status.status === "failure") process.exitCode = 1;
|
|
@@ -3192,10 +3238,10 @@ function printResult2(result, mode, _formatter) {
|
|
|
3192
3238
|
list.push(f);
|
|
3193
3239
|
bySurface.set(f.target.surface, list);
|
|
3194
3240
|
}
|
|
3195
|
-
for (const [surface,
|
|
3241
|
+
for (const [surface, fs51] of bySurface) {
|
|
3196
3242
|
console.log(`
|
|
3197
3243
|
[${surface}]`);
|
|
3198
|
-
for (const f of
|
|
3244
|
+
for (const f of fs51) {
|
|
3199
3245
|
const line = f.target.line !== void 0 ? `:${f.target.line}` : "";
|
|
3200
3246
|
console.log(` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.file}${line}`);
|
|
3201
3247
|
console.log(` "${truncate(f.target.snippet, 80)}"`);
|
|
@@ -3222,7 +3268,7 @@ function truncate(s, n) {
|
|
|
3222
3268
|
|
|
3223
3269
|
// src/commands/cross-check.ts
|
|
3224
3270
|
import { Command as Command26 } from "commander";
|
|
3225
|
-
import * as
|
|
3271
|
+
import * as path15 from "path";
|
|
3226
3272
|
import chalk from "chalk";
|
|
3227
3273
|
function resolveOutputMode3(options) {
|
|
3228
3274
|
if (options.json) return OutputMode.JSON;
|
|
@@ -3233,8 +3279,8 @@ function resolveOutputMode3(options) {
|
|
|
3233
3279
|
async function runCrossCheckCommand(options) {
|
|
3234
3280
|
const mode = resolveOutputMode3(options);
|
|
3235
3281
|
const projectPath = process.cwd();
|
|
3236
|
-
const specsDir =
|
|
3237
|
-
const plansDir =
|
|
3282
|
+
const specsDir = path15.resolve(projectPath, options.specsDir ?? "docs/specs");
|
|
3283
|
+
const plansDir = path15.resolve(projectPath, options.plansDir ?? "docs/plans");
|
|
3238
3284
|
const result = await runCrossCheck({ projectPath, specsDir, plansDir });
|
|
3239
3285
|
if (!result.ok) {
|
|
3240
3286
|
emitCrossCheckError(result.error.message, mode);
|
|
@@ -3287,13 +3333,13 @@ function createCrossCheckCommand() {
|
|
|
3287
3333
|
// src/commands/dashboard.ts
|
|
3288
3334
|
import { Command as Command27 } from "commander";
|
|
3289
3335
|
import { spawn } from "child_process";
|
|
3290
|
-
import { existsSync as
|
|
3336
|
+
import { existsSync as existsSync6 } from "fs";
|
|
3291
3337
|
import { createRequire } from "module";
|
|
3292
|
-
import { dirname as
|
|
3338
|
+
import { dirname as dirname4, join as join10, resolve as resolve15 } from "path";
|
|
3293
3339
|
import { setTimeout } from "timers";
|
|
3294
3340
|
import { fileURLToPath } from "url";
|
|
3295
3341
|
var __filename = fileURLToPath(import.meta.url);
|
|
3296
|
-
var __dirname =
|
|
3342
|
+
var __dirname = dirname4(__filename);
|
|
3297
3343
|
var require2 = createRequire(import.meta.url);
|
|
3298
3344
|
var DEFAULT_CLIENT_PORT = 3700;
|
|
3299
3345
|
var DEFAULT_API_PORT = 3701;
|
|
@@ -3304,22 +3350,22 @@ function openBrowser(url) {
|
|
|
3304
3350
|
function resolveServerScript() {
|
|
3305
3351
|
try {
|
|
3306
3352
|
const pkgPath = require2.resolve("@harness-engineering/dashboard/package.json");
|
|
3307
|
-
const pkgDir =
|
|
3308
|
-
const built2 =
|
|
3309
|
-
if (
|
|
3310
|
-
const dev2 =
|
|
3311
|
-
if (
|
|
3353
|
+
const pkgDir = dirname4(pkgPath);
|
|
3354
|
+
const built2 = join10(pkgDir, "dist", "server", "serve.js");
|
|
3355
|
+
if (existsSync6(built2)) return { script: built2, dev: false };
|
|
3356
|
+
const dev2 = join10(pkgDir, "src", "server", "serve.ts");
|
|
3357
|
+
if (existsSync6(dev2)) return { script: dev2, dev: true };
|
|
3312
3358
|
} catch {
|
|
3313
3359
|
}
|
|
3314
3360
|
const built = [
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
].find((p) =>
|
|
3361
|
+
join10(__dirname, "..", "..", "..", "dashboard", "dist", "server", "serve.js"),
|
|
3362
|
+
join10(__dirname, "..", "..", "..", "..", "packages", "dashboard", "dist", "server", "serve.js")
|
|
3363
|
+
].find((p) => existsSync6(p));
|
|
3318
3364
|
if (built) return { script: built, dev: false };
|
|
3319
3365
|
const dev = [
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
].find((p) =>
|
|
3366
|
+
join10(__dirname, "..", "..", "..", "dashboard", "src", "server", "serve.ts"),
|
|
3367
|
+
join10(__dirname, "..", "..", "..", "..", "packages", "dashboard", "src", "server", "serve.ts")
|
|
3368
|
+
].find((p) => existsSync6(p));
|
|
3323
3369
|
return dev ? { script: dev, dev: true } : null;
|
|
3324
3370
|
}
|
|
3325
3371
|
function spawnDashboardServer(server, env) {
|
|
@@ -3353,7 +3399,7 @@ function buildDashboardEnv(opts, clientPort, apiPort, projectPath) {
|
|
|
3353
3399
|
function runDashboard(opts) {
|
|
3354
3400
|
const clientPort = Number(opts.port ?? DEFAULT_CLIENT_PORT);
|
|
3355
3401
|
const apiPort = Number(opts.apiPort ?? DEFAULT_API_PORT);
|
|
3356
|
-
const projectPath =
|
|
3402
|
+
const projectPath = resolve15(opts.cwd ?? process.cwd());
|
|
3357
3403
|
const url = `http://localhost:${apiPort}`;
|
|
3358
3404
|
const server = resolveServerScript();
|
|
3359
3405
|
if (!server) {
|
|
@@ -3462,9 +3508,9 @@ function verdictBadge(verdict) {
|
|
|
3462
3508
|
|
|
3463
3509
|
// src/commands/doctor.ts
|
|
3464
3510
|
import { Command as Command29 } from "commander";
|
|
3465
|
-
import * as
|
|
3511
|
+
import * as fs9 from "fs";
|
|
3466
3512
|
import * as os from "os";
|
|
3467
|
-
import * as
|
|
3513
|
+
import * as path17 from "path";
|
|
3468
3514
|
import chalk2 from "chalk";
|
|
3469
3515
|
|
|
3470
3516
|
// src/utils/node-version.ts
|
|
@@ -3547,24 +3593,24 @@ var INTEGRATION_REGISTRY = [
|
|
|
3547
3593
|
];
|
|
3548
3594
|
|
|
3549
3595
|
// src/integrations/config.ts
|
|
3550
|
-
import * as
|
|
3551
|
-
import * as
|
|
3596
|
+
import * as fs8 from "fs";
|
|
3597
|
+
import * as path16 from "path";
|
|
3552
3598
|
function readJsonSafe(filePath) {
|
|
3553
|
-
if (!
|
|
3599
|
+
if (!fs8.existsSync(filePath)) return null;
|
|
3554
3600
|
try {
|
|
3555
|
-
return JSON.parse(
|
|
3601
|
+
return JSON.parse(fs8.readFileSync(filePath, "utf-8"));
|
|
3556
3602
|
} catch {
|
|
3557
3603
|
return null;
|
|
3558
3604
|
}
|
|
3559
3605
|
}
|
|
3560
3606
|
function writeJson(filePath, data) {
|
|
3561
|
-
const dir =
|
|
3562
|
-
if (!
|
|
3563
|
-
|
|
3607
|
+
const dir = path16.dirname(filePath);
|
|
3608
|
+
if (!fs8.existsSync(dir)) {
|
|
3609
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
3564
3610
|
}
|
|
3565
3611
|
const tmp = filePath + ".tmp";
|
|
3566
|
-
|
|
3567
|
-
|
|
3612
|
+
fs8.writeFileSync(tmp, JSON.stringify(data, null, 2) + "\n");
|
|
3613
|
+
fs8.renameSync(tmp, filePath);
|
|
3568
3614
|
}
|
|
3569
3615
|
function readMcpConfig(filePath) {
|
|
3570
3616
|
const config = readJsonSafe(filePath);
|
|
@@ -3594,7 +3640,7 @@ function writeOpencodeMcpEntry(filePath, name, entry) {
|
|
|
3594
3640
|
writeJson(filePath, config);
|
|
3595
3641
|
}
|
|
3596
3642
|
function removeMcpEntry(filePath, name) {
|
|
3597
|
-
if (!
|
|
3643
|
+
if (!fs8.existsSync(filePath)) return;
|
|
3598
3644
|
const config = readMcpConfig(filePath);
|
|
3599
3645
|
delete config.mcpServers[name];
|
|
3600
3646
|
writeJson(filePath, config);
|
|
@@ -3633,7 +3679,7 @@ function checkNodeVersion2() {
|
|
|
3633
3679
|
}
|
|
3634
3680
|
function countCommandFiles(dir, ext) {
|
|
3635
3681
|
try {
|
|
3636
|
-
return
|
|
3682
|
+
return fs9.readdirSync(dir).filter((f) => f.endsWith(ext)).length;
|
|
3637
3683
|
} catch {
|
|
3638
3684
|
return 0;
|
|
3639
3685
|
}
|
|
@@ -3642,13 +3688,13 @@ function checkSlashCommands() {
|
|
|
3642
3688
|
const platforms = [
|
|
3643
3689
|
{
|
|
3644
3690
|
name: "Claude Code",
|
|
3645
|
-
dir:
|
|
3691
|
+
dir: path17.join(os.homedir(), ".claude", "commands", "harness"),
|
|
3646
3692
|
ext: ".md",
|
|
3647
3693
|
client: "claude-code"
|
|
3648
3694
|
},
|
|
3649
3695
|
{
|
|
3650
3696
|
name: "Gemini CLI",
|
|
3651
|
-
dir:
|
|
3697
|
+
dir: path17.join(os.homedir(), ".gemini", "commands", "harness"),
|
|
3652
3698
|
ext: ".toml",
|
|
3653
3699
|
client: "gemini-cli"
|
|
3654
3700
|
}
|
|
@@ -3672,7 +3718,7 @@ function checkSlashCommands() {
|
|
|
3672
3718
|
}
|
|
3673
3719
|
function checkMcpConfig(cwd) {
|
|
3674
3720
|
const results = [];
|
|
3675
|
-
const claudeConfig = readMcpConfig(
|
|
3721
|
+
const claudeConfig = readMcpConfig(path17.join(cwd, ".mcp.json"));
|
|
3676
3722
|
if (claudeConfig.mcpServers?.["harness"]) {
|
|
3677
3723
|
results.push({
|
|
3678
3724
|
name: "mcp-claude",
|
|
@@ -3687,9 +3733,9 @@ function checkMcpConfig(cwd) {
|
|
|
3687
3733
|
fix: "Run: harness setup-mcp --client claude"
|
|
3688
3734
|
});
|
|
3689
3735
|
}
|
|
3690
|
-
const geminiDir =
|
|
3691
|
-
if (
|
|
3692
|
-
const geminiConfig = readMcpConfig(
|
|
3736
|
+
const geminiDir = path17.join(cwd, ".gemini");
|
|
3737
|
+
if (fs9.existsSync(geminiDir)) {
|
|
3738
|
+
const geminiConfig = readMcpConfig(path17.join(geminiDir, "settings.json"));
|
|
3693
3739
|
if (geminiConfig.mcpServers?.["harness"]) {
|
|
3694
3740
|
results.push({
|
|
3695
3741
|
name: "mcp-gemini",
|
|
@@ -3708,12 +3754,12 @@ function checkMcpConfig(cwd) {
|
|
|
3708
3754
|
return results;
|
|
3709
3755
|
}
|
|
3710
3756
|
function loadMcpPresence(cwd) {
|
|
3711
|
-
const mcpPath =
|
|
3712
|
-
const geminiDir =
|
|
3713
|
-
const hasGemini =
|
|
3757
|
+
const mcpPath = path17.join(cwd, ".mcp.json");
|
|
3758
|
+
const geminiDir = path17.join(cwd, ".gemini");
|
|
3759
|
+
const hasGemini = fs9.existsSync(geminiDir);
|
|
3714
3760
|
return {
|
|
3715
3761
|
mcpConfig: readMcpConfig(mcpPath),
|
|
3716
|
-
geminiConfig: hasGemini ? readMcpConfig(
|
|
3762
|
+
geminiConfig: hasGemini ? readMcpConfig(path17.join(geminiDir, "settings.json")) : null,
|
|
3717
3763
|
hasGemini
|
|
3718
3764
|
};
|
|
3719
3765
|
}
|
|
@@ -3745,7 +3791,7 @@ function checkTier0Presence(def, presence) {
|
|
|
3745
3791
|
function checkIntegrations(cwd) {
|
|
3746
3792
|
const results = [];
|
|
3747
3793
|
const presence = loadMcpPresence(cwd);
|
|
3748
|
-
const configPath =
|
|
3794
|
+
const configPath = path17.join(cwd, "harness.config.json");
|
|
3749
3795
|
const integrationsConfig = readIntegrationsConfig(configPath);
|
|
3750
3796
|
for (const def of INTEGRATION_REGISTRY.filter((d) => d.tier === 0)) {
|
|
3751
3797
|
results.push(checkTier0Presence(def, presence));
|
|
@@ -3813,12 +3859,12 @@ function checkLivePings(env = process.env) {
|
|
|
3813
3859
|
return results;
|
|
3814
3860
|
}
|
|
3815
3861
|
function listHookFiles(cwd) {
|
|
3816
|
-
const hooksDir =
|
|
3817
|
-
if (!
|
|
3862
|
+
const hooksDir = path17.join(cwd, ".harness", "hooks");
|
|
3863
|
+
if (!fs9.existsSync(hooksDir)) return [];
|
|
3818
3864
|
try {
|
|
3819
|
-
return
|
|
3820
|
-
relativePath:
|
|
3821
|
-
absolutePath:
|
|
3865
|
+
return fs9.readdirSync(hooksDir).filter((f) => !f.startsWith(".")).map((f) => ({
|
|
3866
|
+
relativePath: path17.join(".harness", "hooks", f),
|
|
3867
|
+
absolutePath: path17.join(hooksDir, f)
|
|
3822
3868
|
}));
|
|
3823
3869
|
} catch {
|
|
3824
3870
|
return [];
|
|
@@ -3868,7 +3914,7 @@ function validateShellHook(entry, content) {
|
|
|
3868
3914
|
function validateHookEntry(entry) {
|
|
3869
3915
|
let content;
|
|
3870
3916
|
try {
|
|
3871
|
-
content =
|
|
3917
|
+
content = fs9.readFileSync(entry.absolutePath, "utf-8");
|
|
3872
3918
|
} catch (err) {
|
|
3873
3919
|
return {
|
|
3874
3920
|
name: `hook-validity-${entry.relativePath}`,
|
|
@@ -3915,8 +3961,8 @@ var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
|
3915
3961
|
function checkBaselineFreshness(cwd, now = Date.now()) {
|
|
3916
3962
|
const results = [];
|
|
3917
3963
|
for (const def of BASELINE_FILES) {
|
|
3918
|
-
const absolutePath =
|
|
3919
|
-
if (!
|
|
3964
|
+
const absolutePath = path17.join(cwd, def.relativePath);
|
|
3965
|
+
if (!fs9.existsSync(absolutePath)) {
|
|
3920
3966
|
results.push({
|
|
3921
3967
|
name: `baseline-freshness-${def.relativePath}`,
|
|
3922
3968
|
status: "info",
|
|
@@ -3926,7 +3972,7 @@ function checkBaselineFreshness(cwd, now = Date.now()) {
|
|
|
3926
3972
|
}
|
|
3927
3973
|
let mtimeMs;
|
|
3928
3974
|
try {
|
|
3929
|
-
mtimeMs =
|
|
3975
|
+
mtimeMs = fs9.statSync(absolutePath).mtimeMs;
|
|
3930
3976
|
} catch (err) {
|
|
3931
3977
|
results.push({
|
|
3932
3978
|
name: `baseline-freshness-${def.relativePath}`,
|
|
@@ -3963,20 +4009,20 @@ function checkBaselineFreshness(cwd, now = Date.now()) {
|
|
|
3963
4009
|
}
|
|
3964
4010
|
var SESSIONS_SAMPLE_SIZE = 5;
|
|
3965
4011
|
function listRecentSessions(cwd) {
|
|
3966
|
-
const sessionsDir =
|
|
3967
|
-
if (!
|
|
4012
|
+
const sessionsDir = path17.join(cwd, ".harness", "sessions");
|
|
4013
|
+
if (!fs9.existsSync(sessionsDir)) return [];
|
|
3968
4014
|
let entries;
|
|
3969
4015
|
try {
|
|
3970
|
-
entries =
|
|
4016
|
+
entries = fs9.readdirSync(sessionsDir);
|
|
3971
4017
|
} catch {
|
|
3972
4018
|
return [];
|
|
3973
4019
|
}
|
|
3974
4020
|
const dirs = [];
|
|
3975
4021
|
for (const name of entries) {
|
|
3976
4022
|
if (name.startsWith(".")) continue;
|
|
3977
|
-
const absolutePath =
|
|
4023
|
+
const absolutePath = path17.join(sessionsDir, name);
|
|
3978
4024
|
try {
|
|
3979
|
-
const stat =
|
|
4025
|
+
const stat = fs9.statSync(absolutePath);
|
|
3980
4026
|
if (!stat.isDirectory()) continue;
|
|
3981
4027
|
dirs.push({ name, absolutePath, mtimeMs: stat.mtimeMs });
|
|
3982
4028
|
} catch {
|
|
@@ -3991,13 +4037,13 @@ function scanSessionSummaries(sessions) {
|
|
|
3991
4037
|
let missing = 0;
|
|
3992
4038
|
const corrupt = [];
|
|
3993
4039
|
for (const session of sessions) {
|
|
3994
|
-
const summaryPath =
|
|
3995
|
-
if (!
|
|
4040
|
+
const summaryPath = path17.join(session.absolutePath, "session-summary.json");
|
|
4041
|
+
if (!fs9.existsSync(summaryPath)) {
|
|
3996
4042
|
missing += 1;
|
|
3997
4043
|
continue;
|
|
3998
4044
|
}
|
|
3999
4045
|
try {
|
|
4000
|
-
JSON.parse(
|
|
4046
|
+
JSON.parse(fs9.readFileSync(summaryPath, "utf-8"));
|
|
4001
4047
|
parsed += 1;
|
|
4002
4048
|
} catch {
|
|
4003
4049
|
corrupt.push(session.name);
|
|
@@ -4094,7 +4140,7 @@ function createDoctorCommand() {
|
|
|
4094
4140
|
// src/commands/fix-drift.ts
|
|
4095
4141
|
init_dist3();
|
|
4096
4142
|
import { Command as Command30 } from "commander";
|
|
4097
|
-
import * as
|
|
4143
|
+
import * as path18 from "path";
|
|
4098
4144
|
async function runFixDrift(options) {
|
|
4099
4145
|
const cwd = options.cwd ?? process.cwd();
|
|
4100
4146
|
const dryRun = options.dryRun !== false;
|
|
@@ -4103,12 +4149,12 @@ async function runFixDrift(options) {
|
|
|
4103
4149
|
return Err(configResult.error);
|
|
4104
4150
|
}
|
|
4105
4151
|
const config = configResult.value;
|
|
4106
|
-
const rootDir =
|
|
4107
|
-
const docsDir =
|
|
4152
|
+
const rootDir = path18.resolve(cwd, config.rootDir);
|
|
4153
|
+
const docsDir = path18.resolve(cwd, config.docsDir);
|
|
4108
4154
|
const entropyConfig = {
|
|
4109
4155
|
rootDir,
|
|
4110
4156
|
...config.entropy?.entryPoints && { entryPoints: config.entropy.entryPoints },
|
|
4111
|
-
docPaths: [
|
|
4157
|
+
docPaths: [path18.join(docsDir, "**/*.md")],
|
|
4112
4158
|
analyze: {
|
|
4113
4159
|
drift: true,
|
|
4114
4160
|
deadCode: true,
|
|
@@ -4260,10 +4306,10 @@ import { Command as Command33 } from "commander";
|
|
|
4260
4306
|
|
|
4261
4307
|
// src/commands/gateway/token.ts
|
|
4262
4308
|
import { Command as Command31 } from "commander";
|
|
4263
|
-
import { resolve as
|
|
4309
|
+
import { resolve as resolve17 } from "path";
|
|
4264
4310
|
import { TokenStore } from "@harness-engineering/orchestrator";
|
|
4265
4311
|
function getStore() {
|
|
4266
|
-
const p = process.env["HARNESS_TOKENS_PATH"] ??
|
|
4312
|
+
const p = process.env["HARNESS_TOKENS_PATH"] ?? resolve17(".harness", "tokens.json");
|
|
4267
4313
|
return new TokenStore(p);
|
|
4268
4314
|
}
|
|
4269
4315
|
async function runTokenCreate(opts) {
|
|
@@ -4311,10 +4357,10 @@ function createTokenCommand() {
|
|
|
4311
4357
|
// src/commands/gateway/deliveries.ts
|
|
4312
4358
|
import { Command as Command32 } from "commander";
|
|
4313
4359
|
import { createInterface } from "readline";
|
|
4314
|
-
import { resolve as
|
|
4360
|
+
import { resolve as resolve18 } from "path";
|
|
4315
4361
|
import { WebhookQueue } from "@harness-engineering/orchestrator";
|
|
4316
4362
|
function resolveQueuePath() {
|
|
4317
|
-
return process.env["HARNESS_WEBHOOK_QUEUE_PATH"] ??
|
|
4363
|
+
return process.env["HARNESS_WEBHOOK_QUEUE_PATH"] ?? resolve18(".harness", "webhook-queue.sqlite");
|
|
4318
4364
|
}
|
|
4319
4365
|
function getQueue() {
|
|
4320
4366
|
return new WebhookQueue(resolveQueuePath());
|
|
@@ -4499,13 +4545,13 @@ function createGenerateCommand() {
|
|
|
4499
4545
|
import { Command as Command37 } from "commander";
|
|
4500
4546
|
|
|
4501
4547
|
// src/commands/graph/status.ts
|
|
4502
|
-
import * as
|
|
4548
|
+
import * as path19 from "path";
|
|
4503
4549
|
async function readConnectorSyncStatus(graphDir) {
|
|
4504
|
-
const
|
|
4550
|
+
const fs51 = await import("fs/promises");
|
|
4505
4551
|
const result = {};
|
|
4506
4552
|
try {
|
|
4507
|
-
const syncMetaPath =
|
|
4508
|
-
const syncMeta = JSON.parse(await
|
|
4553
|
+
const syncMetaPath = path19.join(graphDir, "sync-metadata.json");
|
|
4554
|
+
const syncMeta = JSON.parse(await fs51.readFile(syncMetaPath, "utf-8"));
|
|
4509
4555
|
for (const [name, data] of Object.entries(syncMeta.connectors ?? {})) {
|
|
4510
4556
|
result[name] = data.lastSyncTimestamp;
|
|
4511
4557
|
}
|
|
@@ -4515,7 +4561,7 @@ async function readConnectorSyncStatus(graphDir) {
|
|
|
4515
4561
|
}
|
|
4516
4562
|
async function runGraphStatus(projectPath) {
|
|
4517
4563
|
const { GraphStore: GraphStore2, loadGraphMetadata } = await import("./dist-DWBTZXR5.js");
|
|
4518
|
-
const graphDir =
|
|
4564
|
+
const graphDir = path19.join(projectPath, ".harness", "graph");
|
|
4519
4565
|
const metaResult = await loadGraphMetadata(graphDir);
|
|
4520
4566
|
if (metaResult.status === "not_found") {
|
|
4521
4567
|
return { status: "no_graph", message: "No graph found. Run `harness graph scan` first." };
|
|
@@ -4564,10 +4610,10 @@ async function runGraphStatus(projectPath) {
|
|
|
4564
4610
|
}
|
|
4565
4611
|
|
|
4566
4612
|
// src/commands/graph/export.ts
|
|
4567
|
-
import * as
|
|
4613
|
+
import * as path20 from "path";
|
|
4568
4614
|
async function runGraphExport(projectPath, format) {
|
|
4569
4615
|
const { GraphStore: GraphStore2 } = await import("./dist-DWBTZXR5.js");
|
|
4570
|
-
const graphDir =
|
|
4616
|
+
const graphDir = path20.join(projectPath, ".harness", "graph");
|
|
4571
4617
|
const store = new GraphStore2();
|
|
4572
4618
|
const loaded = await store.load(graphDir);
|
|
4573
4619
|
if (!loaded) throw new Error("No graph found. Run `harness graph scan` first.");
|
|
@@ -4597,11 +4643,11 @@ async function runGraphExport(projectPath, format) {
|
|
|
4597
4643
|
|
|
4598
4644
|
// src/commands/graph/query.ts
|
|
4599
4645
|
import { Command as Command35 } from "commander";
|
|
4600
|
-
import * as
|
|
4646
|
+
import * as path21 from "path";
|
|
4601
4647
|
async function runQuery(projectPath, rootNodeId, opts) {
|
|
4602
4648
|
const { GraphStore: GraphStore2, ContextQL } = await import("./dist-DWBTZXR5.js");
|
|
4603
4649
|
const store = new GraphStore2();
|
|
4604
|
-
const graphDir =
|
|
4650
|
+
const graphDir = path21.join(projectPath, ".harness", "graph");
|
|
4605
4651
|
const loaded = await store.load(graphDir);
|
|
4606
4652
|
if (!loaded) throw new Error("No graph found. Run `harness graph scan` first.");
|
|
4607
4653
|
const params = {
|
|
@@ -4623,7 +4669,7 @@ function printQueryResult(result) {
|
|
|
4623
4669
|
}
|
|
4624
4670
|
}
|
|
4625
4671
|
async function runQueryAction(rootNodeId, opts, globalOpts) {
|
|
4626
|
-
const projectPath =
|
|
4672
|
+
const projectPath = path21.resolve(globalOpts.config ? path21.dirname(globalOpts.config) : ".");
|
|
4627
4673
|
try {
|
|
4628
4674
|
const result = await runQuery(projectPath, rootNodeId, {
|
|
4629
4675
|
depth: parseInt(opts.depth),
|
|
@@ -4649,12 +4695,12 @@ function createQueryCommand() {
|
|
|
4649
4695
|
|
|
4650
4696
|
// src/commands/graph/ingest.ts
|
|
4651
4697
|
import { Command as Command36 } from "commander";
|
|
4652
|
-
import * as
|
|
4698
|
+
import * as path22 from "path";
|
|
4653
4699
|
async function loadConnectorConfig(projectPath, source) {
|
|
4654
4700
|
try {
|
|
4655
|
-
const
|
|
4656
|
-
const configPath =
|
|
4657
|
-
const config = JSON.parse(await
|
|
4701
|
+
const fs51 = await import("fs/promises");
|
|
4702
|
+
const configPath = path22.join(projectPath, "harness.config.json");
|
|
4703
|
+
const config = JSON.parse(await fs51.readFile(configPath, "utf-8"));
|
|
4658
4704
|
const connector = config.graph?.connectors?.[source];
|
|
4659
4705
|
return connector ?? {};
|
|
4660
4706
|
} catch {
|
|
@@ -4663,9 +4709,9 @@ async function loadConnectorConfig(projectPath, source) {
|
|
|
4663
4709
|
}
|
|
4664
4710
|
async function ingestBusinessKnowledge(bk, projectPath) {
|
|
4665
4711
|
return mergeResults(
|
|
4666
|
-
await bk.ingest(
|
|
4667
|
-
await bk.ingestSolutions(
|
|
4668
|
-
await bk.ingestStrategy(
|
|
4712
|
+
await bk.ingest(path22.join(projectPath, "docs", "knowledge")),
|
|
4713
|
+
await bk.ingestSolutions(path22.join(projectPath, "docs", "solutions")),
|
|
4714
|
+
await bk.ingestStrategy(path22.join(projectPath, "STRATEGY.md"))
|
|
4669
4715
|
);
|
|
4670
4716
|
}
|
|
4671
4717
|
function mergeResults(...results) {
|
|
@@ -4705,7 +4751,7 @@ async function runIngest(projectPath, source, opts) {
|
|
|
4705
4751
|
FigmaConnector,
|
|
4706
4752
|
MiroConnector
|
|
4707
4753
|
} = await import("./dist-DWBTZXR5.js");
|
|
4708
|
-
const graphDir =
|
|
4754
|
+
const graphDir = path22.join(projectPath, ".harness", "graph");
|
|
4709
4755
|
const store = new GraphStore2();
|
|
4710
4756
|
await store.load(graphDir);
|
|
4711
4757
|
const ingestOptions = loadIngestOptions(projectPath);
|
|
@@ -4717,11 +4763,11 @@ async function runIngest(projectPath, source, opts) {
|
|
|
4717
4763
|
const bkInst = new BusinessKnowledgeIngestor(store);
|
|
4718
4764
|
const bkResult = await ingestBusinessKnowledge(bkInst, projectPath);
|
|
4719
4765
|
const reqResult = await new RequirementIngestor(store).ingestSpecs(
|
|
4720
|
-
|
|
4766
|
+
path22.join(projectPath, "docs", "changes")
|
|
4721
4767
|
);
|
|
4722
4768
|
const gitResult = await new GitIngestor(store).ingest(projectPath);
|
|
4723
4769
|
const { createExtractionRunner } = await import("./dist-DWBTZXR5.js");
|
|
4724
|
-
const extractedDir =
|
|
4770
|
+
const extractedDir = path22.join(projectPath, ".harness", "knowledge", "extracted");
|
|
4725
4771
|
const signalsResult = await createExtractionRunner().run(projectPath, store, extractedDir);
|
|
4726
4772
|
const syncManager = new SyncManager(store, graphDir);
|
|
4727
4773
|
const connectorMap = {
|
|
@@ -4766,12 +4812,12 @@ async function runIngest(projectPath, source, opts) {
|
|
|
4766
4812
|
break;
|
|
4767
4813
|
case "requirements":
|
|
4768
4814
|
result = await new RequirementIngestor(store).ingestSpecs(
|
|
4769
|
-
|
|
4815
|
+
path22.join(projectPath, "docs", "changes")
|
|
4770
4816
|
);
|
|
4771
4817
|
break;
|
|
4772
4818
|
case "business-signals": {
|
|
4773
4819
|
const { createExtractionRunner } = await import("./dist-DWBTZXR5.js");
|
|
4774
|
-
const extractedDir =
|
|
4820
|
+
const extractedDir = path22.join(projectPath, ".harness", "knowledge", "extracted");
|
|
4775
4821
|
result = await createExtractionRunner().run(projectPath, store, extractedDir);
|
|
4776
4822
|
break;
|
|
4777
4823
|
}
|
|
@@ -4820,7 +4866,7 @@ async function handleIngestAction(opts, cmd) {
|
|
|
4820
4866
|
process.exit(1);
|
|
4821
4867
|
}
|
|
4822
4868
|
const globalOpts = cmd.optsWithGlobals();
|
|
4823
|
-
const projectPath =
|
|
4869
|
+
const projectPath = path22.resolve(globalOpts.config ? path22.dirname(globalOpts.config) : ".");
|
|
4824
4870
|
try {
|
|
4825
4871
|
const runOpts = {};
|
|
4826
4872
|
if (opts.full !== void 0) runOpts.full = opts.full;
|
|
@@ -4844,9 +4890,9 @@ function createIngestCommand() {
|
|
|
4844
4890
|
}
|
|
4845
4891
|
|
|
4846
4892
|
// src/commands/graph/index.ts
|
|
4847
|
-
import * as
|
|
4893
|
+
import * as path23 from "path";
|
|
4848
4894
|
function resolveProjectPath(globalOpts) {
|
|
4849
|
-
return
|
|
4895
|
+
return path23.resolve(globalOpts.config ? path23.dirname(globalOpts.config) : ".");
|
|
4850
4896
|
}
|
|
4851
4897
|
function printGraphStatus(result) {
|
|
4852
4898
|
if (result.status === "no_graph" || result.status === "schema_mismatch") {
|
|
@@ -4906,8 +4952,8 @@ import { Command as Command42 } from "commander";
|
|
|
4906
4952
|
|
|
4907
4953
|
// src/commands/hooks/init.ts
|
|
4908
4954
|
import { Command as Command38 } from "commander";
|
|
4909
|
-
import * as
|
|
4910
|
-
import * as
|
|
4955
|
+
import * as fs10 from "fs";
|
|
4956
|
+
import * as path24 from "path";
|
|
4911
4957
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4912
4958
|
|
|
4913
4959
|
// src/hooks/profiles.ts
|
|
@@ -4958,17 +5004,17 @@ function supportFilesFor(hookNames) {
|
|
|
4958
5004
|
|
|
4959
5005
|
// src/commands/hooks/init.ts
|
|
4960
5006
|
var __filename2 = fileURLToPath2(import.meta.url);
|
|
4961
|
-
var __dirname2 =
|
|
5007
|
+
var __dirname2 = path24.dirname(__filename2);
|
|
4962
5008
|
var VALID_PROFILES = ["minimal", "standard", "strict"];
|
|
4963
5009
|
function resolveHookSourceDir() {
|
|
4964
5010
|
const candidates = [
|
|
4965
5011
|
// Dev layout: src/commands/hooks/ → ../../hooks/
|
|
4966
|
-
|
|
5012
|
+
path24.resolve(__dirname2, "..", "..", "hooks"),
|
|
4967
5013
|
// Bundled layout: dist/ → ./hooks/ (copied by copy-assets.mjs)
|
|
4968
|
-
|
|
5014
|
+
path24.resolve(__dirname2, "hooks")
|
|
4969
5015
|
];
|
|
4970
5016
|
for (const candidate of candidates) {
|
|
4971
|
-
if (
|
|
5017
|
+
if (fs10.existsSync(candidate)) {
|
|
4972
5018
|
return candidate;
|
|
4973
5019
|
}
|
|
4974
5020
|
}
|
|
@@ -5005,12 +5051,12 @@ function mergeSettings(existing, hooksConfig) {
|
|
|
5005
5051
|
}
|
|
5006
5052
|
function initHooks(options) {
|
|
5007
5053
|
const { profile, projectDir } = options;
|
|
5008
|
-
const hooksDestDir =
|
|
5009
|
-
|
|
5010
|
-
if (
|
|
5011
|
-
for (const entry of
|
|
5054
|
+
const hooksDestDir = path24.join(projectDir, ".harness", "hooks");
|
|
5055
|
+
fs10.mkdirSync(hooksDestDir, { recursive: true });
|
|
5056
|
+
if (fs10.existsSync(hooksDestDir)) {
|
|
5057
|
+
for (const entry of fs10.readdirSync(hooksDestDir)) {
|
|
5012
5058
|
if (entry.endsWith(".js")) {
|
|
5013
|
-
|
|
5059
|
+
fs10.unlinkSync(path24.join(hooksDestDir, entry));
|
|
5014
5060
|
}
|
|
5015
5061
|
}
|
|
5016
5062
|
}
|
|
@@ -5019,29 +5065,29 @@ function initHooks(options) {
|
|
|
5019
5065
|
const activeNames = PROFILES[profile];
|
|
5020
5066
|
const activeScripts = HOOK_SCRIPTS.filter((h) => activeNames.includes(h.name));
|
|
5021
5067
|
for (const script of activeScripts) {
|
|
5022
|
-
const srcFile =
|
|
5023
|
-
const destFile =
|
|
5024
|
-
if (
|
|
5025
|
-
|
|
5068
|
+
const srcFile = path24.join(sourceDir, `${script.name}.js`);
|
|
5069
|
+
const destFile = path24.join(hooksDestDir, `${script.name}.js`);
|
|
5070
|
+
if (fs10.existsSync(srcFile)) {
|
|
5071
|
+
fs10.copyFileSync(srcFile, destFile);
|
|
5026
5072
|
copiedScripts.push(script.name);
|
|
5027
5073
|
}
|
|
5028
5074
|
}
|
|
5029
5075
|
for (const supportFile of supportFilesFor(activeNames)) {
|
|
5030
|
-
const srcFile =
|
|
5031
|
-
const destFile =
|
|
5032
|
-
if (
|
|
5033
|
-
|
|
5076
|
+
const srcFile = path24.join(sourceDir, supportFile);
|
|
5077
|
+
const destFile = path24.join(hooksDestDir, supportFile);
|
|
5078
|
+
if (fs10.existsSync(srcFile)) {
|
|
5079
|
+
fs10.copyFileSync(srcFile, destFile);
|
|
5034
5080
|
}
|
|
5035
5081
|
}
|
|
5036
|
-
const profilePath =
|
|
5037
|
-
|
|
5038
|
-
const claudeDir =
|
|
5039
|
-
|
|
5040
|
-
const settingsPath =
|
|
5082
|
+
const profilePath = path24.join(hooksDestDir, "profile.json");
|
|
5083
|
+
fs10.writeFileSync(profilePath, JSON.stringify({ profile }, null, 2) + "\n");
|
|
5084
|
+
const claudeDir = path24.join(projectDir, ".claude");
|
|
5085
|
+
fs10.mkdirSync(claudeDir, { recursive: true });
|
|
5086
|
+
const settingsPath = path24.join(claudeDir, "settings.json");
|
|
5041
5087
|
let existing = {};
|
|
5042
|
-
if (
|
|
5088
|
+
if (fs10.existsSync(settingsPath)) {
|
|
5043
5089
|
try {
|
|
5044
|
-
existing = JSON.parse(
|
|
5090
|
+
existing = JSON.parse(fs10.readFileSync(settingsPath, "utf-8"));
|
|
5045
5091
|
} catch (e) {
|
|
5046
5092
|
throw new Error(
|
|
5047
5093
|
`Malformed .claude/settings.json \u2014 fix the JSON syntax before running hooks init. Parse error: ${e instanceof Error ? e.message : String(e)}`,
|
|
@@ -5051,7 +5097,7 @@ function initHooks(options) {
|
|
|
5051
5097
|
}
|
|
5052
5098
|
const hooksConfig = buildSettingsHooks(profile);
|
|
5053
5099
|
const merged = mergeSettings(existing, hooksConfig);
|
|
5054
|
-
|
|
5100
|
+
fs10.writeFileSync(settingsPath, JSON.stringify(merged, null, 2) + "\n");
|
|
5055
5101
|
return { copiedScripts, settingsPath, profilePath };
|
|
5056
5102
|
}
|
|
5057
5103
|
function createInitCommand2() {
|
|
@@ -5080,7 +5126,7 @@ function createInitCommand2() {
|
|
|
5080
5126
|
);
|
|
5081
5127
|
logger.info(`Profile: ${profile}`);
|
|
5082
5128
|
logger.info(
|
|
5083
|
-
`Settings: ${
|
|
5129
|
+
`Settings: ${path24.relative(projectDir, result.settingsPath).replaceAll("\\", "/")}`
|
|
5084
5130
|
);
|
|
5085
5131
|
logger.dim("Run 'harness hooks list' to see installed hooks");
|
|
5086
5132
|
}
|
|
@@ -5094,18 +5140,18 @@ function createInitCommand2() {
|
|
|
5094
5140
|
|
|
5095
5141
|
// src/commands/hooks/list.ts
|
|
5096
5142
|
import { Command as Command39 } from "commander";
|
|
5097
|
-
import * as
|
|
5098
|
-
import * as
|
|
5143
|
+
import * as fs11 from "fs";
|
|
5144
|
+
import * as path25 from "path";
|
|
5099
5145
|
function listHooks(projectDir) {
|
|
5100
|
-
const hooksDir =
|
|
5101
|
-
const profilePath =
|
|
5102
|
-
if (!
|
|
5146
|
+
const hooksDir = path25.join(projectDir, ".harness", "hooks");
|
|
5147
|
+
const profilePath = path25.join(hooksDir, "profile.json");
|
|
5148
|
+
if (!fs11.existsSync(profilePath)) {
|
|
5103
5149
|
return { installed: false, profile: null, hooks: [] };
|
|
5104
5150
|
}
|
|
5105
5151
|
let profile = "standard";
|
|
5106
5152
|
let warning;
|
|
5107
5153
|
try {
|
|
5108
|
-
const data = JSON.parse(
|
|
5154
|
+
const data = JSON.parse(fs11.readFileSync(profilePath, "utf-8"));
|
|
5109
5155
|
if (data.profile && ["minimal", "standard", "strict"].includes(data.profile)) {
|
|
5110
5156
|
profile = data.profile;
|
|
5111
5157
|
}
|
|
@@ -5117,7 +5163,7 @@ function listHooks(projectDir) {
|
|
|
5117
5163
|
name: h.name,
|
|
5118
5164
|
event: h.event,
|
|
5119
5165
|
matcher: h.matcher,
|
|
5120
|
-
scriptPath:
|
|
5166
|
+
scriptPath: path25.join(".harness", "hooks", `${h.name}.js`)
|
|
5121
5167
|
}));
|
|
5122
5168
|
const result = { installed: true, profile, hooks };
|
|
5123
5169
|
if (warning) {
|
|
@@ -5148,27 +5194,27 @@ function createListCommand() {
|
|
|
5148
5194
|
|
|
5149
5195
|
// src/commands/hooks/remove.ts
|
|
5150
5196
|
import { Command as Command40 } from "commander";
|
|
5151
|
-
import * as
|
|
5152
|
-
import * as
|
|
5197
|
+
import * as fs12 from "fs";
|
|
5198
|
+
import * as path26 from "path";
|
|
5153
5199
|
function removeHooks(projectDir) {
|
|
5154
|
-
const hooksDir =
|
|
5155
|
-
const settingsPath =
|
|
5200
|
+
const hooksDir = path26.join(projectDir, ".harness", "hooks");
|
|
5201
|
+
const settingsPath = path26.join(projectDir, ".claude", "settings.json");
|
|
5156
5202
|
let removed = false;
|
|
5157
5203
|
let settingsCleaned = false;
|
|
5158
|
-
if (
|
|
5159
|
-
|
|
5204
|
+
if (fs12.existsSync(hooksDir)) {
|
|
5205
|
+
fs12.rmSync(hooksDir, { recursive: true, force: true });
|
|
5160
5206
|
removed = true;
|
|
5161
5207
|
}
|
|
5162
|
-
if (
|
|
5208
|
+
if (fs12.existsSync(settingsPath)) {
|
|
5163
5209
|
try {
|
|
5164
|
-
const settings = JSON.parse(
|
|
5210
|
+
const settings = JSON.parse(fs12.readFileSync(settingsPath, "utf-8"));
|
|
5165
5211
|
if (settings.hooks !== void 0) {
|
|
5166
5212
|
delete settings.hooks;
|
|
5167
5213
|
settingsCleaned = true;
|
|
5168
5214
|
if (Object.keys(settings).length === 0) {
|
|
5169
|
-
|
|
5215
|
+
fs12.unlinkSync(settingsPath);
|
|
5170
5216
|
} else {
|
|
5171
|
-
|
|
5217
|
+
fs12.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
5172
5218
|
}
|
|
5173
5219
|
}
|
|
5174
5220
|
} catch {
|
|
@@ -5200,20 +5246,20 @@ function createRemoveCommand() {
|
|
|
5200
5246
|
|
|
5201
5247
|
// src/commands/hooks/add.ts
|
|
5202
5248
|
import { Command as Command41 } from "commander";
|
|
5203
|
-
import * as
|
|
5204
|
-
import * as
|
|
5249
|
+
import * as fs13 from "fs";
|
|
5250
|
+
import * as path27 from "path";
|
|
5205
5251
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
5206
|
-
var __dirname3 =
|
|
5252
|
+
var __dirname3 = path27.dirname(fileURLToPath3(import.meta.url));
|
|
5207
5253
|
var ALIASES = {
|
|
5208
5254
|
sentinel: ["sentinel-pre", "sentinel-post"]
|
|
5209
5255
|
};
|
|
5210
5256
|
function hookSourceDir() {
|
|
5211
|
-
const d =
|
|
5212
|
-
if (
|
|
5257
|
+
const d = path27.resolve(__dirname3, "..", "..", "hooks");
|
|
5258
|
+
if (fs13.existsSync(d)) return d;
|
|
5213
5259
|
throw new Error(`Hook scripts not found: ${d}`);
|
|
5214
5260
|
}
|
|
5215
5261
|
function readJson(p) {
|
|
5216
|
-
return
|
|
5262
|
+
return fs13.existsSync(p) ? JSON.parse(fs13.readFileSync(p, "utf-8")) : {};
|
|
5217
5263
|
}
|
|
5218
5264
|
function registerHook(s, ev, matcher, name) {
|
|
5219
5265
|
if (!s.hooks[ev]) s.hooks[ev] = [];
|
|
@@ -5226,11 +5272,11 @@ function addHooks(hookName, projectDir) {
|
|
|
5226
5272
|
const names = ALIASES[hookName] ?? [hookName];
|
|
5227
5273
|
const result = { added: [], alreadyInstalled: [], notFound: [] };
|
|
5228
5274
|
const srcDir = hookSourceDir();
|
|
5229
|
-
const destDir =
|
|
5230
|
-
|
|
5231
|
-
const claudeDir =
|
|
5232
|
-
|
|
5233
|
-
const settingsPath =
|
|
5275
|
+
const destDir = path27.join(projectDir, ".harness", "hooks");
|
|
5276
|
+
fs13.mkdirSync(destDir, { recursive: true });
|
|
5277
|
+
const claudeDir = path27.join(projectDir, ".claude");
|
|
5278
|
+
fs13.mkdirSync(claudeDir, { recursive: true });
|
|
5279
|
+
const settingsPath = path27.join(claudeDir, "settings.json");
|
|
5234
5280
|
const settings = readJson(settingsPath);
|
|
5235
5281
|
if (!settings.hooks) settings.hooks = {};
|
|
5236
5282
|
for (const name of names) {
|
|
@@ -5239,27 +5285,27 @@ function addHooks(hookName, projectDir) {
|
|
|
5239
5285
|
result.notFound.push(name);
|
|
5240
5286
|
continue;
|
|
5241
5287
|
}
|
|
5242
|
-
const src =
|
|
5243
|
-
const dest =
|
|
5244
|
-
if (
|
|
5288
|
+
const src = path27.join(srcDir, `${name}.js`);
|
|
5289
|
+
const dest = path27.join(destDir, `${name}.js`);
|
|
5290
|
+
if (fs13.existsSync(dest)) {
|
|
5245
5291
|
result.alreadyInstalled.push(name);
|
|
5246
|
-
} else if (!
|
|
5292
|
+
} else if (!fs13.existsSync(src)) {
|
|
5247
5293
|
result.notFound.push(name);
|
|
5248
5294
|
continue;
|
|
5249
5295
|
} else {
|
|
5250
|
-
|
|
5296
|
+
fs13.copyFileSync(src, dest);
|
|
5251
5297
|
result.added.push(name);
|
|
5252
5298
|
}
|
|
5253
5299
|
registerHook(settings, def.event, def.matcher, name);
|
|
5254
5300
|
}
|
|
5255
5301
|
for (const supportFile of supportFilesFor(names)) {
|
|
5256
|
-
const src =
|
|
5257
|
-
const dest =
|
|
5258
|
-
if (
|
|
5259
|
-
|
|
5302
|
+
const src = path27.join(srcDir, supportFile);
|
|
5303
|
+
const dest = path27.join(destDir, supportFile);
|
|
5304
|
+
if (fs13.existsSync(src)) {
|
|
5305
|
+
fs13.copyFileSync(src, dest);
|
|
5260
5306
|
}
|
|
5261
5307
|
}
|
|
5262
|
-
|
|
5308
|
+
fs13.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
5263
5309
|
return result;
|
|
5264
5310
|
}
|
|
5265
5311
|
function createAddCommand2() {
|
|
@@ -5299,8 +5345,8 @@ function createHooksCommand() {
|
|
|
5299
5345
|
// src/commands/impact-preview.ts
|
|
5300
5346
|
import { Command as Command43 } from "commander";
|
|
5301
5347
|
import { execSync as execSync4 } from "child_process";
|
|
5302
|
-
import * as
|
|
5303
|
-
import * as
|
|
5348
|
+
import * as path28 from "path";
|
|
5349
|
+
import * as fs14 from "fs";
|
|
5304
5350
|
function getStagedFiles(cwd) {
|
|
5305
5351
|
try {
|
|
5306
5352
|
const output = execSync4("git diff --cached --name-only", {
|
|
@@ -5314,7 +5360,7 @@ function getStagedFiles(cwd) {
|
|
|
5314
5360
|
}
|
|
5315
5361
|
function graphExists(projectPath) {
|
|
5316
5362
|
try {
|
|
5317
|
-
return
|
|
5363
|
+
return fs14.existsSync(path28.join(projectPath, ".harness", "graph", "graph.json"));
|
|
5318
5364
|
} catch {
|
|
5319
5365
|
return false;
|
|
5320
5366
|
}
|
|
@@ -5323,7 +5369,7 @@ function extractNodeName(id) {
|
|
|
5323
5369
|
const parts = id.split(":");
|
|
5324
5370
|
if (parts.length > 1) {
|
|
5325
5371
|
const fullPath = parts.slice(1).join(":");
|
|
5326
|
-
return
|
|
5372
|
+
return path28.basename(fullPath);
|
|
5327
5373
|
}
|
|
5328
5374
|
return id;
|
|
5329
5375
|
}
|
|
@@ -5478,7 +5524,7 @@ function formatImpactOutput(stagedFiles, acc, options) {
|
|
|
5478
5524
|
return options.detailed ? formatDetailed(stagedFiles.length, merged, acc.aggregateCounts) : formatCompact(stagedFiles.length, merged, acc.aggregateCounts);
|
|
5479
5525
|
}
|
|
5480
5526
|
async function runImpactPreview(options) {
|
|
5481
|
-
const projectPath =
|
|
5527
|
+
const projectPath = path28.resolve(options.path ?? process.cwd());
|
|
5482
5528
|
const stagedFiles = getStagedFiles(projectPath);
|
|
5483
5529
|
if (stagedFiles.length === 0) return "Impact Preview: no staged changes";
|
|
5484
5530
|
if (!graphExists(projectPath)) {
|
|
@@ -5512,26 +5558,26 @@ function createImpactPreviewCommand() {
|
|
|
5512
5558
|
init_dist3();
|
|
5513
5559
|
import { Command as Command45 } from "commander";
|
|
5514
5560
|
import chalk4 from "chalk";
|
|
5515
|
-
import * as
|
|
5516
|
-
import * as
|
|
5561
|
+
import * as fs17 from "fs";
|
|
5562
|
+
import * as path31 from "path";
|
|
5517
5563
|
|
|
5518
5564
|
// src/commands/setup-mcp.ts
|
|
5519
5565
|
import { Command as Command44 } from "commander";
|
|
5520
|
-
import * as
|
|
5521
|
-
import * as
|
|
5566
|
+
import * as fs16 from "fs";
|
|
5567
|
+
import * as path30 from "path";
|
|
5522
5568
|
import * as os2 from "os";
|
|
5523
5569
|
import chalk3 from "chalk";
|
|
5524
5570
|
import * as clack from "@clack/prompts";
|
|
5525
5571
|
|
|
5526
5572
|
// src/integrations/toml.ts
|
|
5527
|
-
import * as
|
|
5528
|
-
import * as
|
|
5573
|
+
import * as fs15 from "fs";
|
|
5574
|
+
import * as path29 from "path";
|
|
5529
5575
|
function writeTomlMcpEntry(filePath, name, entry) {
|
|
5530
|
-
const dir =
|
|
5531
|
-
if (!
|
|
5532
|
-
|
|
5576
|
+
const dir = path29.dirname(filePath);
|
|
5577
|
+
if (!fs15.existsSync(dir)) {
|
|
5578
|
+
fs15.mkdirSync(dir, { recursive: true });
|
|
5533
5579
|
}
|
|
5534
|
-
const existing =
|
|
5580
|
+
const existing = fs15.existsSync(filePath) ? fs15.readFileSync(filePath, "utf-8") : "";
|
|
5535
5581
|
const blockHeader = `[mcp_servers.${name}]`;
|
|
5536
5582
|
const newBlock = serializeTomlMcpBlock(name, entry);
|
|
5537
5583
|
let updated;
|
|
@@ -5542,8 +5588,8 @@ function writeTomlMcpEntry(filePath, name, entry) {
|
|
|
5542
5588
|
updated = existing + separator + newBlock;
|
|
5543
5589
|
}
|
|
5544
5590
|
const tmp = filePath + ".tmp";
|
|
5545
|
-
|
|
5546
|
-
|
|
5591
|
+
fs15.writeFileSync(tmp, updated);
|
|
5592
|
+
fs15.renameSync(tmp, filePath);
|
|
5547
5593
|
}
|
|
5548
5594
|
function serializeTomlMcpBlock(name, entry) {
|
|
5549
5595
|
const lines = [`[mcp_servers.${name}]`];
|
|
@@ -5664,6 +5710,7 @@ var ALL_MCP_TOOLS = [
|
|
|
5664
5710
|
"ask_graph",
|
|
5665
5711
|
"check_task_independence",
|
|
5666
5712
|
"predict_conflicts",
|
|
5713
|
+
"plan_parallelization",
|
|
5667
5714
|
"detect_stale_constraints",
|
|
5668
5715
|
"search_skills",
|
|
5669
5716
|
"code_outline",
|
|
@@ -5759,20 +5806,20 @@ function writeCursorMcpEntryWithTools(configPath, tools) {
|
|
|
5759
5806
|
});
|
|
5760
5807
|
}
|
|
5761
5808
|
function readJsonFile(filePath) {
|
|
5762
|
-
if (!
|
|
5809
|
+
if (!fs16.existsSync(filePath)) return null;
|
|
5763
5810
|
try {
|
|
5764
|
-
return JSON.parse(
|
|
5811
|
+
return JSON.parse(fs16.readFileSync(filePath, "utf-8"));
|
|
5765
5812
|
} catch {
|
|
5766
|
-
|
|
5813
|
+
fs16.copyFileSync(filePath, filePath + ".bak");
|
|
5767
5814
|
return null;
|
|
5768
5815
|
}
|
|
5769
5816
|
}
|
|
5770
5817
|
function writeJsonFile(filePath, data) {
|
|
5771
|
-
const dir =
|
|
5772
|
-
if (!
|
|
5773
|
-
|
|
5818
|
+
const dir = path30.dirname(filePath);
|
|
5819
|
+
if (!fs16.existsSync(dir)) {
|
|
5820
|
+
fs16.mkdirSync(dir, { recursive: true });
|
|
5774
5821
|
}
|
|
5775
|
-
|
|
5822
|
+
fs16.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
|
|
5776
5823
|
}
|
|
5777
5824
|
function configureMcpServer(configPath) {
|
|
5778
5825
|
const config = readJsonFile(configPath) ?? {};
|
|
@@ -5787,7 +5834,7 @@ function configureMcpServer(configPath) {
|
|
|
5787
5834
|
return true;
|
|
5788
5835
|
}
|
|
5789
5836
|
function addGeminiTrustedFolder(cwd) {
|
|
5790
|
-
const trustedPath =
|
|
5837
|
+
const trustedPath = path30.join(os2.homedir(), ".gemini", "trustedFolders.json");
|
|
5791
5838
|
const folders = readJsonFile(trustedPath) ?? {};
|
|
5792
5839
|
if (folders[cwd] === "TRUST_FOLDER") {
|
|
5793
5840
|
return false;
|
|
@@ -5801,7 +5848,7 @@ function setupMcp(cwd, client) {
|
|
|
5801
5848
|
const skipped = [];
|
|
5802
5849
|
let trustedFolder = false;
|
|
5803
5850
|
if (client === "all" || client === "claude") {
|
|
5804
|
-
const configPath =
|
|
5851
|
+
const configPath = path30.join(cwd, ".mcp.json");
|
|
5805
5852
|
if (configureMcpServer(configPath)) {
|
|
5806
5853
|
configured.push("Claude Code");
|
|
5807
5854
|
} else {
|
|
@@ -5809,7 +5856,7 @@ function setupMcp(cwd, client) {
|
|
|
5809
5856
|
}
|
|
5810
5857
|
}
|
|
5811
5858
|
if (client === "all" || client === "gemini") {
|
|
5812
|
-
const configPath =
|
|
5859
|
+
const configPath = path30.join(cwd, ".gemini", "settings.json");
|
|
5813
5860
|
if (configureMcpServer(configPath)) {
|
|
5814
5861
|
configured.push("Gemini CLI");
|
|
5815
5862
|
} else {
|
|
@@ -5818,10 +5865,10 @@ function setupMcp(cwd, client) {
|
|
|
5818
5865
|
trustedFolder = addGeminiTrustedFolder(cwd);
|
|
5819
5866
|
}
|
|
5820
5867
|
if (client === "all" || client === "codex") {
|
|
5821
|
-
const configPath =
|
|
5868
|
+
const configPath = path30.join(cwd, ".codex", "config.toml");
|
|
5822
5869
|
const alreadyConfigured = (() => {
|
|
5823
|
-
if (!
|
|
5824
|
-
const content =
|
|
5870
|
+
if (!fs16.existsSync(configPath)) return false;
|
|
5871
|
+
const content = fs16.readFileSync(configPath, "utf-8");
|
|
5825
5872
|
return content.includes("[mcp_servers.harness]");
|
|
5826
5873
|
})();
|
|
5827
5874
|
if (alreadyConfigured) {
|
|
@@ -5836,7 +5883,7 @@ function setupMcp(cwd, client) {
|
|
|
5836
5883
|
}
|
|
5837
5884
|
}
|
|
5838
5885
|
if (client === "all" || client === "cursor") {
|
|
5839
|
-
const configPath =
|
|
5886
|
+
const configPath = path30.join(cwd, ".cursor", "mcp.json");
|
|
5840
5887
|
const existing = readJsonFile(configPath);
|
|
5841
5888
|
if (existing?.mcpServers?.["harness"]) {
|
|
5842
5889
|
skipped.push("Cursor");
|
|
@@ -5846,7 +5893,7 @@ function setupMcp(cwd, client) {
|
|
|
5846
5893
|
}
|
|
5847
5894
|
}
|
|
5848
5895
|
if (client === "all" || client === "opencode") {
|
|
5849
|
-
const configPath =
|
|
5896
|
+
const configPath = path30.join(cwd, "opencode.json");
|
|
5850
5897
|
const existing = readJsonFile(configPath);
|
|
5851
5898
|
if (existing?.mcp?.["harness"]) {
|
|
5852
5899
|
skipped.push("OpenCode");
|
|
@@ -5860,7 +5907,7 @@ function setupMcp(cwd, client) {
|
|
|
5860
5907
|
async function resolveCursorWithPicker(cwd, pick) {
|
|
5861
5908
|
const configured = [];
|
|
5862
5909
|
const skipped = [];
|
|
5863
|
-
const cursorConfigPath =
|
|
5910
|
+
const cursorConfigPath = path30.join(cwd, ".cursor", "mcp.json");
|
|
5864
5911
|
const existing = readJsonFile(cursorConfigPath);
|
|
5865
5912
|
if (existing?.mcpServers?.["harness"] && !pick) {
|
|
5866
5913
|
skipped.push("Cursor");
|
|
@@ -5958,12 +6005,12 @@ function loadEngineAndTemplates(_options) {
|
|
|
5958
6005
|
}
|
|
5959
6006
|
function resolveInitDefaults(options) {
|
|
5960
6007
|
const cwd = options.cwd ?? process.cwd();
|
|
5961
|
-
return { cwd, name: options.name ??
|
|
6008
|
+
return { cwd, name: options.name ?? path31.basename(cwd), force: options.force ?? false };
|
|
5962
6009
|
}
|
|
5963
6010
|
async function runInit(options) {
|
|
5964
6011
|
const { cwd, name, force } = resolveInitDefaults(options);
|
|
5965
|
-
const configPath =
|
|
5966
|
-
if (!force &&
|
|
6012
|
+
const configPath = path31.join(cwd, "harness.config.json");
|
|
6013
|
+
if (!force && fs17.existsSync(configPath)) {
|
|
5967
6014
|
return Err(
|
|
5968
6015
|
new CLIError("Project already initialized. Use --force to overwrite.", ExitCode.ERROR)
|
|
5969
6016
|
);
|
|
@@ -6059,15 +6106,15 @@ async function scaffoldProject(engine, ctx) {
|
|
|
6059
6106
|
}
|
|
6060
6107
|
function scaffoldShardedRoadmap(cwd, name) {
|
|
6061
6108
|
if (roadmapSourceExists(cwd)) return [];
|
|
6062
|
-
const shardDir =
|
|
6063
|
-
|
|
6109
|
+
const shardDir = path31.join(cwd, "docs", "roadmap.d");
|
|
6110
|
+
fs17.mkdirSync(shardDir, { recursive: true });
|
|
6064
6111
|
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
6065
6112
|
const meta = {
|
|
6066
6113
|
frontmatter: { project: name, version: 1, lastSynced: nowIso, lastManualEdit: nowIso },
|
|
6067
6114
|
milestones: []
|
|
6068
6115
|
};
|
|
6069
|
-
|
|
6070
|
-
return [
|
|
6116
|
+
fs17.writeFileSync(path31.join(shardDir, "_meta.md"), serializeMeta(meta), "utf-8");
|
|
6117
|
+
return [path31.join("docs", "roadmap.d", "_meta.md")];
|
|
6071
6118
|
}
|
|
6072
6119
|
function printInitSuccess(filesCreated, mcpConfigured) {
|
|
6073
6120
|
console.log("");
|
|
@@ -6162,7 +6209,7 @@ function createInsightsCommand() {
|
|
|
6162
6209
|
return new Command46("insights").description("Composite project report \u2014 health, entropy, decay, attention, impact.").option("--json", "Emit JSON to stdout instead of pretty text").option("--skip <list>", `Comma-separated keys to skip (${INSIGHTS_KEYS.join(",")})`).action(async (opts) => {
|
|
6163
6210
|
const cwd = process.cwd();
|
|
6164
6211
|
const skip = parseSkip2(opts.skip);
|
|
6165
|
-
const { composeInsights } = await import("./dist-
|
|
6212
|
+
const { composeInsights } = await import("./dist-BWFFVKEW.js");
|
|
6166
6213
|
const report = await composeInsights(cwd, { skip });
|
|
6167
6214
|
if (opts.json) {
|
|
6168
6215
|
console.log(JSON.stringify(report, null, 2));
|
|
@@ -6173,16 +6220,16 @@ function createInsightsCommand() {
|
|
|
6173
6220
|
}
|
|
6174
6221
|
|
|
6175
6222
|
// src/commands/install.ts
|
|
6176
|
-
import * as
|
|
6223
|
+
import * as fs22 from "fs";
|
|
6177
6224
|
import * as os5 from "os";
|
|
6178
|
-
import * as
|
|
6225
|
+
import * as path36 from "path";
|
|
6179
6226
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
6180
6227
|
import { Command as Command47 } from "commander";
|
|
6181
6228
|
import { parse as yamlParse } from "yaml";
|
|
6182
6229
|
|
|
6183
6230
|
// src/registry/npm-client.ts
|
|
6184
|
-
import * as
|
|
6185
|
-
import * as
|
|
6231
|
+
import * as fs18 from "fs";
|
|
6232
|
+
import * as path32 from "path";
|
|
6186
6233
|
import * as os3 from "os";
|
|
6187
6234
|
var NPM_REGISTRY = "https://registry.npmjs.org";
|
|
6188
6235
|
var FETCH_TIMEOUT_MS = 3e4;
|
|
@@ -6205,10 +6252,10 @@ function extractSkillName(packageName) {
|
|
|
6205
6252
|
function readNpmrcToken(registryUrl) {
|
|
6206
6253
|
const { hostname: hostname2, pathname } = new URL(registryUrl);
|
|
6207
6254
|
const registryPath = `//${hostname2}${pathname.replace(/\/$/, "")}/:_authToken=`;
|
|
6208
|
-
const candidates = [
|
|
6255
|
+
const candidates = [path32.join(process.cwd(), ".npmrc"), path32.join(os3.homedir(), ".npmrc")];
|
|
6209
6256
|
for (const npmrcPath of candidates) {
|
|
6210
6257
|
try {
|
|
6211
|
-
const content =
|
|
6258
|
+
const content = fs18.readFileSync(npmrcPath, "utf-8");
|
|
6212
6259
|
for (const line of content.split("\n")) {
|
|
6213
6260
|
const trimmed = line.trim();
|
|
6214
6261
|
if (trimmed.startsWith(registryPath)) {
|
|
@@ -6303,19 +6350,19 @@ async function searchNpmRegistry(query, registryUrl) {
|
|
|
6303
6350
|
}
|
|
6304
6351
|
|
|
6305
6352
|
// src/registry/tarball.ts
|
|
6306
|
-
import * as
|
|
6307
|
-
import * as
|
|
6353
|
+
import * as fs19 from "fs";
|
|
6354
|
+
import * as path33 from "path";
|
|
6308
6355
|
import * as os4 from "os";
|
|
6309
6356
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
6310
6357
|
function extractTarball(tarballBuffer) {
|
|
6311
|
-
const tmpDir =
|
|
6312
|
-
const tarballPath =
|
|
6358
|
+
const tmpDir = fs19.mkdtempSync(path33.join(os4.tmpdir(), "harness-skill-install-"));
|
|
6359
|
+
const tarballPath = path33.join(tmpDir, "package.tgz");
|
|
6313
6360
|
try {
|
|
6314
|
-
|
|
6361
|
+
fs19.writeFileSync(tarballPath, tarballBuffer);
|
|
6315
6362
|
execFileSync2("tar", ["-xzf", tarballPath, "-C", tmpDir], {
|
|
6316
6363
|
timeout: 3e4
|
|
6317
6364
|
});
|
|
6318
|
-
|
|
6365
|
+
fs19.unlinkSync(tarballPath);
|
|
6319
6366
|
} catch (err) {
|
|
6320
6367
|
cleanupTempDir(tmpDir);
|
|
6321
6368
|
throw new Error(
|
|
@@ -6326,37 +6373,37 @@ function extractTarball(tarballBuffer) {
|
|
|
6326
6373
|
return tmpDir;
|
|
6327
6374
|
}
|
|
6328
6375
|
function placeSkillContent(extractedPkgDir, communityBaseDir, skillName, platforms) {
|
|
6329
|
-
const files =
|
|
6376
|
+
const files = fs19.readdirSync(extractedPkgDir);
|
|
6330
6377
|
for (const platform of platforms) {
|
|
6331
|
-
const targetDir =
|
|
6332
|
-
if (
|
|
6333
|
-
|
|
6378
|
+
const targetDir = path33.join(communityBaseDir, platform, skillName);
|
|
6379
|
+
if (fs19.existsSync(targetDir)) {
|
|
6380
|
+
fs19.rmSync(targetDir, { recursive: true, force: true });
|
|
6334
6381
|
}
|
|
6335
|
-
|
|
6382
|
+
fs19.mkdirSync(targetDir, { recursive: true });
|
|
6336
6383
|
for (const file of files) {
|
|
6337
6384
|
if (file === "package.json" || file === "node_modules") continue;
|
|
6338
|
-
const srcPath =
|
|
6339
|
-
const destPath =
|
|
6340
|
-
const stat =
|
|
6385
|
+
const srcPath = path33.join(extractedPkgDir, file);
|
|
6386
|
+
const destPath = path33.join(targetDir, file);
|
|
6387
|
+
const stat = fs19.statSync(srcPath);
|
|
6341
6388
|
if (stat.isDirectory()) {
|
|
6342
|
-
|
|
6389
|
+
fs19.cpSync(srcPath, destPath, { recursive: true });
|
|
6343
6390
|
} else {
|
|
6344
|
-
|
|
6391
|
+
fs19.copyFileSync(srcPath, destPath);
|
|
6345
6392
|
}
|
|
6346
6393
|
}
|
|
6347
6394
|
}
|
|
6348
6395
|
}
|
|
6349
6396
|
function removeSkillContent(communityBaseDir, skillName, platforms) {
|
|
6350
6397
|
for (const platform of platforms) {
|
|
6351
|
-
const targetDir =
|
|
6352
|
-
if (
|
|
6353
|
-
|
|
6398
|
+
const targetDir = path33.join(communityBaseDir, platform, skillName);
|
|
6399
|
+
if (fs19.existsSync(targetDir)) {
|
|
6400
|
+
fs19.rmSync(targetDir, { recursive: true, force: true });
|
|
6354
6401
|
}
|
|
6355
6402
|
}
|
|
6356
6403
|
}
|
|
6357
6404
|
function cleanupTempDir(dirPath) {
|
|
6358
6405
|
try {
|
|
6359
|
-
|
|
6406
|
+
fs19.rmSync(dirPath, { recursive: true, force: true });
|
|
6360
6407
|
} catch {
|
|
6361
6408
|
}
|
|
6362
6409
|
}
|
|
@@ -6398,8 +6445,8 @@ function findDependentsOf(lockfile, targetPackageName) {
|
|
|
6398
6445
|
}
|
|
6399
6446
|
|
|
6400
6447
|
// src/registry/lockfile.ts
|
|
6401
|
-
import * as
|
|
6402
|
-
import * as
|
|
6448
|
+
import * as fs20 from "fs";
|
|
6449
|
+
import * as path34 from "path";
|
|
6403
6450
|
function createEmptyLockfile() {
|
|
6404
6451
|
return { version: 1, skills: {} };
|
|
6405
6452
|
}
|
|
@@ -6419,10 +6466,10 @@ function sortedStringify(obj) {
|
|
|
6419
6466
|
);
|
|
6420
6467
|
}
|
|
6421
6468
|
function readLockfile2(filePath) {
|
|
6422
|
-
if (!
|
|
6469
|
+
if (!fs20.existsSync(filePath)) {
|
|
6423
6470
|
return createEmptyLockfile();
|
|
6424
6471
|
}
|
|
6425
|
-
const raw =
|
|
6472
|
+
const raw = fs20.readFileSync(filePath, "utf-8");
|
|
6426
6473
|
let parsed;
|
|
6427
6474
|
try {
|
|
6428
6475
|
parsed = JSON.parse(raw);
|
|
@@ -6439,9 +6486,9 @@ function readLockfile2(filePath) {
|
|
|
6439
6486
|
return parsed;
|
|
6440
6487
|
}
|
|
6441
6488
|
function writeLockfile2(filePath, lockfile) {
|
|
6442
|
-
const dir =
|
|
6443
|
-
|
|
6444
|
-
|
|
6489
|
+
const dir = path34.dirname(filePath);
|
|
6490
|
+
fs20.mkdirSync(dir, { recursive: true });
|
|
6491
|
+
fs20.writeFileSync(filePath, sortedStringify(lockfile) + "\n", "utf-8");
|
|
6445
6492
|
}
|
|
6446
6493
|
function updateLockfileEntry(lockfile, name, entry) {
|
|
6447
6494
|
return {
|
|
@@ -6464,17 +6511,17 @@ function removeLockfileEntry(lockfile, name) {
|
|
|
6464
6511
|
}
|
|
6465
6512
|
|
|
6466
6513
|
// src/registry/bundled-skills.ts
|
|
6467
|
-
import * as
|
|
6468
|
-
import * as
|
|
6514
|
+
import * as fs21 from "fs";
|
|
6515
|
+
import * as path35 from "path";
|
|
6469
6516
|
function getBundledSkillNames(bundledSkillsDir) {
|
|
6470
|
-
if (!
|
|
6517
|
+
if (!fs21.existsSync(bundledSkillsDir)) {
|
|
6471
6518
|
return /* @__PURE__ */ new Set();
|
|
6472
6519
|
}
|
|
6473
|
-
const entries =
|
|
6520
|
+
const entries = fs21.readdirSync(bundledSkillsDir);
|
|
6474
6521
|
const names = /* @__PURE__ */ new Set();
|
|
6475
6522
|
for (const entry of entries) {
|
|
6476
6523
|
try {
|
|
6477
|
-
const stat =
|
|
6524
|
+
const stat = fs21.statSync(path35.join(bundledSkillsDir, String(entry)));
|
|
6478
6525
|
if (stat.isDirectory()) {
|
|
6479
6526
|
names.add(String(entry));
|
|
6480
6527
|
}
|
|
@@ -6502,12 +6549,12 @@ function validateSkillYaml(parsed) {
|
|
|
6502
6549
|
function resolveCommunityBase(global) {
|
|
6503
6550
|
if (global) {
|
|
6504
6551
|
const communityBase2 = resolveGlobalCommunityBaseDir();
|
|
6505
|
-
return { communityBase: communityBase2, lockfilePath:
|
|
6552
|
+
return { communityBase: communityBase2, lockfilePath: path36.join(communityBase2, "skills-lock.json") };
|
|
6506
6553
|
}
|
|
6507
6554
|
const globalDir = resolveGlobalSkillsDir();
|
|
6508
|
-
const skillsDir =
|
|
6509
|
-
const communityBase =
|
|
6510
|
-
return { communityBase, lockfilePath:
|
|
6555
|
+
const skillsDir = path36.dirname(globalDir);
|
|
6556
|
+
const communityBase = path36.join(skillsDir, "community");
|
|
6557
|
+
return { communityBase, lockfilePath: path36.join(communityBase, "skills-lock.json") };
|
|
6511
6558
|
}
|
|
6512
6559
|
function parseGitHubRef(from) {
|
|
6513
6560
|
const ghPrefix = from.match(/^github:([^/]+)\/([^#]+?)(?:#(.+))?$/);
|
|
@@ -6523,7 +6570,7 @@ function parseGitHubRef(from) {
|
|
|
6523
6570
|
return null;
|
|
6524
6571
|
}
|
|
6525
6572
|
function cloneGitHubRepo(owner, repo, ref) {
|
|
6526
|
-
const tmpDir =
|
|
6573
|
+
const tmpDir = fs22.mkdtempSync(path36.join(os5.tmpdir(), "harness-gh-install-"));
|
|
6527
6574
|
const url = `https://github.com/${owner}/${repo}.git`;
|
|
6528
6575
|
try {
|
|
6529
6576
|
const cloneArgs = ["clone", "--depth", "1"];
|
|
@@ -6544,43 +6591,43 @@ function discoverSkillDirs(rootDir) {
|
|
|
6544
6591
|
const skillDirs = [];
|
|
6545
6592
|
function scan(dir, depth) {
|
|
6546
6593
|
if (depth > 3) return;
|
|
6547
|
-
if (!
|
|
6548
|
-
if (
|
|
6594
|
+
if (!fs22.existsSync(dir)) return;
|
|
6595
|
+
if (fs22.existsSync(path36.join(dir, "skill.yaml"))) {
|
|
6549
6596
|
skillDirs.push(dir);
|
|
6550
6597
|
return;
|
|
6551
6598
|
}
|
|
6552
|
-
const entries =
|
|
6599
|
+
const entries = fs22.readdirSync(dir, { withFileTypes: true });
|
|
6553
6600
|
for (const entry of entries) {
|
|
6554
6601
|
if (!entry.isDirectory()) continue;
|
|
6555
6602
|
if (entry.name.startsWith(".") || DEFAULT_SKIP_DIRS.has(entry.name)) continue;
|
|
6556
|
-
scan(
|
|
6603
|
+
scan(path36.join(dir, entry.name), depth + 1);
|
|
6557
6604
|
}
|
|
6558
6605
|
}
|
|
6559
6606
|
scan(rootDir, 0);
|
|
6560
6607
|
return skillDirs;
|
|
6561
6608
|
}
|
|
6562
6609
|
function resolveLocalPkgDir(fromPath) {
|
|
6563
|
-
const resolvedPath =
|
|
6564
|
-
if (!
|
|
6610
|
+
const resolvedPath = path36.resolve(fromPath);
|
|
6611
|
+
if (!fs22.existsSync(resolvedPath)) {
|
|
6565
6612
|
throw new Error(`--from path does not exist: ${resolvedPath}`);
|
|
6566
6613
|
}
|
|
6567
|
-
const stat =
|
|
6614
|
+
const stat = fs22.statSync(resolvedPath);
|
|
6568
6615
|
if (stat.isDirectory()) {
|
|
6569
6616
|
return { pkgDir: resolvedPath, extractDir: null };
|
|
6570
6617
|
}
|
|
6571
6618
|
if (resolvedPath.endsWith(".tgz") || resolvedPath.endsWith(".tar.gz")) {
|
|
6572
|
-
const tarballBuffer =
|
|
6619
|
+
const tarballBuffer = fs22.readFileSync(resolvedPath);
|
|
6573
6620
|
const extractDir = extractTarball(tarballBuffer);
|
|
6574
|
-
return { pkgDir:
|
|
6621
|
+
return { pkgDir: path36.join(extractDir, "package"), extractDir };
|
|
6575
6622
|
}
|
|
6576
6623
|
throw new Error(`--from path must be a directory or .tgz file. Got: ${resolvedPath}`);
|
|
6577
6624
|
}
|
|
6578
6625
|
function installSkillDir(pkgDir, resolvedPath, options) {
|
|
6579
|
-
const skillYamlPath =
|
|
6580
|
-
if (!
|
|
6626
|
+
const skillYamlPath = path36.join(pkgDir, "skill.yaml");
|
|
6627
|
+
if (!fs22.existsSync(skillYamlPath)) {
|
|
6581
6628
|
throw new Error(`No skill.yaml found at ${skillYamlPath}`);
|
|
6582
6629
|
}
|
|
6583
|
-
const skillYaml = validateSkillYaml(yamlParse(
|
|
6630
|
+
const skillYaml = validateSkillYaml(yamlParse(fs22.readFileSync(skillYamlPath, "utf-8")));
|
|
6584
6631
|
const shortName = skillYaml.name;
|
|
6585
6632
|
const { communityBase, lockfilePath } = resolveCommunityBase(options.global ?? false);
|
|
6586
6633
|
if (!options.global) {
|
|
@@ -6608,7 +6655,7 @@ function installSkillDir(pkgDir, resolvedPath, options) {
|
|
|
6608
6655
|
async function runLocalInstall(fromPath, options) {
|
|
6609
6656
|
const { pkgDir, extractDir } = resolveLocalPkgDir(fromPath);
|
|
6610
6657
|
try {
|
|
6611
|
-
return installSkillDir(pkgDir,
|
|
6658
|
+
return installSkillDir(pkgDir, path36.resolve(fromPath), options);
|
|
6612
6659
|
} finally {
|
|
6613
6660
|
if (extractDir) cleanupTempDir(extractDir);
|
|
6614
6661
|
}
|
|
@@ -6651,8 +6698,8 @@ async function runInstall(skillName, options) {
|
|
|
6651
6698
|
version: installed.map((r) => r.version).join(", ")
|
|
6652
6699
|
};
|
|
6653
6700
|
}
|
|
6654
|
-
const resolvedFrom =
|
|
6655
|
-
if (
|
|
6701
|
+
const resolvedFrom = path36.resolve(options.from);
|
|
6702
|
+
if (fs22.existsSync(resolvedFrom) && fs22.statSync(resolvedFrom).isDirectory() && !fs22.existsSync(path36.join(resolvedFrom, "skill.yaml"))) {
|
|
6656
6703
|
const results = await runBulkInstall(resolvedFrom, options);
|
|
6657
6704
|
const installed = results.filter((r) => r.installed);
|
|
6658
6705
|
return {
|
|
@@ -6693,12 +6740,12 @@ async function runInstall(skillName, options) {
|
|
|
6693
6740
|
const extractDir = extractTarball(tarballBuffer);
|
|
6694
6741
|
let skillYaml;
|
|
6695
6742
|
try {
|
|
6696
|
-
const extractedPkgDir =
|
|
6697
|
-
const skillYamlPath =
|
|
6698
|
-
if (!
|
|
6743
|
+
const extractedPkgDir = path36.join(extractDir, "package");
|
|
6744
|
+
const skillYamlPath = path36.join(extractedPkgDir, "skill.yaml");
|
|
6745
|
+
if (!fs22.existsSync(skillYamlPath)) {
|
|
6699
6746
|
throw new Error(`contains invalid skill.yaml: file not found in package`);
|
|
6700
6747
|
}
|
|
6701
|
-
const rawYaml =
|
|
6748
|
+
const rawYaml = fs22.readFileSync(skillYamlPath, "utf-8");
|
|
6702
6749
|
const parsed = yamlParse(rawYaml);
|
|
6703
6750
|
skillYaml = validateSkillYaml(parsed);
|
|
6704
6751
|
placeSkillContent(extractedPkgDir, communityBase, shortName, skillYaml.platforms);
|
|
@@ -6772,15 +6819,15 @@ function createInstallCommand() {
|
|
|
6772
6819
|
|
|
6773
6820
|
// src/commands/install-constraints.ts
|
|
6774
6821
|
init_dist3();
|
|
6775
|
-
import * as
|
|
6776
|
-
import * as
|
|
6822
|
+
import * as fs23 from "fs/promises";
|
|
6823
|
+
import * as path37 from "path";
|
|
6777
6824
|
import { Command as Command48 } from "commander";
|
|
6778
6825
|
import semver3 from "semver";
|
|
6779
6826
|
async function runInstallConstraints(options) {
|
|
6780
6827
|
const { source, configPath, lockfilePath } = options;
|
|
6781
6828
|
let rawBundle;
|
|
6782
6829
|
try {
|
|
6783
|
-
rawBundle = await
|
|
6830
|
+
rawBundle = await fs23.readFile(source, "utf-8");
|
|
6784
6831
|
} catch (err) {
|
|
6785
6832
|
if (isNodeError(err) && err.code === "ENOENT") {
|
|
6786
6833
|
return { ok: false, error: `Bundle file not found: ${source}` };
|
|
@@ -6823,7 +6870,7 @@ async function runInstallConstraints(options) {
|
|
|
6823
6870
|
}
|
|
6824
6871
|
let localConfig;
|
|
6825
6872
|
try {
|
|
6826
|
-
const raw = await
|
|
6873
|
+
const raw = await fs23.readFile(configPath, "utf-8");
|
|
6827
6874
|
localConfig = JSON.parse(raw);
|
|
6828
6875
|
} catch (err) {
|
|
6829
6876
|
return {
|
|
@@ -6971,7 +7018,7 @@ function isNodeError(err) {
|
|
|
6971
7018
|
return err instanceof Error && "code" in err;
|
|
6972
7019
|
}
|
|
6973
7020
|
function resolveConfigPath(opts) {
|
|
6974
|
-
if (opts.config) return
|
|
7021
|
+
if (opts.config) return path37.resolve(opts.config);
|
|
6975
7022
|
const found = findConfigFile();
|
|
6976
7023
|
if (!found.ok) {
|
|
6977
7024
|
logger.error(found.error.message);
|
|
@@ -7006,9 +7053,9 @@ function logInstallResult(val, opts) {
|
|
|
7006
7053
|
}
|
|
7007
7054
|
async function handleInstallConstraints(source, opts) {
|
|
7008
7055
|
const configPath = resolveConfigPath(opts);
|
|
7009
|
-
const
|
|
7010
|
-
const lockfilePath =
|
|
7011
|
-
const resolvedSource =
|
|
7056
|
+
const projectRoot3 = path37.dirname(configPath);
|
|
7057
|
+
const lockfilePath = path37.join(projectRoot3, ".harness", "constraints.lock.json");
|
|
7058
|
+
const resolvedSource = path37.resolve(source);
|
|
7012
7059
|
if (opts.forceLocal && opts.forcePackage) {
|
|
7013
7060
|
logger.error("Cannot use both --force-local and --force-package.");
|
|
7014
7061
|
process.exit(1);
|
|
@@ -7039,8 +7086,8 @@ import { Command as Command53 } from "commander";
|
|
|
7039
7086
|
// src/commands/integrations/add.ts
|
|
7040
7087
|
init_dist3();
|
|
7041
7088
|
import { Command as Command49 } from "commander";
|
|
7042
|
-
import * as
|
|
7043
|
-
import * as
|
|
7089
|
+
import * as fs24 from "fs";
|
|
7090
|
+
import * as path38 from "path";
|
|
7044
7091
|
import chalk5 from "chalk";
|
|
7045
7092
|
function buildMcpEntry(def) {
|
|
7046
7093
|
const entry = { command: def.mcpConfig.command };
|
|
@@ -7049,14 +7096,14 @@ function buildMcpEntry(def) {
|
|
|
7049
7096
|
return entry;
|
|
7050
7097
|
}
|
|
7051
7098
|
function writeMcpEntries(cwd, defName, mcpEntry) {
|
|
7052
|
-
writeMcpEntry(
|
|
7053
|
-
const geminiDir =
|
|
7054
|
-
if (
|
|
7055
|
-
writeMcpEntry(
|
|
7099
|
+
writeMcpEntry(path38.join(cwd, ".mcp.json"), defName, mcpEntry);
|
|
7100
|
+
const geminiDir = path38.join(cwd, ".gemini");
|
|
7101
|
+
if (fs24.existsSync(geminiDir)) {
|
|
7102
|
+
writeMcpEntry(path38.join(geminiDir, "settings.json"), defName, mcpEntry);
|
|
7056
7103
|
}
|
|
7057
7104
|
}
|
|
7058
7105
|
function updateIntegrationsConfig(cwd, defName) {
|
|
7059
|
-
const configPath =
|
|
7106
|
+
const configPath = path38.join(cwd, "harness.config.json");
|
|
7060
7107
|
const integConfig = readIntegrationsConfig(configPath);
|
|
7061
7108
|
if (!integConfig.enabled.includes(defName)) integConfig.enabled.push(defName);
|
|
7062
7109
|
integConfig.dismissed = integConfig.dismissed.filter((d) => d !== defName);
|
|
@@ -7117,7 +7164,7 @@ function createAddIntegrationCommand() {
|
|
|
7117
7164
|
|
|
7118
7165
|
// src/commands/integrations/list.ts
|
|
7119
7166
|
import { Command as Command50 } from "commander";
|
|
7120
|
-
import * as
|
|
7167
|
+
import * as path39 from "path";
|
|
7121
7168
|
import chalk6 from "chalk";
|
|
7122
7169
|
function printTier0Integrations(tier0, mcpServers) {
|
|
7123
7170
|
console.log(" Tier 0 (zero-config):");
|
|
@@ -7141,8 +7188,8 @@ function printTier1Integrations(tier1, mcpServers, dismissed) {
|
|
|
7141
7188
|
}
|
|
7142
7189
|
async function runListIntegrations(globalOpts) {
|
|
7143
7190
|
const cwd = process.cwd();
|
|
7144
|
-
const mcpConfig = readMcpConfig(
|
|
7145
|
-
const integConfig = readIntegrationsConfig(
|
|
7191
|
+
const mcpConfig = readMcpConfig(path39.join(cwd, ".mcp.json"));
|
|
7192
|
+
const integConfig = readIntegrationsConfig(path39.join(cwd, "harness.config.json"));
|
|
7146
7193
|
const mcpServers = mcpConfig.mcpServers ?? {};
|
|
7147
7194
|
if (globalOpts.json) {
|
|
7148
7195
|
const entries = INTEGRATION_REGISTRY.map((i) => ({
|
|
@@ -7179,8 +7226,8 @@ function createListIntegrationsCommand() {
|
|
|
7179
7226
|
// src/commands/integrations/remove.ts
|
|
7180
7227
|
init_dist3();
|
|
7181
7228
|
import { Command as Command51 } from "commander";
|
|
7182
|
-
import * as
|
|
7183
|
-
import * as
|
|
7229
|
+
import * as fs25 from "fs";
|
|
7230
|
+
import * as path40 from "path";
|
|
7184
7231
|
function removeIntegration(cwd, name) {
|
|
7185
7232
|
const def = INTEGRATION_REGISTRY.find((i) => i.name === name);
|
|
7186
7233
|
if (!def) {
|
|
@@ -7191,14 +7238,14 @@ function removeIntegration(cwd, name) {
|
|
|
7191
7238
|
)
|
|
7192
7239
|
);
|
|
7193
7240
|
}
|
|
7194
|
-
const mcpPath =
|
|
7241
|
+
const mcpPath = path40.join(cwd, ".mcp.json");
|
|
7195
7242
|
removeMcpEntry(mcpPath, def.name);
|
|
7196
|
-
const geminiDir =
|
|
7197
|
-
if (
|
|
7198
|
-
const geminiPath =
|
|
7243
|
+
const geminiDir = path40.join(cwd, ".gemini");
|
|
7244
|
+
if (fs25.existsSync(geminiDir)) {
|
|
7245
|
+
const geminiPath = path40.join(geminiDir, "settings.json");
|
|
7199
7246
|
removeMcpEntry(geminiPath, def.name);
|
|
7200
7247
|
}
|
|
7201
|
-
const configPath =
|
|
7248
|
+
const configPath = path40.join(cwd, "harness.config.json");
|
|
7202
7249
|
const integConfig = readIntegrationsConfig(configPath);
|
|
7203
7250
|
integConfig.enabled = integConfig.enabled.filter((e) => e !== def.name);
|
|
7204
7251
|
writeIntegrationsConfig(configPath, integConfig);
|
|
@@ -7228,7 +7275,7 @@ function createRemoveIntegrationCommand() {
|
|
|
7228
7275
|
// src/commands/integrations/dismiss.ts
|
|
7229
7276
|
init_dist3();
|
|
7230
7277
|
import { Command as Command52 } from "commander";
|
|
7231
|
-
import * as
|
|
7278
|
+
import * as path41 from "path";
|
|
7232
7279
|
function dismissIntegration(cwd, name) {
|
|
7233
7280
|
const def = INTEGRATION_REGISTRY.find((i) => i.name === name);
|
|
7234
7281
|
if (!def) {
|
|
@@ -7239,7 +7286,7 @@ function dismissIntegration(cwd, name) {
|
|
|
7239
7286
|
)
|
|
7240
7287
|
);
|
|
7241
7288
|
}
|
|
7242
|
-
const configPath =
|
|
7289
|
+
const configPath = path41.join(cwd, "harness.config.json");
|
|
7243
7290
|
const integConfig = readIntegrationsConfig(configPath);
|
|
7244
7291
|
if (!integConfig.dismissed.includes(def.name)) {
|
|
7245
7292
|
integConfig.dismissed.push(def.name);
|
|
@@ -7334,10 +7381,10 @@ function printResult3(result, mode, _formatter) {
|
|
|
7334
7381
|
list.push(f);
|
|
7335
7382
|
byFile.set(f.target.file, list);
|
|
7336
7383
|
}
|
|
7337
|
-
for (const [file,
|
|
7384
|
+
for (const [file, fs51] of byFile) {
|
|
7338
7385
|
console.log(`
|
|
7339
7386
|
${file}`);
|
|
7340
|
-
for (const f of
|
|
7387
|
+
for (const f of fs51) {
|
|
7341
7388
|
console.log(` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.relative}`);
|
|
7342
7389
|
console.log(` ${f.message}`);
|
|
7343
7390
|
if (verbose) console.log(` source: ${f.cite.source}`);
|
|
@@ -7352,7 +7399,7 @@ ${file}`);
|
|
|
7352
7399
|
|
|
7353
7400
|
// src/commands/knowledge-pipeline.ts
|
|
7354
7401
|
import { Command as Command55 } from "commander";
|
|
7355
|
-
import * as
|
|
7402
|
+
import * as path42 from "path";
|
|
7356
7403
|
import chalk7 from "chalk";
|
|
7357
7404
|
function resolveInferenceOptions(cfgKnowledge) {
|
|
7358
7405
|
const patterns = cfgKnowledge?.domainPatterns;
|
|
@@ -7519,9 +7566,9 @@ function createKnowledgePipelineCommand() {
|
|
|
7519
7566
|
const globalOpts = cmd.optsWithGlobals();
|
|
7520
7567
|
const projectDir = process.cwd();
|
|
7521
7568
|
try {
|
|
7522
|
-
const graphDir =
|
|
7523
|
-
const
|
|
7524
|
-
await
|
|
7569
|
+
const graphDir = path42.join(projectDir, ".harness", "graph");
|
|
7570
|
+
const fs51 = await import("fs/promises");
|
|
7571
|
+
await fs51.mkdir(graphDir, { recursive: true });
|
|
7525
7572
|
const { GraphStore: GraphStore2, KnowledgePipelineRunner } = await import("./dist-DWBTZXR5.js");
|
|
7526
7573
|
const store = new GraphStore2();
|
|
7527
7574
|
try {
|
|
@@ -7566,9 +7613,9 @@ import { Command as Command57 } from "commander";
|
|
|
7566
7613
|
// src/commands/learnings/prune.ts
|
|
7567
7614
|
init_dist3();
|
|
7568
7615
|
import { Command as Command56 } from "commander";
|
|
7569
|
-
import * as
|
|
7616
|
+
import * as path43 from "path";
|
|
7570
7617
|
async function handlePrune(opts) {
|
|
7571
|
-
const projectPath =
|
|
7618
|
+
const projectPath = path43.resolve(opts.path);
|
|
7572
7619
|
const result = await pruneLearnings(projectPath, opts.stream);
|
|
7573
7620
|
if (!result.ok) {
|
|
7574
7621
|
logger.error(result.error.message);
|
|
@@ -7720,21 +7767,21 @@ function createLinterCommand() {
|
|
|
7720
7767
|
|
|
7721
7768
|
// src/commands/maintenance.ts
|
|
7722
7769
|
import { Command as Command61 } from "commander";
|
|
7723
|
-
import * as
|
|
7770
|
+
import * as path46 from "path";
|
|
7724
7771
|
import chalk8 from "chalk";
|
|
7725
7772
|
import { TaskOutputStore as TaskOutputStore2 } from "@harness-engineering/orchestrator";
|
|
7726
7773
|
|
|
7727
7774
|
// src/commands/maintenance-config.ts
|
|
7728
|
-
import * as
|
|
7729
|
-
import * as
|
|
7775
|
+
import * as path44 from "path";
|
|
7776
|
+
import * as fs26 from "fs";
|
|
7730
7777
|
import {
|
|
7731
7778
|
BUILT_IN_TASKS,
|
|
7732
7779
|
WorkflowLoader,
|
|
7733
7780
|
migrateAgentConfig
|
|
7734
7781
|
} from "@harness-engineering/orchestrator";
|
|
7735
7782
|
async function loadMaintenanceConfig(cwd) {
|
|
7736
|
-
const workflowPath =
|
|
7737
|
-
if (!
|
|
7783
|
+
const workflowPath = path44.join(cwd, "harness.orchestrator.md");
|
|
7784
|
+
if (!fs26.existsSync(workflowPath)) return null;
|
|
7738
7785
|
const loader = new WorkflowLoader();
|
|
7739
7786
|
const result = await loader.loadWorkflow(workflowPath);
|
|
7740
7787
|
if (!result.ok) return null;
|
|
@@ -7742,8 +7789,8 @@ async function loadMaintenanceConfig(cwd) {
|
|
|
7742
7789
|
return result.value.config.maintenance ?? null;
|
|
7743
7790
|
}
|
|
7744
7791
|
async function loadAgentBackends(cwd) {
|
|
7745
|
-
const workflowPath =
|
|
7746
|
-
if (!
|
|
7792
|
+
const workflowPath = path44.join(cwd, "harness.orchestrator.md");
|
|
7793
|
+
if (!fs26.existsSync(workflowPath)) return null;
|
|
7747
7794
|
const loader = new WorkflowLoader();
|
|
7748
7795
|
const result = await loader.loadWorkflow(workflowPath);
|
|
7749
7796
|
if (!result.ok) return null;
|
|
@@ -7814,9 +7861,9 @@ function copyOptional(src, dst, keys) {
|
|
|
7814
7861
|
}
|
|
7815
7862
|
|
|
7816
7863
|
// src/commands/maintenance-run.ts
|
|
7817
|
-
import * as
|
|
7864
|
+
import * as path45 from "path";
|
|
7818
7865
|
import * as os6 from "os";
|
|
7819
|
-
import * as
|
|
7866
|
+
import * as fs27 from "fs";
|
|
7820
7867
|
import { execFile, execFileSync as execFileSync4 } from "child_process";
|
|
7821
7868
|
import { promisify } from "util";
|
|
7822
7869
|
import {
|
|
@@ -7909,7 +7956,7 @@ function createFixDispatcher(resolveBackend, git = realGit) {
|
|
|
7909
7956
|
}
|
|
7910
7957
|
function buildTaskRunner(cwd, config, mode, harnessEntry, resolveBackend = () => null) {
|
|
7911
7958
|
const outputStore = new TaskOutputStore({
|
|
7912
|
-
rootDir:
|
|
7959
|
+
rootDir: path45.join(cwd, ".harness", "maintenance")
|
|
7913
7960
|
});
|
|
7914
7961
|
return new TaskRunner({
|
|
7915
7962
|
config,
|
|
@@ -7924,7 +7971,7 @@ function buildTaskRunner(cwd, config, mode, harnessEntry, resolveBackend = () =>
|
|
|
7924
7971
|
}
|
|
7925
7972
|
async function loadRunHistory(cwd) {
|
|
7926
7973
|
const reporter = new MaintenanceReporter({
|
|
7927
|
-
persistDir:
|
|
7974
|
+
persistDir: path45.join(cwd, ".harness", "maintenance")
|
|
7928
7975
|
});
|
|
7929
7976
|
await reporter.load();
|
|
7930
7977
|
return reporter.getHistory(500, 0);
|
|
@@ -8032,16 +8079,16 @@ function renderTable(report) {
|
|
|
8032
8079
|
var NO_BACKEND_FIX_WARNING = "--fix: no agent backend configured for maintenance dispatch \u2014 dispatch was skipped and nothing was fixed. Configure agent.backends in harness.orchestrator.md (and maintenance.aiBackend), or run maintenance via the orchestrator.";
|
|
8033
8080
|
async function defaultRecord(cwd, results) {
|
|
8034
8081
|
const reporter = new MaintenanceReporter({
|
|
8035
|
-
persistDir:
|
|
8082
|
+
persistDir: path45.join(cwd, ".harness", "maintenance")
|
|
8036
8083
|
});
|
|
8037
8084
|
await reporter.load();
|
|
8038
8085
|
for (const r of results) await reporter.record(r);
|
|
8039
8086
|
}
|
|
8040
8087
|
function writeSummary(cwd, report) {
|
|
8041
|
-
const dir =
|
|
8042
|
-
|
|
8043
|
-
|
|
8044
|
-
|
|
8088
|
+
const dir = path45.join(cwd, ".harness", "maintenance");
|
|
8089
|
+
fs27.mkdirSync(dir, { recursive: true });
|
|
8090
|
+
fs27.writeFileSync(
|
|
8091
|
+
path45.join(dir, "last-run-summary.json"),
|
|
8045
8092
|
JSON.stringify(report, null, 2),
|
|
8046
8093
|
"utf-8"
|
|
8047
8094
|
);
|
|
@@ -8168,7 +8215,7 @@ function createMaintenanceCommand() {
|
|
|
8168
8215
|
"Inspect built-in + custom maintenance tasks and their persisted outputs"
|
|
8169
8216
|
);
|
|
8170
8217
|
command.command("list").description("List all resolved maintenance tasks (built-in + customTasks)").option("--json", "Emit machine-readable JSON", false).option("--path <path>", "Project root path", ".").action(async (opts) => {
|
|
8171
|
-
const cwd =
|
|
8218
|
+
const cwd = path46.resolve(opts.path);
|
|
8172
8219
|
const config = await loadMaintenanceConfig(cwd);
|
|
8173
8220
|
const tasks = mergeResolvedTasks(config);
|
|
8174
8221
|
const rows = tasks.map((t) => ({
|
|
@@ -8189,7 +8236,7 @@ function createMaintenanceCommand() {
|
|
|
8189
8236
|
process.exit(ExitCode.SUCCESS);
|
|
8190
8237
|
});
|
|
8191
8238
|
command.command("run [taskId...]").description("Run overdue (default) / selected maintenance tasks; report-first unless --fix").option("--all", "Run all sweep-eligible tasks (not just overdue)", false).option("--only <ids>", "Comma-separated task ids to run").option("--skip <ids>", "Comma-separated task ids to exclude").option("--fix", "Dispatch fixes per task type (default: report-only)", false).option("--concurrency <n>", "Max parallel tasks (report mode); --fix forces 1").option("--json", "Emit machine-readable consolidated report", false).option("--path <path>", "Project root path", ".").action(async (taskIds, opts, command2) => {
|
|
8192
|
-
const cwd =
|
|
8239
|
+
const cwd = path46.resolve(opts.path);
|
|
8193
8240
|
const json = Boolean(command2.optsWithGlobals().json);
|
|
8194
8241
|
const result = await runMaintenanceRun(cwd, {
|
|
8195
8242
|
positional: taskIds,
|
|
@@ -8209,10 +8256,10 @@ async function runShow(taskId, opts) {
|
|
|
8209
8256
|
console.error(`Invalid task id '${taskId}' (must match ^[a-z0-9][a-z0-9-]*$).`);
|
|
8210
8257
|
process.exit(ExitCode.ERROR);
|
|
8211
8258
|
}
|
|
8212
|
-
const cwd =
|
|
8259
|
+
const cwd = path46.resolve(opts.path ?? ".");
|
|
8213
8260
|
const limit = Math.max(1, parseInt(opts.limit ?? "5", 10) || 5);
|
|
8214
8261
|
const store = new TaskOutputStore2({
|
|
8215
|
-
rootDir:
|
|
8262
|
+
rootDir: path46.join(cwd, ".harness", "maintenance")
|
|
8216
8263
|
});
|
|
8217
8264
|
const entries = await store.list(taskId, limit, 0);
|
|
8218
8265
|
renderShow(taskId, entries, Boolean(opts.json));
|
|
@@ -8273,7 +8320,7 @@ function createMcpCommand() {
|
|
|
8273
8320
|
parseBudget
|
|
8274
8321
|
).action(async (opts) => {
|
|
8275
8322
|
const [{ startServer: startServer2, getToolDefinitions: getToolDefinitions2 }, { selectTier }] = await Promise.all([
|
|
8276
|
-
import("./mcp-
|
|
8323
|
+
import("./mcp-RIPEYDMU.js"),
|
|
8277
8324
|
import("./tool-tiers-OKK5FE57.js")
|
|
8278
8325
|
]);
|
|
8279
8326
|
if (opts.tools && opts.tools.length > 0) {
|
|
@@ -8296,8 +8343,8 @@ function createMcpCommand() {
|
|
|
8296
8343
|
// src/commands/mcp-guard.ts
|
|
8297
8344
|
init_dist3();
|
|
8298
8345
|
import { Command as Command63 } from "commander";
|
|
8299
|
-
import * as
|
|
8300
|
-
import * as
|
|
8346
|
+
import * as fs28 from "fs";
|
|
8347
|
+
import * as path47 from "path";
|
|
8301
8348
|
import chalk9 from "chalk";
|
|
8302
8349
|
function extractNpmPackages(config) {
|
|
8303
8350
|
const out = [];
|
|
@@ -8336,18 +8383,18 @@ function parseNpmSpec(spec) {
|
|
|
8336
8383
|
}
|
|
8337
8384
|
async function runMcpGuardCheck(options = {}) {
|
|
8338
8385
|
const cwd = options.cwd ?? process.cwd();
|
|
8339
|
-
const configPath = options.configPath ??
|
|
8340
|
-
if (!
|
|
8386
|
+
const configPath = options.configPath ?? path47.join(cwd, ".mcp.json");
|
|
8387
|
+
if (!fs28.existsSync(configPath)) {
|
|
8341
8388
|
return { ok: true, checked: [] };
|
|
8342
8389
|
}
|
|
8343
8390
|
let config;
|
|
8344
8391
|
try {
|
|
8345
|
-
config = JSON.parse(
|
|
8392
|
+
config = JSON.parse(fs28.readFileSync(configPath, "utf-8"));
|
|
8346
8393
|
} catch {
|
|
8347
8394
|
return { ok: true, checked: [] };
|
|
8348
8395
|
}
|
|
8349
8396
|
const pkgs = extractNpmPackages(config);
|
|
8350
|
-
const cacheDir =
|
|
8397
|
+
const cacheDir = path47.join(cwd, ".harness", "cache", "osv");
|
|
8351
8398
|
const warnLogger = {
|
|
8352
8399
|
warn: (m, ctx) => logger.warn(ctx ? `${m} ${JSON.stringify(ctx)}` : m)
|
|
8353
8400
|
};
|
|
@@ -8408,7 +8455,7 @@ function createMcpGuardCommand() {
|
|
|
8408
8455
|
'\nUse "harness mcp-guard check" before launching MCP servers to block known malicious packages.'
|
|
8409
8456
|
);
|
|
8410
8457
|
command.command("check").description("Check every MCP/npx package in .mcp.json against OSV.dev advisories").option("--strict", "Fail closed on network errors (default: fail-open)", false).option("--json", "Emit machine-readable JSON", false).option("--path <path>", "Project root path", ".").action(async (opts) => {
|
|
8411
|
-
const cwd =
|
|
8458
|
+
const cwd = path47.resolve(opts.path);
|
|
8412
8459
|
const result = await runMcpGuardCheck({ cwd, strict: opts.strict, json: opts.json });
|
|
8413
8460
|
if (opts.json) {
|
|
8414
8461
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -8419,10 +8466,10 @@ function createMcpGuardCommand() {
|
|
|
8419
8466
|
process.exit(result.ok ? ExitCode.SUCCESS : 2);
|
|
8420
8467
|
});
|
|
8421
8468
|
command.command("cache").description("Manage the on-disk OSV advisory cache").command("clear").option("--path <path>", "Project root path", ".").action(async (opts) => {
|
|
8422
|
-
const cwd =
|
|
8423
|
-
const cacheDir =
|
|
8469
|
+
const cwd = path47.resolve(opts.path);
|
|
8470
|
+
const cacheDir = path47.join(cwd, ".harness", "cache", "osv");
|
|
8424
8471
|
try {
|
|
8425
|
-
|
|
8472
|
+
fs28.rmSync(cacheDir, { recursive: true, force: true });
|
|
8426
8473
|
console.log(`Cleared ${cacheDir}`);
|
|
8427
8474
|
} catch (err) {
|
|
8428
8475
|
logger.error(`Failed to clear cache: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -8438,8 +8485,8 @@ import { Command as Command65 } from "commander";
|
|
|
8438
8485
|
|
|
8439
8486
|
// src/commands/migrate-backends.ts
|
|
8440
8487
|
import { Command as Command64 } from "commander";
|
|
8441
|
-
import * as
|
|
8442
|
-
import * as
|
|
8488
|
+
import * as fs29 from "fs";
|
|
8489
|
+
import * as path48 from "path";
|
|
8443
8490
|
import chalk10 from "chalk";
|
|
8444
8491
|
import { parse as parseYaml2 } from "yaml";
|
|
8445
8492
|
async function runMigrateBackends(opts = {}) {
|
|
@@ -8461,8 +8508,8 @@ async function runMigrateBackends(opts = {}) {
|
|
|
8461
8508
|
};
|
|
8462
8509
|
}
|
|
8463
8510
|
const routing = readRoutingFromOrchestratorMd(mdPath);
|
|
8464
|
-
const jsonPath =
|
|
8465
|
-
if (!
|
|
8511
|
+
const jsonPath = path48.join(path48.dirname(mdPath), "harness.config.json");
|
|
8512
|
+
if (!fs29.existsSync(jsonPath)) {
|
|
8466
8513
|
return {
|
|
8467
8514
|
status: "error",
|
|
8468
8515
|
message: `harness.config.json not found at ${jsonPath}. Run \`harness init\` first, then re-run this migration.`,
|
|
@@ -8471,7 +8518,7 @@ async function runMigrateBackends(opts = {}) {
|
|
|
8471
8518
|
}
|
|
8472
8519
|
let configRaw;
|
|
8473
8520
|
try {
|
|
8474
|
-
configRaw =
|
|
8521
|
+
configRaw = fs29.readFileSync(jsonPath, "utf-8");
|
|
8475
8522
|
} catch (err) {
|
|
8476
8523
|
return {
|
|
8477
8524
|
status: "error",
|
|
@@ -8518,7 +8565,7 @@ async function runMigrateBackends(opts = {}) {
|
|
|
8518
8565
|
};
|
|
8519
8566
|
}
|
|
8520
8567
|
try {
|
|
8521
|
-
|
|
8568
|
+
fs29.writeFileSync(jsonPath, nextJson);
|
|
8522
8569
|
} catch (err) {
|
|
8523
8570
|
return {
|
|
8524
8571
|
status: "error",
|
|
@@ -8535,7 +8582,7 @@ async function runMigrateBackends(opts = {}) {
|
|
|
8535
8582
|
function readRoutingFromOrchestratorMd(mdPath) {
|
|
8536
8583
|
let raw;
|
|
8537
8584
|
try {
|
|
8538
|
-
raw =
|
|
8585
|
+
raw = fs29.readFileSync(mdPath, "utf-8");
|
|
8539
8586
|
} catch {
|
|
8540
8587
|
return null;
|
|
8541
8588
|
}
|
|
@@ -8573,8 +8620,8 @@ function createBackendsSubcommand() {
|
|
|
8573
8620
|
// src/commands/migrate.ts
|
|
8574
8621
|
init_dist3();
|
|
8575
8622
|
init_dist2();
|
|
8576
|
-
import * as
|
|
8577
|
-
import * as
|
|
8623
|
+
import * as fs30 from "fs";
|
|
8624
|
+
import * as path49 from "path";
|
|
8578
8625
|
import * as readline from "readline";
|
|
8579
8626
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
8580
8627
|
import chalk11 from "chalk";
|
|
@@ -8588,25 +8635,25 @@ function isGitRepo(cwd) {
|
|
|
8588
8635
|
}
|
|
8589
8636
|
function readJson2(p) {
|
|
8590
8637
|
try {
|
|
8591
|
-
return JSON.parse(
|
|
8638
|
+
return JSON.parse(fs30.readFileSync(p, "utf8"));
|
|
8592
8639
|
} catch {
|
|
8593
8640
|
return null;
|
|
8594
8641
|
}
|
|
8595
8642
|
}
|
|
8596
8643
|
function listDirs(p) {
|
|
8597
|
-
if (!
|
|
8598
|
-
return
|
|
8644
|
+
if (!fs30.existsSync(p)) return [];
|
|
8645
|
+
return fs30.readdirSync(p, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
8599
8646
|
}
|
|
8600
8647
|
function listFiles(p, predicate) {
|
|
8601
|
-
if (!
|
|
8602
|
-
return
|
|
8648
|
+
if (!fs30.existsSync(p)) return [];
|
|
8649
|
+
return fs30.readdirSync(p, { withFileTypes: true }).filter((e) => e.isFile() && predicate(e.name)).map((e) => e.name);
|
|
8603
8650
|
}
|
|
8604
8651
|
function walk(dir, predicate) {
|
|
8605
|
-
if (!
|
|
8652
|
+
if (!fs30.existsSync(dir)) return [];
|
|
8606
8653
|
const out = [];
|
|
8607
|
-
for (const entry of
|
|
8654
|
+
for (const entry of fs30.readdirSync(dir, { withFileTypes: true })) {
|
|
8608
8655
|
if (DEFAULT_SKIP_DIRS.has(entry.name)) continue;
|
|
8609
|
-
const full =
|
|
8656
|
+
const full = path49.join(dir, entry.name);
|
|
8610
8657
|
if (entry.isDirectory()) out.push(...walk(full, predicate));
|
|
8611
8658
|
else if (predicate(full)) out.push(full);
|
|
8612
8659
|
}
|
|
@@ -8618,11 +8665,11 @@ function extractAutopilotTopic(state) {
|
|
|
8618
8665
|
}
|
|
8619
8666
|
function buildAutopilotMap(cwd) {
|
|
8620
8667
|
const map = /* @__PURE__ */ new Map();
|
|
8621
|
-
const sessionsDir =
|
|
8622
|
-
if (!
|
|
8668
|
+
const sessionsDir = path49.join(cwd, ".harness", "sessions");
|
|
8669
|
+
if (!fs30.existsSync(sessionsDir)) return map;
|
|
8623
8670
|
for (const session of listDirs(sessionsDir)) {
|
|
8624
8671
|
if (!session.startsWith("changes--")) continue;
|
|
8625
|
-
const state = readJson2(
|
|
8672
|
+
const state = readJson2(path49.join(sessionsDir, session, "autopilot-state.json"));
|
|
8626
8673
|
if (!state) continue;
|
|
8627
8674
|
const topic = extractAutopilotTopic(state);
|
|
8628
8675
|
if (!topic) continue;
|
|
@@ -8635,7 +8682,7 @@ function buildAutopilotMap(cwd) {
|
|
|
8635
8682
|
function extractTopicFromHeader(planAbs, proposalTopics) {
|
|
8636
8683
|
let text;
|
|
8637
8684
|
try {
|
|
8638
|
-
text =
|
|
8685
|
+
text = fs30.readFileSync(planAbs, "utf8").slice(0, 4e3);
|
|
8639
8686
|
} catch {
|
|
8640
8687
|
return null;
|
|
8641
8688
|
}
|
|
@@ -8660,8 +8707,8 @@ function extractTopicFromFilename(filename, proposalTopics) {
|
|
|
8660
8707
|
return best;
|
|
8661
8708
|
}
|
|
8662
8709
|
function resolveDocsDir(cwd) {
|
|
8663
|
-
const configPath =
|
|
8664
|
-
const configResult =
|
|
8710
|
+
const configPath = path49.join(cwd, "harness.config.json");
|
|
8711
|
+
const configResult = fs30.existsSync(configPath) ? resolveConfig(configPath) : null;
|
|
8665
8712
|
let docsDir;
|
|
8666
8713
|
if (configResult?.ok) {
|
|
8667
8714
|
docsDir = configResult.value.docsDir;
|
|
@@ -8673,16 +8720,16 @@ function resolveDocsDir(cwd) {
|
|
|
8673
8720
|
} else {
|
|
8674
8721
|
docsDir = "./docs";
|
|
8675
8722
|
}
|
|
8676
|
-
const docsAbs =
|
|
8677
|
-
const relRaw =
|
|
8723
|
+
const docsAbs = path49.resolve(cwd, docsDir);
|
|
8724
|
+
const relRaw = path49.relative(cwd, docsAbs).replaceAll("\\", "/");
|
|
8678
8725
|
return relRaw === "" ? "docs" : relRaw;
|
|
8679
8726
|
}
|
|
8680
8727
|
function collectAdrMoves(cwd, docsRel) {
|
|
8681
8728
|
const moves = [];
|
|
8682
|
-
const legacyAdrDir =
|
|
8683
|
-
if (!
|
|
8729
|
+
const legacyAdrDir = path49.join(cwd, ".harness", "architecture");
|
|
8730
|
+
if (!fs30.existsSync(legacyAdrDir)) return moves;
|
|
8684
8731
|
for (const topic of listDirs(legacyAdrDir)) {
|
|
8685
|
-
const topicSrc =
|
|
8732
|
+
const topicSrc = path49.join(legacyAdrDir, topic);
|
|
8686
8733
|
for (const file of listFiles(topicSrc, (n) => n.endsWith(".md"))) {
|
|
8687
8734
|
moves.push({
|
|
8688
8735
|
src: `.harness/architecture/${topic}/${file}`,
|
|
@@ -8694,9 +8741,9 @@ function collectAdrMoves(cwd, docsRel) {
|
|
|
8694
8741
|
}
|
|
8695
8742
|
function collectProposalTopics(cwd) {
|
|
8696
8743
|
const topics = /* @__PURE__ */ new Set();
|
|
8697
|
-
const changesDir =
|
|
8744
|
+
const changesDir = path49.join(cwd, "docs", "changes");
|
|
8698
8745
|
for (const topic of listDirs(changesDir)) {
|
|
8699
|
-
if (
|
|
8746
|
+
if (fs30.existsSync(path49.join(changesDir, topic, "proposal.md"))) {
|
|
8700
8747
|
topics.add(topic);
|
|
8701
8748
|
}
|
|
8702
8749
|
}
|
|
@@ -8723,12 +8770,12 @@ async function buildPlan(opts) {
|
|
|
8723
8770
|
orphanPlans: [],
|
|
8724
8771
|
proposalTopics: collectProposalTopics(cwd)
|
|
8725
8772
|
};
|
|
8726
|
-
const legacyPlanDir =
|
|
8727
|
-
if (!
|
|
8773
|
+
const legacyPlanDir = path49.join(cwd, "docs", "plans");
|
|
8774
|
+
if (!fs30.existsSync(legacyPlanDir)) return plan;
|
|
8728
8775
|
const autopilotMap = buildAutopilotMap(cwd);
|
|
8729
8776
|
for (const file of listFiles(legacyPlanDir, (n) => n.endsWith(".md") && n !== "index.md")) {
|
|
8730
8777
|
const planRel = `docs/plans/${file}`;
|
|
8731
|
-
const planAbs =
|
|
8778
|
+
const planAbs = path49.join(legacyPlanDir, file);
|
|
8732
8779
|
const subdir = file.includes("VERIFICATION") ? "verifications" : "plans";
|
|
8733
8780
|
const classified = classifyPlan(planRel, planAbs, file, autopilotMap, plan.proposalTopics);
|
|
8734
8781
|
if (classified) {
|
|
@@ -8746,8 +8793,8 @@ async function buildPlan(opts) {
|
|
|
8746
8793
|
return plan;
|
|
8747
8794
|
}
|
|
8748
8795
|
function gitMv(cwd, src, dest, useGit) {
|
|
8749
|
-
const destAbs =
|
|
8750
|
-
|
|
8796
|
+
const destAbs = path49.join(cwd, dest);
|
|
8797
|
+
fs30.mkdirSync(path49.dirname(destAbs), { recursive: true });
|
|
8751
8798
|
if (useGit) {
|
|
8752
8799
|
try {
|
|
8753
8800
|
execFileSync5("git", ["mv", src, dest], { cwd, stdio: "pipe" });
|
|
@@ -8755,7 +8802,7 @@ function gitMv(cwd, src, dest, useGit) {
|
|
|
8755
8802
|
} catch {
|
|
8756
8803
|
}
|
|
8757
8804
|
}
|
|
8758
|
-
|
|
8805
|
+
fs30.renameSync(path49.join(cwd, src), destAbs);
|
|
8759
8806
|
}
|
|
8760
8807
|
function executeMigration(plan) {
|
|
8761
8808
|
let moved = 0;
|
|
@@ -8787,13 +8834,13 @@ function updateReferences(plan) {
|
|
|
8787
8834
|
let files = 0;
|
|
8788
8835
|
let replacements = 0;
|
|
8789
8836
|
const targets = [
|
|
8790
|
-
...walk(
|
|
8791
|
-
...walk(
|
|
8837
|
+
...walk(path49.join(plan.cwd, "docs"), (p) => p.endsWith(".md")),
|
|
8838
|
+
...walk(path49.join(plan.cwd, ".harness", "sessions"), (p) => p.endsWith(".json"))
|
|
8792
8839
|
];
|
|
8793
8840
|
for (const target of targets) {
|
|
8794
8841
|
let text;
|
|
8795
8842
|
try {
|
|
8796
|
-
text =
|
|
8843
|
+
text = fs30.readFileSync(target, "utf8");
|
|
8797
8844
|
} catch {
|
|
8798
8845
|
continue;
|
|
8799
8846
|
}
|
|
@@ -8806,7 +8853,7 @@ function updateReferences(plan) {
|
|
|
8806
8853
|
count += occurrences;
|
|
8807
8854
|
}
|
|
8808
8855
|
if (count > 0 && result !== text) {
|
|
8809
|
-
|
|
8856
|
+
fs30.writeFileSync(target, result);
|
|
8810
8857
|
files++;
|
|
8811
8858
|
replacements += count;
|
|
8812
8859
|
}
|
|
@@ -8815,7 +8862,7 @@ function updateReferences(plan) {
|
|
|
8815
8862
|
}
|
|
8816
8863
|
async function prompt(question, def = "n") {
|
|
8817
8864
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
8818
|
-
const answer = await new Promise((
|
|
8865
|
+
const answer = await new Promise((resolve50) => rl.question(question, resolve50));
|
|
8819
8866
|
rl.close();
|
|
8820
8867
|
return (answer.trim() || def).toLowerCase();
|
|
8821
8868
|
}
|
|
@@ -8865,7 +8912,7 @@ async function resolveOrphans(plan, opts) {
|
|
|
8865
8912
|
}
|
|
8866
8913
|
function bucketOrphans(plan, topic) {
|
|
8867
8914
|
for (const src of plan.orphanPlans) {
|
|
8868
|
-
const file =
|
|
8915
|
+
const file = path49.basename(src);
|
|
8869
8916
|
const isVerification = file.includes("VERIFICATION");
|
|
8870
8917
|
const subdir = isVerification ? "verifications" : "plans";
|
|
8871
8918
|
plan.planMoves.push({
|
|
@@ -8907,10 +8954,10 @@ function summarize(plan) {
|
|
|
8907
8954
|
console.log("");
|
|
8908
8955
|
}
|
|
8909
8956
|
async function detectLegacyArtifacts(cwd) {
|
|
8910
|
-
const adrLegacyDir =
|
|
8911
|
-
const adrLegacy =
|
|
8912
|
-
const planLegacyDir =
|
|
8913
|
-
const planLegacy =
|
|
8957
|
+
const adrLegacyDir = path49.join(cwd, ".harness", "architecture");
|
|
8958
|
+
const adrLegacy = fs30.existsSync(adrLegacyDir) && listDirs(adrLegacyDir).length > 0;
|
|
8959
|
+
const planLegacyDir = path49.join(cwd, "docs", "plans");
|
|
8960
|
+
const planLegacy = fs30.existsSync(planLegacyDir) && listFiles(planLegacyDir, (n) => n.endsWith(".md") && n !== "index.md").length > 0;
|
|
8914
8961
|
return { adrLegacy, planLegacy };
|
|
8915
8962
|
}
|
|
8916
8963
|
async function confirmApply(opts) {
|
|
@@ -9002,7 +9049,9 @@ function createMigrateCommand() {
|
|
|
9002
9049
|
}
|
|
9003
9050
|
|
|
9004
9051
|
// src/commands/models.ts
|
|
9052
|
+
init_dist3();
|
|
9005
9053
|
import { Command as Command66 } from "commander";
|
|
9054
|
+
import { resolve as resolve31 } from "path";
|
|
9006
9055
|
import { defaultFetchModels } from "@harness-engineering/orchestrator";
|
|
9007
9056
|
function errorResult(message, partial) {
|
|
9008
9057
|
return {
|
|
@@ -9119,29 +9168,183 @@ function formatProbe(result) {
|
|
|
9119
9168
|
}
|
|
9120
9169
|
return lines.join("\n");
|
|
9121
9170
|
}
|
|
9122
|
-
function
|
|
9123
|
-
|
|
9124
|
-
|
|
9125
|
-
|
|
9171
|
+
function projectRoot() {
|
|
9172
|
+
return resolve31(process.env["HARNESS_PROJECT_ROOT"] ?? process.cwd());
|
|
9173
|
+
}
|
|
9174
|
+
function summarizeModelProposal(p) {
|
|
9175
|
+
const base = { id: p.id, status: p.status, proposedBy: p.proposedBy, createdAt: p.createdAt };
|
|
9176
|
+
if (p.kind !== "model") return { ...base, kind: p.kind };
|
|
9177
|
+
return {
|
|
9178
|
+
...base,
|
|
9179
|
+
kind: "model",
|
|
9180
|
+
action: p.model.action,
|
|
9181
|
+
target: p.model.target.ollamaName,
|
|
9182
|
+
replaces: p.model.replaces?.ollamaName,
|
|
9183
|
+
scoreDelta: p.model.scoreDelta
|
|
9184
|
+
};
|
|
9185
|
+
}
|
|
9186
|
+
async function runModelsProposals(status = "open") {
|
|
9187
|
+
const opts = { kind: "model" };
|
|
9188
|
+
if (status !== "all") opts.status = status;
|
|
9189
|
+
const proposals = await listProposals(projectRoot(), opts);
|
|
9190
|
+
return proposals.map(summarizeModelProposal);
|
|
9191
|
+
}
|
|
9192
|
+
async function runModelsApprove(id) {
|
|
9193
|
+
const orchestratorUrl = process.env["HARNESS_ORCHESTRATOR_URL"] ?? "http://127.0.0.1:4577";
|
|
9194
|
+
const token = process.env["HARNESS_ADMIN_TOKEN"];
|
|
9195
|
+
if (!token) {
|
|
9196
|
+
return {
|
|
9197
|
+
ok: false,
|
|
9198
|
+
error: "HARNESS_ADMIN_TOKEN is required to approve proposals (manage-proposals scope)."
|
|
9199
|
+
};
|
|
9200
|
+
}
|
|
9201
|
+
try {
|
|
9202
|
+
const res = await fetch(`${orchestratorUrl}/api/v1/proposals/${id}/approve`, {
|
|
9203
|
+
method: "POST",
|
|
9204
|
+
headers: { authorization: `Bearer ${token}` }
|
|
9205
|
+
});
|
|
9206
|
+
return { ok: res.ok, status: res.status, body: await res.text() };
|
|
9207
|
+
} catch (err) {
|
|
9208
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
9209
|
+
}
|
|
9210
|
+
}
|
|
9211
|
+
async function runModelsReject(id, reason) {
|
|
9212
|
+
const orchestratorUrl = process.env["HARNESS_ORCHESTRATOR_URL"] ?? "http://127.0.0.1:4577";
|
|
9213
|
+
const token = process.env["HARNESS_ADMIN_TOKEN"];
|
|
9214
|
+
if (!token) {
|
|
9215
|
+
return {
|
|
9216
|
+
ok: false,
|
|
9217
|
+
error: "HARNESS_ADMIN_TOKEN is required to reject proposals (manage-proposals scope)."
|
|
9218
|
+
};
|
|
9219
|
+
}
|
|
9220
|
+
try {
|
|
9221
|
+
const res = await fetch(`${orchestratorUrl}/api/v1/proposals/${id}/reject`, {
|
|
9222
|
+
method: "POST",
|
|
9223
|
+
headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
|
|
9224
|
+
body: JSON.stringify({ reason })
|
|
9225
|
+
});
|
|
9226
|
+
return { ok: res.ok, status: res.status, body: await res.text() };
|
|
9227
|
+
} catch (err) {
|
|
9228
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
9229
|
+
}
|
|
9230
|
+
}
|
|
9231
|
+
async function runModelsRefresh() {
|
|
9232
|
+
const orchestratorUrl = process.env["HARNESS_ORCHESTRATOR_URL"] ?? "http://127.0.0.1:4577";
|
|
9233
|
+
const token = process.env["HARNESS_ADMIN_TOKEN"];
|
|
9234
|
+
if (!token) {
|
|
9235
|
+
return {
|
|
9236
|
+
ok: false,
|
|
9237
|
+
error: "HARNESS_ADMIN_TOKEN is required to trigger a refresh (manage-proposals scope)."
|
|
9238
|
+
};
|
|
9239
|
+
}
|
|
9240
|
+
try {
|
|
9241
|
+
const res = await fetch(`${orchestratorUrl}/api/v1/local-models/refresh`, {
|
|
9242
|
+
method: "POST",
|
|
9243
|
+
headers: { authorization: `Bearer ${token}` }
|
|
9244
|
+
});
|
|
9245
|
+
return buildRefreshResult(res, await res.text());
|
|
9246
|
+
} catch (err) {
|
|
9247
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
9248
|
+
}
|
|
9249
|
+
}
|
|
9250
|
+
function buildRefreshResult(res, body) {
|
|
9251
|
+
const parsed = safeParseObject(body);
|
|
9252
|
+
const emitted = parsed?.["emitted"];
|
|
9253
|
+
const warnings = parsed?.["warnings"];
|
|
9254
|
+
const error = parsed?.["error"];
|
|
9255
|
+
return {
|
|
9256
|
+
ok: res.ok,
|
|
9257
|
+
status: res.status,
|
|
9258
|
+
...typeof emitted === "number" ? { emitted } : {},
|
|
9259
|
+
...Array.isArray(warnings) ? { warnings } : {},
|
|
9260
|
+
...res.ok ? {} : { error: typeof error === "string" ? error : `HTTP ${res.status}` },
|
|
9261
|
+
body
|
|
9262
|
+
};
|
|
9263
|
+
}
|
|
9264
|
+
function safeParseObject(body) {
|
|
9265
|
+
try {
|
|
9266
|
+
const v = JSON.parse(body);
|
|
9267
|
+
return v !== null && typeof v === "object" ? v : void 0;
|
|
9268
|
+
} catch {
|
|
9269
|
+
return void 0;
|
|
9270
|
+
}
|
|
9271
|
+
}
|
|
9272
|
+
function refreshExitCode(result) {
|
|
9273
|
+
return result.ok ? ExitCode.SUCCESS : ExitCode.ERROR;
|
|
9274
|
+
}
|
|
9275
|
+
function httpErrorLine(r) {
|
|
9276
|
+
return r.error ?? `HTTP ${r.status ?? "?"}: ${r.body ?? ""}`;
|
|
9277
|
+
}
|
|
9278
|
+
function reportMutation(result) {
|
|
9279
|
+
if (!result.ok) {
|
|
9280
|
+
logger.error(httpErrorLine(result));
|
|
9281
|
+
process.exit(ExitCode.ERROR);
|
|
9282
|
+
}
|
|
9283
|
+
console.log(result.body ?? "");
|
|
9284
|
+
}
|
|
9285
|
+
async function probeAction(options) {
|
|
9286
|
+
const result = await runModelsProbe(options);
|
|
9287
|
+
if (options.json) {
|
|
9288
|
+
console.log(JSON.stringify(result, null, 2));
|
|
9289
|
+
} else {
|
|
9290
|
+
const text = formatProbe(result);
|
|
9291
|
+
if (result.status === "error" || result.status === "no-match") logger.error(text);
|
|
9292
|
+
else logger.info(text);
|
|
9293
|
+
}
|
|
9294
|
+
process.exit(result.exitCode);
|
|
9295
|
+
}
|
|
9296
|
+
async function refreshAction() {
|
|
9297
|
+
const result = await runModelsRefresh();
|
|
9298
|
+
if (!result.ok) {
|
|
9299
|
+
logger.error(httpErrorLine(result));
|
|
9300
|
+
} else {
|
|
9301
|
+
logger.info(`refresh: emitted ${result.emitted ?? 0} proposal(s)`);
|
|
9302
|
+
for (const w of result.warnings ?? []) logger.warn(` warning: ${w}`);
|
|
9303
|
+
}
|
|
9304
|
+
process.exitCode = refreshExitCode(result);
|
|
9305
|
+
}
|
|
9306
|
+
function addProbeCommand(cmd) {
|
|
9126
9307
|
cmd.command("probe").description(
|
|
9127
9308
|
"Probe a local backend's /v1/models endpoint and report which configured model is loaded."
|
|
9128
9309
|
).option(
|
|
9129
9310
|
"--backend <name>",
|
|
9130
9311
|
"Name of an entry in agent.backends. Defaults to the first local/pi entry."
|
|
9131
|
-
).option("--endpoint <url>", "Override the backend endpoint (bypasses harness.config.json).").option("--api-key <key>", "Override the API key.").option("--json", "Print machine-readable JSON instead of a human summary.", false).action(
|
|
9132
|
-
|
|
9133
|
-
|
|
9134
|
-
|
|
9135
|
-
|
|
9136
|
-
|
|
9137
|
-
if (result.status === "error" || result.status === "no-match") {
|
|
9138
|
-
logger.error(text);
|
|
9139
|
-
} else {
|
|
9140
|
-
logger.info(text);
|
|
9141
|
-
}
|
|
9142
|
-
}
|
|
9143
|
-
process.exit(result.exitCode);
|
|
9312
|
+
).option("--endpoint <url>", "Override the backend endpoint (bypasses harness.config.json).").option("--api-key <key>", "Override the API key.").option("--json", "Print machine-readable JSON instead of a human summary.", false).action(probeAction);
|
|
9313
|
+
}
|
|
9314
|
+
function addProposalsCommand(cmd) {
|
|
9315
|
+
cmd.command("proposals").description("List model proposals in the local review queue (defaults to pending).").option("--status <status>", "Filter by status (open | approved | rejected | all).", "open").action(async (options) => {
|
|
9316
|
+
const rows = await runModelsProposals(options.status ?? "open");
|
|
9317
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
9144
9318
|
});
|
|
9319
|
+
}
|
|
9320
|
+
function addRejectCommand(cmd) {
|
|
9321
|
+
cmd.command("reject <id>").description(
|
|
9322
|
+
"Reject a model proposal with a one-line reason (records the decision + fires the bus event). Requires the orchestrator to be running and HARNESS_ADMIN_TOKEN."
|
|
9323
|
+
).requiredOption("--reason <text>", "Why the proposal is being rejected").action(async (id, options) => {
|
|
9324
|
+
reportMutation(await runModelsReject(id, options.reason));
|
|
9325
|
+
});
|
|
9326
|
+
}
|
|
9327
|
+
function addApproveCommand(cmd) {
|
|
9328
|
+
cmd.command("approve <id>").description(
|
|
9329
|
+
"Approve a model proposal (drives the installer + pool update). Requires the orchestrator to be running and HARNESS_ADMIN_TOKEN."
|
|
9330
|
+
).action(async (id) => {
|
|
9331
|
+
reportMutation(await runModelsApprove(id));
|
|
9332
|
+
});
|
|
9333
|
+
}
|
|
9334
|
+
function addRefreshCommand(cmd) {
|
|
9335
|
+
cmd.command("refresh").description(
|
|
9336
|
+
"Force a background-scheduler refresh tick now (emits any model proposals). Exits non-zero on an O4 hard failure (HuggingFace unreachable AND no benchmark snapshot). Requires the orchestrator running + HARNESS_ADMIN_TOKEN."
|
|
9337
|
+
).action(refreshAction);
|
|
9338
|
+
}
|
|
9339
|
+
function createModelsCommand() {
|
|
9340
|
+
const cmd = new Command66("models").description(
|
|
9341
|
+
"Inspect and manage local LLM backends. Ships `probe`, model-proposal review (proposals/approve/reject), and `refresh` (force a scheduler tick)."
|
|
9342
|
+
);
|
|
9343
|
+
addProbeCommand(cmd);
|
|
9344
|
+
addProposalsCommand(cmd);
|
|
9345
|
+
addRejectCommand(cmd);
|
|
9346
|
+
addApproveCommand(cmd);
|
|
9347
|
+
addRefreshCommand(cmd);
|
|
9145
9348
|
return cmd;
|
|
9146
9349
|
}
|
|
9147
9350
|
|
|
@@ -9198,10 +9401,10 @@ function printResult4(result, mode, _formatter) {
|
|
|
9198
9401
|
list.push(f);
|
|
9199
9402
|
byFile.set(f.target.file, list);
|
|
9200
9403
|
}
|
|
9201
|
-
for (const [file,
|
|
9404
|
+
for (const [file, fs51] of byFile) {
|
|
9202
9405
|
console.log(`
|
|
9203
9406
|
${file}`);
|
|
9204
|
-
for (const f of
|
|
9407
|
+
for (const f of fs51) {
|
|
9205
9408
|
const line = f.target.line !== void 0 ? `:${f.target.line}` : "";
|
|
9206
9409
|
console.log(
|
|
9207
9410
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.kind} ${f.target.identifier}${line}`
|
|
@@ -9227,7 +9430,7 @@ import { Command as Command69 } from "commander";
|
|
|
9227
9430
|
init_dist3();
|
|
9228
9431
|
import { Command as Command68 } from "commander";
|
|
9229
9432
|
import { randomBytes } from "crypto";
|
|
9230
|
-
import { resolve as
|
|
9433
|
+
import { resolve as resolve32 } from "path";
|
|
9231
9434
|
import { SinkRegistry, SinkConfigError, wrapAsEnvelope } from "@harness-engineering/orchestrator";
|
|
9232
9435
|
function makeTestEvent(message) {
|
|
9233
9436
|
return {
|
|
@@ -9237,8 +9440,8 @@ function makeTestEvent(message) {
|
|
|
9237
9440
|
data: { message, triggeredAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
9238
9441
|
};
|
|
9239
9442
|
}
|
|
9240
|
-
async function runNotificationsTest(sinkId, opts,
|
|
9241
|
-
const cfgResult = loadNotificationsConfig(
|
|
9443
|
+
async function runNotificationsTest(sinkId, opts, projectRoot3 = process.cwd()) {
|
|
9444
|
+
const cfgResult = loadNotificationsConfig(projectRoot3);
|
|
9242
9445
|
if (!cfgResult.ok) {
|
|
9243
9446
|
return { ok: false, error: `Config error: ${cfgResult.error.message}` };
|
|
9244
9447
|
}
|
|
@@ -9279,8 +9482,8 @@ async function runNotificationsTest(sinkId, opts, projectRoot2 = process.cwd())
|
|
|
9279
9482
|
function createNotificationsTestSubcommand() {
|
|
9280
9483
|
return new Command68("test").description("Send a synthetic notification.test event through the named sink").argument("<sink-id>", "Sink id from harness.config.json `notifications.sinks[].id`").option("--message <text>", "Override the default test message").action(async (sinkId, opts, cmd) => {
|
|
9281
9484
|
const globalOpts = cmd.optsWithGlobals();
|
|
9282
|
-
const
|
|
9283
|
-
const result = await runNotificationsTest(sinkId, opts,
|
|
9485
|
+
const projectRoot3 = typeof globalOpts["cwd"] === "string" ? resolve32(globalOpts["cwd"]) : process.cwd();
|
|
9486
|
+
const result = await runNotificationsTest(sinkId, opts, projectRoot3);
|
|
9284
9487
|
if (globalOpts["json"]) {
|
|
9285
9488
|
console.log(JSON.stringify(result, null, 2));
|
|
9286
9489
|
} else if (result.ok) {
|
|
@@ -9303,12 +9506,12 @@ function createNotificationsCommand() {
|
|
|
9303
9506
|
|
|
9304
9507
|
// src/commands/orchestrator.ts
|
|
9305
9508
|
import { Command as Command70 } from "commander";
|
|
9306
|
-
import * as
|
|
9509
|
+
import * as path50 from "path";
|
|
9307
9510
|
import { Orchestrator, WorkflowLoader as WorkflowLoader2, launchTUI } from "@harness-engineering/orchestrator";
|
|
9308
9511
|
function createOrchestratorCommand() {
|
|
9309
9512
|
const orchestrator = new Command70("orchestrator");
|
|
9310
9513
|
orchestrator.command("run").description("Run the orchestrator daemon").option("-w, --workflow <path>", "Path to harness.orchestrator.md", "harness.orchestrator.md").option("--headless", "Run without TUI (server-only mode for use with web dashboard)").action(async (opts) => {
|
|
9311
|
-
const workflowPath =
|
|
9514
|
+
const workflowPath = path50.resolve(process.cwd(), opts.workflow);
|
|
9312
9515
|
const loader = new WorkflowLoader2();
|
|
9313
9516
|
const result = await loader.loadWorkflow(workflowPath);
|
|
9314
9517
|
if (!result.ok) {
|
|
@@ -9343,12 +9546,12 @@ function createOrchestratorCommand() {
|
|
|
9343
9546
|
// src/commands/perf.ts
|
|
9344
9547
|
init_dist3();
|
|
9345
9548
|
import { Command as Command71 } from "commander";
|
|
9346
|
-
import * as
|
|
9549
|
+
import * as path51 from "path";
|
|
9347
9550
|
function registerBenchCommand(perf) {
|
|
9348
9551
|
perf.command("bench [glob]").description("Run benchmarks via vitest bench").action(async (glob2, _opts, cmd) => {
|
|
9349
9552
|
const globalOpts = cmd.optsWithGlobals();
|
|
9350
9553
|
const cwd = process.cwd();
|
|
9351
|
-
const { BenchmarkRunner } = await import("./dist-
|
|
9554
|
+
const { BenchmarkRunner } = await import("./dist-BWFFVKEW.js");
|
|
9352
9555
|
const runner = new BenchmarkRunner();
|
|
9353
9556
|
const benchFiles = runner.discover(cwd, glob2);
|
|
9354
9557
|
if (benchFiles.length === 0) {
|
|
@@ -9412,7 +9615,7 @@ async function runBaselinesShow(globalOpts) {
|
|
|
9412
9615
|
}
|
|
9413
9616
|
async function runBaselinesUpdate(globalOpts) {
|
|
9414
9617
|
const cwd = process.cwd();
|
|
9415
|
-
const { BenchmarkRunner } = await import("./dist-
|
|
9618
|
+
const { BenchmarkRunner } = await import("./dist-BWFFVKEW.js");
|
|
9416
9619
|
const runner = new BenchmarkRunner();
|
|
9417
9620
|
const manager = new BaselineManager(cwd);
|
|
9418
9621
|
logger.info("Running benchmarks to update baselines...");
|
|
@@ -9453,9 +9656,9 @@ function registerReportCommand(perf) {
|
|
|
9453
9656
|
perf.command("report").description("Full performance report with metrics, trends, and hotspots").action(async (_opts, cmd) => {
|
|
9454
9657
|
const globalOpts = cmd.optsWithGlobals();
|
|
9455
9658
|
const cwd = process.cwd();
|
|
9456
|
-
const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-
|
|
9659
|
+
const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-BWFFVKEW.js");
|
|
9457
9660
|
const analyzer = new EntropyAnalyzer2({
|
|
9458
|
-
rootDir:
|
|
9661
|
+
rootDir: path51.resolve(cwd),
|
|
9459
9662
|
analyze: { complexity: true, coupling: true }
|
|
9460
9663
|
});
|
|
9461
9664
|
const result = await analyzer.analyze();
|
|
@@ -9561,34 +9764,34 @@ function createListCommand2() {
|
|
|
9561
9764
|
|
|
9562
9765
|
// src/commands/persona/generate.ts
|
|
9563
9766
|
import { Command as Command73, Option } from "commander";
|
|
9564
|
-
import * as
|
|
9565
|
-
import * as
|
|
9767
|
+
import * as fs31 from "fs";
|
|
9768
|
+
import * as path52 from "path";
|
|
9566
9769
|
function generatePersonaArtifacts(persona, outputDir, only, platform) {
|
|
9567
9770
|
const slug = toKebabCase(persona.name);
|
|
9568
9771
|
const generated = [];
|
|
9569
9772
|
if (!only || only === "runtime") {
|
|
9570
9773
|
const result = generateRuntime(persona);
|
|
9571
9774
|
if (result.ok) {
|
|
9572
|
-
const outPath =
|
|
9573
|
-
|
|
9574
|
-
|
|
9775
|
+
const outPath = path52.join(outputDir, `${slug}.runtime.json`);
|
|
9776
|
+
fs31.mkdirSync(path52.dirname(outPath), { recursive: true });
|
|
9777
|
+
fs31.writeFileSync(outPath, result.value);
|
|
9575
9778
|
generated.push(outPath);
|
|
9576
9779
|
}
|
|
9577
9780
|
}
|
|
9578
9781
|
if (!only || only === "agents-md") {
|
|
9579
9782
|
const result = generateAgentsMd(persona);
|
|
9580
9783
|
if (result.ok) {
|
|
9581
|
-
const outPath =
|
|
9582
|
-
|
|
9784
|
+
const outPath = path52.join(outputDir, `${slug}.agents.md`);
|
|
9785
|
+
fs31.writeFileSync(outPath, result.value);
|
|
9583
9786
|
generated.push(outPath);
|
|
9584
9787
|
}
|
|
9585
9788
|
}
|
|
9586
9789
|
if (!only || only === "ci") {
|
|
9587
9790
|
const result = generateCIWorkflow(persona, platform);
|
|
9588
9791
|
if (result.ok) {
|
|
9589
|
-
const outPath = platform === "gitlab" ?
|
|
9590
|
-
|
|
9591
|
-
|
|
9792
|
+
const outPath = platform === "gitlab" ? path52.join(outputDir, `${slug}.gitlab-ci.yml`) : path52.join(outputDir, ".github", "workflows", `${slug}.yml`);
|
|
9793
|
+
fs31.mkdirSync(path52.dirname(outPath), { recursive: true });
|
|
9794
|
+
fs31.writeFileSync(outPath, result.value);
|
|
9592
9795
|
generated.push(outPath);
|
|
9593
9796
|
}
|
|
9594
9797
|
}
|
|
@@ -9599,14 +9802,14 @@ function createGenerateCommand3() {
|
|
|
9599
9802
|
new Option("--platform <platform>", "CI platform for the ci artifact").choices(["github", "gitlab"]).default("github")
|
|
9600
9803
|
).action(async (name, opts, cmd) => {
|
|
9601
9804
|
const globalOpts = cmd.optsWithGlobals();
|
|
9602
|
-
const personaResult = loadPersona(
|
|
9805
|
+
const personaResult = loadPersona(path52.join(resolvePersonasDir(), `${name}.yaml`));
|
|
9603
9806
|
if (!personaResult.ok) {
|
|
9604
9807
|
logger.error(personaResult.error.message);
|
|
9605
9808
|
process.exit(ExitCode.ERROR);
|
|
9606
9809
|
}
|
|
9607
9810
|
const generated = generatePersonaArtifacts(
|
|
9608
9811
|
personaResult.value,
|
|
9609
|
-
|
|
9812
|
+
path52.resolve(opts.outputDir),
|
|
9610
9813
|
opts.only,
|
|
9611
9814
|
opts.platform
|
|
9612
9815
|
);
|
|
@@ -9760,14 +9963,14 @@ function createPredictCommand() {
|
|
|
9760
9963
|
init_dist2();
|
|
9761
9964
|
import { execFileSync as execFileSync7 } from "child_process";
|
|
9762
9965
|
import { readFileSync as readFileSync18 } from "fs";
|
|
9763
|
-
import { join as
|
|
9966
|
+
import { join as join41 } from "path";
|
|
9764
9967
|
import { Command as Command77 } from "commander";
|
|
9765
9968
|
import { gatherSignals } from "@harness-engineering/signals";
|
|
9766
9969
|
|
|
9767
9970
|
// src/commands/review-ci.ts
|
|
9768
9971
|
init_dist3();
|
|
9769
9972
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
9770
|
-
import { writeFileSync as
|
|
9973
|
+
import { writeFileSync as writeFileSync19 } from "fs";
|
|
9771
9974
|
import { Command as Command76, Option as Option2 } from "commander";
|
|
9772
9975
|
|
|
9773
9976
|
// src/commands/review-ci-local-adapter.ts
|
|
@@ -9940,7 +10143,7 @@ var defaultPostReview = (verdict) => {
|
|
|
9940
10143
|
encoding: "utf-8"
|
|
9941
10144
|
});
|
|
9942
10145
|
};
|
|
9943
|
-
function emitReviewCi(result, opts, writeFile2 = (p, d) =>
|
|
10146
|
+
function emitReviewCi(result, opts, writeFile2 = (p, d) => writeFileSync19(p, d), log = (m) => process.stdout.write(m + "\n"), postReview = defaultPostReview) {
|
|
9944
10147
|
const serialized = JSON.stringify(result.verdict, null, 2);
|
|
9945
10148
|
const jsonToStdout = opts.jsonPath === true;
|
|
9946
10149
|
if (!jsonToStdout) log(result.terminalOutput);
|
|
@@ -10121,11 +10324,11 @@ var defaultPostBrief = (body) => {
|
|
|
10121
10324
|
}
|
|
10122
10325
|
);
|
|
10123
10326
|
};
|
|
10124
|
-
var defaultReadFile = (
|
|
10125
|
-
function readReview(
|
|
10126
|
-
if (!
|
|
10327
|
+
var defaultReadFile = (path81) => readFileSync18(path81, "utf-8");
|
|
10328
|
+
function readReview(path81, readFile4 = defaultReadFile) {
|
|
10329
|
+
if (!path81) return void 0;
|
|
10127
10330
|
try {
|
|
10128
|
-
const parsed = JSON.parse(readFile4(
|
|
10331
|
+
const parsed = JSON.parse(readFile4(path81));
|
|
10129
10332
|
return parsed?.verdict && typeof parsed.verdict === "object" ? parsed.verdict : void 0;
|
|
10130
10333
|
} catch {
|
|
10131
10334
|
return void 0;
|
|
@@ -10143,7 +10346,7 @@ var GRAPH_DIR = ".harness/graph";
|
|
|
10143
10346
|
async function loadOutcomeStore(projectPath) {
|
|
10144
10347
|
try {
|
|
10145
10348
|
const store = new GraphStore();
|
|
10146
|
-
const loaded = await store.load(
|
|
10349
|
+
const loaded = await store.load(join41(projectPath, GRAPH_DIR));
|
|
10147
10350
|
return loaded ? store : void 0;
|
|
10148
10351
|
} catch {
|
|
10149
10352
|
return void 0;
|
|
@@ -10240,31 +10443,44 @@ function createPreMergeBriefCommand() {
|
|
|
10240
10443
|
// src/commands/proposals.ts
|
|
10241
10444
|
init_dist3();
|
|
10242
10445
|
import { Command as Command78 } from "commander";
|
|
10243
|
-
import { resolve as
|
|
10244
|
-
function
|
|
10245
|
-
return
|
|
10446
|
+
import { resolve as resolve36 } from "path";
|
|
10447
|
+
function projectRoot2() {
|
|
10448
|
+
return resolve36(process.env["HARNESS_PROJECT_ROOT"] ?? process.cwd());
|
|
10246
10449
|
}
|
|
10247
10450
|
function summarizeProposal(p) {
|
|
10248
|
-
|
|
10451
|
+
const base = {
|
|
10249
10452
|
id: p.id,
|
|
10250
|
-
kind: p.kind,
|
|
10251
|
-
targetSkill: p.targetSkill,
|
|
10252
|
-
name: p.content.name,
|
|
10253
10453
|
status: p.status,
|
|
10254
10454
|
proposedBy: p.proposedBy,
|
|
10255
|
-
createdAt: p.createdAt
|
|
10256
|
-
|
|
10257
|
-
|
|
10455
|
+
createdAt: p.createdAt
|
|
10456
|
+
};
|
|
10457
|
+
if (p.kind === "skill") {
|
|
10458
|
+
return {
|
|
10459
|
+
...base,
|
|
10460
|
+
kind: p.skillKind,
|
|
10461
|
+
targetSkill: p.targetSkill,
|
|
10462
|
+
name: p.content.name,
|
|
10463
|
+
gateLastRunAt: p.gate?.lastRunAt,
|
|
10464
|
+
findings: p.gate?.findings?.length ?? 0
|
|
10465
|
+
};
|
|
10466
|
+
}
|
|
10467
|
+
return {
|
|
10468
|
+
...base,
|
|
10469
|
+
kind: p.kind,
|
|
10470
|
+
targetSkill: void 0,
|
|
10471
|
+
name: p.model.target.ollamaName,
|
|
10472
|
+
gateLastRunAt: void 0,
|
|
10473
|
+
findings: 0
|
|
10258
10474
|
};
|
|
10259
10475
|
}
|
|
10260
10476
|
async function runProposalsList(status) {
|
|
10261
|
-
const opts = {};
|
|
10477
|
+
const opts = { kind: "skill" };
|
|
10262
10478
|
if (status) opts.status = status;
|
|
10263
|
-
const proposals = await listProposals(
|
|
10479
|
+
const proposals = await listProposals(projectRoot2(), opts);
|
|
10264
10480
|
return proposals.map(summarizeProposal);
|
|
10265
10481
|
}
|
|
10266
10482
|
async function runProposalsShow(id) {
|
|
10267
|
-
return getProposal(
|
|
10483
|
+
return getProposal(projectRoot2(), id);
|
|
10268
10484
|
}
|
|
10269
10485
|
async function runProposalsReject(id, reason) {
|
|
10270
10486
|
const decision = {
|
|
@@ -10273,7 +10489,7 @@ async function runProposalsReject(id, reason) {
|
|
|
10273
10489
|
action: "rejected",
|
|
10274
10490
|
reason
|
|
10275
10491
|
};
|
|
10276
|
-
return updateProposal(
|
|
10492
|
+
return updateProposal(projectRoot2(), id, { status: "rejected", decision });
|
|
10277
10493
|
}
|
|
10278
10494
|
var ALLOWED_STATUSES = [
|
|
10279
10495
|
"open",
|
|
@@ -10349,8 +10565,8 @@ function createProposalsCommand() {
|
|
|
10349
10565
|
}
|
|
10350
10566
|
|
|
10351
10567
|
// src/commands/publish-analyses.ts
|
|
10352
|
-
import * as
|
|
10353
|
-
import * as
|
|
10568
|
+
import * as fs32 from "fs";
|
|
10569
|
+
import * as path53 from "path";
|
|
10354
10570
|
import { Command as Command79 } from "commander";
|
|
10355
10571
|
init_dist3();
|
|
10356
10572
|
import {
|
|
@@ -10359,7 +10575,7 @@ import {
|
|
|
10359
10575
|
savePublishedIndex
|
|
10360
10576
|
} from "@harness-engineering/orchestrator";
|
|
10361
10577
|
function bootstrapTrackerCommand(opts, verb) {
|
|
10362
|
-
const projectPath =
|
|
10578
|
+
const projectPath = path53.resolve(opts.dir);
|
|
10363
10579
|
const trackerConfig = loadTrackerSyncConfig(projectPath);
|
|
10364
10580
|
if (!trackerConfig) {
|
|
10365
10581
|
logger.error(`No tracker config found in harness.config.json. Cannot ${verb}.`);
|
|
@@ -10371,8 +10587,8 @@ function bootstrapTrackerCommand(opts, verb) {
|
|
|
10371
10587
|
);
|
|
10372
10588
|
return null;
|
|
10373
10589
|
}
|
|
10374
|
-
const projectEnvPath =
|
|
10375
|
-
if (
|
|
10590
|
+
const projectEnvPath = path53.join(projectPath, ".env");
|
|
10591
|
+
if (fs32.existsSync(projectEnvPath) && !process.env.GITHUB_TOKEN) {
|
|
10376
10592
|
__require("dotenv").config({ path: projectEnvPath });
|
|
10377
10593
|
}
|
|
10378
10594
|
const token = process.env.GITHUB_TOKEN;
|
|
@@ -10463,8 +10679,8 @@ async function runPublishAnalyses(opts) {
|
|
|
10463
10679
|
const nameToExternalId = await buildNameToExternalIdMap(projectPath);
|
|
10464
10680
|
if (!nameToExternalId) return process.exit(1);
|
|
10465
10681
|
const { AnalysisArchive } = await import("@harness-engineering/orchestrator");
|
|
10466
|
-
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-
|
|
10467
|
-
const archive = new AnalysisArchive(
|
|
10682
|
+
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-BWFFVKEW.js");
|
|
10683
|
+
const archive = new AnalysisArchive(path53.join(projectPath, ".harness", "analyses"));
|
|
10468
10684
|
const analyses = await archive.list();
|
|
10469
10685
|
if (analyses.length === 0) {
|
|
10470
10686
|
logger.info("No analyses found to publish.");
|
|
@@ -10506,8 +10722,8 @@ import { Command as Command81 } from "commander";
|
|
|
10506
10722
|
|
|
10507
10723
|
// src/commands/pulse/run.ts
|
|
10508
10724
|
init_dist3();
|
|
10509
|
-
import { readFileSync as readFileSync19, mkdirSync as
|
|
10510
|
-
import { dirname as
|
|
10725
|
+
import { readFileSync as readFileSync19, mkdirSync as mkdirSync16, writeFileSync as writeFileSync20 } from "fs";
|
|
10726
|
+
import { dirname as dirname19, join as join43, resolve as resolve38 } from "path";
|
|
10511
10727
|
import { Command as Command80 } from "commander";
|
|
10512
10728
|
async function runPulseRunCommand(opts) {
|
|
10513
10729
|
const startedAt = Date.now();
|
|
@@ -10539,9 +10755,9 @@ async function runPulseRunCommand(opts) {
|
|
|
10539
10755
|
const report = assembleReport(result, productName, lookback);
|
|
10540
10756
|
const now = /* @__PURE__ */ new Date();
|
|
10541
10757
|
const ts = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}_${String(now.getHours()).padStart(2, "0")}-${String(now.getMinutes()).padStart(2, "0")}`;
|
|
10542
|
-
const filePath =
|
|
10543
|
-
|
|
10544
|
-
|
|
10758
|
+
const filePath = join43(opts.outputDir, `${ts}.md`);
|
|
10759
|
+
mkdirSync16(dirname19(filePath), { recursive: true });
|
|
10760
|
+
writeFileSync20(filePath, report);
|
|
10545
10761
|
const headlines = extractHeadlines(report);
|
|
10546
10762
|
return emit2(
|
|
10547
10763
|
{
|
|
@@ -10583,8 +10799,8 @@ function createRunCommand2() {
|
|
|
10583
10799
|
async (options) => {
|
|
10584
10800
|
const nonInteractive = options.nonInteractive === true || !process.stdout.isTTY;
|
|
10585
10801
|
const status = await runPulseRunCommand({
|
|
10586
|
-
configPath:
|
|
10587
|
-
outputDir:
|
|
10802
|
+
configPath: resolve38(process.cwd(), options.config),
|
|
10803
|
+
outputDir: resolve38(process.cwd(), options.outputDir),
|
|
10588
10804
|
nonInteractive,
|
|
10589
10805
|
lookback: options.lookback
|
|
10590
10806
|
});
|
|
@@ -10708,13 +10924,13 @@ import { Command as Command89 } from "commander";
|
|
|
10708
10924
|
// src/commands/roadmap/migrate.ts
|
|
10709
10925
|
init_dist3();
|
|
10710
10926
|
import { Command as Command83 } from "commander";
|
|
10711
|
-
import * as
|
|
10712
|
-
import * as
|
|
10927
|
+
import * as fs34 from "fs";
|
|
10928
|
+
import * as path55 from "path";
|
|
10713
10929
|
import chalk14 from "chalk";
|
|
10714
10930
|
|
|
10715
10931
|
// src/commands/roadmap/migrate-lock.ts
|
|
10716
|
-
import * as
|
|
10717
|
-
import * as
|
|
10932
|
+
import * as fs33 from "fs";
|
|
10933
|
+
import * as path54 from "path";
|
|
10718
10934
|
import * as os7 from "os";
|
|
10719
10935
|
var LOCK_FILE_NAME = "migrate.lock";
|
|
10720
10936
|
var STALE_LOCK_MS = 30 * 60 * 1e3;
|
|
@@ -10727,14 +10943,14 @@ function isPidAlive(pid) {
|
|
|
10727
10943
|
return false;
|
|
10728
10944
|
}
|
|
10729
10945
|
}
|
|
10730
|
-
function acquireMigrateLock(
|
|
10731
|
-
const harnessDir =
|
|
10732
|
-
const lockPath =
|
|
10733
|
-
if (!
|
|
10734
|
-
if (
|
|
10946
|
+
function acquireMigrateLock(projectRoot3) {
|
|
10947
|
+
const harnessDir = path54.join(projectRoot3, ".harness");
|
|
10948
|
+
const lockPath = path54.join(harnessDir, LOCK_FILE_NAME);
|
|
10949
|
+
if (!fs33.existsSync(harnessDir)) fs33.mkdirSync(harnessDir, { recursive: true });
|
|
10950
|
+
if (fs33.existsSync(lockPath)) {
|
|
10735
10951
|
const existingRaw = (() => {
|
|
10736
10952
|
try {
|
|
10737
|
-
return
|
|
10953
|
+
return fs33.readFileSync(lockPath, "utf-8");
|
|
10738
10954
|
} catch {
|
|
10739
10955
|
return null;
|
|
10740
10956
|
}
|
|
@@ -10764,11 +10980,11 @@ function acquireMigrateLock(projectRoot2) {
|
|
|
10764
10980
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10765
10981
|
hostname: os7.hostname()
|
|
10766
10982
|
};
|
|
10767
|
-
|
|
10983
|
+
fs33.writeFileSync(lockPath, JSON.stringify(payload, null, 2) + "\n");
|
|
10768
10984
|
return {
|
|
10769
10985
|
release: () => {
|
|
10770
10986
|
try {
|
|
10771
|
-
|
|
10987
|
+
fs33.unlinkSync(lockPath);
|
|
10772
10988
|
} catch {
|
|
10773
10989
|
}
|
|
10774
10990
|
}
|
|
@@ -10924,11 +11140,11 @@ async function runRoadmapMigrate(opts) {
|
|
|
10924
11140
|
if (!clientR.ok) return Err(new CLIError(clientR.error.message));
|
|
10925
11141
|
client = clientR.value;
|
|
10926
11142
|
}
|
|
10927
|
-
const roadmapPath =
|
|
10928
|
-
if (!
|
|
11143
|
+
const roadmapPath = path55.join(cwd, "docs", "roadmap.md");
|
|
11144
|
+
if (!fs34.existsSync(roadmapPath)) {
|
|
10929
11145
|
return Err(new CLIError(`docs/roadmap.md not found in ${cwd}`));
|
|
10930
11146
|
}
|
|
10931
|
-
const roadmapR = parseRoadmap(
|
|
11147
|
+
const roadmapR = parseRoadmap(fs34.readFileSync(roadmapPath, "utf-8"));
|
|
10932
11148
|
if (!roadmapR.ok) {
|
|
10933
11149
|
return Err(new CLIError(`failed to parse docs/roadmap.md: ${roadmapR.error.message}`));
|
|
10934
11150
|
}
|
|
@@ -10949,10 +11165,10 @@ async function runRoadmapMigrate(opts) {
|
|
|
10949
11165
|
if (!isJson) printPlanSummary(plan, opts.dryRun);
|
|
10950
11166
|
const deps = {
|
|
10951
11167
|
client,
|
|
10952
|
-
readFile: (p) =>
|
|
10953
|
-
writeFile: (p, b) =>
|
|
10954
|
-
renameFile: (from, to) =>
|
|
10955
|
-
existsFile: (p) =>
|
|
11168
|
+
readFile: (p) => fs34.readFileSync(p, "utf-8"),
|
|
11169
|
+
writeFile: (p, b) => fs34.writeFileSync(p, b),
|
|
11170
|
+
renameFile: (from, to) => fs34.renameSync(from, to),
|
|
11171
|
+
existsFile: (p) => fs34.existsSync(p)
|
|
10956
11172
|
};
|
|
10957
11173
|
const reportR = await migrate_exports.runMigrationPlan(plan, deps, {
|
|
10958
11174
|
projectRoot: cwd,
|
|
@@ -11010,7 +11226,7 @@ function featuresToRoadmap(features, project, nowIso) {
|
|
|
11010
11226
|
}
|
|
11011
11227
|
function readProjectName(configPath) {
|
|
11012
11228
|
try {
|
|
11013
|
-
const parsed = JSON.parse(
|
|
11229
|
+
const parsed = JSON.parse(fs34.readFileSync(configPath, "utf-8"));
|
|
11014
11230
|
return typeof parsed.name === "string" && parsed.name.length > 0 ? parsed.name : "Roadmap";
|
|
11015
11231
|
} catch {
|
|
11016
11232
|
return "Roadmap";
|
|
@@ -11057,8 +11273,8 @@ async function runReverseMigrate(opts) {
|
|
|
11057
11273
|
const lockResult = acquireMigrateLock(cwd);
|
|
11058
11274
|
if (isRefusal(lockResult)) return Err(new CLIError(lockResult.message));
|
|
11059
11275
|
try {
|
|
11060
|
-
const roadmapPath =
|
|
11061
|
-
if (
|
|
11276
|
+
const roadmapPath = path55.join(cwd, "docs", "roadmap.md");
|
|
11277
|
+
if (fs34.existsSync(roadmapPath)) {
|
|
11062
11278
|
return Err(
|
|
11063
11279
|
new CLIError(`docs/roadmap.md already exists; refusing to overwrite (${roadmapPath})`)
|
|
11064
11280
|
);
|
|
@@ -11078,7 +11294,7 @@ async function runReverseMigrate(opts) {
|
|
|
11078
11294
|
return Err(new CLIError(`failed to fetch tracker features: ${fetchR.error.message}`));
|
|
11079
11295
|
}
|
|
11080
11296
|
const features = fetchR.value.features;
|
|
11081
|
-
const configPath =
|
|
11297
|
+
const configPath = path55.join(cwd, "harness.config.json");
|
|
11082
11298
|
const project = readProjectName(configPath);
|
|
11083
11299
|
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
11084
11300
|
const markdown = serializeRoadmap(featuresToRoadmap(features, project, nowIso));
|
|
@@ -11096,19 +11312,19 @@ async function runReverseMigrate(opts) {
|
|
|
11096
11312
|
emitReverseReport(report, features.length, isJson, true);
|
|
11097
11313
|
return Ok(report);
|
|
11098
11314
|
}
|
|
11099
|
-
|
|
11100
|
-
|
|
11315
|
+
fs34.mkdirSync(path55.dirname(roadmapPath), { recursive: true });
|
|
11316
|
+
fs34.writeFileSync(roadmapPath, markdown);
|
|
11101
11317
|
report.archivedTo = roadmapPath;
|
|
11102
|
-
if (
|
|
11103
|
-
const original =
|
|
11104
|
-
const configBackupPath =
|
|
11105
|
-
|
|
11318
|
+
if (fs34.existsSync(configPath)) {
|
|
11319
|
+
const original = fs34.readFileSync(configPath, "utf-8");
|
|
11320
|
+
const configBackupPath = path55.join(cwd, "harness.config.json.pre-migration");
|
|
11321
|
+
fs34.writeFileSync(configBackupPath, original);
|
|
11106
11322
|
report.configBackup = configBackupPath;
|
|
11107
11323
|
const parsed = JSON.parse(original);
|
|
11108
11324
|
const roadmapSection = parsed.roadmap ?? {};
|
|
11109
11325
|
roadmapSection.mode = "file-backed";
|
|
11110
11326
|
parsed.roadmap = roadmapSection;
|
|
11111
|
-
|
|
11327
|
+
fs34.writeFileSync(configPath, JSON.stringify(parsed, null, 2) + "\n");
|
|
11112
11328
|
}
|
|
11113
11329
|
emitReverseReport(report, features.length, isJson, false);
|
|
11114
11330
|
return Ok(report);
|
|
@@ -11153,23 +11369,23 @@ function createRoadmapMigrateCommand() {
|
|
|
11153
11369
|
// src/commands/roadmap/shard.ts
|
|
11154
11370
|
init_dist3();
|
|
11155
11371
|
import { Command as Command84 } from "commander";
|
|
11156
|
-
import * as
|
|
11372
|
+
import * as path56 from "path";
|
|
11157
11373
|
|
|
11158
11374
|
// src/commands/roadmap/shard-io.ts
|
|
11159
11375
|
import * as fsp from "fs/promises";
|
|
11160
11376
|
function createNodeShardIO() {
|
|
11161
11377
|
return {
|
|
11162
|
-
readFile: (
|
|
11163
|
-
writeFile: (
|
|
11378
|
+
readFile: (path81) => fsp.readFile(path81, "utf-8"),
|
|
11379
|
+
writeFile: (path81, data) => fsp.writeFile(path81, data, "utf-8"),
|
|
11164
11380
|
listDir: (dir) => fsp.readdir(dir),
|
|
11165
|
-
deleteFile: (
|
|
11381
|
+
deleteFile: (path81) => fsp.rm(path81),
|
|
11166
11382
|
mkdirp: async (dir) => {
|
|
11167
11383
|
await fsp.mkdir(dir, { recursive: true });
|
|
11168
11384
|
},
|
|
11169
|
-
rmrf: (
|
|
11170
|
-
exists: async (
|
|
11385
|
+
rmrf: (path81) => fsp.rm(path81, { recursive: true, force: true }),
|
|
11386
|
+
exists: async (path81) => {
|
|
11171
11387
|
try {
|
|
11172
|
-
await fsp.access(
|
|
11388
|
+
await fsp.access(path81);
|
|
11173
11389
|
return true;
|
|
11174
11390
|
} catch {
|
|
11175
11391
|
return false;
|
|
@@ -11186,8 +11402,8 @@ async function runRoadmapShard(opts = {}) {
|
|
|
11186
11402
|
const force = Boolean(opts.force);
|
|
11187
11403
|
const format = opts.format === "json" ? "json" : "human";
|
|
11188
11404
|
const assertRoundTrip = opts.assertRoundTrip ?? assertSemanticRoundTrip;
|
|
11189
|
-
const roadmapPath =
|
|
11190
|
-
const shardDir =
|
|
11405
|
+
const roadmapPath = path56.join(cwd, "docs", "roadmap.md").replaceAll("\\", "/");
|
|
11406
|
+
const shardDir = path56.join(cwd, "docs", "roadmap.d").replaceAll("\\", "/");
|
|
11191
11407
|
if (!await io.exists(roadmapPath)) {
|
|
11192
11408
|
return Err(new CLIError("docs/roadmap.md not found", ExitCode.ERROR));
|
|
11193
11409
|
}
|
|
@@ -11279,14 +11495,14 @@ function createRoadmapShardCommand() {
|
|
|
11279
11495
|
// src/commands/roadmap/unshard.ts
|
|
11280
11496
|
init_dist3();
|
|
11281
11497
|
import { Command as Command85 } from "commander";
|
|
11282
|
-
import * as
|
|
11498
|
+
import * as path57 from "path";
|
|
11283
11499
|
async function runRoadmapUnshard(opts = {}) {
|
|
11284
11500
|
const cwd = opts.cwd ?? process.cwd();
|
|
11285
11501
|
const io = opts.io ?? createNodeShardIO();
|
|
11286
11502
|
const dryRun = Boolean(opts.dryRun);
|
|
11287
11503
|
const format = opts.format === "json" ? "json" : "human";
|
|
11288
|
-
const shardDir =
|
|
11289
|
-
const roadmapPath =
|
|
11504
|
+
const shardDir = path57.join(cwd, "docs", "roadmap.d");
|
|
11505
|
+
const roadmapPath = path57.join(cwd, "docs", "roadmap.md");
|
|
11290
11506
|
if (!await io.exists(shardDir)) {
|
|
11291
11507
|
return Err(new CLIError("docs/roadmap.d not found; nothing to unshard", ExitCode.ERROR));
|
|
11292
11508
|
}
|
|
@@ -11335,14 +11551,14 @@ function createRoadmapUnshardCommand() {
|
|
|
11335
11551
|
// src/commands/roadmap/regen.ts
|
|
11336
11552
|
init_dist3();
|
|
11337
11553
|
import { Command as Command86 } from "commander";
|
|
11338
|
-
import * as
|
|
11554
|
+
import * as path58 from "path";
|
|
11339
11555
|
async function runRoadmapRegen(opts = {}) {
|
|
11340
11556
|
const cwd = opts.cwd ?? process.cwd();
|
|
11341
11557
|
const io = opts.io ?? createNodeShardIO();
|
|
11342
11558
|
const dryRun = Boolean(opts.dryRun);
|
|
11343
11559
|
const format = opts.format === "json" ? "json" : "human";
|
|
11344
|
-
const shardDir =
|
|
11345
|
-
const roadmapPath =
|
|
11560
|
+
const shardDir = path58.join(cwd, "docs", "roadmap.d");
|
|
11561
|
+
const roadmapPath = path58.join(cwd, "docs", "roadmap.md");
|
|
11346
11562
|
if (!await io.exists(shardDir)) {
|
|
11347
11563
|
return Err(new CLIError("docs/roadmap.d not found; project is not sharded", ExitCode.ERROR));
|
|
11348
11564
|
}
|
|
@@ -11388,8 +11604,8 @@ function createRoadmapRegenCommand() {
|
|
|
11388
11604
|
// src/commands/roadmap/reconcile.ts
|
|
11389
11605
|
init_dist3();
|
|
11390
11606
|
import { Command as Command87 } from "commander";
|
|
11391
|
-
import * as
|
|
11392
|
-
import * as
|
|
11607
|
+
import * as fs35 from "fs";
|
|
11608
|
+
import * as path59 from "path";
|
|
11393
11609
|
async function runRoadmapReconcile(opts = {}) {
|
|
11394
11610
|
const cwd = opts.cwd ?? process.cwd();
|
|
11395
11611
|
if (!opts.store && !roadmapSourceExists(cwd)) {
|
|
@@ -11467,8 +11683,8 @@ async function resolveAdapter(opts, cwd) {
|
|
|
11467
11683
|
)
|
|
11468
11684
|
);
|
|
11469
11685
|
}
|
|
11470
|
-
const envPath =
|
|
11471
|
-
if (
|
|
11686
|
+
const envPath = path59.join(cwd, ".env");
|
|
11687
|
+
if (fs35.existsSync(envPath) && !process.env.GITHUB_TOKEN) {
|
|
11472
11688
|
const { config: loadDotenv } = await import("dotenv");
|
|
11473
11689
|
loadDotenv({ path: envPath });
|
|
11474
11690
|
}
|
|
@@ -11580,9 +11796,9 @@ function authHeader() {
|
|
|
11580
11796
|
const tok = process.env["HARNESS_API_TOKEN"];
|
|
11581
11797
|
return tok ? { Authorization: `Bearer ${tok}` } : {};
|
|
11582
11798
|
}
|
|
11583
|
-
async function getJson(
|
|
11799
|
+
async function getJson(path81) {
|
|
11584
11800
|
try {
|
|
11585
|
-
const res = await fetch(`${orchestratorBase()}${
|
|
11801
|
+
const res = await fetch(`${orchestratorBase()}${path81}`, {
|
|
11586
11802
|
headers: { ...authHeader() }
|
|
11587
11803
|
});
|
|
11588
11804
|
const text = await res.text();
|
|
@@ -11597,9 +11813,9 @@ async function getJson(path80) {
|
|
|
11597
11813
|
};
|
|
11598
11814
|
}
|
|
11599
11815
|
}
|
|
11600
|
-
async function postJson(
|
|
11816
|
+
async function postJson(path81, body) {
|
|
11601
11817
|
try {
|
|
11602
|
-
const res = await fetch(`${orchestratorBase()}${
|
|
11818
|
+
const res = await fetch(`${orchestratorBase()}${path81}`, {
|
|
11603
11819
|
method: "POST",
|
|
11604
11820
|
headers: { "Content-Type": "application/json", ...authHeader() },
|
|
11605
11821
|
body: JSON.stringify(body)
|
|
@@ -11804,8 +12020,8 @@ function createRoutingCommand() {
|
|
|
11804
12020
|
// src/commands/scan-config.ts
|
|
11805
12021
|
init_dist3();
|
|
11806
12022
|
import { Command as Command94 } from "commander";
|
|
11807
|
-
import { existsSync as
|
|
11808
|
-
import { join as
|
|
12023
|
+
import { existsSync as existsSync31, readFileSync as readFileSync22, writeFileSync as writeFileSync23 } from "fs";
|
|
12024
|
+
import { join as join50, relative as relative4 } from "path";
|
|
11809
12025
|
var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".gemini/settings.json", "skill.yaml"];
|
|
11810
12026
|
function stripHighSeverityPatterns(content, injectionFindings) {
|
|
11811
12027
|
const highLines = /* @__PURE__ */ new Set();
|
|
@@ -11831,14 +12047,14 @@ function applyFix(filePath, targetDir, content, injectionFindings) {
|
|
|
11831
12047
|
if (!hasHighSeverity) return;
|
|
11832
12048
|
const { cleaned, linesStripped } = stripHighSeverityPatterns(content, injectionFindings);
|
|
11833
12049
|
if (linesStripped > 0) {
|
|
11834
|
-
|
|
12050
|
+
writeFileSync23(filePath, cleaned);
|
|
11835
12051
|
logger.info(
|
|
11836
|
-
`scan-config --fix: stripped ${linesStripped} high-severity line(s) from ${
|
|
12052
|
+
`scan-config --fix: stripped ${linesStripped} high-severity line(s) from ${relative4(targetDir, filePath).replaceAll("\\", "/")}`
|
|
11837
12053
|
);
|
|
11838
12054
|
}
|
|
11839
12055
|
}
|
|
11840
12056
|
function scanSingleFile(filePath, targetDir, scanner, options) {
|
|
11841
|
-
if (!
|
|
12057
|
+
if (!existsSync31(filePath)) return null;
|
|
11842
12058
|
let content;
|
|
11843
12059
|
try {
|
|
11844
12060
|
content = readFileSync22(filePath, "utf8");
|
|
@@ -11853,7 +12069,7 @@ function scanSingleFile(filePath, targetDir, scanner, options) {
|
|
|
11853
12069
|
applyFix(filePath, targetDir, content, injectionFindings);
|
|
11854
12070
|
}
|
|
11855
12071
|
return {
|
|
11856
|
-
file:
|
|
12072
|
+
file: relative4(targetDir, filePath).replaceAll("\\", "/"),
|
|
11857
12073
|
findings,
|
|
11858
12074
|
overallSeverity: computeOverallSeverity(findings)
|
|
11859
12075
|
};
|
|
@@ -11862,7 +12078,7 @@ async function runScanConfig(targetDir, options) {
|
|
|
11862
12078
|
const scanner = new SecurityScanner(parseSecurityConfig({}));
|
|
11863
12079
|
const results = [];
|
|
11864
12080
|
for (const configFile of CONFIG_FILES) {
|
|
11865
|
-
const result = scanSingleFile(
|
|
12081
|
+
const result = scanSingleFile(join50(targetDir, configFile), targetDir, scanner, options);
|
|
11866
12082
|
if (result) results.push(result);
|
|
11867
12083
|
}
|
|
11868
12084
|
return { exitCode: computeScanExitCode(results), results };
|
|
@@ -12040,10 +12256,10 @@ function printResult5(result, mode, _formatter) {
|
|
|
12040
12256
|
list.push(f);
|
|
12041
12257
|
byFile.set(f.target.file, list);
|
|
12042
12258
|
}
|
|
12043
|
-
for (const [file,
|
|
12259
|
+
for (const [file, fs51] of byFile) {
|
|
12044
12260
|
console.log(`
|
|
12045
12261
|
${file}`);
|
|
12046
|
-
for (const f of
|
|
12262
|
+
for (const f of fs51) {
|
|
12047
12263
|
console.log(
|
|
12048
12264
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.signal}:${f.target.line}`
|
|
12049
12265
|
);
|
|
@@ -12060,23 +12276,23 @@ ${file}`);
|
|
|
12060
12276
|
|
|
12061
12277
|
// src/commands/setup.ts
|
|
12062
12278
|
import { Command as Command97 } from "commander";
|
|
12063
|
-
import * as
|
|
12279
|
+
import * as fs38 from "fs";
|
|
12064
12280
|
import * as os9 from "os";
|
|
12065
|
-
import * as
|
|
12281
|
+
import * as path62 from "path";
|
|
12066
12282
|
import chalk16 from "chalk";
|
|
12067
12283
|
|
|
12068
12284
|
// src/utils/first-run.ts
|
|
12069
|
-
import * as
|
|
12285
|
+
import * as fs36 from "fs";
|
|
12070
12286
|
import * as os8 from "os";
|
|
12071
|
-
import * as
|
|
12072
|
-
var HARNESS_DIR =
|
|
12073
|
-
var MARKER_FILE =
|
|
12287
|
+
import * as path60 from "path";
|
|
12288
|
+
var HARNESS_DIR = path60.join(os8.homedir(), ".harness");
|
|
12289
|
+
var MARKER_FILE = path60.join(HARNESS_DIR, ".setup-complete");
|
|
12074
12290
|
function isFirstRun() {
|
|
12075
|
-
return !
|
|
12291
|
+
return !fs36.existsSync(MARKER_FILE);
|
|
12076
12292
|
}
|
|
12077
12293
|
function markSetupComplete() {
|
|
12078
|
-
|
|
12079
|
-
|
|
12294
|
+
fs36.mkdirSync(HARNESS_DIR, { recursive: true });
|
|
12295
|
+
fs36.writeFileSync(MARKER_FILE, "", "utf-8");
|
|
12080
12296
|
}
|
|
12081
12297
|
function printFirstRunWelcome() {
|
|
12082
12298
|
try {
|
|
@@ -12089,8 +12305,8 @@ function printFirstRunWelcome() {
|
|
|
12089
12305
|
}
|
|
12090
12306
|
|
|
12091
12307
|
// src/commands/telemetry-wizard.ts
|
|
12092
|
-
import * as
|
|
12093
|
-
import * as
|
|
12308
|
+
import * as fs37 from "fs";
|
|
12309
|
+
import * as path61 from "path";
|
|
12094
12310
|
import readline2 from "readline";
|
|
12095
12311
|
import chalk15 from "chalk";
|
|
12096
12312
|
function prompt2(question) {
|
|
@@ -12098,10 +12314,10 @@ function prompt2(question) {
|
|
|
12098
12314
|
input: process.stdin,
|
|
12099
12315
|
output: process.stdout
|
|
12100
12316
|
});
|
|
12101
|
-
return new Promise((
|
|
12317
|
+
return new Promise((resolve50) => {
|
|
12102
12318
|
rl.question(question, (answer) => {
|
|
12103
12319
|
rl.close();
|
|
12104
|
-
|
|
12320
|
+
resolve50(answer.trim().toLowerCase());
|
|
12105
12321
|
});
|
|
12106
12322
|
});
|
|
12107
12323
|
}
|
|
@@ -12110,10 +12326,10 @@ function promptRaw(question) {
|
|
|
12110
12326
|
input: process.stdin,
|
|
12111
12327
|
output: process.stdout
|
|
12112
12328
|
});
|
|
12113
|
-
return new Promise((
|
|
12329
|
+
return new Promise((resolve50) => {
|
|
12114
12330
|
rl.question(question, (answer) => {
|
|
12115
12331
|
rl.close();
|
|
12116
|
-
|
|
12332
|
+
resolve50(answer.trim());
|
|
12117
12333
|
});
|
|
12118
12334
|
});
|
|
12119
12335
|
}
|
|
@@ -12121,21 +12337,21 @@ function isNo(answer) {
|
|
|
12121
12337
|
return answer === "n" || answer === "no";
|
|
12122
12338
|
}
|
|
12123
12339
|
function isTelemetryConfigured(cwd) {
|
|
12124
|
-
const configPath =
|
|
12340
|
+
const configPath = path61.join(cwd, "harness.config.json");
|
|
12125
12341
|
try {
|
|
12126
|
-
const raw = JSON.parse(
|
|
12342
|
+
const raw = JSON.parse(fs37.readFileSync(configPath, "utf-8"));
|
|
12127
12343
|
return raw.telemetry !== void 0 && raw.adoption !== void 0;
|
|
12128
12344
|
} catch {
|
|
12129
12345
|
return false;
|
|
12130
12346
|
}
|
|
12131
12347
|
}
|
|
12132
12348
|
function writeTelemetryConfig(cwd, result) {
|
|
12133
|
-
const configPath =
|
|
12349
|
+
const configPath = path61.join(cwd, "harness.config.json");
|
|
12134
12350
|
try {
|
|
12135
|
-
const raw = JSON.parse(
|
|
12351
|
+
const raw = JSON.parse(fs37.readFileSync(configPath, "utf-8"));
|
|
12136
12352
|
raw.telemetry = { enabled: result.telemetryEnabled };
|
|
12137
12353
|
raw.adoption = { enabled: result.adoptionEnabled };
|
|
12138
|
-
|
|
12354
|
+
fs37.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
|
|
12139
12355
|
} catch (err) {
|
|
12140
12356
|
console.warn(
|
|
12141
12357
|
`\u26A0 Could not update ${configPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -12143,10 +12359,10 @@ function writeTelemetryConfig(cwd, result) {
|
|
|
12143
12359
|
}
|
|
12144
12360
|
const hasIdentity = result.identity.project || result.identity.team || result.identity.alias;
|
|
12145
12361
|
if (hasIdentity) {
|
|
12146
|
-
const harnessDir =
|
|
12147
|
-
|
|
12148
|
-
const telemetryFile =
|
|
12149
|
-
|
|
12362
|
+
const harnessDir = path61.join(cwd, ".harness");
|
|
12363
|
+
fs37.mkdirSync(harnessDir, { recursive: true });
|
|
12364
|
+
const telemetryFile = path61.join(harnessDir, "telemetry.json");
|
|
12365
|
+
fs37.writeFileSync(telemetryFile, JSON.stringify({ identity: result.identity }, null, 2) + "\n");
|
|
12150
12366
|
}
|
|
12151
12367
|
}
|
|
12152
12368
|
async function promptIdentity() {
|
|
@@ -12192,8 +12408,8 @@ async function runTelemetryWizard() {
|
|
|
12192
12408
|
return { telemetryEnabled, adoptionEnabled, identity };
|
|
12193
12409
|
}
|
|
12194
12410
|
async function ensureTelemetryConfigured(cwd) {
|
|
12195
|
-
const configPath =
|
|
12196
|
-
if (!
|
|
12411
|
+
const configPath = path61.join(cwd, "harness.config.json");
|
|
12412
|
+
if (!fs37.existsSync(configPath)) {
|
|
12197
12413
|
return { status: "warn", message: "Not a harness project \u2014 skipped telemetry configuration" };
|
|
12198
12414
|
}
|
|
12199
12415
|
if (isTelemetryConfigured(cwd)) {
|
|
@@ -12239,7 +12455,7 @@ function runSlashCommandGeneration() {
|
|
|
12239
12455
|
}
|
|
12240
12456
|
}
|
|
12241
12457
|
function detectClient(dirName) {
|
|
12242
|
-
return
|
|
12458
|
+
return fs38.existsSync(path62.join(os9.homedir(), dirName));
|
|
12243
12459
|
}
|
|
12244
12460
|
async function runMcpSetup(cwd) {
|
|
12245
12461
|
const results = [];
|
|
@@ -12255,7 +12471,7 @@ async function runMcpSetup(cwd) {
|
|
|
12255
12471
|
{ name: "Cursor", dir: ".cursor", client: "cursor", configTarget: ".cursor/mcp.json" },
|
|
12256
12472
|
{
|
|
12257
12473
|
name: "OpenCode",
|
|
12258
|
-
dir:
|
|
12474
|
+
dir: path62.join(".config", "opencode"),
|
|
12259
12475
|
client: "opencode",
|
|
12260
12476
|
configTarget: "opencode.json"
|
|
12261
12477
|
}
|
|
@@ -12287,7 +12503,7 @@ function formatStep(result) {
|
|
|
12287
12503
|
}
|
|
12288
12504
|
function configureTier0Integrations(cwd) {
|
|
12289
12505
|
try {
|
|
12290
|
-
const mcpPath =
|
|
12506
|
+
const mcpPath = path62.join(cwd, ".mcp.json");
|
|
12291
12507
|
const config = readMcpConfig(mcpPath);
|
|
12292
12508
|
const tier0 = INTEGRATION_REGISTRY.filter((i) => i.tier === 0);
|
|
12293
12509
|
const added = [];
|
|
@@ -12296,9 +12512,9 @@ function configureTier0Integrations(cwd) {
|
|
|
12296
12512
|
writeMcpEntry(mcpPath, integration.name, integration.mcpConfig);
|
|
12297
12513
|
added.push(integration.displayName);
|
|
12298
12514
|
}
|
|
12299
|
-
const geminiDir =
|
|
12300
|
-
if (
|
|
12301
|
-
const geminiPath =
|
|
12515
|
+
const geminiDir = path62.join(cwd, ".gemini");
|
|
12516
|
+
if (fs38.existsSync(geminiDir)) {
|
|
12517
|
+
const geminiPath = path62.join(geminiDir, "settings.json");
|
|
12302
12518
|
const geminiConfig = readMcpConfig(geminiPath);
|
|
12303
12519
|
for (const integration of tier0) {
|
|
12304
12520
|
if (!geminiConfig.mcpServers[integration.name]) {
|
|
@@ -12306,9 +12522,9 @@ function configureTier0Integrations(cwd) {
|
|
|
12306
12522
|
}
|
|
12307
12523
|
}
|
|
12308
12524
|
}
|
|
12309
|
-
const opencodePath =
|
|
12310
|
-
const opencodeGlobalDir =
|
|
12311
|
-
if (
|
|
12525
|
+
const opencodePath = path62.join(cwd, "opencode.json");
|
|
12526
|
+
const opencodeGlobalDir = path62.join(os9.homedir(), ".config", "opencode");
|
|
12527
|
+
if (fs38.existsSync(opencodePath) || fs38.existsSync(opencodeGlobalDir)) {
|
|
12312
12528
|
for (const integration of tier0) {
|
|
12313
12529
|
writeOpencodeMcpEntry(opencodePath, integration.name, integration.mcpConfig);
|
|
12314
12530
|
}
|
|
@@ -12326,14 +12542,14 @@ function configureTier0Integrations(cwd) {
|
|
|
12326
12542
|
}
|
|
12327
12543
|
}
|
|
12328
12544
|
function ensureHooks(cwd) {
|
|
12329
|
-
const configPath =
|
|
12330
|
-
if (!
|
|
12545
|
+
const configPath = path62.join(cwd, "harness.config.json");
|
|
12546
|
+
if (!fs38.existsSync(configPath)) {
|
|
12331
12547
|
return { status: "warn", message: "Not a harness project \u2014 skipped hook installation" };
|
|
12332
12548
|
}
|
|
12333
12549
|
let profile = "standard";
|
|
12334
|
-
const profilePath =
|
|
12550
|
+
const profilePath = path62.join(cwd, ".harness", "hooks", "profile.json");
|
|
12335
12551
|
try {
|
|
12336
|
-
const data = JSON.parse(
|
|
12552
|
+
const data = JSON.parse(fs38.readFileSync(profilePath, "utf-8"));
|
|
12337
12553
|
if (data.profile && ["minimal", "standard", "strict"].includes(data.profile)) {
|
|
12338
12554
|
profile = data.profile;
|
|
12339
12555
|
}
|
|
@@ -12428,14 +12644,14 @@ function createSetupCommand() {
|
|
|
12428
12644
|
// src/commands/share.ts
|
|
12429
12645
|
init_dist3();
|
|
12430
12646
|
import { Command as Command98 } from "commander";
|
|
12431
|
-
import * as
|
|
12432
|
-
import * as
|
|
12647
|
+
import * as fs39 from "fs";
|
|
12648
|
+
import * as path63 from "path";
|
|
12433
12649
|
import { parse as parseYaml3 } from "yaml";
|
|
12434
12650
|
var MANIFEST_FILENAME = "constraints.yaml";
|
|
12435
12651
|
async function runShareAction(projectPath, options) {
|
|
12436
|
-
const rootDir =
|
|
12437
|
-
const manifestPath =
|
|
12438
|
-
if (!
|
|
12652
|
+
const rootDir = path63.resolve(projectPath);
|
|
12653
|
+
const manifestPath = path63.join(rootDir, MANIFEST_FILENAME);
|
|
12654
|
+
if (!fs39.existsSync(manifestPath)) {
|
|
12439
12655
|
logger.error(
|
|
12440
12656
|
`No ${MANIFEST_FILENAME} found at ${manifestPath}.
|
|
12441
12657
|
Create a constraints.yaml in your project root to define what to share.`
|
|
@@ -12444,7 +12660,7 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
12444
12660
|
}
|
|
12445
12661
|
let parsed;
|
|
12446
12662
|
try {
|
|
12447
|
-
const raw =
|
|
12663
|
+
const raw = fs39.readFileSync(manifestPath, "utf-8");
|
|
12448
12664
|
parsed = parseYaml3(raw);
|
|
12449
12665
|
} catch (err) {
|
|
12450
12666
|
logger.error(
|
|
@@ -12458,7 +12674,7 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
12458
12674
|
process.exit(1);
|
|
12459
12675
|
}
|
|
12460
12676
|
const manifest = manifestResult.value;
|
|
12461
|
-
const configResult = resolveConfig(
|
|
12677
|
+
const configResult = resolveConfig(path63.join(rootDir, "harness.config.json"));
|
|
12462
12678
|
if (!configResult.ok) {
|
|
12463
12679
|
logger.error(configResult.error.message);
|
|
12464
12680
|
process.exit(1);
|
|
@@ -12476,8 +12692,8 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
12476
12692
|
);
|
|
12477
12693
|
process.exit(1);
|
|
12478
12694
|
}
|
|
12479
|
-
const outputDir =
|
|
12480
|
-
const outputPath =
|
|
12695
|
+
const outputDir = path63.resolve(options.output);
|
|
12696
|
+
const outputPath = path63.join(outputDir, `${manifest.name}.harness-constraints.json`);
|
|
12481
12697
|
const writeResult = await writeConfig(outputPath, bundle);
|
|
12482
12698
|
if (!writeResult.ok) {
|
|
12483
12699
|
logger.error(`Failed to write bundle: ${writeResult.error.message}`);
|
|
@@ -12496,18 +12712,18 @@ import { Command as Command106 } from "commander";
|
|
|
12496
12712
|
|
|
12497
12713
|
// src/commands/skill/list.ts
|
|
12498
12714
|
import { Command as Command99 } from "commander";
|
|
12499
|
-
import * as
|
|
12500
|
-
import * as
|
|
12715
|
+
import * as fs40 from "fs";
|
|
12716
|
+
import * as path64 from "path";
|
|
12501
12717
|
import { parse } from "yaml";
|
|
12502
12718
|
function scanDirectory(dirPath, source) {
|
|
12503
|
-
if (!
|
|
12504
|
-
const entries =
|
|
12719
|
+
if (!fs40.existsSync(dirPath)) return [];
|
|
12720
|
+
const entries = fs40.readdirSync(dirPath, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
12505
12721
|
const skills = [];
|
|
12506
12722
|
for (const name of entries) {
|
|
12507
|
-
const yamlPath =
|
|
12508
|
-
if (!
|
|
12723
|
+
const yamlPath = path64.join(dirPath, name, "skill.yaml");
|
|
12724
|
+
if (!fs40.existsSync(yamlPath)) continue;
|
|
12509
12725
|
try {
|
|
12510
|
-
const raw =
|
|
12726
|
+
const raw = fs40.readFileSync(yamlPath, "utf-8");
|
|
12511
12727
|
const parsed = parse(raw);
|
|
12512
12728
|
const result = SkillMetadataSchema.safeParse(parsed);
|
|
12513
12729
|
if (result.success) {
|
|
@@ -12525,10 +12741,10 @@ function scanDirectory(dirPath, source) {
|
|
|
12525
12741
|
}
|
|
12526
12742
|
function collectCommunitySkills(seen, allSkills) {
|
|
12527
12743
|
const globalDir = resolveGlobalSkillsDir();
|
|
12528
|
-
const skillsDir =
|
|
12529
|
-
const communityBase =
|
|
12530
|
-
const communityPlatformDir =
|
|
12531
|
-
const lockfilePath =
|
|
12744
|
+
const skillsDir = path64.dirname(globalDir);
|
|
12745
|
+
const communityBase = path64.join(skillsDir, "community");
|
|
12746
|
+
const communityPlatformDir = path64.join(communityBase, "claude-code");
|
|
12747
|
+
const lockfilePath = path64.join(communityBase, "skills-lock.json");
|
|
12532
12748
|
const lockfile = readLockfile2(lockfilePath);
|
|
12533
12749
|
const communitySkills = scanDirectory(communityPlatformDir, "community");
|
|
12534
12750
|
for (const skill of communitySkills) {
|
|
@@ -12614,8 +12830,8 @@ function createListCommand3() {
|
|
|
12614
12830
|
|
|
12615
12831
|
// src/commands/skill/run.ts
|
|
12616
12832
|
import { Command as Command100 } from "commander";
|
|
12617
|
-
import * as
|
|
12618
|
-
import * as
|
|
12833
|
+
import * as fs41 from "fs";
|
|
12834
|
+
import * as path65 from "path";
|
|
12619
12835
|
import { parse as parse2 } from "yaml";
|
|
12620
12836
|
|
|
12621
12837
|
// src/skill/complexity.ts
|
|
@@ -12711,10 +12927,10 @@ ${options.principles}`);
|
|
|
12711
12927
|
|
|
12712
12928
|
// src/commands/skill/run.ts
|
|
12713
12929
|
function loadSkillMetadata(skillDir) {
|
|
12714
|
-
const yamlPath =
|
|
12715
|
-
if (!
|
|
12930
|
+
const yamlPath = path65.join(skillDir, "skill.yaml");
|
|
12931
|
+
if (!fs41.existsSync(yamlPath)) return null;
|
|
12716
12932
|
try {
|
|
12717
|
-
const result = SkillMetadataSchema.safeParse(parse2(
|
|
12933
|
+
const result = SkillMetadataSchema.safeParse(parse2(fs41.readFileSync(yamlPath, "utf-8")));
|
|
12718
12934
|
return result.success ? result.data : null;
|
|
12719
12935
|
} catch {
|
|
12720
12936
|
return null;
|
|
@@ -12726,22 +12942,22 @@ function resolveComplexity(metadata, requested, projectPath) {
|
|
|
12726
12942
|
return requested;
|
|
12727
12943
|
}
|
|
12728
12944
|
function loadPrinciples(projectPath) {
|
|
12729
|
-
const principlesPath =
|
|
12730
|
-
return
|
|
12945
|
+
const principlesPath = path65.join(projectPath, "docs", "principles.md");
|
|
12946
|
+
return fs41.existsSync(principlesPath) ? fs41.readFileSync(principlesPath, "utf-8") : void 0;
|
|
12731
12947
|
}
|
|
12732
12948
|
function readMostRecentFileInDir(dirPath) {
|
|
12733
|
-
const files =
|
|
12734
|
-
if (files.length > 0) return
|
|
12949
|
+
const files = fs41.readdirSync(dirPath).map((f) => ({ name: f, mtime: fs41.statSync(path65.join(dirPath, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
12950
|
+
if (files.length > 0) return fs41.readFileSync(path65.join(dirPath, files[0].name), "utf-8");
|
|
12735
12951
|
return void 0;
|
|
12736
12952
|
}
|
|
12737
12953
|
function loadPriorState(metadata, projectPath) {
|
|
12738
12954
|
if (!metadata?.state.persistent || metadata.state.files.length === 0) return void 0;
|
|
12739
12955
|
for (const stateFilePath of metadata.state.files) {
|
|
12740
|
-
const fullPath =
|
|
12741
|
-
if (!
|
|
12742
|
-
const stat =
|
|
12956
|
+
const fullPath = path65.join(projectPath, stateFilePath);
|
|
12957
|
+
if (!fs41.existsSync(fullPath)) continue;
|
|
12958
|
+
const stat = fs41.statSync(fullPath);
|
|
12743
12959
|
if (stat.isDirectory()) return readMostRecentFileInDir(fullPath);
|
|
12744
|
-
return
|
|
12960
|
+
return fs41.readFileSync(fullPath, "utf-8");
|
|
12745
12961
|
}
|
|
12746
12962
|
return void 0;
|
|
12747
12963
|
}
|
|
@@ -12761,7 +12977,7 @@ function resolvePhaseState(metadata, projectPath, phase) {
|
|
|
12761
12977
|
}
|
|
12762
12978
|
async function appendProjectState(content, metadata, projectPath, hasPathOpt) {
|
|
12763
12979
|
if (!metadata?.state.persistent || !hasPathOpt) return content;
|
|
12764
|
-
const { readHarnessState: readHarnessState2, isEmptyHarnessState } = await import("./state-events-
|
|
12980
|
+
const { readHarnessState: readHarnessState2, isEmptyHarnessState } = await import("./state-events-PBDNQPXT.js");
|
|
12765
12981
|
const stateResult = await readHarnessState2(projectPath);
|
|
12766
12982
|
if (!stateResult.ok || isEmptyHarnessState(stateResult.value)) return content;
|
|
12767
12983
|
const stateContent = JSON.stringify(stateResult.value, null, 2);
|
|
@@ -12782,7 +12998,7 @@ async function runSkill(name, opts) {
|
|
|
12782
12998
|
return;
|
|
12783
12999
|
}
|
|
12784
13000
|
const metadata = loadSkillMetadata(skillDir);
|
|
12785
|
-
const projectPath = opts.path ?
|
|
13001
|
+
const projectPath = opts.path ? path65.resolve(opts.path) : process.cwd();
|
|
12786
13002
|
const complexity = resolveComplexity(
|
|
12787
13003
|
metadata,
|
|
12788
13004
|
opts.complexity ?? "standard",
|
|
@@ -12809,14 +13025,14 @@ async function runSkill(name, opts) {
|
|
|
12809
13025
|
...stateWarning !== void 0 && { stateWarning },
|
|
12810
13026
|
...opts.party !== void 0 && { party: opts.party }
|
|
12811
13027
|
});
|
|
12812
|
-
const skillMdPath =
|
|
12813
|
-
if (!
|
|
13028
|
+
const skillMdPath = path65.join(skillDir, "SKILL.md");
|
|
13029
|
+
if (!fs41.existsSync(skillMdPath)) {
|
|
12814
13030
|
logger.error(`SKILL.md not found for skill: ${name}`);
|
|
12815
13031
|
process.exit(ExitCode.ERROR);
|
|
12816
13032
|
return;
|
|
12817
13033
|
}
|
|
12818
13034
|
const content = await appendProjectState(
|
|
12819
|
-
|
|
13035
|
+
fs41.readFileSync(skillMdPath, "utf-8"),
|
|
12820
13036
|
metadata,
|
|
12821
13037
|
projectPath,
|
|
12822
13038
|
!!opts.path
|
|
@@ -12835,8 +13051,8 @@ function createRunCommand3() {
|
|
|
12835
13051
|
|
|
12836
13052
|
// src/commands/skill/validate.ts
|
|
12837
13053
|
import { Command as Command101 } from "commander";
|
|
12838
|
-
import * as
|
|
12839
|
-
import * as
|
|
13054
|
+
import * as fs42 from "fs";
|
|
13055
|
+
import * as path66 from "path";
|
|
12840
13056
|
import { parse as parse3 } from "yaml";
|
|
12841
13057
|
var BEHAVIORAL_REQUIRED_SECTIONS = [
|
|
12842
13058
|
"## When to Use",
|
|
@@ -12848,11 +13064,11 @@ var BEHAVIORAL_REQUIRED_SECTIONS = [
|
|
|
12848
13064
|
];
|
|
12849
13065
|
var KNOWLEDGE_REQUIRED_SECTIONS = ["## Instructions"];
|
|
12850
13066
|
function validateSkillMd(name, skillMdPath, skillType, errors) {
|
|
12851
|
-
if (!
|
|
13067
|
+
if (!fs42.existsSync(skillMdPath)) {
|
|
12852
13068
|
errors.push(`${name}: missing SKILL.md`);
|
|
12853
13069
|
return;
|
|
12854
13070
|
}
|
|
12855
|
-
const mdContent =
|
|
13071
|
+
const mdContent = fs42.readFileSync(skillMdPath, "utf-8");
|
|
12856
13072
|
if (!mdContent.trim().startsWith("# ")) {
|
|
12857
13073
|
errors.push(`${name}/SKILL.md: must start with an h1 heading`);
|
|
12858
13074
|
}
|
|
@@ -12877,20 +13093,20 @@ function validateSkillMd(name, skillMdPath, skillType, errors) {
|
|
|
12877
13093
|
}
|
|
12878
13094
|
}
|
|
12879
13095
|
function validateSkillEntry(name, skillsDir, errors) {
|
|
12880
|
-
const skillDir =
|
|
12881
|
-
const yamlPath =
|
|
12882
|
-
if (!
|
|
13096
|
+
const skillDir = path66.join(skillsDir, name);
|
|
13097
|
+
const yamlPath = path66.join(skillDir, "skill.yaml");
|
|
13098
|
+
if (!fs42.existsSync(yamlPath)) {
|
|
12883
13099
|
errors.push(`${name}: missing skill.yaml`);
|
|
12884
13100
|
return false;
|
|
12885
13101
|
}
|
|
12886
13102
|
try {
|
|
12887
|
-
const raw =
|
|
13103
|
+
const raw = fs42.readFileSync(yamlPath, "utf-8");
|
|
12888
13104
|
const result = SkillMetadataSchema.safeParse(parse3(raw));
|
|
12889
13105
|
if (!result.success) {
|
|
12890
13106
|
errors.push(`${name}/skill.yaml: ${result.error.message}`);
|
|
12891
13107
|
return false;
|
|
12892
13108
|
}
|
|
12893
|
-
validateSkillMd(name,
|
|
13109
|
+
validateSkillMd(name, path66.join(skillDir, "SKILL.md"), result.data.type, errors);
|
|
12894
13110
|
return true;
|
|
12895
13111
|
} catch (e) {
|
|
12896
13112
|
errors.push(`${name}: parse error \u2014 ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -12901,12 +13117,12 @@ function createValidateCommand2() {
|
|
|
12901
13117
|
return new Command101("validate").description("Validate all skill.yaml files and SKILL.md structure").action(async (_opts, cmd) => {
|
|
12902
13118
|
const globalOpts = cmd.optsWithGlobals();
|
|
12903
13119
|
const skillsDir = resolveSkillsDir();
|
|
12904
|
-
if (!
|
|
13120
|
+
if (!fs42.existsSync(skillsDir)) {
|
|
12905
13121
|
logger.info("No skills directory found.");
|
|
12906
13122
|
process.exit(ExitCode.SUCCESS);
|
|
12907
13123
|
return;
|
|
12908
13124
|
}
|
|
12909
|
-
const entries =
|
|
13125
|
+
const entries = fs42.readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
12910
13126
|
const errors = [];
|
|
12911
13127
|
let validated = 0;
|
|
12912
13128
|
for (const name of entries) {
|
|
@@ -12929,8 +13145,8 @@ function createValidateCommand2() {
|
|
|
12929
13145
|
|
|
12930
13146
|
// src/commands/skill/info.ts
|
|
12931
13147
|
import { Command as Command102 } from "commander";
|
|
12932
|
-
import * as
|
|
12933
|
-
import * as
|
|
13148
|
+
import * as fs43 from "fs";
|
|
13149
|
+
import * as path67 from "path";
|
|
12934
13150
|
import { parse as parse4 } from "yaml";
|
|
12935
13151
|
function loadSkillMetadata2(name) {
|
|
12936
13152
|
const skillDir = resolveSkillDir(name);
|
|
@@ -12938,13 +13154,13 @@ function loadSkillMetadata2(name) {
|
|
|
12938
13154
|
logger.error(`Skill not found: ${name}`);
|
|
12939
13155
|
return { ok: false, exitCode: ExitCode.ERROR };
|
|
12940
13156
|
}
|
|
12941
|
-
const yamlPath =
|
|
12942
|
-
if (!
|
|
13157
|
+
const yamlPath = path67.join(skillDir, "skill.yaml");
|
|
13158
|
+
if (!fs43.existsSync(yamlPath)) {
|
|
12943
13159
|
logger.error(`skill.yaml not found for skill: ${name}`);
|
|
12944
13160
|
return { ok: false, exitCode: ExitCode.ERROR };
|
|
12945
13161
|
}
|
|
12946
13162
|
try {
|
|
12947
|
-
const raw =
|
|
13163
|
+
const raw = fs43.readFileSync(yamlPath, "utf-8");
|
|
12948
13164
|
const parsed = parse4(raw);
|
|
12949
13165
|
const result = SkillMetadataSchema.safeParse(parsed);
|
|
12950
13166
|
if (!result.success) {
|
|
@@ -13042,8 +13258,8 @@ function createSearchCommand2() {
|
|
|
13042
13258
|
|
|
13043
13259
|
// src/commands/skill/create.ts
|
|
13044
13260
|
import { Command as Command104 } from "commander";
|
|
13045
|
-
import * as
|
|
13046
|
-
import * as
|
|
13261
|
+
import * as path68 from "path";
|
|
13262
|
+
import * as fs44 from "fs";
|
|
13047
13263
|
import YAML from "yaml";
|
|
13048
13264
|
var KEBAB_CASE_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
13049
13265
|
function buildReadme(name, description) {
|
|
@@ -13127,22 +13343,22 @@ function runCreate(name, opts) {
|
|
|
13127
13343
|
if (!KEBAB_CASE_RE.test(name)) {
|
|
13128
13344
|
throw new Error(`Invalid skill name "${name}". Must be kebab-case (e.g., my-skill).`);
|
|
13129
13345
|
}
|
|
13130
|
-
const baseDir = opts.outputDir ??
|
|
13131
|
-
const skillDir =
|
|
13132
|
-
if (
|
|
13346
|
+
const baseDir = opts.outputDir ?? path68.join(process.cwd(), "agents", "skills", "claude-code");
|
|
13347
|
+
const skillDir = path68.join(baseDir, name);
|
|
13348
|
+
if (fs44.existsSync(skillDir)) {
|
|
13133
13349
|
throw new Error(`Skill directory already exists: ${skillDir}`);
|
|
13134
13350
|
}
|
|
13135
|
-
|
|
13351
|
+
fs44.mkdirSync(skillDir, { recursive: true });
|
|
13136
13352
|
const description = opts.description || `A community skill: ${name}`;
|
|
13137
13353
|
const skillYaml = buildSkillYaml(name, opts);
|
|
13138
|
-
const skillYamlPath =
|
|
13139
|
-
|
|
13354
|
+
const skillYamlPath = path68.join(skillDir, "skill.yaml");
|
|
13355
|
+
fs44.writeFileSync(skillYamlPath, YAML.stringify(skillYaml));
|
|
13140
13356
|
const skillMd = buildSkillMd(name, description);
|
|
13141
|
-
const skillMdPath =
|
|
13142
|
-
|
|
13357
|
+
const skillMdPath = path68.join(skillDir, "SKILL.md");
|
|
13358
|
+
fs44.writeFileSync(skillMdPath, skillMd);
|
|
13143
13359
|
const readme = buildReadme(name, description);
|
|
13144
|
-
const readmePath =
|
|
13145
|
-
|
|
13360
|
+
const readmePath = path68.join(skillDir, "README.md");
|
|
13361
|
+
fs44.writeFileSync(readmePath, readme);
|
|
13146
13362
|
return {
|
|
13147
13363
|
name,
|
|
13148
13364
|
directory: skillDir,
|
|
@@ -13156,7 +13372,7 @@ function printCreateResult(name, result) {
|
|
|
13156
13372
|
}
|
|
13157
13373
|
logger.info(`
|
|
13158
13374
|
Next steps:`);
|
|
13159
|
-
logger.info(` 1. Edit ${
|
|
13375
|
+
logger.info(` 1. Edit ${path68.join(result.directory, "SKILL.md")} with your skill content`);
|
|
13160
13376
|
logger.info(` 2. Run: harness skill validate ${name}`);
|
|
13161
13377
|
logger.info(` 3. Run: harness skills publish`);
|
|
13162
13378
|
}
|
|
@@ -13185,25 +13401,25 @@ function createCreateCommand() {
|
|
|
13185
13401
|
|
|
13186
13402
|
// src/commands/skill/publish.ts
|
|
13187
13403
|
import { Command as Command105 } from "commander";
|
|
13188
|
-
import * as
|
|
13189
|
-
import * as
|
|
13404
|
+
import * as fs46 from "fs";
|
|
13405
|
+
import * as path70 from "path";
|
|
13190
13406
|
import { execFileSync as execFileSync9 } from "child_process";
|
|
13191
13407
|
|
|
13192
13408
|
// src/registry/validator.ts
|
|
13193
|
-
import * as
|
|
13194
|
-
import * as
|
|
13409
|
+
import * as fs45 from "fs";
|
|
13410
|
+
import * as path69 from "path";
|
|
13195
13411
|
import { parse as parse5 } from "yaml";
|
|
13196
13412
|
import semver4 from "semver";
|
|
13197
13413
|
async function validateForPublish(skillDir, registryUrl) {
|
|
13198
13414
|
const errors = [];
|
|
13199
|
-
const skillYamlPath =
|
|
13200
|
-
if (!
|
|
13415
|
+
const skillYamlPath = path69.join(skillDir, "skill.yaml");
|
|
13416
|
+
if (!fs45.existsSync(skillYamlPath)) {
|
|
13201
13417
|
errors.push("skill.yaml not found. Create one with: harness skill create <name>");
|
|
13202
13418
|
return { valid: false, errors };
|
|
13203
13419
|
}
|
|
13204
13420
|
let skillMeta;
|
|
13205
13421
|
try {
|
|
13206
|
-
const raw =
|
|
13422
|
+
const raw = fs45.readFileSync(skillYamlPath, "utf-8");
|
|
13207
13423
|
const parsed = parse5(raw);
|
|
13208
13424
|
const result = SkillMetadataSchema.safeParse(parsed);
|
|
13209
13425
|
if (!result.success) {
|
|
@@ -13225,11 +13441,11 @@ async function validateForPublish(skillDir, registryUrl) {
|
|
|
13225
13441
|
if (!skillMeta.triggers || skillMeta.triggers.length === 0) {
|
|
13226
13442
|
errors.push("At least one trigger is required. Add triggers to skill.yaml.");
|
|
13227
13443
|
}
|
|
13228
|
-
const skillMdPath =
|
|
13229
|
-
if (!
|
|
13444
|
+
const skillMdPath = path69.join(skillDir, "SKILL.md");
|
|
13445
|
+
if (!fs45.existsSync(skillMdPath)) {
|
|
13230
13446
|
errors.push("SKILL.md not found. Create it with content describing your skill.");
|
|
13231
13447
|
} else {
|
|
13232
|
-
const content =
|
|
13448
|
+
const content = fs45.readFileSync(skillMdPath, "utf-8");
|
|
13233
13449
|
if (!content.includes("## When to Use")) {
|
|
13234
13450
|
errors.push('SKILL.md must contain a "## When to Use" section.');
|
|
13235
13451
|
}
|
|
@@ -13309,11 +13525,11 @@ ${errorList}`);
|
|
|
13309
13525
|
}
|
|
13310
13526
|
const meta = validation.skillMeta;
|
|
13311
13527
|
const pkg = derivePackageJson(meta);
|
|
13312
|
-
const pkgPath =
|
|
13313
|
-
|
|
13314
|
-
const readmePath =
|
|
13315
|
-
if (!
|
|
13316
|
-
const skillMdContent =
|
|
13528
|
+
const pkgPath = path70.join(skillDir, "package.json");
|
|
13529
|
+
fs46.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
13530
|
+
const readmePath = path70.join(skillDir, "README.md");
|
|
13531
|
+
if (!fs46.existsSync(readmePath)) {
|
|
13532
|
+
const skillMdContent = fs46.readFileSync(path70.join(skillDir, "SKILL.md"), "utf-8");
|
|
13317
13533
|
const readme = `# ${pkg.name}
|
|
13318
13534
|
|
|
13319
13535
|
${meta.description}
|
|
@@ -13327,7 +13543,7 @@ harness install ${meta.name}
|
|
|
13327
13543
|
---
|
|
13328
13544
|
|
|
13329
13545
|
${skillMdContent}`;
|
|
13330
|
-
|
|
13546
|
+
fs46.writeFileSync(readmePath, readme);
|
|
13331
13547
|
}
|
|
13332
13548
|
if (opts.dryRun) {
|
|
13333
13549
|
return {
|
|
@@ -13631,10 +13847,10 @@ function printResult6(result, mode, _formatter) {
|
|
|
13631
13847
|
list.push(f);
|
|
13632
13848
|
byFile.set(f.target.file, list);
|
|
13633
13849
|
}
|
|
13634
|
-
for (const [file,
|
|
13850
|
+
for (const [file, fs51] of byFile) {
|
|
13635
13851
|
console.log(`
|
|
13636
13852
|
${file}`);
|
|
13637
|
-
for (const f of
|
|
13853
|
+
for (const f of fs51) {
|
|
13638
13854
|
console.log(
|
|
13639
13855
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ## ${f.target.section}:${f.target.line}`
|
|
13640
13856
|
);
|
|
@@ -13760,7 +13976,7 @@ import { Command as Command114 } from "commander";
|
|
|
13760
13976
|
|
|
13761
13977
|
// src/commands/state/show.ts
|
|
13762
13978
|
import { Command as Command110 } from "commander";
|
|
13763
|
-
import * as
|
|
13979
|
+
import * as path71 from "path";
|
|
13764
13980
|
function printStateText(state, stream) {
|
|
13765
13981
|
if (stream) console.log(`Stream: ${stream}`);
|
|
13766
13982
|
console.log(`Schema Version: ${state.schemaVersion}`);
|
|
@@ -13786,7 +14002,7 @@ function printStateProgress(progress) {
|
|
|
13786
14002
|
function createShowCommand() {
|
|
13787
14003
|
return new Command110("show").description("Show current project state").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(async (opts, cmd) => {
|
|
13788
14004
|
const globalOpts = cmd.optsWithGlobals();
|
|
13789
|
-
const result = await readHarnessState(
|
|
14005
|
+
const result = await readHarnessState(path71.resolve(opts.path), { stream: opts.stream });
|
|
13790
14006
|
if (!result.ok) {
|
|
13791
14007
|
logger.error(result.error.message);
|
|
13792
14008
|
process.exit(ExitCode.ERROR);
|
|
@@ -13803,18 +14019,18 @@ function createShowCommand() {
|
|
|
13803
14019
|
// src/commands/state/reset.ts
|
|
13804
14020
|
init_dist3();
|
|
13805
14021
|
import { Command as Command111 } from "commander";
|
|
13806
|
-
import * as
|
|
14022
|
+
import * as path72 from "path";
|
|
13807
14023
|
import * as readline3 from "readline";
|
|
13808
14024
|
async function confirmReset() {
|
|
13809
14025
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
13810
|
-
const answer = await new Promise((
|
|
13811
|
-
rl.question("Reset project state? This cannot be undone. [y/N] ",
|
|
14026
|
+
const answer = await new Promise((resolve50) => {
|
|
14027
|
+
rl.question("Reset project state? This cannot be undone. [y/N] ", resolve50);
|
|
13812
14028
|
});
|
|
13813
14029
|
rl.close();
|
|
13814
14030
|
return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
|
|
13815
14031
|
}
|
|
13816
14032
|
async function runReset(opts) {
|
|
13817
|
-
const projectPath =
|
|
14033
|
+
const projectPath = path72.resolve(opts.path);
|
|
13818
14034
|
if (!opts.yes && !await confirmReset()) {
|
|
13819
14035
|
logger.info("Reset cancelled.");
|
|
13820
14036
|
process.exit(ExitCode.SUCCESS);
|
|
@@ -13834,10 +14050,10 @@ function createResetCommand() {
|
|
|
13834
14050
|
// src/commands/state/learn.ts
|
|
13835
14051
|
init_dist3();
|
|
13836
14052
|
import { Command as Command112 } from "commander";
|
|
13837
|
-
import * as
|
|
14053
|
+
import * as path73 from "path";
|
|
13838
14054
|
function createLearnCommand() {
|
|
13839
14055
|
return new Command112("learn").description("Append a learning to .harness/learnings.md").argument("<message>", "The learning to record").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(async (message, opts, _cmd) => {
|
|
13840
|
-
const projectPath =
|
|
14056
|
+
const projectPath = path73.resolve(opts.path);
|
|
13841
14057
|
const result = await appendLearning(projectPath, message, void 0, void 0, opts.stream);
|
|
13842
14058
|
if (!result.ok) {
|
|
13843
14059
|
logger.error(result.error.message);
|
|
@@ -13852,9 +14068,9 @@ function createLearnCommand() {
|
|
|
13852
14068
|
// src/commands/state/streams.ts
|
|
13853
14069
|
init_dist3();
|
|
13854
14070
|
import { Command as Command113 } from "commander";
|
|
13855
|
-
import * as
|
|
14071
|
+
import * as path74 from "path";
|
|
13856
14072
|
async function runListStreams(opts, globalOpts) {
|
|
13857
|
-
const projectPath =
|
|
14073
|
+
const projectPath = path74.resolve(opts.path);
|
|
13858
14074
|
const indexResult = await loadStreamIndex(projectPath);
|
|
13859
14075
|
const result = await listStreams(projectPath);
|
|
13860
14076
|
if (!result.ok) {
|
|
@@ -13875,7 +14091,7 @@ async function runListStreams(opts, globalOpts) {
|
|
|
13875
14091
|
process.exit(ExitCode.SUCCESS);
|
|
13876
14092
|
}
|
|
13877
14093
|
async function runCreateStream(name, opts) {
|
|
13878
|
-
const result = await createStream(
|
|
14094
|
+
const result = await createStream(path74.resolve(opts.path), name, opts.branch);
|
|
13879
14095
|
if (!result.ok) {
|
|
13880
14096
|
logger.error(result.error.message);
|
|
13881
14097
|
process.exit(ExitCode.ERROR);
|
|
@@ -13884,7 +14100,7 @@ async function runCreateStream(name, opts) {
|
|
|
13884
14100
|
process.exit(ExitCode.SUCCESS);
|
|
13885
14101
|
}
|
|
13886
14102
|
async function runArchiveStream(name, opts) {
|
|
13887
|
-
const result = await archiveStream(
|
|
14103
|
+
const result = await archiveStream(path74.resolve(opts.path), name);
|
|
13888
14104
|
if (!result.ok) {
|
|
13889
14105
|
logger.error(result.error.message);
|
|
13890
14106
|
process.exit(ExitCode.ERROR);
|
|
@@ -13893,7 +14109,7 @@ async function runArchiveStream(name, opts) {
|
|
|
13893
14109
|
process.exit(ExitCode.SUCCESS);
|
|
13894
14110
|
}
|
|
13895
14111
|
async function runActivateStream(name, opts) {
|
|
13896
|
-
const result = await setActiveStream(
|
|
14112
|
+
const result = await setActiveStream(path74.resolve(opts.path), name);
|
|
13897
14113
|
if (!result.ok) {
|
|
13898
14114
|
logger.error(result.error.message);
|
|
13899
14115
|
process.exit(ExitCode.ERROR);
|
|
@@ -13921,8 +14137,8 @@ function createStateCommand() {
|
|
|
13921
14137
|
}
|
|
13922
14138
|
|
|
13923
14139
|
// src/commands/sync-analyses.ts
|
|
13924
|
-
import * as
|
|
13925
|
-
import * as
|
|
14140
|
+
import * as fs47 from "fs";
|
|
14141
|
+
import * as path75 from "path";
|
|
13926
14142
|
import { Command as Command115 } from "commander";
|
|
13927
14143
|
init_dist3();
|
|
13928
14144
|
function isValidRecord(record) {
|
|
@@ -13949,7 +14165,7 @@ function extractAnalysisFromComments(comments) {
|
|
|
13949
14165
|
return null;
|
|
13950
14166
|
}
|
|
13951
14167
|
function bootstrapTrackerCommand2(opts, verb) {
|
|
13952
|
-
const projectPath =
|
|
14168
|
+
const projectPath = path75.resolve(opts.dir);
|
|
13953
14169
|
const trackerConfig = loadTrackerSyncConfig(projectPath);
|
|
13954
14170
|
if (!trackerConfig) {
|
|
13955
14171
|
logger.error(`No tracker config found in harness.config.json. Cannot ${verb}.`);
|
|
@@ -13961,8 +14177,8 @@ function bootstrapTrackerCommand2(opts, verb) {
|
|
|
13961
14177
|
);
|
|
13962
14178
|
return null;
|
|
13963
14179
|
}
|
|
13964
|
-
const projectEnvPath =
|
|
13965
|
-
if (
|
|
14180
|
+
const projectEnvPath = path75.join(projectPath, ".env");
|
|
14181
|
+
if (fs47.existsSync(projectEnvPath) && !process.env.GITHUB_TOKEN) {
|
|
13966
14182
|
__require("dotenv").config({ path: projectEnvPath });
|
|
13967
14183
|
}
|
|
13968
14184
|
const token = process.env.GITHUB_TOKEN;
|
|
@@ -14048,9 +14264,9 @@ async function runSyncAnalyses(opts) {
|
|
|
14048
14264
|
return;
|
|
14049
14265
|
}
|
|
14050
14266
|
const { AnalysisArchive, loadPublishedIndex: loadPublishedIndex2, savePublishedIndex: savePublishedIndex2 } = await import("@harness-engineering/orchestrator");
|
|
14051
|
-
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-
|
|
14267
|
+
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-BWFFVKEW.js");
|
|
14052
14268
|
const adapter = new GitHubIssuesSyncAdapter2({ token, config: trackerConfig });
|
|
14053
|
-
const archive = new AnalysisArchive(
|
|
14269
|
+
const archive = new AnalysisArchive(path75.join(projectPath, ".harness", "analyses"));
|
|
14054
14270
|
const publishedIndex = loadPublishedIndex2(projectPath);
|
|
14055
14271
|
const { syncedCount, skippedCount, warnCount } = await syncFeatureAnalyses(
|
|
14056
14272
|
features,
|
|
@@ -14085,7 +14301,7 @@ function createSyncAnalysesCommand() {
|
|
|
14085
14301
|
|
|
14086
14302
|
// src/commands/sync-main.ts
|
|
14087
14303
|
import { Command as Command116 } from "commander";
|
|
14088
|
-
import * as
|
|
14304
|
+
import * as path76 from "path";
|
|
14089
14305
|
import {
|
|
14090
14306
|
syncMain as defaultSyncMain
|
|
14091
14307
|
} from "@harness-engineering/orchestrator";
|
|
@@ -14116,7 +14332,7 @@ function formatHuman2(r) {
|
|
|
14116
14332
|
}
|
|
14117
14333
|
function createSyncMainCommand() {
|
|
14118
14334
|
return new Command116("sync-main").description("Fast-forward the local default branch from origin (no-op on conflict)").option("--json", "Emit a SyncMainResult JSON object", false).option("--path <path>", "Project root path", ".").action(async (opts) => {
|
|
14119
|
-
const cwd =
|
|
14335
|
+
const cwd = path76.resolve(opts.path ?? ".");
|
|
14120
14336
|
const exitCode = await runSyncMain({ cwd, json: Boolean(opts.json) });
|
|
14121
14337
|
process.exit(exitCode);
|
|
14122
14338
|
});
|
|
@@ -14132,8 +14348,8 @@ function registerClearCommand(taint) {
|
|
|
14132
14348
|
taint.command("clear [sessionId]").description(
|
|
14133
14349
|
"Clear session taint \u2014 removes taint file(s) and re-enables destructive operations"
|
|
14134
14350
|
).action((sessionId) => {
|
|
14135
|
-
const
|
|
14136
|
-
const count = clearTaint(
|
|
14351
|
+
const projectRoot3 = getProjectRoot();
|
|
14352
|
+
const count = clearTaint(projectRoot3, sessionId);
|
|
14137
14353
|
if (count === 0) {
|
|
14138
14354
|
if (sessionId) {
|
|
14139
14355
|
logger.info(`No taint found for session "${sessionId}".`);
|
|
@@ -14151,8 +14367,8 @@ function registerClearCommand(taint) {
|
|
|
14151
14367
|
}
|
|
14152
14368
|
});
|
|
14153
14369
|
}
|
|
14154
|
-
function printSingleSessionStatus(
|
|
14155
|
-
const result = checkTaint(
|
|
14370
|
+
function printSingleSessionStatus(projectRoot3, sessionId) {
|
|
14371
|
+
const result = checkTaint(projectRoot3, sessionId);
|
|
14156
14372
|
if (result.expired) {
|
|
14157
14373
|
logger.info(`Session "${sessionId}": taint expired (cleared).`);
|
|
14158
14374
|
} else if (result.tainted && result.state) {
|
|
@@ -14167,15 +14383,15 @@ function printSingleSessionStatus(projectRoot2, sessionId) {
|
|
|
14167
14383
|
logger.info(`Session "${sessionId}": clean (no taint).`);
|
|
14168
14384
|
}
|
|
14169
14385
|
}
|
|
14170
|
-
function printAllSessionsStatus(
|
|
14171
|
-
const sessions = listTaintedSessions(
|
|
14386
|
+
function printAllSessionsStatus(projectRoot3) {
|
|
14387
|
+
const sessions = listTaintedSessions(projectRoot3);
|
|
14172
14388
|
if (sessions.length === 0) {
|
|
14173
14389
|
logger.info("No active taint sessions.");
|
|
14174
14390
|
return;
|
|
14175
14391
|
}
|
|
14176
14392
|
logger.info(`Active taint sessions (${sessions.length}):`);
|
|
14177
14393
|
for (const id of sessions) {
|
|
14178
|
-
const result = checkTaint(
|
|
14394
|
+
const result = checkTaint(projectRoot3, id);
|
|
14179
14395
|
if (result.tainted && result.state) {
|
|
14180
14396
|
const remaining = Math.max(
|
|
14181
14397
|
0,
|
|
@@ -14187,11 +14403,11 @@ function printAllSessionsStatus(projectRoot2) {
|
|
|
14187
14403
|
}
|
|
14188
14404
|
function registerStatusCommand(taint) {
|
|
14189
14405
|
taint.command("status [sessionId]").description("Show current taint status for a session or all sessions").action((sessionId) => {
|
|
14190
|
-
const
|
|
14406
|
+
const projectRoot3 = getProjectRoot();
|
|
14191
14407
|
if (sessionId) {
|
|
14192
|
-
printSingleSessionStatus(
|
|
14408
|
+
printSingleSessionStatus(projectRoot3, sessionId);
|
|
14193
14409
|
} else {
|
|
14194
|
-
printAllSessionsStatus(
|
|
14410
|
+
printAllSessionsStatus(projectRoot3);
|
|
14195
14411
|
}
|
|
14196
14412
|
});
|
|
14197
14413
|
}
|
|
@@ -14208,16 +14424,16 @@ import { Command as Command121 } from "commander";
|
|
|
14208
14424
|
// src/commands/telemetry/identify.ts
|
|
14209
14425
|
init_dist3();
|
|
14210
14426
|
import { Command as Command118 } from "commander";
|
|
14211
|
-
import * as
|
|
14212
|
-
import * as
|
|
14427
|
+
import * as fs48 from "fs";
|
|
14428
|
+
import * as path77 from "path";
|
|
14213
14429
|
function telemetryFilePath(cwd) {
|
|
14214
|
-
return
|
|
14430
|
+
return path77.join(cwd, ".harness", "telemetry.json");
|
|
14215
14431
|
}
|
|
14216
14432
|
function writeTelemetryFile(cwd, data) {
|
|
14217
14433
|
const filePath = telemetryFilePath(cwd);
|
|
14218
|
-
const dir =
|
|
14219
|
-
|
|
14220
|
-
|
|
14434
|
+
const dir = path77.dirname(filePath);
|
|
14435
|
+
fs48.mkdirSync(dir, { recursive: true });
|
|
14436
|
+
fs48.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
14221
14437
|
}
|
|
14222
14438
|
var IDENTITY_FIELDS = ["project", "team", "alias"];
|
|
14223
14439
|
function applyIdentityFields(opts, identity) {
|
|
@@ -14334,17 +14550,17 @@ init_dist3();
|
|
|
14334
14550
|
import { Command as Command120 } from "commander";
|
|
14335
14551
|
|
|
14336
14552
|
// src/bin/command-telemetry.ts
|
|
14337
|
-
import { existsSync as
|
|
14338
|
-
import { join as
|
|
14553
|
+
import { existsSync as existsSync44, readFileSync as readFileSync32, mkdirSync as mkdirSync23, appendFileSync, writeFileSync as writeFileSync29 } from "fs";
|
|
14554
|
+
import { join as join64, parse as parsePath, resolve as resolve47 } from "path";
|
|
14339
14555
|
import { spawn as spawn2 } from "child_process";
|
|
14340
14556
|
var POSTHOG_API_KEY = "phc_wNTdCMcfJXZPgdNeDociZW6vwoGGo4nb7vqEfWThFfsG";
|
|
14341
14557
|
var EXCLUDED_COMMANDS = /* @__PURE__ */ new Set(["help", "completion"]);
|
|
14342
14558
|
function findProjectRoot(cwd) {
|
|
14343
|
-
let dir =
|
|
14559
|
+
let dir = resolve47(cwd);
|
|
14344
14560
|
const { root } = parsePath(dir);
|
|
14345
14561
|
while (dir !== root) {
|
|
14346
|
-
if (
|
|
14347
|
-
dir =
|
|
14562
|
+
if (existsSync44(join64(dir, "harness.config.json"))) return dir;
|
|
14563
|
+
dir = resolve47(dir, "..");
|
|
14348
14564
|
}
|
|
14349
14565
|
return cwd;
|
|
14350
14566
|
}
|
|
@@ -14353,8 +14569,8 @@ var startTime = 0;
|
|
|
14353
14569
|
var recorded = false;
|
|
14354
14570
|
function installCommandTelemetry(program, cwd) {
|
|
14355
14571
|
if (typeof program.hook !== "function") return;
|
|
14356
|
-
const
|
|
14357
|
-
flushTelemetryBackground(
|
|
14572
|
+
const projectRoot3 = findProjectRoot(cwd);
|
|
14573
|
+
flushTelemetryBackground(projectRoot3);
|
|
14358
14574
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
14359
14575
|
commandName = resolveCommandName(actionCommand);
|
|
14360
14576
|
startTime = Date.now();
|
|
@@ -14364,7 +14580,7 @@ function installCommandTelemetry(program, cwd) {
|
|
|
14364
14580
|
recorded = true;
|
|
14365
14581
|
const duration = Date.now() - startTime;
|
|
14366
14582
|
const outcome = code === 0 ? "completed" : "failed";
|
|
14367
|
-
writeCommandRecordSync(
|
|
14583
|
+
writeCommandRecordSync(projectRoot3, commandName, duration, outcome);
|
|
14368
14584
|
});
|
|
14369
14585
|
}
|
|
14370
14586
|
function resolveCommandName(cmd) {
|
|
@@ -14381,8 +14597,8 @@ function resolveCommandName(cmd) {
|
|
|
14381
14597
|
}
|
|
14382
14598
|
function writeCommandRecordSync(cwd, command, duration, outcome) {
|
|
14383
14599
|
try {
|
|
14384
|
-
const metricsDir =
|
|
14385
|
-
|
|
14600
|
+
const metricsDir = join64(cwd, ".harness", "metrics");
|
|
14601
|
+
mkdirSync23(metricsDir, { recursive: true });
|
|
14386
14602
|
const record = {
|
|
14387
14603
|
skill: command,
|
|
14388
14604
|
session: `cli-${Date.now()}`,
|
|
@@ -14391,14 +14607,14 @@ function writeCommandRecordSync(cwd, command, duration, outcome) {
|
|
|
14391
14607
|
outcome,
|
|
14392
14608
|
phasesReached: []
|
|
14393
14609
|
};
|
|
14394
|
-
const adoptionFile =
|
|
14610
|
+
const adoptionFile = join64(metricsDir, "adoption.jsonl");
|
|
14395
14611
|
appendFileSync(adoptionFile, JSON.stringify(record) + "\n");
|
|
14396
14612
|
} catch {
|
|
14397
14613
|
}
|
|
14398
14614
|
}
|
|
14399
14615
|
function isTelemetryDisabledByConfig(cwd) {
|
|
14400
|
-
const configPath =
|
|
14401
|
-
if (!
|
|
14616
|
+
const configPath = join64(cwd, "harness.config.json");
|
|
14617
|
+
if (!existsSync44(configPath)) return false;
|
|
14402
14618
|
try {
|
|
14403
14619
|
const config = JSON.parse(readFileSync32(configPath, "utf-8"));
|
|
14404
14620
|
return config?.telemetry?.enabled === false;
|
|
@@ -14412,11 +14628,11 @@ function isTelemetryOptedOut(cwd) {
|
|
|
14412
14628
|
}
|
|
14413
14629
|
function flushTelemetryBackground(cwd) {
|
|
14414
14630
|
try {
|
|
14415
|
-
const adoptionFile =
|
|
14416
|
-
if (!
|
|
14631
|
+
const adoptionFile = join64(cwd, ".harness", "metrics", "adoption.jsonl");
|
|
14632
|
+
if (!existsSync44(adoptionFile)) return;
|
|
14417
14633
|
if (isTelemetryOptedOut(cwd)) return;
|
|
14418
|
-
const reporterPath =
|
|
14419
|
-
if (!
|
|
14634
|
+
const reporterPath = join64(cwd, ".harness", "hooks", "telemetry-reporter.js");
|
|
14635
|
+
if (!existsSync44(reporterPath)) return;
|
|
14420
14636
|
const child = spawn2(process.execPath, [reporterPath], {
|
|
14421
14637
|
cwd,
|
|
14422
14638
|
stdio: ["pipe", "ignore", "ignore"],
|
|
@@ -14548,10 +14764,10 @@ function printResult7(result, mode, _formatter) {
|
|
|
14548
14764
|
list.push(f);
|
|
14549
14765
|
byFile.set(f.target.file, list);
|
|
14550
14766
|
}
|
|
14551
|
-
for (const [file,
|
|
14767
|
+
for (const [file, fs51] of byFile) {
|
|
14552
14768
|
console.log(`
|
|
14553
14769
|
${file}`);
|
|
14554
|
-
for (const f of
|
|
14770
|
+
for (const f of fs51) {
|
|
14555
14771
|
const nestingStr = f.target.nesting.length > 0 ? f.target.nesting.join(" > ") + " > " : "";
|
|
14556
14772
|
console.log(
|
|
14557
14773
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.framework}:${f.target.line}`
|
|
@@ -14723,15 +14939,15 @@ function createTraceabilityCommand() {
|
|
|
14723
14939
|
}
|
|
14724
14940
|
|
|
14725
14941
|
// src/commands/uninstall.ts
|
|
14726
|
-
import * as
|
|
14942
|
+
import * as path78 from "path";
|
|
14727
14943
|
import { Command as Command124 } from "commander";
|
|
14728
14944
|
async function runUninstall(skillName, options) {
|
|
14729
14945
|
const packageName = resolvePackageName(skillName);
|
|
14730
14946
|
const shortName = extractSkillName(packageName);
|
|
14731
14947
|
const globalDir = resolveGlobalSkillsDir();
|
|
14732
|
-
const skillsDir =
|
|
14733
|
-
const communityBase =
|
|
14734
|
-
const lockfilePath =
|
|
14948
|
+
const skillsDir = path78.dirname(globalDir);
|
|
14949
|
+
const communityBase = path78.join(skillsDir, "community");
|
|
14950
|
+
const lockfilePath = path78.join(communityBase, "skills-lock.json");
|
|
14735
14951
|
const lockfile = readLockfile2(lockfilePath);
|
|
14736
14952
|
const entry = lockfile.skills[packageName];
|
|
14737
14953
|
if (!entry) {
|
|
@@ -14781,8 +14997,8 @@ function createUninstallCommand() {
|
|
|
14781
14997
|
|
|
14782
14998
|
// src/commands/uninstall-constraints.ts
|
|
14783
14999
|
init_dist3();
|
|
14784
|
-
import * as
|
|
14785
|
-
import * as
|
|
15000
|
+
import * as fs49 from "fs/promises";
|
|
15001
|
+
import * as path79 from "path";
|
|
14786
15002
|
import { Command as Command125 } from "commander";
|
|
14787
15003
|
async function runUninstallConstraints(options) {
|
|
14788
15004
|
const { packageName, configPath, lockfilePath } = options;
|
|
@@ -14803,7 +15019,7 @@ async function runUninstallConstraints(options) {
|
|
|
14803
15019
|
}
|
|
14804
15020
|
let localConfig;
|
|
14805
15021
|
try {
|
|
14806
|
-
const raw = await
|
|
15022
|
+
const raw = await fs49.readFile(configPath, "utf-8");
|
|
14807
15023
|
localConfig = JSON.parse(raw);
|
|
14808
15024
|
} catch (err) {
|
|
14809
15025
|
return {
|
|
@@ -14844,7 +15060,7 @@ function createUninstallConstraintsCommand() {
|
|
|
14844
15060
|
cmd.description("Remove a previously installed constraints package").argument("<name>", "Name of the constraint package to uninstall").option("-c, --config <path>", "Path to harness.config.json").action(async (name, opts) => {
|
|
14845
15061
|
let configPath;
|
|
14846
15062
|
if (opts.config) {
|
|
14847
|
-
configPath =
|
|
15063
|
+
configPath = path79.resolve(opts.config);
|
|
14848
15064
|
} else {
|
|
14849
15065
|
const found = findConfigFile();
|
|
14850
15066
|
if (!found.ok) {
|
|
@@ -14853,8 +15069,8 @@ function createUninstallConstraintsCommand() {
|
|
|
14853
15069
|
}
|
|
14854
15070
|
configPath = found.value;
|
|
14855
15071
|
}
|
|
14856
|
-
const
|
|
14857
|
-
const lockfilePath =
|
|
15072
|
+
const projectRoot3 = path79.dirname(configPath);
|
|
15073
|
+
const lockfilePath = path79.join(projectRoot3, ".harness", "constraints.lock.json");
|
|
14858
15074
|
const result = await runUninstallConstraints({
|
|
14859
15075
|
packageName: name,
|
|
14860
15076
|
configPath,
|
|
@@ -14882,8 +15098,8 @@ function createUninstallConstraintsCommand() {
|
|
|
14882
15098
|
init_dist3();
|
|
14883
15099
|
import { Command as Command126 } from "commander";
|
|
14884
15100
|
import { execFile as execFile2, execFileSync as execFileSync10 } from "child_process";
|
|
14885
|
-
import { realpathSync, existsSync as
|
|
14886
|
-
import { dirname as
|
|
15101
|
+
import { realpathSync, existsSync as existsSync45, readFileSync as readFileSync33 } from "fs";
|
|
15102
|
+
import { dirname as dirname25, join as join67 } from "path";
|
|
14887
15103
|
import { promisify as promisify2 } from "util";
|
|
14888
15104
|
import readline4 from "readline";
|
|
14889
15105
|
import chalk20 from "chalk";
|
|
@@ -14964,24 +15180,24 @@ function prompt3(question) {
|
|
|
14964
15180
|
input: process.stdin,
|
|
14965
15181
|
output: process.stdout
|
|
14966
15182
|
});
|
|
14967
|
-
return new Promise((
|
|
15183
|
+
return new Promise((resolve50) => {
|
|
14968
15184
|
rl.question(question, (answer) => {
|
|
14969
15185
|
rl.close();
|
|
14970
|
-
|
|
15186
|
+
resolve50(answer.trim().toLowerCase());
|
|
14971
15187
|
});
|
|
14972
15188
|
});
|
|
14973
15189
|
}
|
|
14974
15190
|
function findPackageDir(resolvedBin) {
|
|
14975
|
-
let dir =
|
|
15191
|
+
let dir = dirname25(resolvedBin);
|
|
14976
15192
|
for (let i = 0; i < 6; i++) {
|
|
14977
|
-
if (
|
|
15193
|
+
if (existsSync45(join67(dir, "package.json"))) {
|
|
14978
15194
|
try {
|
|
14979
|
-
const pkg = JSON.parse(readFileSync33(
|
|
15195
|
+
const pkg = JSON.parse(readFileSync33(join67(dir, "package.json"), "utf-8"));
|
|
14980
15196
|
if (pkg?.name === CLI_PACKAGE) return dir;
|
|
14981
15197
|
} catch {
|
|
14982
15198
|
}
|
|
14983
15199
|
}
|
|
14984
|
-
const parent =
|
|
15200
|
+
const parent = dirname25(dir);
|
|
14985
15201
|
if (parent === dir) return null;
|
|
14986
15202
|
dir = parent;
|
|
14987
15203
|
}
|
|
@@ -15021,7 +15237,7 @@ function findAllInstalls() {
|
|
|
15021
15237
|
if (!packageDir) continue;
|
|
15022
15238
|
let version = null;
|
|
15023
15239
|
try {
|
|
15024
|
-
const pkg = JSON.parse(readFileSync33(
|
|
15240
|
+
const pkg = JSON.parse(readFileSync33(join67(packageDir, "package.json"), "utf-8"));
|
|
15025
15241
|
if (typeof pkg?.version === "string") version = pkg.version;
|
|
15026
15242
|
} catch {
|
|
15027
15243
|
}
|
|
@@ -15114,10 +15330,10 @@ async function ensureTelemetryIfNeeded() {
|
|
|
15114
15330
|
}
|
|
15115
15331
|
function refreshHooks() {
|
|
15116
15332
|
const cwd = process.cwd();
|
|
15117
|
-
const configPath =
|
|
15118
|
-
if (!
|
|
15333
|
+
const configPath = join67(cwd, "harness.config.json");
|
|
15334
|
+
if (!existsSync45(configPath)) return;
|
|
15119
15335
|
let profile = "standard";
|
|
15120
|
-
const profilePath =
|
|
15336
|
+
const profilePath = join67(cwd, ".harness", "hooks", "profile.json");
|
|
15121
15337
|
try {
|
|
15122
15338
|
const data = JSON.parse(readFileSync33(profilePath, "utf-8"));
|
|
15123
15339
|
if (data.profile && ["minimal", "standard", "strict"].includes(data.profile)) {
|
|
@@ -15260,7 +15476,7 @@ function createUpdateCommand() {
|
|
|
15260
15476
|
// src/commands/usage.ts
|
|
15261
15477
|
import { Command as Command127 } from "commander";
|
|
15262
15478
|
async function loadAndPriceRecords(cwd, includeClaudeSessions = false) {
|
|
15263
|
-
const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-
|
|
15479
|
+
const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-BWFFVKEW.js");
|
|
15264
15480
|
const records = readCostRecords(cwd);
|
|
15265
15481
|
if (includeClaudeSessions) {
|
|
15266
15482
|
const ccRecords = parseCCRecords();
|
|
@@ -15353,7 +15569,7 @@ function registerDailyCommand(usage) {
|
|
|
15353
15569
|
else logger.info("No usage data found. Run some harness sessions first.");
|
|
15354
15570
|
return;
|
|
15355
15571
|
}
|
|
15356
|
-
const { aggregateByDay } = await import("./dist-
|
|
15572
|
+
const { aggregateByDay } = await import("./dist-BWFFVKEW.js");
|
|
15357
15573
|
const limited = aggregateByDay(records).slice(0, days);
|
|
15358
15574
|
if (globalOpts.json) {
|
|
15359
15575
|
console.log(
|
|
@@ -15382,7 +15598,7 @@ function registerSessionsCommand(usage) {
|
|
|
15382
15598
|
}
|
|
15383
15599
|
return;
|
|
15384
15600
|
}
|
|
15385
|
-
const { aggregateBySession } = await import("./dist-
|
|
15601
|
+
const { aggregateBySession } = await import("./dist-BWFFVKEW.js");
|
|
15386
15602
|
const sessionData = aggregateBySession(records);
|
|
15387
15603
|
const limited = sessionData.slice(0, limit);
|
|
15388
15604
|
if (globalOpts.json) {
|
|
@@ -15462,7 +15678,7 @@ function registerSessionCommand(usage) {
|
|
|
15462
15678
|
cwd,
|
|
15463
15679
|
globalOpts.includeClaudeSessions
|
|
15464
15680
|
);
|
|
15465
|
-
const { aggregateBySession } = await import("./dist-
|
|
15681
|
+
const { aggregateBySession } = await import("./dist-BWFFVKEW.js");
|
|
15466
15682
|
const sessionData = aggregateBySession(records);
|
|
15467
15683
|
const match = sessionData.find((s) => s.sessionId === id);
|
|
15468
15684
|
if (!match) {
|
|
@@ -15497,7 +15713,7 @@ function registerLatestCommand(usage) {
|
|
|
15497
15713
|
}
|
|
15498
15714
|
return;
|
|
15499
15715
|
}
|
|
15500
|
-
const { aggregateBySession } = await import("./dist-
|
|
15716
|
+
const { aggregateBySession } = await import("./dist-BWFFVKEW.js");
|
|
15501
15717
|
const sessionData = aggregateBySession(records);
|
|
15502
15718
|
const latest = sessionData[0];
|
|
15503
15719
|
if (!latest) {
|
|
@@ -15539,8 +15755,8 @@ function createUsageCommand() {
|
|
|
15539
15755
|
init_dist3();
|
|
15540
15756
|
init_dist3();
|
|
15541
15757
|
import { Command as Command128 } from "commander";
|
|
15542
|
-
import * as
|
|
15543
|
-
import * as
|
|
15758
|
+
import * as fs50 from "fs";
|
|
15759
|
+
import * as path80 from "path";
|
|
15544
15760
|
import { execFileSync as execFileSync11 } from "child_process";
|
|
15545
15761
|
async function runValidate(options) {
|
|
15546
15762
|
const configResult = resolveConfig(options.configPath);
|
|
@@ -15548,7 +15764,7 @@ async function runValidate(options) {
|
|
|
15548
15764
|
return configResult;
|
|
15549
15765
|
}
|
|
15550
15766
|
const config = configResult.value;
|
|
15551
|
-
const cwd = options.cwd ?? (options.configPath ?
|
|
15767
|
+
const cwd = options.cwd ?? (options.configPath ? path80.dirname(path80.resolve(options.configPath)) : process.cwd());
|
|
15552
15768
|
const result = {
|
|
15553
15769
|
valid: true,
|
|
15554
15770
|
checks: {
|
|
@@ -15558,7 +15774,7 @@ async function runValidate(options) {
|
|
|
15558
15774
|
},
|
|
15559
15775
|
issues: []
|
|
15560
15776
|
};
|
|
15561
|
-
const agentsMapPath =
|
|
15777
|
+
const agentsMapPath = path80.resolve(cwd, config.agentsMapPath);
|
|
15562
15778
|
const agentsResult = await validateAgentsMap(agentsMapPath);
|
|
15563
15779
|
if (agentsResult.ok) {
|
|
15564
15780
|
result.checks.agentsMap = true;
|
|
@@ -15655,9 +15871,9 @@ async function runValidate(options) {
|
|
|
15655
15871
|
}
|
|
15656
15872
|
});
|
|
15657
15873
|
}
|
|
15658
|
-
const gitattributesPath =
|
|
15659
|
-
if (
|
|
15660
|
-
const gitattributesContent =
|
|
15874
|
+
const gitattributesPath = path80.join(cwd, ".gitattributes");
|
|
15875
|
+
if (fs50.existsSync(gitattributesPath)) {
|
|
15876
|
+
const gitattributesContent = fs50.readFileSync(gitattributesPath, "utf-8");
|
|
15661
15877
|
let driverConfigured = false;
|
|
15662
15878
|
try {
|
|
15663
15879
|
const out = execFileSync11("git", ["config", "--get", "merge.ours.driver"], {
|
|
@@ -15681,10 +15897,10 @@ async function runValidate(options) {
|
|
|
15681
15897
|
}
|
|
15682
15898
|
}
|
|
15683
15899
|
if (detectRoadmapStorageMode(cwd) === "sharded") {
|
|
15684
|
-
const shardDir =
|
|
15900
|
+
const shardDir = path80.join(cwd, "docs", "roadmap.d");
|
|
15685
15901
|
const regenerated = await regenerate(shardDir, createNodeRoadmapIO());
|
|
15686
|
-
const aggregatePath =
|
|
15687
|
-
const committedAggregate =
|
|
15902
|
+
const aggregatePath = path80.join(cwd, "docs", "roadmap.md");
|
|
15903
|
+
const committedAggregate = fs50.existsSync(aggregatePath) ? fs50.readFileSync(aggregatePath, "utf-8") : null;
|
|
15688
15904
|
const drift = checkRoadmapAggregateDrift({
|
|
15689
15905
|
shardDirExists: true,
|
|
15690
15906
|
committedAggregate,
|
|
@@ -15702,9 +15918,9 @@ async function runValidate(options) {
|
|
|
15702
15918
|
result.checks.roadmapAggregateDrift = true;
|
|
15703
15919
|
}
|
|
15704
15920
|
}
|
|
15705
|
-
const roadmapPath =
|
|
15706
|
-
if (
|
|
15707
|
-
const parsed = parseRoadmap(
|
|
15921
|
+
const roadmapPath = path80.join(cwd, "docs", "roadmap.md");
|
|
15922
|
+
if (fs50.existsSync(roadmapPath)) {
|
|
15923
|
+
const parsed = parseRoadmap(fs50.readFileSync(roadmapPath, "utf-8"));
|
|
15708
15924
|
if (parsed.ok) {
|
|
15709
15925
|
const findings = checkRoadmapHealth(parsed.value);
|
|
15710
15926
|
result.checks.roadmapHealth = !findings.some((f) => f.severity === "error");
|
|
@@ -15846,11 +16062,11 @@ function resolveValidateMode(globalOpts) {
|
|
|
15846
16062
|
return OutputMode.TEXT;
|
|
15847
16063
|
}
|
|
15848
16064
|
async function printCrossCheckWarnings(mode) {
|
|
15849
|
-
const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-
|
|
16065
|
+
const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-H2OUHTID.js");
|
|
15850
16066
|
const cwd = process.cwd();
|
|
15851
16067
|
const crossResult = await runCrossCheck2({
|
|
15852
|
-
specsDir:
|
|
15853
|
-
plansDir:
|
|
16068
|
+
specsDir: path80.join(cwd, "docs", "specs"),
|
|
16069
|
+
plansDir: path80.join(cwd, "docs", "plans"),
|
|
15854
16070
|
projectPath: cwd
|
|
15855
16071
|
});
|
|
15856
16072
|
if (!crossResult.ok || crossResult.value.warnings === 0) return;
|