@harness-engineering/cli 2.8.0 → 3.0.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-audit-harness-strength/SKILL.md +188 -0
- package/dist/agents/skills/claude-code/harness-audit-harness-strength/skill.yaml +54 -0
- package/dist/agents/skills/claude-code/harness-autopilot/SKILL.md +2 -0
- package/dist/agents/skills/claude-code/harness-brainstorming/SKILL.md +28 -14
- package/dist/agents/skills/claude-code/harness-execution/SKILL.md +1 -1
- package/dist/agents/skills/claude-code/harness-git-workflow/SKILL.md +2 -0
- package/dist/agents/skills/claude-code/harness-ideate/SKILL.md +2 -0
- package/dist/agents/skills/claude-code/harness-knowledge-pipeline/SKILL.md +2 -0
- package/dist/agents/skills/claude-code/harness-roadmap/SKILL.md +62 -4
- package/dist/agents/skills/claude-code/harness-roadmap-pilot/SKILL.md +2 -0
- package/dist/agents/skills/claude-code/harness-test-advisor/SKILL.md +80 -2
- package/dist/agents/skills/claude-code/harness-test-advisor/skill.yaml +4 -1
- package/dist/agents/skills/claude-code/initialize-harness-project/SKILL.md +27 -25
- package/dist/agents/skills/claude-code/initialize-harness-project/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/outcome-eval/SKILL.md +97 -0
- package/dist/agents/skills/claude-code/outcome-eval/skill.yaml +51 -0
- package/dist/agents/skills/codex/harness-audit-harness-strength/SKILL.md +188 -0
- package/dist/agents/skills/codex/harness-audit-harness-strength/skill.yaml +54 -0
- package/dist/agents/skills/codex/harness-autopilot/SKILL.md +2 -0
- package/dist/agents/skills/codex/harness-brainstorming/SKILL.md +28 -14
- package/dist/agents/skills/codex/harness-execution/SKILL.md +1 -1
- package/dist/agents/skills/codex/harness-git-workflow/SKILL.md +2 -0
- package/dist/agents/skills/codex/harness-ideate/SKILL.md +2 -0
- package/dist/agents/skills/codex/harness-knowledge-pipeline/SKILL.md +2 -0
- package/dist/agents/skills/codex/harness-roadmap/SKILL.md +62 -4
- package/dist/agents/skills/codex/harness-roadmap-pilot/SKILL.md +2 -0
- package/dist/agents/skills/codex/harness-test-advisor/SKILL.md +80 -2
- package/dist/agents/skills/codex/harness-test-advisor/skill.yaml +4 -1
- package/dist/agents/skills/codex/initialize-harness-project/SKILL.md +27 -25
- package/dist/agents/skills/codex/initialize-harness-project/skill.yaml +1 -0
- package/dist/agents/skills/codex/outcome-eval/SKILL.md +97 -0
- package/dist/agents/skills/codex/outcome-eval/skill.yaml +51 -0
- package/dist/agents/skills/cursor/harness-audit-harness-strength/SKILL.md +188 -0
- package/dist/agents/skills/cursor/harness-audit-harness-strength/skill.yaml +54 -0
- package/dist/agents/skills/cursor/harness-autopilot/SKILL.md +2 -0
- package/dist/agents/skills/cursor/harness-brainstorming/SKILL.md +28 -14
- package/dist/agents/skills/cursor/harness-execution/SKILL.md +1 -1
- package/dist/agents/skills/cursor/harness-git-workflow/SKILL.md +2 -0
- package/dist/agents/skills/cursor/harness-ideate/SKILL.md +2 -0
- package/dist/agents/skills/cursor/harness-knowledge-pipeline/SKILL.md +2 -0
- package/dist/agents/skills/cursor/harness-roadmap/SKILL.md +62 -4
- package/dist/agents/skills/cursor/harness-roadmap-pilot/SKILL.md +2 -0
- package/dist/agents/skills/cursor/harness-test-advisor/SKILL.md +80 -2
- package/dist/agents/skills/cursor/harness-test-advisor/skill.yaml +4 -1
- package/dist/agents/skills/cursor/initialize-harness-project/SKILL.md +27 -25
- package/dist/agents/skills/cursor/initialize-harness-project/skill.yaml +1 -0
- package/dist/agents/skills/cursor/outcome-eval/SKILL.md +97 -0
- package/dist/agents/skills/cursor/outcome-eval/skill.yaml +51 -0
- package/dist/agents/skills/gemini-cli/harness-audit-harness-strength/SKILL.md +188 -0
- package/dist/agents/skills/gemini-cli/harness-audit-harness-strength/skill.yaml +54 -0
- package/dist/agents/skills/gemini-cli/harness-autopilot/SKILL.md +2 -0
- package/dist/agents/skills/gemini-cli/harness-brainstorming/SKILL.md +28 -14
- package/dist/agents/skills/gemini-cli/harness-execution/SKILL.md +1 -1
- package/dist/agents/skills/gemini-cli/harness-git-workflow/SKILL.md +2 -0
- package/dist/agents/skills/gemini-cli/harness-ideate/SKILL.md +2 -0
- package/dist/agents/skills/gemini-cli/harness-knowledge-pipeline/SKILL.md +2 -0
- package/dist/agents/skills/gemini-cli/harness-roadmap/SKILL.md +62 -4
- package/dist/agents/skills/gemini-cli/harness-roadmap-pilot/SKILL.md +2 -0
- package/dist/agents/skills/gemini-cli/harness-test-advisor/SKILL.md +80 -2
- package/dist/agents/skills/gemini-cli/harness-test-advisor/skill.yaml +4 -1
- package/dist/agents/skills/gemini-cli/initialize-harness-project/SKILL.md +27 -25
- package/dist/agents/skills/gemini-cli/initialize-harness-project/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/outcome-eval/SKILL.md +97 -0
- package/dist/agents/skills/gemini-cli/outcome-eval/skill.yaml +51 -0
- package/dist/agents/skills/tests/harness-test-advisor.test.ts +94 -0
- package/dist/{agents-md-WMJO5HSM.js → agents-md-XOJE5ETA.js} +2 -2
- package/dist/{architecture-EVYVGPRZ.js → architecture-JEZPJ725.js} +3 -3
- package/dist/{assess-project-KZG563SO.js → assess-project-RHXOQNMM.js} +1 -1
- package/dist/bin/harness-mcp.js +13 -13
- package/dist/bin/harness.js +22 -22
- package/dist/{check-phase-gate-MK364VXS.js → check-phase-gate-GNDOMS35.js} +3 -3
- package/dist/{chunk-VBNJAKUH.js → chunk-3ISHDWO7.js} +1 -1
- package/dist/{chunk-UJFNIWC7.js → chunk-527G27VI.js} +2 -2
- package/dist/{chunk-5XL5QJ5B.js → chunk-6WQCLCBO.js} +1 -1
- package/dist/{chunk-UXPWSV3B.js → chunk-BYVT5LVO.js} +3 -3
- package/dist/{chunk-HRSE6IQD.js → chunk-DE5U6KOL.js} +1 -1
- package/dist/{chunk-B7KMTHN3.js → chunk-GGKRA7A7.js} +1 -1
- package/dist/{chunk-STESM73J.js → chunk-GSP2XIVS.js} +1 -1
- package/dist/{chunk-KBCPELBC.js → chunk-HEIMJJI4.js} +2 -2
- package/dist/{chunk-KI6PHH6Q.js → chunk-HWTUUQOW.js} +3 -3
- package/dist/{chunk-2SNRCAC3.js → chunk-KAJOS3BA.js} +2 -2
- package/dist/{chunk-C7J55C6E.js → chunk-MGUPSE6D.js} +5 -5
- package/dist/{chunk-GOHWCHCS.js → chunk-N24HCQA4.js} +2243 -776
- package/dist/{chunk-EEFRPEWT.js → chunk-NWLGL3IR.js} +1 -1
- package/dist/{chunk-UUO4K7NX.js → chunk-ONCPJGHY.js} +6 -6
- package/dist/{chunk-X4EFYVCZ.js → chunk-PC2R5RUJ.js} +1 -1
- package/dist/{chunk-7EQAZKJ5.js → chunk-QBC7DI4Q.js} +1 -1
- package/dist/{chunk-UBZQE3JN.js → chunk-UX3PQKVZ.js} +7 -2
- package/dist/{chunk-CAQPSFPT.js → chunk-WWEVS7RO.js} +453 -91
- package/dist/{chunk-4BDOSCNY.js → chunk-XFU6SB2Q.js} +1015 -648
- package/dist/{chunk-LADPYELQ.js → chunk-XX4OLNWQ.js} +2 -2
- package/dist/{chunk-FZ5MBXEG.js → chunk-YQSYCBAH.js} +6 -6
- package/dist/{chunk-ACOOG3UW.js → chunk-Z4AWEIBY.js} +3 -3
- package/dist/{ci-workflow-IW2H5DC4.js → ci-workflow-ORZ4F45F.js} +2 -2
- package/dist/{dist-JCFK263L.js → dist-LHINSVK4.js} +85 -1
- package/dist/{docs-7CP6VV42.js → docs-AU3DXFFO.js} +3 -3
- package/dist/{engine-MKJEBWU7.js → engine-6GPWOYQH.js} +2 -2
- package/dist/{entropy-6JOZJYG7.js → entropy-VLKDRMRT.js} +2 -2
- package/dist/{feedback-T65AEJEG.js → feedback-YXYY44ZC.js} +1 -1
- package/dist/{generate-agent-definitions-4VI4YTO2.js → generate-agent-definitions-XIHQPKG3.js} +3 -3
- package/dist/hooks/cost-tracker.js +29 -6
- package/dist/index.js +22 -22
- package/dist/{loader-AWTHHE3A.js → loader-YAN56MT3.js} +2 -2
- package/dist/{mcp-JBIBINO2.js → mcp-RK3SFVYB.js} +13 -13
- package/dist/{performance-NCOUDOMW.js → performance-64DPTLLQ.js} +3 -3
- package/dist/{review-pipeline-YIC6JPMY.js → review-pipeline-NENG6LW7.js} +2 -2
- package/dist/{runtime-5MHV4UEE.js → runtime-FFIIRT2T.js} +2 -2
- package/dist/{security-VZPJNIDP.js → security-3PIBN35B.js} +1 -1
- package/dist/templates/basic/harness.config.json.hbs +34 -0
- package/dist/templates/ci/README.md +86 -0
- package/dist/templates/ci/required-review.ruleset.json +20 -0
- package/dist/templates/ci/required-review.yml.hbs +57 -0
- package/dist/templates/ci/template.json +5 -0
- package/dist/templates/intermediate/harness.config.json.hbs +36 -0
- package/dist/templates/orchestrator/harness.orchestrator.md +9 -0
- package/dist/{tool-tiers-B7JC2XC3.js → tool-tiers-OKK5FE57.js} +2 -0
- package/dist/{validate-7BE4VTFI.js → validate-7IAF2ES3.js} +3 -3
- package/dist/{validate-cross-check-F7GEBRDH.js → validate-cross-check-QPSAOZKD.js} +2 -2
- package/package.json +7 -7
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
generateRuntime
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-6WQCLCBO.js";
|
|
4
4
|
import {
|
|
5
5
|
generateAgentsMd
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-QBC7DI4Q.js";
|
|
7
7
|
import {
|
|
8
8
|
generateCIWorkflow
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-GSP2XIVS.js";
|
|
10
10
|
import {
|
|
11
11
|
createGenerateAgentDefinitionsCommand,
|
|
12
12
|
generateAgentDefinitions
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-NWLGL3IR.js";
|
|
14
14
|
import {
|
|
15
15
|
toKebabCase
|
|
16
16
|
} from "./chunk-KET4QQZB.js";
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
} from "./chunk-2N4OENGE.js";
|
|
21
21
|
import {
|
|
22
22
|
TemplateEngine
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-UX3PQKVZ.js";
|
|
24
24
|
import {
|
|
25
25
|
generate,
|
|
26
26
|
validate
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
import {
|
|
32
32
|
listPersonas,
|
|
33
33
|
loadPersona
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-PC2R5RUJ.js";
|
|
35
35
|
import {
|
|
36
36
|
runPersona
|
|
37
37
|
} from "./chunk-GISMXMVL.js";
|
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
OutputMode,
|
|
47
47
|
createCheckPhaseGateCommand,
|
|
48
48
|
findFiles
|
|
49
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-KAJOS3BA.js";
|
|
50
50
|
import {
|
|
51
51
|
appendFrameworkAgents,
|
|
52
52
|
captureHealthSnapshot,
|
|
@@ -74,7 +74,7 @@ import {
|
|
|
74
74
|
runSecurityCraft,
|
|
75
75
|
runSpecCraft,
|
|
76
76
|
runTestCraft
|
|
77
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-WWEVS7RO.js";
|
|
78
78
|
import {
|
|
79
79
|
VALID_PLATFORMS
|
|
80
80
|
} from "./chunk-3ISINLYT.js";
|
|
@@ -99,7 +99,7 @@ import {
|
|
|
99
99
|
BranchingConfigSchema,
|
|
100
100
|
findConfigFile,
|
|
101
101
|
resolveConfig
|
|
102
|
-
} from "./chunk-
|
|
102
|
+
} from "./chunk-GGKRA7A7.js";
|
|
103
103
|
import {
|
|
104
104
|
logger
|
|
105
105
|
} from "./chunk-EBJQ6N4M.js";
|
|
@@ -114,10 +114,13 @@ import {
|
|
|
114
114
|
BlueprintGenerator,
|
|
115
115
|
BundleSchema,
|
|
116
116
|
CINotifier,
|
|
117
|
+
CI_ASSESSMENTS,
|
|
117
118
|
CriticalPathResolver,
|
|
118
119
|
GitHubIssuesSyncAdapter,
|
|
120
|
+
HarnessStrengthAuditor,
|
|
119
121
|
PredictionEngine,
|
|
120
122
|
ProjectScanner,
|
|
123
|
+
RUNNER_PRESETS,
|
|
121
124
|
SecurityScanner,
|
|
122
125
|
SecurityTimelineManager,
|
|
123
126
|
SpecImpactEstimator,
|
|
@@ -128,6 +131,7 @@ import {
|
|
|
128
131
|
assembleCandidateReport,
|
|
129
132
|
assembleReport,
|
|
130
133
|
checkDocCoverage,
|
|
134
|
+
checkRoadmapHealth,
|
|
131
135
|
checkTaint,
|
|
132
136
|
clearTaint,
|
|
133
137
|
computeHotspots,
|
|
@@ -179,6 +183,7 @@ import {
|
|
|
179
183
|
resolveStreamPath,
|
|
180
184
|
runAll,
|
|
181
185
|
runCIChecks,
|
|
186
|
+
runCiReview,
|
|
182
187
|
runPulse,
|
|
183
188
|
runReviewPipeline,
|
|
184
189
|
scanForInjection,
|
|
@@ -194,7 +199,7 @@ import {
|
|
|
194
199
|
validateStrategy,
|
|
195
200
|
writeConfig,
|
|
196
201
|
writeLockfile
|
|
197
|
-
} from "./chunk-
|
|
202
|
+
} from "./chunk-N24HCQA4.js";
|
|
198
203
|
import {
|
|
199
204
|
CLI_VERSION
|
|
200
205
|
} from "./chunk-BM3PWGXQ.js";
|
|
@@ -231,7 +236,7 @@ import {
|
|
|
231
236
|
} from "./chunk-KFQGP6VL.js";
|
|
232
237
|
|
|
233
238
|
// src/index.ts
|
|
234
|
-
import { Command as
|
|
239
|
+
import { Command as Command122 } from "commander";
|
|
235
240
|
|
|
236
241
|
// src/commands/add.ts
|
|
237
242
|
init_dist3();
|
|
@@ -420,7 +425,7 @@ function registerSkillsCommand(adoption) {
|
|
|
420
425
|
const globalOpts = cmd.optsWithGlobals();
|
|
421
426
|
const limit = Math.max(parseInt(opts.limit, 10) || 20, 1);
|
|
422
427
|
const cwd = process.cwd();
|
|
423
|
-
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-
|
|
428
|
+
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-LHINSVK4.js");
|
|
424
429
|
const records = readAdoptionRecords(cwd);
|
|
425
430
|
if (records.length === 0) {
|
|
426
431
|
if (globalOpts.json) {
|
|
@@ -456,7 +461,7 @@ function registerRecentCommand(adoption) {
|
|
|
456
461
|
const globalOpts = cmd.optsWithGlobals();
|
|
457
462
|
const limit = Math.max(parseInt(opts.limit, 10) || 20, 1);
|
|
458
463
|
const cwd = process.cwd();
|
|
459
|
-
const { readAdoptionRecords } = await import("./dist-
|
|
464
|
+
const { readAdoptionRecords } = await import("./dist-LHINSVK4.js");
|
|
460
465
|
const records = readAdoptionRecords(cwd);
|
|
461
466
|
if (records.length === 0) {
|
|
462
467
|
if (globalOpts.json) {
|
|
@@ -534,7 +539,7 @@ function registerSkillCommand(adoption) {
|
|
|
534
539
|
adoption.command("skill <name>").description("Show detail for a specific skill").action(async (name, _opts, cmd) => {
|
|
535
540
|
const globalOpts = cmd.optsWithGlobals();
|
|
536
541
|
const cwd = process.cwd();
|
|
537
|
-
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-
|
|
542
|
+
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-LHINSVK4.js");
|
|
538
543
|
const records = readAdoptionRecords(cwd);
|
|
539
544
|
const skillRecords = records.filter((r) => r.skill === name);
|
|
540
545
|
if (skillRecords.length === 0) {
|
|
@@ -1664,9 +1669,9 @@ ${name} (${count} finding${count === 1 ? "" : "s"})`);
|
|
|
1664
1669
|
}
|
|
1665
1670
|
function printAnatomyFindings(findings, verbose) {
|
|
1666
1671
|
const byFile = groupByFile(findings, (f) => f.file);
|
|
1667
|
-
for (const [file,
|
|
1672
|
+
for (const [file, fs49] of byFile) {
|
|
1668
1673
|
console.log(` ${file}`);
|
|
1669
|
-
for (const f of
|
|
1674
|
+
for (const f of fs49) {
|
|
1670
1675
|
const line = f.line !== null && f.line !== void 0 ? `:${f.line}` : "";
|
|
1671
1676
|
console.log(` ${f.code} [${f.severity}] line${line}: ${f.message}`);
|
|
1672
1677
|
if (verbose) {
|
|
@@ -1677,9 +1682,9 @@ function printAnatomyFindings(findings, verbose) {
|
|
|
1677
1682
|
}
|
|
1678
1683
|
function printCraftFindings(findings, verbose) {
|
|
1679
1684
|
const byFile = groupByFile(findings, (f) => f.target.file);
|
|
1680
|
-
for (const [file,
|
|
1685
|
+
for (const [file, fs49] of byFile) {
|
|
1681
1686
|
console.log(` ${file}`);
|
|
1682
|
-
for (const f of
|
|
1687
|
+
for (const f of fs49) {
|
|
1683
1688
|
const line = f.target.line !== void 0 ? `:${f.target.line}` : "";
|
|
1684
1689
|
const sev = craftTierToSeverity(f.tier);
|
|
1685
1690
|
console.log(
|
|
@@ -1693,9 +1698,9 @@ function printCraftFindings(findings, verbose) {
|
|
|
1693
1698
|
}
|
|
1694
1699
|
function printDriftFindings(findings, verbose) {
|
|
1695
1700
|
const byFile = groupByFile(findings, (f) => f.file);
|
|
1696
|
-
for (const [file,
|
|
1701
|
+
for (const [file, fs49] of byFile) {
|
|
1697
1702
|
console.log(` ${file}`);
|
|
1698
|
-
for (const f of
|
|
1703
|
+
for (const f of fs49) {
|
|
1699
1704
|
const line = f.line !== null && f.line !== void 0 ? `:${f.line}` : "";
|
|
1700
1705
|
console.log(` ${f.code} [${f.severity}] line${line}: ${f.message}`);
|
|
1701
1706
|
if (verbose) {
|
|
@@ -1706,9 +1711,9 @@ function printDriftFindings(findings, verbose) {
|
|
|
1706
1711
|
}
|
|
1707
1712
|
function printBrandFindings(findings, verbose) {
|
|
1708
1713
|
const byFile = groupByFile(findings, (f) => f.file);
|
|
1709
|
-
for (const [file,
|
|
1714
|
+
for (const [file, fs49] of byFile) {
|
|
1710
1715
|
console.log(` ${file}`);
|
|
1711
|
-
for (const f of
|
|
1716
|
+
for (const f of fs49) {
|
|
1712
1717
|
const line = f.line !== null && f.line !== void 0 ? `:${f.line}` : "";
|
|
1713
1718
|
console.log(` ${f.code} [${f.severity}] line${line}: ${f.message}`);
|
|
1714
1719
|
if (verbose) {
|
|
@@ -1877,14 +1882,90 @@ function createCheckDocsCommand() {
|
|
|
1877
1882
|
return command;
|
|
1878
1883
|
}
|
|
1879
1884
|
|
|
1880
|
-
// src/commands/check-
|
|
1885
|
+
// src/commands/check-harness-strength.ts
|
|
1881
1886
|
init_dist3();
|
|
1882
1887
|
import { Command as Command14 } from "commander";
|
|
1883
1888
|
import * as path8 from "path";
|
|
1889
|
+
var SEVERITY_RANK = { error: 3, warning: 2, info: 1 };
|
|
1890
|
+
function runCheckHarnessStrength(cwd, options) {
|
|
1891
|
+
const projectRoot2 = path8.resolve(cwd);
|
|
1892
|
+
const auditor = new HarnessStrengthAuditor();
|
|
1893
|
+
const result = auditor.audit(projectRoot2, options.mode ? { mode: options.mode } : {});
|
|
1894
|
+
if (!result.ok) return result;
|
|
1895
|
+
const audit = result.value;
|
|
1896
|
+
const threshold = options.severity ?? "warning";
|
|
1897
|
+
const thresholdRank = SEVERITY_RANK[threshold];
|
|
1898
|
+
const filtered = audit.findings.filter((f) => SEVERITY_RANK[f.severity] >= thresholdRank);
|
|
1899
|
+
const hasErrors = filtered.some((f) => f.severity === "error");
|
|
1900
|
+
return Ok({ valid: !hasErrors, audit, filtered });
|
|
1901
|
+
}
|
|
1902
|
+
async function runCheckHarnessStrengthAction(opts, globalOpts) {
|
|
1903
|
+
const outMode = globalOpts.json ? OutputMode.JSON : globalOpts.quiet ? OutputMode.QUIET : globalOpts.verbose ? OutputMode.VERBOSE : OutputMode.TEXT;
|
|
1904
|
+
const formatter = new OutputFormatter(outMode);
|
|
1905
|
+
const resolvedMode = opts.mode ?? (opts.toolkit ? "toolkit" : opts.adopter ? "adopter" : void 0);
|
|
1906
|
+
const result = runCheckHarnessStrength(process.cwd(), {
|
|
1907
|
+
severity: opts.severity,
|
|
1908
|
+
...resolvedMode !== void 0 && { mode: resolvedMode },
|
|
1909
|
+
...opts.reportOnly !== void 0 && { reportOnly: opts.reportOnly }
|
|
1910
|
+
});
|
|
1911
|
+
if (!result.ok) {
|
|
1912
|
+
if (outMode === OutputMode.JSON) {
|
|
1913
|
+
console.log(JSON.stringify({ error: result.error.message }));
|
|
1914
|
+
} else {
|
|
1915
|
+
logger.error(result.error.message);
|
|
1916
|
+
}
|
|
1917
|
+
process.exit(ExitCode.ERROR);
|
|
1918
|
+
}
|
|
1919
|
+
const { valid, audit, filtered } = result.value;
|
|
1920
|
+
if (outMode === OutputMode.JSON) {
|
|
1921
|
+
console.log(JSON.stringify(audit, null, 2));
|
|
1922
|
+
process.exit(opts.reportOnly || valid ? ExitCode.SUCCESS : ExitCode.VALIDATION_FAILED);
|
|
1923
|
+
}
|
|
1924
|
+
const issues = filtered.map((f) => ({
|
|
1925
|
+
file: f.line !== void 0 ? `${f.file}:${f.line}` : f.file,
|
|
1926
|
+
message: `[${f.id}] ${f.severity.toUpperCase()} ${f.message} -> ${f.remediation}`
|
|
1927
|
+
}));
|
|
1928
|
+
const header = formatter.formatSummary(
|
|
1929
|
+
`harness strength (${audit.mode})`,
|
|
1930
|
+
`${audit.score}/100 (${audit.tier})`,
|
|
1931
|
+
valid
|
|
1932
|
+
);
|
|
1933
|
+
if (header) console.log(header);
|
|
1934
|
+
const output = formatter.formatValidation({ valid, issues });
|
|
1935
|
+
if (output) console.log(output);
|
|
1936
|
+
const summaryLine = formatter.formatSummary(
|
|
1937
|
+
"findings",
|
|
1938
|
+
`${audit.summary.errors} error / ${audit.summary.warnings} warning / ${audit.summary.info} info`,
|
|
1939
|
+
valid
|
|
1940
|
+
);
|
|
1941
|
+
if (summaryLine) console.log(summaryLine);
|
|
1942
|
+
process.exit(opts.reportOnly || valid ? ExitCode.SUCCESS : ExitCode.VALIDATION_FAILED);
|
|
1943
|
+
}
|
|
1944
|
+
function createCheckHarnessStrengthCommand() {
|
|
1945
|
+
const command = new Command14("check-harness-strength").description("Mechanically audit this project's harness setup against the 7 strength patterns").option("--severity <level>", "Minimum severity threshold to display and gate on", "warning").option("--mode <mode>", "Audit mode: adopter | toolkit (default: auto-detect)").option("--toolkit", "Force toolkit mode").option("--adopter", "Force adopter mode").option("--report-only", "Always exit 0 regardless of findings").hook("preAction", (thisCommand) => {
|
|
1946
|
+
const { severity, mode } = thisCommand.opts();
|
|
1947
|
+
if (!["error", "warning", "info"].includes(severity)) {
|
|
1948
|
+
logger.error(`Invalid severity: "${severity}". Must be one of: error, warning, info`);
|
|
1949
|
+
process.exit(ExitCode.ERROR);
|
|
1950
|
+
}
|
|
1951
|
+
if (mode !== void 0 && !["adopter", "toolkit"].includes(mode)) {
|
|
1952
|
+
logger.error(`Invalid mode: "${mode}". Must be one of: adopter, toolkit`);
|
|
1953
|
+
process.exit(ExitCode.ERROR);
|
|
1954
|
+
}
|
|
1955
|
+
}).action(async (opts, cmd) => {
|
|
1956
|
+
await runCheckHarnessStrengthAction(opts, cmd.optsWithGlobals());
|
|
1957
|
+
});
|
|
1958
|
+
return command;
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
// src/commands/check-perf.ts
|
|
1962
|
+
init_dist3();
|
|
1963
|
+
import { Command as Command15 } from "commander";
|
|
1964
|
+
import * as path9 from "path";
|
|
1884
1965
|
async function runCheckPerf(cwd, options) {
|
|
1885
1966
|
const runAll2 = !options.structural && !options.size && !options.coupling;
|
|
1886
1967
|
const analyzer = new EntropyAnalyzer({
|
|
1887
|
-
rootDir:
|
|
1968
|
+
rootDir: path9.resolve(cwd),
|
|
1888
1969
|
analyze: {
|
|
1889
1970
|
complexity: runAll2 || !!options.structural,
|
|
1890
1971
|
coupling: runAll2 || !!options.coupling,
|
|
@@ -1996,7 +2077,7 @@ async function runCheckPerfAction(opts, globalOpts) {
|
|
|
1996
2077
|
process.exit(result.value.valid ? ExitCode.SUCCESS : ExitCode.VALIDATION_FAILED);
|
|
1997
2078
|
}
|
|
1998
2079
|
function createCheckPerfCommand() {
|
|
1999
|
-
const command = new
|
|
2080
|
+
const command = new Command15("check-perf").description("Run performance checks: structural complexity, coupling, and size budgets").option("--structural", "Run structural complexity checks only").option("--coupling", "Run coupling metric checks only").option("--size", "Run size budget checks only").action(async (opts, cmd) => {
|
|
2000
2081
|
await runCheckPerfAction(opts, cmd.optsWithGlobals());
|
|
2001
2082
|
});
|
|
2002
2083
|
return command;
|
|
@@ -2004,10 +2085,10 @@ function createCheckPerfCommand() {
|
|
|
2004
2085
|
|
|
2005
2086
|
// src/commands/check-security.ts
|
|
2006
2087
|
init_dist3();
|
|
2007
|
-
import { Command as
|
|
2008
|
-
import * as
|
|
2088
|
+
import { Command as Command16 } from "commander";
|
|
2089
|
+
import * as path10 from "path";
|
|
2009
2090
|
import { execSync as execSync3 } from "child_process";
|
|
2010
|
-
var
|
|
2091
|
+
var SEVERITY_RANK2 = {
|
|
2011
2092
|
error: 3,
|
|
2012
2093
|
warning: 2,
|
|
2013
2094
|
info: 1
|
|
@@ -2018,19 +2099,19 @@ function getChangedFiles(cwd) {
|
|
|
2018
2099
|
cwd,
|
|
2019
2100
|
encoding: "utf-8"
|
|
2020
2101
|
});
|
|
2021
|
-
return output.trim().split("\n").filter((f) => f.length > 0).map((f) =>
|
|
2102
|
+
return output.trim().split("\n").filter((f) => f.length > 0).map((f) => path10.resolve(cwd, f));
|
|
2022
2103
|
} catch {
|
|
2023
2104
|
return [];
|
|
2024
2105
|
}
|
|
2025
2106
|
}
|
|
2026
2107
|
async function runCheckSecurity(cwd, options) {
|
|
2027
|
-
const projectRoot2 =
|
|
2108
|
+
const projectRoot2 = path10.resolve(cwd);
|
|
2028
2109
|
let configData = {};
|
|
2029
2110
|
try {
|
|
2030
|
-
const
|
|
2031
|
-
const configPath =
|
|
2032
|
-
if (
|
|
2033
|
-
const raw =
|
|
2111
|
+
const fs49 = await import("fs");
|
|
2112
|
+
const configPath = path10.join(projectRoot2, "harness.config.json");
|
|
2113
|
+
if (fs49.existsSync(configPath)) {
|
|
2114
|
+
const raw = fs49.readFileSync(configPath, "utf-8");
|
|
2034
2115
|
const parsed = JSON.parse(raw);
|
|
2035
2116
|
configData = parsed.security ?? {};
|
|
2036
2117
|
}
|
|
@@ -2065,8 +2146,8 @@ async function runCheckSecurity(cwd, options) {
|
|
|
2065
2146
|
} catch {
|
|
2066
2147
|
}
|
|
2067
2148
|
const threshold = options.severity ?? "warning";
|
|
2068
|
-
const thresholdRank =
|
|
2069
|
-
const filtered = result.findings.filter((f) =>
|
|
2149
|
+
const thresholdRank = SEVERITY_RANK2[threshold];
|
|
2150
|
+
const filtered = result.findings.filter((f) => SEVERITY_RANK2[f.severity] >= thresholdRank);
|
|
2070
2151
|
const hasErrors = filtered.some((f) => f.severity === "error");
|
|
2071
2152
|
return Ok({
|
|
2072
2153
|
valid: !hasErrors,
|
|
@@ -2109,7 +2190,7 @@ async function runCheckSecurityAction(opts, globalOpts) {
|
|
|
2109
2190
|
process.exit(result.value.valid ? ExitCode.SUCCESS : ExitCode.VALIDATION_FAILED);
|
|
2110
2191
|
}
|
|
2111
2192
|
function createCheckSecurityCommand() {
|
|
2112
|
-
const command = new
|
|
2193
|
+
const command = new Command16("check-security").description("Run lightweight security scan: secrets, injection, XSS, weak crypto").option("--severity <level>", "Minimum severity threshold", "warning").hook("preAction", (thisCommand) => {
|
|
2113
2194
|
const severity = thisCommand.opts().severity;
|
|
2114
2195
|
if (!["error", "warning", "info"].includes(severity)) {
|
|
2115
2196
|
logger.error(`Invalid severity: "${severity}". Must be one of: error, warning, info`);
|
|
@@ -2122,11 +2203,11 @@ function createCheckSecurityCommand() {
|
|
|
2122
2203
|
}
|
|
2123
2204
|
|
|
2124
2205
|
// src/commands/ci/index.ts
|
|
2125
|
-
import { Command as
|
|
2206
|
+
import { Command as Command20 } from "commander";
|
|
2126
2207
|
|
|
2127
2208
|
// src/commands/ci/check.ts
|
|
2128
2209
|
init_dist3();
|
|
2129
|
-
import { Command as
|
|
2210
|
+
import { Command as Command17 } from "commander";
|
|
2130
2211
|
var VALID_CHECKS = [
|
|
2131
2212
|
"validate",
|
|
2132
2213
|
"deps",
|
|
@@ -2216,16 +2297,16 @@ async function runCheckAction(opts, globalOpts) {
|
|
|
2216
2297
|
process.exit(report.exitCode);
|
|
2217
2298
|
}
|
|
2218
2299
|
function createCheckCommand() {
|
|
2219
|
-
return new
|
|
2300
|
+
return new Command17("check").description("Run all harness checks for CI (validate, deps, docs, entropy, phase-gate, arch)").option("--skip <checks>", "Comma-separated checks to skip (e.g., entropy,docs)").option("--fail-on <severity>", "Fail on severity level: error (default) or warning", "error").action(async (opts, cmd) => {
|
|
2220
2301
|
await runCheckAction(opts, cmd.optsWithGlobals());
|
|
2221
2302
|
});
|
|
2222
2303
|
}
|
|
2223
2304
|
|
|
2224
2305
|
// src/commands/ci/init.ts
|
|
2225
2306
|
init_dist3();
|
|
2226
|
-
import { Command as
|
|
2307
|
+
import { Command as Command18 } from "commander";
|
|
2227
2308
|
import * as fs4 from "fs";
|
|
2228
|
-
import * as
|
|
2309
|
+
import * as path11 from "path";
|
|
2229
2310
|
var ALL_CHECKS = [
|
|
2230
2311
|
"validate",
|
|
2231
2312
|
"deps",
|
|
@@ -2243,8 +2324,74 @@ function buildSkipFlag(checks) {
|
|
|
2243
2324
|
if (skipChecks.length === 0) return "";
|
|
2244
2325
|
return ` --skip ${skipChecks.join(",")}`;
|
|
2245
2326
|
}
|
|
2246
|
-
function
|
|
2247
|
-
|
|
2327
|
+
function stepsForLanguage(language) {
|
|
2328
|
+
switch (language) {
|
|
2329
|
+
case "python":
|
|
2330
|
+
return {
|
|
2331
|
+
setup: ` - uses: actions/setup-python@v5
|
|
2332
|
+
with:
|
|
2333
|
+
python-version: '3.12'`,
|
|
2334
|
+
install: "pip install -e .",
|
|
2335
|
+
lint: "ruff check .",
|
|
2336
|
+
test: "pytest"
|
|
2337
|
+
};
|
|
2338
|
+
case "go":
|
|
2339
|
+
return {
|
|
2340
|
+
setup: ` - uses: actions/setup-go@v5
|
|
2341
|
+
with:
|
|
2342
|
+
go-version: 'stable'`,
|
|
2343
|
+
install: "go mod download",
|
|
2344
|
+
build: "go build ./...",
|
|
2345
|
+
lint: "golangci-lint run",
|
|
2346
|
+
test: "go test ./..."
|
|
2347
|
+
};
|
|
2348
|
+
case "rust":
|
|
2349
|
+
return {
|
|
2350
|
+
setup: ` - uses: dtolnay/rust-toolchain@stable`,
|
|
2351
|
+
install: "cargo fetch",
|
|
2352
|
+
build: "cargo build",
|
|
2353
|
+
lint: "cargo clippy",
|
|
2354
|
+
test: "cargo test"
|
|
2355
|
+
};
|
|
2356
|
+
case "java":
|
|
2357
|
+
return {
|
|
2358
|
+
setup: ` - uses: actions/setup-java@v4
|
|
2359
|
+
with:
|
|
2360
|
+
distribution: 'temurin'
|
|
2361
|
+
java-version: '21'`,
|
|
2362
|
+
install: "mvn -B -q install -DskipTests",
|
|
2363
|
+
test: "mvn -B verify"
|
|
2364
|
+
};
|
|
2365
|
+
case "typescript":
|
|
2366
|
+
default:
|
|
2367
|
+
return {
|
|
2368
|
+
setup: ` - uses: pnpm/action-setup@v4
|
|
2369
|
+
- uses: actions/setup-node@v4
|
|
2370
|
+
with:
|
|
2371
|
+
node-version: '22'`,
|
|
2372
|
+
install: "pnpm i --frozen-lockfile",
|
|
2373
|
+
build: "pnpm build",
|
|
2374
|
+
lint: "pnpm lint",
|
|
2375
|
+
test: "pnpm test"
|
|
2376
|
+
};
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
function generateGitHubActions(skipFlag, language) {
|
|
2380
|
+
const steps = stepsForLanguage(language);
|
|
2381
|
+
const commandSteps = [];
|
|
2382
|
+
commandSteps.push(` - name: Install dependencies
|
|
2383
|
+
run: ${steps.install}`);
|
|
2384
|
+
if (steps.build) {
|
|
2385
|
+
commandSteps.push(` - name: Build
|
|
2386
|
+
run: ${steps.build}`);
|
|
2387
|
+
}
|
|
2388
|
+
if (steps.lint) {
|
|
2389
|
+
commandSteps.push(` - name: Lint
|
|
2390
|
+
run: ${steps.lint}`);
|
|
2391
|
+
}
|
|
2392
|
+
commandSteps.push(` - name: Test
|
|
2393
|
+
run: ${steps.test}`);
|
|
2394
|
+
return `name: CI
|
|
2248
2395
|
|
|
2249
2396
|
on:
|
|
2250
2397
|
push:
|
|
@@ -2257,15 +2404,15 @@ concurrency:
|
|
|
2257
2404
|
cancel-in-progress: true
|
|
2258
2405
|
|
|
2259
2406
|
jobs:
|
|
2260
|
-
|
|
2407
|
+
ci:
|
|
2261
2408
|
runs-on: ubuntu-latest
|
|
2262
2409
|
steps:
|
|
2263
2410
|
- uses: actions/checkout@v4
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
- name:
|
|
2411
|
+
${steps.setup}
|
|
2412
|
+
${commandSteps.join("\n")}
|
|
2413
|
+
- name: Install harness CLI
|
|
2414
|
+
run: npm install -g @harness-engineering/cli
|
|
2415
|
+
- name: Harness gate
|
|
2269
2416
|
run: harness ci check --json${skipFlag}
|
|
2270
2417
|
`;
|
|
2271
2418
|
}
|
|
@@ -2310,10 +2457,13 @@ exit $EXIT_CODE
|
|
|
2310
2457
|
`;
|
|
2311
2458
|
}
|
|
2312
2459
|
function generateCIConfig(options) {
|
|
2313
|
-
const { platform, checks } = options;
|
|
2460
|
+
const { platform, checks, language } = options;
|
|
2314
2461
|
const skipFlag = buildSkipFlag(checks);
|
|
2315
2462
|
const generators = {
|
|
2316
|
-
github: {
|
|
2463
|
+
github: {
|
|
2464
|
+
filename: ".github/workflows/ci.yml",
|
|
2465
|
+
generate: (skip) => generateGitHubActions(skip, language)
|
|
2466
|
+
},
|
|
2317
2467
|
gitlab: { filename: ".gitlab-ci-harness.yml", generate: generateGitLabCI },
|
|
2318
2468
|
generic: { filename: "harness-ci.sh", generate: generateGenericScript }
|
|
2319
2469
|
};
|
|
@@ -2332,8 +2482,8 @@ function detectPlatform() {
|
|
|
2332
2482
|
return null;
|
|
2333
2483
|
}
|
|
2334
2484
|
function writeGeneratedConfig(filename, content, platform) {
|
|
2335
|
-
const targetPath =
|
|
2336
|
-
const dir =
|
|
2485
|
+
const targetPath = path11.resolve(filename);
|
|
2486
|
+
const dir = path11.dirname(targetPath);
|
|
2337
2487
|
fs4.mkdirSync(dir, { recursive: true });
|
|
2338
2488
|
fs4.writeFileSync(targetPath, content);
|
|
2339
2489
|
if (platform === "generic" && process.platform !== "win32") {
|
|
@@ -2345,6 +2495,7 @@ async function runInitAction(opts, globalOpts) {
|
|
|
2345
2495
|
const checks = opts.checks ? opts.checks.split(",").map((s) => s.trim()) : void 0;
|
|
2346
2496
|
const configOpts = { platform };
|
|
2347
2497
|
if (checks) configOpts.checks = checks;
|
|
2498
|
+
if (opts.language) configOpts.language = opts.language;
|
|
2348
2499
|
const result = generateCIConfig(configOpts);
|
|
2349
2500
|
if (!result.ok) {
|
|
2350
2501
|
logger.error(result.error.message);
|
|
@@ -2360,14 +2511,14 @@ async function runInitAction(opts, globalOpts) {
|
|
|
2360
2511
|
}
|
|
2361
2512
|
}
|
|
2362
2513
|
function createInitCommand() {
|
|
2363
|
-
return new
|
|
2514
|
+
return new Command18("init").description("Generate CI configuration for harness checks").option("--platform <platform>", "CI platform: github, gitlab, or generic").option("--language <language>", "Project language for build/lint/test steps").option("--checks <list>", "Comma-separated list of checks to include").action(async (opts, cmd) => {
|
|
2364
2515
|
await runInitAction(opts, cmd.optsWithGlobals());
|
|
2365
2516
|
});
|
|
2366
2517
|
}
|
|
2367
2518
|
|
|
2368
2519
|
// src/commands/ci/notify.ts
|
|
2369
2520
|
init_dist3();
|
|
2370
|
-
import { Command as
|
|
2521
|
+
import { Command as Command19 } from "commander";
|
|
2371
2522
|
import * as fs5 from "fs";
|
|
2372
2523
|
function loadReport(reportPath) {
|
|
2373
2524
|
try {
|
|
@@ -2464,14 +2615,14 @@ async function runNotifyAction(reportPath, opts, globalOpts) {
|
|
|
2464
2615
|
}
|
|
2465
2616
|
}
|
|
2466
2617
|
function createNotifyCommand() {
|
|
2467
|
-
return new
|
|
2618
|
+
return new Command19("notify").description("Post CI check results to GitHub (PR comment or issue)").argument("<report>", "Path to CI check report JSON file (from harness ci check --json)").requiredOption("--target <target>", "Notification target: pr-comment or issue").option("--pr <number>", "PR number (required for pr-comment target)").option("--title <title>", "Custom issue title (for issue target)").option("--labels <labels>", "Comma-separated labels for created issues").action(async (reportPath, opts, cmd) => {
|
|
2468
2619
|
await runNotifyAction(reportPath, opts, cmd.optsWithGlobals());
|
|
2469
2620
|
});
|
|
2470
2621
|
}
|
|
2471
2622
|
|
|
2472
2623
|
// src/commands/ci/index.ts
|
|
2473
2624
|
function createCICommand() {
|
|
2474
|
-
const command = new
|
|
2625
|
+
const command = new Command20("ci").description("CI/CD integration commands");
|
|
2475
2626
|
command.addCommand(createCheckCommand());
|
|
2476
2627
|
command.addCommand(createInitCommand());
|
|
2477
2628
|
command.addCommand(createNotifyCommand());
|
|
@@ -2480,8 +2631,8 @@ function createCICommand() {
|
|
|
2480
2631
|
|
|
2481
2632
|
// src/commands/cleanup.ts
|
|
2482
2633
|
init_dist3();
|
|
2483
|
-
import { Command as
|
|
2484
|
-
import * as
|
|
2634
|
+
import { Command as Command21 } from "commander";
|
|
2635
|
+
import * as path12 from "path";
|
|
2485
2636
|
async function runCleanup(options) {
|
|
2486
2637
|
const cwd = options.cwd ?? process.cwd();
|
|
2487
2638
|
const type = options.type ?? "all";
|
|
@@ -2496,12 +2647,12 @@ async function runCleanup(options) {
|
|
|
2496
2647
|
patternViolations: [],
|
|
2497
2648
|
totalIssues: 0
|
|
2498
2649
|
};
|
|
2499
|
-
const rootDir =
|
|
2500
|
-
const docsDir =
|
|
2650
|
+
const rootDir = path12.resolve(cwd, config.rootDir);
|
|
2651
|
+
const docsDir = path12.resolve(cwd, config.docsDir);
|
|
2501
2652
|
const entropyConfig = {
|
|
2502
2653
|
rootDir,
|
|
2503
2654
|
...config.entropy?.entryPoints && { entryPoints: config.entropy.entryPoints },
|
|
2504
|
-
docPaths: [
|
|
2655
|
+
docPaths: [path12.join(docsDir, "**/*.md")],
|
|
2505
2656
|
analyze: {
|
|
2506
2657
|
drift: type === "all" || type === "drift",
|
|
2507
2658
|
deadCode: type === "all" || type === "dead-code",
|
|
@@ -2569,7 +2720,7 @@ function printCleanupResult(value, formatter) {
|
|
|
2569
2720
|
}
|
|
2570
2721
|
}
|
|
2571
2722
|
function createCleanupCommand() {
|
|
2572
|
-
const command = new
|
|
2723
|
+
const command = new Command21("cleanup").description("Detect entropy issues (doc drift, dead code, patterns)").option("-t, --type <type>", "Issue type: drift, dead-code, patterns, all", "all").action(async (opts, cmd) => {
|
|
2573
2724
|
const globalOpts = cmd.optsWithGlobals();
|
|
2574
2725
|
const mode = resolveOutputMode(globalOpts);
|
|
2575
2726
|
const formatter = new OutputFormatter(mode);
|
|
@@ -2600,9 +2751,9 @@ function createCleanupCommand() {
|
|
|
2600
2751
|
|
|
2601
2752
|
// src/commands/cleanup-sessions.ts
|
|
2602
2753
|
init_dist3();
|
|
2603
|
-
import { Command as
|
|
2754
|
+
import { Command as Command22 } from "commander";
|
|
2604
2755
|
import * as fs6 from "fs";
|
|
2605
|
-
import * as
|
|
2756
|
+
import * as path13 from "path";
|
|
2606
2757
|
var STALE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
2607
2758
|
var ONE_HOUR_MS = 60 * 60 * 1e3;
|
|
2608
2759
|
var ONE_DAY_MS = 24 * ONE_HOUR_MS;
|
|
@@ -2619,7 +2770,7 @@ function getMostRecentMtime(dirPath) {
|
|
|
2619
2770
|
try {
|
|
2620
2771
|
const entries = fs6.readdirSync(dirPath, { withFileTypes: true });
|
|
2621
2772
|
for (const entry of entries) {
|
|
2622
|
-
const fullPath =
|
|
2773
|
+
const fullPath = path13.join(dirPath, entry.name);
|
|
2623
2774
|
const stat = fs6.statSync(fullPath);
|
|
2624
2775
|
if (stat.mtimeMs > latest) latest = stat.mtimeMs;
|
|
2625
2776
|
}
|
|
@@ -2632,7 +2783,7 @@ function getMostRecentMtime(dirPath) {
|
|
|
2632
2783
|
async function runCleanupSessions(options) {
|
|
2633
2784
|
const cwd = options.cwd ?? process.cwd();
|
|
2634
2785
|
const dryRun = options.dryRun ?? false;
|
|
2635
|
-
const sessionsDir =
|
|
2786
|
+
const sessionsDir = path13.join(cwd, ".harness", "sessions");
|
|
2636
2787
|
const result = { removed: [], kept: [] };
|
|
2637
2788
|
if (!fs6.existsSync(sessionsDir)) {
|
|
2638
2789
|
return Ok(result);
|
|
@@ -2651,7 +2802,7 @@ async function runCleanupSessions(options) {
|
|
|
2651
2802
|
const now = Date.now();
|
|
2652
2803
|
for (const entry of entries) {
|
|
2653
2804
|
if (!entry.isDirectory()) continue;
|
|
2654
|
-
const sessionPath =
|
|
2805
|
+
const sessionPath = path13.join(sessionsDir, entry.name);
|
|
2655
2806
|
const mostRecent = getMostRecentMtime(sessionPath);
|
|
2656
2807
|
const ageMs = now - mostRecent;
|
|
2657
2808
|
if (ageMs > STALE_TTL_MS) {
|
|
@@ -2715,7 +2866,7 @@ async function runCleanupAll(opts) {
|
|
|
2715
2866
|
const results = [];
|
|
2716
2867
|
for (const target of targets) {
|
|
2717
2868
|
const ttlMs = (ttlOverrides[target.name] ?? 0) > 0 ? ttlOverrides[target.name] * ONE_HOUR_MS : target.defaultTtlMs;
|
|
2718
|
-
const dir =
|
|
2869
|
+
const dir = path13.join(cwd, ".harness", target.relativeDir);
|
|
2719
2870
|
const sweepResult = await sweepDirectory(dir, ttlMs, dryRun);
|
|
2720
2871
|
if (!sweepResult.ok) return Err(sweepResult.error);
|
|
2721
2872
|
results.push({ target: target.name, ...sweepResult.value });
|
|
@@ -2739,7 +2890,7 @@ async function sweepDirectory(dir, ttlMs, dryRun) {
|
|
|
2739
2890
|
}
|
|
2740
2891
|
const now = Date.now();
|
|
2741
2892
|
for (const entry of entries) {
|
|
2742
|
-
const entryPath =
|
|
2893
|
+
const entryPath = path13.join(dir, entry.name);
|
|
2743
2894
|
const ageMs = now - mtimeOf(entryPath);
|
|
2744
2895
|
if (ageMs > ttlMs) {
|
|
2745
2896
|
removed.push(entry.name);
|
|
@@ -2796,14 +2947,14 @@ ${dryRun ? "Would remove" : "Removed"} ${totalRemoved} stale entry(ies) across t
|
|
|
2796
2947
|
}
|
|
2797
2948
|
}
|
|
2798
2949
|
function createCleanupSessionsCommand() {
|
|
2799
|
-
const command = new
|
|
2950
|
+
const command = new Command22("cleanup-sessions").description(
|
|
2800
2951
|
"Remove stale entries from .harness/. Default: only .harness/sessions/ (no write in 24h)."
|
|
2801
2952
|
).option("--dry-run", "List stale entries without deleting them", false).option("--path <path>", "Project root path", ".").option("--all", "Sweep every registered .harness/ target").option(
|
|
2802
2953
|
"--include <list>",
|
|
2803
2954
|
"Comma-separated target names (mutually-exclusive with --exclude/--all)"
|
|
2804
2955
|
).option("--exclude <list>", "Comma-separated target names to skip").action(async (opts, cmd) => {
|
|
2805
2956
|
const globalOpts = cmd.optsWithGlobals();
|
|
2806
|
-
const cwd =
|
|
2957
|
+
const cwd = path13.resolve(opts.path);
|
|
2807
2958
|
const asJson = Boolean(globalOpts.json);
|
|
2808
2959
|
if (opts.all || opts.include || opts.exclude) {
|
|
2809
2960
|
await runExtendedSweep(cwd, opts, asJson);
|
|
@@ -2845,13 +2996,13 @@ async function runSessionsOnlySweep(cwd, opts, asJson) {
|
|
|
2845
2996
|
}
|
|
2846
2997
|
|
|
2847
2998
|
// src/commands/compound/index.ts
|
|
2848
|
-
import { Command as
|
|
2999
|
+
import { Command as Command24 } from "commander";
|
|
2849
3000
|
|
|
2850
3001
|
// src/commands/compound/scan-candidates.ts
|
|
2851
3002
|
init_dist3();
|
|
2852
3003
|
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
2853
|
-
import { dirname as dirname2, join as join8, resolve as
|
|
2854
|
-
import { Command as
|
|
3004
|
+
import { dirname as dirname2, join as join8, resolve as resolve12 } from "path";
|
|
3005
|
+
import { Command as Command23 } from "commander";
|
|
2855
3006
|
var HOTSPOT_THRESHOLD = 7;
|
|
2856
3007
|
var DEFAULT_LOOKBACK = "7d";
|
|
2857
3008
|
async function runCompoundScanCandidatesCommand(opts) {
|
|
@@ -2906,7 +3057,7 @@ function emit(status, nonInteractive) {
|
|
|
2906
3057
|
return status;
|
|
2907
3058
|
}
|
|
2908
3059
|
function createScanCandidatesCommand() {
|
|
2909
|
-
return new
|
|
3060
|
+
return new Command23("scan-candidates").description(
|
|
2910
3061
|
"Scan recent fixes and hotspots for undocumented learnings; write candidate prompts"
|
|
2911
3062
|
).option("--lookback <window>", "Lookback window (e.g. 7d, 14d).", DEFAULT_LOOKBACK).option(
|
|
2912
3063
|
"--non-interactive",
|
|
@@ -2919,9 +3070,9 @@ function createScanCandidatesCommand() {
|
|
|
2919
3070
|
const nonInteractive = options.nonInteractive === true || !process.stdout.isTTY;
|
|
2920
3071
|
const status = await runCompoundScanCandidatesCommand({
|
|
2921
3072
|
lookback: options.lookback,
|
|
2922
|
-
configPath:
|
|
2923
|
-
outputPath: options.outputPath ?
|
|
2924
|
-
solutionsDir:
|
|
3073
|
+
configPath: resolve12(process.cwd(), options.config),
|
|
3074
|
+
outputPath: options.outputPath ? resolve12(process.cwd(), options.outputPath) : void 0,
|
|
3075
|
+
solutionsDir: resolve12(process.cwd(), options.solutionsDir),
|
|
2925
3076
|
nonInteractive
|
|
2926
3077
|
});
|
|
2927
3078
|
if (status.status === "failure") process.exitCode = 1;
|
|
@@ -2931,15 +3082,15 @@ function createScanCandidatesCommand() {
|
|
|
2931
3082
|
|
|
2932
3083
|
// src/commands/compound/index.ts
|
|
2933
3084
|
function createCompoundCommand() {
|
|
2934
|
-
const command = new
|
|
3085
|
+
const command = new Command24("compound").description("Compound (post-mortem playbook) commands");
|
|
2935
3086
|
command.addCommand(createScanCandidatesCommand());
|
|
2936
3087
|
return command;
|
|
2937
3088
|
}
|
|
2938
3089
|
|
|
2939
3090
|
// src/commands/copy-craft.ts
|
|
2940
|
-
import { Command as
|
|
3091
|
+
import { Command as Command25 } from "commander";
|
|
2941
3092
|
function createCopyCraftCommand() {
|
|
2942
|
-
return new
|
|
3093
|
+
return new Command25("copy-craft").description(
|
|
2943
3094
|
"LLM-judgment critique of prose-in-code across six surfaces: error messages, log lines, CLI output, commit subjects, PR descriptions, code comments. Third craft-pipeline ceiling skill. Graceful degradation when git/gh prereqs absent."
|
|
2944
3095
|
).option("-f, --files <files...>", "Optional source file/glob scope").option(
|
|
2945
3096
|
"-s, --surfaces <surfaces...>",
|
|
@@ -2991,10 +3142,10 @@ function printResult2(result, mode, _formatter) {
|
|
|
2991
3142
|
list.push(f);
|
|
2992
3143
|
bySurface.set(f.target.surface, list);
|
|
2993
3144
|
}
|
|
2994
|
-
for (const [surface,
|
|
3145
|
+
for (const [surface, fs49] of bySurface) {
|
|
2995
3146
|
console.log(`
|
|
2996
3147
|
[${surface}]`);
|
|
2997
|
-
for (const f of
|
|
3148
|
+
for (const f of fs49) {
|
|
2998
3149
|
const line = f.target.line !== void 0 ? `:${f.target.line}` : "";
|
|
2999
3150
|
console.log(` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.file}${line}`);
|
|
3000
3151
|
console.log(` "${truncate(f.target.snippet, 80)}"`);
|
|
@@ -3020,11 +3171,11 @@ function truncate(s, n) {
|
|
|
3020
3171
|
}
|
|
3021
3172
|
|
|
3022
3173
|
// src/commands/dashboard.ts
|
|
3023
|
-
import { Command as
|
|
3174
|
+
import { Command as Command26 } from "commander";
|
|
3024
3175
|
import { spawn } from "child_process";
|
|
3025
3176
|
import { existsSync as existsSync5 } from "fs";
|
|
3026
3177
|
import { createRequire } from "module";
|
|
3027
|
-
import { dirname as dirname3, join as join9, resolve as
|
|
3178
|
+
import { dirname as dirname3, join as join9, resolve as resolve13 } from "path";
|
|
3028
3179
|
import { setTimeout } from "timers";
|
|
3029
3180
|
import { fileURLToPath } from "url";
|
|
3030
3181
|
var __filename = fileURLToPath(import.meta.url);
|
|
@@ -3088,7 +3239,7 @@ function buildDashboardEnv(opts, clientPort, apiPort, projectPath) {
|
|
|
3088
3239
|
function runDashboard(opts) {
|
|
3089
3240
|
const clientPort = Number(opts.port ?? DEFAULT_CLIENT_PORT);
|
|
3090
3241
|
const apiPort = Number(opts.apiPort ?? DEFAULT_API_PORT);
|
|
3091
|
-
const projectPath =
|
|
3242
|
+
const projectPath = resolve13(opts.cwd ?? process.cwd());
|
|
3092
3243
|
const url = `http://localhost:${apiPort}`;
|
|
3093
3244
|
const server = resolveServerScript();
|
|
3094
3245
|
if (!server) {
|
|
@@ -3104,13 +3255,13 @@ function runDashboard(opts) {
|
|
|
3104
3255
|
}
|
|
3105
3256
|
}
|
|
3106
3257
|
function createDashboardCommand() {
|
|
3107
|
-
return new
|
|
3258
|
+
return new Command26("dashboard").description("Start the Harness local web dashboard").option("--port <port>", "Client dev server port", String(DEFAULT_CLIENT_PORT)).option("--api-port <port>", "API server port", String(DEFAULT_API_PORT)).option("--orchestrator-url <url>", "Orchestrator URL (default: http://localhost:8080)").option("--no-open", "Do not automatically open browser").option("--cwd <path>", "Project directory (defaults to cwd)").action((opts) => runDashboard(opts));
|
|
3108
3259
|
}
|
|
3109
3260
|
|
|
3110
3261
|
// src/commands/design-pipeline.ts
|
|
3111
|
-
import { Command as
|
|
3262
|
+
import { Command as Command27 } from "commander";
|
|
3112
3263
|
function createDesignPipelineCommand() {
|
|
3113
|
-
return new
|
|
3264
|
+
return new Command27("design-pipeline").description(
|
|
3114
3265
|
"Run the design-pipeline orchestrator: FRESHEN \u2192 DETECT \u2192 FIX \u2192 AUDIT \u2192 FILL \u2192 REPORT. Composes detect-design-drift, align-design-system, audit-component-anatomy, audit-brand-compliance, and design-craft-elevator into a single sequential pipeline with convergence-based remediation."
|
|
3115
3266
|
).option("--fix", "Enable convergence-based remediation (default: detect + report only)").option("--no-freshen", "Skip the FRESHEN phase").option("--no-fill", "Skip the FILL phase (input bootstrap + craft polish)").option("--ci", "Non-interactive: safe fixes only, no prompts").option("-f, --files <files...>", "Optional file/glob scope passed to each verifier").option("-m, --mode <mode>", "Verifier mode: fast | full", "fast").option(
|
|
3116
3267
|
"--design-strictness <level>",
|
|
@@ -3196,10 +3347,10 @@ function verdictBadge(verdict) {
|
|
|
3196
3347
|
}
|
|
3197
3348
|
|
|
3198
3349
|
// src/commands/doctor.ts
|
|
3199
|
-
import { Command as
|
|
3350
|
+
import { Command as Command28 } from "commander";
|
|
3200
3351
|
import * as fs8 from "fs";
|
|
3201
3352
|
import * as os from "os";
|
|
3202
|
-
import * as
|
|
3353
|
+
import * as path15 from "path";
|
|
3203
3354
|
import chalk from "chalk";
|
|
3204
3355
|
|
|
3205
3356
|
// src/utils/node-version.ts
|
|
@@ -3283,7 +3434,7 @@ var INTEGRATION_REGISTRY = [
|
|
|
3283
3434
|
|
|
3284
3435
|
// src/integrations/config.ts
|
|
3285
3436
|
import * as fs7 from "fs";
|
|
3286
|
-
import * as
|
|
3437
|
+
import * as path14 from "path";
|
|
3287
3438
|
function readJsonSafe(filePath) {
|
|
3288
3439
|
if (!fs7.existsSync(filePath)) return null;
|
|
3289
3440
|
try {
|
|
@@ -3293,7 +3444,7 @@ function readJsonSafe(filePath) {
|
|
|
3293
3444
|
}
|
|
3294
3445
|
}
|
|
3295
3446
|
function writeJson(filePath, data) {
|
|
3296
|
-
const dir =
|
|
3447
|
+
const dir = path14.dirname(filePath);
|
|
3297
3448
|
if (!fs7.existsSync(dir)) {
|
|
3298
3449
|
fs7.mkdirSync(dir, { recursive: true });
|
|
3299
3450
|
}
|
|
@@ -3377,13 +3528,13 @@ function checkSlashCommands() {
|
|
|
3377
3528
|
const platforms = [
|
|
3378
3529
|
{
|
|
3379
3530
|
name: "Claude Code",
|
|
3380
|
-
dir:
|
|
3531
|
+
dir: path15.join(os.homedir(), ".claude", "commands", "harness"),
|
|
3381
3532
|
ext: ".md",
|
|
3382
3533
|
client: "claude-code"
|
|
3383
3534
|
},
|
|
3384
3535
|
{
|
|
3385
3536
|
name: "Gemini CLI",
|
|
3386
|
-
dir:
|
|
3537
|
+
dir: path15.join(os.homedir(), ".gemini", "commands", "harness"),
|
|
3387
3538
|
ext: ".toml",
|
|
3388
3539
|
client: "gemini-cli"
|
|
3389
3540
|
}
|
|
@@ -3407,7 +3558,7 @@ function checkSlashCommands() {
|
|
|
3407
3558
|
}
|
|
3408
3559
|
function checkMcpConfig(cwd) {
|
|
3409
3560
|
const results = [];
|
|
3410
|
-
const claudeConfig = readMcpConfig(
|
|
3561
|
+
const claudeConfig = readMcpConfig(path15.join(cwd, ".mcp.json"));
|
|
3411
3562
|
if (claudeConfig.mcpServers?.["harness"]) {
|
|
3412
3563
|
results.push({
|
|
3413
3564
|
name: "mcp-claude",
|
|
@@ -3422,9 +3573,9 @@ function checkMcpConfig(cwd) {
|
|
|
3422
3573
|
fix: "Run: harness setup-mcp --client claude"
|
|
3423
3574
|
});
|
|
3424
3575
|
}
|
|
3425
|
-
const geminiDir =
|
|
3576
|
+
const geminiDir = path15.join(cwd, ".gemini");
|
|
3426
3577
|
if (fs8.existsSync(geminiDir)) {
|
|
3427
|
-
const geminiConfig = readMcpConfig(
|
|
3578
|
+
const geminiConfig = readMcpConfig(path15.join(geminiDir, "settings.json"));
|
|
3428
3579
|
if (geminiConfig.mcpServers?.["harness"]) {
|
|
3429
3580
|
results.push({
|
|
3430
3581
|
name: "mcp-gemini",
|
|
@@ -3443,12 +3594,12 @@ function checkMcpConfig(cwd) {
|
|
|
3443
3594
|
return results;
|
|
3444
3595
|
}
|
|
3445
3596
|
function loadMcpPresence(cwd) {
|
|
3446
|
-
const mcpPath =
|
|
3447
|
-
const geminiDir =
|
|
3597
|
+
const mcpPath = path15.join(cwd, ".mcp.json");
|
|
3598
|
+
const geminiDir = path15.join(cwd, ".gemini");
|
|
3448
3599
|
const hasGemini = fs8.existsSync(geminiDir);
|
|
3449
3600
|
return {
|
|
3450
3601
|
mcpConfig: readMcpConfig(mcpPath),
|
|
3451
|
-
geminiConfig: hasGemini ? readMcpConfig(
|
|
3602
|
+
geminiConfig: hasGemini ? readMcpConfig(path15.join(geminiDir, "settings.json")) : null,
|
|
3452
3603
|
hasGemini
|
|
3453
3604
|
};
|
|
3454
3605
|
}
|
|
@@ -3480,7 +3631,7 @@ function checkTier0Presence(def, presence) {
|
|
|
3480
3631
|
function checkIntegrations(cwd) {
|
|
3481
3632
|
const results = [];
|
|
3482
3633
|
const presence = loadMcpPresence(cwd);
|
|
3483
|
-
const configPath =
|
|
3634
|
+
const configPath = path15.join(cwd, "harness.config.json");
|
|
3484
3635
|
const integrationsConfig = readIntegrationsConfig(configPath);
|
|
3485
3636
|
for (const def of INTEGRATION_REGISTRY.filter((d) => d.tier === 0)) {
|
|
3486
3637
|
results.push(checkTier0Presence(def, presence));
|
|
@@ -3548,12 +3699,12 @@ function checkLivePings(env = process.env) {
|
|
|
3548
3699
|
return results;
|
|
3549
3700
|
}
|
|
3550
3701
|
function listHookFiles(cwd) {
|
|
3551
|
-
const hooksDir =
|
|
3702
|
+
const hooksDir = path15.join(cwd, ".harness", "hooks");
|
|
3552
3703
|
if (!fs8.existsSync(hooksDir)) return [];
|
|
3553
3704
|
try {
|
|
3554
3705
|
return fs8.readdirSync(hooksDir).filter((f) => !f.startsWith(".")).map((f) => ({
|
|
3555
|
-
relativePath:
|
|
3556
|
-
absolutePath:
|
|
3706
|
+
relativePath: path15.join(".harness", "hooks", f),
|
|
3707
|
+
absolutePath: path15.join(hooksDir, f)
|
|
3557
3708
|
}));
|
|
3558
3709
|
} catch {
|
|
3559
3710
|
return [];
|
|
@@ -3650,7 +3801,7 @@ var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
|
3650
3801
|
function checkBaselineFreshness(cwd, now = Date.now()) {
|
|
3651
3802
|
const results = [];
|
|
3652
3803
|
for (const def of BASELINE_FILES) {
|
|
3653
|
-
const absolutePath =
|
|
3804
|
+
const absolutePath = path15.join(cwd, def.relativePath);
|
|
3654
3805
|
if (!fs8.existsSync(absolutePath)) {
|
|
3655
3806
|
results.push({
|
|
3656
3807
|
name: `baseline-freshness-${def.relativePath}`,
|
|
@@ -3698,7 +3849,7 @@ function checkBaselineFreshness(cwd, now = Date.now()) {
|
|
|
3698
3849
|
}
|
|
3699
3850
|
var SESSIONS_SAMPLE_SIZE = 5;
|
|
3700
3851
|
function listRecentSessions(cwd) {
|
|
3701
|
-
const sessionsDir =
|
|
3852
|
+
const sessionsDir = path15.join(cwd, ".harness", "sessions");
|
|
3702
3853
|
if (!fs8.existsSync(sessionsDir)) return [];
|
|
3703
3854
|
let entries;
|
|
3704
3855
|
try {
|
|
@@ -3709,7 +3860,7 @@ function listRecentSessions(cwd) {
|
|
|
3709
3860
|
const dirs = [];
|
|
3710
3861
|
for (const name of entries) {
|
|
3711
3862
|
if (name.startsWith(".")) continue;
|
|
3712
|
-
const absolutePath =
|
|
3863
|
+
const absolutePath = path15.join(sessionsDir, name);
|
|
3713
3864
|
try {
|
|
3714
3865
|
const stat = fs8.statSync(absolutePath);
|
|
3715
3866
|
if (!stat.isDirectory()) continue;
|
|
@@ -3726,7 +3877,7 @@ function scanSessionSummaries(sessions) {
|
|
|
3726
3877
|
let missing = 0;
|
|
3727
3878
|
const corrupt = [];
|
|
3728
3879
|
for (const session of sessions) {
|
|
3729
|
-
const summaryPath =
|
|
3880
|
+
const summaryPath = path15.join(session.absolutePath, "session-summary.json");
|
|
3730
3881
|
if (!fs8.existsSync(summaryPath)) {
|
|
3731
3882
|
missing += 1;
|
|
3732
3883
|
continue;
|
|
@@ -3800,7 +3951,7 @@ function formatCheck(check) {
|
|
|
3800
3951
|
return line;
|
|
3801
3952
|
}
|
|
3802
3953
|
function createDoctorCommand() {
|
|
3803
|
-
return new
|
|
3954
|
+
return new Command28("doctor").description(
|
|
3804
3955
|
"Check environment health: Node, slash commands, MCP, integrations, integration credentials, hooks, baselines, sessions"
|
|
3805
3956
|
).action((_opts, cmd) => {
|
|
3806
3957
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -3828,8 +3979,8 @@ function createDoctorCommand() {
|
|
|
3828
3979
|
|
|
3829
3980
|
// src/commands/fix-drift.ts
|
|
3830
3981
|
init_dist3();
|
|
3831
|
-
import { Command as
|
|
3832
|
-
import * as
|
|
3982
|
+
import { Command as Command29 } from "commander";
|
|
3983
|
+
import * as path16 from "path";
|
|
3833
3984
|
async function runFixDrift(options) {
|
|
3834
3985
|
const cwd = options.cwd ?? process.cwd();
|
|
3835
3986
|
const dryRun = options.dryRun !== false;
|
|
@@ -3838,12 +3989,12 @@ async function runFixDrift(options) {
|
|
|
3838
3989
|
return Err(configResult.error);
|
|
3839
3990
|
}
|
|
3840
3991
|
const config = configResult.value;
|
|
3841
|
-
const rootDir =
|
|
3842
|
-
const docsDir =
|
|
3992
|
+
const rootDir = path16.resolve(cwd, config.rootDir);
|
|
3993
|
+
const docsDir = path16.resolve(cwd, config.docsDir);
|
|
3843
3994
|
const entropyConfig = {
|
|
3844
3995
|
rootDir,
|
|
3845
3996
|
...config.entropy?.entryPoints && { entryPoints: config.entropy.entryPoints },
|
|
3846
|
-
docPaths: [
|
|
3997
|
+
docPaths: [path16.join(docsDir, "**/*.md")],
|
|
3847
3998
|
analyze: {
|
|
3848
3999
|
drift: true,
|
|
3849
4000
|
deadCode: true,
|
|
@@ -3961,7 +4112,7 @@ function printFixDriftResult(value, mode, formatter) {
|
|
|
3961
4112
|
}
|
|
3962
4113
|
}
|
|
3963
4114
|
function createFixDriftCommand() {
|
|
3964
|
-
const command = new
|
|
4115
|
+
const command = new Command29("fix-drift").description("Auto-fix entropy issues (doc drift, dead code)").option("--no-dry-run", "Actually apply fixes (default is dry-run mode)").action(async (opts, cmd) => {
|
|
3965
4116
|
const globalOpts = cmd.optsWithGlobals();
|
|
3966
4117
|
const mode = resolveOutputMode(globalOpts);
|
|
3967
4118
|
const formatter = new OutputFormatter(mode);
|
|
@@ -3991,14 +4142,14 @@ function createFixDriftCommand() {
|
|
|
3991
4142
|
}
|
|
3992
4143
|
|
|
3993
4144
|
// src/commands/gateway/index.ts
|
|
3994
|
-
import { Command as
|
|
4145
|
+
import { Command as Command32 } from "commander";
|
|
3995
4146
|
|
|
3996
4147
|
// src/commands/gateway/token.ts
|
|
3997
|
-
import { Command as
|
|
3998
|
-
import { resolve as
|
|
4148
|
+
import { Command as Command30 } from "commander";
|
|
4149
|
+
import { resolve as resolve15 } from "path";
|
|
3999
4150
|
import { TokenStore } from "@harness-engineering/orchestrator";
|
|
4000
4151
|
function getStore() {
|
|
4001
|
-
const p = process.env["HARNESS_TOKENS_PATH"] ??
|
|
4152
|
+
const p = process.env["HARNESS_TOKENS_PATH"] ?? resolve15(".harness", "tokens.json");
|
|
4002
4153
|
return new TokenStore(p);
|
|
4003
4154
|
}
|
|
4004
4155
|
async function runTokenCreate(opts) {
|
|
@@ -4015,7 +4166,7 @@ async function runTokenRevoke(id) {
|
|
|
4015
4166
|
return store.revoke(id);
|
|
4016
4167
|
}
|
|
4017
4168
|
function createTokenCommand() {
|
|
4018
|
-
const cmd = new
|
|
4169
|
+
const cmd = new Command30("token").description("Manage Gateway API tokens");
|
|
4019
4170
|
cmd.command("create").description("Create a new auth token. Secret is shown ONCE.").requiredOption("--name <name>", "Human label").requiredOption("--scopes <scopes>", "Comma-separated scopes").option("--bridge <kind>", "slack | discord | github-app | custom").option("--tenant <id>", "Tenant identifier").option("--expires <iso>", "Expiry ISO-8601").action(
|
|
4020
4171
|
async (opts) => {
|
|
4021
4172
|
const scopes = opts.scopes.split(",").map((s) => s.trim());
|
|
@@ -4044,12 +4195,12 @@ function createTokenCommand() {
|
|
|
4044
4195
|
}
|
|
4045
4196
|
|
|
4046
4197
|
// src/commands/gateway/deliveries.ts
|
|
4047
|
-
import { Command as
|
|
4198
|
+
import { Command as Command31 } from "commander";
|
|
4048
4199
|
import { createInterface } from "readline";
|
|
4049
|
-
import { resolve as
|
|
4200
|
+
import { resolve as resolve16 } from "path";
|
|
4050
4201
|
import { WebhookQueue } from "@harness-engineering/orchestrator";
|
|
4051
4202
|
function resolveQueuePath() {
|
|
4052
|
-
return process.env["HARNESS_WEBHOOK_QUEUE_PATH"] ??
|
|
4203
|
+
return process.env["HARNESS_WEBHOOK_QUEUE_PATH"] ?? resolve16(".harness", "webhook-queue.sqlite");
|
|
4053
4204
|
}
|
|
4054
4205
|
function getQueue() {
|
|
4055
4206
|
return new WebhookQueue(resolveQueuePath());
|
|
@@ -4084,7 +4235,7 @@ function promptYesNo(message) {
|
|
|
4084
4235
|
});
|
|
4085
4236
|
}
|
|
4086
4237
|
function createDeliveriesCommand() {
|
|
4087
|
-
const cmd = new
|
|
4238
|
+
const cmd = new Command31("deliveries").description("Inspect and manage webhook delivery queue");
|
|
4088
4239
|
cmd.command("list").description("List delivery queue entries (newest first; capped at 200 rows)").option("--status <status>", "Filter by status: pending|failed|delivered|dead").option("--subscription <id>", "Filter by subscription ID").action((opts) => {
|
|
4089
4240
|
const queue = getQueue();
|
|
4090
4241
|
try {
|
|
@@ -4144,14 +4295,14 @@ function createDeliveriesCommand() {
|
|
|
4144
4295
|
|
|
4145
4296
|
// src/commands/gateway/index.ts
|
|
4146
4297
|
function createGatewayCommand() {
|
|
4147
|
-
const cmd = new
|
|
4298
|
+
const cmd = new Command32("gateway").description("Gateway API administration");
|
|
4148
4299
|
cmd.addCommand(createTokenCommand());
|
|
4149
4300
|
cmd.addCommand(createDeliveriesCommand());
|
|
4150
4301
|
return cmd;
|
|
4151
4302
|
}
|
|
4152
4303
|
|
|
4153
4304
|
// src/commands/generate.ts
|
|
4154
|
-
import { Command as
|
|
4305
|
+
import { Command as Command33 } from "commander";
|
|
4155
4306
|
function validatePlatforms(platforms) {
|
|
4156
4307
|
for (const p of platforms) {
|
|
4157
4308
|
if (!VALID_PLATFORMS.includes(p)) {
|
|
@@ -4211,22 +4362,22 @@ async function runGenerateAction(opts, globalOpts) {
|
|
|
4211
4362
|
}
|
|
4212
4363
|
}
|
|
4213
4364
|
function createGenerateCommand() {
|
|
4214
|
-
return new
|
|
4365
|
+
return new Command33("generate").description("Generate all platform integrations (slash commands + agent definitions)").option("--platforms <list>", "Target platforms (comma-separated)", "claude-code,gemini-cli").option("--global", "Write to global directories", false).option("--include-global", "Include built-in global skills", false).option("--output <dir>", "Custom output directory").option("--dry-run", "Show what would change without writing", false).option("--yes", "Skip deletion confirmation prompts", false).action(async (opts, cmd) => {
|
|
4215
4366
|
await runGenerateAction(opts, cmd.optsWithGlobals());
|
|
4216
4367
|
});
|
|
4217
4368
|
}
|
|
4218
4369
|
|
|
4219
4370
|
// src/commands/graph/index.ts
|
|
4220
|
-
import { Command as
|
|
4371
|
+
import { Command as Command36 } from "commander";
|
|
4221
4372
|
|
|
4222
4373
|
// src/commands/graph/status.ts
|
|
4223
|
-
import * as
|
|
4374
|
+
import * as path17 from "path";
|
|
4224
4375
|
async function readConnectorSyncStatus(graphDir) {
|
|
4225
|
-
const
|
|
4376
|
+
const fs49 = await import("fs/promises");
|
|
4226
4377
|
const result = {};
|
|
4227
4378
|
try {
|
|
4228
|
-
const syncMetaPath =
|
|
4229
|
-
const syncMeta = JSON.parse(await
|
|
4379
|
+
const syncMetaPath = path17.join(graphDir, "sync-metadata.json");
|
|
4380
|
+
const syncMeta = JSON.parse(await fs49.readFile(syncMetaPath, "utf-8"));
|
|
4230
4381
|
for (const [name, data] of Object.entries(syncMeta.connectors ?? {})) {
|
|
4231
4382
|
result[name] = data.lastSyncTimestamp;
|
|
4232
4383
|
}
|
|
@@ -4236,7 +4387,7 @@ async function readConnectorSyncStatus(graphDir) {
|
|
|
4236
4387
|
}
|
|
4237
4388
|
async function runGraphStatus(projectPath) {
|
|
4238
4389
|
const { GraphStore: GraphStore2, loadGraphMetadata } = await import("./dist-OWHMNL4W.js");
|
|
4239
|
-
const graphDir =
|
|
4390
|
+
const graphDir = path17.join(projectPath, ".harness", "graph");
|
|
4240
4391
|
const metaResult = await loadGraphMetadata(graphDir);
|
|
4241
4392
|
if (metaResult.status === "not_found") {
|
|
4242
4393
|
return { status: "no_graph", message: "No graph found. Run `harness scan` first." };
|
|
@@ -4285,10 +4436,10 @@ async function runGraphStatus(projectPath) {
|
|
|
4285
4436
|
}
|
|
4286
4437
|
|
|
4287
4438
|
// src/commands/graph/export.ts
|
|
4288
|
-
import * as
|
|
4439
|
+
import * as path18 from "path";
|
|
4289
4440
|
async function runGraphExport(projectPath, format) {
|
|
4290
4441
|
const { GraphStore: GraphStore2 } = await import("./dist-OWHMNL4W.js");
|
|
4291
|
-
const graphDir =
|
|
4442
|
+
const graphDir = path18.join(projectPath, ".harness", "graph");
|
|
4292
4443
|
const store = new GraphStore2();
|
|
4293
4444
|
const loaded = await store.load(graphDir);
|
|
4294
4445
|
if (!loaded) throw new Error("No graph found. Run `harness scan` first.");
|
|
@@ -4317,15 +4468,15 @@ async function runGraphExport(projectPath, format) {
|
|
|
4317
4468
|
}
|
|
4318
4469
|
|
|
4319
4470
|
// src/commands/graph/index.ts
|
|
4320
|
-
import * as
|
|
4471
|
+
import * as path21 from "path";
|
|
4321
4472
|
|
|
4322
4473
|
// src/commands/graph/query.ts
|
|
4323
|
-
import { Command as
|
|
4324
|
-
import * as
|
|
4474
|
+
import { Command as Command34 } from "commander";
|
|
4475
|
+
import * as path19 from "path";
|
|
4325
4476
|
async function runQuery(projectPath, rootNodeId, opts) {
|
|
4326
4477
|
const { GraphStore: GraphStore2, ContextQL } = await import("./dist-OWHMNL4W.js");
|
|
4327
4478
|
const store = new GraphStore2();
|
|
4328
|
-
const graphDir =
|
|
4479
|
+
const graphDir = path19.join(projectPath, ".harness", "graph");
|
|
4329
4480
|
const loaded = await store.load(graphDir);
|
|
4330
4481
|
if (!loaded) throw new Error("No graph found. Run `harness scan` first.");
|
|
4331
4482
|
const params = {
|
|
@@ -4347,7 +4498,7 @@ function printQueryResult(result) {
|
|
|
4347
4498
|
}
|
|
4348
4499
|
}
|
|
4349
4500
|
async function runQueryAction(rootNodeId, opts, globalOpts) {
|
|
4350
|
-
const projectPath =
|
|
4501
|
+
const projectPath = path19.resolve(globalOpts.config ? path19.dirname(globalOpts.config) : ".");
|
|
4351
4502
|
try {
|
|
4352
4503
|
const result = await runQuery(projectPath, rootNodeId, {
|
|
4353
4504
|
depth: parseInt(opts.depth),
|
|
@@ -4366,19 +4517,19 @@ async function runQueryAction(rootNodeId, opts, globalOpts) {
|
|
|
4366
4517
|
}
|
|
4367
4518
|
}
|
|
4368
4519
|
function createQueryCommand() {
|
|
4369
|
-
return new
|
|
4520
|
+
return new Command34("query").description("Query the knowledge graph").argument("<rootNodeId>", "Starting node ID").option("--depth <n>", "Max traversal depth", "3").option("--types <types>", "Comma-separated node types to include").option("--edges <edges>", "Comma-separated edge types to include").option("--bidirectional", "Traverse both directions").action(async (rootNodeId, opts, cmd) => {
|
|
4370
4521
|
await runQueryAction(rootNodeId, opts, cmd.optsWithGlobals());
|
|
4371
4522
|
});
|
|
4372
4523
|
}
|
|
4373
4524
|
|
|
4374
4525
|
// src/commands/graph/ingest.ts
|
|
4375
|
-
import { Command as
|
|
4376
|
-
import * as
|
|
4526
|
+
import { Command as Command35 } from "commander";
|
|
4527
|
+
import * as path20 from "path";
|
|
4377
4528
|
async function loadConnectorConfig(projectPath, source) {
|
|
4378
4529
|
try {
|
|
4379
|
-
const
|
|
4380
|
-
const configPath =
|
|
4381
|
-
const config = JSON.parse(await
|
|
4530
|
+
const fs49 = await import("fs/promises");
|
|
4531
|
+
const configPath = path20.join(projectPath, "harness.config.json");
|
|
4532
|
+
const config = JSON.parse(await fs49.readFile(configPath, "utf-8"));
|
|
4382
4533
|
const connector = config.graph?.connectors?.[source];
|
|
4383
4534
|
return connector ?? {};
|
|
4384
4535
|
} catch {
|
|
@@ -4387,9 +4538,9 @@ async function loadConnectorConfig(projectPath, source) {
|
|
|
4387
4538
|
}
|
|
4388
4539
|
async function ingestBusinessKnowledge(bk, projectPath) {
|
|
4389
4540
|
return mergeResults(
|
|
4390
|
-
await bk.ingest(
|
|
4391
|
-
await bk.ingestSolutions(
|
|
4392
|
-
await bk.ingestStrategy(
|
|
4541
|
+
await bk.ingest(path20.join(projectPath, "docs", "knowledge")),
|
|
4542
|
+
await bk.ingestSolutions(path20.join(projectPath, "docs", "solutions")),
|
|
4543
|
+
await bk.ingestStrategy(path20.join(projectPath, "STRATEGY.md"))
|
|
4393
4544
|
);
|
|
4394
4545
|
}
|
|
4395
4546
|
function mergeResults(...results) {
|
|
@@ -4429,7 +4580,7 @@ async function runIngest(projectPath, source, opts) {
|
|
|
4429
4580
|
FigmaConnector,
|
|
4430
4581
|
MiroConnector
|
|
4431
4582
|
} = await import("./dist-OWHMNL4W.js");
|
|
4432
|
-
const graphDir =
|
|
4583
|
+
const graphDir = path20.join(projectPath, ".harness", "graph");
|
|
4433
4584
|
const store = new GraphStore2();
|
|
4434
4585
|
await store.load(graphDir);
|
|
4435
4586
|
const ingestOptions = loadIngestOptions(projectPath);
|
|
@@ -4441,11 +4592,11 @@ async function runIngest(projectPath, source, opts) {
|
|
|
4441
4592
|
const bkInst = new BusinessKnowledgeIngestor(store);
|
|
4442
4593
|
const bkResult = await ingestBusinessKnowledge(bkInst, projectPath);
|
|
4443
4594
|
const reqResult = await new RequirementIngestor(store).ingestSpecs(
|
|
4444
|
-
|
|
4595
|
+
path20.join(projectPath, "docs", "changes")
|
|
4445
4596
|
);
|
|
4446
4597
|
const gitResult = await new GitIngestor(store).ingest(projectPath);
|
|
4447
4598
|
const { createExtractionRunner } = await import("./dist-OWHMNL4W.js");
|
|
4448
|
-
const extractedDir =
|
|
4599
|
+
const extractedDir = path20.join(projectPath, ".harness", "knowledge", "extracted");
|
|
4449
4600
|
const signalsResult = await createExtractionRunner().run(projectPath, store, extractedDir);
|
|
4450
4601
|
const syncManager = new SyncManager(store, graphDir);
|
|
4451
4602
|
const connectorMap = {
|
|
@@ -4490,12 +4641,12 @@ async function runIngest(projectPath, source, opts) {
|
|
|
4490
4641
|
break;
|
|
4491
4642
|
case "requirements":
|
|
4492
4643
|
result = await new RequirementIngestor(store).ingestSpecs(
|
|
4493
|
-
|
|
4644
|
+
path20.join(projectPath, "docs", "changes")
|
|
4494
4645
|
);
|
|
4495
4646
|
break;
|
|
4496
4647
|
case "business-signals": {
|
|
4497
4648
|
const { createExtractionRunner } = await import("./dist-OWHMNL4W.js");
|
|
4498
|
-
const extractedDir =
|
|
4649
|
+
const extractedDir = path20.join(projectPath, ".harness", "knowledge", "extracted");
|
|
4499
4650
|
result = await createExtractionRunner().run(projectPath, store, extractedDir);
|
|
4500
4651
|
break;
|
|
4501
4652
|
}
|
|
@@ -4544,7 +4695,7 @@ async function handleIngestAction(opts, cmd) {
|
|
|
4544
4695
|
process.exit(1);
|
|
4545
4696
|
}
|
|
4546
4697
|
const globalOpts = cmd.optsWithGlobals();
|
|
4547
|
-
const projectPath =
|
|
4698
|
+
const projectPath = path20.resolve(globalOpts.config ? path20.dirname(globalOpts.config) : ".");
|
|
4548
4699
|
try {
|
|
4549
4700
|
const runOpts = {};
|
|
4550
4701
|
if (opts.full !== void 0) runOpts.full = opts.full;
|
|
@@ -4561,7 +4712,7 @@ async function handleIngestAction(opts, cmd) {
|
|
|
4561
4712
|
}
|
|
4562
4713
|
}
|
|
4563
4714
|
function createIngestCommand() {
|
|
4564
|
-
return new
|
|
4715
|
+
return new Command35("ingest").description("Ingest data into the knowledge graph").option(
|
|
4565
4716
|
"--source <name>",
|
|
4566
4717
|
"Source to ingest (code, knowledge, git, requirements, business-signals, jira, slack, ci, confluence, figma, miro)"
|
|
4567
4718
|
).option("--all", "Run all sources (code, knowledge, git, and configured connectors)").option("--full", "Force full re-ingestion").action(handleIngestAction);
|
|
@@ -4569,7 +4720,7 @@ function createIngestCommand() {
|
|
|
4569
4720
|
|
|
4570
4721
|
// src/commands/graph/index.ts
|
|
4571
4722
|
function resolveProjectPath(globalOpts) {
|
|
4572
|
-
return
|
|
4723
|
+
return path21.resolve(globalOpts.config ? path21.dirname(globalOpts.config) : ".");
|
|
4573
4724
|
}
|
|
4574
4725
|
function printGraphStatus(result) {
|
|
4575
4726
|
if (result.status === "no_graph" || result.status === "schema_mismatch") {
|
|
@@ -4615,19 +4766,19 @@ async function runExportAction(opts, cmd) {
|
|
|
4615
4766
|
}
|
|
4616
4767
|
}
|
|
4617
4768
|
function createGraphCommand() {
|
|
4618
|
-
const graph = new
|
|
4769
|
+
const graph = new Command36("graph").description("Knowledge graph management");
|
|
4619
4770
|
graph.command("status").description("Show graph statistics").action(runStatusAction);
|
|
4620
4771
|
graph.command("export").description("Export graph").requiredOption("--format <format>", "Output format (json, mermaid)").action(runExportAction);
|
|
4621
4772
|
return graph;
|
|
4622
4773
|
}
|
|
4623
4774
|
|
|
4624
4775
|
// src/commands/hooks/index.ts
|
|
4625
|
-
import { Command as
|
|
4776
|
+
import { Command as Command41 } from "commander";
|
|
4626
4777
|
|
|
4627
4778
|
// src/commands/hooks/init.ts
|
|
4628
|
-
import { Command as
|
|
4779
|
+
import { Command as Command37 } from "commander";
|
|
4629
4780
|
import * as fs9 from "fs";
|
|
4630
|
-
import * as
|
|
4781
|
+
import * as path22 from "path";
|
|
4631
4782
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4632
4783
|
|
|
4633
4784
|
// src/hooks/profiles.ts
|
|
@@ -4657,14 +4808,14 @@ var PROFILES = {
|
|
|
4657
4808
|
|
|
4658
4809
|
// src/commands/hooks/init.ts
|
|
4659
4810
|
var __filename2 = fileURLToPath2(import.meta.url);
|
|
4660
|
-
var __dirname2 =
|
|
4811
|
+
var __dirname2 = path22.dirname(__filename2);
|
|
4661
4812
|
var VALID_PROFILES = ["minimal", "standard", "strict"];
|
|
4662
4813
|
function resolveHookSourceDir() {
|
|
4663
4814
|
const candidates = [
|
|
4664
4815
|
// Dev layout: src/commands/hooks/ → ../../hooks/
|
|
4665
|
-
|
|
4816
|
+
path22.resolve(__dirname2, "..", "..", "hooks"),
|
|
4666
4817
|
// Bundled layout: dist/ → ./hooks/ (copied by copy-assets.mjs)
|
|
4667
|
-
|
|
4818
|
+
path22.resolve(__dirname2, "hooks")
|
|
4668
4819
|
];
|
|
4669
4820
|
for (const candidate of candidates) {
|
|
4670
4821
|
if (fs9.existsSync(candidate)) {
|
|
@@ -4704,12 +4855,12 @@ function mergeSettings(existing, hooksConfig) {
|
|
|
4704
4855
|
}
|
|
4705
4856
|
function initHooks(options) {
|
|
4706
4857
|
const { profile, projectDir } = options;
|
|
4707
|
-
const hooksDestDir =
|
|
4858
|
+
const hooksDestDir = path22.join(projectDir, ".harness", "hooks");
|
|
4708
4859
|
fs9.mkdirSync(hooksDestDir, { recursive: true });
|
|
4709
4860
|
if (fs9.existsSync(hooksDestDir)) {
|
|
4710
4861
|
for (const entry of fs9.readdirSync(hooksDestDir)) {
|
|
4711
4862
|
if (entry.endsWith(".js")) {
|
|
4712
|
-
fs9.unlinkSync(
|
|
4863
|
+
fs9.unlinkSync(path22.join(hooksDestDir, entry));
|
|
4713
4864
|
}
|
|
4714
4865
|
}
|
|
4715
4866
|
}
|
|
@@ -4718,18 +4869,18 @@ function initHooks(options) {
|
|
|
4718
4869
|
const activeNames = PROFILES[profile];
|
|
4719
4870
|
const activeScripts = HOOK_SCRIPTS.filter((h) => activeNames.includes(h.name));
|
|
4720
4871
|
for (const script of activeScripts) {
|
|
4721
|
-
const srcFile =
|
|
4722
|
-
const destFile =
|
|
4872
|
+
const srcFile = path22.join(sourceDir, `${script.name}.js`);
|
|
4873
|
+
const destFile = path22.join(hooksDestDir, `${script.name}.js`);
|
|
4723
4874
|
if (fs9.existsSync(srcFile)) {
|
|
4724
4875
|
fs9.copyFileSync(srcFile, destFile);
|
|
4725
4876
|
copiedScripts.push(script.name);
|
|
4726
4877
|
}
|
|
4727
4878
|
}
|
|
4728
|
-
const profilePath =
|
|
4879
|
+
const profilePath = path22.join(hooksDestDir, "profile.json");
|
|
4729
4880
|
fs9.writeFileSync(profilePath, JSON.stringify({ profile }, null, 2) + "\n");
|
|
4730
|
-
const claudeDir =
|
|
4881
|
+
const claudeDir = path22.join(projectDir, ".claude");
|
|
4731
4882
|
fs9.mkdirSync(claudeDir, { recursive: true });
|
|
4732
|
-
const settingsPath =
|
|
4883
|
+
const settingsPath = path22.join(claudeDir, "settings.json");
|
|
4733
4884
|
let existing = {};
|
|
4734
4885
|
if (fs9.existsSync(settingsPath)) {
|
|
4735
4886
|
try {
|
|
@@ -4747,7 +4898,7 @@ function initHooks(options) {
|
|
|
4747
4898
|
return { copiedScripts, settingsPath, profilePath };
|
|
4748
4899
|
}
|
|
4749
4900
|
function createInitCommand2() {
|
|
4750
|
-
return new
|
|
4901
|
+
return new Command37("init").description("Install Claude Code hook configurations into the current project").option("--profile <profile>", "Hook profile: minimal, standard, or strict", "standard").action(async (opts, cmd) => {
|
|
4751
4902
|
const globalOpts = cmd.optsWithGlobals();
|
|
4752
4903
|
const profile = opts.profile;
|
|
4753
4904
|
if (!VALID_PROFILES.includes(profile)) {
|
|
@@ -4772,7 +4923,7 @@ function createInitCommand2() {
|
|
|
4772
4923
|
);
|
|
4773
4924
|
logger.info(`Profile: ${profile}`);
|
|
4774
4925
|
logger.info(
|
|
4775
|
-
`Settings: ${
|
|
4926
|
+
`Settings: ${path22.relative(projectDir, result.settingsPath).replaceAll("\\", "/")}`
|
|
4776
4927
|
);
|
|
4777
4928
|
logger.dim("Run 'harness hooks list' to see installed hooks");
|
|
4778
4929
|
}
|
|
@@ -4785,12 +4936,12 @@ function createInitCommand2() {
|
|
|
4785
4936
|
}
|
|
4786
4937
|
|
|
4787
4938
|
// src/commands/hooks/list.ts
|
|
4788
|
-
import { Command as
|
|
4939
|
+
import { Command as Command38 } from "commander";
|
|
4789
4940
|
import * as fs10 from "fs";
|
|
4790
|
-
import * as
|
|
4941
|
+
import * as path23 from "path";
|
|
4791
4942
|
function listHooks(projectDir) {
|
|
4792
|
-
const hooksDir =
|
|
4793
|
-
const profilePath =
|
|
4943
|
+
const hooksDir = path23.join(projectDir, ".harness", "hooks");
|
|
4944
|
+
const profilePath = path23.join(hooksDir, "profile.json");
|
|
4794
4945
|
if (!fs10.existsSync(profilePath)) {
|
|
4795
4946
|
return { installed: false, profile: null, hooks: [] };
|
|
4796
4947
|
}
|
|
@@ -4809,7 +4960,7 @@ function listHooks(projectDir) {
|
|
|
4809
4960
|
name: h.name,
|
|
4810
4961
|
event: h.event,
|
|
4811
4962
|
matcher: h.matcher,
|
|
4812
|
-
scriptPath:
|
|
4963
|
+
scriptPath: path23.join(".harness", "hooks", `${h.name}.js`)
|
|
4813
4964
|
}));
|
|
4814
4965
|
const result = { installed: true, profile, hooks };
|
|
4815
4966
|
if (warning) {
|
|
@@ -4818,7 +4969,7 @@ function listHooks(projectDir) {
|
|
|
4818
4969
|
return result;
|
|
4819
4970
|
}
|
|
4820
4971
|
function createListCommand() {
|
|
4821
|
-
return new
|
|
4972
|
+
return new Command38("list").description("Show installed hooks and active profile").action(async (_opts, cmd) => {
|
|
4822
4973
|
const globalOpts = cmd.optsWithGlobals();
|
|
4823
4974
|
const projectDir = process.cwd();
|
|
4824
4975
|
const result = listHooks(projectDir);
|
|
@@ -4839,12 +4990,12 @@ function createListCommand() {
|
|
|
4839
4990
|
}
|
|
4840
4991
|
|
|
4841
4992
|
// src/commands/hooks/remove.ts
|
|
4842
|
-
import { Command as
|
|
4993
|
+
import { Command as Command39 } from "commander";
|
|
4843
4994
|
import * as fs11 from "fs";
|
|
4844
|
-
import * as
|
|
4995
|
+
import * as path24 from "path";
|
|
4845
4996
|
function removeHooks(projectDir) {
|
|
4846
|
-
const hooksDir =
|
|
4847
|
-
const settingsPath =
|
|
4997
|
+
const hooksDir = path24.join(projectDir, ".harness", "hooks");
|
|
4998
|
+
const settingsPath = path24.join(projectDir, ".claude", "settings.json");
|
|
4848
4999
|
let removed = false;
|
|
4849
5000
|
let settingsCleaned = false;
|
|
4850
5001
|
if (fs11.existsSync(hooksDir)) {
|
|
@@ -4869,7 +5020,7 @@ function removeHooks(projectDir) {
|
|
|
4869
5020
|
return { removed, hooksDir, settingsCleaned };
|
|
4870
5021
|
}
|
|
4871
5022
|
function createRemoveCommand() {
|
|
4872
|
-
return new
|
|
5023
|
+
return new Command39("remove").description("Remove harness-managed hooks from the current project").action(async (_opts, cmd) => {
|
|
4873
5024
|
const globalOpts = cmd.optsWithGlobals();
|
|
4874
5025
|
const projectDir = process.cwd();
|
|
4875
5026
|
const result = removeHooks(projectDir);
|
|
@@ -4891,16 +5042,16 @@ function createRemoveCommand() {
|
|
|
4891
5042
|
}
|
|
4892
5043
|
|
|
4893
5044
|
// src/commands/hooks/add.ts
|
|
4894
|
-
import { Command as
|
|
5045
|
+
import { Command as Command40 } from "commander";
|
|
4895
5046
|
import * as fs12 from "fs";
|
|
4896
|
-
import * as
|
|
5047
|
+
import * as path25 from "path";
|
|
4897
5048
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
4898
|
-
var __dirname3 =
|
|
5049
|
+
var __dirname3 = path25.dirname(fileURLToPath3(import.meta.url));
|
|
4899
5050
|
var ALIASES = {
|
|
4900
5051
|
sentinel: ["sentinel-pre", "sentinel-post"]
|
|
4901
5052
|
};
|
|
4902
5053
|
function hookSourceDir() {
|
|
4903
|
-
const d =
|
|
5054
|
+
const d = path25.resolve(__dirname3, "..", "..", "hooks");
|
|
4904
5055
|
if (fs12.existsSync(d)) return d;
|
|
4905
5056
|
throw new Error(`Hook scripts not found: ${d}`);
|
|
4906
5057
|
}
|
|
@@ -4918,11 +5069,11 @@ function addHooks(hookName, projectDir) {
|
|
|
4918
5069
|
const names = ALIASES[hookName] ?? [hookName];
|
|
4919
5070
|
const result = { added: [], alreadyInstalled: [], notFound: [] };
|
|
4920
5071
|
const srcDir = hookSourceDir();
|
|
4921
|
-
const destDir =
|
|
5072
|
+
const destDir = path25.join(projectDir, ".harness", "hooks");
|
|
4922
5073
|
fs12.mkdirSync(destDir, { recursive: true });
|
|
4923
|
-
const claudeDir =
|
|
5074
|
+
const claudeDir = path25.join(projectDir, ".claude");
|
|
4924
5075
|
fs12.mkdirSync(claudeDir, { recursive: true });
|
|
4925
|
-
const settingsPath =
|
|
5076
|
+
const settingsPath = path25.join(claudeDir, "settings.json");
|
|
4926
5077
|
const settings = readJson(settingsPath);
|
|
4927
5078
|
if (!settings.hooks) settings.hooks = {};
|
|
4928
5079
|
for (const name of names) {
|
|
@@ -4931,8 +5082,8 @@ function addHooks(hookName, projectDir) {
|
|
|
4931
5082
|
result.notFound.push(name);
|
|
4932
5083
|
continue;
|
|
4933
5084
|
}
|
|
4934
|
-
const src =
|
|
4935
|
-
const dest =
|
|
5085
|
+
const src = path25.join(srcDir, `${name}.js`);
|
|
5086
|
+
const dest = path25.join(destDir, `${name}.js`);
|
|
4936
5087
|
if (fs12.existsSync(dest)) {
|
|
4937
5088
|
result.alreadyInstalled.push(name);
|
|
4938
5089
|
} else if (!fs12.existsSync(src)) {
|
|
@@ -4948,7 +5099,7 @@ function addHooks(hookName, projectDir) {
|
|
|
4948
5099
|
return result;
|
|
4949
5100
|
}
|
|
4950
5101
|
function createAddCommand2() {
|
|
4951
|
-
return new
|
|
5102
|
+
return new Command40("add").argument("<hook-name>", "Hook name or alias (e.g., sentinel)").description("Add a hook without changing the profile").action(async (hookName, _opts, cmd) => {
|
|
4952
5103
|
const projectDir = process.cwd();
|
|
4953
5104
|
try {
|
|
4954
5105
|
const res = addHooks(hookName, projectDir);
|
|
@@ -4973,7 +5124,7 @@ function createAddCommand2() {
|
|
|
4973
5124
|
|
|
4974
5125
|
// src/commands/hooks/index.ts
|
|
4975
5126
|
function createHooksCommand() {
|
|
4976
|
-
const command = new
|
|
5127
|
+
const command = new Command41("hooks").description("Manage Claude Code hook configurations");
|
|
4977
5128
|
command.addCommand(createInitCommand2());
|
|
4978
5129
|
command.addCommand(createListCommand());
|
|
4979
5130
|
command.addCommand(createRemoveCommand());
|
|
@@ -4982,9 +5133,9 @@ function createHooksCommand() {
|
|
|
4982
5133
|
}
|
|
4983
5134
|
|
|
4984
5135
|
// src/commands/impact-preview.ts
|
|
4985
|
-
import { Command as
|
|
5136
|
+
import { Command as Command42 } from "commander";
|
|
4986
5137
|
import { execSync as execSync4 } from "child_process";
|
|
4987
|
-
import * as
|
|
5138
|
+
import * as path26 from "path";
|
|
4988
5139
|
import * as fs13 from "fs";
|
|
4989
5140
|
function getStagedFiles(cwd) {
|
|
4990
5141
|
try {
|
|
@@ -4999,7 +5150,7 @@ function getStagedFiles(cwd) {
|
|
|
4999
5150
|
}
|
|
5000
5151
|
function graphExists(projectPath) {
|
|
5001
5152
|
try {
|
|
5002
|
-
return fs13.existsSync(
|
|
5153
|
+
return fs13.existsSync(path26.join(projectPath, ".harness", "graph", "graph.json"));
|
|
5003
5154
|
} catch {
|
|
5004
5155
|
return false;
|
|
5005
5156
|
}
|
|
@@ -5008,7 +5159,7 @@ function extractNodeName(id) {
|
|
|
5008
5159
|
const parts = id.split(":");
|
|
5009
5160
|
if (parts.length > 1) {
|
|
5010
5161
|
const fullPath = parts.slice(1).join(":");
|
|
5011
|
-
return
|
|
5162
|
+
return path26.basename(fullPath);
|
|
5012
5163
|
}
|
|
5013
5164
|
return id;
|
|
5014
5165
|
}
|
|
@@ -5163,7 +5314,7 @@ function formatImpactOutput(stagedFiles, acc, options) {
|
|
|
5163
5314
|
return options.detailed ? formatDetailed(stagedFiles.length, merged, acc.aggregateCounts) : formatCompact(stagedFiles.length, merged, acc.aggregateCounts);
|
|
5164
5315
|
}
|
|
5165
5316
|
async function runImpactPreview(options) {
|
|
5166
|
-
const projectPath =
|
|
5317
|
+
const projectPath = path26.resolve(options.path ?? process.cwd());
|
|
5167
5318
|
const stagedFiles = getStagedFiles(projectPath);
|
|
5168
5319
|
if (stagedFiles.length === 0) return "Impact Preview: no staged changes";
|
|
5169
5320
|
if (!graphExists(projectPath)) {
|
|
@@ -5181,7 +5332,7 @@ async function runImpactPreview(options) {
|
|
|
5181
5332
|
return formatImpactOutput(stagedFiles, acc, options);
|
|
5182
5333
|
}
|
|
5183
5334
|
function createImpactPreviewCommand() {
|
|
5184
|
-
const command = new
|
|
5335
|
+
const command = new Command42("impact-preview").description("Show blast radius of staged changes using the knowledge graph").option("--detailed", "Show all affected files instead of top items").option("--per-file", "Show impact per staged file instead of aggregate").option("--path <dir>", "Project root (default: cwd)").action(async (opts) => {
|
|
5185
5336
|
const output = await runImpactPreview({
|
|
5186
5337
|
detailed: opts.detailed,
|
|
5187
5338
|
perFile: opts.perFile,
|
|
@@ -5195,24 +5346,24 @@ function createImpactPreviewCommand() {
|
|
|
5195
5346
|
|
|
5196
5347
|
// src/commands/init.ts
|
|
5197
5348
|
init_dist3();
|
|
5198
|
-
import { Command as
|
|
5349
|
+
import { Command as Command44 } from "commander";
|
|
5199
5350
|
import chalk3 from "chalk";
|
|
5200
5351
|
import * as fs16 from "fs";
|
|
5201
|
-
import * as
|
|
5352
|
+
import * as path29 from "path";
|
|
5202
5353
|
|
|
5203
5354
|
// src/commands/setup-mcp.ts
|
|
5204
|
-
import { Command as
|
|
5355
|
+
import { Command as Command43 } from "commander";
|
|
5205
5356
|
import * as fs15 from "fs";
|
|
5206
|
-
import * as
|
|
5357
|
+
import * as path28 from "path";
|
|
5207
5358
|
import * as os2 from "os";
|
|
5208
5359
|
import chalk2 from "chalk";
|
|
5209
5360
|
import * as clack from "@clack/prompts";
|
|
5210
5361
|
|
|
5211
5362
|
// src/integrations/toml.ts
|
|
5212
5363
|
import * as fs14 from "fs";
|
|
5213
|
-
import * as
|
|
5364
|
+
import * as path27 from "path";
|
|
5214
5365
|
function writeTomlMcpEntry(filePath, name, entry) {
|
|
5215
|
-
const dir =
|
|
5366
|
+
const dir = path27.dirname(filePath);
|
|
5216
5367
|
if (!fs14.existsSync(dir)) {
|
|
5217
5368
|
fs14.mkdirSync(dir, { recursive: true });
|
|
5218
5369
|
}
|
|
@@ -5342,6 +5493,8 @@ var ALL_MCP_TOOLS = [
|
|
|
5342
5493
|
"run_code_review",
|
|
5343
5494
|
"gather_context",
|
|
5344
5495
|
"assess_project",
|
|
5496
|
+
"canary_probe",
|
|
5497
|
+
"canary_recommend_framework",
|
|
5345
5498
|
"review_changes",
|
|
5346
5499
|
"detect_anomalies",
|
|
5347
5500
|
"ask_graph",
|
|
@@ -5399,6 +5552,8 @@ var ALL_MCP_TOOLS = [
|
|
|
5399
5552
|
"knowledge_craft",
|
|
5400
5553
|
// craft-pipeline #10 — security-craft LLM-judgment ceiling skill (AST-driven, conservative-confidence)
|
|
5401
5554
|
"security_craft",
|
|
5555
|
+
// outcome-eval — post-execution spec-satisfaction verdict (tiered confidence→authority)
|
|
5556
|
+
"outcome_eval",
|
|
5402
5557
|
// strategic-anchor — STRATEGY.md read/validate/write so projects can call
|
|
5403
5558
|
// these via MCP without needing @harness-engineering/core in node_modules
|
|
5404
5559
|
"validate_strategy",
|
|
@@ -5447,7 +5602,7 @@ function readJsonFile(filePath) {
|
|
|
5447
5602
|
}
|
|
5448
5603
|
}
|
|
5449
5604
|
function writeJsonFile(filePath, data) {
|
|
5450
|
-
const dir =
|
|
5605
|
+
const dir = path28.dirname(filePath);
|
|
5451
5606
|
if (!fs15.existsSync(dir)) {
|
|
5452
5607
|
fs15.mkdirSync(dir, { recursive: true });
|
|
5453
5608
|
}
|
|
@@ -5466,7 +5621,7 @@ function configureMcpServer(configPath) {
|
|
|
5466
5621
|
return true;
|
|
5467
5622
|
}
|
|
5468
5623
|
function addGeminiTrustedFolder(cwd) {
|
|
5469
|
-
const trustedPath =
|
|
5624
|
+
const trustedPath = path28.join(os2.homedir(), ".gemini", "trustedFolders.json");
|
|
5470
5625
|
const folders = readJsonFile(trustedPath) ?? {};
|
|
5471
5626
|
if (folders[cwd] === "TRUST_FOLDER") {
|
|
5472
5627
|
return false;
|
|
@@ -5480,7 +5635,7 @@ function setupMcp(cwd, client) {
|
|
|
5480
5635
|
const skipped = [];
|
|
5481
5636
|
let trustedFolder = false;
|
|
5482
5637
|
if (client === "all" || client === "claude") {
|
|
5483
|
-
const configPath =
|
|
5638
|
+
const configPath = path28.join(cwd, ".mcp.json");
|
|
5484
5639
|
if (configureMcpServer(configPath)) {
|
|
5485
5640
|
configured.push("Claude Code");
|
|
5486
5641
|
} else {
|
|
@@ -5488,7 +5643,7 @@ function setupMcp(cwd, client) {
|
|
|
5488
5643
|
}
|
|
5489
5644
|
}
|
|
5490
5645
|
if (client === "all" || client === "gemini") {
|
|
5491
|
-
const configPath =
|
|
5646
|
+
const configPath = path28.join(cwd, ".gemini", "settings.json");
|
|
5492
5647
|
if (configureMcpServer(configPath)) {
|
|
5493
5648
|
configured.push("Gemini CLI");
|
|
5494
5649
|
} else {
|
|
@@ -5497,7 +5652,7 @@ function setupMcp(cwd, client) {
|
|
|
5497
5652
|
trustedFolder = addGeminiTrustedFolder(cwd);
|
|
5498
5653
|
}
|
|
5499
5654
|
if (client === "all" || client === "codex") {
|
|
5500
|
-
const configPath =
|
|
5655
|
+
const configPath = path28.join(cwd, ".codex", "config.toml");
|
|
5501
5656
|
const alreadyConfigured = (() => {
|
|
5502
5657
|
if (!fs15.existsSync(configPath)) return false;
|
|
5503
5658
|
const content = fs15.readFileSync(configPath, "utf-8");
|
|
@@ -5515,7 +5670,7 @@ function setupMcp(cwd, client) {
|
|
|
5515
5670
|
}
|
|
5516
5671
|
}
|
|
5517
5672
|
if (client === "all" || client === "cursor") {
|
|
5518
|
-
const configPath =
|
|
5673
|
+
const configPath = path28.join(cwd, ".cursor", "mcp.json");
|
|
5519
5674
|
const existing = readJsonFile(configPath);
|
|
5520
5675
|
if (existing?.mcpServers?.["harness"]) {
|
|
5521
5676
|
skipped.push("Cursor");
|
|
@@ -5525,7 +5680,7 @@ function setupMcp(cwd, client) {
|
|
|
5525
5680
|
}
|
|
5526
5681
|
}
|
|
5527
5682
|
if (client === "all" || client === "opencode") {
|
|
5528
|
-
const configPath =
|
|
5683
|
+
const configPath = path28.join(cwd, "opencode.json");
|
|
5529
5684
|
const existing = readJsonFile(configPath);
|
|
5530
5685
|
if (existing?.mcp?.["harness"]) {
|
|
5531
5686
|
skipped.push("OpenCode");
|
|
@@ -5539,7 +5694,7 @@ function setupMcp(cwd, client) {
|
|
|
5539
5694
|
async function resolveCursorWithPicker(cwd, pick) {
|
|
5540
5695
|
const configured = [];
|
|
5541
5696
|
const skipped = [];
|
|
5542
|
-
const cursorConfigPath =
|
|
5697
|
+
const cursorConfigPath = path28.join(cwd, ".cursor", "mcp.json");
|
|
5543
5698
|
const existing = readJsonFile(cursorConfigPath);
|
|
5544
5699
|
if (existing?.mcpServers?.["harness"] && !pick) {
|
|
5545
5700
|
skipped.push("Cursor");
|
|
@@ -5583,7 +5738,7 @@ function printMcpResult(configured, skipped, trustedFolder) {
|
|
|
5583
5738
|
console.log("");
|
|
5584
5739
|
}
|
|
5585
5740
|
function createSetupMcpCommand() {
|
|
5586
|
-
return new
|
|
5741
|
+
return new Command43("setup-mcp").description("Configure MCP server for AI agent integration").option(
|
|
5587
5742
|
"--client <client>",
|
|
5588
5743
|
"Client to configure (claude, gemini, codex, cursor, opencode, all)",
|
|
5589
5744
|
"all"
|
|
@@ -5614,11 +5769,11 @@ function loadEngineAndTemplates(_options) {
|
|
|
5614
5769
|
}
|
|
5615
5770
|
function resolveInitDefaults(options) {
|
|
5616
5771
|
const cwd = options.cwd ?? process.cwd();
|
|
5617
|
-
return { cwd, name: options.name ??
|
|
5772
|
+
return { cwd, name: options.name ?? path29.basename(cwd), force: options.force ?? false };
|
|
5618
5773
|
}
|
|
5619
5774
|
async function runInit(options) {
|
|
5620
5775
|
const { cwd, name, force } = resolveInitDefaults(options);
|
|
5621
|
-
const configPath =
|
|
5776
|
+
const configPath = path29.join(cwd, "harness.config.json");
|
|
5622
5777
|
if (!force && fs16.existsSync(configPath)) {
|
|
5623
5778
|
return Err(
|
|
5624
5779
|
new CLIError("Project already initialized. Use --force to overwrite.", ExitCode.ERROR)
|
|
@@ -5672,6 +5827,18 @@ function scaffoldProject(engine, ctx) {
|
|
|
5672
5827
|
...language !== void 0 && { language }
|
|
5673
5828
|
});
|
|
5674
5829
|
if (!renderResult.ok) return Err(new CLIError(renderResult.error.message, ExitCode.ERROR));
|
|
5830
|
+
const ciResult = generateCIConfig({
|
|
5831
|
+
platform: "github",
|
|
5832
|
+
...language !== void 0 && { language }
|
|
5833
|
+
});
|
|
5834
|
+
if (ciResult.ok) {
|
|
5835
|
+
renderResult.value.files.push({
|
|
5836
|
+
relativePath: ciResult.value.filename,
|
|
5837
|
+
content: ciResult.value.content
|
|
5838
|
+
});
|
|
5839
|
+
} else {
|
|
5840
|
+
logger.warn(`CI workflow was not generated: ${ciResult.error.message}`);
|
|
5841
|
+
}
|
|
5675
5842
|
const existingProject = !force && engine.isExistingProject(cwd);
|
|
5676
5843
|
const writeResult = engine.write(renderResult.value, cwd, {
|
|
5677
5844
|
overwrite: force,
|
|
@@ -5729,7 +5896,7 @@ async function runInitAction2(opts) {
|
|
|
5729
5896
|
process.exit(ExitCode.SUCCESS);
|
|
5730
5897
|
}
|
|
5731
5898
|
function createInitCommand3() {
|
|
5732
|
-
const command = new
|
|
5899
|
+
const command = new Command44("init").description("Initialize a new harness-engineering project").option("-n, --name <name>", "Project name").option("-l, --level <level>", "Adoption level (basic, intermediate, advanced)", "basic").option("-t, --template <template>", "Specific template name (e.g. orchestrator)").option("--framework <framework>", "Framework overlay (nextjs)").option("--language <language>", "Target language (typescript, python, go, rust, java)").option("-f, --force", "Overwrite existing files").option("-y, --yes", "Use defaults without prompting").action(async (opts, cmd) => {
|
|
5733
5900
|
const globalOpts = cmd.optsWithGlobals();
|
|
5734
5901
|
await runInitAction2({
|
|
5735
5902
|
name: opts.name,
|
|
@@ -5745,7 +5912,7 @@ function createInitCommand3() {
|
|
|
5745
5912
|
|
|
5746
5913
|
// src/commands/insights.ts
|
|
5747
5914
|
init_dist();
|
|
5748
|
-
import { Command as
|
|
5915
|
+
import { Command as Command45 } from "commander";
|
|
5749
5916
|
function parseSkip2(raw) {
|
|
5750
5917
|
if (!raw) return [];
|
|
5751
5918
|
const valid = new Set(INSIGHTS_KEYS);
|
|
@@ -5783,10 +5950,10 @@ function renderPretty(report) {
|
|
|
5783
5950
|
}
|
|
5784
5951
|
}
|
|
5785
5952
|
function createInsightsCommand() {
|
|
5786
|
-
return new
|
|
5953
|
+
return new Command45("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) => {
|
|
5787
5954
|
const cwd = process.cwd();
|
|
5788
5955
|
const skip = parseSkip2(opts.skip);
|
|
5789
|
-
const { composeInsights } = await import("./dist-
|
|
5956
|
+
const { composeInsights } = await import("./dist-LHINSVK4.js");
|
|
5790
5957
|
const report = await composeInsights(cwd, { skip });
|
|
5791
5958
|
if (opts.json) {
|
|
5792
5959
|
console.log(JSON.stringify(report, null, 2));
|
|
@@ -5799,14 +5966,14 @@ function createInsightsCommand() {
|
|
|
5799
5966
|
// src/commands/install.ts
|
|
5800
5967
|
import * as fs21 from "fs";
|
|
5801
5968
|
import * as os5 from "os";
|
|
5802
|
-
import * as
|
|
5969
|
+
import * as path34 from "path";
|
|
5803
5970
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
5804
|
-
import { Command as
|
|
5971
|
+
import { Command as Command46 } from "commander";
|
|
5805
5972
|
import { parse as yamlParse } from "yaml";
|
|
5806
5973
|
|
|
5807
5974
|
// src/registry/npm-client.ts
|
|
5808
5975
|
import * as fs17 from "fs";
|
|
5809
|
-
import * as
|
|
5976
|
+
import * as path30 from "path";
|
|
5810
5977
|
import * as os3 from "os";
|
|
5811
5978
|
var NPM_REGISTRY = "https://registry.npmjs.org";
|
|
5812
5979
|
var FETCH_TIMEOUT_MS = 3e4;
|
|
@@ -5829,7 +5996,7 @@ function extractSkillName(packageName) {
|
|
|
5829
5996
|
function readNpmrcToken(registryUrl) {
|
|
5830
5997
|
const { hostname: hostname2, pathname } = new URL(registryUrl);
|
|
5831
5998
|
const registryPath = `//${hostname2}${pathname.replace(/\/$/, "")}/:_authToken=`;
|
|
5832
|
-
const candidates = [
|
|
5999
|
+
const candidates = [path30.join(process.cwd(), ".npmrc"), path30.join(os3.homedir(), ".npmrc")];
|
|
5833
6000
|
for (const npmrcPath of candidates) {
|
|
5834
6001
|
try {
|
|
5835
6002
|
const content = fs17.readFileSync(npmrcPath, "utf-8");
|
|
@@ -5928,12 +6095,12 @@ async function searchNpmRegistry(query, registryUrl) {
|
|
|
5928
6095
|
|
|
5929
6096
|
// src/registry/tarball.ts
|
|
5930
6097
|
import * as fs18 from "fs";
|
|
5931
|
-
import * as
|
|
6098
|
+
import * as path31 from "path";
|
|
5932
6099
|
import * as os4 from "os";
|
|
5933
6100
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
5934
6101
|
function extractTarball(tarballBuffer) {
|
|
5935
|
-
const tmpDir = fs18.mkdtempSync(
|
|
5936
|
-
const tarballPath =
|
|
6102
|
+
const tmpDir = fs18.mkdtempSync(path31.join(os4.tmpdir(), "harness-skill-install-"));
|
|
6103
|
+
const tarballPath = path31.join(tmpDir, "package.tgz");
|
|
5937
6104
|
try {
|
|
5938
6105
|
fs18.writeFileSync(tarballPath, tarballBuffer);
|
|
5939
6106
|
execFileSync2("tar", ["-xzf", tarballPath, "-C", tmpDir], {
|
|
@@ -5952,15 +6119,15 @@ function extractTarball(tarballBuffer) {
|
|
|
5952
6119
|
function placeSkillContent(extractedPkgDir, communityBaseDir, skillName, platforms) {
|
|
5953
6120
|
const files = fs18.readdirSync(extractedPkgDir);
|
|
5954
6121
|
for (const platform of platforms) {
|
|
5955
|
-
const targetDir =
|
|
6122
|
+
const targetDir = path31.join(communityBaseDir, platform, skillName);
|
|
5956
6123
|
if (fs18.existsSync(targetDir)) {
|
|
5957
6124
|
fs18.rmSync(targetDir, { recursive: true, force: true });
|
|
5958
6125
|
}
|
|
5959
6126
|
fs18.mkdirSync(targetDir, { recursive: true });
|
|
5960
6127
|
for (const file of files) {
|
|
5961
6128
|
if (file === "package.json" || file === "node_modules") continue;
|
|
5962
|
-
const srcPath =
|
|
5963
|
-
const destPath =
|
|
6129
|
+
const srcPath = path31.join(extractedPkgDir, file);
|
|
6130
|
+
const destPath = path31.join(targetDir, file);
|
|
5964
6131
|
const stat = fs18.statSync(srcPath);
|
|
5965
6132
|
if (stat.isDirectory()) {
|
|
5966
6133
|
fs18.cpSync(srcPath, destPath, { recursive: true });
|
|
@@ -5972,7 +6139,7 @@ function placeSkillContent(extractedPkgDir, communityBaseDir, skillName, platfor
|
|
|
5972
6139
|
}
|
|
5973
6140
|
function removeSkillContent(communityBaseDir, skillName, platforms) {
|
|
5974
6141
|
for (const platform of platforms) {
|
|
5975
|
-
const targetDir =
|
|
6142
|
+
const targetDir = path31.join(communityBaseDir, platform, skillName);
|
|
5976
6143
|
if (fs18.existsSync(targetDir)) {
|
|
5977
6144
|
fs18.rmSync(targetDir, { recursive: true, force: true });
|
|
5978
6145
|
}
|
|
@@ -6023,7 +6190,7 @@ function findDependentsOf(lockfile, targetPackageName) {
|
|
|
6023
6190
|
|
|
6024
6191
|
// src/registry/lockfile.ts
|
|
6025
6192
|
import * as fs19 from "fs";
|
|
6026
|
-
import * as
|
|
6193
|
+
import * as path32 from "path";
|
|
6027
6194
|
function createEmptyLockfile() {
|
|
6028
6195
|
return { version: 1, skills: {} };
|
|
6029
6196
|
}
|
|
@@ -6063,7 +6230,7 @@ function readLockfile2(filePath) {
|
|
|
6063
6230
|
return parsed;
|
|
6064
6231
|
}
|
|
6065
6232
|
function writeLockfile2(filePath, lockfile) {
|
|
6066
|
-
const dir =
|
|
6233
|
+
const dir = path32.dirname(filePath);
|
|
6067
6234
|
fs19.mkdirSync(dir, { recursive: true });
|
|
6068
6235
|
fs19.writeFileSync(filePath, sortedStringify(lockfile) + "\n", "utf-8");
|
|
6069
6236
|
}
|
|
@@ -6089,7 +6256,7 @@ function removeLockfileEntry(lockfile, name) {
|
|
|
6089
6256
|
|
|
6090
6257
|
// src/registry/bundled-skills.ts
|
|
6091
6258
|
import * as fs20 from "fs";
|
|
6092
|
-
import * as
|
|
6259
|
+
import * as path33 from "path";
|
|
6093
6260
|
function getBundledSkillNames(bundledSkillsDir) {
|
|
6094
6261
|
if (!fs20.existsSync(bundledSkillsDir)) {
|
|
6095
6262
|
return /* @__PURE__ */ new Set();
|
|
@@ -6098,7 +6265,7 @@ function getBundledSkillNames(bundledSkillsDir) {
|
|
|
6098
6265
|
const names = /* @__PURE__ */ new Set();
|
|
6099
6266
|
for (const entry of entries) {
|
|
6100
6267
|
try {
|
|
6101
|
-
const stat = fs20.statSync(
|
|
6268
|
+
const stat = fs20.statSync(path33.join(bundledSkillsDir, String(entry)));
|
|
6102
6269
|
if (stat.isDirectory()) {
|
|
6103
6270
|
names.add(String(entry));
|
|
6104
6271
|
}
|
|
@@ -6126,12 +6293,12 @@ function validateSkillYaml(parsed) {
|
|
|
6126
6293
|
function resolveCommunityBase(global) {
|
|
6127
6294
|
if (global) {
|
|
6128
6295
|
const communityBase2 = resolveGlobalCommunityBaseDir();
|
|
6129
|
-
return { communityBase: communityBase2, lockfilePath:
|
|
6296
|
+
return { communityBase: communityBase2, lockfilePath: path34.join(communityBase2, "skills-lock.json") };
|
|
6130
6297
|
}
|
|
6131
6298
|
const globalDir = resolveGlobalSkillsDir();
|
|
6132
|
-
const skillsDir =
|
|
6133
|
-
const communityBase =
|
|
6134
|
-
return { communityBase, lockfilePath:
|
|
6299
|
+
const skillsDir = path34.dirname(globalDir);
|
|
6300
|
+
const communityBase = path34.join(skillsDir, "community");
|
|
6301
|
+
return { communityBase, lockfilePath: path34.join(communityBase, "skills-lock.json") };
|
|
6135
6302
|
}
|
|
6136
6303
|
function parseGitHubRef(from) {
|
|
6137
6304
|
const ghPrefix = from.match(/^github:([^/]+)\/([^#]+?)(?:#(.+))?$/);
|
|
@@ -6147,7 +6314,7 @@ function parseGitHubRef(from) {
|
|
|
6147
6314
|
return null;
|
|
6148
6315
|
}
|
|
6149
6316
|
function cloneGitHubRepo(owner, repo, ref) {
|
|
6150
|
-
const tmpDir = fs21.mkdtempSync(
|
|
6317
|
+
const tmpDir = fs21.mkdtempSync(path34.join(os5.tmpdir(), "harness-gh-install-"));
|
|
6151
6318
|
const url = `https://github.com/${owner}/${repo}.git`;
|
|
6152
6319
|
try {
|
|
6153
6320
|
const cloneArgs = ["clone", "--depth", "1"];
|
|
@@ -6169,7 +6336,7 @@ function discoverSkillDirs(rootDir) {
|
|
|
6169
6336
|
function scan(dir, depth) {
|
|
6170
6337
|
if (depth > 3) return;
|
|
6171
6338
|
if (!fs21.existsSync(dir)) return;
|
|
6172
|
-
if (fs21.existsSync(
|
|
6339
|
+
if (fs21.existsSync(path34.join(dir, "skill.yaml"))) {
|
|
6173
6340
|
skillDirs.push(dir);
|
|
6174
6341
|
return;
|
|
6175
6342
|
}
|
|
@@ -6177,14 +6344,14 @@ function discoverSkillDirs(rootDir) {
|
|
|
6177
6344
|
for (const entry of entries) {
|
|
6178
6345
|
if (!entry.isDirectory()) continue;
|
|
6179
6346
|
if (entry.name.startsWith(".") || DEFAULT_SKIP_DIRS.has(entry.name)) continue;
|
|
6180
|
-
scan(
|
|
6347
|
+
scan(path34.join(dir, entry.name), depth + 1);
|
|
6181
6348
|
}
|
|
6182
6349
|
}
|
|
6183
6350
|
scan(rootDir, 0);
|
|
6184
6351
|
return skillDirs;
|
|
6185
6352
|
}
|
|
6186
6353
|
function resolveLocalPkgDir(fromPath) {
|
|
6187
|
-
const resolvedPath =
|
|
6354
|
+
const resolvedPath = path34.resolve(fromPath);
|
|
6188
6355
|
if (!fs21.existsSync(resolvedPath)) {
|
|
6189
6356
|
throw new Error(`--from path does not exist: ${resolvedPath}`);
|
|
6190
6357
|
}
|
|
@@ -6195,12 +6362,12 @@ function resolveLocalPkgDir(fromPath) {
|
|
|
6195
6362
|
if (resolvedPath.endsWith(".tgz") || resolvedPath.endsWith(".tar.gz")) {
|
|
6196
6363
|
const tarballBuffer = fs21.readFileSync(resolvedPath);
|
|
6197
6364
|
const extractDir = extractTarball(tarballBuffer);
|
|
6198
|
-
return { pkgDir:
|
|
6365
|
+
return { pkgDir: path34.join(extractDir, "package"), extractDir };
|
|
6199
6366
|
}
|
|
6200
6367
|
throw new Error(`--from path must be a directory or .tgz file. Got: ${resolvedPath}`);
|
|
6201
6368
|
}
|
|
6202
6369
|
function installSkillDir(pkgDir, resolvedPath, options) {
|
|
6203
|
-
const skillYamlPath =
|
|
6370
|
+
const skillYamlPath = path34.join(pkgDir, "skill.yaml");
|
|
6204
6371
|
if (!fs21.existsSync(skillYamlPath)) {
|
|
6205
6372
|
throw new Error(`No skill.yaml found at ${skillYamlPath}`);
|
|
6206
6373
|
}
|
|
@@ -6232,7 +6399,7 @@ function installSkillDir(pkgDir, resolvedPath, options) {
|
|
|
6232
6399
|
async function runLocalInstall(fromPath, options) {
|
|
6233
6400
|
const { pkgDir, extractDir } = resolveLocalPkgDir(fromPath);
|
|
6234
6401
|
try {
|
|
6235
|
-
return installSkillDir(pkgDir,
|
|
6402
|
+
return installSkillDir(pkgDir, path34.resolve(fromPath), options);
|
|
6236
6403
|
} finally {
|
|
6237
6404
|
if (extractDir) cleanupTempDir(extractDir);
|
|
6238
6405
|
}
|
|
@@ -6275,8 +6442,8 @@ async function runInstall(skillName, options) {
|
|
|
6275
6442
|
version: installed.map((r) => r.version).join(", ")
|
|
6276
6443
|
};
|
|
6277
6444
|
}
|
|
6278
|
-
const resolvedFrom =
|
|
6279
|
-
if (fs21.existsSync(resolvedFrom) && fs21.statSync(resolvedFrom).isDirectory() && !fs21.existsSync(
|
|
6445
|
+
const resolvedFrom = path34.resolve(options.from);
|
|
6446
|
+
if (fs21.existsSync(resolvedFrom) && fs21.statSync(resolvedFrom).isDirectory() && !fs21.existsSync(path34.join(resolvedFrom, "skill.yaml"))) {
|
|
6280
6447
|
const results = await runBulkInstall(resolvedFrom, options);
|
|
6281
6448
|
const installed = results.filter((r) => r.installed);
|
|
6282
6449
|
return {
|
|
@@ -6317,8 +6484,8 @@ async function runInstall(skillName, options) {
|
|
|
6317
6484
|
const extractDir = extractTarball(tarballBuffer);
|
|
6318
6485
|
let skillYaml;
|
|
6319
6486
|
try {
|
|
6320
|
-
const extractedPkgDir =
|
|
6321
|
-
const skillYamlPath =
|
|
6487
|
+
const extractedPkgDir = path34.join(extractDir, "package");
|
|
6488
|
+
const skillYamlPath = path34.join(extractedPkgDir, "skill.yaml");
|
|
6322
6489
|
if (!fs21.existsSync(skillYamlPath)) {
|
|
6323
6490
|
throw new Error(`contains invalid skill.yaml: file not found in package`);
|
|
6324
6491
|
}
|
|
@@ -6362,7 +6529,7 @@ async function runInstall(skillName, options) {
|
|
|
6362
6529
|
return result;
|
|
6363
6530
|
}
|
|
6364
6531
|
function createInstallCommand() {
|
|
6365
|
-
const cmd = new
|
|
6532
|
+
const cmd = new Command46("install");
|
|
6366
6533
|
cmd.description("Install skills from npm registry, local directory, or GitHub repository").argument("<skill>", 'Skill name, @harness-skills/scoped package, or "." for bulk install').option("--version <range>", "Semver range or exact version to install").option("--force", "Force reinstall even if same version is already installed").option(
|
|
6367
6534
|
"--from <source>",
|
|
6368
6535
|
"Install from local path, directory, or GitHub (github:owner/repo, https://github.com/owner/repo)"
|
|
@@ -6397,8 +6564,8 @@ function createInstallCommand() {
|
|
|
6397
6564
|
// src/commands/install-constraints.ts
|
|
6398
6565
|
init_dist3();
|
|
6399
6566
|
import * as fs22 from "fs/promises";
|
|
6400
|
-
import * as
|
|
6401
|
-
import { Command as
|
|
6567
|
+
import * as path35 from "path";
|
|
6568
|
+
import { Command as Command47 } from "commander";
|
|
6402
6569
|
import semver3 from "semver";
|
|
6403
6570
|
async function runInstallConstraints(options) {
|
|
6404
6571
|
const { source, configPath, lockfilePath } = options;
|
|
@@ -6595,7 +6762,7 @@ function isNodeError(err) {
|
|
|
6595
6762
|
return err instanceof Error && "code" in err;
|
|
6596
6763
|
}
|
|
6597
6764
|
function resolveConfigPath(opts) {
|
|
6598
|
-
if (opts.config) return
|
|
6765
|
+
if (opts.config) return path35.resolve(opts.config);
|
|
6599
6766
|
const found = findConfigFile();
|
|
6600
6767
|
if (!found.ok) {
|
|
6601
6768
|
logger.error(found.error.message);
|
|
@@ -6630,9 +6797,9 @@ function logInstallResult(val, opts) {
|
|
|
6630
6797
|
}
|
|
6631
6798
|
async function handleInstallConstraints(source, opts) {
|
|
6632
6799
|
const configPath = resolveConfigPath(opts);
|
|
6633
|
-
const projectRoot2 =
|
|
6634
|
-
const lockfilePath =
|
|
6635
|
-
const resolvedSource =
|
|
6800
|
+
const projectRoot2 = path35.dirname(configPath);
|
|
6801
|
+
const lockfilePath = path35.join(projectRoot2, ".harness", "constraints.lock.json");
|
|
6802
|
+
const resolvedSource = path35.resolve(source);
|
|
6636
6803
|
if (opts.forceLocal && opts.forcePackage) {
|
|
6637
6804
|
logger.error("Cannot use both --force-local and --force-package.");
|
|
6638
6805
|
process.exit(1);
|
|
@@ -6652,19 +6819,19 @@ async function handleInstallConstraints(source, opts) {
|
|
|
6652
6819
|
logInstallResult(result.value, opts);
|
|
6653
6820
|
}
|
|
6654
6821
|
function createInstallConstraintsCommand() {
|
|
6655
|
-
const cmd = new
|
|
6822
|
+
const cmd = new Command47("install-constraints");
|
|
6656
6823
|
cmd.description("Install a constraints bundle into the local harness config").argument("<source>", "Path to a .harness-constraints.json bundle file").option("--force-local", "Resolve all conflicts by keeping local values").option("--force-package", "Resolve all conflicts by using package values").option("--dry-run", "Show what would change without writing files").option("-c, --config <path>", "Path to harness.config.json").action(handleInstallConstraints);
|
|
6657
6824
|
return cmd;
|
|
6658
6825
|
}
|
|
6659
6826
|
|
|
6660
6827
|
// src/commands/integrations/index.ts
|
|
6661
|
-
import { Command as
|
|
6828
|
+
import { Command as Command52 } from "commander";
|
|
6662
6829
|
|
|
6663
6830
|
// src/commands/integrations/add.ts
|
|
6664
6831
|
init_dist3();
|
|
6665
|
-
import { Command as
|
|
6832
|
+
import { Command as Command48 } from "commander";
|
|
6666
6833
|
import * as fs23 from "fs";
|
|
6667
|
-
import * as
|
|
6834
|
+
import * as path36 from "path";
|
|
6668
6835
|
import chalk4 from "chalk";
|
|
6669
6836
|
function buildMcpEntry(def) {
|
|
6670
6837
|
const entry = { command: def.mcpConfig.command };
|
|
@@ -6673,14 +6840,14 @@ function buildMcpEntry(def) {
|
|
|
6673
6840
|
return entry;
|
|
6674
6841
|
}
|
|
6675
6842
|
function writeMcpEntries(cwd, defName, mcpEntry) {
|
|
6676
|
-
writeMcpEntry(
|
|
6677
|
-
const geminiDir =
|
|
6843
|
+
writeMcpEntry(path36.join(cwd, ".mcp.json"), defName, mcpEntry);
|
|
6844
|
+
const geminiDir = path36.join(cwd, ".gemini");
|
|
6678
6845
|
if (fs23.existsSync(geminiDir)) {
|
|
6679
|
-
writeMcpEntry(
|
|
6846
|
+
writeMcpEntry(path36.join(geminiDir, "settings.json"), defName, mcpEntry);
|
|
6680
6847
|
}
|
|
6681
6848
|
}
|
|
6682
6849
|
function updateIntegrationsConfig(cwd, defName) {
|
|
6683
|
-
const configPath =
|
|
6850
|
+
const configPath = path36.join(cwd, "harness.config.json");
|
|
6684
6851
|
const integConfig = readIntegrationsConfig(configPath);
|
|
6685
6852
|
if (!integConfig.enabled.includes(defName)) integConfig.enabled.push(defName);
|
|
6686
6853
|
integConfig.dismissed = integConfig.dismissed.filter((d) => d !== defName);
|
|
@@ -6726,7 +6893,7 @@ function printAddSuccess(value) {
|
|
|
6726
6893
|
}
|
|
6727
6894
|
}
|
|
6728
6895
|
function createAddIntegrationCommand() {
|
|
6729
|
-
return new
|
|
6896
|
+
return new Command48("add").description("Enable an MCP integration").argument("<name>", "Integration name (e.g. perplexity, augment-code)").action(async (name, _opts, cmd) => {
|
|
6730
6897
|
const globalOpts = cmd.optsWithGlobals();
|
|
6731
6898
|
const result = addIntegration(process.cwd(), name);
|
|
6732
6899
|
if (!result.ok) {
|
|
@@ -6740,8 +6907,8 @@ function createAddIntegrationCommand() {
|
|
|
6740
6907
|
}
|
|
6741
6908
|
|
|
6742
6909
|
// src/commands/integrations/list.ts
|
|
6743
|
-
import { Command as
|
|
6744
|
-
import * as
|
|
6910
|
+
import { Command as Command49 } from "commander";
|
|
6911
|
+
import * as path37 from "path";
|
|
6745
6912
|
import chalk5 from "chalk";
|
|
6746
6913
|
function printTier0Integrations(tier0, mcpServers) {
|
|
6747
6914
|
console.log(" Tier 0 (zero-config):");
|
|
@@ -6765,8 +6932,8 @@ function printTier1Integrations(tier1, mcpServers, dismissed) {
|
|
|
6765
6932
|
}
|
|
6766
6933
|
async function runListIntegrations(globalOpts) {
|
|
6767
6934
|
const cwd = process.cwd();
|
|
6768
|
-
const mcpConfig = readMcpConfig(
|
|
6769
|
-
const integConfig = readIntegrationsConfig(
|
|
6935
|
+
const mcpConfig = readMcpConfig(path37.join(cwd, ".mcp.json"));
|
|
6936
|
+
const integConfig = readIntegrationsConfig(path37.join(cwd, "harness.config.json"));
|
|
6770
6937
|
const mcpServers = mcpConfig.mcpServers ?? {};
|
|
6771
6938
|
if (globalOpts.json) {
|
|
6772
6939
|
const entries = INTEGRATION_REGISTRY.map((i) => ({
|
|
@@ -6797,14 +6964,14 @@ async function runListIntegrations(globalOpts) {
|
|
|
6797
6964
|
process.exit(ExitCode.SUCCESS);
|
|
6798
6965
|
}
|
|
6799
6966
|
function createListIntegrationsCommand() {
|
|
6800
|
-
return new
|
|
6967
|
+
return new Command49("list").description("Show all MCP integrations with status").action(async (_opts, cmd) => runListIntegrations(cmd.optsWithGlobals()));
|
|
6801
6968
|
}
|
|
6802
6969
|
|
|
6803
6970
|
// src/commands/integrations/remove.ts
|
|
6804
6971
|
init_dist3();
|
|
6805
|
-
import { Command as
|
|
6972
|
+
import { Command as Command50 } from "commander";
|
|
6806
6973
|
import * as fs24 from "fs";
|
|
6807
|
-
import * as
|
|
6974
|
+
import * as path38 from "path";
|
|
6808
6975
|
function removeIntegration(cwd, name) {
|
|
6809
6976
|
const def = INTEGRATION_REGISTRY.find((i) => i.name === name);
|
|
6810
6977
|
if (!def) {
|
|
@@ -6815,21 +6982,21 @@ function removeIntegration(cwd, name) {
|
|
|
6815
6982
|
)
|
|
6816
6983
|
);
|
|
6817
6984
|
}
|
|
6818
|
-
const mcpPath =
|
|
6985
|
+
const mcpPath = path38.join(cwd, ".mcp.json");
|
|
6819
6986
|
removeMcpEntry(mcpPath, def.name);
|
|
6820
|
-
const geminiDir =
|
|
6987
|
+
const geminiDir = path38.join(cwd, ".gemini");
|
|
6821
6988
|
if (fs24.existsSync(geminiDir)) {
|
|
6822
|
-
const geminiPath =
|
|
6989
|
+
const geminiPath = path38.join(geminiDir, "settings.json");
|
|
6823
6990
|
removeMcpEntry(geminiPath, def.name);
|
|
6824
6991
|
}
|
|
6825
|
-
const configPath =
|
|
6992
|
+
const configPath = path38.join(cwd, "harness.config.json");
|
|
6826
6993
|
const integConfig = readIntegrationsConfig(configPath);
|
|
6827
6994
|
integConfig.enabled = integConfig.enabled.filter((e) => e !== def.name);
|
|
6828
6995
|
writeIntegrationsConfig(configPath, integConfig);
|
|
6829
6996
|
return Ok(def.displayName);
|
|
6830
6997
|
}
|
|
6831
6998
|
function createRemoveIntegrationCommand() {
|
|
6832
|
-
return new
|
|
6999
|
+
return new Command50("remove").description("Remove an MCP integration").argument("<name>", "Integration name (e.g. perplexity, augment-code)").action(async (name, _opts, cmd) => {
|
|
6833
7000
|
const globalOpts = cmd.optsWithGlobals();
|
|
6834
7001
|
const cwd = process.cwd();
|
|
6835
7002
|
const result = removeIntegration(cwd, name);
|
|
@@ -6851,8 +7018,8 @@ function createRemoveIntegrationCommand() {
|
|
|
6851
7018
|
|
|
6852
7019
|
// src/commands/integrations/dismiss.ts
|
|
6853
7020
|
init_dist3();
|
|
6854
|
-
import { Command as
|
|
6855
|
-
import * as
|
|
7021
|
+
import { Command as Command51 } from "commander";
|
|
7022
|
+
import * as path39 from "path";
|
|
6856
7023
|
function dismissIntegration(cwd, name) {
|
|
6857
7024
|
const def = INTEGRATION_REGISTRY.find((i) => i.name === name);
|
|
6858
7025
|
if (!def) {
|
|
@@ -6863,7 +7030,7 @@ function dismissIntegration(cwd, name) {
|
|
|
6863
7030
|
)
|
|
6864
7031
|
);
|
|
6865
7032
|
}
|
|
6866
|
-
const configPath =
|
|
7033
|
+
const configPath = path39.join(cwd, "harness.config.json");
|
|
6867
7034
|
const integConfig = readIntegrationsConfig(configPath);
|
|
6868
7035
|
if (!integConfig.dismissed.includes(def.name)) {
|
|
6869
7036
|
integConfig.dismissed.push(def.name);
|
|
@@ -6873,7 +7040,7 @@ function dismissIntegration(cwd, name) {
|
|
|
6873
7040
|
return Ok(def.displayName);
|
|
6874
7041
|
}
|
|
6875
7042
|
function createDismissIntegrationCommand() {
|
|
6876
|
-
return new
|
|
7043
|
+
return new Command51("dismiss").description("Suppress doctor recommendations for an integration").argument("<name>", "Integration name (e.g. perplexity, augment-code)").action(async (name, _opts, cmd) => {
|
|
6877
7044
|
const globalOpts = cmd.optsWithGlobals();
|
|
6878
7045
|
const cwd = process.cwd();
|
|
6879
7046
|
const result = dismissIntegration(cwd, name);
|
|
@@ -6897,7 +7064,7 @@ function createDismissIntegrationCommand() {
|
|
|
6897
7064
|
|
|
6898
7065
|
// src/commands/integrations/index.ts
|
|
6899
7066
|
function createIntegrationsCommand() {
|
|
6900
|
-
const command = new
|
|
7067
|
+
const command = new Command52("integrations").description(
|
|
6901
7068
|
"Manage MCP peer integrations (add, list, remove, dismiss)"
|
|
6902
7069
|
);
|
|
6903
7070
|
command.addCommand(createListIntegrationsCommand());
|
|
@@ -6908,9 +7075,9 @@ function createIntegrationsCommand() {
|
|
|
6908
7075
|
}
|
|
6909
7076
|
|
|
6910
7077
|
// src/commands/knowledge-craft.ts
|
|
6911
|
-
import { Command as
|
|
7078
|
+
import { Command as Command53 } from "commander";
|
|
6912
7079
|
function createKnowledgeCraftCommand() {
|
|
6913
|
-
return new
|
|
7080
|
+
return new Command53("knowledge-craft").description(
|
|
6914
7081
|
"LLM-judgment critique of knowledge-entry quality (docs/knowledge/, excluding decisions/). Fifth non-design craft-pipeline ceiling skill; 7 seed rubrics (load-bearing-fact, earns-graph-place, carries-forward-decision, \u2026). Per-file critique."
|
|
6915
7082
|
).option("-f, --files <files...>", "Optional file scope (overrides discovery)").option(
|
|
6916
7083
|
"--exclude-dirs <dirs...>",
|
|
@@ -6958,10 +7125,10 @@ function printResult3(result, mode, _formatter) {
|
|
|
6958
7125
|
list.push(f);
|
|
6959
7126
|
byFile.set(f.target.file, list);
|
|
6960
7127
|
}
|
|
6961
|
-
for (const [file,
|
|
7128
|
+
for (const [file, fs49] of byFile) {
|
|
6962
7129
|
console.log(`
|
|
6963
7130
|
${file}`);
|
|
6964
|
-
for (const f of
|
|
7131
|
+
for (const f of fs49) {
|
|
6965
7132
|
console.log(` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.relative}`);
|
|
6966
7133
|
console.log(` ${f.message}`);
|
|
6967
7134
|
if (verbose) console.log(` source: ${f.cite.source}`);
|
|
@@ -6975,17 +7142,17 @@ ${file}`);
|
|
|
6975
7142
|
}
|
|
6976
7143
|
|
|
6977
7144
|
// src/commands/knowledge-pipeline.ts
|
|
6978
|
-
import { Command as
|
|
6979
|
-
import * as
|
|
7145
|
+
import { Command as Command54 } from "commander";
|
|
7146
|
+
import * as path40 from "path";
|
|
6980
7147
|
import chalk6 from "chalk";
|
|
6981
7148
|
function createKnowledgePipelineCommand() {
|
|
6982
|
-
return new
|
|
7149
|
+
return new Command54("knowledge-pipeline").description("Run knowledge extraction, drift detection, and gap analysis").option("--fix", "Enable convergence-based auto-remediation (default: detect-only)").option("--ci", "Non-interactive mode \u2014 apply safe fixes only, report everything else").option("--domain <name>", "Limit pipeline to a specific knowledge domain").option("--drift-check", "Exit 1 if unresolved drift exists (CI gate mode)").option("--analyze-images", "Enable vision model analysis of image files").option("--image-paths <paths>", "Comma-separated image file paths for analysis").option("--coverage", "Display per-domain coverage report").option("--check-contradictions", "Display cross-source contradiction report").action(async (opts, cmd) => {
|
|
6983
7150
|
const globalOpts = cmd.optsWithGlobals();
|
|
6984
7151
|
const projectDir = process.cwd();
|
|
6985
7152
|
try {
|
|
6986
|
-
const graphDir =
|
|
6987
|
-
const
|
|
6988
|
-
await
|
|
7153
|
+
const graphDir = path40.join(projectDir, ".harness", "graph");
|
|
7154
|
+
const fs49 = await import("fs/promises");
|
|
7155
|
+
await fs49.mkdir(graphDir, { recursive: true });
|
|
6989
7156
|
const { GraphStore: GraphStore2, KnowledgePipelineRunner } = await import("./dist-OWHMNL4W.js");
|
|
6990
7157
|
const store = new GraphStore2();
|
|
6991
7158
|
try {
|
|
@@ -7151,14 +7318,14 @@ function createKnowledgePipelineCommand() {
|
|
|
7151
7318
|
}
|
|
7152
7319
|
|
|
7153
7320
|
// src/commands/learnings/index.ts
|
|
7154
|
-
import { Command as
|
|
7321
|
+
import { Command as Command56 } from "commander";
|
|
7155
7322
|
|
|
7156
7323
|
// src/commands/learnings/prune.ts
|
|
7157
7324
|
init_dist3();
|
|
7158
|
-
import { Command as
|
|
7159
|
-
import * as
|
|
7325
|
+
import { Command as Command55 } from "commander";
|
|
7326
|
+
import * as path41 from "path";
|
|
7160
7327
|
async function handlePrune(opts) {
|
|
7161
|
-
const projectPath =
|
|
7328
|
+
const projectPath = path41.resolve(opts.path);
|
|
7162
7329
|
const result = await pruneLearnings(projectPath, opts.stream);
|
|
7163
7330
|
if (!result.ok) {
|
|
7164
7331
|
logger.error(result.error.message);
|
|
@@ -7197,23 +7364,23 @@ function printPatternProposals(patterns) {
|
|
|
7197
7364
|
);
|
|
7198
7365
|
}
|
|
7199
7366
|
function createPruneCommand() {
|
|
7200
|
-
return new
|
|
7367
|
+
return new Command55("prune").description(
|
|
7201
7368
|
"Analyze global learnings for patterns, present improvement proposals, and archive old entries"
|
|
7202
7369
|
).option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(handlePrune);
|
|
7203
7370
|
}
|
|
7204
7371
|
|
|
7205
7372
|
// src/commands/learnings/index.ts
|
|
7206
7373
|
function createLearningsCommand() {
|
|
7207
|
-
const command = new
|
|
7374
|
+
const command = new Command56("learnings").description("Learnings management commands");
|
|
7208
7375
|
command.addCommand(createPruneCommand());
|
|
7209
7376
|
return command;
|
|
7210
7377
|
}
|
|
7211
7378
|
|
|
7212
7379
|
// src/commands/linter/index.ts
|
|
7213
|
-
import { Command as
|
|
7380
|
+
import { Command as Command59 } from "commander";
|
|
7214
7381
|
|
|
7215
7382
|
// src/commands/linter/generate.ts
|
|
7216
|
-
import { Command as
|
|
7383
|
+
import { Command as Command57 } from "commander";
|
|
7217
7384
|
function formatGenerateError(e) {
|
|
7218
7385
|
switch (e.type) {
|
|
7219
7386
|
case "parse":
|
|
@@ -7257,7 +7424,7 @@ function handleSuccess(result, useJson) {
|
|
|
7257
7424
|
Generated ${result.rulesGenerated.length} rules to ${result.outputDir}`);
|
|
7258
7425
|
}
|
|
7259
7426
|
function createGenerateCommand2() {
|
|
7260
|
-
return new
|
|
7427
|
+
return new Command57("generate").description("Generate ESLint rules from harness-linter.yml").option("-c, --config <path>", "Path to harness-linter.yml", "./harness-linter.yml").option("-o, --output <dir>", "Override output directory").option("--clean", "Remove existing files before generating").option("--dry-run", "Preview without writing files").option("--json", "Output as JSON").option("--verbose", "Show detailed output").action(async (options) => {
|
|
7261
7428
|
try {
|
|
7262
7429
|
if (options.verbose) logger.info(`Parsing config: ${options.config}`);
|
|
7263
7430
|
const result = await generate({
|
|
@@ -7279,9 +7446,9 @@ function createGenerateCommand2() {
|
|
|
7279
7446
|
}
|
|
7280
7447
|
|
|
7281
7448
|
// src/commands/linter/validate.ts
|
|
7282
|
-
import { Command as
|
|
7449
|
+
import { Command as Command58 } from "commander";
|
|
7283
7450
|
function createValidateCommand() {
|
|
7284
|
-
return new
|
|
7451
|
+
return new Command58("validate").description("Validate harness-linter.yml config").option("-c, --config <path>", "Path to harness-linter.yml", "./harness-linter.yml").option("--json", "Output as JSON").action(async (options) => {
|
|
7285
7452
|
try {
|
|
7286
7453
|
const result = await validate({ configPath: options.config });
|
|
7287
7454
|
if (options.json) {
|
|
@@ -7300,7 +7467,7 @@ function createValidateCommand() {
|
|
|
7300
7467
|
|
|
7301
7468
|
// src/commands/linter/index.ts
|
|
7302
7469
|
function createLinterCommand() {
|
|
7303
|
-
const linter = new
|
|
7470
|
+
const linter = new Command59("linter").description(
|
|
7304
7471
|
"Generate and validate ESLint rules from YAML config"
|
|
7305
7472
|
);
|
|
7306
7473
|
linter.addCommand(createGenerateCommand2());
|
|
@@ -7309,8 +7476,8 @@ function createLinterCommand() {
|
|
|
7309
7476
|
}
|
|
7310
7477
|
|
|
7311
7478
|
// src/commands/maintenance.ts
|
|
7312
|
-
import { Command as
|
|
7313
|
-
import * as
|
|
7479
|
+
import { Command as Command60 } from "commander";
|
|
7480
|
+
import * as path42 from "path";
|
|
7314
7481
|
import * as fs25 from "fs";
|
|
7315
7482
|
import chalk7 from "chalk";
|
|
7316
7483
|
import {
|
|
@@ -7319,7 +7486,7 @@ import {
|
|
|
7319
7486
|
WorkflowLoader
|
|
7320
7487
|
} from "@harness-engineering/orchestrator";
|
|
7321
7488
|
async function loadMaintenanceConfig(cwd) {
|
|
7322
|
-
const workflowPath =
|
|
7489
|
+
const workflowPath = path42.join(cwd, "harness.orchestrator.md");
|
|
7323
7490
|
if (!fs25.existsSync(workflowPath)) return null;
|
|
7324
7491
|
const loader = new WorkflowLoader();
|
|
7325
7492
|
const result = await loader.loadWorkflow(workflowPath);
|
|
@@ -7401,11 +7568,11 @@ function printListTable(rows) {
|
|
|
7401
7568
|
}
|
|
7402
7569
|
}
|
|
7403
7570
|
function createMaintenanceCommand() {
|
|
7404
|
-
const command = new
|
|
7571
|
+
const command = new Command60("maintenance").description(
|
|
7405
7572
|
"Inspect built-in + custom maintenance tasks and their persisted outputs"
|
|
7406
7573
|
);
|
|
7407
7574
|
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) => {
|
|
7408
|
-
const cwd =
|
|
7575
|
+
const cwd = path42.resolve(opts.path);
|
|
7409
7576
|
const config = await loadMaintenanceConfig(cwd);
|
|
7410
7577
|
const tasks = mergeResolvedTasks(config);
|
|
7411
7578
|
const rows = tasks.map((t) => ({
|
|
@@ -7432,10 +7599,10 @@ async function runShow(taskId, opts) {
|
|
|
7432
7599
|
console.error(`Invalid task id '${taskId}' (must match ^[a-z0-9][a-z0-9-]*$).`);
|
|
7433
7600
|
process.exit(ExitCode.ERROR);
|
|
7434
7601
|
}
|
|
7435
|
-
const cwd =
|
|
7602
|
+
const cwd = path42.resolve(opts.path ?? ".");
|
|
7436
7603
|
const limit = Math.max(1, parseInt(opts.limit ?? "5", 10) || 5);
|
|
7437
7604
|
const store = new TaskOutputStore({
|
|
7438
|
-
rootDir:
|
|
7605
|
+
rootDir: path42.join(cwd, ".harness", "maintenance")
|
|
7439
7606
|
});
|
|
7440
7607
|
const entries = await store.list(taskId, limit, 0);
|
|
7441
7608
|
renderShow(taskId, entries, Boolean(opts.json));
|
|
@@ -7469,7 +7636,7 @@ function describeOrigin(origin) {
|
|
|
7469
7636
|
}
|
|
7470
7637
|
|
|
7471
7638
|
// src/commands/mcp.ts
|
|
7472
|
-
import { Command as
|
|
7639
|
+
import { Command as Command61 } from "commander";
|
|
7473
7640
|
var VALID_TIERS = /* @__PURE__ */ new Set(["core", "standard", "full"]);
|
|
7474
7641
|
function parseTier(value) {
|
|
7475
7642
|
const lower = value.toLowerCase();
|
|
@@ -7486,7 +7653,7 @@ function parseBudget(value) {
|
|
|
7486
7653
|
return n;
|
|
7487
7654
|
}
|
|
7488
7655
|
function createMcpCommand() {
|
|
7489
|
-
return new
|
|
7656
|
+
return new Command61("mcp").description("Start the MCP (Model Context Protocol) server on stdio").option("--tools <tools...>", "Only register the specified tools (used by Cursor integration)").option(
|
|
7490
7657
|
"--tier <core|standard|full>",
|
|
7491
7658
|
"Load a preset tool tier instead of all tools",
|
|
7492
7659
|
parseTier
|
|
@@ -7496,8 +7663,8 @@ function createMcpCommand() {
|
|
|
7496
7663
|
parseBudget
|
|
7497
7664
|
).action(async (opts) => {
|
|
7498
7665
|
const [{ startServer: startServer2, getToolDefinitions: getToolDefinitions2 }, { selectTier }] = await Promise.all([
|
|
7499
|
-
import("./mcp-
|
|
7500
|
-
import("./tool-tiers-
|
|
7666
|
+
import("./mcp-RK3SFVYB.js"),
|
|
7667
|
+
import("./tool-tiers-OKK5FE57.js")
|
|
7501
7668
|
]);
|
|
7502
7669
|
if (opts.tools && opts.tools.length > 0) {
|
|
7503
7670
|
await startServer2(opts.tools);
|
|
@@ -7518,9 +7685,9 @@ function createMcpCommand() {
|
|
|
7518
7685
|
|
|
7519
7686
|
// src/commands/mcp-guard.ts
|
|
7520
7687
|
init_dist3();
|
|
7521
|
-
import { Command as
|
|
7688
|
+
import { Command as Command62 } from "commander";
|
|
7522
7689
|
import * as fs26 from "fs";
|
|
7523
|
-
import * as
|
|
7690
|
+
import * as path43 from "path";
|
|
7524
7691
|
import chalk8 from "chalk";
|
|
7525
7692
|
function extractNpmPackages(config) {
|
|
7526
7693
|
const out = [];
|
|
@@ -7559,7 +7726,7 @@ function parseNpmSpec(spec) {
|
|
|
7559
7726
|
}
|
|
7560
7727
|
async function runMcpGuardCheck(options = {}) {
|
|
7561
7728
|
const cwd = options.cwd ?? process.cwd();
|
|
7562
|
-
const configPath = options.configPath ??
|
|
7729
|
+
const configPath = options.configPath ?? path43.join(cwd, ".mcp.json");
|
|
7563
7730
|
if (!fs26.existsSync(configPath)) {
|
|
7564
7731
|
return { ok: true, checked: [] };
|
|
7565
7732
|
}
|
|
@@ -7570,7 +7737,7 @@ async function runMcpGuardCheck(options = {}) {
|
|
|
7570
7737
|
return { ok: true, checked: [] };
|
|
7571
7738
|
}
|
|
7572
7739
|
const pkgs = extractNpmPackages(config);
|
|
7573
|
-
const cacheDir =
|
|
7740
|
+
const cacheDir = path43.join(cwd, ".harness", "cache", "osv");
|
|
7574
7741
|
const warnLogger = {
|
|
7575
7742
|
warn: (m, ctx) => logger.warn(ctx ? `${m} ${JSON.stringify(ctx)}` : m)
|
|
7576
7743
|
};
|
|
@@ -7626,12 +7793,12 @@ function formatHuman(result) {
|
|
|
7626
7793
|
}
|
|
7627
7794
|
}
|
|
7628
7795
|
function createMcpGuardCommand() {
|
|
7629
|
-
const command = new
|
|
7796
|
+
const command = new Command62("mcp-guard").description("Pre-launch OSV malware guard for MCP/npx packages").addHelpText(
|
|
7630
7797
|
"after",
|
|
7631
7798
|
'\nUse "harness mcp-guard check" before launching MCP servers to block known malicious packages.'
|
|
7632
7799
|
);
|
|
7633
7800
|
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) => {
|
|
7634
|
-
const cwd =
|
|
7801
|
+
const cwd = path43.resolve(opts.path);
|
|
7635
7802
|
const result = await runMcpGuardCheck({ cwd, strict: opts.strict, json: opts.json });
|
|
7636
7803
|
if (opts.json) {
|
|
7637
7804
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -7642,8 +7809,8 @@ function createMcpGuardCommand() {
|
|
|
7642
7809
|
process.exit(result.ok ? ExitCode.SUCCESS : 2);
|
|
7643
7810
|
});
|
|
7644
7811
|
command.command("cache").description("Manage the on-disk OSV advisory cache").command("clear").option("--path <path>", "Project root path", ".").action(async (opts) => {
|
|
7645
|
-
const cwd =
|
|
7646
|
-
const cacheDir =
|
|
7812
|
+
const cwd = path43.resolve(opts.path);
|
|
7813
|
+
const cacheDir = path43.join(cwd, ".harness", "cache", "osv");
|
|
7647
7814
|
try {
|
|
7648
7815
|
fs26.rmSync(cacheDir, { recursive: true, force: true });
|
|
7649
7816
|
console.log(`Cleared ${cacheDir}`);
|
|
@@ -7657,12 +7824,12 @@ function createMcpGuardCommand() {
|
|
|
7657
7824
|
}
|
|
7658
7825
|
|
|
7659
7826
|
// src/commands/migrate.ts
|
|
7660
|
-
import { Command as
|
|
7827
|
+
import { Command as Command64 } from "commander";
|
|
7661
7828
|
|
|
7662
7829
|
// src/commands/migrate-backends.ts
|
|
7663
|
-
import { Command as
|
|
7830
|
+
import { Command as Command63 } from "commander";
|
|
7664
7831
|
import * as fs27 from "fs";
|
|
7665
|
-
import * as
|
|
7832
|
+
import * as path44 from "path";
|
|
7666
7833
|
import chalk9 from "chalk";
|
|
7667
7834
|
import { parse as parseYaml2 } from "yaml";
|
|
7668
7835
|
async function runMigrateBackends(opts = {}) {
|
|
@@ -7684,7 +7851,7 @@ async function runMigrateBackends(opts = {}) {
|
|
|
7684
7851
|
};
|
|
7685
7852
|
}
|
|
7686
7853
|
const routing = readRoutingFromOrchestratorMd(mdPath);
|
|
7687
|
-
const jsonPath =
|
|
7854
|
+
const jsonPath = path44.join(path44.dirname(mdPath), "harness.config.json");
|
|
7688
7855
|
if (!fs27.existsSync(jsonPath)) {
|
|
7689
7856
|
return {
|
|
7690
7857
|
status: "error",
|
|
@@ -7777,7 +7944,7 @@ function readRoutingFromOrchestratorMd(mdPath) {
|
|
|
7777
7944
|
return routing ?? null;
|
|
7778
7945
|
}
|
|
7779
7946
|
function createBackendsSubcommand() {
|
|
7780
|
-
return new
|
|
7947
|
+
return new Command63("backends").description(
|
|
7781
7948
|
"Copy agent.backends (and routing) from harness.orchestrator.md into harness.config.json."
|
|
7782
7949
|
).option("--dry-run", "Show what would change without writing", false).option("--force", "Overwrite existing agent.backends in harness.config.json", false).action(async (options) => {
|
|
7783
7950
|
const result = await runMigrateBackends({
|
|
@@ -7797,7 +7964,7 @@ function createBackendsSubcommand() {
|
|
|
7797
7964
|
init_dist3();
|
|
7798
7965
|
init_dist2();
|
|
7799
7966
|
import * as fs28 from "fs";
|
|
7800
|
-
import * as
|
|
7967
|
+
import * as path45 from "path";
|
|
7801
7968
|
import * as readline from "readline";
|
|
7802
7969
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
7803
7970
|
import chalk10 from "chalk";
|
|
@@ -7829,7 +7996,7 @@ function walk(dir, predicate) {
|
|
|
7829
7996
|
const out = [];
|
|
7830
7997
|
for (const entry of fs28.readdirSync(dir, { withFileTypes: true })) {
|
|
7831
7998
|
if (DEFAULT_SKIP_DIRS.has(entry.name)) continue;
|
|
7832
|
-
const full =
|
|
7999
|
+
const full = path45.join(dir, entry.name);
|
|
7833
8000
|
if (entry.isDirectory()) out.push(...walk(full, predicate));
|
|
7834
8001
|
else if (predicate(full)) out.push(full);
|
|
7835
8002
|
}
|
|
@@ -7841,11 +8008,11 @@ function extractAutopilotTopic(state) {
|
|
|
7841
8008
|
}
|
|
7842
8009
|
function buildAutopilotMap(cwd) {
|
|
7843
8010
|
const map = /* @__PURE__ */ new Map();
|
|
7844
|
-
const sessionsDir =
|
|
8011
|
+
const sessionsDir = path45.join(cwd, ".harness", "sessions");
|
|
7845
8012
|
if (!fs28.existsSync(sessionsDir)) return map;
|
|
7846
8013
|
for (const session of listDirs(sessionsDir)) {
|
|
7847
8014
|
if (!session.startsWith("changes--")) continue;
|
|
7848
|
-
const state = readJson2(
|
|
8015
|
+
const state = readJson2(path45.join(sessionsDir, session, "autopilot-state.json"));
|
|
7849
8016
|
if (!state) continue;
|
|
7850
8017
|
const topic = extractAutopilotTopic(state);
|
|
7851
8018
|
if (!topic) continue;
|
|
@@ -7883,7 +8050,7 @@ function extractTopicFromFilename(filename, proposalTopics) {
|
|
|
7883
8050
|
return best;
|
|
7884
8051
|
}
|
|
7885
8052
|
function resolveDocsDir(cwd) {
|
|
7886
|
-
const configPath =
|
|
8053
|
+
const configPath = path45.join(cwd, "harness.config.json");
|
|
7887
8054
|
const configResult = fs28.existsSync(configPath) ? resolveConfig(configPath) : null;
|
|
7888
8055
|
let docsDir;
|
|
7889
8056
|
if (configResult?.ok) {
|
|
@@ -7896,16 +8063,16 @@ function resolveDocsDir(cwd) {
|
|
|
7896
8063
|
} else {
|
|
7897
8064
|
docsDir = "./docs";
|
|
7898
8065
|
}
|
|
7899
|
-
const docsAbs =
|
|
7900
|
-
const relRaw =
|
|
8066
|
+
const docsAbs = path45.resolve(cwd, docsDir);
|
|
8067
|
+
const relRaw = path45.relative(cwd, docsAbs).replaceAll("\\", "/");
|
|
7901
8068
|
return relRaw === "" ? "docs" : relRaw;
|
|
7902
8069
|
}
|
|
7903
8070
|
function collectAdrMoves(cwd, docsRel) {
|
|
7904
8071
|
const moves = [];
|
|
7905
|
-
const legacyAdrDir =
|
|
8072
|
+
const legacyAdrDir = path45.join(cwd, ".harness", "architecture");
|
|
7906
8073
|
if (!fs28.existsSync(legacyAdrDir)) return moves;
|
|
7907
8074
|
for (const topic of listDirs(legacyAdrDir)) {
|
|
7908
|
-
const topicSrc =
|
|
8075
|
+
const topicSrc = path45.join(legacyAdrDir, topic);
|
|
7909
8076
|
for (const file of listFiles(topicSrc, (n) => n.endsWith(".md"))) {
|
|
7910
8077
|
moves.push({
|
|
7911
8078
|
src: `.harness/architecture/${topic}/${file}`,
|
|
@@ -7917,9 +8084,9 @@ function collectAdrMoves(cwd, docsRel) {
|
|
|
7917
8084
|
}
|
|
7918
8085
|
function collectProposalTopics(cwd) {
|
|
7919
8086
|
const topics = /* @__PURE__ */ new Set();
|
|
7920
|
-
const changesDir =
|
|
8087
|
+
const changesDir = path45.join(cwd, "docs", "changes");
|
|
7921
8088
|
for (const topic of listDirs(changesDir)) {
|
|
7922
|
-
if (fs28.existsSync(
|
|
8089
|
+
if (fs28.existsSync(path45.join(changesDir, topic, "proposal.md"))) {
|
|
7923
8090
|
topics.add(topic);
|
|
7924
8091
|
}
|
|
7925
8092
|
}
|
|
@@ -7946,12 +8113,12 @@ async function buildPlan(opts) {
|
|
|
7946
8113
|
orphanPlans: [],
|
|
7947
8114
|
proposalTopics: collectProposalTopics(cwd)
|
|
7948
8115
|
};
|
|
7949
|
-
const legacyPlanDir =
|
|
8116
|
+
const legacyPlanDir = path45.join(cwd, "docs", "plans");
|
|
7950
8117
|
if (!fs28.existsSync(legacyPlanDir)) return plan;
|
|
7951
8118
|
const autopilotMap = buildAutopilotMap(cwd);
|
|
7952
8119
|
for (const file of listFiles(legacyPlanDir, (n) => n.endsWith(".md") && n !== "index.md")) {
|
|
7953
8120
|
const planRel = `docs/plans/${file}`;
|
|
7954
|
-
const planAbs =
|
|
8121
|
+
const planAbs = path45.join(legacyPlanDir, file);
|
|
7955
8122
|
const subdir = file.includes("VERIFICATION") ? "verifications" : "plans";
|
|
7956
8123
|
const classified = classifyPlan(planRel, planAbs, file, autopilotMap, plan.proposalTopics);
|
|
7957
8124
|
if (classified) {
|
|
@@ -7969,8 +8136,8 @@ async function buildPlan(opts) {
|
|
|
7969
8136
|
return plan;
|
|
7970
8137
|
}
|
|
7971
8138
|
function gitMv(cwd, src, dest, useGit) {
|
|
7972
|
-
const destAbs =
|
|
7973
|
-
fs28.mkdirSync(
|
|
8139
|
+
const destAbs = path45.join(cwd, dest);
|
|
8140
|
+
fs28.mkdirSync(path45.dirname(destAbs), { recursive: true });
|
|
7974
8141
|
if (useGit) {
|
|
7975
8142
|
try {
|
|
7976
8143
|
execFileSync4("git", ["mv", src, dest], { cwd, stdio: "pipe" });
|
|
@@ -7978,7 +8145,7 @@ function gitMv(cwd, src, dest, useGit) {
|
|
|
7978
8145
|
} catch {
|
|
7979
8146
|
}
|
|
7980
8147
|
}
|
|
7981
|
-
fs28.renameSync(
|
|
8148
|
+
fs28.renameSync(path45.join(cwd, src), destAbs);
|
|
7982
8149
|
}
|
|
7983
8150
|
function executeMigration(plan) {
|
|
7984
8151
|
let moved = 0;
|
|
@@ -8010,8 +8177,8 @@ function updateReferences(plan) {
|
|
|
8010
8177
|
let files = 0;
|
|
8011
8178
|
let replacements = 0;
|
|
8012
8179
|
const targets = [
|
|
8013
|
-
...walk(
|
|
8014
|
-
...walk(
|
|
8180
|
+
...walk(path45.join(plan.cwd, "docs"), (p) => p.endsWith(".md")),
|
|
8181
|
+
...walk(path45.join(plan.cwd, ".harness", "sessions"), (p) => p.endsWith(".json"))
|
|
8015
8182
|
];
|
|
8016
8183
|
for (const target of targets) {
|
|
8017
8184
|
let text;
|
|
@@ -8038,7 +8205,7 @@ function updateReferences(plan) {
|
|
|
8038
8205
|
}
|
|
8039
8206
|
async function prompt(question, def = "n") {
|
|
8040
8207
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
8041
|
-
const answer = await new Promise((
|
|
8208
|
+
const answer = await new Promise((resolve47) => rl.question(question, resolve47));
|
|
8042
8209
|
rl.close();
|
|
8043
8210
|
return (answer.trim() || def).toLowerCase();
|
|
8044
8211
|
}
|
|
@@ -8088,7 +8255,7 @@ async function resolveOrphans(plan, opts) {
|
|
|
8088
8255
|
}
|
|
8089
8256
|
function bucketOrphans(plan, topic) {
|
|
8090
8257
|
for (const src of plan.orphanPlans) {
|
|
8091
|
-
const file =
|
|
8258
|
+
const file = path45.basename(src);
|
|
8092
8259
|
const isVerification = file.includes("VERIFICATION");
|
|
8093
8260
|
const subdir = isVerification ? "verifications" : "plans";
|
|
8094
8261
|
plan.planMoves.push({
|
|
@@ -8130,9 +8297,9 @@ function summarize(plan) {
|
|
|
8130
8297
|
console.log("");
|
|
8131
8298
|
}
|
|
8132
8299
|
async function detectLegacyArtifacts(cwd) {
|
|
8133
|
-
const adrLegacyDir =
|
|
8300
|
+
const adrLegacyDir = path45.join(cwd, ".harness", "architecture");
|
|
8134
8301
|
const adrLegacy = fs28.existsSync(adrLegacyDir) && listDirs(adrLegacyDir).length > 0;
|
|
8135
|
-
const planLegacyDir =
|
|
8302
|
+
const planLegacyDir = path45.join(cwd, "docs", "plans");
|
|
8136
8303
|
const planLegacy = fs28.existsSync(planLegacyDir) && listFiles(planLegacyDir, (n) => n.endsWith(".md") && n !== "index.md").length > 0;
|
|
8137
8304
|
return { adrLegacy, planLegacy };
|
|
8138
8305
|
}
|
|
@@ -8201,7 +8368,7 @@ async function runMigrate(opts) {
|
|
|
8201
8368
|
});
|
|
8202
8369
|
}
|
|
8203
8370
|
function createMigrateCommand() {
|
|
8204
|
-
const cmd = new
|
|
8371
|
+
const cmd = new Command64("migrate").description("Migrate legacy harness artifact locations to current layout").option("--dry-run", "Show the migration plan without moving files", false).option("--yes", "Skip confirmation prompt", false).option(
|
|
8205
8372
|
"--skip-references",
|
|
8206
8373
|
"Do not update path references in docs/sessions after moves",
|
|
8207
8374
|
false
|
|
@@ -8225,7 +8392,7 @@ function createMigrateCommand() {
|
|
|
8225
8392
|
}
|
|
8226
8393
|
|
|
8227
8394
|
// src/commands/models.ts
|
|
8228
|
-
import { Command as
|
|
8395
|
+
import { Command as Command65 } from "commander";
|
|
8229
8396
|
import { defaultFetchModels } from "@harness-engineering/orchestrator";
|
|
8230
8397
|
function errorResult(message, partial) {
|
|
8231
8398
|
return {
|
|
@@ -8343,7 +8510,7 @@ function formatProbe(result) {
|
|
|
8343
8510
|
return lines.join("\n");
|
|
8344
8511
|
}
|
|
8345
8512
|
function createModelsCommand() {
|
|
8346
|
-
const cmd = new
|
|
8513
|
+
const cmd = new Command65("models").description(
|
|
8347
8514
|
"Inspect and manage local LLM backends. Currently ships `probe`; LMLM phases add status/suggest/pool/proposals."
|
|
8348
8515
|
);
|
|
8349
8516
|
cmd.command("probe").description(
|
|
@@ -8369,9 +8536,9 @@ function createModelsCommand() {
|
|
|
8369
8536
|
}
|
|
8370
8537
|
|
|
8371
8538
|
// src/commands/naming-craft.ts
|
|
8372
|
-
import { Command as
|
|
8539
|
+
import { Command as Command66 } from "commander";
|
|
8373
8540
|
function createNamingCraftCommand() {
|
|
8374
|
-
return new
|
|
8541
|
+
return new Command66("naming-craft").description(
|
|
8375
8542
|
"LLM-judgment critique of identifier names (variables, functions, types, files). First craft-pipeline ceiling skill; uses curated rubric catalog from Martin/Beck/Karlton."
|
|
8376
8543
|
).option("-f, --files <files...>", "Optional file/glob scope").option(
|
|
8377
8544
|
"-k, --kinds <kinds...>",
|
|
@@ -8421,10 +8588,10 @@ function printResult4(result, mode, _formatter) {
|
|
|
8421
8588
|
list.push(f);
|
|
8422
8589
|
byFile.set(f.target.file, list);
|
|
8423
8590
|
}
|
|
8424
|
-
for (const [file,
|
|
8591
|
+
for (const [file, fs49] of byFile) {
|
|
8425
8592
|
console.log(`
|
|
8426
8593
|
${file}`);
|
|
8427
|
-
for (const f of
|
|
8594
|
+
for (const f of fs49) {
|
|
8428
8595
|
const line = f.target.line !== void 0 ? `:${f.target.line}` : "";
|
|
8429
8596
|
console.log(
|
|
8430
8597
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.kind} ${f.target.identifier}${line}`
|
|
@@ -8444,13 +8611,13 @@ ${file}`);
|
|
|
8444
8611
|
}
|
|
8445
8612
|
|
|
8446
8613
|
// src/commands/notifications/index.ts
|
|
8447
|
-
import { Command as
|
|
8614
|
+
import { Command as Command68 } from "commander";
|
|
8448
8615
|
|
|
8449
8616
|
// src/commands/notifications/test.ts
|
|
8450
8617
|
init_dist3();
|
|
8451
|
-
import { Command as
|
|
8618
|
+
import { Command as Command67 } from "commander";
|
|
8452
8619
|
import { randomBytes } from "crypto";
|
|
8453
|
-
import { resolve as
|
|
8620
|
+
import { resolve as resolve29 } from "path";
|
|
8454
8621
|
import { SinkRegistry, SinkConfigError, wrapAsEnvelope } from "@harness-engineering/orchestrator";
|
|
8455
8622
|
function makeTestEvent(message) {
|
|
8456
8623
|
return {
|
|
@@ -8500,9 +8667,9 @@ async function runNotificationsTest(sinkId, opts, projectRoot2 = process.cwd())
|
|
|
8500
8667
|
return { ok: false, error: result.error };
|
|
8501
8668
|
}
|
|
8502
8669
|
function createNotificationsTestSubcommand() {
|
|
8503
|
-
return new
|
|
8670
|
+
return new Command67("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) => {
|
|
8504
8671
|
const globalOpts = cmd.optsWithGlobals();
|
|
8505
|
-
const projectRoot2 = typeof globalOpts["cwd"] === "string" ?
|
|
8672
|
+
const projectRoot2 = typeof globalOpts["cwd"] === "string" ? resolve29(globalOpts["cwd"]) : process.cwd();
|
|
8506
8673
|
const result = await runNotificationsTest(sinkId, opts, projectRoot2);
|
|
8507
8674
|
if (globalOpts["json"]) {
|
|
8508
8675
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -8517,7 +8684,7 @@ function createNotificationsTestSubcommand() {
|
|
|
8517
8684
|
|
|
8518
8685
|
// src/commands/notifications/index.ts
|
|
8519
8686
|
function createNotificationsCommand() {
|
|
8520
|
-
const cmd = new
|
|
8687
|
+
const cmd = new Command68("notifications").description(
|
|
8521
8688
|
"Manage notification sinks (Slack and others)"
|
|
8522
8689
|
);
|
|
8523
8690
|
cmd.addCommand(createNotificationsTestSubcommand());
|
|
@@ -8525,13 +8692,13 @@ function createNotificationsCommand() {
|
|
|
8525
8692
|
}
|
|
8526
8693
|
|
|
8527
8694
|
// src/commands/orchestrator.ts
|
|
8528
|
-
import { Command as
|
|
8529
|
-
import * as
|
|
8695
|
+
import { Command as Command69 } from "commander";
|
|
8696
|
+
import * as path46 from "path";
|
|
8530
8697
|
import { Orchestrator, WorkflowLoader as WorkflowLoader2, launchTUI } from "@harness-engineering/orchestrator";
|
|
8531
8698
|
function createOrchestratorCommand() {
|
|
8532
|
-
const orchestrator = new
|
|
8699
|
+
const orchestrator = new Command69("orchestrator");
|
|
8533
8700
|
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) => {
|
|
8534
|
-
const workflowPath =
|
|
8701
|
+
const workflowPath = path46.resolve(process.cwd(), opts.workflow);
|
|
8535
8702
|
const loader = new WorkflowLoader2();
|
|
8536
8703
|
const result = await loader.loadWorkflow(workflowPath);
|
|
8537
8704
|
if (!result.ok) {
|
|
@@ -8565,13 +8732,13 @@ function createOrchestratorCommand() {
|
|
|
8565
8732
|
|
|
8566
8733
|
// src/commands/perf.ts
|
|
8567
8734
|
init_dist3();
|
|
8568
|
-
import { Command as
|
|
8569
|
-
import * as
|
|
8735
|
+
import { Command as Command70 } from "commander";
|
|
8736
|
+
import * as path47 from "path";
|
|
8570
8737
|
function registerBenchCommand(perf) {
|
|
8571
8738
|
perf.command("bench [glob]").description("Run benchmarks via vitest bench").action(async (glob2, _opts, cmd) => {
|
|
8572
8739
|
const globalOpts = cmd.optsWithGlobals();
|
|
8573
8740
|
const cwd = process.cwd();
|
|
8574
|
-
const { BenchmarkRunner } = await import("./dist-
|
|
8741
|
+
const { BenchmarkRunner } = await import("./dist-LHINSVK4.js");
|
|
8575
8742
|
const runner = new BenchmarkRunner();
|
|
8576
8743
|
const benchFiles = runner.discover(cwd, glob2);
|
|
8577
8744
|
if (benchFiles.length === 0) {
|
|
@@ -8635,7 +8802,7 @@ async function runBaselinesShow(globalOpts) {
|
|
|
8635
8802
|
}
|
|
8636
8803
|
async function runBaselinesUpdate(globalOpts) {
|
|
8637
8804
|
const cwd = process.cwd();
|
|
8638
|
-
const { BenchmarkRunner } = await import("./dist-
|
|
8805
|
+
const { BenchmarkRunner } = await import("./dist-LHINSVK4.js");
|
|
8639
8806
|
const runner = new BenchmarkRunner();
|
|
8640
8807
|
const manager = new BaselineManager(cwd);
|
|
8641
8808
|
logger.info("Running benchmarks to update baselines...");
|
|
@@ -8676,9 +8843,9 @@ function registerReportCommand(perf) {
|
|
|
8676
8843
|
perf.command("report").description("Full performance report with metrics, trends, and hotspots").action(async (_opts, cmd) => {
|
|
8677
8844
|
const globalOpts = cmd.optsWithGlobals();
|
|
8678
8845
|
const cwd = process.cwd();
|
|
8679
|
-
const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-
|
|
8846
|
+
const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-LHINSVK4.js");
|
|
8680
8847
|
const analyzer = new EntropyAnalyzer2({
|
|
8681
|
-
rootDir:
|
|
8848
|
+
rootDir: path47.resolve(cwd),
|
|
8682
8849
|
analyze: { complexity: true, coupling: true }
|
|
8683
8850
|
});
|
|
8684
8851
|
const result = await analyzer.analyze();
|
|
@@ -8737,7 +8904,7 @@ function registerCriticalPathsCommand(perf) {
|
|
|
8737
8904
|
});
|
|
8738
8905
|
}
|
|
8739
8906
|
function createPerfCommand() {
|
|
8740
|
-
const perf = new
|
|
8907
|
+
const perf = new Command70("perf").description("Performance benchmark and baseline management");
|
|
8741
8908
|
registerBenchCommand(perf);
|
|
8742
8909
|
registerBaselinesCommands(perf);
|
|
8743
8910
|
registerReportCommand(perf);
|
|
@@ -8746,10 +8913,10 @@ function createPerfCommand() {
|
|
|
8746
8913
|
}
|
|
8747
8914
|
|
|
8748
8915
|
// src/commands/persona/index.ts
|
|
8749
|
-
import { Command as
|
|
8916
|
+
import { Command as Command73 } from "commander";
|
|
8750
8917
|
|
|
8751
8918
|
// src/commands/persona/list.ts
|
|
8752
|
-
import { Command as
|
|
8919
|
+
import { Command as Command71 } from "commander";
|
|
8753
8920
|
function printPersonaList(personas) {
|
|
8754
8921
|
if (personas.length === 0) {
|
|
8755
8922
|
logger.info("No personas found.");
|
|
@@ -8763,7 +8930,7 @@ function printPersonaList(personas) {
|
|
|
8763
8930
|
}
|
|
8764
8931
|
}
|
|
8765
8932
|
function createListCommand2() {
|
|
8766
|
-
return new
|
|
8933
|
+
return new Command71("list").description("List available agent personas").action(async (_opts, cmd) => {
|
|
8767
8934
|
const globalOpts = cmd.optsWithGlobals();
|
|
8768
8935
|
const personasDir = resolvePersonasDir();
|
|
8769
8936
|
const result = listPersonas(personasDir);
|
|
@@ -8783,17 +8950,17 @@ function createListCommand2() {
|
|
|
8783
8950
|
}
|
|
8784
8951
|
|
|
8785
8952
|
// src/commands/persona/generate.ts
|
|
8786
|
-
import { Command as
|
|
8953
|
+
import { Command as Command72 } from "commander";
|
|
8787
8954
|
import * as fs29 from "fs";
|
|
8788
|
-
import * as
|
|
8955
|
+
import * as path48 from "path";
|
|
8789
8956
|
function generatePersonaArtifacts(persona, outputDir, only) {
|
|
8790
8957
|
const slug = toKebabCase(persona.name);
|
|
8791
8958
|
const generated = [];
|
|
8792
8959
|
if (!only || only === "runtime") {
|
|
8793
8960
|
const result = generateRuntime(persona);
|
|
8794
8961
|
if (result.ok) {
|
|
8795
|
-
const outPath =
|
|
8796
|
-
fs29.mkdirSync(
|
|
8962
|
+
const outPath = path48.join(outputDir, `${slug}.runtime.json`);
|
|
8963
|
+
fs29.mkdirSync(path48.dirname(outPath), { recursive: true });
|
|
8797
8964
|
fs29.writeFileSync(outPath, result.value);
|
|
8798
8965
|
generated.push(outPath);
|
|
8799
8966
|
}
|
|
@@ -8801,7 +8968,7 @@ function generatePersonaArtifacts(persona, outputDir, only) {
|
|
|
8801
8968
|
if (!only || only === "agents-md") {
|
|
8802
8969
|
const result = generateAgentsMd(persona);
|
|
8803
8970
|
if (result.ok) {
|
|
8804
|
-
const outPath =
|
|
8971
|
+
const outPath = path48.join(outputDir, `${slug}.agents.md`);
|
|
8805
8972
|
fs29.writeFileSync(outPath, result.value);
|
|
8806
8973
|
generated.push(outPath);
|
|
8807
8974
|
}
|
|
@@ -8809,8 +8976,8 @@ function generatePersonaArtifacts(persona, outputDir, only) {
|
|
|
8809
8976
|
if (!only || only === "ci") {
|
|
8810
8977
|
const result = generateCIWorkflow(persona, "github");
|
|
8811
8978
|
if (result.ok) {
|
|
8812
|
-
const outPath =
|
|
8813
|
-
fs29.mkdirSync(
|
|
8979
|
+
const outPath = path48.join(outputDir, ".github", "workflows", `${slug}.yml`);
|
|
8980
|
+
fs29.mkdirSync(path48.dirname(outPath), { recursive: true });
|
|
8814
8981
|
fs29.writeFileSync(outPath, result.value);
|
|
8815
8982
|
generated.push(outPath);
|
|
8816
8983
|
}
|
|
@@ -8818,16 +8985,16 @@ function generatePersonaArtifacts(persona, outputDir, only) {
|
|
|
8818
8985
|
return generated;
|
|
8819
8986
|
}
|
|
8820
8987
|
function createGenerateCommand3() {
|
|
8821
|
-
return new
|
|
8988
|
+
return new Command72("generate").description("Generate artifacts from a persona config").argument("<name>", "Persona name (e.g., architecture-enforcer)").option("--output-dir <dir>", "Output directory", ".").option("--only <type>", "Generate only: ci, agents-md, runtime").action(async (name, opts, cmd) => {
|
|
8822
8989
|
const globalOpts = cmd.optsWithGlobals();
|
|
8823
|
-
const personaResult = loadPersona(
|
|
8990
|
+
const personaResult = loadPersona(path48.join(resolvePersonasDir(), `${name}.yaml`));
|
|
8824
8991
|
if (!personaResult.ok) {
|
|
8825
8992
|
logger.error(personaResult.error.message);
|
|
8826
8993
|
process.exit(ExitCode.ERROR);
|
|
8827
8994
|
}
|
|
8828
8995
|
const generated = generatePersonaArtifacts(
|
|
8829
8996
|
personaResult.value,
|
|
8830
|
-
|
|
8997
|
+
path48.resolve(opts.outputDir),
|
|
8831
8998
|
opts.only
|
|
8832
8999
|
);
|
|
8833
9000
|
if (!globalOpts.quiet) {
|
|
@@ -8840,7 +9007,7 @@ function createGenerateCommand3() {
|
|
|
8840
9007
|
|
|
8841
9008
|
// src/commands/persona/index.ts
|
|
8842
9009
|
function createPersonaCommand() {
|
|
8843
|
-
const command = new
|
|
9010
|
+
const command = new Command73("persona").description("Agent persona management commands");
|
|
8844
9011
|
command.addCommand(createListCommand2());
|
|
8845
9012
|
command.addCommand(createGenerateCommand3());
|
|
8846
9013
|
return command;
|
|
@@ -8848,7 +9015,7 @@ function createPersonaCommand() {
|
|
|
8848
9015
|
|
|
8849
9016
|
// src/commands/predict.ts
|
|
8850
9017
|
init_dist3();
|
|
8851
|
-
import { Command as
|
|
9018
|
+
import { Command as Command74 } from "commander";
|
|
8852
9019
|
import chalk11 from "chalk";
|
|
8853
9020
|
var CATEGORY_ORDER = [
|
|
8854
9021
|
"circular-deps",
|
|
@@ -8954,7 +9121,7 @@ function handlePredictError(err, mode) {
|
|
|
8954
9121
|
process.exit(ExitCode.ERROR);
|
|
8955
9122
|
}
|
|
8956
9123
|
function createPredictCommand() {
|
|
8957
|
-
const command = new
|
|
9124
|
+
const command = new Command74("predict").description("Predict which architectural constraints will break and when").option("--category <name>", "Filter to a single metric category").option("--no-roadmap", "Baseline only \u2014 skip roadmap spec impact").option("--horizon <weeks>", "Forecast horizon in weeks (default: 12)", "12").action(async (opts, cmd) => {
|
|
8958
9125
|
const globalOpts = cmd.optsWithGlobals();
|
|
8959
9126
|
const mode = globalOpts.json ? OutputMode.JSON : OutputMode.TEXT;
|
|
8960
9127
|
try {
|
|
@@ -8978,10 +9145,10 @@ function createPredictCommand() {
|
|
|
8978
9145
|
|
|
8979
9146
|
// src/commands/proposals.ts
|
|
8980
9147
|
init_dist3();
|
|
8981
|
-
import { Command as
|
|
8982
|
-
import { resolve as
|
|
9148
|
+
import { Command as Command75 } from "commander";
|
|
9149
|
+
import { resolve as resolve33 } from "path";
|
|
8983
9150
|
function projectRoot() {
|
|
8984
|
-
return
|
|
9151
|
+
return resolve33(process.env["HARNESS_PROJECT_ROOT"] ?? process.cwd());
|
|
8985
9152
|
}
|
|
8986
9153
|
function summarizeProposal(p) {
|
|
8987
9154
|
return {
|
|
@@ -9073,7 +9240,7 @@ async function actApproveCommand(id) {
|
|
|
9073
9240
|
}
|
|
9074
9241
|
}
|
|
9075
9242
|
function createProposalsCommand() {
|
|
9076
|
-
const cmd = new
|
|
9243
|
+
const cmd = new Command75("proposals").description("Skill-proposal review queue");
|
|
9077
9244
|
cmd.command("list").description("List skill proposals in the local queue").option(
|
|
9078
9245
|
"--status <status>",
|
|
9079
9246
|
`Filter by status \u2014 one of ${ALLOWED_STATUSES.join(" | ")}`,
|
|
@@ -9089,8 +9256,8 @@ function createProposalsCommand() {
|
|
|
9089
9256
|
|
|
9090
9257
|
// src/commands/publish-analyses.ts
|
|
9091
9258
|
import * as fs30 from "fs";
|
|
9092
|
-
import * as
|
|
9093
|
-
import { Command as
|
|
9259
|
+
import * as path49 from "path";
|
|
9260
|
+
import { Command as Command76 } from "commander";
|
|
9094
9261
|
init_dist3();
|
|
9095
9262
|
import {
|
|
9096
9263
|
renderAnalysisComment,
|
|
@@ -9098,7 +9265,7 @@ import {
|
|
|
9098
9265
|
savePublishedIndex
|
|
9099
9266
|
} from "@harness-engineering/orchestrator";
|
|
9100
9267
|
function bootstrapTrackerCommand(opts, verb) {
|
|
9101
|
-
const projectPath =
|
|
9268
|
+
const projectPath = path49.resolve(opts.dir);
|
|
9102
9269
|
const trackerConfig = loadTrackerSyncConfig(projectPath);
|
|
9103
9270
|
if (!trackerConfig) {
|
|
9104
9271
|
logger.error(`No tracker config found in harness.config.json. Cannot ${verb}.`);
|
|
@@ -9110,7 +9277,7 @@ function bootstrapTrackerCommand(opts, verb) {
|
|
|
9110
9277
|
);
|
|
9111
9278
|
return null;
|
|
9112
9279
|
}
|
|
9113
|
-
const projectEnvPath =
|
|
9280
|
+
const projectEnvPath = path49.join(projectPath, ".env");
|
|
9114
9281
|
if (fs30.existsSync(projectEnvPath) && !process.env.GITHUB_TOKEN) {
|
|
9115
9282
|
__require("dotenv").config({ path: projectEnvPath });
|
|
9116
9283
|
}
|
|
@@ -9147,7 +9314,7 @@ async function buildNameToExternalIdMap(projectPath) {
|
|
|
9147
9314
|
}
|
|
9148
9315
|
return fileLessMap;
|
|
9149
9316
|
}
|
|
9150
|
-
const roadmapFile =
|
|
9317
|
+
const roadmapFile = path49.join(projectPath, "docs", "roadmap.md");
|
|
9151
9318
|
if (!fs30.existsSync(roadmapFile)) {
|
|
9152
9319
|
logger.error("No docs/roadmap.md found. Cannot map issue hashes to external IDs.");
|
|
9153
9320
|
return null;
|
|
@@ -9203,8 +9370,8 @@ async function runPublishAnalyses(opts) {
|
|
|
9203
9370
|
const nameToExternalId = await buildNameToExternalIdMap(projectPath);
|
|
9204
9371
|
if (!nameToExternalId) return process.exit(1);
|
|
9205
9372
|
const { AnalysisArchive } = await import("@harness-engineering/orchestrator");
|
|
9206
|
-
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-
|
|
9207
|
-
const archive = new AnalysisArchive(
|
|
9373
|
+
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-LHINSVK4.js");
|
|
9374
|
+
const archive = new AnalysisArchive(path49.join(projectPath, ".harness", "analyses"));
|
|
9208
9375
|
const analyses = await archive.list();
|
|
9209
9376
|
if (analyses.length === 0) {
|
|
9210
9377
|
logger.info("No analyses found to publish.");
|
|
@@ -9226,7 +9393,7 @@ async function runPublishAnalyses(opts) {
|
|
|
9226
9393
|
}
|
|
9227
9394
|
}
|
|
9228
9395
|
function createPublishAnalysesCommand() {
|
|
9229
|
-
const command = new
|
|
9396
|
+
const command = new Command76("publish-analyses").description(
|
|
9230
9397
|
"Publishes locally generated intelligence analyses to the external issue tracker (e.g., GitHub)"
|
|
9231
9398
|
).option("-d, --dir <path>", "Workspace directory", process.cwd()).action(async (opts) => {
|
|
9232
9399
|
try {
|
|
@@ -9242,13 +9409,13 @@ function createPublishAnalysesCommand() {
|
|
|
9242
9409
|
}
|
|
9243
9410
|
|
|
9244
9411
|
// src/commands/pulse/index.ts
|
|
9245
|
-
import { Command as
|
|
9412
|
+
import { Command as Command78 } from "commander";
|
|
9246
9413
|
|
|
9247
9414
|
// src/commands/pulse/run.ts
|
|
9248
9415
|
init_dist3();
|
|
9249
9416
|
import { readFileSync as readFileSync19, mkdirSync as mkdirSync13, writeFileSync as writeFileSync16 } from "fs";
|
|
9250
|
-
import { dirname as dirname18, join as join39, resolve as
|
|
9251
|
-
import { Command as
|
|
9417
|
+
import { dirname as dirname18, join as join39, resolve as resolve35 } from "path";
|
|
9418
|
+
import { Command as Command77 } from "commander";
|
|
9252
9419
|
async function runPulseRunCommand(opts) {
|
|
9253
9420
|
const startedAt = Date.now();
|
|
9254
9421
|
let raw;
|
|
@@ -9313,7 +9480,7 @@ function emit2(status, nonInteractive) {
|
|
|
9313
9480
|
return status;
|
|
9314
9481
|
}
|
|
9315
9482
|
function createRunCommand2() {
|
|
9316
|
-
return new
|
|
9483
|
+
return new Command77("run").description("Run a pulse: query configured adapters, sanitize, assemble single-page report").option(
|
|
9317
9484
|
"--lookback <window>",
|
|
9318
9485
|
"Lookback window (e.g. 24h, 7d). Defaults to pulse.lookbackDefault."
|
|
9319
9486
|
).option(
|
|
@@ -9323,8 +9490,8 @@ function createRunCommand2() {
|
|
|
9323
9490
|
async (options) => {
|
|
9324
9491
|
const nonInteractive = options.nonInteractive === true || !process.stdout.isTTY;
|
|
9325
9492
|
const status = await runPulseRunCommand({
|
|
9326
|
-
configPath:
|
|
9327
|
-
outputDir:
|
|
9493
|
+
configPath: resolve35(process.cwd(), options.config),
|
|
9494
|
+
outputDir: resolve35(process.cwd(), options.outputDir),
|
|
9328
9495
|
nonInteractive,
|
|
9329
9496
|
lookback: options.lookback
|
|
9330
9497
|
});
|
|
@@ -9337,13 +9504,13 @@ function createRunCommand2() {
|
|
|
9337
9504
|
|
|
9338
9505
|
// src/commands/pulse/index.ts
|
|
9339
9506
|
function createPulseCommand() {
|
|
9340
|
-
const command = new
|
|
9507
|
+
const command = new Command78("pulse").description("Pulse (read-side observability) commands");
|
|
9341
9508
|
command.addCommand(createRunCommand2());
|
|
9342
9509
|
return command;
|
|
9343
9510
|
}
|
|
9344
9511
|
|
|
9345
9512
|
// src/commands/recommend.ts
|
|
9346
|
-
import { Command as
|
|
9513
|
+
import { Command as Command79 } from "commander";
|
|
9347
9514
|
import chalk12 from "chalk";
|
|
9348
9515
|
async function resolveSnapshot(cwd, noCache) {
|
|
9349
9516
|
if (!noCache) {
|
|
@@ -9407,7 +9574,7 @@ function printRecommendations(result) {
|
|
|
9407
9574
|
console.log("");
|
|
9408
9575
|
}
|
|
9409
9576
|
function createRecommendCommand() {
|
|
9410
|
-
const command = new
|
|
9577
|
+
const command = new Command79("recommend").description("Recommend skills based on codebase health analysis").option("--no-cache", "Force fresh health snapshot").option("--top <n>", "Max recommendations (default 5)", "5").action(async (opts, cmd) => {
|
|
9411
9578
|
const globalOpts = cmd.optsWithGlobals();
|
|
9412
9579
|
const mode = globalOpts.json ? OutputMode.JSON : OutputMode.TEXT;
|
|
9413
9580
|
try {
|
|
@@ -9442,19 +9609,197 @@ function createRecommendCommand() {
|
|
|
9442
9609
|
return command;
|
|
9443
9610
|
}
|
|
9444
9611
|
|
|
9612
|
+
// src/commands/review-ci.ts
|
|
9613
|
+
init_dist3();
|
|
9614
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
9615
|
+
import { writeFileSync as writeFileSync17 } from "fs";
|
|
9616
|
+
import { Command as Command80, Option } from "commander";
|
|
9617
|
+
|
|
9618
|
+
// src/commands/review-ci-local-adapter.ts
|
|
9619
|
+
import { z } from "zod";
|
|
9620
|
+
import { OpenAICompatibleAnalysisProvider } from "@harness-engineering/intelligence";
|
|
9621
|
+
var FindingSchema = z.object({
|
|
9622
|
+
id: z.string(),
|
|
9623
|
+
file: z.string(),
|
|
9624
|
+
lineRange: z.tuple([z.number(), z.number()]),
|
|
9625
|
+
domain: z.enum(["compliance", "bug", "security", "architecture", "learnings"]),
|
|
9626
|
+
severity: z.enum(["critical", "important", "suggestion"]),
|
|
9627
|
+
title: z.string(),
|
|
9628
|
+
rationale: z.string(),
|
|
9629
|
+
suggestion: z.string().optional(),
|
|
9630
|
+
evidence: z.array(z.string())
|
|
9631
|
+
});
|
|
9632
|
+
var LocalVerdictSchema = z.object({
|
|
9633
|
+
assessment: z.enum(["approve", "comment", "request-changes"]),
|
|
9634
|
+
findings: z.array(FindingSchema)
|
|
9635
|
+
});
|
|
9636
|
+
var MAX_SURFACED_ERROR_LEN = 200;
|
|
9637
|
+
function sanitizeProviderError(err) {
|
|
9638
|
+
const raw = err instanceof Error ? err.message : String(err);
|
|
9639
|
+
const redacted = raw.replace(/https?:\/\/\S+/gi, "[endpoint]").replace(/\b(bearer|authorization|api[-_]?key|token)\b\s*[:=]?\s*\S+/gi, "[redacted]").replace(/\s+/g, " ").trim();
|
|
9640
|
+
const capped = redacted.length > MAX_SURFACED_ERROR_LEN ? redacted.slice(0, MAX_SURFACED_ERROR_LEN - 1) + "\u2026" : redacted;
|
|
9641
|
+
return `local runner provider error: ${capped}`;
|
|
9642
|
+
}
|
|
9643
|
+
function createLocalInvoke() {
|
|
9644
|
+
return async ({ endpoint, model, instruction, diff: diff2 }) => {
|
|
9645
|
+
if (!/^https?:\/\//i.test(endpoint)) {
|
|
9646
|
+
throw new Error(
|
|
9647
|
+
`HARNESS_LOCAL_ENDPOINT must be an http(s) URL (got '${endpoint}'); e.g. http://localhost:1234/v1`
|
|
9648
|
+
);
|
|
9649
|
+
}
|
|
9650
|
+
const provider = new OpenAICompatibleAnalysisProvider({
|
|
9651
|
+
apiKey: process.env.HARNESS_LOCAL_API_KEY ?? "local",
|
|
9652
|
+
baseUrl: endpoint,
|
|
9653
|
+
defaultModel: model
|
|
9654
|
+
});
|
|
9655
|
+
try {
|
|
9656
|
+
const { result } = await provider.analyze({
|
|
9657
|
+
prompt: `${instruction}
|
|
9658
|
+
|
|
9659
|
+
---
|
|
9660
|
+
DIFF UNDER REVIEW:
|
|
9661
|
+
${diff2}`,
|
|
9662
|
+
responseSchema: LocalVerdictSchema,
|
|
9663
|
+
model
|
|
9664
|
+
});
|
|
9665
|
+
return JSON.stringify(result);
|
|
9666
|
+
} catch (err) {
|
|
9667
|
+
throw new Error(sanitizeProviderError(err), { cause: err });
|
|
9668
|
+
}
|
|
9669
|
+
};
|
|
9670
|
+
}
|
|
9671
|
+
|
|
9672
|
+
// src/commands/review-ci.ts
|
|
9673
|
+
var KNOWN_RUNNERS = Object.keys(RUNNER_PRESETS);
|
|
9674
|
+
var BLOCK_ON_LEVELS = [...CI_ASSESSMENTS, "none"];
|
|
9675
|
+
function assertKnownRunner(runner) {
|
|
9676
|
+
if (runner === void 0) return;
|
|
9677
|
+
if (!KNOWN_RUNNERS.includes(runner)) {
|
|
9678
|
+
throw new Error(`unknown runner '${runner}' (expected: ${KNOWN_RUNNERS.join("|")})`);
|
|
9679
|
+
}
|
|
9680
|
+
}
|
|
9681
|
+
var defaultRunGit = (args) => execFileSync5("git", args, { encoding: "utf-8" }).toString().trim();
|
|
9682
|
+
function resolveDiffRange(opts) {
|
|
9683
|
+
if (opts.range) return opts.range;
|
|
9684
|
+
const runGit = opts.runGit ?? defaultRunGit;
|
|
9685
|
+
let base = "main";
|
|
9686
|
+
try {
|
|
9687
|
+
const ref = runGit(["symbolic-ref", "refs/remotes/origin/HEAD"]);
|
|
9688
|
+
const m = ref.match(/origin\/(.+)$/);
|
|
9689
|
+
if (m?.[1]) base = m[1];
|
|
9690
|
+
} catch {
|
|
9691
|
+
}
|
|
9692
|
+
return `origin/${base}...HEAD`;
|
|
9693
|
+
}
|
|
9694
|
+
function splitDiffByFile(rawDiff) {
|
|
9695
|
+
const sections = /* @__PURE__ */ new Map();
|
|
9696
|
+
const headerRe = /^diff --git a\/(.+?) b\/(.+?)$/;
|
|
9697
|
+
let currentPath = null;
|
|
9698
|
+
let buffer = [];
|
|
9699
|
+
const flush = () => {
|
|
9700
|
+
if (currentPath !== null) sections.set(currentPath, buffer.join("\n"));
|
|
9701
|
+
};
|
|
9702
|
+
for (const line of rawDiff.split("\n")) {
|
|
9703
|
+
const m = line.match(headerRe);
|
|
9704
|
+
if (m?.[2]) {
|
|
9705
|
+
flush();
|
|
9706
|
+
currentPath = m[2];
|
|
9707
|
+
buffer = [line];
|
|
9708
|
+
} else if (currentPath !== null) {
|
|
9709
|
+
buffer.push(line);
|
|
9710
|
+
}
|
|
9711
|
+
}
|
|
9712
|
+
flush();
|
|
9713
|
+
return sections;
|
|
9714
|
+
}
|
|
9715
|
+
function buildDiffInfo(rawDiff) {
|
|
9716
|
+
const parsed = parseDiff(rawDiff);
|
|
9717
|
+
if (!parsed.ok) throw new Error(`Failed to parse diff: ${parsed.error.message}`);
|
|
9718
|
+
const files = parsed.value.files;
|
|
9719
|
+
const perFile = splitDiffByFile(rawDiff);
|
|
9720
|
+
return {
|
|
9721
|
+
changedFiles: files.map((f) => f.path),
|
|
9722
|
+
newFiles: files.filter((f) => f.status === "added").map((f) => f.path),
|
|
9723
|
+
deletedFiles: files.filter((f) => f.status === "deleted").map((f) => f.path),
|
|
9724
|
+
totalDiffLines: rawDiff ? rawDiff.split("\n").length : 0,
|
|
9725
|
+
// On a path-key miss, fall back to '' (the file contributes no per-file diff)
|
|
9726
|
+
// rather than the ENTIRE raw diff — substituting the whole diff would duplicate
|
|
9727
|
+
// every file's content into one section, inflating the STDIN payload to the LLM
|
|
9728
|
+
// (and risking core's execMaxStdoutBytes). With splitDiffByFile aligned to core's
|
|
9729
|
+
// path extraction, ordinary paths never miss; this is the safe degenerate case.
|
|
9730
|
+
fileDiffs: new Map(files.map((f) => [f.path, perFile.get(f.path) ?? ""]))
|
|
9731
|
+
};
|
|
9732
|
+
}
|
|
9733
|
+
function defaultResolveRaw(range, _cwd, runGit) {
|
|
9734
|
+
return runGit(["diff", range]);
|
|
9735
|
+
}
|
|
9736
|
+
function buildCallOpts(opts, cwd, diff2) {
|
|
9737
|
+
const runner = opts.runner;
|
|
9738
|
+
return {
|
|
9739
|
+
projectRoot: cwd,
|
|
9740
|
+
diff: diff2,
|
|
9741
|
+
...runner ? { runner } : {},
|
|
9742
|
+
...opts.blockOn ? { blockOn: opts.blockOn } : {},
|
|
9743
|
+
...runner === "local" ? { localInvoke: opts.localInvoke ?? createLocalInvoke() } : {}
|
|
9744
|
+
};
|
|
9745
|
+
}
|
|
9746
|
+
async function runReviewCi(opts) {
|
|
9747
|
+
assertKnownRunner(opts.runner);
|
|
9748
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
9749
|
+
const runGit = opts.runGit ?? defaultRunGit;
|
|
9750
|
+
const range = resolveDiffRange({
|
|
9751
|
+
...opts.diffRange ? { range: opts.diffRange } : {},
|
|
9752
|
+
cwd,
|
|
9753
|
+
runGit
|
|
9754
|
+
});
|
|
9755
|
+
const rawDiff = (opts.resolveRaw ?? defaultResolveRaw)(range, cwd, runGit);
|
|
9756
|
+
const diff2 = buildDiffInfo(rawDiff);
|
|
9757
|
+
const callOpts = buildCallOpts(opts, cwd, diff2);
|
|
9758
|
+
return (opts.runCiReviewImpl ?? runCiReview)(callOpts);
|
|
9759
|
+
}
|
|
9760
|
+
function emitReviewCi(result, opts, writeFile = (p, d) => writeFileSync17(p, d), log = (m) => process.stdout.write(m + "\n")) {
|
|
9761
|
+
log(result.terminalOutput);
|
|
9762
|
+
if (opts.jsonPath) writeFile(opts.jsonPath, JSON.stringify(result.verdict, null, 2));
|
|
9763
|
+
if (opts.comment) {
|
|
9764
|
+
logger.warn(
|
|
9765
|
+
"review-ci: --comment posting is not yet wired (no gh PR poster; Phase 3 stub). The verdict above is authoritative; use --json to capture the artifact. PR-review posting lands in a later phase."
|
|
9766
|
+
);
|
|
9767
|
+
}
|
|
9768
|
+
}
|
|
9769
|
+
function createReviewCiCommand() {
|
|
9770
|
+
return new Command80("review-ci").description("Run the tiered code-review gate (floor + optional LLM runner) for CI").addOption(
|
|
9771
|
+
new Option("--runner <runner>", `${KNOWN_RUNNERS.join(" | ")} (omit = floor-only)`).choices(
|
|
9772
|
+
KNOWN_RUNNERS
|
|
9773
|
+
)
|
|
9774
|
+
).addOption(
|
|
9775
|
+
new Option("--block-on <level>", BLOCK_ON_LEVELS.join(" | ")).choices(BLOCK_ON_LEVELS).default("request-changes")
|
|
9776
|
+
).option("--diff <range>", "git range (default: origin/<base>...HEAD)").option("--comment", "post verdict as a PR review (stubbed in this phase)").option("--json <path>", "write the verdict artifact to this path").action(async (opts) => {
|
|
9777
|
+
const result = await runReviewCi({
|
|
9778
|
+
runner: opts.runner,
|
|
9779
|
+
blockOn: opts.blockOn,
|
|
9780
|
+
diffRange: opts.diff
|
|
9781
|
+
});
|
|
9782
|
+
emitReviewCi(result, {
|
|
9783
|
+
jsonPath: opts.json,
|
|
9784
|
+
comment: opts.comment
|
|
9785
|
+
});
|
|
9786
|
+
process.exit(result.exitCode);
|
|
9787
|
+
});
|
|
9788
|
+
}
|
|
9789
|
+
|
|
9445
9790
|
// src/commands/roadmap/index.ts
|
|
9446
|
-
import { Command as
|
|
9791
|
+
import { Command as Command82 } from "commander";
|
|
9447
9792
|
|
|
9448
9793
|
// src/commands/roadmap/migrate.ts
|
|
9449
9794
|
init_dist3();
|
|
9450
|
-
import { Command as
|
|
9795
|
+
import { Command as Command81 } from "commander";
|
|
9451
9796
|
import * as fs32 from "fs";
|
|
9452
|
-
import * as
|
|
9797
|
+
import * as path51 from "path";
|
|
9453
9798
|
import chalk13 from "chalk";
|
|
9454
9799
|
|
|
9455
9800
|
// src/commands/roadmap/migrate-lock.ts
|
|
9456
9801
|
import * as fs31 from "fs";
|
|
9457
|
-
import * as
|
|
9802
|
+
import * as path50 from "path";
|
|
9458
9803
|
import * as os6 from "os";
|
|
9459
9804
|
var LOCK_FILE_NAME = "migrate.lock";
|
|
9460
9805
|
var STALE_LOCK_MS = 30 * 60 * 1e3;
|
|
@@ -9468,8 +9813,8 @@ function isPidAlive(pid) {
|
|
|
9468
9813
|
}
|
|
9469
9814
|
}
|
|
9470
9815
|
function acquireMigrateLock(projectRoot2) {
|
|
9471
|
-
const harnessDir =
|
|
9472
|
-
const lockPath =
|
|
9816
|
+
const harnessDir = path50.join(projectRoot2, ".harness");
|
|
9817
|
+
const lockPath = path50.join(harnessDir, LOCK_FILE_NAME);
|
|
9473
9818
|
if (!fs31.existsSync(harnessDir)) fs31.mkdirSync(harnessDir, { recursive: true });
|
|
9474
9819
|
if (fs31.existsSync(lockPath)) {
|
|
9475
9820
|
const existingRaw = (() => {
|
|
@@ -9668,7 +10013,7 @@ async function runRoadmapMigrate(opts) {
|
|
|
9668
10013
|
if (!clientR.ok) return Err(new CLIError(clientR.error.message));
|
|
9669
10014
|
client = clientR.value;
|
|
9670
10015
|
}
|
|
9671
|
-
const roadmapPath =
|
|
10016
|
+
const roadmapPath = path51.join(cwd, "docs", "roadmap.md");
|
|
9672
10017
|
if (!fs32.existsSync(roadmapPath)) {
|
|
9673
10018
|
return Err(new CLIError(`docs/roadmap.md not found in ${cwd}`));
|
|
9674
10019
|
}
|
|
@@ -9717,7 +10062,7 @@ async function runRoadmapMigrate(opts) {
|
|
|
9717
10062
|
}
|
|
9718
10063
|
}
|
|
9719
10064
|
function createRoadmapMigrateCommand() {
|
|
9720
|
-
return new
|
|
10065
|
+
return new Command81("migrate").description("Migrate the project roadmap to a different storage mode").requiredOption("--to <target>", 'Migration target (only "file-less" supported today)').option("--dry-run", "Print the migration plan without making any changes", false).option(
|
|
9721
10066
|
"--format <fmt>",
|
|
9722
10067
|
'Output format: "human" (default) or "json" (single JSON object for CI consumers)',
|
|
9723
10068
|
"human"
|
|
@@ -9752,16 +10097,16 @@ function createRoadmapMigrateCommand() {
|
|
|
9752
10097
|
|
|
9753
10098
|
// src/commands/roadmap/index.ts
|
|
9754
10099
|
function createRoadmapCommand() {
|
|
9755
|
-
const roadmap = new
|
|
10100
|
+
const roadmap = new Command82("roadmap").description("Roadmap management");
|
|
9756
10101
|
roadmap.addCommand(createRoadmapMigrateCommand());
|
|
9757
10102
|
return roadmap;
|
|
9758
10103
|
}
|
|
9759
10104
|
|
|
9760
10105
|
// src/commands/routing/index.ts
|
|
9761
|
-
import { Command as
|
|
10106
|
+
import { Command as Command86 } from "commander";
|
|
9762
10107
|
|
|
9763
10108
|
// src/commands/routing/config.ts
|
|
9764
|
-
import { Command as
|
|
10109
|
+
import { Command as Command83 } from "commander";
|
|
9765
10110
|
|
|
9766
10111
|
// src/commands/routing/http-client.ts
|
|
9767
10112
|
function orchestratorBase() {
|
|
@@ -9771,9 +10116,9 @@ function authHeader() {
|
|
|
9771
10116
|
const tok = process.env["HARNESS_API_TOKEN"];
|
|
9772
10117
|
return tok ? { Authorization: `Bearer ${tok}` } : {};
|
|
9773
10118
|
}
|
|
9774
|
-
async function getJson(
|
|
10119
|
+
async function getJson(path73) {
|
|
9775
10120
|
try {
|
|
9776
|
-
const res = await fetch(`${orchestratorBase()}${
|
|
10121
|
+
const res = await fetch(`${orchestratorBase()}${path73}`, {
|
|
9777
10122
|
headers: { ...authHeader() }
|
|
9778
10123
|
});
|
|
9779
10124
|
const text = await res.text();
|
|
@@ -9788,9 +10133,9 @@ async function getJson(path72) {
|
|
|
9788
10133
|
};
|
|
9789
10134
|
}
|
|
9790
10135
|
}
|
|
9791
|
-
async function postJson(
|
|
10136
|
+
async function postJson(path73, body) {
|
|
9792
10137
|
try {
|
|
9793
|
-
const res = await fetch(`${orchestratorBase()}${
|
|
10138
|
+
const res = await fetch(`${orchestratorBase()}${path73}`, {
|
|
9794
10139
|
method: "POST",
|
|
9795
10140
|
headers: { "Content-Type": "application/json", ...authHeader() },
|
|
9796
10141
|
body: JSON.stringify(body)
|
|
@@ -9826,7 +10171,7 @@ function renderHuman(data) {
|
|
|
9826
10171
|
}
|
|
9827
10172
|
}
|
|
9828
10173
|
function createConfigCommand() {
|
|
9829
|
-
return new
|
|
10174
|
+
return new Command83("config").description("Print active routing config and resolved fallback chains").option("--json", "Emit JSON to stdout instead of human-readable text").action(async (opts) => {
|
|
9830
10175
|
const r = await getJson("/api/v1/routing/config");
|
|
9831
10176
|
if (!r.ok) {
|
|
9832
10177
|
if (r.status === 0) {
|
|
@@ -9852,7 +10197,7 @@ function createConfigCommand() {
|
|
|
9852
10197
|
}
|
|
9853
10198
|
|
|
9854
10199
|
// src/commands/routing/trace.ts
|
|
9855
|
-
import { Command as
|
|
10200
|
+
import { Command as Command84 } from "commander";
|
|
9856
10201
|
function buildUseCase(opts) {
|
|
9857
10202
|
if (opts.skill) {
|
|
9858
10203
|
return opts.mode ? { kind: "skill", skillName: opts.skill, cognitiveMode: opts.mode } : { kind: "skill", skillName: opts.skill };
|
|
@@ -9873,7 +10218,7 @@ function renderHuman2(r) {
|
|
|
9873
10218
|
}
|
|
9874
10219
|
}
|
|
9875
10220
|
function createTraceCommand() {
|
|
9876
|
-
return new
|
|
10221
|
+
return new Command84("trace").description("Dry-run a routing decision without dispatching (Spec B F7)").option("--skill <name>", "Skill name to trace").option("--mode <m>", "Cognitive mode to trace (or attach to --skill per spec D12)").option("--json", "Emit JSON to stdout instead of human-readable text").action(async (opts) => {
|
|
9877
10222
|
const useCase = buildUseCase(opts);
|
|
9878
10223
|
if (!useCase) {
|
|
9879
10224
|
logger.error("Either --skill <name> or --mode <m> is required");
|
|
@@ -9905,7 +10250,7 @@ function createTraceCommand() {
|
|
|
9905
10250
|
}
|
|
9906
10251
|
|
|
9907
10252
|
// src/commands/routing/decisions.ts
|
|
9908
|
-
import { Command as
|
|
10253
|
+
import { Command as Command85 } from "commander";
|
|
9909
10254
|
function summarizeUseCase(uc) {
|
|
9910
10255
|
switch (uc.kind) {
|
|
9911
10256
|
case "skill":
|
|
@@ -9953,7 +10298,7 @@ function buildQuery(opts) {
|
|
|
9953
10298
|
return q ? `?${q}` : "";
|
|
9954
10299
|
}
|
|
9955
10300
|
function createDecisionsCommand() {
|
|
9956
|
-
return new
|
|
10301
|
+
return new Command85("decisions").description("List recent routing decisions from the orchestrator ring buffer (Spec B F8)").option("--skill <name>", "Filter by useCase.skillName").option("--mode <m>", "Filter by useCase.cognitiveMode").option("--backend <name>", "Filter by chosen backendName").option("--last <N>", "Limit to the N most recent decisions").option("--json", "Emit JSON to stdout instead of human-readable text").action(
|
|
9957
10302
|
async (opts) => {
|
|
9958
10303
|
const query = buildQuery(opts);
|
|
9959
10304
|
const r = await getJson(`/api/v1/routing/decisions${query}`);
|
|
@@ -9983,7 +10328,7 @@ function createDecisionsCommand() {
|
|
|
9983
10328
|
|
|
9984
10329
|
// src/commands/routing/index.ts
|
|
9985
10330
|
function createRoutingCommand() {
|
|
9986
|
-
const cmd = new
|
|
10331
|
+
const cmd = new Command86("routing").description(
|
|
9987
10332
|
"Inspect routing config, trace decisions, and read recent dispatches"
|
|
9988
10333
|
);
|
|
9989
10334
|
cmd.addCommand(createConfigCommand());
|
|
@@ -9994,8 +10339,8 @@ function createRoutingCommand() {
|
|
|
9994
10339
|
|
|
9995
10340
|
// src/commands/scan-config.ts
|
|
9996
10341
|
init_dist3();
|
|
9997
|
-
import { Command as
|
|
9998
|
-
import { existsSync as existsSync29, readFileSync as readFileSync22, writeFileSync as
|
|
10342
|
+
import { Command as Command87 } from "commander";
|
|
10343
|
+
import { existsSync as existsSync29, readFileSync as readFileSync22, writeFileSync as writeFileSync20 } from "fs";
|
|
9999
10344
|
import { join as join42, relative as relative3 } from "path";
|
|
10000
10345
|
var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".gemini/settings.json", "skill.yaml"];
|
|
10001
10346
|
function stripHighSeverityPatterns(content, injectionFindings) {
|
|
@@ -10022,7 +10367,7 @@ function applyFix(filePath, targetDir, content, injectionFindings) {
|
|
|
10022
10367
|
if (!hasHighSeverity) return;
|
|
10023
10368
|
const { cleaned, linesStripped } = stripHighSeverityPatterns(content, injectionFindings);
|
|
10024
10369
|
if (linesStripped > 0) {
|
|
10025
|
-
|
|
10370
|
+
writeFileSync20(filePath, cleaned);
|
|
10026
10371
|
logger.info(
|
|
10027
10372
|
`scan-config --fix: stripped ${linesStripped} high-severity line(s) from ${relative3(targetDir, filePath).replaceAll("\\", "/")}`
|
|
10028
10373
|
);
|
|
@@ -10083,7 +10428,7 @@ function formatTextOutput(result) {
|
|
|
10083
10428
|
}
|
|
10084
10429
|
}
|
|
10085
10430
|
function createScanConfigCommand() {
|
|
10086
|
-
const command = new
|
|
10431
|
+
const command = new Command87("scan-config").description(
|
|
10087
10432
|
"Scan CLAUDE.md, AGENTS.md, .gemini/settings.json, and skill.yaml for prompt injection patterns"
|
|
10088
10433
|
).option("--path <dir>", "Target directory to scan (default: cwd)").option("--fix", "Strip high-severity patterns from files in-place").action(async (opts, cmd) => {
|
|
10089
10434
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -10101,7 +10446,7 @@ function createScanConfigCommand() {
|
|
|
10101
10446
|
|
|
10102
10447
|
// src/commands/search.ts
|
|
10103
10448
|
init_dist();
|
|
10104
|
-
import { Command as
|
|
10449
|
+
import { Command as Command88 } from "commander";
|
|
10105
10450
|
function parseFileKinds(raw) {
|
|
10106
10451
|
if (!raw) return void 0;
|
|
10107
10452
|
const set = /* @__PURE__ */ new Set();
|
|
@@ -10122,7 +10467,7 @@ function fmtBytes(n) {
|
|
|
10122
10467
|
return n.toFixed(0);
|
|
10123
10468
|
}
|
|
10124
10469
|
function createSearchCommand() {
|
|
10125
|
-
return new
|
|
10470
|
+
return new Command88("search").description("Full-text search over archived + live session content.").argument(
|
|
10126
10471
|
"<query>",
|
|
10127
10472
|
"FTS5 query (bare words AND-joined; quotes/AND/OR/NOT/column: for advanced syntax)"
|
|
10128
10473
|
).option("-n, --limit <n>", "Max results", "20").option("--archived-only", "Skip live sessions, only search archived ones").option("--json", "Emit JSON to stdout instead of pretty text").option(
|
|
@@ -10182,9 +10527,9 @@ function createSearchCommand() {
|
|
|
10182
10527
|
}
|
|
10183
10528
|
|
|
10184
10529
|
// src/commands/security-craft.ts
|
|
10185
|
-
import { Command as
|
|
10530
|
+
import { Command as Command89 } from "commander";
|
|
10186
10531
|
function createSecurityCraftCommand() {
|
|
10187
|
-
return new
|
|
10532
|
+
return new Command89("security-craft").description(
|
|
10188
10533
|
"LLM-judgment critique of security posture (TS/JS source). Sixth non-design craft-pipeline ceiling skill (the final sub-project). AST-driven signal detection fires only on files with security-relevant constructs; conservative confidence defaults manage the FP risk inherent in judgment-based security."
|
|
10189
10534
|
).option("-f, --files <files...>", "Optional file scope (overrides discovery)").option("-p, --packages <names...>", "Restrict to specific packages under packages/").option("--max-files <n>", "Cap source-file count (default: 100)").option("--max-signals-per-file <n>", "Cap per-file signal critique (default: 10)").action(async (opts, cmd) => {
|
|
10190
10535
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -10231,10 +10576,10 @@ function printResult5(result, mode, _formatter) {
|
|
|
10231
10576
|
list.push(f);
|
|
10232
10577
|
byFile.set(f.target.file, list);
|
|
10233
10578
|
}
|
|
10234
|
-
for (const [file,
|
|
10579
|
+
for (const [file, fs49] of byFile) {
|
|
10235
10580
|
console.log(`
|
|
10236
10581
|
${file}`);
|
|
10237
|
-
for (const f of
|
|
10582
|
+
for (const f of fs49) {
|
|
10238
10583
|
console.log(
|
|
10239
10584
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.signal}:${f.target.line}`
|
|
10240
10585
|
);
|
|
@@ -10250,18 +10595,18 @@ ${file}`);
|
|
|
10250
10595
|
}
|
|
10251
10596
|
|
|
10252
10597
|
// src/commands/setup.ts
|
|
10253
|
-
import { Command as
|
|
10598
|
+
import { Command as Command90 } from "commander";
|
|
10254
10599
|
import * as fs35 from "fs";
|
|
10255
10600
|
import * as os8 from "os";
|
|
10256
|
-
import * as
|
|
10601
|
+
import * as path54 from "path";
|
|
10257
10602
|
import chalk15 from "chalk";
|
|
10258
10603
|
|
|
10259
10604
|
// src/utils/first-run.ts
|
|
10260
10605
|
import * as fs33 from "fs";
|
|
10261
10606
|
import * as os7 from "os";
|
|
10262
|
-
import * as
|
|
10263
|
-
var HARNESS_DIR =
|
|
10264
|
-
var MARKER_FILE =
|
|
10607
|
+
import * as path52 from "path";
|
|
10608
|
+
var HARNESS_DIR = path52.join(os7.homedir(), ".harness");
|
|
10609
|
+
var MARKER_FILE = path52.join(HARNESS_DIR, ".setup-complete");
|
|
10265
10610
|
function isFirstRun() {
|
|
10266
10611
|
return !fs33.existsSync(MARKER_FILE);
|
|
10267
10612
|
}
|
|
@@ -10281,7 +10626,7 @@ function printFirstRunWelcome() {
|
|
|
10281
10626
|
|
|
10282
10627
|
// src/commands/telemetry-wizard.ts
|
|
10283
10628
|
import * as fs34 from "fs";
|
|
10284
|
-
import * as
|
|
10629
|
+
import * as path53 from "path";
|
|
10285
10630
|
import readline2 from "readline";
|
|
10286
10631
|
import chalk14 from "chalk";
|
|
10287
10632
|
function prompt2(question) {
|
|
@@ -10289,10 +10634,10 @@ function prompt2(question) {
|
|
|
10289
10634
|
input: process.stdin,
|
|
10290
10635
|
output: process.stdout
|
|
10291
10636
|
});
|
|
10292
|
-
return new Promise((
|
|
10637
|
+
return new Promise((resolve47) => {
|
|
10293
10638
|
rl.question(question, (answer) => {
|
|
10294
10639
|
rl.close();
|
|
10295
|
-
|
|
10640
|
+
resolve47(answer.trim().toLowerCase());
|
|
10296
10641
|
});
|
|
10297
10642
|
});
|
|
10298
10643
|
}
|
|
@@ -10301,10 +10646,10 @@ function promptRaw(question) {
|
|
|
10301
10646
|
input: process.stdin,
|
|
10302
10647
|
output: process.stdout
|
|
10303
10648
|
});
|
|
10304
|
-
return new Promise((
|
|
10649
|
+
return new Promise((resolve47) => {
|
|
10305
10650
|
rl.question(question, (answer) => {
|
|
10306
10651
|
rl.close();
|
|
10307
|
-
|
|
10652
|
+
resolve47(answer.trim());
|
|
10308
10653
|
});
|
|
10309
10654
|
});
|
|
10310
10655
|
}
|
|
@@ -10312,7 +10657,7 @@ function isNo(answer) {
|
|
|
10312
10657
|
return answer === "n" || answer === "no";
|
|
10313
10658
|
}
|
|
10314
10659
|
function isTelemetryConfigured(cwd) {
|
|
10315
|
-
const configPath =
|
|
10660
|
+
const configPath = path53.join(cwd, "harness.config.json");
|
|
10316
10661
|
try {
|
|
10317
10662
|
const raw = JSON.parse(fs34.readFileSync(configPath, "utf-8"));
|
|
10318
10663
|
return raw.telemetry !== void 0 && raw.adoption !== void 0;
|
|
@@ -10321,7 +10666,7 @@ function isTelemetryConfigured(cwd) {
|
|
|
10321
10666
|
}
|
|
10322
10667
|
}
|
|
10323
10668
|
function writeTelemetryConfig(cwd, result) {
|
|
10324
|
-
const configPath =
|
|
10669
|
+
const configPath = path53.join(cwd, "harness.config.json");
|
|
10325
10670
|
try {
|
|
10326
10671
|
const raw = JSON.parse(fs34.readFileSync(configPath, "utf-8"));
|
|
10327
10672
|
raw.telemetry = { enabled: result.telemetryEnabled };
|
|
@@ -10334,9 +10679,9 @@ function writeTelemetryConfig(cwd, result) {
|
|
|
10334
10679
|
}
|
|
10335
10680
|
const hasIdentity = result.identity.project || result.identity.team || result.identity.alias;
|
|
10336
10681
|
if (hasIdentity) {
|
|
10337
|
-
const harnessDir =
|
|
10682
|
+
const harnessDir = path53.join(cwd, ".harness");
|
|
10338
10683
|
fs34.mkdirSync(harnessDir, { recursive: true });
|
|
10339
|
-
const telemetryFile =
|
|
10684
|
+
const telemetryFile = path53.join(harnessDir, "telemetry.json");
|
|
10340
10685
|
fs34.writeFileSync(telemetryFile, JSON.stringify({ identity: result.identity }, null, 2) + "\n");
|
|
10341
10686
|
}
|
|
10342
10687
|
}
|
|
@@ -10383,7 +10728,7 @@ async function runTelemetryWizard() {
|
|
|
10383
10728
|
return { telemetryEnabled, adoptionEnabled, identity };
|
|
10384
10729
|
}
|
|
10385
10730
|
async function ensureTelemetryConfigured(cwd) {
|
|
10386
|
-
const configPath =
|
|
10731
|
+
const configPath = path53.join(cwd, "harness.config.json");
|
|
10387
10732
|
if (!fs34.existsSync(configPath)) {
|
|
10388
10733
|
return { status: "warn", message: "Not a harness project \u2014 skipped telemetry configuration" };
|
|
10389
10734
|
}
|
|
@@ -10430,7 +10775,7 @@ function runSlashCommandGeneration() {
|
|
|
10430
10775
|
}
|
|
10431
10776
|
}
|
|
10432
10777
|
function detectClient(dirName) {
|
|
10433
|
-
return fs35.existsSync(
|
|
10778
|
+
return fs35.existsSync(path54.join(os8.homedir(), dirName));
|
|
10434
10779
|
}
|
|
10435
10780
|
async function runMcpSetup(cwd) {
|
|
10436
10781
|
const results = [];
|
|
@@ -10446,7 +10791,7 @@ async function runMcpSetup(cwd) {
|
|
|
10446
10791
|
{ name: "Cursor", dir: ".cursor", client: "cursor", configTarget: ".cursor/mcp.json" },
|
|
10447
10792
|
{
|
|
10448
10793
|
name: "OpenCode",
|
|
10449
|
-
dir:
|
|
10794
|
+
dir: path54.join(".config", "opencode"),
|
|
10450
10795
|
client: "opencode",
|
|
10451
10796
|
configTarget: "opencode.json"
|
|
10452
10797
|
}
|
|
@@ -10478,7 +10823,7 @@ function formatStep(result) {
|
|
|
10478
10823
|
}
|
|
10479
10824
|
function configureTier0Integrations(cwd) {
|
|
10480
10825
|
try {
|
|
10481
|
-
const mcpPath =
|
|
10826
|
+
const mcpPath = path54.join(cwd, ".mcp.json");
|
|
10482
10827
|
const config = readMcpConfig(mcpPath);
|
|
10483
10828
|
const tier0 = INTEGRATION_REGISTRY.filter((i) => i.tier === 0);
|
|
10484
10829
|
const added = [];
|
|
@@ -10487,9 +10832,9 @@ function configureTier0Integrations(cwd) {
|
|
|
10487
10832
|
writeMcpEntry(mcpPath, integration.name, integration.mcpConfig);
|
|
10488
10833
|
added.push(integration.displayName);
|
|
10489
10834
|
}
|
|
10490
|
-
const geminiDir =
|
|
10835
|
+
const geminiDir = path54.join(cwd, ".gemini");
|
|
10491
10836
|
if (fs35.existsSync(geminiDir)) {
|
|
10492
|
-
const geminiPath =
|
|
10837
|
+
const geminiPath = path54.join(geminiDir, "settings.json");
|
|
10493
10838
|
const geminiConfig = readMcpConfig(geminiPath);
|
|
10494
10839
|
for (const integration of tier0) {
|
|
10495
10840
|
if (!geminiConfig.mcpServers[integration.name]) {
|
|
@@ -10497,8 +10842,8 @@ function configureTier0Integrations(cwd) {
|
|
|
10497
10842
|
}
|
|
10498
10843
|
}
|
|
10499
10844
|
}
|
|
10500
|
-
const opencodePath =
|
|
10501
|
-
const opencodeGlobalDir =
|
|
10845
|
+
const opencodePath = path54.join(cwd, "opencode.json");
|
|
10846
|
+
const opencodeGlobalDir = path54.join(os8.homedir(), ".config", "opencode");
|
|
10502
10847
|
if (fs35.existsSync(opencodePath) || fs35.existsSync(opencodeGlobalDir)) {
|
|
10503
10848
|
for (const integration of tier0) {
|
|
10504
10849
|
writeOpencodeMcpEntry(opencodePath, integration.name, integration.mcpConfig);
|
|
@@ -10517,12 +10862,12 @@ function configureTier0Integrations(cwd) {
|
|
|
10517
10862
|
}
|
|
10518
10863
|
}
|
|
10519
10864
|
function ensureHooks(cwd) {
|
|
10520
|
-
const configPath =
|
|
10865
|
+
const configPath = path54.join(cwd, "harness.config.json");
|
|
10521
10866
|
if (!fs35.existsSync(configPath)) {
|
|
10522
10867
|
return { status: "warn", message: "Not a harness project \u2014 skipped hook installation" };
|
|
10523
10868
|
}
|
|
10524
10869
|
let profile = "standard";
|
|
10525
|
-
const profilePath =
|
|
10870
|
+
const profilePath = path54.join(cwd, ".harness", "hooks", "profile.json");
|
|
10526
10871
|
try {
|
|
10527
10872
|
const data = JSON.parse(fs35.readFileSync(profilePath, "utf-8"));
|
|
10528
10873
|
if (data.profile && ["minimal", "standard", "strict"].includes(data.profile)) {
|
|
@@ -10595,7 +10940,7 @@ async function runSetup(cwd) {
|
|
|
10595
10940
|
return { steps, success };
|
|
10596
10941
|
}
|
|
10597
10942
|
function createSetupCommand() {
|
|
10598
|
-
return new
|
|
10943
|
+
return new Command90("setup").description("Configure harness environment: slash commands, MCP, and more").action(async () => {
|
|
10599
10944
|
const cwd = process.cwd();
|
|
10600
10945
|
console.log("");
|
|
10601
10946
|
console.log(` ${chalk15.bold("harness setup")}`);
|
|
@@ -10618,14 +10963,14 @@ function createSetupCommand() {
|
|
|
10618
10963
|
|
|
10619
10964
|
// src/commands/share.ts
|
|
10620
10965
|
init_dist3();
|
|
10621
|
-
import { Command as
|
|
10966
|
+
import { Command as Command91 } from "commander";
|
|
10622
10967
|
import * as fs36 from "fs";
|
|
10623
|
-
import * as
|
|
10968
|
+
import * as path55 from "path";
|
|
10624
10969
|
import { parse as parseYaml3 } from "yaml";
|
|
10625
10970
|
var MANIFEST_FILENAME = "constraints.yaml";
|
|
10626
10971
|
async function runShareAction(projectPath, options) {
|
|
10627
|
-
const rootDir =
|
|
10628
|
-
const manifestPath =
|
|
10972
|
+
const rootDir = path55.resolve(projectPath);
|
|
10973
|
+
const manifestPath = path55.join(rootDir, MANIFEST_FILENAME);
|
|
10629
10974
|
if (!fs36.existsSync(manifestPath)) {
|
|
10630
10975
|
logger.error(
|
|
10631
10976
|
`No ${MANIFEST_FILENAME} found at ${manifestPath}.
|
|
@@ -10649,7 +10994,7 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
10649
10994
|
process.exit(1);
|
|
10650
10995
|
}
|
|
10651
10996
|
const manifest = manifestResult.value;
|
|
10652
|
-
const configResult = resolveConfig(
|
|
10997
|
+
const configResult = resolveConfig(path55.join(rootDir, "harness.config.json"));
|
|
10653
10998
|
if (!configResult.ok) {
|
|
10654
10999
|
logger.error(configResult.error.message);
|
|
10655
11000
|
process.exit(1);
|
|
@@ -10667,8 +11012,8 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
10667
11012
|
);
|
|
10668
11013
|
process.exit(1);
|
|
10669
11014
|
}
|
|
10670
|
-
const outputDir =
|
|
10671
|
-
const outputPath =
|
|
11015
|
+
const outputDir = path55.resolve(options.output);
|
|
11016
|
+
const outputPath = path55.join(outputDir, `${manifest.name}.harness-constraints.json`);
|
|
10672
11017
|
const writeResult = await writeConfig(outputPath, bundle);
|
|
10673
11018
|
if (!writeResult.ok) {
|
|
10674
11019
|
logger.error(`Failed to write bundle: ${writeResult.error.message}`);
|
|
@@ -10677,25 +11022,25 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
10677
11022
|
logger.success(`Bundle written to ${outputPath}`);
|
|
10678
11023
|
}
|
|
10679
11024
|
function createShareCommand() {
|
|
10680
|
-
return new
|
|
11025
|
+
return new Command91("share").description("Extract and publish a constraints bundle from constraints.yaml").argument("[path]", "Path to the project root", ".").option("-o, --output <dir>", "Output directory for the bundle", ".").action(async (projectPath, options) => {
|
|
10681
11026
|
await runShareAction(projectPath, options);
|
|
10682
11027
|
});
|
|
10683
11028
|
}
|
|
10684
11029
|
|
|
10685
11030
|
// src/commands/skill/index.ts
|
|
10686
|
-
import { Command as
|
|
11031
|
+
import { Command as Command99 } from "commander";
|
|
10687
11032
|
|
|
10688
11033
|
// src/commands/skill/list.ts
|
|
10689
|
-
import { Command as
|
|
11034
|
+
import { Command as Command92 } from "commander";
|
|
10690
11035
|
import * as fs37 from "fs";
|
|
10691
|
-
import * as
|
|
11036
|
+
import * as path56 from "path";
|
|
10692
11037
|
import { parse } from "yaml";
|
|
10693
11038
|
function scanDirectory(dirPath, source) {
|
|
10694
11039
|
if (!fs37.existsSync(dirPath)) return [];
|
|
10695
11040
|
const entries = fs37.readdirSync(dirPath, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
10696
11041
|
const skills = [];
|
|
10697
11042
|
for (const name of entries) {
|
|
10698
|
-
const yamlPath =
|
|
11043
|
+
const yamlPath = path56.join(dirPath, name, "skill.yaml");
|
|
10699
11044
|
if (!fs37.existsSync(yamlPath)) continue;
|
|
10700
11045
|
try {
|
|
10701
11046
|
const raw = fs37.readFileSync(yamlPath, "utf-8");
|
|
@@ -10716,10 +11061,10 @@ function scanDirectory(dirPath, source) {
|
|
|
10716
11061
|
}
|
|
10717
11062
|
function collectCommunitySkills(seen, allSkills) {
|
|
10718
11063
|
const globalDir = resolveGlobalSkillsDir();
|
|
10719
|
-
const skillsDir =
|
|
10720
|
-
const communityBase =
|
|
10721
|
-
const communityPlatformDir =
|
|
10722
|
-
const lockfilePath =
|
|
11064
|
+
const skillsDir = path56.dirname(globalDir);
|
|
11065
|
+
const communityBase = path56.join(skillsDir, "community");
|
|
11066
|
+
const communityPlatformDir = path56.join(communityBase, "claude-code");
|
|
11067
|
+
const lockfilePath = path56.join(communityBase, "skills-lock.json");
|
|
10723
11068
|
const lockfile = readLockfile2(lockfilePath);
|
|
10724
11069
|
const communitySkills = scanDirectory(communityPlatformDir, "community");
|
|
10725
11070
|
for (const skill of communitySkills) {
|
|
@@ -10789,7 +11134,7 @@ function printSkillsVerbose(skills) {
|
|
|
10789
11134
|
for (const s of skills) printSkillEntry(s);
|
|
10790
11135
|
}
|
|
10791
11136
|
function createListCommand3() {
|
|
10792
|
-
return new
|
|
11137
|
+
return new Command92("list").description("List available skills").option("--installed", "Show only community-installed skills").option("--local", "Show only project-local skills").option("--all", "Show all skills (default)").action(async (opts, cmd) => {
|
|
10793
11138
|
const globalOpts = cmd.optsWithGlobals();
|
|
10794
11139
|
const skills = collectSkills({ filter: resolveFilter(opts) });
|
|
10795
11140
|
if (globalOpts.json) {
|
|
@@ -10804,13 +11149,13 @@ function createListCommand3() {
|
|
|
10804
11149
|
}
|
|
10805
11150
|
|
|
10806
11151
|
// src/commands/skill/run.ts
|
|
10807
|
-
import { Command as
|
|
11152
|
+
import { Command as Command93 } from "commander";
|
|
10808
11153
|
import * as fs38 from "fs";
|
|
10809
|
-
import * as
|
|
11154
|
+
import * as path57 from "path";
|
|
10810
11155
|
import { parse as parse2 } from "yaml";
|
|
10811
11156
|
|
|
10812
11157
|
// src/skill/complexity.ts
|
|
10813
|
-
import { execFileSync as
|
|
11158
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
10814
11159
|
function evaluateSignals(signals) {
|
|
10815
11160
|
if (signals.fileCount >= 3) return "thorough";
|
|
10816
11161
|
if (signals.newDir) return "thorough";
|
|
@@ -10822,17 +11167,17 @@ function evaluateSignals(signals) {
|
|
|
10822
11167
|
}
|
|
10823
11168
|
function detectComplexity(projectPath) {
|
|
10824
11169
|
try {
|
|
10825
|
-
const base =
|
|
11170
|
+
const base = execFileSync6("git", ["merge-base", "HEAD", "main"], {
|
|
10826
11171
|
cwd: projectPath,
|
|
10827
11172
|
encoding: "utf-8",
|
|
10828
11173
|
stdio: ["pipe", "pipe", "pipe"]
|
|
10829
11174
|
}).trim();
|
|
10830
|
-
const diffFiles =
|
|
11175
|
+
const diffFiles = execFileSync6("git", ["diff", "--name-only", base], {
|
|
10831
11176
|
cwd: projectPath,
|
|
10832
11177
|
encoding: "utf-8",
|
|
10833
11178
|
stdio: ["pipe", "pipe", "pipe"]
|
|
10834
11179
|
}).trim().split("\n").filter(Boolean);
|
|
10835
|
-
const diffStat =
|
|
11180
|
+
const diffStat = execFileSync6("git", ["diff", "--stat", base], {
|
|
10836
11181
|
cwd: projectPath,
|
|
10837
11182
|
encoding: "utf-8",
|
|
10838
11183
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -10902,7 +11247,7 @@ ${options.principles}`);
|
|
|
10902
11247
|
|
|
10903
11248
|
// src/commands/skill/run.ts
|
|
10904
11249
|
function loadSkillMetadata(skillDir) {
|
|
10905
|
-
const yamlPath =
|
|
11250
|
+
const yamlPath = path57.join(skillDir, "skill.yaml");
|
|
10906
11251
|
if (!fs38.existsSync(yamlPath)) return null;
|
|
10907
11252
|
try {
|
|
10908
11253
|
const result = SkillMetadataSchema.safeParse(parse2(fs38.readFileSync(yamlPath, "utf-8")));
|
|
@@ -10917,18 +11262,18 @@ function resolveComplexity(metadata, requested, projectPath) {
|
|
|
10917
11262
|
return requested;
|
|
10918
11263
|
}
|
|
10919
11264
|
function loadPrinciples(projectPath) {
|
|
10920
|
-
const principlesPath =
|
|
11265
|
+
const principlesPath = path57.join(projectPath, "docs", "principles.md");
|
|
10921
11266
|
return fs38.existsSync(principlesPath) ? fs38.readFileSync(principlesPath, "utf-8") : void 0;
|
|
10922
11267
|
}
|
|
10923
11268
|
function readMostRecentFileInDir(dirPath) {
|
|
10924
|
-
const files = fs38.readdirSync(dirPath).map((f) => ({ name: f, mtime: fs38.statSync(
|
|
10925
|
-
if (files.length > 0) return fs38.readFileSync(
|
|
11269
|
+
const files = fs38.readdirSync(dirPath).map((f) => ({ name: f, mtime: fs38.statSync(path57.join(dirPath, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
11270
|
+
if (files.length > 0) return fs38.readFileSync(path57.join(dirPath, files[0].name), "utf-8");
|
|
10926
11271
|
return void 0;
|
|
10927
11272
|
}
|
|
10928
11273
|
function loadPriorState(metadata, projectPath) {
|
|
10929
11274
|
if (!metadata?.state.persistent || metadata.state.files.length === 0) return void 0;
|
|
10930
11275
|
for (const stateFilePath of metadata.state.files) {
|
|
10931
|
-
const fullPath =
|
|
11276
|
+
const fullPath = path57.join(projectPath, stateFilePath);
|
|
10932
11277
|
if (!fs38.existsSync(fullPath)) continue;
|
|
10933
11278
|
const stat = fs38.statSync(fullPath);
|
|
10934
11279
|
if (stat.isDirectory()) return readMostRecentFileInDir(fullPath);
|
|
@@ -10952,7 +11297,7 @@ function resolvePhaseState(metadata, projectPath, phase) {
|
|
|
10952
11297
|
}
|
|
10953
11298
|
function appendProjectState(content, metadata, projectPath, hasPathOpt) {
|
|
10954
11299
|
if (!metadata?.state.persistent || !hasPathOpt) return content;
|
|
10955
|
-
const stateFile =
|
|
11300
|
+
const stateFile = path57.join(projectPath, ".harness", "state.json");
|
|
10956
11301
|
if (!fs38.existsSync(stateFile)) return content;
|
|
10957
11302
|
const stateContent = fs38.readFileSync(stateFile, "utf-8");
|
|
10958
11303
|
return content + `
|
|
@@ -10966,14 +11311,14 @@ ${stateContent}
|
|
|
10966
11311
|
}
|
|
10967
11312
|
async function runSkill(name, opts) {
|
|
10968
11313
|
const skillsDir = resolveSkillsDir();
|
|
10969
|
-
const skillDir =
|
|
11314
|
+
const skillDir = path57.join(skillsDir, name);
|
|
10970
11315
|
if (!fs38.existsSync(skillDir)) {
|
|
10971
11316
|
logger.error(`Skill not found: ${name}`);
|
|
10972
11317
|
process.exit(ExitCode.ERROR);
|
|
10973
11318
|
return;
|
|
10974
11319
|
}
|
|
10975
11320
|
const metadata = loadSkillMetadata(skillDir);
|
|
10976
|
-
const projectPath = opts.path ?
|
|
11321
|
+
const projectPath = opts.path ? path57.resolve(opts.path) : process.cwd();
|
|
10977
11322
|
const complexity = resolveComplexity(
|
|
10978
11323
|
metadata,
|
|
10979
11324
|
opts.complexity ?? "standard",
|
|
@@ -11000,7 +11345,7 @@ async function runSkill(name, opts) {
|
|
|
11000
11345
|
...stateWarning !== void 0 && { stateWarning },
|
|
11001
11346
|
...opts.party !== void 0 && { party: opts.party }
|
|
11002
11347
|
});
|
|
11003
|
-
const skillMdPath =
|
|
11348
|
+
const skillMdPath = path57.join(skillDir, "SKILL.md");
|
|
11004
11349
|
if (!fs38.existsSync(skillMdPath)) {
|
|
11005
11350
|
logger.error(`SKILL.md not found for skill: ${name}`);
|
|
11006
11351
|
process.exit(ExitCode.ERROR);
|
|
@@ -11018,16 +11363,16 @@ async function runSkill(name, opts) {
|
|
|
11018
11363
|
process.exit(ExitCode.SUCCESS);
|
|
11019
11364
|
}
|
|
11020
11365
|
function createRunCommand3() {
|
|
11021
|
-
return new
|
|
11366
|
+
return new Command93("run").description("Run a skill (outputs SKILL.md content with context preamble)").argument("<name>", "Skill name (e.g., harness-tdd)").option("--path <path>", "Project root path for context injection").option("--complexity <level>", "Rigor level: fast, standard, thorough", "standard").option("--phase <name>", "Start at a specific phase (for re-entry)").option("--party", "Enable multi-perspective evaluation").option(
|
|
11022
11367
|
"--backend <name>",
|
|
11023
11368
|
"Spec B: one-shot routing override forwarded to the orchestrator as HARNESS_BACKEND_OVERRIDE"
|
|
11024
11369
|
).action(async (name, opts) => runSkill(name, opts));
|
|
11025
11370
|
}
|
|
11026
11371
|
|
|
11027
11372
|
// src/commands/skill/validate.ts
|
|
11028
|
-
import { Command as
|
|
11373
|
+
import { Command as Command94 } from "commander";
|
|
11029
11374
|
import * as fs39 from "fs";
|
|
11030
|
-
import * as
|
|
11375
|
+
import * as path58 from "path";
|
|
11031
11376
|
import { parse as parse3 } from "yaml";
|
|
11032
11377
|
var BEHAVIORAL_REQUIRED_SECTIONS = [
|
|
11033
11378
|
"## When to Use",
|
|
@@ -11068,8 +11413,8 @@ function validateSkillMd(name, skillMdPath, skillType, errors) {
|
|
|
11068
11413
|
}
|
|
11069
11414
|
}
|
|
11070
11415
|
function validateSkillEntry(name, skillsDir, errors) {
|
|
11071
|
-
const skillDir =
|
|
11072
|
-
const yamlPath =
|
|
11416
|
+
const skillDir = path58.join(skillsDir, name);
|
|
11417
|
+
const yamlPath = path58.join(skillDir, "skill.yaml");
|
|
11073
11418
|
if (!fs39.existsSync(yamlPath)) {
|
|
11074
11419
|
errors.push(`${name}: missing skill.yaml`);
|
|
11075
11420
|
return false;
|
|
@@ -11081,7 +11426,7 @@ function validateSkillEntry(name, skillsDir, errors) {
|
|
|
11081
11426
|
errors.push(`${name}/skill.yaml: ${result.error.message}`);
|
|
11082
11427
|
return false;
|
|
11083
11428
|
}
|
|
11084
|
-
validateSkillMd(name,
|
|
11429
|
+
validateSkillMd(name, path58.join(skillDir, "SKILL.md"), result.data.type, errors);
|
|
11085
11430
|
return true;
|
|
11086
11431
|
} catch (e) {
|
|
11087
11432
|
errors.push(`${name}: parse error \u2014 ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -11089,7 +11434,7 @@ function validateSkillEntry(name, skillsDir, errors) {
|
|
|
11089
11434
|
}
|
|
11090
11435
|
}
|
|
11091
11436
|
function createValidateCommand2() {
|
|
11092
|
-
return new
|
|
11437
|
+
return new Command94("validate").description("Validate all skill.yaml files and SKILL.md structure").action(async (_opts, cmd) => {
|
|
11093
11438
|
const globalOpts = cmd.optsWithGlobals();
|
|
11094
11439
|
const skillsDir = resolveSkillsDir();
|
|
11095
11440
|
if (!fs39.existsSync(skillsDir)) {
|
|
@@ -11119,18 +11464,18 @@ function createValidateCommand2() {
|
|
|
11119
11464
|
}
|
|
11120
11465
|
|
|
11121
11466
|
// src/commands/skill/info.ts
|
|
11122
|
-
import { Command as
|
|
11467
|
+
import { Command as Command95 } from "commander";
|
|
11123
11468
|
import * as fs40 from "fs";
|
|
11124
|
-
import * as
|
|
11469
|
+
import * as path59 from "path";
|
|
11125
11470
|
import { parse as parse4 } from "yaml";
|
|
11126
11471
|
function loadSkillMetadata2(name) {
|
|
11127
11472
|
const skillsDir = resolveSkillsDir();
|
|
11128
|
-
const skillDir =
|
|
11473
|
+
const skillDir = path59.join(skillsDir, name);
|
|
11129
11474
|
if (!fs40.existsSync(skillDir)) {
|
|
11130
11475
|
logger.error(`Skill not found: ${name}`);
|
|
11131
11476
|
return { ok: false, exitCode: ExitCode.ERROR };
|
|
11132
11477
|
}
|
|
11133
|
-
const yamlPath =
|
|
11478
|
+
const yamlPath = path59.join(skillDir, "skill.yaml");
|
|
11134
11479
|
if (!fs40.existsSync(yamlPath)) {
|
|
11135
11480
|
logger.error(`skill.yaml not found for skill: ${name}`);
|
|
11136
11481
|
return { ok: false, exitCode: ExitCode.ERROR };
|
|
@@ -11165,7 +11510,7 @@ function printSkillInfo(skill) {
|
|
|
11165
11510
|
console.log(`Persistent: ${skill.state.persistent}`);
|
|
11166
11511
|
}
|
|
11167
11512
|
function createInfoCommand() {
|
|
11168
|
-
return new
|
|
11513
|
+
return new Command95("info").description("Show metadata for a skill").argument("<name>", "Skill name (e.g., harness-tdd)").action(async (name, _opts, cmd) => {
|
|
11169
11514
|
const globalOpts = cmd.optsWithGlobals();
|
|
11170
11515
|
const loaded = loadSkillMetadata2(name);
|
|
11171
11516
|
if (!loaded.ok) {
|
|
@@ -11182,7 +11527,7 @@ function createInfoCommand() {
|
|
|
11182
11527
|
}
|
|
11183
11528
|
|
|
11184
11529
|
// src/commands/skill/search.ts
|
|
11185
|
-
import { Command as
|
|
11530
|
+
import { Command as Command96 } from "commander";
|
|
11186
11531
|
async function runSearch(query, opts) {
|
|
11187
11532
|
const results = await searchNpmRegistry(query, opts.registry);
|
|
11188
11533
|
return results.filter((r) => {
|
|
@@ -11227,14 +11572,14 @@ async function runSearchAction(query, opts, globalOpts) {
|
|
|
11227
11572
|
}
|
|
11228
11573
|
}
|
|
11229
11574
|
function createSearchCommand2() {
|
|
11230
|
-
return new
|
|
11575
|
+
return new Command96("search").description("Search for community skills on the @harness-skills registry").argument("<query>", "Search query").option("--platform <platform>", "Filter by platform (e.g., claude-code)").option("--trigger <trigger>", "Filter by trigger type (e.g., manual, automatic)").option("--registry <url>", "Use a custom npm registry URL").action(async (query, opts, cmd) => {
|
|
11231
11576
|
await runSearchAction(query, opts, cmd.optsWithGlobals());
|
|
11232
11577
|
});
|
|
11233
11578
|
}
|
|
11234
11579
|
|
|
11235
11580
|
// src/commands/skill/create.ts
|
|
11236
|
-
import { Command as
|
|
11237
|
-
import * as
|
|
11581
|
+
import { Command as Command97 } from "commander";
|
|
11582
|
+
import * as path60 from "path";
|
|
11238
11583
|
import * as fs41 from "fs";
|
|
11239
11584
|
import YAML from "yaml";
|
|
11240
11585
|
var KEBAB_CASE_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
@@ -11319,21 +11664,21 @@ function runCreate(name, opts) {
|
|
|
11319
11664
|
if (!KEBAB_CASE_RE.test(name)) {
|
|
11320
11665
|
throw new Error(`Invalid skill name "${name}". Must be kebab-case (e.g., my-skill).`);
|
|
11321
11666
|
}
|
|
11322
|
-
const baseDir = opts.outputDir ??
|
|
11323
|
-
const skillDir =
|
|
11667
|
+
const baseDir = opts.outputDir ?? path60.join(process.cwd(), "agents", "skills", "claude-code");
|
|
11668
|
+
const skillDir = path60.join(baseDir, name);
|
|
11324
11669
|
if (fs41.existsSync(skillDir)) {
|
|
11325
11670
|
throw new Error(`Skill directory already exists: ${skillDir}`);
|
|
11326
11671
|
}
|
|
11327
11672
|
fs41.mkdirSync(skillDir, { recursive: true });
|
|
11328
11673
|
const description = opts.description || `A community skill: ${name}`;
|
|
11329
11674
|
const skillYaml = buildSkillYaml(name, opts);
|
|
11330
|
-
const skillYamlPath =
|
|
11675
|
+
const skillYamlPath = path60.join(skillDir, "skill.yaml");
|
|
11331
11676
|
fs41.writeFileSync(skillYamlPath, YAML.stringify(skillYaml));
|
|
11332
11677
|
const skillMd = buildSkillMd(name, description);
|
|
11333
|
-
const skillMdPath =
|
|
11678
|
+
const skillMdPath = path60.join(skillDir, "SKILL.md");
|
|
11334
11679
|
fs41.writeFileSync(skillMdPath, skillMd);
|
|
11335
11680
|
const readme = buildReadme(name, description);
|
|
11336
|
-
const readmePath =
|
|
11681
|
+
const readmePath = path60.join(skillDir, "README.md");
|
|
11337
11682
|
fs41.writeFileSync(readmePath, readme);
|
|
11338
11683
|
return {
|
|
11339
11684
|
name,
|
|
@@ -11348,12 +11693,12 @@ function printCreateResult(name, result) {
|
|
|
11348
11693
|
}
|
|
11349
11694
|
logger.info(`
|
|
11350
11695
|
Next steps:`);
|
|
11351
|
-
logger.info(` 1. Edit ${
|
|
11696
|
+
logger.info(` 1. Edit ${path60.join(result.directory, "SKILL.md")} with your skill content`);
|
|
11352
11697
|
logger.info(` 2. Run: harness skill validate ${name}`);
|
|
11353
11698
|
logger.info(` 3. Run: harness skills publish`);
|
|
11354
11699
|
}
|
|
11355
11700
|
function createCreateCommand() {
|
|
11356
|
-
return new
|
|
11701
|
+
return new Command97("create").description("Scaffold a new community skill").argument("<name>", "Skill name (kebab-case)").option("--description <desc>", "Skill description").option("--type <type>", "Skill type: rigid or flexible", "flexible").option("--platforms <platforms>", "Comma-separated platforms (default: claude-code)").option("--triggers <triggers>", "Comma-separated triggers (default: manual)").option("--output-dir <dir>", "Output directory (default: agents/skills/claude-code/)").action(async (name, opts, cmd) => {
|
|
11357
11702
|
const globalOpts = cmd.optsWithGlobals();
|
|
11358
11703
|
try {
|
|
11359
11704
|
const result = runCreate(name, {
|
|
@@ -11376,19 +11721,19 @@ function createCreateCommand() {
|
|
|
11376
11721
|
}
|
|
11377
11722
|
|
|
11378
11723
|
// src/commands/skill/publish.ts
|
|
11379
|
-
import { Command as
|
|
11724
|
+
import { Command as Command98 } from "commander";
|
|
11380
11725
|
import * as fs43 from "fs";
|
|
11381
|
-
import * as
|
|
11382
|
-
import { execFileSync as
|
|
11726
|
+
import * as path62 from "path";
|
|
11727
|
+
import { execFileSync as execFileSync7 } from "child_process";
|
|
11383
11728
|
|
|
11384
11729
|
// src/registry/validator.ts
|
|
11385
11730
|
import * as fs42 from "fs";
|
|
11386
|
-
import * as
|
|
11731
|
+
import * as path61 from "path";
|
|
11387
11732
|
import { parse as parse5 } from "yaml";
|
|
11388
11733
|
import semver4 from "semver";
|
|
11389
11734
|
async function validateForPublish(skillDir, registryUrl) {
|
|
11390
11735
|
const errors = [];
|
|
11391
|
-
const skillYamlPath =
|
|
11736
|
+
const skillYamlPath = path61.join(skillDir, "skill.yaml");
|
|
11392
11737
|
if (!fs42.existsSync(skillYamlPath)) {
|
|
11393
11738
|
errors.push("skill.yaml not found. Create one with: harness skill create <name>");
|
|
11394
11739
|
return { valid: false, errors };
|
|
@@ -11417,7 +11762,7 @@ async function validateForPublish(skillDir, registryUrl) {
|
|
|
11417
11762
|
if (!skillMeta.triggers || skillMeta.triggers.length === 0) {
|
|
11418
11763
|
errors.push("At least one trigger is required. Add triggers to skill.yaml.");
|
|
11419
11764
|
}
|
|
11420
|
-
const skillMdPath =
|
|
11765
|
+
const skillMdPath = path61.join(skillDir, "SKILL.md");
|
|
11421
11766
|
if (!fs42.existsSync(skillMdPath)) {
|
|
11422
11767
|
errors.push("SKILL.md not found. Create it with content describing your skill.");
|
|
11423
11768
|
} else {
|
|
@@ -11501,11 +11846,11 @@ ${errorList}`);
|
|
|
11501
11846
|
}
|
|
11502
11847
|
const meta = validation.skillMeta;
|
|
11503
11848
|
const pkg = derivePackageJson(meta);
|
|
11504
|
-
const pkgPath =
|
|
11849
|
+
const pkgPath = path62.join(skillDir, "package.json");
|
|
11505
11850
|
fs43.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
11506
|
-
const readmePath =
|
|
11851
|
+
const readmePath = path62.join(skillDir, "README.md");
|
|
11507
11852
|
if (!fs43.existsSync(readmePath)) {
|
|
11508
|
-
const skillMdContent = fs43.readFileSync(
|
|
11853
|
+
const skillMdContent = fs43.readFileSync(path62.join(skillDir, "SKILL.md"), "utf-8");
|
|
11509
11854
|
const readme = `# ${pkg.name}
|
|
11510
11855
|
|
|
11511
11856
|
${meta.description}
|
|
@@ -11533,7 +11878,7 @@ ${skillMdContent}`;
|
|
|
11533
11878
|
if (opts.registry) {
|
|
11534
11879
|
publishArgs.push("--registry", opts.registry);
|
|
11535
11880
|
}
|
|
11536
|
-
|
|
11881
|
+
execFileSync7("npm", publishArgs, {
|
|
11537
11882
|
cwd: skillDir,
|
|
11538
11883
|
stdio: "pipe",
|
|
11539
11884
|
timeout: 6e4
|
|
@@ -11545,7 +11890,7 @@ ${skillMdContent}`;
|
|
|
11545
11890
|
};
|
|
11546
11891
|
}
|
|
11547
11892
|
function createPublishCommand() {
|
|
11548
|
-
return new
|
|
11893
|
+
return new Command98("publish").description("Validate and publish a skill to @harness-skills on npm").option("--dry-run", "Run validation and generate package.json without publishing").option("--dir <dir>", "Skill directory (default: current directory)").option("--registry <url>", "Use a custom npm registry URL").action(async (opts, cmd) => {
|
|
11549
11894
|
const globalOpts = cmd.optsWithGlobals();
|
|
11550
11895
|
const skillDir = opts.dir || process.cwd();
|
|
11551
11896
|
try {
|
|
@@ -11570,7 +11915,7 @@ function createPublishCommand() {
|
|
|
11570
11915
|
|
|
11571
11916
|
// src/commands/skill/index.ts
|
|
11572
11917
|
function createSkillCommand() {
|
|
11573
|
-
const command = new
|
|
11918
|
+
const command = new Command99("skill").description("Skill management commands");
|
|
11574
11919
|
command.addCommand(createListCommand3());
|
|
11575
11920
|
command.addCommand(createRunCommand3());
|
|
11576
11921
|
command.addCommand(createValidateCommand2());
|
|
@@ -11583,7 +11928,7 @@ function createSkillCommand() {
|
|
|
11583
11928
|
|
|
11584
11929
|
// src/commands/snapshot.ts
|
|
11585
11930
|
init_dist3();
|
|
11586
|
-
import { Command as
|
|
11931
|
+
import { Command as Command100 } from "commander";
|
|
11587
11932
|
import { execSync as execSync5 } from "child_process";
|
|
11588
11933
|
import chalk16 from "chalk";
|
|
11589
11934
|
function getCommitHash3(cwd) {
|
|
@@ -11765,7 +12110,7 @@ function registerListCommand(parent) {
|
|
|
11765
12110
|
});
|
|
11766
12111
|
}
|
|
11767
12112
|
function createSnapshotCommand() {
|
|
11768
|
-
const command = new
|
|
12113
|
+
const command = new Command100("snapshot").description("Architecture timeline snapshot commands");
|
|
11769
12114
|
registerCaptureCommand(command);
|
|
11770
12115
|
registerTrendsCommand(command);
|
|
11771
12116
|
registerListCommand(command);
|
|
@@ -11773,9 +12118,9 @@ function createSnapshotCommand() {
|
|
|
11773
12118
|
}
|
|
11774
12119
|
|
|
11775
12120
|
// src/commands/spec-craft.ts
|
|
11776
|
-
import { Command as
|
|
12121
|
+
import { Command as Command101 } from "commander";
|
|
11777
12122
|
function createSpecCraftCommand() {
|
|
11778
|
-
return new
|
|
12123
|
+
return new Command101("spec-craft").description(
|
|
11779
12124
|
"LLM-judgment critique of spec quality (proposals + ADRs). Second craft-pipeline ceiling skill; 7 seed rubrics from the spec-quality canon. Per-section critique."
|
|
11780
12125
|
).option("-f, --files <files...>", "Optional spec file/glob scope").option("-k, --kinds <kinds...>", "Restrict to proposal / adr (default: both)").option("-s, --sections <names...>", "Restrict to specific canonical section names").option("--max-files <n>", "Cap doc count (default: 50)").option("--max-sections-per-file <n>", "Cap per-doc section critique (default: 10)").action(async (opts, cmd) => {
|
|
11781
12126
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -11823,10 +12168,10 @@ function printResult6(result, mode, _formatter) {
|
|
|
11823
12168
|
list.push(f);
|
|
11824
12169
|
byFile.set(f.target.file, list);
|
|
11825
12170
|
}
|
|
11826
|
-
for (const [file,
|
|
12171
|
+
for (const [file, fs49] of byFile) {
|
|
11827
12172
|
console.log(`
|
|
11828
12173
|
${file}`);
|
|
11829
|
-
for (const f of
|
|
12174
|
+
for (const f of fs49) {
|
|
11830
12175
|
console.log(
|
|
11831
12176
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ## ${f.target.section}:${f.target.line}`
|
|
11832
12177
|
);
|
|
@@ -11842,12 +12187,12 @@ ${file}`);
|
|
|
11842
12187
|
}
|
|
11843
12188
|
|
|
11844
12189
|
// src/commands/state/index.ts
|
|
11845
|
-
import { Command as
|
|
12190
|
+
import { Command as Command106 } from "commander";
|
|
11846
12191
|
|
|
11847
12192
|
// src/commands/state/show.ts
|
|
11848
12193
|
init_dist3();
|
|
11849
|
-
import { Command as
|
|
11850
|
-
import * as
|
|
12194
|
+
import { Command as Command102 } from "commander";
|
|
12195
|
+
import * as path63 from "path";
|
|
11851
12196
|
function printStateText(state, stream) {
|
|
11852
12197
|
if (stream) console.log(`Stream: ${stream}`);
|
|
11853
12198
|
console.log(`Schema Version: ${state.schemaVersion}`);
|
|
@@ -11871,9 +12216,9 @@ function printStateProgress(progress) {
|
|
|
11871
12216
|
}
|
|
11872
12217
|
}
|
|
11873
12218
|
function createShowCommand() {
|
|
11874
|
-
return new
|
|
12219
|
+
return new Command102("show").description("Show current project state").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(async (opts, cmd) => {
|
|
11875
12220
|
const globalOpts = cmd.optsWithGlobals();
|
|
11876
|
-
const result = await loadState(
|
|
12221
|
+
const result = await loadState(path63.resolve(opts.path), opts.stream);
|
|
11877
12222
|
if (!result.ok) {
|
|
11878
12223
|
logger.error(result.error.message);
|
|
11879
12224
|
process.exit(ExitCode.ERROR);
|
|
@@ -11889,9 +12234,9 @@ function createShowCommand() {
|
|
|
11889
12234
|
|
|
11890
12235
|
// src/commands/state/reset.ts
|
|
11891
12236
|
init_dist3();
|
|
11892
|
-
import { Command as
|
|
12237
|
+
import { Command as Command103 } from "commander";
|
|
11893
12238
|
import * as fs44 from "fs";
|
|
11894
|
-
import * as
|
|
12239
|
+
import * as path64 from "path";
|
|
11895
12240
|
import * as readline3 from "readline";
|
|
11896
12241
|
async function resolveStatePath(projectPath, stream) {
|
|
11897
12242
|
if (stream) {
|
|
@@ -11900,20 +12245,20 @@ async function resolveStatePath(projectPath, stream) {
|
|
|
11900
12245
|
logger.error(streamResult.error.message);
|
|
11901
12246
|
process.exit(ExitCode.ERROR);
|
|
11902
12247
|
}
|
|
11903
|
-
return
|
|
12248
|
+
return path64.join(streamResult.value, "state.json");
|
|
11904
12249
|
}
|
|
11905
|
-
return
|
|
12250
|
+
return path64.join(projectPath, ".harness", "state.json");
|
|
11906
12251
|
}
|
|
11907
12252
|
async function confirmReset() {
|
|
11908
12253
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
11909
|
-
const answer = await new Promise((
|
|
11910
|
-
rl.question("Reset project state? This cannot be undone. [y/N] ",
|
|
12254
|
+
const answer = await new Promise((resolve47) => {
|
|
12255
|
+
rl.question("Reset project state? This cannot be undone. [y/N] ", resolve47);
|
|
11911
12256
|
});
|
|
11912
12257
|
rl.close();
|
|
11913
12258
|
return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
|
|
11914
12259
|
}
|
|
11915
12260
|
async function runReset(opts) {
|
|
11916
|
-
const projectPath =
|
|
12261
|
+
const projectPath = path64.resolve(opts.path);
|
|
11917
12262
|
const statePath = await resolveStatePath(projectPath, opts.stream);
|
|
11918
12263
|
if (!fs44.existsSync(statePath)) {
|
|
11919
12264
|
logger.info("No state file found. Nothing to reset.");
|
|
@@ -11933,16 +12278,16 @@ async function runReset(opts) {
|
|
|
11933
12278
|
process.exit(ExitCode.SUCCESS);
|
|
11934
12279
|
}
|
|
11935
12280
|
function createResetCommand() {
|
|
11936
|
-
return new
|
|
12281
|
+
return new Command103("reset").description("Reset project state (deletes .harness/state.json)").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").option("--yes", "Skip confirmation prompt").action(async (opts) => runReset(opts));
|
|
11937
12282
|
}
|
|
11938
12283
|
|
|
11939
12284
|
// src/commands/state/learn.ts
|
|
11940
12285
|
init_dist3();
|
|
11941
|
-
import { Command as
|
|
11942
|
-
import * as
|
|
12286
|
+
import { Command as Command104 } from "commander";
|
|
12287
|
+
import * as path65 from "path";
|
|
11943
12288
|
function createLearnCommand() {
|
|
11944
|
-
return new
|
|
11945
|
-
const projectPath =
|
|
12289
|
+
return new Command104("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) => {
|
|
12290
|
+
const projectPath = path65.resolve(opts.path);
|
|
11946
12291
|
const result = await appendLearning(projectPath, message, void 0, void 0, opts.stream);
|
|
11947
12292
|
if (!result.ok) {
|
|
11948
12293
|
logger.error(result.error.message);
|
|
@@ -11956,10 +12301,10 @@ function createLearnCommand() {
|
|
|
11956
12301
|
|
|
11957
12302
|
// src/commands/state/streams.ts
|
|
11958
12303
|
init_dist3();
|
|
11959
|
-
import { Command as
|
|
11960
|
-
import * as
|
|
12304
|
+
import { Command as Command105 } from "commander";
|
|
12305
|
+
import * as path66 from "path";
|
|
11961
12306
|
async function runListStreams(opts, globalOpts) {
|
|
11962
|
-
const projectPath =
|
|
12307
|
+
const projectPath = path66.resolve(opts.path);
|
|
11963
12308
|
const indexResult = await loadStreamIndex(projectPath);
|
|
11964
12309
|
const result = await listStreams(projectPath);
|
|
11965
12310
|
if (!result.ok) {
|
|
@@ -11980,7 +12325,7 @@ async function runListStreams(opts, globalOpts) {
|
|
|
11980
12325
|
process.exit(ExitCode.SUCCESS);
|
|
11981
12326
|
}
|
|
11982
12327
|
async function runCreateStream(name, opts) {
|
|
11983
|
-
const result = await createStream(
|
|
12328
|
+
const result = await createStream(path66.resolve(opts.path), name, opts.branch);
|
|
11984
12329
|
if (!result.ok) {
|
|
11985
12330
|
logger.error(result.error.message);
|
|
11986
12331
|
process.exit(ExitCode.ERROR);
|
|
@@ -11989,7 +12334,7 @@ async function runCreateStream(name, opts) {
|
|
|
11989
12334
|
process.exit(ExitCode.SUCCESS);
|
|
11990
12335
|
}
|
|
11991
12336
|
async function runArchiveStream(name, opts) {
|
|
11992
|
-
const result = await archiveStream(
|
|
12337
|
+
const result = await archiveStream(path66.resolve(opts.path), name);
|
|
11993
12338
|
if (!result.ok) {
|
|
11994
12339
|
logger.error(result.error.message);
|
|
11995
12340
|
process.exit(ExitCode.ERROR);
|
|
@@ -11998,7 +12343,7 @@ async function runArchiveStream(name, opts) {
|
|
|
11998
12343
|
process.exit(ExitCode.SUCCESS);
|
|
11999
12344
|
}
|
|
12000
12345
|
async function runActivateStream(name, opts) {
|
|
12001
|
-
const result = await setActiveStream(
|
|
12346
|
+
const result = await setActiveStream(path66.resolve(opts.path), name);
|
|
12002
12347
|
if (!result.ok) {
|
|
12003
12348
|
logger.error(result.error.message);
|
|
12004
12349
|
process.exit(ExitCode.ERROR);
|
|
@@ -12007,7 +12352,7 @@ async function runActivateStream(name, opts) {
|
|
|
12007
12352
|
process.exit(ExitCode.SUCCESS);
|
|
12008
12353
|
}
|
|
12009
12354
|
function createStreamsCommand() {
|
|
12010
|
-
const command = new
|
|
12355
|
+
const command = new Command105("streams").description("Manage state streams");
|
|
12011
12356
|
command.command("list").description("List all known streams").option("--path <path>", "Project root path", ".").action(async (opts, cmd) => runListStreams(opts, cmd.optsWithGlobals()));
|
|
12012
12357
|
command.command("create <name>").description("Create a new stream").option("--path <path>", "Project root path", ".").option("--branch <branch>", "Associate with a git branch").action(async (name, opts) => runCreateStream(name, opts));
|
|
12013
12358
|
command.command("archive <name>").description("Archive a stream").option("--path <path>", "Project root path", ".").action(async (name, opts) => runArchiveStream(name, opts));
|
|
@@ -12017,7 +12362,7 @@ function createStreamsCommand() {
|
|
|
12017
12362
|
|
|
12018
12363
|
// src/commands/state/index.ts
|
|
12019
12364
|
function createStateCommand() {
|
|
12020
|
-
const command = new
|
|
12365
|
+
const command = new Command106("state").description("Project state management commands");
|
|
12021
12366
|
command.addCommand(createShowCommand());
|
|
12022
12367
|
command.addCommand(createResetCommand());
|
|
12023
12368
|
command.addCommand(createLearnCommand());
|
|
@@ -12027,8 +12372,8 @@ function createStateCommand() {
|
|
|
12027
12372
|
|
|
12028
12373
|
// src/commands/sync-analyses.ts
|
|
12029
12374
|
import * as fs45 from "fs";
|
|
12030
|
-
import * as
|
|
12031
|
-
import { Command as
|
|
12375
|
+
import * as path67 from "path";
|
|
12376
|
+
import { Command as Command107 } from "commander";
|
|
12032
12377
|
init_dist3();
|
|
12033
12378
|
function isValidRecord(record) {
|
|
12034
12379
|
return typeof record.issueId === "string" && !!record.issueId && typeof record.identifier === "string" && !!record.identifier && typeof record.analyzedAt === "string" && !/[/\\]/.test(record.issueId);
|
|
@@ -12054,7 +12399,7 @@ function extractAnalysisFromComments(comments) {
|
|
|
12054
12399
|
return null;
|
|
12055
12400
|
}
|
|
12056
12401
|
function bootstrapTrackerCommand2(opts, verb) {
|
|
12057
|
-
const projectPath =
|
|
12402
|
+
const projectPath = path67.resolve(opts.dir);
|
|
12058
12403
|
const trackerConfig = loadTrackerSyncConfig(projectPath);
|
|
12059
12404
|
if (!trackerConfig) {
|
|
12060
12405
|
logger.error(`No tracker config found in harness.config.json. Cannot ${verb}.`);
|
|
@@ -12066,7 +12411,7 @@ function bootstrapTrackerCommand2(opts, verb) {
|
|
|
12066
12411
|
);
|
|
12067
12412
|
return null;
|
|
12068
12413
|
}
|
|
12069
|
-
const projectEnvPath =
|
|
12414
|
+
const projectEnvPath = path67.join(projectPath, ".env");
|
|
12070
12415
|
if (fs45.existsSync(projectEnvPath) && !process.env.GITHUB_TOKEN) {
|
|
12071
12416
|
__require("dotenv").config({ path: projectEnvPath });
|
|
12072
12417
|
}
|
|
@@ -12097,7 +12442,7 @@ async function loadRoadmapFeatures(projectPath) {
|
|
|
12097
12442
|
}
|
|
12098
12443
|
return r.value.features.filter((f) => !!f.externalId).map((f) => ({ name: f.name, externalId: f.externalId }));
|
|
12099
12444
|
}
|
|
12100
|
-
const roadmapFile =
|
|
12445
|
+
const roadmapFile = path67.join(projectPath, "docs", "roadmap.md");
|
|
12101
12446
|
if (!fs45.existsSync(roadmapFile)) {
|
|
12102
12447
|
logger.error("No docs/roadmap.md found. Cannot discover features with externalIds.");
|
|
12103
12448
|
return null;
|
|
@@ -12154,9 +12499,9 @@ async function runSyncAnalyses(opts) {
|
|
|
12154
12499
|
return;
|
|
12155
12500
|
}
|
|
12156
12501
|
const { AnalysisArchive, loadPublishedIndex: loadPublishedIndex2, savePublishedIndex: savePublishedIndex2 } = await import("@harness-engineering/orchestrator");
|
|
12157
|
-
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-
|
|
12502
|
+
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-LHINSVK4.js");
|
|
12158
12503
|
const adapter = new GitHubIssuesSyncAdapter2({ token, config: trackerConfig });
|
|
12159
|
-
const archive = new AnalysisArchive(
|
|
12504
|
+
const archive = new AnalysisArchive(path67.join(projectPath, ".harness", "analyses"));
|
|
12160
12505
|
const publishedIndex = loadPublishedIndex2(projectPath);
|
|
12161
12506
|
const { syncedCount, skippedCount, warnCount } = await syncFeatureAnalyses(
|
|
12162
12507
|
features,
|
|
@@ -12176,7 +12521,7 @@ async function runSyncAnalyses(opts) {
|
|
|
12176
12521
|
}
|
|
12177
12522
|
}
|
|
12178
12523
|
function createSyncAnalysesCommand() {
|
|
12179
|
-
const command = new
|
|
12524
|
+
const command = new Command107("sync-analyses").description(
|
|
12180
12525
|
"Pull published intelligence analyses from the external issue tracker into the local .harness/analyses/ directory"
|
|
12181
12526
|
).option("-d, --dir <path>", "Workspace directory", process.cwd()).action(async (opts) => {
|
|
12182
12527
|
try {
|
|
@@ -12190,8 +12535,8 @@ function createSyncAnalysesCommand() {
|
|
|
12190
12535
|
}
|
|
12191
12536
|
|
|
12192
12537
|
// src/commands/sync-main.ts
|
|
12193
|
-
import { Command as
|
|
12194
|
-
import * as
|
|
12538
|
+
import { Command as Command108 } from "commander";
|
|
12539
|
+
import * as path68 from "path";
|
|
12195
12540
|
import {
|
|
12196
12541
|
syncMain as defaultSyncMain
|
|
12197
12542
|
} from "@harness-engineering/orchestrator";
|
|
@@ -12221,8 +12566,8 @@ function formatHuman2(r) {
|
|
|
12221
12566
|
}
|
|
12222
12567
|
}
|
|
12223
12568
|
function createSyncMainCommand() {
|
|
12224
|
-
return new
|
|
12225
|
-
const cwd =
|
|
12569
|
+
return new Command108("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) => {
|
|
12570
|
+
const cwd = path68.resolve(opts.path ?? ".");
|
|
12226
12571
|
const exitCode = await runSyncMain({ cwd, json: Boolean(opts.json) });
|
|
12227
12572
|
process.exit(exitCode);
|
|
12228
12573
|
});
|
|
@@ -12230,7 +12575,7 @@ function createSyncMainCommand() {
|
|
|
12230
12575
|
|
|
12231
12576
|
// src/commands/taint.ts
|
|
12232
12577
|
init_dist3();
|
|
12233
|
-
import { Command as
|
|
12578
|
+
import { Command as Command109 } from "commander";
|
|
12234
12579
|
function getProjectRoot() {
|
|
12235
12580
|
return process.cwd();
|
|
12236
12581
|
}
|
|
@@ -12302,26 +12647,26 @@ function registerStatusCommand(taint) {
|
|
|
12302
12647
|
});
|
|
12303
12648
|
}
|
|
12304
12649
|
function createTaintCommand() {
|
|
12305
|
-
const taint = new
|
|
12650
|
+
const taint = new Command109("taint").description("Manage sentinel session taint state");
|
|
12306
12651
|
registerClearCommand(taint);
|
|
12307
12652
|
registerStatusCommand(taint);
|
|
12308
12653
|
return taint;
|
|
12309
12654
|
}
|
|
12310
12655
|
|
|
12311
12656
|
// src/commands/telemetry/index.ts
|
|
12312
|
-
import { Command as
|
|
12657
|
+
import { Command as Command113 } from "commander";
|
|
12313
12658
|
|
|
12314
12659
|
// src/commands/telemetry/identify.ts
|
|
12315
12660
|
init_dist3();
|
|
12316
|
-
import { Command as
|
|
12661
|
+
import { Command as Command110 } from "commander";
|
|
12317
12662
|
import * as fs46 from "fs";
|
|
12318
|
-
import * as
|
|
12663
|
+
import * as path69 from "path";
|
|
12319
12664
|
function telemetryFilePath(cwd) {
|
|
12320
|
-
return
|
|
12665
|
+
return path69.join(cwd, ".harness", "telemetry.json");
|
|
12321
12666
|
}
|
|
12322
12667
|
function writeTelemetryFile(cwd, data) {
|
|
12323
12668
|
const filePath = telemetryFilePath(cwd);
|
|
12324
|
-
const dir =
|
|
12669
|
+
const dir = path69.dirname(filePath);
|
|
12325
12670
|
fs46.mkdirSync(dir, { recursive: true });
|
|
12326
12671
|
fs46.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
12327
12672
|
}
|
|
@@ -12337,7 +12682,7 @@ function printIdentity(identity) {
|
|
|
12337
12682
|
}
|
|
12338
12683
|
}
|
|
12339
12684
|
function createIdentifyCommand() {
|
|
12340
|
-
const cmd = new
|
|
12685
|
+
const cmd = new Command110("identify").description("Set or clear telemetry identity fields in .harness/telemetry.json").option("--project <name>", "Project name").option("--team <name>", "Team name").option("--alias <name>", "User alias").option("--clear", "Remove all identity fields").action((opts) => {
|
|
12341
12686
|
const cwd = process.cwd();
|
|
12342
12687
|
if (opts.clear) {
|
|
12343
12688
|
writeTelemetryFile(cwd, { identity: {} });
|
|
@@ -12362,7 +12707,7 @@ function createIdentifyCommand() {
|
|
|
12362
12707
|
|
|
12363
12708
|
// src/commands/telemetry/status.ts
|
|
12364
12709
|
init_dist3();
|
|
12365
|
-
import { Command as
|
|
12710
|
+
import { Command as Command111 } from "commander";
|
|
12366
12711
|
function gatherEnvOverrides() {
|
|
12367
12712
|
const overrides = {};
|
|
12368
12713
|
if (process.env.DO_NOT_TRACK) overrides.DO_NOT_TRACK = process.env.DO_NOT_TRACK;
|
|
@@ -12404,7 +12749,7 @@ function printHumanStatus(result) {
|
|
|
12404
12749
|
printEnvOverrides(result.envOverrides);
|
|
12405
12750
|
}
|
|
12406
12751
|
function createStatusCommand() {
|
|
12407
|
-
const cmd = new
|
|
12752
|
+
const cmd = new Command111("status").description("Show current telemetry consent state, install ID, and identity").option("--json", "Output as JSON").action((opts) => {
|
|
12408
12753
|
const cwd = process.cwd();
|
|
12409
12754
|
const envOverrides = gatherEnvOverrides();
|
|
12410
12755
|
const consent = resolveConsent(cwd, void 0);
|
|
@@ -12437,20 +12782,20 @@ function createStatusCommand() {
|
|
|
12437
12782
|
|
|
12438
12783
|
// src/commands/telemetry/test.ts
|
|
12439
12784
|
init_dist3();
|
|
12440
|
-
import { Command as
|
|
12785
|
+
import { Command as Command112 } from "commander";
|
|
12441
12786
|
|
|
12442
12787
|
// src/bin/command-telemetry.ts
|
|
12443
|
-
import { existsSync as existsSync43, readFileSync as readFileSync33, mkdirSync as mkdirSync19, appendFileSync, writeFileSync as
|
|
12444
|
-
import { join as join57, parse as parsePath, resolve as
|
|
12788
|
+
import { existsSync as existsSync43, readFileSync as readFileSync33, mkdirSync as mkdirSync19, appendFileSync, writeFileSync as writeFileSync26 } from "fs";
|
|
12789
|
+
import { join as join57, parse as parsePath, resolve as resolve44 } from "path";
|
|
12445
12790
|
import { spawn as spawn2 } from "child_process";
|
|
12446
12791
|
var POSTHOG_API_KEY = "phc_wNTdCMcfJXZPgdNeDociZW6vwoGGo4nb7vqEfWThFfsG";
|
|
12447
12792
|
var EXCLUDED_COMMANDS = /* @__PURE__ */ new Set(["help", "completion"]);
|
|
12448
12793
|
function findProjectRoot(cwd) {
|
|
12449
|
-
let dir =
|
|
12794
|
+
let dir = resolve44(cwd);
|
|
12450
12795
|
const { root } = parsePath(dir);
|
|
12451
12796
|
while (dir !== root) {
|
|
12452
12797
|
if (existsSync43(join57(dir, "harness.config.json"))) return dir;
|
|
12453
|
-
dir =
|
|
12798
|
+
dir = resolve44(dir, "..");
|
|
12454
12799
|
}
|
|
12455
12800
|
return cwd;
|
|
12456
12801
|
}
|
|
@@ -12559,7 +12904,7 @@ function reportTestSuccess(status, distinctId, identity) {
|
|
|
12559
12904
|
if (identity.team) logger.info(` team: ${identity.team}`);
|
|
12560
12905
|
}
|
|
12561
12906
|
function createTestCommand() {
|
|
12562
|
-
return new
|
|
12907
|
+
return new Command112("test").description("Send a test event to PostHog and verify connectivity").action(async () => {
|
|
12563
12908
|
const cwd = process.cwd();
|
|
12564
12909
|
const consent = resolveConsent(cwd, void 0);
|
|
12565
12910
|
if (!consent.allowed) {
|
|
@@ -12596,7 +12941,7 @@ function createTestCommand() {
|
|
|
12596
12941
|
|
|
12597
12942
|
// src/commands/telemetry/index.ts
|
|
12598
12943
|
function createTelemetryCommand() {
|
|
12599
|
-
const command = new
|
|
12944
|
+
const command = new Command113("telemetry").description("Telemetry identity and status management");
|
|
12600
12945
|
command.addCommand(createIdentifyCommand());
|
|
12601
12946
|
command.addCommand(createStatusCommand());
|
|
12602
12947
|
command.addCommand(createTestCommand());
|
|
@@ -12604,9 +12949,9 @@ function createTelemetryCommand() {
|
|
|
12604
12949
|
}
|
|
12605
12950
|
|
|
12606
12951
|
// src/commands/test-craft.ts
|
|
12607
|
-
import { Command as
|
|
12952
|
+
import { Command as Command114 } from "commander";
|
|
12608
12953
|
function createTestCraftCommand() {
|
|
12609
|
-
return new
|
|
12954
|
+
return new Command114("test-craft").description(
|
|
12610
12955
|
"LLM-judgment critique of test quality across vitest/jest/mocha/playwright. Fourth craft-pipeline ceiling skill. Per-test critique with best-effort source pairing."
|
|
12611
12956
|
).option("-f, --files <files...>", "Optional test file/glob scope").option("--frameworks <names...>", "Restrict to: vitest / jest / mocha / playwright").option("--max-files <n>", "Cap test file count (default: 100)").option("--max-tests-per-file <n>", "Cap per-file test critique (default: 20)").option("--no-source-pair", "Skip source-pairing resolution").action(async (opts, cmd) => {
|
|
12612
12957
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -12654,10 +12999,10 @@ function printResult7(result, mode, _formatter) {
|
|
|
12654
12999
|
list.push(f);
|
|
12655
13000
|
byFile.set(f.target.file, list);
|
|
12656
13001
|
}
|
|
12657
|
-
for (const [file,
|
|
13002
|
+
for (const [file, fs49] of byFile) {
|
|
12658
13003
|
console.log(`
|
|
12659
13004
|
${file}`);
|
|
12660
|
-
for (const f of
|
|
13005
|
+
for (const f of fs49) {
|
|
12661
13006
|
const nestingStr = f.target.nesting.length > 0 ? f.target.nesting.join(" > ") + " > " : "";
|
|
12662
13007
|
console.log(
|
|
12663
13008
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.framework}:${f.target.line}`
|
|
@@ -12676,7 +13021,7 @@ ${file}`);
|
|
|
12676
13021
|
}
|
|
12677
13022
|
|
|
12678
13023
|
// src/commands/traceability.ts
|
|
12679
|
-
import { Command as
|
|
13024
|
+
import { Command as Command115 } from "commander";
|
|
12680
13025
|
import chalk17 from "chalk";
|
|
12681
13026
|
function confidenceLabel(maxConfidence) {
|
|
12682
13027
|
if (maxConfidence >= 0.8) return "explicit";
|
|
@@ -12812,7 +13157,7 @@ async function runTraceability(options) {
|
|
|
12812
13157
|
process.exit(ExitCode.SUCCESS);
|
|
12813
13158
|
}
|
|
12814
13159
|
function createTraceabilityCommand() {
|
|
12815
|
-
return new
|
|
13160
|
+
return new Command115("traceability").description("Show spec-to-implementation traceability from the knowledge graph").option("--spec <path>", "Filter by spec file path").option("--feature <name>", "Filter by feature name").action(async (opts, cmd) => {
|
|
12816
13161
|
const globalOpts = cmd.optsWithGlobals();
|
|
12817
13162
|
await runTraceability({
|
|
12818
13163
|
spec: opts.spec,
|
|
@@ -12825,15 +13170,15 @@ function createTraceabilityCommand() {
|
|
|
12825
13170
|
}
|
|
12826
13171
|
|
|
12827
13172
|
// src/commands/uninstall.ts
|
|
12828
|
-
import * as
|
|
12829
|
-
import { Command as
|
|
13173
|
+
import * as path70 from "path";
|
|
13174
|
+
import { Command as Command116 } from "commander";
|
|
12830
13175
|
async function runUninstall(skillName, options) {
|
|
12831
13176
|
const packageName = resolvePackageName(skillName);
|
|
12832
13177
|
const shortName = extractSkillName(packageName);
|
|
12833
13178
|
const globalDir = resolveGlobalSkillsDir();
|
|
12834
|
-
const skillsDir =
|
|
12835
|
-
const communityBase =
|
|
12836
|
-
const lockfilePath =
|
|
13179
|
+
const skillsDir = path70.dirname(globalDir);
|
|
13180
|
+
const communityBase = path70.join(skillsDir, "community");
|
|
13181
|
+
const lockfilePath = path70.join(communityBase, "skills-lock.json");
|
|
12837
13182
|
const lockfile = readLockfile2(lockfilePath);
|
|
12838
13183
|
const entry = lockfile.skills[packageName];
|
|
12839
13184
|
if (!entry) {
|
|
@@ -12863,7 +13208,7 @@ async function runUninstall(skillName, options) {
|
|
|
12863
13208
|
return result;
|
|
12864
13209
|
}
|
|
12865
13210
|
function createUninstallCommand() {
|
|
12866
|
-
const cmd = new
|
|
13211
|
+
const cmd = new Command116("uninstall");
|
|
12867
13212
|
cmd.description("Uninstall a community skill").argument("<skill>", "Skill name or @harness-skills/scoped package name").option("--force", "Remove even if other skills depend on this one").action(async (skill, opts) => {
|
|
12868
13213
|
try {
|
|
12869
13214
|
const result = await runUninstall(skill, opts);
|
|
@@ -12884,8 +13229,8 @@ function createUninstallCommand() {
|
|
|
12884
13229
|
// src/commands/uninstall-constraints.ts
|
|
12885
13230
|
init_dist3();
|
|
12886
13231
|
import * as fs47 from "fs/promises";
|
|
12887
|
-
import * as
|
|
12888
|
-
import { Command as
|
|
13232
|
+
import * as path71 from "path";
|
|
13233
|
+
import { Command as Command117 } from "commander";
|
|
12889
13234
|
async function runUninstallConstraints(options) {
|
|
12890
13235
|
const { packageName, configPath, lockfilePath } = options;
|
|
12891
13236
|
const lockfileResult = await readLockfile(lockfilePath);
|
|
@@ -12942,11 +13287,11 @@ async function runUninstallConstraints(options) {
|
|
|
12942
13287
|
};
|
|
12943
13288
|
}
|
|
12944
13289
|
function createUninstallConstraintsCommand() {
|
|
12945
|
-
const cmd = new
|
|
13290
|
+
const cmd = new Command117("uninstall-constraints");
|
|
12946
13291
|
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) => {
|
|
12947
13292
|
let configPath;
|
|
12948
13293
|
if (opts.config) {
|
|
12949
|
-
configPath =
|
|
13294
|
+
configPath = path71.resolve(opts.config);
|
|
12950
13295
|
} else {
|
|
12951
13296
|
const found = findConfigFile();
|
|
12952
13297
|
if (!found.ok) {
|
|
@@ -12955,8 +13300,8 @@ function createUninstallConstraintsCommand() {
|
|
|
12955
13300
|
}
|
|
12956
13301
|
configPath = found.value;
|
|
12957
13302
|
}
|
|
12958
|
-
const projectRoot2 =
|
|
12959
|
-
const lockfilePath =
|
|
13303
|
+
const projectRoot2 = path71.dirname(configPath);
|
|
13304
|
+
const lockfilePath = path71.join(projectRoot2, ".harness", "constraints.lock.json");
|
|
12960
13305
|
const result = await runUninstallConstraints({
|
|
12961
13306
|
packageName: name,
|
|
12962
13307
|
configPath,
|
|
@@ -12982,8 +13327,8 @@ function createUninstallConstraintsCommand() {
|
|
|
12982
13327
|
|
|
12983
13328
|
// src/commands/update.ts
|
|
12984
13329
|
init_dist3();
|
|
12985
|
-
import { Command as
|
|
12986
|
-
import { execFile, execFileSync as
|
|
13330
|
+
import { Command as Command118 } from "commander";
|
|
13331
|
+
import { execFile, execFileSync as execFileSync8 } from "child_process";
|
|
12987
13332
|
import { realpathSync, existsSync as existsSync44, readFileSync as readFileSync34 } from "fs";
|
|
12988
13333
|
import { dirname as dirname23, join as join60 } from "path";
|
|
12989
13334
|
import { promisify } from "util";
|
|
@@ -13025,7 +13370,7 @@ async function getLatestVersionAsync(pkg) {
|
|
|
13025
13370
|
function getInstalledVersions(pm, packages) {
|
|
13026
13371
|
const versions = {};
|
|
13027
13372
|
try {
|
|
13028
|
-
const output =
|
|
13373
|
+
const output = execFileSync8(pm, ["list", "-g", "--json"], {
|
|
13029
13374
|
encoding: "utf-8",
|
|
13030
13375
|
timeout: 15e3
|
|
13031
13376
|
});
|
|
@@ -13046,7 +13391,7 @@ function getInstalledVersions(pm, packages) {
|
|
|
13046
13391
|
}
|
|
13047
13392
|
function getInstalledPackages(pm) {
|
|
13048
13393
|
try {
|
|
13049
|
-
const output =
|
|
13394
|
+
const output = execFileSync8(pm, ["list", "-g", "--json"], {
|
|
13050
13395
|
encoding: "utf-8",
|
|
13051
13396
|
timeout: 15e3
|
|
13052
13397
|
});
|
|
@@ -13066,10 +13411,10 @@ function prompt3(question) {
|
|
|
13066
13411
|
input: process.stdin,
|
|
13067
13412
|
output: process.stdout
|
|
13068
13413
|
});
|
|
13069
|
-
return new Promise((
|
|
13414
|
+
return new Promise((resolve47) => {
|
|
13070
13415
|
rl.question(question, (answer) => {
|
|
13071
13416
|
rl.close();
|
|
13072
|
-
|
|
13417
|
+
resolve47(answer.trim().toLowerCase());
|
|
13073
13418
|
});
|
|
13074
13419
|
});
|
|
13075
13420
|
}
|
|
@@ -13101,7 +13446,7 @@ function findAllInstalls() {
|
|
|
13101
13446
|
const args = isWindows ? ["harness"] : ["-a", "harness"];
|
|
13102
13447
|
let output;
|
|
13103
13448
|
try {
|
|
13104
|
-
const raw =
|
|
13449
|
+
const raw = execFileSync8(cmd, args, { encoding: "utf-8", timeout: 5e3 });
|
|
13105
13450
|
if (typeof raw !== "string") return [];
|
|
13106
13451
|
output = raw;
|
|
13107
13452
|
} catch {
|
|
@@ -13178,7 +13523,7 @@ async function offerCleanupOfOtherInstalls(activeDir) {
|
|
|
13178
13523
|
}
|
|
13179
13524
|
try {
|
|
13180
13525
|
logger.info(`Uninstalling: ${cmd.display}`);
|
|
13181
|
-
|
|
13526
|
+
execFileSync8(cmd.bin, cmd.args, { stdio: "inherit", timeout: 12e4 });
|
|
13182
13527
|
} catch {
|
|
13183
13528
|
logger.warn(`Uninstall failed for ${cmd.install.binPath}.`);
|
|
13184
13529
|
console.log(` Run manually: ${chalk18.cyan(cmd.display)}`);
|
|
@@ -13238,7 +13583,7 @@ function refreshHooks() {
|
|
|
13238
13583
|
function runLocalGraphScan() {
|
|
13239
13584
|
try {
|
|
13240
13585
|
logger.info("Scanning codebase to rebuild knowledge graph...");
|
|
13241
|
-
|
|
13586
|
+
execFileSync8("harness", ["graph", "scan", "."], { stdio: "inherit" });
|
|
13242
13587
|
} catch {
|
|
13243
13588
|
logger.warn("Graph scan failed. Run manually:");
|
|
13244
13589
|
console.log(` ${chalk18.cyan("harness graph scan .")}`);
|
|
@@ -13253,7 +13598,7 @@ async function offerRegeneration() {
|
|
|
13253
13598
|
const scopeAnswer = await prompt3("Generate for (G)lobal or (l)ocal project? (G/l) ");
|
|
13254
13599
|
const isGlobal = scopeAnswer !== "l" && scopeAnswer !== "local";
|
|
13255
13600
|
try {
|
|
13256
|
-
|
|
13601
|
+
execFileSync8("harness", ["generate", ...isGlobal ? ["--global"] : []], {
|
|
13257
13602
|
stdio: "inherit"
|
|
13258
13603
|
});
|
|
13259
13604
|
} catch {
|
|
@@ -13333,7 +13678,7 @@ async function runUpdateAction(opts, globalOpts) {
|
|
|
13333
13678
|
}
|
|
13334
13679
|
try {
|
|
13335
13680
|
logger.info("Updating packages...");
|
|
13336
|
-
|
|
13681
|
+
execFileSync8(pm, ["install", "-g", ...installPkgs], { stdio: "inherit", timeout: 12e4 });
|
|
13337
13682
|
console.log("");
|
|
13338
13683
|
logger.success("Update complete");
|
|
13339
13684
|
invalidateCheckState();
|
|
@@ -13350,7 +13695,7 @@ async function runUpdateAction(opts, globalOpts) {
|
|
|
13350
13695
|
process.exit(ExitCode.SUCCESS);
|
|
13351
13696
|
}
|
|
13352
13697
|
function createUpdateCommand() {
|
|
13353
|
-
return new
|
|
13698
|
+
return new Command118("update").description("Update all @harness-engineering packages to the latest version").option("--version <semver>", "Pin @harness-engineering/cli to a specific version").option("--force", "Force update even if versions match").option(
|
|
13354
13699
|
"--regenerate",
|
|
13355
13700
|
"Only regenerate slash commands and agent definitions (skip package updates)"
|
|
13356
13701
|
).action(async (opts, cmd) => {
|
|
@@ -13360,9 +13705,9 @@ function createUpdateCommand() {
|
|
|
13360
13705
|
}
|
|
13361
13706
|
|
|
13362
13707
|
// src/commands/usage.ts
|
|
13363
|
-
import { Command as
|
|
13708
|
+
import { Command as Command119 } from "commander";
|
|
13364
13709
|
async function loadAndPriceRecords(cwd, includeClaudeSessions = false) {
|
|
13365
|
-
const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-
|
|
13710
|
+
const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-LHINSVK4.js");
|
|
13366
13711
|
const records = readCostRecords(cwd);
|
|
13367
13712
|
if (includeClaudeSessions) {
|
|
13368
13713
|
const ccRecords = parseCCRecords();
|
|
@@ -13455,7 +13800,7 @@ function registerDailyCommand(usage) {
|
|
|
13455
13800
|
else logger.info("No usage data found. Run some harness sessions first.");
|
|
13456
13801
|
return;
|
|
13457
13802
|
}
|
|
13458
|
-
const { aggregateByDay } = await import("./dist-
|
|
13803
|
+
const { aggregateByDay } = await import("./dist-LHINSVK4.js");
|
|
13459
13804
|
const limited = aggregateByDay(records).slice(0, days);
|
|
13460
13805
|
if (globalOpts.json) {
|
|
13461
13806
|
console.log(
|
|
@@ -13484,7 +13829,7 @@ function registerSessionsCommand(usage) {
|
|
|
13484
13829
|
}
|
|
13485
13830
|
return;
|
|
13486
13831
|
}
|
|
13487
|
-
const { aggregateBySession } = await import("./dist-
|
|
13832
|
+
const { aggregateBySession } = await import("./dist-LHINSVK4.js");
|
|
13488
13833
|
const sessionData = aggregateBySession(records);
|
|
13489
13834
|
const limited = sessionData.slice(0, limit);
|
|
13490
13835
|
if (globalOpts.json) {
|
|
@@ -13564,7 +13909,7 @@ function registerSessionCommand(usage) {
|
|
|
13564
13909
|
cwd,
|
|
13565
13910
|
globalOpts.includeClaudeSessions
|
|
13566
13911
|
);
|
|
13567
|
-
const { aggregateBySession } = await import("./dist-
|
|
13912
|
+
const { aggregateBySession } = await import("./dist-LHINSVK4.js");
|
|
13568
13913
|
const sessionData = aggregateBySession(records);
|
|
13569
13914
|
const match = sessionData.find((s) => s.sessionId === id);
|
|
13570
13915
|
if (!match) {
|
|
@@ -13599,7 +13944,7 @@ function registerLatestCommand(usage) {
|
|
|
13599
13944
|
}
|
|
13600
13945
|
return;
|
|
13601
13946
|
}
|
|
13602
|
-
const { aggregateBySession } = await import("./dist-
|
|
13947
|
+
const { aggregateBySession } = await import("./dist-LHINSVK4.js");
|
|
13603
13948
|
const sessionData = aggregateBySession(records);
|
|
13604
13949
|
const latest = sessionData[0];
|
|
13605
13950
|
if (!latest) {
|
|
@@ -13625,7 +13970,7 @@ function registerLatestCommand(usage) {
|
|
|
13625
13970
|
});
|
|
13626
13971
|
}
|
|
13627
13972
|
function createUsageCommand() {
|
|
13628
|
-
const usage = new
|
|
13973
|
+
const usage = new Command119("usage").description("Token usage and cost tracking");
|
|
13629
13974
|
usage.option(
|
|
13630
13975
|
"--include-claude-sessions",
|
|
13631
13976
|
"Include Claude Code session data from ~/.claude/projects/"
|
|
@@ -13640,15 +13985,16 @@ function createUsageCommand() {
|
|
|
13640
13985
|
// src/commands/validate.ts
|
|
13641
13986
|
init_dist3();
|
|
13642
13987
|
init_dist3();
|
|
13643
|
-
import { Command as
|
|
13644
|
-
import * as
|
|
13988
|
+
import { Command as Command120 } from "commander";
|
|
13989
|
+
import * as fs48 from "fs";
|
|
13990
|
+
import * as path72 from "path";
|
|
13645
13991
|
async function runValidate(options) {
|
|
13646
13992
|
const configResult = resolveConfig(options.configPath);
|
|
13647
13993
|
if (!configResult.ok) {
|
|
13648
13994
|
return configResult;
|
|
13649
13995
|
}
|
|
13650
13996
|
const config = configResult.value;
|
|
13651
|
-
const cwd = options.cwd ?? (options.configPath ?
|
|
13997
|
+
const cwd = options.cwd ?? (options.configPath ? path72.dirname(path72.resolve(options.configPath)) : process.cwd());
|
|
13652
13998
|
const result = {
|
|
13653
13999
|
valid: true,
|
|
13654
14000
|
checks: {
|
|
@@ -13658,7 +14004,7 @@ async function runValidate(options) {
|
|
|
13658
14004
|
},
|
|
13659
14005
|
issues: []
|
|
13660
14006
|
};
|
|
13661
|
-
const agentsMapPath =
|
|
14007
|
+
const agentsMapPath = path72.resolve(cwd, config.agentsMapPath);
|
|
13662
14008
|
const agentsResult = await validateAgentsMap(agentsMapPath);
|
|
13663
14009
|
if (agentsResult.ok) {
|
|
13664
14010
|
result.checks.agentsMap = true;
|
|
@@ -13755,6 +14101,25 @@ async function runValidate(options) {
|
|
|
13755
14101
|
}
|
|
13756
14102
|
});
|
|
13757
14103
|
}
|
|
14104
|
+
const roadmapPath = path72.join(cwd, "docs", "roadmap.md");
|
|
14105
|
+
if (fs48.existsSync(roadmapPath)) {
|
|
14106
|
+
const parsed = parseRoadmap(fs48.readFileSync(roadmapPath, "utf-8"));
|
|
14107
|
+
if (parsed.ok) {
|
|
14108
|
+
const findings = checkRoadmapHealth(parsed.value);
|
|
14109
|
+
result.checks.roadmapHealth = !findings.some((f) => f.severity === "error");
|
|
14110
|
+
if (!result.checks.roadmapHealth) result.valid = false;
|
|
14111
|
+
for (const finding of findings) {
|
|
14112
|
+
result.issues.push({
|
|
14113
|
+
check: "roadmapHealth",
|
|
14114
|
+
file: "docs/roadmap.md",
|
|
14115
|
+
ruleId: finding.ruleId,
|
|
14116
|
+
severity: finding.severity === "error" ? "error" : "warning",
|
|
14117
|
+
message: finding.feature ? `${finding.feature} (${finding.milestone}): ${finding.message}` : finding.message,
|
|
14118
|
+
...finding.suggestion !== void 0 && { suggestion: finding.suggestion }
|
|
14119
|
+
});
|
|
14120
|
+
}
|
|
14121
|
+
}
|
|
14122
|
+
}
|
|
13758
14123
|
const anatomyEnabled = config.design?.audit?.componentAnatomy?.enabled !== false;
|
|
13759
14124
|
if (anatomyEnabled) {
|
|
13760
14125
|
try {
|
|
@@ -13880,11 +14245,11 @@ function resolveValidateMode(globalOpts) {
|
|
|
13880
14245
|
return OutputMode.TEXT;
|
|
13881
14246
|
}
|
|
13882
14247
|
async function printCrossCheckWarnings(mode) {
|
|
13883
|
-
const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-
|
|
14248
|
+
const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-QPSAOZKD.js");
|
|
13884
14249
|
const cwd = process.cwd();
|
|
13885
14250
|
const crossResult = await runCrossCheck2({
|
|
13886
|
-
specsDir:
|
|
13887
|
-
plansDir:
|
|
14251
|
+
specsDir: path72.join(cwd, "docs", "specs"),
|
|
14252
|
+
plansDir: path72.join(cwd, "docs", "plans"),
|
|
13888
14253
|
projectPath: cwd
|
|
13889
14254
|
});
|
|
13890
14255
|
if (!crossResult.ok || crossResult.value.warnings === 0) return;
|
|
@@ -13896,7 +14261,7 @@ async function printCrossCheckWarnings(mode) {
|
|
|
13896
14261
|
${crossResult.value.warnings} warnings`);
|
|
13897
14262
|
}
|
|
13898
14263
|
function createValidateCommand3() {
|
|
13899
|
-
const command = new
|
|
14264
|
+
const command = new Command120("validate").description("Run all validation checks").option("--cross-check", "Run cross-artifact consistency validation").option(
|
|
13900
14265
|
"--agent-configs",
|
|
13901
14266
|
"Validate agent configs (CLAUDE.md, hooks, skills) via agnix or built-in fallback rules"
|
|
13902
14267
|
).option("--strict", "Treat warnings as errors (applies to --agent-configs)").option("--agnix-bin <path>", "Override the agnix binary path discovered on PATH").action(async (opts, cmd) => runValidateAction(opts, cmd.optsWithGlobals()));
|
|
@@ -13945,7 +14310,7 @@ Agent configs checked via ${engineLabel}${note}`);
|
|
|
13945
14310
|
|
|
13946
14311
|
// src/commands/verify.ts
|
|
13947
14312
|
init_dist3();
|
|
13948
|
-
import { Command as
|
|
14313
|
+
import { Command as Command121 } from "commander";
|
|
13949
14314
|
import { execSync as execSync6 } from "child_process";
|
|
13950
14315
|
function loadBranchingConfig(configPath) {
|
|
13951
14316
|
const shouldLoad = configPath !== void 0 || findConfigFile().ok;
|
|
@@ -14008,7 +14373,7 @@ async function runVerify(options) {
|
|
|
14008
14373
|
process.exit(ExitCode.VALIDATION_FAILED);
|
|
14009
14374
|
}
|
|
14010
14375
|
function createVerifyCommand() {
|
|
14011
|
-
return new
|
|
14376
|
+
return new Command121("verify").description(
|
|
14012
14377
|
"Verify project conventions (currently: branch naming). Works with or without a harness.config.json."
|
|
14013
14378
|
).option(
|
|
14014
14379
|
"--branch <name>",
|
|
@@ -14037,6 +14402,7 @@ var commandCreators = [
|
|
|
14037
14402
|
createCheckDepsCommand,
|
|
14038
14403
|
createCheckDesignCommand,
|
|
14039
14404
|
createCheckDocsCommand,
|
|
14405
|
+
createCheckHarnessStrengthCommand,
|
|
14040
14406
|
createCheckPerfCommand,
|
|
14041
14407
|
createCheckPhaseGateCommand,
|
|
14042
14408
|
createCheckSecurityCommand,
|
|
@@ -14083,6 +14449,7 @@ var commandCreators = [
|
|
|
14083
14449
|
createPulseCommand,
|
|
14084
14450
|
createQueryCommand,
|
|
14085
14451
|
createRecommendCommand,
|
|
14452
|
+
createReviewCiCommand,
|
|
14086
14453
|
createRoadmapCommand,
|
|
14087
14454
|
createRoutingCommand,
|
|
14088
14455
|
createScanCommand,
|
|
@@ -14112,7 +14479,7 @@ var commandCreators = [
|
|
|
14112
14479
|
|
|
14113
14480
|
// src/index.ts
|
|
14114
14481
|
function createProgram() {
|
|
14115
|
-
const program = new
|
|
14482
|
+
const program = new Command122();
|
|
14116
14483
|
program.name("harness").description("CLI for Harness Engineering toolkit").version(CLI_VERSION).option("-c, --config <path>", "Path to config file").option("--json", "Output as JSON").option("--verbose", "Verbose output").option("--quiet", "Minimal output");
|
|
14117
14484
|
for (const creator of commandCreators) {
|
|
14118
14485
|
program.addCommand(creator());
|