@harness-engineering/cli 4.1.0 → 4.2.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-BK2UPKUC.js} +4 -4
- package/dist/{analyzer-XRCV63KC-HLTEIKAJ.js → analyzer-XRCV63KC-6T2Z32XC.js} +2 -2
- package/dist/{architecture-RCHFDI7E.js → architecture-7PNQZOWV.js} +5 -5
- package/dist/{assess-project-YUDPNRS6.js → assess-project-2QPCNN7N.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-RZUSDB3O.js} +5 -5
- package/dist/{chunk-UACYZCW4.js → chunk-33OU3LYX.js} +351 -4
- package/dist/{chunk-Y5HPJMTM.js → chunk-6STDKKAM.js} +2 -2
- package/dist/{chunk-GLHLXTKC.js → chunk-BJDT3VGQ.js} +6 -6
- package/dist/{chunk-YXOPQEFZ.js → chunk-D5YCPIK6.js} +2 -2
- package/dist/{chunk-Y33P35U3.js → chunk-DBCC7KIW.js} +5 -5
- package/dist/{chunk-XZNCONFG.js → chunk-DVG6DDX6.js} +1 -1
- package/dist/{chunk-GSYBGOY3.js → chunk-DW6HE5DH.js} +2 -2
- package/dist/{chunk-RY2U77OY.js → chunk-E5KJOUHI.js} +7 -7
- package/dist/{chunk-RZQZXWEL.js → chunk-HU7NS7JA.js} +6 -6
- package/dist/{chunk-AU4YPEIE.js → chunk-HUC73DZC.js} +3 -3
- package/dist/{chunk-ZU6T7W3U.js → chunk-J5MFNKE7.js} +2 -2
- package/dist/{chunk-4SKWGVYD.js → chunk-J7H5I4FO.js} +2 -2
- package/dist/{chunk-Z4XWWNYX.js → chunk-K5R2I37T.js} +203 -92
- package/dist/{chunk-YWRPQZL7.js → chunk-KHL6WFRL.js} +2 -2
- package/dist/{chunk-DGVT4DBI.js → chunk-KYFXXEK3.js} +3 -3
- package/dist/{chunk-NCMJXBEO.js → chunk-MHHISNS2.js} +2 -2
- package/dist/{chunk-ABRYIEWJ.js → chunk-RRCS27GT.js} +847 -800
- package/dist/{chunk-YJYUJSWK.js → chunk-RZTLXZHJ.js} +1 -1
- package/dist/{chunk-P4NA5OQX.js → chunk-SI4O2W4H.js} +2 -2
- package/dist/{chunk-BDGTZRZ6.js → chunk-UEXQMI6K.js} +56 -44
- package/dist/{chunk-XW5XVVMI.js → chunk-V2GJE5J5.js} +3 -3
- package/dist/{chunk-VG4G3R2V.js → chunk-VC2B275U.js} +2 -2
- package/dist/{chunk-OZSKLJQW.js → chunk-XACP5P2J.js} +2 -2
- package/dist/{chunk-IGWJRAJT.js → chunk-YTJIRQON.js} +2 -2
- package/dist/{chunk-N4AI2WFH.js → chunk-ZIJABBGZ.js} +2 -2
- package/dist/{ci-workflow-TFAA5BK7.js → ci-workflow-LGN7LHHO.js} +4 -4
- package/dist/{dist-ICW4QUEW.js → dist-FBBADRYS.js} +3 -1
- package/dist/{dist-MCTEX5QH.js → dist-LE5AU7FR.js} +17 -3
- package/dist/{docs-BBMAN3CY.js → docs-3BTPSHKE.js} +5 -5
- package/dist/{engine-R2YH74AL.js → engine-GWGU4AMH.js} +4 -4
- package/dist/{entropy-5UP5N6AF.js → entropy-35XURODP.js} +5 -5
- package/dist/{feedback-6MDCCFZL.js → feedback-NDGZZ37W.js} +1 -1
- package/dist/{generate-agent-definitions-KKAN5DB5.js → generate-agent-definitions-PYZ3E7SP.js} +5 -5
- package/dist/index.js +25 -25
- package/dist/{loader-2WT2ZE2T.js → loader-QK2OJXKB.js} +4 -4
- package/dist/{mcp-5V4LPMB3.js → mcp-Z5YMV7TA.js} +15 -15
- package/dist/{performance-2N3CFXLR.js → performance-VAYOYPVK.js} +5 -5
- package/dist/{review-pipeline-XZIGBU3Q.js → review-pipeline-UETEOWSS.js} +4 -4
- package/dist/{runtime-POME3GTR.js → runtime-J7KFACTK.js} +4 -4
- package/dist/{security-QLJLMSIW.js → security-6AIUZXF5.js} +1 -1
- package/dist/{state-events-YY5KBZTC.js → state-events-XQPHGLG5.js} +4 -4
- package/dist/{validate-VZORVK43.js → validate-ON7YMVLY.js} +5 -5
- package/dist/{validate-cross-check-OYKEDCST.js → validate-cross-check-HKKQA7K5.js} +4 -4
- package/package.json +8 -8
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import {
|
|
2
2
|
generateRuntime
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-ZIJABBGZ.js";
|
|
4
4
|
import {
|
|
5
5
|
generateAgentsMd
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-VC2B275U.js";
|
|
7
7
|
import {
|
|
8
8
|
generateCIWorkflow
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-SI4O2W4H.js";
|
|
10
10
|
import {
|
|
11
11
|
readHarnessState
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-KHL6WFRL.js";
|
|
13
13
|
import {
|
|
14
14
|
runCrossCheck
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-DW6HE5DH.js";
|
|
16
16
|
import {
|
|
17
17
|
createGenerateAgentDefinitionsCommand,
|
|
18
18
|
generateAgentDefinitions
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-DVG6DDX6.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-YTJIRQON.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-MHHISNS2.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-V2GJE5J5.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-K5R2I37T.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-6STDKKAM.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-33OU3LYX.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-RZTLXZHJ.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-UEXQMI6K.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-LE5AU7FR.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-LE5AU7FR.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-LE5AU7FR.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(projectRoot2) {
|
|
590
|
+
const skillsDir = path2.join(projectRoot2, "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-LE5AU7FR.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
1191
|
function runBackfillSkillProvenance(projectRoot2) {
|
|
1146
|
-
const skillsRoot =
|
|
1192
|
+
const skillsRoot = path5.join(projectRoot2, "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,10 +1965,10 @@ 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 projectRoot2 =
|
|
1971
|
+
const projectRoot2 = path9.resolve(cwd);
|
|
1926
1972
|
const auditor = new HarnessStrengthAuditor();
|
|
1927
1973
|
const result = auditor.audit(projectRoot2, options.mode ? { mode: options.mode } : {});
|
|
1928
1974
|
if (!result.ok) return result;
|
|
@@ -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 projectRoot2 =
|
|
2202
|
+
const projectRoot2 = 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(projectRoot2, "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
|
}
|
|
@@ -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-LE5AU7FR.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 projectRoot2 =
|
|
7010
|
-
const lockfilePath =
|
|
7011
|
-
const resolvedSource =
|
|
7056
|
+
const projectRoot2 = path37.dirname(configPath);
|
|
7057
|
+
const lockfilePath = path37.join(projectRoot2, ".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-Z5YMV7TA.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((resolve49) => rl.question(question, resolve49));
|
|
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) {
|
|
@@ -9198,10 +9245,10 @@ function printResult4(result, mode, _formatter) {
|
|
|
9198
9245
|
list.push(f);
|
|
9199
9246
|
byFile.set(f.target.file, list);
|
|
9200
9247
|
}
|
|
9201
|
-
for (const [file,
|
|
9248
|
+
for (const [file, fs51] of byFile) {
|
|
9202
9249
|
console.log(`
|
|
9203
9250
|
${file}`);
|
|
9204
|
-
for (const f of
|
|
9251
|
+
for (const f of fs51) {
|
|
9205
9252
|
const line = f.target.line !== void 0 ? `:${f.target.line}` : "";
|
|
9206
9253
|
console.log(
|
|
9207
9254
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.kind} ${f.target.identifier}${line}`
|
|
@@ -9227,7 +9274,7 @@ import { Command as Command69 } from "commander";
|
|
|
9227
9274
|
init_dist3();
|
|
9228
9275
|
import { Command as Command68 } from "commander";
|
|
9229
9276
|
import { randomBytes } from "crypto";
|
|
9230
|
-
import { resolve as
|
|
9277
|
+
import { resolve as resolve31 } from "path";
|
|
9231
9278
|
import { SinkRegistry, SinkConfigError, wrapAsEnvelope } from "@harness-engineering/orchestrator";
|
|
9232
9279
|
function makeTestEvent(message) {
|
|
9233
9280
|
return {
|
|
@@ -9279,7 +9326,7 @@ async function runNotificationsTest(sinkId, opts, projectRoot2 = process.cwd())
|
|
|
9279
9326
|
function createNotificationsTestSubcommand() {
|
|
9280
9327
|
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
9328
|
const globalOpts = cmd.optsWithGlobals();
|
|
9282
|
-
const projectRoot2 = typeof globalOpts["cwd"] === "string" ?
|
|
9329
|
+
const projectRoot2 = typeof globalOpts["cwd"] === "string" ? resolve31(globalOpts["cwd"]) : process.cwd();
|
|
9283
9330
|
const result = await runNotificationsTest(sinkId, opts, projectRoot2);
|
|
9284
9331
|
if (globalOpts["json"]) {
|
|
9285
9332
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -9303,12 +9350,12 @@ function createNotificationsCommand() {
|
|
|
9303
9350
|
|
|
9304
9351
|
// src/commands/orchestrator.ts
|
|
9305
9352
|
import { Command as Command70 } from "commander";
|
|
9306
|
-
import * as
|
|
9353
|
+
import * as path50 from "path";
|
|
9307
9354
|
import { Orchestrator, WorkflowLoader as WorkflowLoader2, launchTUI } from "@harness-engineering/orchestrator";
|
|
9308
9355
|
function createOrchestratorCommand() {
|
|
9309
9356
|
const orchestrator = new Command70("orchestrator");
|
|
9310
9357
|
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 =
|
|
9358
|
+
const workflowPath = path50.resolve(process.cwd(), opts.workflow);
|
|
9312
9359
|
const loader = new WorkflowLoader2();
|
|
9313
9360
|
const result = await loader.loadWorkflow(workflowPath);
|
|
9314
9361
|
if (!result.ok) {
|
|
@@ -9343,12 +9390,12 @@ function createOrchestratorCommand() {
|
|
|
9343
9390
|
// src/commands/perf.ts
|
|
9344
9391
|
init_dist3();
|
|
9345
9392
|
import { Command as Command71 } from "commander";
|
|
9346
|
-
import * as
|
|
9393
|
+
import * as path51 from "path";
|
|
9347
9394
|
function registerBenchCommand(perf) {
|
|
9348
9395
|
perf.command("bench [glob]").description("Run benchmarks via vitest bench").action(async (glob2, _opts, cmd) => {
|
|
9349
9396
|
const globalOpts = cmd.optsWithGlobals();
|
|
9350
9397
|
const cwd = process.cwd();
|
|
9351
|
-
const { BenchmarkRunner } = await import("./dist-
|
|
9398
|
+
const { BenchmarkRunner } = await import("./dist-LE5AU7FR.js");
|
|
9352
9399
|
const runner = new BenchmarkRunner();
|
|
9353
9400
|
const benchFiles = runner.discover(cwd, glob2);
|
|
9354
9401
|
if (benchFiles.length === 0) {
|
|
@@ -9412,7 +9459,7 @@ async function runBaselinesShow(globalOpts) {
|
|
|
9412
9459
|
}
|
|
9413
9460
|
async function runBaselinesUpdate(globalOpts) {
|
|
9414
9461
|
const cwd = process.cwd();
|
|
9415
|
-
const { BenchmarkRunner } = await import("./dist-
|
|
9462
|
+
const { BenchmarkRunner } = await import("./dist-LE5AU7FR.js");
|
|
9416
9463
|
const runner = new BenchmarkRunner();
|
|
9417
9464
|
const manager = new BaselineManager(cwd);
|
|
9418
9465
|
logger.info("Running benchmarks to update baselines...");
|
|
@@ -9453,9 +9500,9 @@ function registerReportCommand(perf) {
|
|
|
9453
9500
|
perf.command("report").description("Full performance report with metrics, trends, and hotspots").action(async (_opts, cmd) => {
|
|
9454
9501
|
const globalOpts = cmd.optsWithGlobals();
|
|
9455
9502
|
const cwd = process.cwd();
|
|
9456
|
-
const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-
|
|
9503
|
+
const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-LE5AU7FR.js");
|
|
9457
9504
|
const analyzer = new EntropyAnalyzer2({
|
|
9458
|
-
rootDir:
|
|
9505
|
+
rootDir: path51.resolve(cwd),
|
|
9459
9506
|
analyze: { complexity: true, coupling: true }
|
|
9460
9507
|
});
|
|
9461
9508
|
const result = await analyzer.analyze();
|
|
@@ -9561,34 +9608,34 @@ function createListCommand2() {
|
|
|
9561
9608
|
|
|
9562
9609
|
// src/commands/persona/generate.ts
|
|
9563
9610
|
import { Command as Command73, Option } from "commander";
|
|
9564
|
-
import * as
|
|
9565
|
-
import * as
|
|
9611
|
+
import * as fs31 from "fs";
|
|
9612
|
+
import * as path52 from "path";
|
|
9566
9613
|
function generatePersonaArtifacts(persona, outputDir, only, platform) {
|
|
9567
9614
|
const slug = toKebabCase(persona.name);
|
|
9568
9615
|
const generated = [];
|
|
9569
9616
|
if (!only || only === "runtime") {
|
|
9570
9617
|
const result = generateRuntime(persona);
|
|
9571
9618
|
if (result.ok) {
|
|
9572
|
-
const outPath =
|
|
9573
|
-
|
|
9574
|
-
|
|
9619
|
+
const outPath = path52.join(outputDir, `${slug}.runtime.json`);
|
|
9620
|
+
fs31.mkdirSync(path52.dirname(outPath), { recursive: true });
|
|
9621
|
+
fs31.writeFileSync(outPath, result.value);
|
|
9575
9622
|
generated.push(outPath);
|
|
9576
9623
|
}
|
|
9577
9624
|
}
|
|
9578
9625
|
if (!only || only === "agents-md") {
|
|
9579
9626
|
const result = generateAgentsMd(persona);
|
|
9580
9627
|
if (result.ok) {
|
|
9581
|
-
const outPath =
|
|
9582
|
-
|
|
9628
|
+
const outPath = path52.join(outputDir, `${slug}.agents.md`);
|
|
9629
|
+
fs31.writeFileSync(outPath, result.value);
|
|
9583
9630
|
generated.push(outPath);
|
|
9584
9631
|
}
|
|
9585
9632
|
}
|
|
9586
9633
|
if (!only || only === "ci") {
|
|
9587
9634
|
const result = generateCIWorkflow(persona, platform);
|
|
9588
9635
|
if (result.ok) {
|
|
9589
|
-
const outPath = platform === "gitlab" ?
|
|
9590
|
-
|
|
9591
|
-
|
|
9636
|
+
const outPath = platform === "gitlab" ? path52.join(outputDir, `${slug}.gitlab-ci.yml`) : path52.join(outputDir, ".github", "workflows", `${slug}.yml`);
|
|
9637
|
+
fs31.mkdirSync(path52.dirname(outPath), { recursive: true });
|
|
9638
|
+
fs31.writeFileSync(outPath, result.value);
|
|
9592
9639
|
generated.push(outPath);
|
|
9593
9640
|
}
|
|
9594
9641
|
}
|
|
@@ -9599,14 +9646,14 @@ function createGenerateCommand3() {
|
|
|
9599
9646
|
new Option("--platform <platform>", "CI platform for the ci artifact").choices(["github", "gitlab"]).default("github")
|
|
9600
9647
|
).action(async (name, opts, cmd) => {
|
|
9601
9648
|
const globalOpts = cmd.optsWithGlobals();
|
|
9602
|
-
const personaResult = loadPersona(
|
|
9649
|
+
const personaResult = loadPersona(path52.join(resolvePersonasDir(), `${name}.yaml`));
|
|
9603
9650
|
if (!personaResult.ok) {
|
|
9604
9651
|
logger.error(personaResult.error.message);
|
|
9605
9652
|
process.exit(ExitCode.ERROR);
|
|
9606
9653
|
}
|
|
9607
9654
|
const generated = generatePersonaArtifacts(
|
|
9608
9655
|
personaResult.value,
|
|
9609
|
-
|
|
9656
|
+
path52.resolve(opts.outputDir),
|
|
9610
9657
|
opts.only,
|
|
9611
9658
|
opts.platform
|
|
9612
9659
|
);
|
|
@@ -9760,14 +9807,14 @@ function createPredictCommand() {
|
|
|
9760
9807
|
init_dist2();
|
|
9761
9808
|
import { execFileSync as execFileSync7 } from "child_process";
|
|
9762
9809
|
import { readFileSync as readFileSync18 } from "fs";
|
|
9763
|
-
import { join as
|
|
9810
|
+
import { join as join41 } from "path";
|
|
9764
9811
|
import { Command as Command77 } from "commander";
|
|
9765
9812
|
import { gatherSignals } from "@harness-engineering/signals";
|
|
9766
9813
|
|
|
9767
9814
|
// src/commands/review-ci.ts
|
|
9768
9815
|
init_dist3();
|
|
9769
9816
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
9770
|
-
import { writeFileSync as
|
|
9817
|
+
import { writeFileSync as writeFileSync19 } from "fs";
|
|
9771
9818
|
import { Command as Command76, Option as Option2 } from "commander";
|
|
9772
9819
|
|
|
9773
9820
|
// src/commands/review-ci-local-adapter.ts
|
|
@@ -9940,7 +9987,7 @@ var defaultPostReview = (verdict) => {
|
|
|
9940
9987
|
encoding: "utf-8"
|
|
9941
9988
|
});
|
|
9942
9989
|
};
|
|
9943
|
-
function emitReviewCi(result, opts, writeFile2 = (p, d) =>
|
|
9990
|
+
function emitReviewCi(result, opts, writeFile2 = (p, d) => writeFileSync19(p, d), log = (m) => process.stdout.write(m + "\n"), postReview = defaultPostReview) {
|
|
9944
9991
|
const serialized = JSON.stringify(result.verdict, null, 2);
|
|
9945
9992
|
const jsonToStdout = opts.jsonPath === true;
|
|
9946
9993
|
if (!jsonToStdout) log(result.terminalOutput);
|
|
@@ -10121,11 +10168,11 @@ var defaultPostBrief = (body) => {
|
|
|
10121
10168
|
}
|
|
10122
10169
|
);
|
|
10123
10170
|
};
|
|
10124
|
-
var defaultReadFile = (
|
|
10125
|
-
function readReview(
|
|
10126
|
-
if (!
|
|
10171
|
+
var defaultReadFile = (path81) => readFileSync18(path81, "utf-8");
|
|
10172
|
+
function readReview(path81, readFile4 = defaultReadFile) {
|
|
10173
|
+
if (!path81) return void 0;
|
|
10127
10174
|
try {
|
|
10128
|
-
const parsed = JSON.parse(readFile4(
|
|
10175
|
+
const parsed = JSON.parse(readFile4(path81));
|
|
10129
10176
|
return parsed?.verdict && typeof parsed.verdict === "object" ? parsed.verdict : void 0;
|
|
10130
10177
|
} catch {
|
|
10131
10178
|
return void 0;
|
|
@@ -10143,7 +10190,7 @@ var GRAPH_DIR = ".harness/graph";
|
|
|
10143
10190
|
async function loadOutcomeStore(projectPath) {
|
|
10144
10191
|
try {
|
|
10145
10192
|
const store = new GraphStore();
|
|
10146
|
-
const loaded = await store.load(
|
|
10193
|
+
const loaded = await store.load(join41(projectPath, GRAPH_DIR));
|
|
10147
10194
|
return loaded ? store : void 0;
|
|
10148
10195
|
} catch {
|
|
10149
10196
|
return void 0;
|
|
@@ -10240,9 +10287,9 @@ function createPreMergeBriefCommand() {
|
|
|
10240
10287
|
// src/commands/proposals.ts
|
|
10241
10288
|
init_dist3();
|
|
10242
10289
|
import { Command as Command78 } from "commander";
|
|
10243
|
-
import { resolve as
|
|
10290
|
+
import { resolve as resolve35 } from "path";
|
|
10244
10291
|
function projectRoot() {
|
|
10245
|
-
return
|
|
10292
|
+
return resolve35(process.env["HARNESS_PROJECT_ROOT"] ?? process.cwd());
|
|
10246
10293
|
}
|
|
10247
10294
|
function summarizeProposal(p) {
|
|
10248
10295
|
return {
|
|
@@ -10349,8 +10396,8 @@ function createProposalsCommand() {
|
|
|
10349
10396
|
}
|
|
10350
10397
|
|
|
10351
10398
|
// src/commands/publish-analyses.ts
|
|
10352
|
-
import * as
|
|
10353
|
-
import * as
|
|
10399
|
+
import * as fs32 from "fs";
|
|
10400
|
+
import * as path53 from "path";
|
|
10354
10401
|
import { Command as Command79 } from "commander";
|
|
10355
10402
|
init_dist3();
|
|
10356
10403
|
import {
|
|
@@ -10359,7 +10406,7 @@ import {
|
|
|
10359
10406
|
savePublishedIndex
|
|
10360
10407
|
} from "@harness-engineering/orchestrator";
|
|
10361
10408
|
function bootstrapTrackerCommand(opts, verb) {
|
|
10362
|
-
const projectPath =
|
|
10409
|
+
const projectPath = path53.resolve(opts.dir);
|
|
10363
10410
|
const trackerConfig = loadTrackerSyncConfig(projectPath);
|
|
10364
10411
|
if (!trackerConfig) {
|
|
10365
10412
|
logger.error(`No tracker config found in harness.config.json. Cannot ${verb}.`);
|
|
@@ -10371,8 +10418,8 @@ function bootstrapTrackerCommand(opts, verb) {
|
|
|
10371
10418
|
);
|
|
10372
10419
|
return null;
|
|
10373
10420
|
}
|
|
10374
|
-
const projectEnvPath =
|
|
10375
|
-
if (
|
|
10421
|
+
const projectEnvPath = path53.join(projectPath, ".env");
|
|
10422
|
+
if (fs32.existsSync(projectEnvPath) && !process.env.GITHUB_TOKEN) {
|
|
10376
10423
|
__require("dotenv").config({ path: projectEnvPath });
|
|
10377
10424
|
}
|
|
10378
10425
|
const token = process.env.GITHUB_TOKEN;
|
|
@@ -10463,8 +10510,8 @@ async function runPublishAnalyses(opts) {
|
|
|
10463
10510
|
const nameToExternalId = await buildNameToExternalIdMap(projectPath);
|
|
10464
10511
|
if (!nameToExternalId) return process.exit(1);
|
|
10465
10512
|
const { AnalysisArchive } = await import("@harness-engineering/orchestrator");
|
|
10466
|
-
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-
|
|
10467
|
-
const archive = new AnalysisArchive(
|
|
10513
|
+
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-LE5AU7FR.js");
|
|
10514
|
+
const archive = new AnalysisArchive(path53.join(projectPath, ".harness", "analyses"));
|
|
10468
10515
|
const analyses = await archive.list();
|
|
10469
10516
|
if (analyses.length === 0) {
|
|
10470
10517
|
logger.info("No analyses found to publish.");
|
|
@@ -10506,8 +10553,8 @@ import { Command as Command81 } from "commander";
|
|
|
10506
10553
|
|
|
10507
10554
|
// src/commands/pulse/run.ts
|
|
10508
10555
|
init_dist3();
|
|
10509
|
-
import { readFileSync as readFileSync19, mkdirSync as
|
|
10510
|
-
import { dirname as
|
|
10556
|
+
import { readFileSync as readFileSync19, mkdirSync as mkdirSync16, writeFileSync as writeFileSync20 } from "fs";
|
|
10557
|
+
import { dirname as dirname19, join as join43, resolve as resolve37 } from "path";
|
|
10511
10558
|
import { Command as Command80 } from "commander";
|
|
10512
10559
|
async function runPulseRunCommand(opts) {
|
|
10513
10560
|
const startedAt = Date.now();
|
|
@@ -10539,9 +10586,9 @@ async function runPulseRunCommand(opts) {
|
|
|
10539
10586
|
const report = assembleReport(result, productName, lookback);
|
|
10540
10587
|
const now = /* @__PURE__ */ new Date();
|
|
10541
10588
|
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
|
-
|
|
10589
|
+
const filePath = join43(opts.outputDir, `${ts}.md`);
|
|
10590
|
+
mkdirSync16(dirname19(filePath), { recursive: true });
|
|
10591
|
+
writeFileSync20(filePath, report);
|
|
10545
10592
|
const headlines = extractHeadlines(report);
|
|
10546
10593
|
return emit2(
|
|
10547
10594
|
{
|
|
@@ -10583,8 +10630,8 @@ function createRunCommand2() {
|
|
|
10583
10630
|
async (options) => {
|
|
10584
10631
|
const nonInteractive = options.nonInteractive === true || !process.stdout.isTTY;
|
|
10585
10632
|
const status = await runPulseRunCommand({
|
|
10586
|
-
configPath:
|
|
10587
|
-
outputDir:
|
|
10633
|
+
configPath: resolve37(process.cwd(), options.config),
|
|
10634
|
+
outputDir: resolve37(process.cwd(), options.outputDir),
|
|
10588
10635
|
nonInteractive,
|
|
10589
10636
|
lookback: options.lookback
|
|
10590
10637
|
});
|
|
@@ -10708,13 +10755,13 @@ import { Command as Command89 } from "commander";
|
|
|
10708
10755
|
// src/commands/roadmap/migrate.ts
|
|
10709
10756
|
init_dist3();
|
|
10710
10757
|
import { Command as Command83 } from "commander";
|
|
10711
|
-
import * as
|
|
10712
|
-
import * as
|
|
10758
|
+
import * as fs34 from "fs";
|
|
10759
|
+
import * as path55 from "path";
|
|
10713
10760
|
import chalk14 from "chalk";
|
|
10714
10761
|
|
|
10715
10762
|
// src/commands/roadmap/migrate-lock.ts
|
|
10716
|
-
import * as
|
|
10717
|
-
import * as
|
|
10763
|
+
import * as fs33 from "fs";
|
|
10764
|
+
import * as path54 from "path";
|
|
10718
10765
|
import * as os7 from "os";
|
|
10719
10766
|
var LOCK_FILE_NAME = "migrate.lock";
|
|
10720
10767
|
var STALE_LOCK_MS = 30 * 60 * 1e3;
|
|
@@ -10728,13 +10775,13 @@ function isPidAlive(pid) {
|
|
|
10728
10775
|
}
|
|
10729
10776
|
}
|
|
10730
10777
|
function acquireMigrateLock(projectRoot2) {
|
|
10731
|
-
const harnessDir =
|
|
10732
|
-
const lockPath =
|
|
10733
|
-
if (!
|
|
10734
|
-
if (
|
|
10778
|
+
const harnessDir = path54.join(projectRoot2, ".harness");
|
|
10779
|
+
const lockPath = path54.join(harnessDir, LOCK_FILE_NAME);
|
|
10780
|
+
if (!fs33.existsSync(harnessDir)) fs33.mkdirSync(harnessDir, { recursive: true });
|
|
10781
|
+
if (fs33.existsSync(lockPath)) {
|
|
10735
10782
|
const existingRaw = (() => {
|
|
10736
10783
|
try {
|
|
10737
|
-
return
|
|
10784
|
+
return fs33.readFileSync(lockPath, "utf-8");
|
|
10738
10785
|
} catch {
|
|
10739
10786
|
return null;
|
|
10740
10787
|
}
|
|
@@ -10764,11 +10811,11 @@ function acquireMigrateLock(projectRoot2) {
|
|
|
10764
10811
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10765
10812
|
hostname: os7.hostname()
|
|
10766
10813
|
};
|
|
10767
|
-
|
|
10814
|
+
fs33.writeFileSync(lockPath, JSON.stringify(payload, null, 2) + "\n");
|
|
10768
10815
|
return {
|
|
10769
10816
|
release: () => {
|
|
10770
10817
|
try {
|
|
10771
|
-
|
|
10818
|
+
fs33.unlinkSync(lockPath);
|
|
10772
10819
|
} catch {
|
|
10773
10820
|
}
|
|
10774
10821
|
}
|
|
@@ -10924,11 +10971,11 @@ async function runRoadmapMigrate(opts) {
|
|
|
10924
10971
|
if (!clientR.ok) return Err(new CLIError(clientR.error.message));
|
|
10925
10972
|
client = clientR.value;
|
|
10926
10973
|
}
|
|
10927
|
-
const roadmapPath =
|
|
10928
|
-
if (!
|
|
10974
|
+
const roadmapPath = path55.join(cwd, "docs", "roadmap.md");
|
|
10975
|
+
if (!fs34.existsSync(roadmapPath)) {
|
|
10929
10976
|
return Err(new CLIError(`docs/roadmap.md not found in ${cwd}`));
|
|
10930
10977
|
}
|
|
10931
|
-
const roadmapR = parseRoadmap(
|
|
10978
|
+
const roadmapR = parseRoadmap(fs34.readFileSync(roadmapPath, "utf-8"));
|
|
10932
10979
|
if (!roadmapR.ok) {
|
|
10933
10980
|
return Err(new CLIError(`failed to parse docs/roadmap.md: ${roadmapR.error.message}`));
|
|
10934
10981
|
}
|
|
@@ -10949,10 +10996,10 @@ async function runRoadmapMigrate(opts) {
|
|
|
10949
10996
|
if (!isJson) printPlanSummary(plan, opts.dryRun);
|
|
10950
10997
|
const deps = {
|
|
10951
10998
|
client,
|
|
10952
|
-
readFile: (p) =>
|
|
10953
|
-
writeFile: (p, b) =>
|
|
10954
|
-
renameFile: (from, to) =>
|
|
10955
|
-
existsFile: (p) =>
|
|
10999
|
+
readFile: (p) => fs34.readFileSync(p, "utf-8"),
|
|
11000
|
+
writeFile: (p, b) => fs34.writeFileSync(p, b),
|
|
11001
|
+
renameFile: (from, to) => fs34.renameSync(from, to),
|
|
11002
|
+
existsFile: (p) => fs34.existsSync(p)
|
|
10956
11003
|
};
|
|
10957
11004
|
const reportR = await migrate_exports.runMigrationPlan(plan, deps, {
|
|
10958
11005
|
projectRoot: cwd,
|
|
@@ -11010,7 +11057,7 @@ function featuresToRoadmap(features, project, nowIso) {
|
|
|
11010
11057
|
}
|
|
11011
11058
|
function readProjectName(configPath) {
|
|
11012
11059
|
try {
|
|
11013
|
-
const parsed = JSON.parse(
|
|
11060
|
+
const parsed = JSON.parse(fs34.readFileSync(configPath, "utf-8"));
|
|
11014
11061
|
return typeof parsed.name === "string" && parsed.name.length > 0 ? parsed.name : "Roadmap";
|
|
11015
11062
|
} catch {
|
|
11016
11063
|
return "Roadmap";
|
|
@@ -11057,8 +11104,8 @@ async function runReverseMigrate(opts) {
|
|
|
11057
11104
|
const lockResult = acquireMigrateLock(cwd);
|
|
11058
11105
|
if (isRefusal(lockResult)) return Err(new CLIError(lockResult.message));
|
|
11059
11106
|
try {
|
|
11060
|
-
const roadmapPath =
|
|
11061
|
-
if (
|
|
11107
|
+
const roadmapPath = path55.join(cwd, "docs", "roadmap.md");
|
|
11108
|
+
if (fs34.existsSync(roadmapPath)) {
|
|
11062
11109
|
return Err(
|
|
11063
11110
|
new CLIError(`docs/roadmap.md already exists; refusing to overwrite (${roadmapPath})`)
|
|
11064
11111
|
);
|
|
@@ -11078,7 +11125,7 @@ async function runReverseMigrate(opts) {
|
|
|
11078
11125
|
return Err(new CLIError(`failed to fetch tracker features: ${fetchR.error.message}`));
|
|
11079
11126
|
}
|
|
11080
11127
|
const features = fetchR.value.features;
|
|
11081
|
-
const configPath =
|
|
11128
|
+
const configPath = path55.join(cwd, "harness.config.json");
|
|
11082
11129
|
const project = readProjectName(configPath);
|
|
11083
11130
|
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
11084
11131
|
const markdown = serializeRoadmap(featuresToRoadmap(features, project, nowIso));
|
|
@@ -11096,19 +11143,19 @@ async function runReverseMigrate(opts) {
|
|
|
11096
11143
|
emitReverseReport(report, features.length, isJson, true);
|
|
11097
11144
|
return Ok(report);
|
|
11098
11145
|
}
|
|
11099
|
-
|
|
11100
|
-
|
|
11146
|
+
fs34.mkdirSync(path55.dirname(roadmapPath), { recursive: true });
|
|
11147
|
+
fs34.writeFileSync(roadmapPath, markdown);
|
|
11101
11148
|
report.archivedTo = roadmapPath;
|
|
11102
|
-
if (
|
|
11103
|
-
const original =
|
|
11104
|
-
const configBackupPath =
|
|
11105
|
-
|
|
11149
|
+
if (fs34.existsSync(configPath)) {
|
|
11150
|
+
const original = fs34.readFileSync(configPath, "utf-8");
|
|
11151
|
+
const configBackupPath = path55.join(cwd, "harness.config.json.pre-migration");
|
|
11152
|
+
fs34.writeFileSync(configBackupPath, original);
|
|
11106
11153
|
report.configBackup = configBackupPath;
|
|
11107
11154
|
const parsed = JSON.parse(original);
|
|
11108
11155
|
const roadmapSection = parsed.roadmap ?? {};
|
|
11109
11156
|
roadmapSection.mode = "file-backed";
|
|
11110
11157
|
parsed.roadmap = roadmapSection;
|
|
11111
|
-
|
|
11158
|
+
fs34.writeFileSync(configPath, JSON.stringify(parsed, null, 2) + "\n");
|
|
11112
11159
|
}
|
|
11113
11160
|
emitReverseReport(report, features.length, isJson, false);
|
|
11114
11161
|
return Ok(report);
|
|
@@ -11153,23 +11200,23 @@ function createRoadmapMigrateCommand() {
|
|
|
11153
11200
|
// src/commands/roadmap/shard.ts
|
|
11154
11201
|
init_dist3();
|
|
11155
11202
|
import { Command as Command84 } from "commander";
|
|
11156
|
-
import * as
|
|
11203
|
+
import * as path56 from "path";
|
|
11157
11204
|
|
|
11158
11205
|
// src/commands/roadmap/shard-io.ts
|
|
11159
11206
|
import * as fsp from "fs/promises";
|
|
11160
11207
|
function createNodeShardIO() {
|
|
11161
11208
|
return {
|
|
11162
|
-
readFile: (
|
|
11163
|
-
writeFile: (
|
|
11209
|
+
readFile: (path81) => fsp.readFile(path81, "utf-8"),
|
|
11210
|
+
writeFile: (path81, data) => fsp.writeFile(path81, data, "utf-8"),
|
|
11164
11211
|
listDir: (dir) => fsp.readdir(dir),
|
|
11165
|
-
deleteFile: (
|
|
11212
|
+
deleteFile: (path81) => fsp.rm(path81),
|
|
11166
11213
|
mkdirp: async (dir) => {
|
|
11167
11214
|
await fsp.mkdir(dir, { recursive: true });
|
|
11168
11215
|
},
|
|
11169
|
-
rmrf: (
|
|
11170
|
-
exists: async (
|
|
11216
|
+
rmrf: (path81) => fsp.rm(path81, { recursive: true, force: true }),
|
|
11217
|
+
exists: async (path81) => {
|
|
11171
11218
|
try {
|
|
11172
|
-
await fsp.access(
|
|
11219
|
+
await fsp.access(path81);
|
|
11173
11220
|
return true;
|
|
11174
11221
|
} catch {
|
|
11175
11222
|
return false;
|
|
@@ -11186,8 +11233,8 @@ async function runRoadmapShard(opts = {}) {
|
|
|
11186
11233
|
const force = Boolean(opts.force);
|
|
11187
11234
|
const format = opts.format === "json" ? "json" : "human";
|
|
11188
11235
|
const assertRoundTrip = opts.assertRoundTrip ?? assertSemanticRoundTrip;
|
|
11189
|
-
const roadmapPath =
|
|
11190
|
-
const shardDir =
|
|
11236
|
+
const roadmapPath = path56.join(cwd, "docs", "roadmap.md").replaceAll("\\", "/");
|
|
11237
|
+
const shardDir = path56.join(cwd, "docs", "roadmap.d").replaceAll("\\", "/");
|
|
11191
11238
|
if (!await io.exists(roadmapPath)) {
|
|
11192
11239
|
return Err(new CLIError("docs/roadmap.md not found", ExitCode.ERROR));
|
|
11193
11240
|
}
|
|
@@ -11279,14 +11326,14 @@ function createRoadmapShardCommand() {
|
|
|
11279
11326
|
// src/commands/roadmap/unshard.ts
|
|
11280
11327
|
init_dist3();
|
|
11281
11328
|
import { Command as Command85 } from "commander";
|
|
11282
|
-
import * as
|
|
11329
|
+
import * as path57 from "path";
|
|
11283
11330
|
async function runRoadmapUnshard(opts = {}) {
|
|
11284
11331
|
const cwd = opts.cwd ?? process.cwd();
|
|
11285
11332
|
const io = opts.io ?? createNodeShardIO();
|
|
11286
11333
|
const dryRun = Boolean(opts.dryRun);
|
|
11287
11334
|
const format = opts.format === "json" ? "json" : "human";
|
|
11288
|
-
const shardDir =
|
|
11289
|
-
const roadmapPath =
|
|
11335
|
+
const shardDir = path57.join(cwd, "docs", "roadmap.d");
|
|
11336
|
+
const roadmapPath = path57.join(cwd, "docs", "roadmap.md");
|
|
11290
11337
|
if (!await io.exists(shardDir)) {
|
|
11291
11338
|
return Err(new CLIError("docs/roadmap.d not found; nothing to unshard", ExitCode.ERROR));
|
|
11292
11339
|
}
|
|
@@ -11335,14 +11382,14 @@ function createRoadmapUnshardCommand() {
|
|
|
11335
11382
|
// src/commands/roadmap/regen.ts
|
|
11336
11383
|
init_dist3();
|
|
11337
11384
|
import { Command as Command86 } from "commander";
|
|
11338
|
-
import * as
|
|
11385
|
+
import * as path58 from "path";
|
|
11339
11386
|
async function runRoadmapRegen(opts = {}) {
|
|
11340
11387
|
const cwd = opts.cwd ?? process.cwd();
|
|
11341
11388
|
const io = opts.io ?? createNodeShardIO();
|
|
11342
11389
|
const dryRun = Boolean(opts.dryRun);
|
|
11343
11390
|
const format = opts.format === "json" ? "json" : "human";
|
|
11344
|
-
const shardDir =
|
|
11345
|
-
const roadmapPath =
|
|
11391
|
+
const shardDir = path58.join(cwd, "docs", "roadmap.d");
|
|
11392
|
+
const roadmapPath = path58.join(cwd, "docs", "roadmap.md");
|
|
11346
11393
|
if (!await io.exists(shardDir)) {
|
|
11347
11394
|
return Err(new CLIError("docs/roadmap.d not found; project is not sharded", ExitCode.ERROR));
|
|
11348
11395
|
}
|
|
@@ -11388,8 +11435,8 @@ function createRoadmapRegenCommand() {
|
|
|
11388
11435
|
// src/commands/roadmap/reconcile.ts
|
|
11389
11436
|
init_dist3();
|
|
11390
11437
|
import { Command as Command87 } from "commander";
|
|
11391
|
-
import * as
|
|
11392
|
-
import * as
|
|
11438
|
+
import * as fs35 from "fs";
|
|
11439
|
+
import * as path59 from "path";
|
|
11393
11440
|
async function runRoadmapReconcile(opts = {}) {
|
|
11394
11441
|
const cwd = opts.cwd ?? process.cwd();
|
|
11395
11442
|
if (!opts.store && !roadmapSourceExists(cwd)) {
|
|
@@ -11467,8 +11514,8 @@ async function resolveAdapter(opts, cwd) {
|
|
|
11467
11514
|
)
|
|
11468
11515
|
);
|
|
11469
11516
|
}
|
|
11470
|
-
const envPath =
|
|
11471
|
-
if (
|
|
11517
|
+
const envPath = path59.join(cwd, ".env");
|
|
11518
|
+
if (fs35.existsSync(envPath) && !process.env.GITHUB_TOKEN) {
|
|
11472
11519
|
const { config: loadDotenv } = await import("dotenv");
|
|
11473
11520
|
loadDotenv({ path: envPath });
|
|
11474
11521
|
}
|
|
@@ -11580,9 +11627,9 @@ function authHeader() {
|
|
|
11580
11627
|
const tok = process.env["HARNESS_API_TOKEN"];
|
|
11581
11628
|
return tok ? { Authorization: `Bearer ${tok}` } : {};
|
|
11582
11629
|
}
|
|
11583
|
-
async function getJson(
|
|
11630
|
+
async function getJson(path81) {
|
|
11584
11631
|
try {
|
|
11585
|
-
const res = await fetch(`${orchestratorBase()}${
|
|
11632
|
+
const res = await fetch(`${orchestratorBase()}${path81}`, {
|
|
11586
11633
|
headers: { ...authHeader() }
|
|
11587
11634
|
});
|
|
11588
11635
|
const text = await res.text();
|
|
@@ -11597,9 +11644,9 @@ async function getJson(path80) {
|
|
|
11597
11644
|
};
|
|
11598
11645
|
}
|
|
11599
11646
|
}
|
|
11600
|
-
async function postJson(
|
|
11647
|
+
async function postJson(path81, body) {
|
|
11601
11648
|
try {
|
|
11602
|
-
const res = await fetch(`${orchestratorBase()}${
|
|
11649
|
+
const res = await fetch(`${orchestratorBase()}${path81}`, {
|
|
11603
11650
|
method: "POST",
|
|
11604
11651
|
headers: { "Content-Type": "application/json", ...authHeader() },
|
|
11605
11652
|
body: JSON.stringify(body)
|
|
@@ -11804,8 +11851,8 @@ function createRoutingCommand() {
|
|
|
11804
11851
|
// src/commands/scan-config.ts
|
|
11805
11852
|
init_dist3();
|
|
11806
11853
|
import { Command as Command94 } from "commander";
|
|
11807
|
-
import { existsSync as
|
|
11808
|
-
import { join as
|
|
11854
|
+
import { existsSync as existsSync31, readFileSync as readFileSync22, writeFileSync as writeFileSync23 } from "fs";
|
|
11855
|
+
import { join as join50, relative as relative4 } from "path";
|
|
11809
11856
|
var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".gemini/settings.json", "skill.yaml"];
|
|
11810
11857
|
function stripHighSeverityPatterns(content, injectionFindings) {
|
|
11811
11858
|
const highLines = /* @__PURE__ */ new Set();
|
|
@@ -11831,14 +11878,14 @@ function applyFix(filePath, targetDir, content, injectionFindings) {
|
|
|
11831
11878
|
if (!hasHighSeverity) return;
|
|
11832
11879
|
const { cleaned, linesStripped } = stripHighSeverityPatterns(content, injectionFindings);
|
|
11833
11880
|
if (linesStripped > 0) {
|
|
11834
|
-
|
|
11881
|
+
writeFileSync23(filePath, cleaned);
|
|
11835
11882
|
logger.info(
|
|
11836
|
-
`scan-config --fix: stripped ${linesStripped} high-severity line(s) from ${
|
|
11883
|
+
`scan-config --fix: stripped ${linesStripped} high-severity line(s) from ${relative4(targetDir, filePath).replaceAll("\\", "/")}`
|
|
11837
11884
|
);
|
|
11838
11885
|
}
|
|
11839
11886
|
}
|
|
11840
11887
|
function scanSingleFile(filePath, targetDir, scanner, options) {
|
|
11841
|
-
if (!
|
|
11888
|
+
if (!existsSync31(filePath)) return null;
|
|
11842
11889
|
let content;
|
|
11843
11890
|
try {
|
|
11844
11891
|
content = readFileSync22(filePath, "utf8");
|
|
@@ -11853,7 +11900,7 @@ function scanSingleFile(filePath, targetDir, scanner, options) {
|
|
|
11853
11900
|
applyFix(filePath, targetDir, content, injectionFindings);
|
|
11854
11901
|
}
|
|
11855
11902
|
return {
|
|
11856
|
-
file:
|
|
11903
|
+
file: relative4(targetDir, filePath).replaceAll("\\", "/"),
|
|
11857
11904
|
findings,
|
|
11858
11905
|
overallSeverity: computeOverallSeverity(findings)
|
|
11859
11906
|
};
|
|
@@ -11862,7 +11909,7 @@ async function runScanConfig(targetDir, options) {
|
|
|
11862
11909
|
const scanner = new SecurityScanner(parseSecurityConfig({}));
|
|
11863
11910
|
const results = [];
|
|
11864
11911
|
for (const configFile of CONFIG_FILES) {
|
|
11865
|
-
const result = scanSingleFile(
|
|
11912
|
+
const result = scanSingleFile(join50(targetDir, configFile), targetDir, scanner, options);
|
|
11866
11913
|
if (result) results.push(result);
|
|
11867
11914
|
}
|
|
11868
11915
|
return { exitCode: computeScanExitCode(results), results };
|
|
@@ -12040,10 +12087,10 @@ function printResult5(result, mode, _formatter) {
|
|
|
12040
12087
|
list.push(f);
|
|
12041
12088
|
byFile.set(f.target.file, list);
|
|
12042
12089
|
}
|
|
12043
|
-
for (const [file,
|
|
12090
|
+
for (const [file, fs51] of byFile) {
|
|
12044
12091
|
console.log(`
|
|
12045
12092
|
${file}`);
|
|
12046
|
-
for (const f of
|
|
12093
|
+
for (const f of fs51) {
|
|
12047
12094
|
console.log(
|
|
12048
12095
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.signal}:${f.target.line}`
|
|
12049
12096
|
);
|
|
@@ -12060,23 +12107,23 @@ ${file}`);
|
|
|
12060
12107
|
|
|
12061
12108
|
// src/commands/setup.ts
|
|
12062
12109
|
import { Command as Command97 } from "commander";
|
|
12063
|
-
import * as
|
|
12110
|
+
import * as fs38 from "fs";
|
|
12064
12111
|
import * as os9 from "os";
|
|
12065
|
-
import * as
|
|
12112
|
+
import * as path62 from "path";
|
|
12066
12113
|
import chalk16 from "chalk";
|
|
12067
12114
|
|
|
12068
12115
|
// src/utils/first-run.ts
|
|
12069
|
-
import * as
|
|
12116
|
+
import * as fs36 from "fs";
|
|
12070
12117
|
import * as os8 from "os";
|
|
12071
|
-
import * as
|
|
12072
|
-
var HARNESS_DIR =
|
|
12073
|
-
var MARKER_FILE =
|
|
12118
|
+
import * as path60 from "path";
|
|
12119
|
+
var HARNESS_DIR = path60.join(os8.homedir(), ".harness");
|
|
12120
|
+
var MARKER_FILE = path60.join(HARNESS_DIR, ".setup-complete");
|
|
12074
12121
|
function isFirstRun() {
|
|
12075
|
-
return !
|
|
12122
|
+
return !fs36.existsSync(MARKER_FILE);
|
|
12076
12123
|
}
|
|
12077
12124
|
function markSetupComplete() {
|
|
12078
|
-
|
|
12079
|
-
|
|
12125
|
+
fs36.mkdirSync(HARNESS_DIR, { recursive: true });
|
|
12126
|
+
fs36.writeFileSync(MARKER_FILE, "", "utf-8");
|
|
12080
12127
|
}
|
|
12081
12128
|
function printFirstRunWelcome() {
|
|
12082
12129
|
try {
|
|
@@ -12089,8 +12136,8 @@ function printFirstRunWelcome() {
|
|
|
12089
12136
|
}
|
|
12090
12137
|
|
|
12091
12138
|
// src/commands/telemetry-wizard.ts
|
|
12092
|
-
import * as
|
|
12093
|
-
import * as
|
|
12139
|
+
import * as fs37 from "fs";
|
|
12140
|
+
import * as path61 from "path";
|
|
12094
12141
|
import readline2 from "readline";
|
|
12095
12142
|
import chalk15 from "chalk";
|
|
12096
12143
|
function prompt2(question) {
|
|
@@ -12098,10 +12145,10 @@ function prompt2(question) {
|
|
|
12098
12145
|
input: process.stdin,
|
|
12099
12146
|
output: process.stdout
|
|
12100
12147
|
});
|
|
12101
|
-
return new Promise((
|
|
12148
|
+
return new Promise((resolve49) => {
|
|
12102
12149
|
rl.question(question, (answer) => {
|
|
12103
12150
|
rl.close();
|
|
12104
|
-
|
|
12151
|
+
resolve49(answer.trim().toLowerCase());
|
|
12105
12152
|
});
|
|
12106
12153
|
});
|
|
12107
12154
|
}
|
|
@@ -12110,10 +12157,10 @@ function promptRaw(question) {
|
|
|
12110
12157
|
input: process.stdin,
|
|
12111
12158
|
output: process.stdout
|
|
12112
12159
|
});
|
|
12113
|
-
return new Promise((
|
|
12160
|
+
return new Promise((resolve49) => {
|
|
12114
12161
|
rl.question(question, (answer) => {
|
|
12115
12162
|
rl.close();
|
|
12116
|
-
|
|
12163
|
+
resolve49(answer.trim());
|
|
12117
12164
|
});
|
|
12118
12165
|
});
|
|
12119
12166
|
}
|
|
@@ -12121,21 +12168,21 @@ function isNo(answer) {
|
|
|
12121
12168
|
return answer === "n" || answer === "no";
|
|
12122
12169
|
}
|
|
12123
12170
|
function isTelemetryConfigured(cwd) {
|
|
12124
|
-
const configPath =
|
|
12171
|
+
const configPath = path61.join(cwd, "harness.config.json");
|
|
12125
12172
|
try {
|
|
12126
|
-
const raw = JSON.parse(
|
|
12173
|
+
const raw = JSON.parse(fs37.readFileSync(configPath, "utf-8"));
|
|
12127
12174
|
return raw.telemetry !== void 0 && raw.adoption !== void 0;
|
|
12128
12175
|
} catch {
|
|
12129
12176
|
return false;
|
|
12130
12177
|
}
|
|
12131
12178
|
}
|
|
12132
12179
|
function writeTelemetryConfig(cwd, result) {
|
|
12133
|
-
const configPath =
|
|
12180
|
+
const configPath = path61.join(cwd, "harness.config.json");
|
|
12134
12181
|
try {
|
|
12135
|
-
const raw = JSON.parse(
|
|
12182
|
+
const raw = JSON.parse(fs37.readFileSync(configPath, "utf-8"));
|
|
12136
12183
|
raw.telemetry = { enabled: result.telemetryEnabled };
|
|
12137
12184
|
raw.adoption = { enabled: result.adoptionEnabled };
|
|
12138
|
-
|
|
12185
|
+
fs37.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
|
|
12139
12186
|
} catch (err) {
|
|
12140
12187
|
console.warn(
|
|
12141
12188
|
`\u26A0 Could not update ${configPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -12143,10 +12190,10 @@ function writeTelemetryConfig(cwd, result) {
|
|
|
12143
12190
|
}
|
|
12144
12191
|
const hasIdentity = result.identity.project || result.identity.team || result.identity.alias;
|
|
12145
12192
|
if (hasIdentity) {
|
|
12146
|
-
const harnessDir =
|
|
12147
|
-
|
|
12148
|
-
const telemetryFile =
|
|
12149
|
-
|
|
12193
|
+
const harnessDir = path61.join(cwd, ".harness");
|
|
12194
|
+
fs37.mkdirSync(harnessDir, { recursive: true });
|
|
12195
|
+
const telemetryFile = path61.join(harnessDir, "telemetry.json");
|
|
12196
|
+
fs37.writeFileSync(telemetryFile, JSON.stringify({ identity: result.identity }, null, 2) + "\n");
|
|
12150
12197
|
}
|
|
12151
12198
|
}
|
|
12152
12199
|
async function promptIdentity() {
|
|
@@ -12192,8 +12239,8 @@ async function runTelemetryWizard() {
|
|
|
12192
12239
|
return { telemetryEnabled, adoptionEnabled, identity };
|
|
12193
12240
|
}
|
|
12194
12241
|
async function ensureTelemetryConfigured(cwd) {
|
|
12195
|
-
const configPath =
|
|
12196
|
-
if (!
|
|
12242
|
+
const configPath = path61.join(cwd, "harness.config.json");
|
|
12243
|
+
if (!fs37.existsSync(configPath)) {
|
|
12197
12244
|
return { status: "warn", message: "Not a harness project \u2014 skipped telemetry configuration" };
|
|
12198
12245
|
}
|
|
12199
12246
|
if (isTelemetryConfigured(cwd)) {
|
|
@@ -12239,7 +12286,7 @@ function runSlashCommandGeneration() {
|
|
|
12239
12286
|
}
|
|
12240
12287
|
}
|
|
12241
12288
|
function detectClient(dirName) {
|
|
12242
|
-
return
|
|
12289
|
+
return fs38.existsSync(path62.join(os9.homedir(), dirName));
|
|
12243
12290
|
}
|
|
12244
12291
|
async function runMcpSetup(cwd) {
|
|
12245
12292
|
const results = [];
|
|
@@ -12255,7 +12302,7 @@ async function runMcpSetup(cwd) {
|
|
|
12255
12302
|
{ name: "Cursor", dir: ".cursor", client: "cursor", configTarget: ".cursor/mcp.json" },
|
|
12256
12303
|
{
|
|
12257
12304
|
name: "OpenCode",
|
|
12258
|
-
dir:
|
|
12305
|
+
dir: path62.join(".config", "opencode"),
|
|
12259
12306
|
client: "opencode",
|
|
12260
12307
|
configTarget: "opencode.json"
|
|
12261
12308
|
}
|
|
@@ -12287,7 +12334,7 @@ function formatStep(result) {
|
|
|
12287
12334
|
}
|
|
12288
12335
|
function configureTier0Integrations(cwd) {
|
|
12289
12336
|
try {
|
|
12290
|
-
const mcpPath =
|
|
12337
|
+
const mcpPath = path62.join(cwd, ".mcp.json");
|
|
12291
12338
|
const config = readMcpConfig(mcpPath);
|
|
12292
12339
|
const tier0 = INTEGRATION_REGISTRY.filter((i) => i.tier === 0);
|
|
12293
12340
|
const added = [];
|
|
@@ -12296,9 +12343,9 @@ function configureTier0Integrations(cwd) {
|
|
|
12296
12343
|
writeMcpEntry(mcpPath, integration.name, integration.mcpConfig);
|
|
12297
12344
|
added.push(integration.displayName);
|
|
12298
12345
|
}
|
|
12299
|
-
const geminiDir =
|
|
12300
|
-
if (
|
|
12301
|
-
const geminiPath =
|
|
12346
|
+
const geminiDir = path62.join(cwd, ".gemini");
|
|
12347
|
+
if (fs38.existsSync(geminiDir)) {
|
|
12348
|
+
const geminiPath = path62.join(geminiDir, "settings.json");
|
|
12302
12349
|
const geminiConfig = readMcpConfig(geminiPath);
|
|
12303
12350
|
for (const integration of tier0) {
|
|
12304
12351
|
if (!geminiConfig.mcpServers[integration.name]) {
|
|
@@ -12306,9 +12353,9 @@ function configureTier0Integrations(cwd) {
|
|
|
12306
12353
|
}
|
|
12307
12354
|
}
|
|
12308
12355
|
}
|
|
12309
|
-
const opencodePath =
|
|
12310
|
-
const opencodeGlobalDir =
|
|
12311
|
-
if (
|
|
12356
|
+
const opencodePath = path62.join(cwd, "opencode.json");
|
|
12357
|
+
const opencodeGlobalDir = path62.join(os9.homedir(), ".config", "opencode");
|
|
12358
|
+
if (fs38.existsSync(opencodePath) || fs38.existsSync(opencodeGlobalDir)) {
|
|
12312
12359
|
for (const integration of tier0) {
|
|
12313
12360
|
writeOpencodeMcpEntry(opencodePath, integration.name, integration.mcpConfig);
|
|
12314
12361
|
}
|
|
@@ -12326,14 +12373,14 @@ function configureTier0Integrations(cwd) {
|
|
|
12326
12373
|
}
|
|
12327
12374
|
}
|
|
12328
12375
|
function ensureHooks(cwd) {
|
|
12329
|
-
const configPath =
|
|
12330
|
-
if (!
|
|
12376
|
+
const configPath = path62.join(cwd, "harness.config.json");
|
|
12377
|
+
if (!fs38.existsSync(configPath)) {
|
|
12331
12378
|
return { status: "warn", message: "Not a harness project \u2014 skipped hook installation" };
|
|
12332
12379
|
}
|
|
12333
12380
|
let profile = "standard";
|
|
12334
|
-
const profilePath =
|
|
12381
|
+
const profilePath = path62.join(cwd, ".harness", "hooks", "profile.json");
|
|
12335
12382
|
try {
|
|
12336
|
-
const data = JSON.parse(
|
|
12383
|
+
const data = JSON.parse(fs38.readFileSync(profilePath, "utf-8"));
|
|
12337
12384
|
if (data.profile && ["minimal", "standard", "strict"].includes(data.profile)) {
|
|
12338
12385
|
profile = data.profile;
|
|
12339
12386
|
}
|
|
@@ -12428,14 +12475,14 @@ function createSetupCommand() {
|
|
|
12428
12475
|
// src/commands/share.ts
|
|
12429
12476
|
init_dist3();
|
|
12430
12477
|
import { Command as Command98 } from "commander";
|
|
12431
|
-
import * as
|
|
12432
|
-
import * as
|
|
12478
|
+
import * as fs39 from "fs";
|
|
12479
|
+
import * as path63 from "path";
|
|
12433
12480
|
import { parse as parseYaml3 } from "yaml";
|
|
12434
12481
|
var MANIFEST_FILENAME = "constraints.yaml";
|
|
12435
12482
|
async function runShareAction(projectPath, options) {
|
|
12436
|
-
const rootDir =
|
|
12437
|
-
const manifestPath =
|
|
12438
|
-
if (!
|
|
12483
|
+
const rootDir = path63.resolve(projectPath);
|
|
12484
|
+
const manifestPath = path63.join(rootDir, MANIFEST_FILENAME);
|
|
12485
|
+
if (!fs39.existsSync(manifestPath)) {
|
|
12439
12486
|
logger.error(
|
|
12440
12487
|
`No ${MANIFEST_FILENAME} found at ${manifestPath}.
|
|
12441
12488
|
Create a constraints.yaml in your project root to define what to share.`
|
|
@@ -12444,7 +12491,7 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
12444
12491
|
}
|
|
12445
12492
|
let parsed;
|
|
12446
12493
|
try {
|
|
12447
|
-
const raw =
|
|
12494
|
+
const raw = fs39.readFileSync(manifestPath, "utf-8");
|
|
12448
12495
|
parsed = parseYaml3(raw);
|
|
12449
12496
|
} catch (err) {
|
|
12450
12497
|
logger.error(
|
|
@@ -12458,7 +12505,7 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
12458
12505
|
process.exit(1);
|
|
12459
12506
|
}
|
|
12460
12507
|
const manifest = manifestResult.value;
|
|
12461
|
-
const configResult = resolveConfig(
|
|
12508
|
+
const configResult = resolveConfig(path63.join(rootDir, "harness.config.json"));
|
|
12462
12509
|
if (!configResult.ok) {
|
|
12463
12510
|
logger.error(configResult.error.message);
|
|
12464
12511
|
process.exit(1);
|
|
@@ -12476,8 +12523,8 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
12476
12523
|
);
|
|
12477
12524
|
process.exit(1);
|
|
12478
12525
|
}
|
|
12479
|
-
const outputDir =
|
|
12480
|
-
const outputPath =
|
|
12526
|
+
const outputDir = path63.resolve(options.output);
|
|
12527
|
+
const outputPath = path63.join(outputDir, `${manifest.name}.harness-constraints.json`);
|
|
12481
12528
|
const writeResult = await writeConfig(outputPath, bundle);
|
|
12482
12529
|
if (!writeResult.ok) {
|
|
12483
12530
|
logger.error(`Failed to write bundle: ${writeResult.error.message}`);
|
|
@@ -12496,18 +12543,18 @@ import { Command as Command106 } from "commander";
|
|
|
12496
12543
|
|
|
12497
12544
|
// src/commands/skill/list.ts
|
|
12498
12545
|
import { Command as Command99 } from "commander";
|
|
12499
|
-
import * as
|
|
12500
|
-
import * as
|
|
12546
|
+
import * as fs40 from "fs";
|
|
12547
|
+
import * as path64 from "path";
|
|
12501
12548
|
import { parse } from "yaml";
|
|
12502
12549
|
function scanDirectory(dirPath, source) {
|
|
12503
|
-
if (!
|
|
12504
|
-
const entries =
|
|
12550
|
+
if (!fs40.existsSync(dirPath)) return [];
|
|
12551
|
+
const entries = fs40.readdirSync(dirPath, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
12505
12552
|
const skills = [];
|
|
12506
12553
|
for (const name of entries) {
|
|
12507
|
-
const yamlPath =
|
|
12508
|
-
if (!
|
|
12554
|
+
const yamlPath = path64.join(dirPath, name, "skill.yaml");
|
|
12555
|
+
if (!fs40.existsSync(yamlPath)) continue;
|
|
12509
12556
|
try {
|
|
12510
|
-
const raw =
|
|
12557
|
+
const raw = fs40.readFileSync(yamlPath, "utf-8");
|
|
12511
12558
|
const parsed = parse(raw);
|
|
12512
12559
|
const result = SkillMetadataSchema.safeParse(parsed);
|
|
12513
12560
|
if (result.success) {
|
|
@@ -12525,10 +12572,10 @@ function scanDirectory(dirPath, source) {
|
|
|
12525
12572
|
}
|
|
12526
12573
|
function collectCommunitySkills(seen, allSkills) {
|
|
12527
12574
|
const globalDir = resolveGlobalSkillsDir();
|
|
12528
|
-
const skillsDir =
|
|
12529
|
-
const communityBase =
|
|
12530
|
-
const communityPlatformDir =
|
|
12531
|
-
const lockfilePath =
|
|
12575
|
+
const skillsDir = path64.dirname(globalDir);
|
|
12576
|
+
const communityBase = path64.join(skillsDir, "community");
|
|
12577
|
+
const communityPlatformDir = path64.join(communityBase, "claude-code");
|
|
12578
|
+
const lockfilePath = path64.join(communityBase, "skills-lock.json");
|
|
12532
12579
|
const lockfile = readLockfile2(lockfilePath);
|
|
12533
12580
|
const communitySkills = scanDirectory(communityPlatformDir, "community");
|
|
12534
12581
|
for (const skill of communitySkills) {
|
|
@@ -12614,8 +12661,8 @@ function createListCommand3() {
|
|
|
12614
12661
|
|
|
12615
12662
|
// src/commands/skill/run.ts
|
|
12616
12663
|
import { Command as Command100 } from "commander";
|
|
12617
|
-
import * as
|
|
12618
|
-
import * as
|
|
12664
|
+
import * as fs41 from "fs";
|
|
12665
|
+
import * as path65 from "path";
|
|
12619
12666
|
import { parse as parse2 } from "yaml";
|
|
12620
12667
|
|
|
12621
12668
|
// src/skill/complexity.ts
|
|
@@ -12711,10 +12758,10 @@ ${options.principles}`);
|
|
|
12711
12758
|
|
|
12712
12759
|
// src/commands/skill/run.ts
|
|
12713
12760
|
function loadSkillMetadata(skillDir) {
|
|
12714
|
-
const yamlPath =
|
|
12715
|
-
if (!
|
|
12761
|
+
const yamlPath = path65.join(skillDir, "skill.yaml");
|
|
12762
|
+
if (!fs41.existsSync(yamlPath)) return null;
|
|
12716
12763
|
try {
|
|
12717
|
-
const result = SkillMetadataSchema.safeParse(parse2(
|
|
12764
|
+
const result = SkillMetadataSchema.safeParse(parse2(fs41.readFileSync(yamlPath, "utf-8")));
|
|
12718
12765
|
return result.success ? result.data : null;
|
|
12719
12766
|
} catch {
|
|
12720
12767
|
return null;
|
|
@@ -12726,22 +12773,22 @@ function resolveComplexity(metadata, requested, projectPath) {
|
|
|
12726
12773
|
return requested;
|
|
12727
12774
|
}
|
|
12728
12775
|
function loadPrinciples(projectPath) {
|
|
12729
|
-
const principlesPath =
|
|
12730
|
-
return
|
|
12776
|
+
const principlesPath = path65.join(projectPath, "docs", "principles.md");
|
|
12777
|
+
return fs41.existsSync(principlesPath) ? fs41.readFileSync(principlesPath, "utf-8") : void 0;
|
|
12731
12778
|
}
|
|
12732
12779
|
function readMostRecentFileInDir(dirPath) {
|
|
12733
|
-
const files =
|
|
12734
|
-
if (files.length > 0) return
|
|
12780
|
+
const files = fs41.readdirSync(dirPath).map((f) => ({ name: f, mtime: fs41.statSync(path65.join(dirPath, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
12781
|
+
if (files.length > 0) return fs41.readFileSync(path65.join(dirPath, files[0].name), "utf-8");
|
|
12735
12782
|
return void 0;
|
|
12736
12783
|
}
|
|
12737
12784
|
function loadPriorState(metadata, projectPath) {
|
|
12738
12785
|
if (!metadata?.state.persistent || metadata.state.files.length === 0) return void 0;
|
|
12739
12786
|
for (const stateFilePath of metadata.state.files) {
|
|
12740
|
-
const fullPath =
|
|
12741
|
-
if (!
|
|
12742
|
-
const stat =
|
|
12787
|
+
const fullPath = path65.join(projectPath, stateFilePath);
|
|
12788
|
+
if (!fs41.existsSync(fullPath)) continue;
|
|
12789
|
+
const stat = fs41.statSync(fullPath);
|
|
12743
12790
|
if (stat.isDirectory()) return readMostRecentFileInDir(fullPath);
|
|
12744
|
-
return
|
|
12791
|
+
return fs41.readFileSync(fullPath, "utf-8");
|
|
12745
12792
|
}
|
|
12746
12793
|
return void 0;
|
|
12747
12794
|
}
|
|
@@ -12761,7 +12808,7 @@ function resolvePhaseState(metadata, projectPath, phase) {
|
|
|
12761
12808
|
}
|
|
12762
12809
|
async function appendProjectState(content, metadata, projectPath, hasPathOpt) {
|
|
12763
12810
|
if (!metadata?.state.persistent || !hasPathOpt) return content;
|
|
12764
|
-
const { readHarnessState: readHarnessState2, isEmptyHarnessState } = await import("./state-events-
|
|
12811
|
+
const { readHarnessState: readHarnessState2, isEmptyHarnessState } = await import("./state-events-XQPHGLG5.js");
|
|
12765
12812
|
const stateResult = await readHarnessState2(projectPath);
|
|
12766
12813
|
if (!stateResult.ok || isEmptyHarnessState(stateResult.value)) return content;
|
|
12767
12814
|
const stateContent = JSON.stringify(stateResult.value, null, 2);
|
|
@@ -12782,7 +12829,7 @@ async function runSkill(name, opts) {
|
|
|
12782
12829
|
return;
|
|
12783
12830
|
}
|
|
12784
12831
|
const metadata = loadSkillMetadata(skillDir);
|
|
12785
|
-
const projectPath = opts.path ?
|
|
12832
|
+
const projectPath = opts.path ? path65.resolve(opts.path) : process.cwd();
|
|
12786
12833
|
const complexity = resolveComplexity(
|
|
12787
12834
|
metadata,
|
|
12788
12835
|
opts.complexity ?? "standard",
|
|
@@ -12809,14 +12856,14 @@ async function runSkill(name, opts) {
|
|
|
12809
12856
|
...stateWarning !== void 0 && { stateWarning },
|
|
12810
12857
|
...opts.party !== void 0 && { party: opts.party }
|
|
12811
12858
|
});
|
|
12812
|
-
const skillMdPath =
|
|
12813
|
-
if (!
|
|
12859
|
+
const skillMdPath = path65.join(skillDir, "SKILL.md");
|
|
12860
|
+
if (!fs41.existsSync(skillMdPath)) {
|
|
12814
12861
|
logger.error(`SKILL.md not found for skill: ${name}`);
|
|
12815
12862
|
process.exit(ExitCode.ERROR);
|
|
12816
12863
|
return;
|
|
12817
12864
|
}
|
|
12818
12865
|
const content = await appendProjectState(
|
|
12819
|
-
|
|
12866
|
+
fs41.readFileSync(skillMdPath, "utf-8"),
|
|
12820
12867
|
metadata,
|
|
12821
12868
|
projectPath,
|
|
12822
12869
|
!!opts.path
|
|
@@ -12835,8 +12882,8 @@ function createRunCommand3() {
|
|
|
12835
12882
|
|
|
12836
12883
|
// src/commands/skill/validate.ts
|
|
12837
12884
|
import { Command as Command101 } from "commander";
|
|
12838
|
-
import * as
|
|
12839
|
-
import * as
|
|
12885
|
+
import * as fs42 from "fs";
|
|
12886
|
+
import * as path66 from "path";
|
|
12840
12887
|
import { parse as parse3 } from "yaml";
|
|
12841
12888
|
var BEHAVIORAL_REQUIRED_SECTIONS = [
|
|
12842
12889
|
"## When to Use",
|
|
@@ -12848,11 +12895,11 @@ var BEHAVIORAL_REQUIRED_SECTIONS = [
|
|
|
12848
12895
|
];
|
|
12849
12896
|
var KNOWLEDGE_REQUIRED_SECTIONS = ["## Instructions"];
|
|
12850
12897
|
function validateSkillMd(name, skillMdPath, skillType, errors) {
|
|
12851
|
-
if (!
|
|
12898
|
+
if (!fs42.existsSync(skillMdPath)) {
|
|
12852
12899
|
errors.push(`${name}: missing SKILL.md`);
|
|
12853
12900
|
return;
|
|
12854
12901
|
}
|
|
12855
|
-
const mdContent =
|
|
12902
|
+
const mdContent = fs42.readFileSync(skillMdPath, "utf-8");
|
|
12856
12903
|
if (!mdContent.trim().startsWith("# ")) {
|
|
12857
12904
|
errors.push(`${name}/SKILL.md: must start with an h1 heading`);
|
|
12858
12905
|
}
|
|
@@ -12877,20 +12924,20 @@ function validateSkillMd(name, skillMdPath, skillType, errors) {
|
|
|
12877
12924
|
}
|
|
12878
12925
|
}
|
|
12879
12926
|
function validateSkillEntry(name, skillsDir, errors) {
|
|
12880
|
-
const skillDir =
|
|
12881
|
-
const yamlPath =
|
|
12882
|
-
if (!
|
|
12927
|
+
const skillDir = path66.join(skillsDir, name);
|
|
12928
|
+
const yamlPath = path66.join(skillDir, "skill.yaml");
|
|
12929
|
+
if (!fs42.existsSync(yamlPath)) {
|
|
12883
12930
|
errors.push(`${name}: missing skill.yaml`);
|
|
12884
12931
|
return false;
|
|
12885
12932
|
}
|
|
12886
12933
|
try {
|
|
12887
|
-
const raw =
|
|
12934
|
+
const raw = fs42.readFileSync(yamlPath, "utf-8");
|
|
12888
12935
|
const result = SkillMetadataSchema.safeParse(parse3(raw));
|
|
12889
12936
|
if (!result.success) {
|
|
12890
12937
|
errors.push(`${name}/skill.yaml: ${result.error.message}`);
|
|
12891
12938
|
return false;
|
|
12892
12939
|
}
|
|
12893
|
-
validateSkillMd(name,
|
|
12940
|
+
validateSkillMd(name, path66.join(skillDir, "SKILL.md"), result.data.type, errors);
|
|
12894
12941
|
return true;
|
|
12895
12942
|
} catch (e) {
|
|
12896
12943
|
errors.push(`${name}: parse error \u2014 ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -12901,12 +12948,12 @@ function createValidateCommand2() {
|
|
|
12901
12948
|
return new Command101("validate").description("Validate all skill.yaml files and SKILL.md structure").action(async (_opts, cmd) => {
|
|
12902
12949
|
const globalOpts = cmd.optsWithGlobals();
|
|
12903
12950
|
const skillsDir = resolveSkillsDir();
|
|
12904
|
-
if (!
|
|
12951
|
+
if (!fs42.existsSync(skillsDir)) {
|
|
12905
12952
|
logger.info("No skills directory found.");
|
|
12906
12953
|
process.exit(ExitCode.SUCCESS);
|
|
12907
12954
|
return;
|
|
12908
12955
|
}
|
|
12909
|
-
const entries =
|
|
12956
|
+
const entries = fs42.readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
12910
12957
|
const errors = [];
|
|
12911
12958
|
let validated = 0;
|
|
12912
12959
|
for (const name of entries) {
|
|
@@ -12929,8 +12976,8 @@ function createValidateCommand2() {
|
|
|
12929
12976
|
|
|
12930
12977
|
// src/commands/skill/info.ts
|
|
12931
12978
|
import { Command as Command102 } from "commander";
|
|
12932
|
-
import * as
|
|
12933
|
-
import * as
|
|
12979
|
+
import * as fs43 from "fs";
|
|
12980
|
+
import * as path67 from "path";
|
|
12934
12981
|
import { parse as parse4 } from "yaml";
|
|
12935
12982
|
function loadSkillMetadata2(name) {
|
|
12936
12983
|
const skillDir = resolveSkillDir(name);
|
|
@@ -12938,13 +12985,13 @@ function loadSkillMetadata2(name) {
|
|
|
12938
12985
|
logger.error(`Skill not found: ${name}`);
|
|
12939
12986
|
return { ok: false, exitCode: ExitCode.ERROR };
|
|
12940
12987
|
}
|
|
12941
|
-
const yamlPath =
|
|
12942
|
-
if (!
|
|
12988
|
+
const yamlPath = path67.join(skillDir, "skill.yaml");
|
|
12989
|
+
if (!fs43.existsSync(yamlPath)) {
|
|
12943
12990
|
logger.error(`skill.yaml not found for skill: ${name}`);
|
|
12944
12991
|
return { ok: false, exitCode: ExitCode.ERROR };
|
|
12945
12992
|
}
|
|
12946
12993
|
try {
|
|
12947
|
-
const raw =
|
|
12994
|
+
const raw = fs43.readFileSync(yamlPath, "utf-8");
|
|
12948
12995
|
const parsed = parse4(raw);
|
|
12949
12996
|
const result = SkillMetadataSchema.safeParse(parsed);
|
|
12950
12997
|
if (!result.success) {
|
|
@@ -13042,8 +13089,8 @@ function createSearchCommand2() {
|
|
|
13042
13089
|
|
|
13043
13090
|
// src/commands/skill/create.ts
|
|
13044
13091
|
import { Command as Command104 } from "commander";
|
|
13045
|
-
import * as
|
|
13046
|
-
import * as
|
|
13092
|
+
import * as path68 from "path";
|
|
13093
|
+
import * as fs44 from "fs";
|
|
13047
13094
|
import YAML from "yaml";
|
|
13048
13095
|
var KEBAB_CASE_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
13049
13096
|
function buildReadme(name, description) {
|
|
@@ -13127,22 +13174,22 @@ function runCreate(name, opts) {
|
|
|
13127
13174
|
if (!KEBAB_CASE_RE.test(name)) {
|
|
13128
13175
|
throw new Error(`Invalid skill name "${name}". Must be kebab-case (e.g., my-skill).`);
|
|
13129
13176
|
}
|
|
13130
|
-
const baseDir = opts.outputDir ??
|
|
13131
|
-
const skillDir =
|
|
13132
|
-
if (
|
|
13177
|
+
const baseDir = opts.outputDir ?? path68.join(process.cwd(), "agents", "skills", "claude-code");
|
|
13178
|
+
const skillDir = path68.join(baseDir, name);
|
|
13179
|
+
if (fs44.existsSync(skillDir)) {
|
|
13133
13180
|
throw new Error(`Skill directory already exists: ${skillDir}`);
|
|
13134
13181
|
}
|
|
13135
|
-
|
|
13182
|
+
fs44.mkdirSync(skillDir, { recursive: true });
|
|
13136
13183
|
const description = opts.description || `A community skill: ${name}`;
|
|
13137
13184
|
const skillYaml = buildSkillYaml(name, opts);
|
|
13138
|
-
const skillYamlPath =
|
|
13139
|
-
|
|
13185
|
+
const skillYamlPath = path68.join(skillDir, "skill.yaml");
|
|
13186
|
+
fs44.writeFileSync(skillYamlPath, YAML.stringify(skillYaml));
|
|
13140
13187
|
const skillMd = buildSkillMd(name, description);
|
|
13141
|
-
const skillMdPath =
|
|
13142
|
-
|
|
13188
|
+
const skillMdPath = path68.join(skillDir, "SKILL.md");
|
|
13189
|
+
fs44.writeFileSync(skillMdPath, skillMd);
|
|
13143
13190
|
const readme = buildReadme(name, description);
|
|
13144
|
-
const readmePath =
|
|
13145
|
-
|
|
13191
|
+
const readmePath = path68.join(skillDir, "README.md");
|
|
13192
|
+
fs44.writeFileSync(readmePath, readme);
|
|
13146
13193
|
return {
|
|
13147
13194
|
name,
|
|
13148
13195
|
directory: skillDir,
|
|
@@ -13156,7 +13203,7 @@ function printCreateResult(name, result) {
|
|
|
13156
13203
|
}
|
|
13157
13204
|
logger.info(`
|
|
13158
13205
|
Next steps:`);
|
|
13159
|
-
logger.info(` 1. Edit ${
|
|
13206
|
+
logger.info(` 1. Edit ${path68.join(result.directory, "SKILL.md")} with your skill content`);
|
|
13160
13207
|
logger.info(` 2. Run: harness skill validate ${name}`);
|
|
13161
13208
|
logger.info(` 3. Run: harness skills publish`);
|
|
13162
13209
|
}
|
|
@@ -13185,25 +13232,25 @@ function createCreateCommand() {
|
|
|
13185
13232
|
|
|
13186
13233
|
// src/commands/skill/publish.ts
|
|
13187
13234
|
import { Command as Command105 } from "commander";
|
|
13188
|
-
import * as
|
|
13189
|
-
import * as
|
|
13235
|
+
import * as fs46 from "fs";
|
|
13236
|
+
import * as path70 from "path";
|
|
13190
13237
|
import { execFileSync as execFileSync9 } from "child_process";
|
|
13191
13238
|
|
|
13192
13239
|
// src/registry/validator.ts
|
|
13193
|
-
import * as
|
|
13194
|
-
import * as
|
|
13240
|
+
import * as fs45 from "fs";
|
|
13241
|
+
import * as path69 from "path";
|
|
13195
13242
|
import { parse as parse5 } from "yaml";
|
|
13196
13243
|
import semver4 from "semver";
|
|
13197
13244
|
async function validateForPublish(skillDir, registryUrl) {
|
|
13198
13245
|
const errors = [];
|
|
13199
|
-
const skillYamlPath =
|
|
13200
|
-
if (!
|
|
13246
|
+
const skillYamlPath = path69.join(skillDir, "skill.yaml");
|
|
13247
|
+
if (!fs45.existsSync(skillYamlPath)) {
|
|
13201
13248
|
errors.push("skill.yaml not found. Create one with: harness skill create <name>");
|
|
13202
13249
|
return { valid: false, errors };
|
|
13203
13250
|
}
|
|
13204
13251
|
let skillMeta;
|
|
13205
13252
|
try {
|
|
13206
|
-
const raw =
|
|
13253
|
+
const raw = fs45.readFileSync(skillYamlPath, "utf-8");
|
|
13207
13254
|
const parsed = parse5(raw);
|
|
13208
13255
|
const result = SkillMetadataSchema.safeParse(parsed);
|
|
13209
13256
|
if (!result.success) {
|
|
@@ -13225,11 +13272,11 @@ async function validateForPublish(skillDir, registryUrl) {
|
|
|
13225
13272
|
if (!skillMeta.triggers || skillMeta.triggers.length === 0) {
|
|
13226
13273
|
errors.push("At least one trigger is required. Add triggers to skill.yaml.");
|
|
13227
13274
|
}
|
|
13228
|
-
const skillMdPath =
|
|
13229
|
-
if (!
|
|
13275
|
+
const skillMdPath = path69.join(skillDir, "SKILL.md");
|
|
13276
|
+
if (!fs45.existsSync(skillMdPath)) {
|
|
13230
13277
|
errors.push("SKILL.md not found. Create it with content describing your skill.");
|
|
13231
13278
|
} else {
|
|
13232
|
-
const content =
|
|
13279
|
+
const content = fs45.readFileSync(skillMdPath, "utf-8");
|
|
13233
13280
|
if (!content.includes("## When to Use")) {
|
|
13234
13281
|
errors.push('SKILL.md must contain a "## When to Use" section.');
|
|
13235
13282
|
}
|
|
@@ -13309,11 +13356,11 @@ ${errorList}`);
|
|
|
13309
13356
|
}
|
|
13310
13357
|
const meta = validation.skillMeta;
|
|
13311
13358
|
const pkg = derivePackageJson(meta);
|
|
13312
|
-
const pkgPath =
|
|
13313
|
-
|
|
13314
|
-
const readmePath =
|
|
13315
|
-
if (!
|
|
13316
|
-
const skillMdContent =
|
|
13359
|
+
const pkgPath = path70.join(skillDir, "package.json");
|
|
13360
|
+
fs46.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
13361
|
+
const readmePath = path70.join(skillDir, "README.md");
|
|
13362
|
+
if (!fs46.existsSync(readmePath)) {
|
|
13363
|
+
const skillMdContent = fs46.readFileSync(path70.join(skillDir, "SKILL.md"), "utf-8");
|
|
13317
13364
|
const readme = `# ${pkg.name}
|
|
13318
13365
|
|
|
13319
13366
|
${meta.description}
|
|
@@ -13327,7 +13374,7 @@ harness install ${meta.name}
|
|
|
13327
13374
|
---
|
|
13328
13375
|
|
|
13329
13376
|
${skillMdContent}`;
|
|
13330
|
-
|
|
13377
|
+
fs46.writeFileSync(readmePath, readme);
|
|
13331
13378
|
}
|
|
13332
13379
|
if (opts.dryRun) {
|
|
13333
13380
|
return {
|
|
@@ -13631,10 +13678,10 @@ function printResult6(result, mode, _formatter) {
|
|
|
13631
13678
|
list.push(f);
|
|
13632
13679
|
byFile.set(f.target.file, list);
|
|
13633
13680
|
}
|
|
13634
|
-
for (const [file,
|
|
13681
|
+
for (const [file, fs51] of byFile) {
|
|
13635
13682
|
console.log(`
|
|
13636
13683
|
${file}`);
|
|
13637
|
-
for (const f of
|
|
13684
|
+
for (const f of fs51) {
|
|
13638
13685
|
console.log(
|
|
13639
13686
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ## ${f.target.section}:${f.target.line}`
|
|
13640
13687
|
);
|
|
@@ -13760,7 +13807,7 @@ import { Command as Command114 } from "commander";
|
|
|
13760
13807
|
|
|
13761
13808
|
// src/commands/state/show.ts
|
|
13762
13809
|
import { Command as Command110 } from "commander";
|
|
13763
|
-
import * as
|
|
13810
|
+
import * as path71 from "path";
|
|
13764
13811
|
function printStateText(state, stream) {
|
|
13765
13812
|
if (stream) console.log(`Stream: ${stream}`);
|
|
13766
13813
|
console.log(`Schema Version: ${state.schemaVersion}`);
|
|
@@ -13786,7 +13833,7 @@ function printStateProgress(progress) {
|
|
|
13786
13833
|
function createShowCommand() {
|
|
13787
13834
|
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
13835
|
const globalOpts = cmd.optsWithGlobals();
|
|
13789
|
-
const result = await readHarnessState(
|
|
13836
|
+
const result = await readHarnessState(path71.resolve(opts.path), { stream: opts.stream });
|
|
13790
13837
|
if (!result.ok) {
|
|
13791
13838
|
logger.error(result.error.message);
|
|
13792
13839
|
process.exit(ExitCode.ERROR);
|
|
@@ -13803,18 +13850,18 @@ function createShowCommand() {
|
|
|
13803
13850
|
// src/commands/state/reset.ts
|
|
13804
13851
|
init_dist3();
|
|
13805
13852
|
import { Command as Command111 } from "commander";
|
|
13806
|
-
import * as
|
|
13853
|
+
import * as path72 from "path";
|
|
13807
13854
|
import * as readline3 from "readline";
|
|
13808
13855
|
async function confirmReset() {
|
|
13809
13856
|
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] ",
|
|
13857
|
+
const answer = await new Promise((resolve49) => {
|
|
13858
|
+
rl.question("Reset project state? This cannot be undone. [y/N] ", resolve49);
|
|
13812
13859
|
});
|
|
13813
13860
|
rl.close();
|
|
13814
13861
|
return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
|
|
13815
13862
|
}
|
|
13816
13863
|
async function runReset(opts) {
|
|
13817
|
-
const projectPath =
|
|
13864
|
+
const projectPath = path72.resolve(opts.path);
|
|
13818
13865
|
if (!opts.yes && !await confirmReset()) {
|
|
13819
13866
|
logger.info("Reset cancelled.");
|
|
13820
13867
|
process.exit(ExitCode.SUCCESS);
|
|
@@ -13834,10 +13881,10 @@ function createResetCommand() {
|
|
|
13834
13881
|
// src/commands/state/learn.ts
|
|
13835
13882
|
init_dist3();
|
|
13836
13883
|
import { Command as Command112 } from "commander";
|
|
13837
|
-
import * as
|
|
13884
|
+
import * as path73 from "path";
|
|
13838
13885
|
function createLearnCommand() {
|
|
13839
13886
|
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 =
|
|
13887
|
+
const projectPath = path73.resolve(opts.path);
|
|
13841
13888
|
const result = await appendLearning(projectPath, message, void 0, void 0, opts.stream);
|
|
13842
13889
|
if (!result.ok) {
|
|
13843
13890
|
logger.error(result.error.message);
|
|
@@ -13852,9 +13899,9 @@ function createLearnCommand() {
|
|
|
13852
13899
|
// src/commands/state/streams.ts
|
|
13853
13900
|
init_dist3();
|
|
13854
13901
|
import { Command as Command113 } from "commander";
|
|
13855
|
-
import * as
|
|
13902
|
+
import * as path74 from "path";
|
|
13856
13903
|
async function runListStreams(opts, globalOpts) {
|
|
13857
|
-
const projectPath =
|
|
13904
|
+
const projectPath = path74.resolve(opts.path);
|
|
13858
13905
|
const indexResult = await loadStreamIndex(projectPath);
|
|
13859
13906
|
const result = await listStreams(projectPath);
|
|
13860
13907
|
if (!result.ok) {
|
|
@@ -13875,7 +13922,7 @@ async function runListStreams(opts, globalOpts) {
|
|
|
13875
13922
|
process.exit(ExitCode.SUCCESS);
|
|
13876
13923
|
}
|
|
13877
13924
|
async function runCreateStream(name, opts) {
|
|
13878
|
-
const result = await createStream(
|
|
13925
|
+
const result = await createStream(path74.resolve(opts.path), name, opts.branch);
|
|
13879
13926
|
if (!result.ok) {
|
|
13880
13927
|
logger.error(result.error.message);
|
|
13881
13928
|
process.exit(ExitCode.ERROR);
|
|
@@ -13884,7 +13931,7 @@ async function runCreateStream(name, opts) {
|
|
|
13884
13931
|
process.exit(ExitCode.SUCCESS);
|
|
13885
13932
|
}
|
|
13886
13933
|
async function runArchiveStream(name, opts) {
|
|
13887
|
-
const result = await archiveStream(
|
|
13934
|
+
const result = await archiveStream(path74.resolve(opts.path), name);
|
|
13888
13935
|
if (!result.ok) {
|
|
13889
13936
|
logger.error(result.error.message);
|
|
13890
13937
|
process.exit(ExitCode.ERROR);
|
|
@@ -13893,7 +13940,7 @@ async function runArchiveStream(name, opts) {
|
|
|
13893
13940
|
process.exit(ExitCode.SUCCESS);
|
|
13894
13941
|
}
|
|
13895
13942
|
async function runActivateStream(name, opts) {
|
|
13896
|
-
const result = await setActiveStream(
|
|
13943
|
+
const result = await setActiveStream(path74.resolve(opts.path), name);
|
|
13897
13944
|
if (!result.ok) {
|
|
13898
13945
|
logger.error(result.error.message);
|
|
13899
13946
|
process.exit(ExitCode.ERROR);
|
|
@@ -13921,8 +13968,8 @@ function createStateCommand() {
|
|
|
13921
13968
|
}
|
|
13922
13969
|
|
|
13923
13970
|
// src/commands/sync-analyses.ts
|
|
13924
|
-
import * as
|
|
13925
|
-
import * as
|
|
13971
|
+
import * as fs47 from "fs";
|
|
13972
|
+
import * as path75 from "path";
|
|
13926
13973
|
import { Command as Command115 } from "commander";
|
|
13927
13974
|
init_dist3();
|
|
13928
13975
|
function isValidRecord(record) {
|
|
@@ -13949,7 +13996,7 @@ function extractAnalysisFromComments(comments) {
|
|
|
13949
13996
|
return null;
|
|
13950
13997
|
}
|
|
13951
13998
|
function bootstrapTrackerCommand2(opts, verb) {
|
|
13952
|
-
const projectPath =
|
|
13999
|
+
const projectPath = path75.resolve(opts.dir);
|
|
13953
14000
|
const trackerConfig = loadTrackerSyncConfig(projectPath);
|
|
13954
14001
|
if (!trackerConfig) {
|
|
13955
14002
|
logger.error(`No tracker config found in harness.config.json. Cannot ${verb}.`);
|
|
@@ -13961,8 +14008,8 @@ function bootstrapTrackerCommand2(opts, verb) {
|
|
|
13961
14008
|
);
|
|
13962
14009
|
return null;
|
|
13963
14010
|
}
|
|
13964
|
-
const projectEnvPath =
|
|
13965
|
-
if (
|
|
14011
|
+
const projectEnvPath = path75.join(projectPath, ".env");
|
|
14012
|
+
if (fs47.existsSync(projectEnvPath) && !process.env.GITHUB_TOKEN) {
|
|
13966
14013
|
__require("dotenv").config({ path: projectEnvPath });
|
|
13967
14014
|
}
|
|
13968
14015
|
const token = process.env.GITHUB_TOKEN;
|
|
@@ -14048,9 +14095,9 @@ async function runSyncAnalyses(opts) {
|
|
|
14048
14095
|
return;
|
|
14049
14096
|
}
|
|
14050
14097
|
const { AnalysisArchive, loadPublishedIndex: loadPublishedIndex2, savePublishedIndex: savePublishedIndex2 } = await import("@harness-engineering/orchestrator");
|
|
14051
|
-
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-
|
|
14098
|
+
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-LE5AU7FR.js");
|
|
14052
14099
|
const adapter = new GitHubIssuesSyncAdapter2({ token, config: trackerConfig });
|
|
14053
|
-
const archive = new AnalysisArchive(
|
|
14100
|
+
const archive = new AnalysisArchive(path75.join(projectPath, ".harness", "analyses"));
|
|
14054
14101
|
const publishedIndex = loadPublishedIndex2(projectPath);
|
|
14055
14102
|
const { syncedCount, skippedCount, warnCount } = await syncFeatureAnalyses(
|
|
14056
14103
|
features,
|
|
@@ -14085,7 +14132,7 @@ function createSyncAnalysesCommand() {
|
|
|
14085
14132
|
|
|
14086
14133
|
// src/commands/sync-main.ts
|
|
14087
14134
|
import { Command as Command116 } from "commander";
|
|
14088
|
-
import * as
|
|
14135
|
+
import * as path76 from "path";
|
|
14089
14136
|
import {
|
|
14090
14137
|
syncMain as defaultSyncMain
|
|
14091
14138
|
} from "@harness-engineering/orchestrator";
|
|
@@ -14116,7 +14163,7 @@ function formatHuman2(r) {
|
|
|
14116
14163
|
}
|
|
14117
14164
|
function createSyncMainCommand() {
|
|
14118
14165
|
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 =
|
|
14166
|
+
const cwd = path76.resolve(opts.path ?? ".");
|
|
14120
14167
|
const exitCode = await runSyncMain({ cwd, json: Boolean(opts.json) });
|
|
14121
14168
|
process.exit(exitCode);
|
|
14122
14169
|
});
|
|
@@ -14208,16 +14255,16 @@ import { Command as Command121 } from "commander";
|
|
|
14208
14255
|
// src/commands/telemetry/identify.ts
|
|
14209
14256
|
init_dist3();
|
|
14210
14257
|
import { Command as Command118 } from "commander";
|
|
14211
|
-
import * as
|
|
14212
|
-
import * as
|
|
14258
|
+
import * as fs48 from "fs";
|
|
14259
|
+
import * as path77 from "path";
|
|
14213
14260
|
function telemetryFilePath(cwd) {
|
|
14214
|
-
return
|
|
14261
|
+
return path77.join(cwd, ".harness", "telemetry.json");
|
|
14215
14262
|
}
|
|
14216
14263
|
function writeTelemetryFile(cwd, data) {
|
|
14217
14264
|
const filePath = telemetryFilePath(cwd);
|
|
14218
|
-
const dir =
|
|
14219
|
-
|
|
14220
|
-
|
|
14265
|
+
const dir = path77.dirname(filePath);
|
|
14266
|
+
fs48.mkdirSync(dir, { recursive: true });
|
|
14267
|
+
fs48.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
14221
14268
|
}
|
|
14222
14269
|
var IDENTITY_FIELDS = ["project", "team", "alias"];
|
|
14223
14270
|
function applyIdentityFields(opts, identity) {
|
|
@@ -14334,17 +14381,17 @@ init_dist3();
|
|
|
14334
14381
|
import { Command as Command120 } from "commander";
|
|
14335
14382
|
|
|
14336
14383
|
// src/bin/command-telemetry.ts
|
|
14337
|
-
import { existsSync as
|
|
14338
|
-
import { join as
|
|
14384
|
+
import { existsSync as existsSync44, readFileSync as readFileSync32, mkdirSync as mkdirSync23, appendFileSync, writeFileSync as writeFileSync29 } from "fs";
|
|
14385
|
+
import { join as join64, parse as parsePath, resolve as resolve46 } from "path";
|
|
14339
14386
|
import { spawn as spawn2 } from "child_process";
|
|
14340
14387
|
var POSTHOG_API_KEY = "phc_wNTdCMcfJXZPgdNeDociZW6vwoGGo4nb7vqEfWThFfsG";
|
|
14341
14388
|
var EXCLUDED_COMMANDS = /* @__PURE__ */ new Set(["help", "completion"]);
|
|
14342
14389
|
function findProjectRoot(cwd) {
|
|
14343
|
-
let dir =
|
|
14390
|
+
let dir = resolve46(cwd);
|
|
14344
14391
|
const { root } = parsePath(dir);
|
|
14345
14392
|
while (dir !== root) {
|
|
14346
|
-
if (
|
|
14347
|
-
dir =
|
|
14393
|
+
if (existsSync44(join64(dir, "harness.config.json"))) return dir;
|
|
14394
|
+
dir = resolve46(dir, "..");
|
|
14348
14395
|
}
|
|
14349
14396
|
return cwd;
|
|
14350
14397
|
}
|
|
@@ -14381,8 +14428,8 @@ function resolveCommandName(cmd) {
|
|
|
14381
14428
|
}
|
|
14382
14429
|
function writeCommandRecordSync(cwd, command, duration, outcome) {
|
|
14383
14430
|
try {
|
|
14384
|
-
const metricsDir =
|
|
14385
|
-
|
|
14431
|
+
const metricsDir = join64(cwd, ".harness", "metrics");
|
|
14432
|
+
mkdirSync23(metricsDir, { recursive: true });
|
|
14386
14433
|
const record = {
|
|
14387
14434
|
skill: command,
|
|
14388
14435
|
session: `cli-${Date.now()}`,
|
|
@@ -14391,14 +14438,14 @@ function writeCommandRecordSync(cwd, command, duration, outcome) {
|
|
|
14391
14438
|
outcome,
|
|
14392
14439
|
phasesReached: []
|
|
14393
14440
|
};
|
|
14394
|
-
const adoptionFile =
|
|
14441
|
+
const adoptionFile = join64(metricsDir, "adoption.jsonl");
|
|
14395
14442
|
appendFileSync(adoptionFile, JSON.stringify(record) + "\n");
|
|
14396
14443
|
} catch {
|
|
14397
14444
|
}
|
|
14398
14445
|
}
|
|
14399
14446
|
function isTelemetryDisabledByConfig(cwd) {
|
|
14400
|
-
const configPath =
|
|
14401
|
-
if (!
|
|
14447
|
+
const configPath = join64(cwd, "harness.config.json");
|
|
14448
|
+
if (!existsSync44(configPath)) return false;
|
|
14402
14449
|
try {
|
|
14403
14450
|
const config = JSON.parse(readFileSync32(configPath, "utf-8"));
|
|
14404
14451
|
return config?.telemetry?.enabled === false;
|
|
@@ -14412,11 +14459,11 @@ function isTelemetryOptedOut(cwd) {
|
|
|
14412
14459
|
}
|
|
14413
14460
|
function flushTelemetryBackground(cwd) {
|
|
14414
14461
|
try {
|
|
14415
|
-
const adoptionFile =
|
|
14416
|
-
if (!
|
|
14462
|
+
const adoptionFile = join64(cwd, ".harness", "metrics", "adoption.jsonl");
|
|
14463
|
+
if (!existsSync44(adoptionFile)) return;
|
|
14417
14464
|
if (isTelemetryOptedOut(cwd)) return;
|
|
14418
|
-
const reporterPath =
|
|
14419
|
-
if (!
|
|
14465
|
+
const reporterPath = join64(cwd, ".harness", "hooks", "telemetry-reporter.js");
|
|
14466
|
+
if (!existsSync44(reporterPath)) return;
|
|
14420
14467
|
const child = spawn2(process.execPath, [reporterPath], {
|
|
14421
14468
|
cwd,
|
|
14422
14469
|
stdio: ["pipe", "ignore", "ignore"],
|
|
@@ -14548,10 +14595,10 @@ function printResult7(result, mode, _formatter) {
|
|
|
14548
14595
|
list.push(f);
|
|
14549
14596
|
byFile.set(f.target.file, list);
|
|
14550
14597
|
}
|
|
14551
|
-
for (const [file,
|
|
14598
|
+
for (const [file, fs51] of byFile) {
|
|
14552
14599
|
console.log(`
|
|
14553
14600
|
${file}`);
|
|
14554
|
-
for (const f of
|
|
14601
|
+
for (const f of fs51) {
|
|
14555
14602
|
const nestingStr = f.target.nesting.length > 0 ? f.target.nesting.join(" > ") + " > " : "";
|
|
14556
14603
|
console.log(
|
|
14557
14604
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.framework}:${f.target.line}`
|
|
@@ -14723,15 +14770,15 @@ function createTraceabilityCommand() {
|
|
|
14723
14770
|
}
|
|
14724
14771
|
|
|
14725
14772
|
// src/commands/uninstall.ts
|
|
14726
|
-
import * as
|
|
14773
|
+
import * as path78 from "path";
|
|
14727
14774
|
import { Command as Command124 } from "commander";
|
|
14728
14775
|
async function runUninstall(skillName, options) {
|
|
14729
14776
|
const packageName = resolvePackageName(skillName);
|
|
14730
14777
|
const shortName = extractSkillName(packageName);
|
|
14731
14778
|
const globalDir = resolveGlobalSkillsDir();
|
|
14732
|
-
const skillsDir =
|
|
14733
|
-
const communityBase =
|
|
14734
|
-
const lockfilePath =
|
|
14779
|
+
const skillsDir = path78.dirname(globalDir);
|
|
14780
|
+
const communityBase = path78.join(skillsDir, "community");
|
|
14781
|
+
const lockfilePath = path78.join(communityBase, "skills-lock.json");
|
|
14735
14782
|
const lockfile = readLockfile2(lockfilePath);
|
|
14736
14783
|
const entry = lockfile.skills[packageName];
|
|
14737
14784
|
if (!entry) {
|
|
@@ -14781,8 +14828,8 @@ function createUninstallCommand() {
|
|
|
14781
14828
|
|
|
14782
14829
|
// src/commands/uninstall-constraints.ts
|
|
14783
14830
|
init_dist3();
|
|
14784
|
-
import * as
|
|
14785
|
-
import * as
|
|
14831
|
+
import * as fs49 from "fs/promises";
|
|
14832
|
+
import * as path79 from "path";
|
|
14786
14833
|
import { Command as Command125 } from "commander";
|
|
14787
14834
|
async function runUninstallConstraints(options) {
|
|
14788
14835
|
const { packageName, configPath, lockfilePath } = options;
|
|
@@ -14803,7 +14850,7 @@ async function runUninstallConstraints(options) {
|
|
|
14803
14850
|
}
|
|
14804
14851
|
let localConfig;
|
|
14805
14852
|
try {
|
|
14806
|
-
const raw = await
|
|
14853
|
+
const raw = await fs49.readFile(configPath, "utf-8");
|
|
14807
14854
|
localConfig = JSON.parse(raw);
|
|
14808
14855
|
} catch (err) {
|
|
14809
14856
|
return {
|
|
@@ -14844,7 +14891,7 @@ function createUninstallConstraintsCommand() {
|
|
|
14844
14891
|
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
14892
|
let configPath;
|
|
14846
14893
|
if (opts.config) {
|
|
14847
|
-
configPath =
|
|
14894
|
+
configPath = path79.resolve(opts.config);
|
|
14848
14895
|
} else {
|
|
14849
14896
|
const found = findConfigFile();
|
|
14850
14897
|
if (!found.ok) {
|
|
@@ -14853,8 +14900,8 @@ function createUninstallConstraintsCommand() {
|
|
|
14853
14900
|
}
|
|
14854
14901
|
configPath = found.value;
|
|
14855
14902
|
}
|
|
14856
|
-
const projectRoot2 =
|
|
14857
|
-
const lockfilePath =
|
|
14903
|
+
const projectRoot2 = path79.dirname(configPath);
|
|
14904
|
+
const lockfilePath = path79.join(projectRoot2, ".harness", "constraints.lock.json");
|
|
14858
14905
|
const result = await runUninstallConstraints({
|
|
14859
14906
|
packageName: name,
|
|
14860
14907
|
configPath,
|
|
@@ -14882,8 +14929,8 @@ function createUninstallConstraintsCommand() {
|
|
|
14882
14929
|
init_dist3();
|
|
14883
14930
|
import { Command as Command126 } from "commander";
|
|
14884
14931
|
import { execFile as execFile2, execFileSync as execFileSync10 } from "child_process";
|
|
14885
|
-
import { realpathSync, existsSync as
|
|
14886
|
-
import { dirname as
|
|
14932
|
+
import { realpathSync, existsSync as existsSync45, readFileSync as readFileSync33 } from "fs";
|
|
14933
|
+
import { dirname as dirname25, join as join67 } from "path";
|
|
14887
14934
|
import { promisify as promisify2 } from "util";
|
|
14888
14935
|
import readline4 from "readline";
|
|
14889
14936
|
import chalk20 from "chalk";
|
|
@@ -14964,24 +15011,24 @@ function prompt3(question) {
|
|
|
14964
15011
|
input: process.stdin,
|
|
14965
15012
|
output: process.stdout
|
|
14966
15013
|
});
|
|
14967
|
-
return new Promise((
|
|
15014
|
+
return new Promise((resolve49) => {
|
|
14968
15015
|
rl.question(question, (answer) => {
|
|
14969
15016
|
rl.close();
|
|
14970
|
-
|
|
15017
|
+
resolve49(answer.trim().toLowerCase());
|
|
14971
15018
|
});
|
|
14972
15019
|
});
|
|
14973
15020
|
}
|
|
14974
15021
|
function findPackageDir(resolvedBin) {
|
|
14975
|
-
let dir =
|
|
15022
|
+
let dir = dirname25(resolvedBin);
|
|
14976
15023
|
for (let i = 0; i < 6; i++) {
|
|
14977
|
-
if (
|
|
15024
|
+
if (existsSync45(join67(dir, "package.json"))) {
|
|
14978
15025
|
try {
|
|
14979
|
-
const pkg = JSON.parse(readFileSync33(
|
|
15026
|
+
const pkg = JSON.parse(readFileSync33(join67(dir, "package.json"), "utf-8"));
|
|
14980
15027
|
if (pkg?.name === CLI_PACKAGE) return dir;
|
|
14981
15028
|
} catch {
|
|
14982
15029
|
}
|
|
14983
15030
|
}
|
|
14984
|
-
const parent =
|
|
15031
|
+
const parent = dirname25(dir);
|
|
14985
15032
|
if (parent === dir) return null;
|
|
14986
15033
|
dir = parent;
|
|
14987
15034
|
}
|
|
@@ -15021,7 +15068,7 @@ function findAllInstalls() {
|
|
|
15021
15068
|
if (!packageDir) continue;
|
|
15022
15069
|
let version = null;
|
|
15023
15070
|
try {
|
|
15024
|
-
const pkg = JSON.parse(readFileSync33(
|
|
15071
|
+
const pkg = JSON.parse(readFileSync33(join67(packageDir, "package.json"), "utf-8"));
|
|
15025
15072
|
if (typeof pkg?.version === "string") version = pkg.version;
|
|
15026
15073
|
} catch {
|
|
15027
15074
|
}
|
|
@@ -15114,10 +15161,10 @@ async function ensureTelemetryIfNeeded() {
|
|
|
15114
15161
|
}
|
|
15115
15162
|
function refreshHooks() {
|
|
15116
15163
|
const cwd = process.cwd();
|
|
15117
|
-
const configPath =
|
|
15118
|
-
if (!
|
|
15164
|
+
const configPath = join67(cwd, "harness.config.json");
|
|
15165
|
+
if (!existsSync45(configPath)) return;
|
|
15119
15166
|
let profile = "standard";
|
|
15120
|
-
const profilePath =
|
|
15167
|
+
const profilePath = join67(cwd, ".harness", "hooks", "profile.json");
|
|
15121
15168
|
try {
|
|
15122
15169
|
const data = JSON.parse(readFileSync33(profilePath, "utf-8"));
|
|
15123
15170
|
if (data.profile && ["minimal", "standard", "strict"].includes(data.profile)) {
|
|
@@ -15260,7 +15307,7 @@ function createUpdateCommand() {
|
|
|
15260
15307
|
// src/commands/usage.ts
|
|
15261
15308
|
import { Command as Command127 } from "commander";
|
|
15262
15309
|
async function loadAndPriceRecords(cwd, includeClaudeSessions = false) {
|
|
15263
|
-
const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-
|
|
15310
|
+
const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-LE5AU7FR.js");
|
|
15264
15311
|
const records = readCostRecords(cwd);
|
|
15265
15312
|
if (includeClaudeSessions) {
|
|
15266
15313
|
const ccRecords = parseCCRecords();
|
|
@@ -15353,7 +15400,7 @@ function registerDailyCommand(usage) {
|
|
|
15353
15400
|
else logger.info("No usage data found. Run some harness sessions first.");
|
|
15354
15401
|
return;
|
|
15355
15402
|
}
|
|
15356
|
-
const { aggregateByDay } = await import("./dist-
|
|
15403
|
+
const { aggregateByDay } = await import("./dist-LE5AU7FR.js");
|
|
15357
15404
|
const limited = aggregateByDay(records).slice(0, days);
|
|
15358
15405
|
if (globalOpts.json) {
|
|
15359
15406
|
console.log(
|
|
@@ -15382,7 +15429,7 @@ function registerSessionsCommand(usage) {
|
|
|
15382
15429
|
}
|
|
15383
15430
|
return;
|
|
15384
15431
|
}
|
|
15385
|
-
const { aggregateBySession } = await import("./dist-
|
|
15432
|
+
const { aggregateBySession } = await import("./dist-LE5AU7FR.js");
|
|
15386
15433
|
const sessionData = aggregateBySession(records);
|
|
15387
15434
|
const limited = sessionData.slice(0, limit);
|
|
15388
15435
|
if (globalOpts.json) {
|
|
@@ -15462,7 +15509,7 @@ function registerSessionCommand(usage) {
|
|
|
15462
15509
|
cwd,
|
|
15463
15510
|
globalOpts.includeClaudeSessions
|
|
15464
15511
|
);
|
|
15465
|
-
const { aggregateBySession } = await import("./dist-
|
|
15512
|
+
const { aggregateBySession } = await import("./dist-LE5AU7FR.js");
|
|
15466
15513
|
const sessionData = aggregateBySession(records);
|
|
15467
15514
|
const match = sessionData.find((s) => s.sessionId === id);
|
|
15468
15515
|
if (!match) {
|
|
@@ -15497,7 +15544,7 @@ function registerLatestCommand(usage) {
|
|
|
15497
15544
|
}
|
|
15498
15545
|
return;
|
|
15499
15546
|
}
|
|
15500
|
-
const { aggregateBySession } = await import("./dist-
|
|
15547
|
+
const { aggregateBySession } = await import("./dist-LE5AU7FR.js");
|
|
15501
15548
|
const sessionData = aggregateBySession(records);
|
|
15502
15549
|
const latest = sessionData[0];
|
|
15503
15550
|
if (!latest) {
|
|
@@ -15539,8 +15586,8 @@ function createUsageCommand() {
|
|
|
15539
15586
|
init_dist3();
|
|
15540
15587
|
init_dist3();
|
|
15541
15588
|
import { Command as Command128 } from "commander";
|
|
15542
|
-
import * as
|
|
15543
|
-
import * as
|
|
15589
|
+
import * as fs50 from "fs";
|
|
15590
|
+
import * as path80 from "path";
|
|
15544
15591
|
import { execFileSync as execFileSync11 } from "child_process";
|
|
15545
15592
|
async function runValidate(options) {
|
|
15546
15593
|
const configResult = resolveConfig(options.configPath);
|
|
@@ -15548,7 +15595,7 @@ async function runValidate(options) {
|
|
|
15548
15595
|
return configResult;
|
|
15549
15596
|
}
|
|
15550
15597
|
const config = configResult.value;
|
|
15551
|
-
const cwd = options.cwd ?? (options.configPath ?
|
|
15598
|
+
const cwd = options.cwd ?? (options.configPath ? path80.dirname(path80.resolve(options.configPath)) : process.cwd());
|
|
15552
15599
|
const result = {
|
|
15553
15600
|
valid: true,
|
|
15554
15601
|
checks: {
|
|
@@ -15558,7 +15605,7 @@ async function runValidate(options) {
|
|
|
15558
15605
|
},
|
|
15559
15606
|
issues: []
|
|
15560
15607
|
};
|
|
15561
|
-
const agentsMapPath =
|
|
15608
|
+
const agentsMapPath = path80.resolve(cwd, config.agentsMapPath);
|
|
15562
15609
|
const agentsResult = await validateAgentsMap(agentsMapPath);
|
|
15563
15610
|
if (agentsResult.ok) {
|
|
15564
15611
|
result.checks.agentsMap = true;
|
|
@@ -15655,9 +15702,9 @@ async function runValidate(options) {
|
|
|
15655
15702
|
}
|
|
15656
15703
|
});
|
|
15657
15704
|
}
|
|
15658
|
-
const gitattributesPath =
|
|
15659
|
-
if (
|
|
15660
|
-
const gitattributesContent =
|
|
15705
|
+
const gitattributesPath = path80.join(cwd, ".gitattributes");
|
|
15706
|
+
if (fs50.existsSync(gitattributesPath)) {
|
|
15707
|
+
const gitattributesContent = fs50.readFileSync(gitattributesPath, "utf-8");
|
|
15661
15708
|
let driverConfigured = false;
|
|
15662
15709
|
try {
|
|
15663
15710
|
const out = execFileSync11("git", ["config", "--get", "merge.ours.driver"], {
|
|
@@ -15681,10 +15728,10 @@ async function runValidate(options) {
|
|
|
15681
15728
|
}
|
|
15682
15729
|
}
|
|
15683
15730
|
if (detectRoadmapStorageMode(cwd) === "sharded") {
|
|
15684
|
-
const shardDir =
|
|
15731
|
+
const shardDir = path80.join(cwd, "docs", "roadmap.d");
|
|
15685
15732
|
const regenerated = await regenerate(shardDir, createNodeRoadmapIO());
|
|
15686
|
-
const aggregatePath =
|
|
15687
|
-
const committedAggregate =
|
|
15733
|
+
const aggregatePath = path80.join(cwd, "docs", "roadmap.md");
|
|
15734
|
+
const committedAggregate = fs50.existsSync(aggregatePath) ? fs50.readFileSync(aggregatePath, "utf-8") : null;
|
|
15688
15735
|
const drift = checkRoadmapAggregateDrift({
|
|
15689
15736
|
shardDirExists: true,
|
|
15690
15737
|
committedAggregate,
|
|
@@ -15702,9 +15749,9 @@ async function runValidate(options) {
|
|
|
15702
15749
|
result.checks.roadmapAggregateDrift = true;
|
|
15703
15750
|
}
|
|
15704
15751
|
}
|
|
15705
|
-
const roadmapPath =
|
|
15706
|
-
if (
|
|
15707
|
-
const parsed = parseRoadmap(
|
|
15752
|
+
const roadmapPath = path80.join(cwd, "docs", "roadmap.md");
|
|
15753
|
+
if (fs50.existsSync(roadmapPath)) {
|
|
15754
|
+
const parsed = parseRoadmap(fs50.readFileSync(roadmapPath, "utf-8"));
|
|
15708
15755
|
if (parsed.ok) {
|
|
15709
15756
|
const findings = checkRoadmapHealth(parsed.value);
|
|
15710
15757
|
result.checks.roadmapHealth = !findings.some((f) => f.severity === "error");
|
|
@@ -15846,11 +15893,11 @@ function resolveValidateMode(globalOpts) {
|
|
|
15846
15893
|
return OutputMode.TEXT;
|
|
15847
15894
|
}
|
|
15848
15895
|
async function printCrossCheckWarnings(mode) {
|
|
15849
|
-
const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-
|
|
15896
|
+
const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-HKKQA7K5.js");
|
|
15850
15897
|
const cwd = process.cwd();
|
|
15851
15898
|
const crossResult = await runCrossCheck2({
|
|
15852
|
-
specsDir:
|
|
15853
|
-
plansDir:
|
|
15899
|
+
specsDir: path80.join(cwd, "docs", "specs"),
|
|
15900
|
+
plansDir: path80.join(cwd, "docs", "plans"),
|
|
15854
15901
|
projectPath: cwd
|
|
15855
15902
|
});
|
|
15856
15903
|
if (!crossResult.ok || crossResult.value.warnings === 0) return;
|