@harness-engineering/cli 4.0.1 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/skills/claude-code/harness-autopilot/SKILL.md +9 -8
- package/dist/agents/skills/claude-code/pre-merge-brief/SKILL.md +86 -0
- package/dist/agents/skills/claude-code/pre-merge-brief/skill.yaml +67 -0
- package/dist/agents/skills/codex/harness-autopilot/SKILL.md +9 -8
- package/dist/agents/skills/codex/pre-merge-brief/SKILL.md +86 -0
- package/dist/agents/skills/codex/pre-merge-brief/skill.yaml +67 -0
- package/dist/agents/skills/cursor/harness-autopilot/SKILL.md +9 -8
- package/dist/agents/skills/cursor/pre-merge-brief/SKILL.md +86 -0
- package/dist/agents/skills/cursor/pre-merge-brief/skill.yaml +67 -0
- package/dist/agents/skills/gemini-cli/harness-autopilot/SKILL.md +9 -8
- package/dist/agents/skills/gemini-cli/pre-merge-brief/SKILL.md +86 -0
- package/dist/agents/skills/gemini-cli/pre-merge-brief/skill.yaml +67 -0
- package/dist/{agents-md-VJLYEH2S.js → agents-md-7L4PKKOM.js} +3 -3
- package/dist/analyzer-XRCV63KC-HLTEIKAJ.js +26 -0
- package/dist/{architecture-YQH2NYXY.js → architecture-RCHFDI7E.js} +4 -4
- package/dist/{assess-project-KOTAPAHD.js → assess-project-YUDPNRS6.js} +5 -3
- package/dist/bin/harness-mcp.js +14 -14
- package/dist/bin/harness.js +24 -24
- package/dist/{check-phase-gate-CHU2E3OU.js → check-phase-gate-56GS3A72.js} +4 -4
- package/dist/{chunk-JPVOI7GP.js → chunk-4SKWGVYD.js} +2 -2
- package/dist/{chunk-GHBFNLV2.js → chunk-ABRYIEWJ.js} +923 -614
- package/dist/{chunk-36GQREOE.js → chunk-AU4YPEIE.js} +3 -3
- package/dist/{chunk-OQ6KOQEV.js → chunk-DGVT4DBI.js} +3 -3
- package/dist/{chunk-I3CGCZJF.js → chunk-GLHLXTKC.js} +5 -5
- package/dist/{chunk-4VXFZSYF.js → chunk-GSYBGOY3.js} +1 -1
- package/dist/{chunk-UWVMJEUJ.js → chunk-IGWJRAJT.js} +1 -1
- package/dist/{chunk-YANR2RL7.js → chunk-N4AI2WFH.js} +1 -1
- package/dist/{chunk-5RSXUAMN.js → chunk-NCMJXBEO.js} +1 -1
- package/dist/{chunk-SEA2PFVI.js → chunk-OZSKLJQW.js} +1 -1
- package/dist/{chunk-AX5KGRM6.js → chunk-P4NA5OQX.js} +1 -1
- package/dist/{chunk-7PJCK7UE.js → chunk-RY2U77OY.js} +6 -6
- package/dist/{chunk-6SCYTIF4.js → chunk-RZQZXWEL.js} +32 -7
- package/dist/{chunk-MMLQPHZ5.js → chunk-UACYZCW4.js} +31 -13
- package/dist/{chunk-T746REAE.js → chunk-VG4G3R2V.js} +1 -1
- package/dist/{chunk-7TIJXWG3.js → chunk-XW5XVVMI.js} +2 -2
- package/dist/{chunk-E5EW5HVJ.js → chunk-XZNCONFG.js} +1 -1
- package/dist/{chunk-SDMV4QHM.js → chunk-Y33P35U3.js} +24 -6
- package/dist/{chunk-XAVMTAUT.js → chunk-Y5HPJMTM.js} +18 -2
- package/dist/{chunk-6YFHUIGT.js → chunk-YJYUJSWK.js} +58 -20
- package/dist/{chunk-AAUY53CC.js → chunk-YWRPQZL7.js} +1 -1
- package/dist/{chunk-M7F2PPN3.js → chunk-YXOPQEFZ.js} +2 -2
- package/dist/{chunk-RG3TNIUB.js → chunk-Z4XWWNYX.js} +86 -86
- package/dist/{chunk-EYMOPFSD.js → chunk-ZU6T7W3U.js} +2 -2
- package/dist/{ci-workflow-HO4WKGNG.js → ci-workflow-TFAA5BK7.js} +3 -3
- package/dist/{dist-6D2F2J57.js → dist-MCTEX5QH.js} +4 -2
- package/dist/{docs-FP47JONV.js → docs-BBMAN3CY.js} +4 -4
- package/dist/{engine-YUA6IYOG.js → engine-R2YH74AL.js} +3 -3
- package/dist/{entropy-DBW2INZU.js → entropy-5UP5N6AF.js} +6 -3
- package/dist/{feedback-BPHADTRQ.js → feedback-6MDCCFZL.js} +1 -1
- package/dist/{generate-agent-definitions-KPVGBGDY.js → generate-agent-definitions-KKAN5DB5.js} +4 -4
- package/dist/hooks/sentinel-pre.js +15 -138
- package/dist/index.d.ts +61 -0
- package/dist/index.js +24 -24
- package/dist/{loader-QOJQ6NZM.js → loader-2WT2ZE2T.js} +3 -3
- package/dist/{mcp-ZPX6O767.js → mcp-5V4LPMB3.js} +14 -14
- package/dist/{performance-FXYTGJGQ.js → performance-2N3CFXLR.js} +4 -4
- package/dist/{review-pipeline-2TFQ5OAE.js → review-pipeline-XZIGBU3Q.js} +3 -3
- package/dist/{runtime-XK6ZCHKE.js → runtime-POME3GTR.js} +3 -3
- package/dist/{security-4W2P5ZGI.js → security-QLJLMSIW.js} +1 -1
- package/dist/{state-events-ECQAIZLU.js → state-events-YY5KBZTC.js} +3 -3
- package/dist/{validate-64KTEGIE.js → validate-VZORVK43.js} +4 -4
- package/dist/{validate-cross-check-J5PVGICB.js → validate-cross-check-OYKEDCST.js} +3 -3
- package/package.json +5 -4
- package/dist/analyzer-V633GUHY-OV335HGM.js +0 -26
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import {
|
|
2
2
|
generateRuntime
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-N4AI2WFH.js";
|
|
4
4
|
import {
|
|
5
5
|
generateAgentsMd
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-VG4G3R2V.js";
|
|
7
7
|
import {
|
|
8
8
|
generateCIWorkflow
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-P4NA5OQX.js";
|
|
10
10
|
import {
|
|
11
11
|
readHarnessState
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-YWRPQZL7.js";
|
|
13
13
|
import {
|
|
14
14
|
runCrossCheck
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-GSYBGOY3.js";
|
|
16
16
|
import {
|
|
17
17
|
createGenerateAgentDefinitionsCommand,
|
|
18
18
|
generateAgentDefinitions
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-XZNCONFG.js";
|
|
20
20
|
import {
|
|
21
21
|
toKebabCase
|
|
22
22
|
} from "./chunk-KET4QQZB.js";
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
} from "./chunk-YYFSFSB3.js";
|
|
27
27
|
import {
|
|
28
28
|
TemplateEngine
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-IGWJRAJT.js";
|
|
30
30
|
import {
|
|
31
31
|
generate,
|
|
32
32
|
validate
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
import {
|
|
38
38
|
listPersonas,
|
|
39
39
|
loadPersona
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-NCMJXBEO.js";
|
|
41
41
|
import {
|
|
42
42
|
runPersona
|
|
43
43
|
} from "./chunk-GISMXMVL.js";
|
|
@@ -51,7 +51,7 @@ import {
|
|
|
51
51
|
OutputFormatter,
|
|
52
52
|
OutputMode,
|
|
53
53
|
createCheckPhaseGateCommand
|
|
54
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-XW5XVVMI.js";
|
|
55
55
|
import {
|
|
56
56
|
appendFrameworkAgents,
|
|
57
57
|
captureHealthSnapshot,
|
|
@@ -79,7 +79,7 @@ import {
|
|
|
79
79
|
runSecurityCraft,
|
|
80
80
|
runSpecCraft,
|
|
81
81
|
runTestCraft
|
|
82
|
-
} from "./chunk-
|
|
82
|
+
} from "./chunk-Z4XWWNYX.js";
|
|
83
83
|
import {
|
|
84
84
|
VALID_PLATFORMS
|
|
85
85
|
} from "./chunk-3ISINLYT.js";
|
|
@@ -108,7 +108,7 @@ import {
|
|
|
108
108
|
BranchingConfigSchema,
|
|
109
109
|
findConfigFile,
|
|
110
110
|
resolveConfig
|
|
111
|
-
} from "./chunk-
|
|
111
|
+
} from "./chunk-Y5HPJMTM.js";
|
|
112
112
|
import {
|
|
113
113
|
logger
|
|
114
114
|
} from "./chunk-EBJQ6N4M.js";
|
|
@@ -188,6 +188,7 @@ import {
|
|
|
188
188
|
parseDiff,
|
|
189
189
|
parseFileRegions,
|
|
190
190
|
parseManifest,
|
|
191
|
+
parseReferencedIssues,
|
|
191
192
|
parseRoadmap,
|
|
192
193
|
parseSecurityConfig,
|
|
193
194
|
pruneLearnings,
|
|
@@ -224,7 +225,7 @@ import {
|
|
|
224
225
|
writeConfig,
|
|
225
226
|
writeLockfile,
|
|
226
227
|
writeRegeneratedRoadmap
|
|
227
|
-
} from "./chunk-
|
|
228
|
+
} from "./chunk-UACYZCW4.js";
|
|
228
229
|
import {
|
|
229
230
|
CLI_VERSION
|
|
230
231
|
} from "./chunk-BM3PWGXQ.js";
|
|
@@ -237,7 +238,7 @@ import {
|
|
|
237
238
|
detectDocDrift,
|
|
238
239
|
generateSuggestions,
|
|
239
240
|
validateDependencies
|
|
240
|
-
} from "./chunk-
|
|
241
|
+
} from "./chunk-YJYUJSWK.js";
|
|
241
242
|
import {
|
|
242
243
|
Err,
|
|
243
244
|
INDEXED_FILE_KINDS,
|
|
@@ -261,7 +262,7 @@ import {
|
|
|
261
262
|
} from "./chunk-KFQGP6VL.js";
|
|
262
263
|
|
|
263
264
|
// src/index.ts
|
|
264
|
-
import { Command as
|
|
265
|
+
import { Command as Command130 } from "commander";
|
|
265
266
|
|
|
266
267
|
// src/commands/add.ts
|
|
267
268
|
init_dist3();
|
|
@@ -450,7 +451,7 @@ function registerSkillsCommand(adoption) {
|
|
|
450
451
|
const globalOpts = cmd.optsWithGlobals();
|
|
451
452
|
const limit = Math.max(parseInt(opts.limit, 10) || 20, 1);
|
|
452
453
|
const cwd = process.cwd();
|
|
453
|
-
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-
|
|
454
|
+
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-MCTEX5QH.js");
|
|
454
455
|
const records = readAdoptionRecords(cwd);
|
|
455
456
|
if (records.length === 0) {
|
|
456
457
|
if (globalOpts.json) {
|
|
@@ -486,7 +487,7 @@ function registerRecentCommand(adoption) {
|
|
|
486
487
|
const globalOpts = cmd.optsWithGlobals();
|
|
487
488
|
const limit = Math.max(parseInt(opts.limit, 10) || 20, 1);
|
|
488
489
|
const cwd = process.cwd();
|
|
489
|
-
const { readAdoptionRecords } = await import("./dist-
|
|
490
|
+
const { readAdoptionRecords } = await import("./dist-MCTEX5QH.js");
|
|
490
491
|
const records = readAdoptionRecords(cwd);
|
|
491
492
|
if (records.length === 0) {
|
|
492
493
|
if (globalOpts.json) {
|
|
@@ -564,7 +565,7 @@ function registerSkillCommand(adoption) {
|
|
|
564
565
|
adoption.command("skill <name>").description("Show detail for a specific skill").action(async (name, _opts, cmd) => {
|
|
565
566
|
const globalOpts = cmd.optsWithGlobals();
|
|
566
567
|
const cwd = process.cwd();
|
|
567
|
-
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-
|
|
568
|
+
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-MCTEX5QH.js");
|
|
568
569
|
const records = readAdoptionRecords(cwd);
|
|
569
570
|
const skillRecords = records.filter((r) => r.skill === name);
|
|
570
571
|
if (skillRecords.length === 0) {
|
|
@@ -2696,12 +2697,14 @@ async function runCleanup(options) {
|
|
|
2696
2697
|
};
|
|
2697
2698
|
const rootDir = path12.resolve(cwd, config.rootDir);
|
|
2698
2699
|
const docsDir = path12.resolve(cwd, config.docsDir);
|
|
2700
|
+
const driftEnabled = type === "all" || type === "drift";
|
|
2701
|
+
const driftConfig = config.entropy?.drift;
|
|
2699
2702
|
const entropyConfig = {
|
|
2700
2703
|
rootDir,
|
|
2701
2704
|
...config.entropy?.entryPoints && { entryPoints: config.entropy.entryPoints },
|
|
2702
2705
|
docPaths: [path12.join(docsDir, "**/*.md")],
|
|
2703
2706
|
analyze: {
|
|
2704
|
-
drift:
|
|
2707
|
+
drift: driftEnabled ? driftConfig ?? true : false,
|
|
2705
2708
|
deadCode: type === "all" || type === "dead-code",
|
|
2706
2709
|
patterns: type === "all" || type === "patterns" ? { patterns: [] } : false
|
|
2707
2710
|
},
|
|
@@ -4515,12 +4518,12 @@ async function runGraphStatus(projectPath) {
|
|
|
4515
4518
|
const graphDir = path18.join(projectPath, ".harness", "graph");
|
|
4516
4519
|
const metaResult = await loadGraphMetadata(graphDir);
|
|
4517
4520
|
if (metaResult.status === "not_found") {
|
|
4518
|
-
return { status: "no_graph", message: "No graph found. Run `harness scan` first." };
|
|
4521
|
+
return { status: "no_graph", message: "No graph found. Run `harness graph scan` first." };
|
|
4519
4522
|
}
|
|
4520
4523
|
if (metaResult.status === "schema_mismatch") {
|
|
4521
4524
|
return {
|
|
4522
4525
|
status: "schema_mismatch",
|
|
4523
|
-
message: `Graph schema version mismatch: file is v${metaResult.found}, CLI expects v${metaResult.expected}. Run \`harness scan\` to rebuild.`
|
|
4526
|
+
message: `Graph schema version mismatch: file is v${metaResult.found}, CLI expects v${metaResult.expected}. Run \`harness graph scan\` to rebuild.`
|
|
4524
4527
|
};
|
|
4525
4528
|
}
|
|
4526
4529
|
const meta = metaResult.metadata;
|
|
@@ -4567,7 +4570,7 @@ async function runGraphExport(projectPath, format) {
|
|
|
4567
4570
|
const graphDir = path19.join(projectPath, ".harness", "graph");
|
|
4568
4571
|
const store = new GraphStore2();
|
|
4569
4572
|
const loaded = await store.load(graphDir);
|
|
4570
|
-
if (!loaded) throw new Error("No graph found. Run `harness scan` first.");
|
|
4573
|
+
if (!loaded) throw new Error("No graph found. Run `harness graph scan` first.");
|
|
4571
4574
|
if (format === "json") {
|
|
4572
4575
|
const nodes = store.findNodes({});
|
|
4573
4576
|
const edges = store.getEdges({});
|
|
@@ -4600,7 +4603,7 @@ async function runQuery(projectPath, rootNodeId, opts) {
|
|
|
4600
4603
|
const store = new GraphStore2();
|
|
4601
4604
|
const graphDir = path20.join(projectPath, ".harness", "graph");
|
|
4602
4605
|
const loaded = await store.load(graphDir);
|
|
4603
|
-
if (!loaded) throw new Error("No graph found. Run `harness scan` first.");
|
|
4606
|
+
if (!loaded) throw new Error("No graph found. Run `harness graph scan` first.");
|
|
4604
4607
|
const params = {
|
|
4605
4608
|
rootNodeIds: [rootNodeId],
|
|
4606
4609
|
maxDepth: opts.depth ?? 3,
|
|
@@ -5479,7 +5482,7 @@ async function runImpactPreview(options) {
|
|
|
5479
5482
|
const stagedFiles = getStagedFiles(projectPath);
|
|
5480
5483
|
if (stagedFiles.length === 0) return "Impact Preview: no staged changes";
|
|
5481
5484
|
if (!graphExists(projectPath)) {
|
|
5482
|
-
return "Impact Preview: skipped (no graph \u2014 run `harness scan` to enable)";
|
|
5485
|
+
return "Impact Preview: skipped (no graph \u2014 run `harness graph scan` to enable)";
|
|
5483
5486
|
}
|
|
5484
5487
|
const mode = options.detailed ? "detailed" : "summary";
|
|
5485
5488
|
const acc = {
|
|
@@ -6159,7 +6162,7 @@ function createInsightsCommand() {
|
|
|
6159
6162
|
return new Command46("insights").description("Composite project report \u2014 health, entropy, decay, attention, impact.").option("--json", "Emit JSON to stdout instead of pretty text").option("--skip <list>", `Comma-separated keys to skip (${INSIGHTS_KEYS.join(",")})`).action(async (opts) => {
|
|
6160
6163
|
const cwd = process.cwd();
|
|
6161
6164
|
const skip = parseSkip2(opts.skip);
|
|
6162
|
-
const { composeInsights } = await import("./dist-
|
|
6165
|
+
const { composeInsights } = await import("./dist-MCTEX5QH.js");
|
|
6163
6166
|
const report = await composeInsights(cwd, { skip });
|
|
6164
6167
|
if (opts.json) {
|
|
6165
6168
|
console.log(JSON.stringify(report, null, 2));
|
|
@@ -8270,7 +8273,7 @@ function createMcpCommand() {
|
|
|
8270
8273
|
parseBudget
|
|
8271
8274
|
).action(async (opts) => {
|
|
8272
8275
|
const [{ startServer: startServer2, getToolDefinitions: getToolDefinitions2 }, { selectTier }] = await Promise.all([
|
|
8273
|
-
import("./mcp-
|
|
8276
|
+
import("./mcp-5V4LPMB3.js"),
|
|
8274
8277
|
import("./tool-tiers-OKK5FE57.js")
|
|
8275
8278
|
]);
|
|
8276
8279
|
if (opts.tools && opts.tools.length > 0) {
|
|
@@ -9345,7 +9348,7 @@ function registerBenchCommand(perf) {
|
|
|
9345
9348
|
perf.command("bench [glob]").description("Run benchmarks via vitest bench").action(async (glob2, _opts, cmd) => {
|
|
9346
9349
|
const globalOpts = cmd.optsWithGlobals();
|
|
9347
9350
|
const cwd = process.cwd();
|
|
9348
|
-
const { BenchmarkRunner } = await import("./dist-
|
|
9351
|
+
const { BenchmarkRunner } = await import("./dist-MCTEX5QH.js");
|
|
9349
9352
|
const runner = new BenchmarkRunner();
|
|
9350
9353
|
const benchFiles = runner.discover(cwd, glob2);
|
|
9351
9354
|
if (benchFiles.length === 0) {
|
|
@@ -9409,7 +9412,7 @@ async function runBaselinesShow(globalOpts) {
|
|
|
9409
9412
|
}
|
|
9410
9413
|
async function runBaselinesUpdate(globalOpts) {
|
|
9411
9414
|
const cwd = process.cwd();
|
|
9412
|
-
const { BenchmarkRunner } = await import("./dist-
|
|
9415
|
+
const { BenchmarkRunner } = await import("./dist-MCTEX5QH.js");
|
|
9413
9416
|
const runner = new BenchmarkRunner();
|
|
9414
9417
|
const manager = new BaselineManager(cwd);
|
|
9415
9418
|
logger.info("Running benchmarks to update baselines...");
|
|
@@ -9450,7 +9453,7 @@ function registerReportCommand(perf) {
|
|
|
9450
9453
|
perf.command("report").description("Full performance report with metrics, trends, and hotspots").action(async (_opts, cmd) => {
|
|
9451
9454
|
const globalOpts = cmd.optsWithGlobals();
|
|
9452
9455
|
const cwd = process.cwd();
|
|
9453
|
-
const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-
|
|
9456
|
+
const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-MCTEX5QH.js");
|
|
9454
9457
|
const analyzer = new EntropyAnalyzer2({
|
|
9455
9458
|
rootDir: path50.resolve(cwd),
|
|
9456
9459
|
analyze: { complexity: true, coupling: true }
|
|
@@ -9753,165 +9756,646 @@ function createPredictCommand() {
|
|
|
9753
9756
|
return command;
|
|
9754
9757
|
}
|
|
9755
9758
|
|
|
9756
|
-
// src/commands/
|
|
9759
|
+
// src/commands/pre-merge-brief.ts
|
|
9760
|
+
init_dist2();
|
|
9761
|
+
import { execFileSync as execFileSync7 } from "child_process";
|
|
9762
|
+
import { readFileSync as readFileSync18 } from "fs";
|
|
9763
|
+
import { join as join40 } from "path";
|
|
9764
|
+
import { Command as Command77 } from "commander";
|
|
9765
|
+
import { gatherSignals } from "@harness-engineering/signals";
|
|
9766
|
+
|
|
9767
|
+
// src/commands/review-ci.ts
|
|
9757
9768
|
init_dist3();
|
|
9758
|
-
import {
|
|
9759
|
-
import {
|
|
9760
|
-
|
|
9761
|
-
|
|
9762
|
-
|
|
9763
|
-
|
|
9764
|
-
|
|
9765
|
-
|
|
9766
|
-
|
|
9767
|
-
|
|
9768
|
-
|
|
9769
|
-
|
|
9770
|
-
|
|
9771
|
-
|
|
9772
|
-
|
|
9773
|
-
|
|
9774
|
-
|
|
9775
|
-
}
|
|
9776
|
-
|
|
9777
|
-
|
|
9778
|
-
|
|
9779
|
-
|
|
9780
|
-
|
|
9781
|
-
|
|
9782
|
-
|
|
9783
|
-
|
|
9769
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
9770
|
+
import { writeFileSync as writeFileSync18 } from "fs";
|
|
9771
|
+
import { Command as Command76, Option as Option2 } from "commander";
|
|
9772
|
+
|
|
9773
|
+
// src/commands/review-ci-local-adapter.ts
|
|
9774
|
+
import { z } from "zod";
|
|
9775
|
+
import { OpenAICompatibleAnalysisProvider } from "@harness-engineering/intelligence";
|
|
9776
|
+
var FindingSchema = z.object({
|
|
9777
|
+
id: z.string(),
|
|
9778
|
+
file: z.string(),
|
|
9779
|
+
lineRange: z.tuple([z.number(), z.number()]),
|
|
9780
|
+
domain: z.enum(["compliance", "bug", "security", "architecture", "learnings"]),
|
|
9781
|
+
severity: z.enum(["critical", "important", "suggestion"]),
|
|
9782
|
+
title: z.string(),
|
|
9783
|
+
rationale: z.string(),
|
|
9784
|
+
suggestion: z.string().optional(),
|
|
9785
|
+
evidence: z.array(z.string())
|
|
9786
|
+
});
|
|
9787
|
+
var LocalVerdictSchema = z.object({
|
|
9788
|
+
assessment: z.enum(["approve", "comment", "request-changes"]),
|
|
9789
|
+
findings: z.array(FindingSchema)
|
|
9790
|
+
});
|
|
9791
|
+
var MAX_SURFACED_ERROR_LEN = 200;
|
|
9792
|
+
function sanitizeProviderError(err) {
|
|
9793
|
+
const raw = err instanceof Error ? err.message : String(err);
|
|
9794
|
+
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();
|
|
9795
|
+
const capped = redacted.length > MAX_SURFACED_ERROR_LEN ? redacted.slice(0, MAX_SURFACED_ERROR_LEN - 1) + "\u2026" : redacted;
|
|
9796
|
+
return `local runner provider error: ${capped}`;
|
|
9784
9797
|
}
|
|
9785
|
-
|
|
9786
|
-
|
|
9787
|
-
|
|
9788
|
-
|
|
9789
|
-
|
|
9790
|
-
|
|
9798
|
+
function createLocalInvoke() {
|
|
9799
|
+
return async ({ endpoint, model, instruction, diff: diff2 }) => {
|
|
9800
|
+
if (!/^https?:\/\//i.test(endpoint)) {
|
|
9801
|
+
throw new Error(
|
|
9802
|
+
`HARNESS_LOCAL_ENDPOINT must be an http(s) URL (got '${endpoint}'); e.g. http://localhost:1234/v1`
|
|
9803
|
+
);
|
|
9804
|
+
}
|
|
9805
|
+
const provider = new OpenAICompatibleAnalysisProvider({
|
|
9806
|
+
apiKey: process.env.HARNESS_LOCAL_API_KEY ?? "local",
|
|
9807
|
+
baseUrl: endpoint,
|
|
9808
|
+
defaultModel: model
|
|
9809
|
+
});
|
|
9810
|
+
try {
|
|
9811
|
+
const { result } = await provider.analyze({
|
|
9812
|
+
prompt: `${instruction}
|
|
9813
|
+
|
|
9814
|
+
---
|
|
9815
|
+
DIFF UNDER REVIEW:
|
|
9816
|
+
${diff2}`,
|
|
9817
|
+
responseSchema: LocalVerdictSchema,
|
|
9818
|
+
model
|
|
9819
|
+
});
|
|
9820
|
+
return JSON.stringify(result);
|
|
9821
|
+
} catch (err) {
|
|
9822
|
+
throw new Error(sanitizeProviderError(err), { cause: err });
|
|
9823
|
+
}
|
|
9791
9824
|
};
|
|
9792
|
-
return updateProposal(projectRoot(), id, { status: "rejected", decision });
|
|
9793
|
-
}
|
|
9794
|
-
var ALLOWED_STATUSES = [
|
|
9795
|
-
"open",
|
|
9796
|
-
"gate-running",
|
|
9797
|
-
"gate-failed",
|
|
9798
|
-
"approved",
|
|
9799
|
-
"rejected",
|
|
9800
|
-
"all"
|
|
9801
|
-
];
|
|
9802
|
-
function fail(message) {
|
|
9803
|
-
console.error(message);
|
|
9804
|
-
process.exitCode = 1;
|
|
9805
|
-
}
|
|
9806
|
-
async function actListCommand(opts) {
|
|
9807
|
-
const raw = opts.status ?? "open";
|
|
9808
|
-
if (!ALLOWED_STATUSES.includes(raw)) {
|
|
9809
|
-
fail(`Error: unknown status "${raw}"`);
|
|
9810
|
-
return;
|
|
9811
|
-
}
|
|
9812
|
-
const proposals = await runProposalsList(raw);
|
|
9813
|
-
console.log(JSON.stringify(proposals, null, 2));
|
|
9814
9825
|
}
|
|
9815
|
-
|
|
9816
|
-
|
|
9817
|
-
|
|
9818
|
-
|
|
9819
|
-
|
|
9826
|
+
|
|
9827
|
+
// src/commands/review-ci.ts
|
|
9828
|
+
var KNOWN_RUNNERS = Object.keys(RUNNER_PRESETS);
|
|
9829
|
+
var BLOCK_ON_LEVELS = [...CI_ASSESSMENTS, "none"];
|
|
9830
|
+
function assertKnownRunner(runner) {
|
|
9831
|
+
if (runner === void 0) return;
|
|
9832
|
+
if (!KNOWN_RUNNERS.includes(runner)) {
|
|
9833
|
+
throw new Error(`unknown runner '${runner}' (expected: ${KNOWN_RUNNERS.join("|")})`);
|
|
9820
9834
|
}
|
|
9821
|
-
console.log(JSON.stringify(proposal, null, 2));
|
|
9822
9835
|
}
|
|
9823
|
-
|
|
9836
|
+
var defaultRunGit = (args) => execFileSync6("git", args, { encoding: "utf-8" }).toString().trim();
|
|
9837
|
+
function resolveDiffRange(opts) {
|
|
9838
|
+
if (opts.range) return opts.range;
|
|
9839
|
+
const runGit = opts.runGit ?? defaultRunGit;
|
|
9840
|
+
let base = "main";
|
|
9824
9841
|
try {
|
|
9825
|
-
const
|
|
9826
|
-
|
|
9827
|
-
|
|
9828
|
-
|
|
9842
|
+
const ref = runGit(["symbolic-ref", "refs/remotes/origin/HEAD"]);
|
|
9843
|
+
const m = ref.match(/origin\/(.+)$/);
|
|
9844
|
+
if (m?.[1]) base = m[1];
|
|
9845
|
+
} catch {
|
|
9829
9846
|
}
|
|
9847
|
+
return `origin/${base}...HEAD`;
|
|
9830
9848
|
}
|
|
9831
|
-
|
|
9832
|
-
const
|
|
9833
|
-
const
|
|
9834
|
-
|
|
9835
|
-
|
|
9836
|
-
|
|
9837
|
-
|
|
9838
|
-
|
|
9839
|
-
|
|
9840
|
-
|
|
9841
|
-
|
|
9842
|
-
|
|
9843
|
-
|
|
9844
|
-
|
|
9845
|
-
|
|
9849
|
+
function splitDiffByFile(rawDiff) {
|
|
9850
|
+
const sections = /* @__PURE__ */ new Map();
|
|
9851
|
+
const headerRe = /^diff --git a\/(.+?) b\/(.+?)$/;
|
|
9852
|
+
let currentPath = null;
|
|
9853
|
+
let buffer = [];
|
|
9854
|
+
const flush = () => {
|
|
9855
|
+
if (currentPath !== null) sections.set(currentPath, buffer.join("\n"));
|
|
9856
|
+
};
|
|
9857
|
+
for (const line of rawDiff.split("\n")) {
|
|
9858
|
+
const m = line.match(headerRe);
|
|
9859
|
+
if (m?.[2]) {
|
|
9860
|
+
flush();
|
|
9861
|
+
currentPath = m[2];
|
|
9862
|
+
buffer = [line];
|
|
9863
|
+
} else if (currentPath !== null) {
|
|
9864
|
+
buffer.push(line);
|
|
9846
9865
|
}
|
|
9847
|
-
console.log(await res.text());
|
|
9848
|
-
} catch (err) {
|
|
9849
|
-
fail(err instanceof Error ? err.message : String(err));
|
|
9850
9866
|
}
|
|
9867
|
+
flush();
|
|
9868
|
+
return sections;
|
|
9851
9869
|
}
|
|
9852
|
-
function
|
|
9853
|
-
const
|
|
9854
|
-
|
|
9855
|
-
|
|
9856
|
-
|
|
9857
|
-
|
|
9858
|
-
|
|
9859
|
-
|
|
9860
|
-
|
|
9861
|
-
|
|
9862
|
-
|
|
9863
|
-
|
|
9864
|
-
|
|
9870
|
+
function buildDiffInfo(rawDiff) {
|
|
9871
|
+
const parsed = parseDiff(rawDiff);
|
|
9872
|
+
if (!parsed.ok) throw new Error(`Failed to parse diff: ${parsed.error.message}`);
|
|
9873
|
+
const files = parsed.value.files;
|
|
9874
|
+
const perFile = splitDiffByFile(rawDiff);
|
|
9875
|
+
return {
|
|
9876
|
+
changedFiles: files.map((f) => f.path),
|
|
9877
|
+
newFiles: files.filter((f) => f.status === "added").map((f) => f.path),
|
|
9878
|
+
deletedFiles: files.filter((f) => f.status === "deleted").map((f) => f.path),
|
|
9879
|
+
totalDiffLines: rawDiff ? rawDiff.split("\n").length : 0,
|
|
9880
|
+
// On a path-key miss, fall back to '' (the file contributes no per-file diff)
|
|
9881
|
+
// rather than the ENTIRE raw diff — substituting the whole diff would duplicate
|
|
9882
|
+
// every file's content into one section, inflating the STDIN payload to the LLM
|
|
9883
|
+
// (and risking core's execMaxStdoutBytes). With splitDiffByFile aligned to core's
|
|
9884
|
+
// path extraction, ordinary paths never miss; this is the safe degenerate case.
|
|
9885
|
+
fileDiffs: new Map(files.map((f) => [f.path, perFile.get(f.path) ?? ""]))
|
|
9886
|
+
};
|
|
9865
9887
|
}
|
|
9866
|
-
|
|
9867
|
-
|
|
9868
|
-
import * as fs31 from "fs";
|
|
9869
|
-
import * as path52 from "path";
|
|
9870
|
-
import { Command as Command77 } from "commander";
|
|
9871
|
-
init_dist3();
|
|
9872
|
-
import {
|
|
9873
|
-
renderAnalysisComment,
|
|
9874
|
-
loadPublishedIndex,
|
|
9875
|
-
savePublishedIndex
|
|
9876
|
-
} from "@harness-engineering/orchestrator";
|
|
9877
|
-
function bootstrapTrackerCommand(opts, verb) {
|
|
9878
|
-
const projectPath = path52.resolve(opts.dir);
|
|
9879
|
-
const trackerConfig = loadTrackerSyncConfig(projectPath);
|
|
9880
|
-
if (!trackerConfig) {
|
|
9881
|
-
logger.error(`No tracker config found in harness.config.json. Cannot ${verb}.`);
|
|
9882
|
-
return null;
|
|
9883
|
-
}
|
|
9884
|
-
if (trackerConfig.kind !== "github") {
|
|
9885
|
-
logger.error(
|
|
9886
|
-
`${verb} currently only supports 'github' tracker kind. Found: ${String(trackerConfig.kind)}`
|
|
9887
|
-
);
|
|
9888
|
-
return null;
|
|
9889
|
-
}
|
|
9890
|
-
const projectEnvPath = path52.join(projectPath, ".env");
|
|
9891
|
-
if (fs31.existsSync(projectEnvPath) && !process.env.GITHUB_TOKEN) {
|
|
9892
|
-
__require("dotenv").config({ path: projectEnvPath });
|
|
9893
|
-
}
|
|
9894
|
-
const token = process.env.GITHUB_TOKEN;
|
|
9895
|
-
if (!token) {
|
|
9896
|
-
logger.error("No GITHUB_TOKEN environment variable found.");
|
|
9897
|
-
return null;
|
|
9898
|
-
}
|
|
9899
|
-
return { token, projectPath, trackerConfig };
|
|
9888
|
+
function defaultResolveRaw(range, _cwd, runGit) {
|
|
9889
|
+
return runGit(["diff", range]);
|
|
9900
9890
|
}
|
|
9901
|
-
|
|
9902
|
-
const
|
|
9903
|
-
|
|
9904
|
-
|
|
9905
|
-
|
|
9906
|
-
|
|
9907
|
-
|
|
9908
|
-
}
|
|
9909
|
-
|
|
9910
|
-
|
|
9911
|
-
|
|
9912
|
-
|
|
9913
|
-
|
|
9914
|
-
|
|
9891
|
+
function buildCallOpts(opts, cwd, diff2) {
|
|
9892
|
+
const runner = opts.runner;
|
|
9893
|
+
return {
|
|
9894
|
+
projectRoot: cwd,
|
|
9895
|
+
diff: diff2,
|
|
9896
|
+
...runner ? { runner } : {},
|
|
9897
|
+
...opts.blockOn ? { blockOn: opts.blockOn } : {},
|
|
9898
|
+
...runner === "local" ? { localInvoke: opts.localInvoke ?? createLocalInvoke() } : {}
|
|
9899
|
+
};
|
|
9900
|
+
}
|
|
9901
|
+
async function runReviewCi(opts) {
|
|
9902
|
+
assertKnownRunner(opts.runner);
|
|
9903
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
9904
|
+
const runGit = opts.runGit ?? defaultRunGit;
|
|
9905
|
+
const range = resolveDiffRange({
|
|
9906
|
+
...opts.diffRange ? { range: opts.diffRange } : {},
|
|
9907
|
+
cwd,
|
|
9908
|
+
runGit
|
|
9909
|
+
});
|
|
9910
|
+
const rawDiff = (opts.resolveRaw ?? defaultResolveRaw)(range, cwd, runGit);
|
|
9911
|
+
const diff2 = buildDiffInfo(rawDiff);
|
|
9912
|
+
const callOpts = buildCallOpts(opts, cwd, diff2);
|
|
9913
|
+
return (opts.runCiReviewImpl ?? runCiReview)(callOpts);
|
|
9914
|
+
}
|
|
9915
|
+
function findingLine(f) {
|
|
9916
|
+
const loc = f.lineRange ? `${f.file}:${f.lineRange[0]}` : f.file;
|
|
9917
|
+
return `- \`${f.severity}\` **${loc}** \u2014 ${f.title}`;
|
|
9918
|
+
}
|
|
9919
|
+
function buildReviewBody(verdict) {
|
|
9920
|
+
const findings = verdict.findings ?? [];
|
|
9921
|
+
const blocking = verdict.blockingFindings ?? [];
|
|
9922
|
+
const icon = verdict.assessment === "approve" ? "\u2705" : verdict.assessment === "request-changes" ? "\u{1F6D1}" : "\u{1F4AC}";
|
|
9923
|
+
const out = [
|
|
9924
|
+
`## ${icon} harness review-ci \u2014 ${verdict.assessment}`,
|
|
9925
|
+
"",
|
|
9926
|
+
`**Runner:** \`${verdict.runner}\` \u2022 **Findings:** ${findings.length} (blocking: ${blocking.length})`
|
|
9927
|
+
];
|
|
9928
|
+
if (verdict.skipped) out.push("", `> \u26A0\uFE0F ${verdict.skipReason ?? "A review tier was skipped."}`);
|
|
9929
|
+
if (blocking.length) out.push("", "### Blocking", ...blocking.map(findingLine));
|
|
9930
|
+
const nonBlocking = findings.filter((f) => !blocking.some((b) => b.id === f.id));
|
|
9931
|
+
if (nonBlocking.length) out.push("", "### Other findings", ...nonBlocking.map(findingLine));
|
|
9932
|
+
if (!findings.length) out.push("", "No findings. \u{1F389}");
|
|
9933
|
+
out.push("", "<sub>Posted by `harness review-ci`. The CI exit code is authoritative.</sub>");
|
|
9934
|
+
return out.join("\n");
|
|
9935
|
+
}
|
|
9936
|
+
var defaultPostReview = (verdict) => {
|
|
9937
|
+
execFileSync6("gh", ["pr", "comment", "--body-file", "-"], {
|
|
9938
|
+
input: buildReviewBody(verdict),
|
|
9939
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
9940
|
+
encoding: "utf-8"
|
|
9941
|
+
});
|
|
9942
|
+
};
|
|
9943
|
+
function emitReviewCi(result, opts, writeFile2 = (p, d) => writeFileSync18(p, d), log = (m) => process.stdout.write(m + "\n"), postReview = defaultPostReview) {
|
|
9944
|
+
const serialized = JSON.stringify(result.verdict, null, 2);
|
|
9945
|
+
const jsonToStdout = opts.jsonPath === true;
|
|
9946
|
+
if (!jsonToStdout) log(result.terminalOutput);
|
|
9947
|
+
if (typeof opts.jsonPath === "string") writeFile2(opts.jsonPath, serialized);
|
|
9948
|
+
else if (jsonToStdout) log(serialized);
|
|
9949
|
+
if (opts.comment) {
|
|
9950
|
+
try {
|
|
9951
|
+
postReview(result.verdict);
|
|
9952
|
+
} catch (err) {
|
|
9953
|
+
logger.warn(
|
|
9954
|
+
`review-ci: failed to post PR comment (${err instanceof Error ? err.message : String(err)}). The verdict above is authoritative; the exit code still reflects the gate.`
|
|
9955
|
+
);
|
|
9956
|
+
}
|
|
9957
|
+
}
|
|
9958
|
+
}
|
|
9959
|
+
function createReviewCiCommand() {
|
|
9960
|
+
return new Command76("review-ci").description("Run the tiered code-review gate (floor + optional LLM runner) for CI").addOption(
|
|
9961
|
+
new Option2("--runner <runner>", `${KNOWN_RUNNERS.join(" | ")} (omit = floor-only)`).choices(
|
|
9962
|
+
KNOWN_RUNNERS
|
|
9963
|
+
)
|
|
9964
|
+
).addOption(
|
|
9965
|
+
new Option2("--block-on <level>", BLOCK_ON_LEVELS.join(" | ")).choices(BLOCK_ON_LEVELS).default("request-changes")
|
|
9966
|
+
).option("--diff <range>", "git range (default: origin/<base>...HEAD)").option("--comment", "post the verdict as a comment on the current branch's PR via gh").option(
|
|
9967
|
+
"--out <path>",
|
|
9968
|
+
"write the verdict JSON artifact to a file (use the global --json to stream it to stdout instead)"
|
|
9969
|
+
).action(async (opts, cmd) => {
|
|
9970
|
+
const result = await runReviewCi({
|
|
9971
|
+
runner: opts.runner,
|
|
9972
|
+
blockOn: opts.blockOn,
|
|
9973
|
+
diffRange: opts.diff
|
|
9974
|
+
});
|
|
9975
|
+
const streamJson = cmd.optsWithGlobals().json === true;
|
|
9976
|
+
emitReviewCi(result, {
|
|
9977
|
+
jsonPath: typeof opts.out === "string" ? opts.out : streamJson ? true : void 0,
|
|
9978
|
+
comment: opts.comment
|
|
9979
|
+
});
|
|
9980
|
+
process.exit(result.exitCode);
|
|
9981
|
+
});
|
|
9982
|
+
}
|
|
9983
|
+
|
|
9984
|
+
// src/commands/pre-merge-brief.ts
|
|
9985
|
+
var BRIEF_MARKER = "<!-- harness:pre-merge-brief -->";
|
|
9986
|
+
var UNAVAILABLE = "> _unavailable / not configured._";
|
|
9987
|
+
function renderDiffSummary(diff2) {
|
|
9988
|
+
const out = ["## Diff summary", ""];
|
|
9989
|
+
if (!diff2) {
|
|
9990
|
+
out.push(UNAVAILABLE);
|
|
9991
|
+
return out;
|
|
9992
|
+
}
|
|
9993
|
+
out.push(
|
|
9994
|
+
`**Files changed:** ${diff2.changedFiles.length} (new: ${diff2.newFiles.length}, deleted: ${diff2.deletedFiles.length}) \u2022 **Diff lines:** ${diff2.totalDiffLines}`
|
|
9995
|
+
);
|
|
9996
|
+
return out;
|
|
9997
|
+
}
|
|
9998
|
+
function findingLine2(f) {
|
|
9999
|
+
const loc = f.lineRange ? `${f.file}:${f.lineRange[0]}` : f.file;
|
|
10000
|
+
return `- \`${f.severity}\` **${loc}** \u2014 ${f.title}`;
|
|
10001
|
+
}
|
|
10002
|
+
function renderPopulatedVerdict(verdict) {
|
|
10003
|
+
const findings = verdict.findings ?? [];
|
|
10004
|
+
const blocking = verdict.blockingFindings ?? [];
|
|
10005
|
+
const out = [
|
|
10006
|
+
`**Assessment:** \`${verdict.assessment}\` \u2022 **Runner:** \`${verdict.runner}\` \u2022 **Findings:** ${findings.length} (blocking: ${blocking.length})`
|
|
10007
|
+
];
|
|
10008
|
+
if (verdict.skipped) {
|
|
10009
|
+
out.push("", `> \u26A0\uFE0F ${verdict.skipReason ?? "A review tier was skipped."}`);
|
|
10010
|
+
}
|
|
10011
|
+
if (blocking.length) out.push("", "### Blocking", ...blocking.map(findingLine2));
|
|
10012
|
+
const nonBlocking = findings.filter((f) => !blocking.some((b) => b.id === f.id));
|
|
10013
|
+
if (nonBlocking.length) out.push("", "### Other findings", ...nonBlocking.map(findingLine2));
|
|
10014
|
+
return out;
|
|
10015
|
+
}
|
|
10016
|
+
function renderReviewVerdict(verdict) {
|
|
10017
|
+
if (!verdict) return ["## Review verdict", "", UNAVAILABLE];
|
|
10018
|
+
return ["## Review verdict", "", ...renderPopulatedVerdict(verdict)];
|
|
10019
|
+
}
|
|
10020
|
+
function signalLine(s) {
|
|
10021
|
+
const value = s.value === null ? "\u2014" : `${s.value}${s.unit ?? ""}`;
|
|
10022
|
+
return `- \`${s.status}\` **${s.label}** \u2014 ${value}`;
|
|
10023
|
+
}
|
|
10024
|
+
function renderSignalStatus(signals) {
|
|
10025
|
+
const out = ["## Signal status", ""];
|
|
10026
|
+
if (!signals || signals.length === 0) {
|
|
10027
|
+
out.push(UNAVAILABLE);
|
|
10028
|
+
return out;
|
|
10029
|
+
}
|
|
10030
|
+
out.push(...signals.map(signalLine));
|
|
10031
|
+
return out;
|
|
10032
|
+
}
|
|
10033
|
+
function renderOutcomeEval(outcome) {
|
|
10034
|
+
const out = ["## Outcome evaluation", ""];
|
|
10035
|
+
if (!outcome) {
|
|
10036
|
+
out.push("> _not yet evaluated._");
|
|
10037
|
+
return out;
|
|
10038
|
+
}
|
|
10039
|
+
out.push(
|
|
10040
|
+
`**Verdict:** \`${outcome.verdict}\` \u2022 **Confidence:** \`${outcome.confidence}\` \u2022 **Authority:** \`${outcome.authority}\``,
|
|
10041
|
+
"",
|
|
10042
|
+
outcome.rationale
|
|
10043
|
+
);
|
|
10044
|
+
return out;
|
|
10045
|
+
}
|
|
10046
|
+
function blockingBullets(inputs) {
|
|
10047
|
+
return (inputs.review?.blockingFindings ?? []).map(
|
|
10048
|
+
(f) => `- \u{1F6D1} ${findingLine2(f).replace(/^- /, "")}`
|
|
10049
|
+
);
|
|
10050
|
+
}
|
|
10051
|
+
function signalBullets(inputs) {
|
|
10052
|
+
return (inputs.signals ?? []).filter((s) => s.status === "warn" || s.status === "alert").map((s) => `- \u{1F4CA} ${signalLine(s).replace(/^- /, "")}`);
|
|
10053
|
+
}
|
|
10054
|
+
function unmetBullets(inputs) {
|
|
10055
|
+
const unmet = inputs.outcome?.verdict === "NOT_SATISFIED" ? inputs.outcome.unmetCriteria ?? [] : [];
|
|
10056
|
+
return unmet.map((c) => `- \u{1F3AF} ${c}`);
|
|
10057
|
+
}
|
|
10058
|
+
function collectWorthYourEyesBullets(inputs) {
|
|
10059
|
+
return [...blockingBullets(inputs), ...signalBullets(inputs), ...unmetBullets(inputs)];
|
|
10060
|
+
}
|
|
10061
|
+
function deriveWorthYourEyes(inputs) {
|
|
10062
|
+
const bullets = collectWorthYourEyesBullets(inputs);
|
|
10063
|
+
if (bullets.length === 0) {
|
|
10064
|
+
return [
|
|
10065
|
+
"## \u{1F440} Worth your eyes",
|
|
10066
|
+
"",
|
|
10067
|
+
"_Nothing flagged \u2014 no blocking findings, no warn/alert signals, no unmet criteria._"
|
|
10068
|
+
];
|
|
10069
|
+
}
|
|
10070
|
+
return ["## \u{1F440} Worth your eyes", "", ...bullets];
|
|
10071
|
+
}
|
|
10072
|
+
function buildBriefBody(inputs) {
|
|
10073
|
+
const lines = [
|
|
10074
|
+
BRIEF_MARKER,
|
|
10075
|
+
"# \u{1F9ED} Pre-merge brief",
|
|
10076
|
+
"",
|
|
10077
|
+
...renderDiffSummary(inputs.diff),
|
|
10078
|
+
"",
|
|
10079
|
+
...renderReviewVerdict(inputs.review),
|
|
10080
|
+
"",
|
|
10081
|
+
...renderSignalStatus(inputs.signals),
|
|
10082
|
+
"",
|
|
10083
|
+
...renderOutcomeEval(inputs.outcome),
|
|
10084
|
+
"",
|
|
10085
|
+
...deriveWorthYourEyes(inputs)
|
|
10086
|
+
];
|
|
10087
|
+
return lines.join("\n");
|
|
10088
|
+
}
|
|
10089
|
+
function upsertComment(comments, body, patch, post) {
|
|
10090
|
+
const marked = comments.find((c) => c.body.trimStart().startsWith(BRIEF_MARKER));
|
|
10091
|
+
if (marked) {
|
|
10092
|
+
patch(marked.id, body);
|
|
10093
|
+
} else {
|
|
10094
|
+
post(body);
|
|
10095
|
+
}
|
|
10096
|
+
}
|
|
10097
|
+
var defaultPostBrief = (body) => {
|
|
10098
|
+
const raw = execFileSync7("gh", ["pr", "view", "--json", "comments"], {
|
|
10099
|
+
encoding: "utf-8"
|
|
10100
|
+
}).toString();
|
|
10101
|
+
const parsed = JSON.parse(raw);
|
|
10102
|
+
const comments = (parsed.comments ?? []).filter(
|
|
10103
|
+
(c) => typeof c.id === "number" && typeof c.body === "string"
|
|
10104
|
+
).map((c) => ({ id: c.id, body: c.body }));
|
|
10105
|
+
upsertComment(
|
|
10106
|
+
comments,
|
|
10107
|
+
body,
|
|
10108
|
+
(id, patchBody) => {
|
|
10109
|
+
execFileSync7(
|
|
10110
|
+
"gh",
|
|
10111
|
+
["api", "-X", "PATCH", `/repos/{owner}/{repo}/issues/comments/${id}`, "-F", "body=@-"],
|
|
10112
|
+
{ input: patchBody, stdio: ["pipe", "pipe", "pipe"], encoding: "utf-8" }
|
|
10113
|
+
);
|
|
10114
|
+
},
|
|
10115
|
+
(postBody) => {
|
|
10116
|
+
execFileSync7("gh", ["pr", "comment", "--body-file", "-"], {
|
|
10117
|
+
input: postBody,
|
|
10118
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
10119
|
+
encoding: "utf-8"
|
|
10120
|
+
});
|
|
10121
|
+
}
|
|
10122
|
+
);
|
|
10123
|
+
};
|
|
10124
|
+
var defaultReadFile = (path80) => readFileSync18(path80, "utf-8");
|
|
10125
|
+
function readReview(path80, readFile4 = defaultReadFile) {
|
|
10126
|
+
if (!path80) return void 0;
|
|
10127
|
+
try {
|
|
10128
|
+
const parsed = JSON.parse(readFile4(path80));
|
|
10129
|
+
return parsed?.verdict && typeof parsed.verdict === "object" ? parsed.verdict : void 0;
|
|
10130
|
+
} catch {
|
|
10131
|
+
return void 0;
|
|
10132
|
+
}
|
|
10133
|
+
}
|
|
10134
|
+
async function gatherSignalsSafe(projectPath, gather = gatherSignals) {
|
|
10135
|
+
try {
|
|
10136
|
+
const result = await gather(projectPath);
|
|
10137
|
+
return result.signals;
|
|
10138
|
+
} catch {
|
|
10139
|
+
return [];
|
|
10140
|
+
}
|
|
10141
|
+
}
|
|
10142
|
+
var GRAPH_DIR = ".harness/graph";
|
|
10143
|
+
async function loadOutcomeStore(projectPath) {
|
|
10144
|
+
try {
|
|
10145
|
+
const store = new GraphStore();
|
|
10146
|
+
const loaded = await store.load(join40(projectPath, GRAPH_DIR));
|
|
10147
|
+
return loaded ? store : void 0;
|
|
10148
|
+
} catch {
|
|
10149
|
+
return void 0;
|
|
10150
|
+
}
|
|
10151
|
+
}
|
|
10152
|
+
function findOutcomeVerdict(store, headSha) {
|
|
10153
|
+
if (!store || !headSha) return void 0;
|
|
10154
|
+
try {
|
|
10155
|
+
const nodes = store.findNodes({ type: "execution_outcome" });
|
|
10156
|
+
const match = nodes.find((n) => {
|
|
10157
|
+
const m2 = n.metadata ?? {};
|
|
10158
|
+
return m2.commit === headSha || m2.headSha === headSha;
|
|
10159
|
+
});
|
|
10160
|
+
if (!match) return void 0;
|
|
10161
|
+
const m = match.metadata;
|
|
10162
|
+
return {
|
|
10163
|
+
verdict: m.verdict,
|
|
10164
|
+
confidence: m.confidence ?? "low",
|
|
10165
|
+
rationale: m.rationale ?? "",
|
|
10166
|
+
judgedAgainst: m.judgedAgainst ?? "success-criteria",
|
|
10167
|
+
unmetCriteria: Array.isArray(m.unmetCriteria) ? m.unmetCriteria : [],
|
|
10168
|
+
authority: m.authority ?? "advisory"
|
|
10169
|
+
};
|
|
10170
|
+
} catch {
|
|
10171
|
+
return void 0;
|
|
10172
|
+
}
|
|
10173
|
+
}
|
|
10174
|
+
function defaultResolveRaw2(range, _cwd, runGit) {
|
|
10175
|
+
return runGit(["diff", range]);
|
|
10176
|
+
}
|
|
10177
|
+
async function runPreMergeBrief(opts) {
|
|
10178
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
10179
|
+
const runGit = opts.runGit ?? ((args) => execFileSync7("git", args, { encoding: "utf-8" }).toString().trim());
|
|
10180
|
+
const range = resolveDiffRange({
|
|
10181
|
+
...opts.diffRange ? { range: opts.diffRange } : {},
|
|
10182
|
+
cwd,
|
|
10183
|
+
runGit
|
|
10184
|
+
});
|
|
10185
|
+
let diff2;
|
|
10186
|
+
try {
|
|
10187
|
+
const rawDiff = (opts.resolveRaw ?? defaultResolveRaw2)(range, cwd, runGit);
|
|
10188
|
+
diff2 = rawDiff.trim() ? buildDiffInfo(rawDiff) : void 0;
|
|
10189
|
+
} catch {
|
|
10190
|
+
diff2 = void 0;
|
|
10191
|
+
}
|
|
10192
|
+
const review = readReview(opts.from, opts.readFile);
|
|
10193
|
+
const signals = await gatherSignalsSafe(cwd, opts.gather);
|
|
10194
|
+
const outcome = findOutcomeVerdict(opts.store, opts.headSha);
|
|
10195
|
+
const body = buildBriefBody({ diff: diff2, review, signals, outcome });
|
|
10196
|
+
const log = opts.log ?? ((m) => process.stdout.write(m + "\n"));
|
|
10197
|
+
if (opts.comment) {
|
|
10198
|
+
try {
|
|
10199
|
+
(opts.postBrief ?? defaultPostBrief)(body);
|
|
10200
|
+
} catch (err) {
|
|
10201
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
10202
|
+
(opts.warn ?? ((m) => process.stderr.write(m + "\n")))(
|
|
10203
|
+
`\u26A0\uFE0F pre-merge-brief: could not post sticky comment (${reason}); printing brief instead.`
|
|
10204
|
+
);
|
|
10205
|
+
log(body);
|
|
10206
|
+
}
|
|
10207
|
+
} else {
|
|
10208
|
+
log(body);
|
|
10209
|
+
}
|
|
10210
|
+
return { body };
|
|
10211
|
+
}
|
|
10212
|
+
function createPreMergeBriefCommand() {
|
|
10213
|
+
return new Command77("pre-merge-brief").description(
|
|
10214
|
+
'Compose a senior-facing pre-merge PR brief (diff, review, signals, outcome, "worth your eyes")'
|
|
10215
|
+
).option("--from <path>", "review-ci --json verdict artifact").option("--comment", "upsert the brief as a sticky comment on the current branch's PR").option("--diff <range>", "git range (default: origin/<base>...HEAD)").option("--head <sha>", "head commit sha for the outcome lookup (default: git rev-parse HEAD)").action(async (opts) => {
|
|
10216
|
+
const cwd = process.cwd();
|
|
10217
|
+
const runGit = (args) => execFileSync7("git", args, { encoding: "utf-8" }).toString().trim();
|
|
10218
|
+
let headSha = opts.head;
|
|
10219
|
+
if (!headSha) {
|
|
10220
|
+
try {
|
|
10221
|
+
headSha = runGit(["rev-parse", "HEAD"]) || void 0;
|
|
10222
|
+
} catch {
|
|
10223
|
+
headSha = void 0;
|
|
10224
|
+
}
|
|
10225
|
+
}
|
|
10226
|
+
const store = await loadOutcomeStore(cwd);
|
|
10227
|
+
await runPreMergeBrief({
|
|
10228
|
+
cwd,
|
|
10229
|
+
from: opts.from,
|
|
10230
|
+
comment: opts.comment,
|
|
10231
|
+
diffRange: opts.diff,
|
|
10232
|
+
headSha,
|
|
10233
|
+
store,
|
|
10234
|
+
runGit
|
|
10235
|
+
});
|
|
10236
|
+
process.exit(0);
|
|
10237
|
+
});
|
|
10238
|
+
}
|
|
10239
|
+
|
|
10240
|
+
// src/commands/proposals.ts
|
|
10241
|
+
init_dist3();
|
|
10242
|
+
import { Command as Command78 } from "commander";
|
|
10243
|
+
import { resolve as resolve34 } from "path";
|
|
10244
|
+
function projectRoot() {
|
|
10245
|
+
return resolve34(process.env["HARNESS_PROJECT_ROOT"] ?? process.cwd());
|
|
10246
|
+
}
|
|
10247
|
+
function summarizeProposal(p) {
|
|
10248
|
+
return {
|
|
10249
|
+
id: p.id,
|
|
10250
|
+
kind: p.kind,
|
|
10251
|
+
targetSkill: p.targetSkill,
|
|
10252
|
+
name: p.content.name,
|
|
10253
|
+
status: p.status,
|
|
10254
|
+
proposedBy: p.proposedBy,
|
|
10255
|
+
createdAt: p.createdAt,
|
|
10256
|
+
gateLastRunAt: p.gate?.lastRunAt,
|
|
10257
|
+
findings: p.gate?.findings?.length ?? 0
|
|
10258
|
+
};
|
|
10259
|
+
}
|
|
10260
|
+
async function runProposalsList(status) {
|
|
10261
|
+
const opts = {};
|
|
10262
|
+
if (status) opts.status = status;
|
|
10263
|
+
const proposals = await listProposals(projectRoot(), opts);
|
|
10264
|
+
return proposals.map(summarizeProposal);
|
|
10265
|
+
}
|
|
10266
|
+
async function runProposalsShow(id) {
|
|
10267
|
+
return getProposal(projectRoot(), id);
|
|
10268
|
+
}
|
|
10269
|
+
async function runProposalsReject(id, reason) {
|
|
10270
|
+
const decision = {
|
|
10271
|
+
decidedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10272
|
+
decidedBy: process.env["USER"] ?? "cli",
|
|
10273
|
+
action: "rejected",
|
|
10274
|
+
reason
|
|
10275
|
+
};
|
|
10276
|
+
return updateProposal(projectRoot(), id, { status: "rejected", decision });
|
|
10277
|
+
}
|
|
10278
|
+
var ALLOWED_STATUSES = [
|
|
10279
|
+
"open",
|
|
10280
|
+
"gate-running",
|
|
10281
|
+
"gate-failed",
|
|
10282
|
+
"approved",
|
|
10283
|
+
"rejected",
|
|
10284
|
+
"all"
|
|
10285
|
+
];
|
|
10286
|
+
function fail(message) {
|
|
10287
|
+
console.error(message);
|
|
10288
|
+
process.exitCode = 1;
|
|
10289
|
+
}
|
|
10290
|
+
async function actListCommand(opts) {
|
|
10291
|
+
const raw = opts.status ?? "open";
|
|
10292
|
+
if (!ALLOWED_STATUSES.includes(raw)) {
|
|
10293
|
+
fail(`Error: unknown status "${raw}"`);
|
|
10294
|
+
return;
|
|
10295
|
+
}
|
|
10296
|
+
const proposals = await runProposalsList(raw);
|
|
10297
|
+
console.log(JSON.stringify(proposals, null, 2));
|
|
10298
|
+
}
|
|
10299
|
+
async function actShowCommand(id) {
|
|
10300
|
+
const proposal = await runProposalsShow(id);
|
|
10301
|
+
if (!proposal) {
|
|
10302
|
+
fail(`No such proposal: ${id}`);
|
|
10303
|
+
return;
|
|
10304
|
+
}
|
|
10305
|
+
console.log(JSON.stringify(proposal, null, 2));
|
|
10306
|
+
}
|
|
10307
|
+
async function actRejectCommand(id, opts) {
|
|
10308
|
+
try {
|
|
10309
|
+
const updated = await runProposalsReject(id, opts.reason);
|
|
10310
|
+
console.log(JSON.stringify(summarizeProposal(updated), null, 2));
|
|
10311
|
+
} catch (err) {
|
|
10312
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
10313
|
+
}
|
|
10314
|
+
}
|
|
10315
|
+
async function actApproveCommand(id) {
|
|
10316
|
+
const orchestratorUrl = process.env["HARNESS_ORCHESTRATOR_URL"] ?? "http://127.0.0.1:4577";
|
|
10317
|
+
const token = process.env["HARNESS_ADMIN_TOKEN"];
|
|
10318
|
+
if (!token) {
|
|
10319
|
+
fail("HARNESS_ADMIN_TOKEN is required to approve proposals (manage-proposals scope).");
|
|
10320
|
+
return;
|
|
10321
|
+
}
|
|
10322
|
+
try {
|
|
10323
|
+
const res = await fetch(`${orchestratorUrl}/api/v1/proposals/${id}/approve`, {
|
|
10324
|
+
method: "POST",
|
|
10325
|
+
headers: { authorization: `Bearer ${token}` }
|
|
10326
|
+
});
|
|
10327
|
+
if (!res.ok) {
|
|
10328
|
+
fail(`HTTP ${res.status}: ${await res.text()}`);
|
|
10329
|
+
return;
|
|
10330
|
+
}
|
|
10331
|
+
console.log(await res.text());
|
|
10332
|
+
} catch (err) {
|
|
10333
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
10334
|
+
}
|
|
10335
|
+
}
|
|
10336
|
+
function createProposalsCommand() {
|
|
10337
|
+
const cmd = new Command78("proposals").description("Skill-proposal review queue");
|
|
10338
|
+
cmd.command("list").description("List skill proposals in the local queue").option(
|
|
10339
|
+
"--status <status>",
|
|
10340
|
+
`Filter by status \u2014 one of ${ALLOWED_STATUSES.join(" | ")}`,
|
|
10341
|
+
"open"
|
|
10342
|
+
).action(actListCommand);
|
|
10343
|
+
cmd.command("show <id>").description("Show a single proposal in full").action(actShowCommand);
|
|
10344
|
+
cmd.command("reject <id>").description("Reject a proposal with a one-line reason").requiredOption("--reason <text>", "Why the proposal is being rejected").action(actRejectCommand);
|
|
10345
|
+
cmd.command("approve <id>").description(
|
|
10346
|
+
"Approve a proposal (runs the soundness-review gate then promotes). Requires the orchestrator to be running."
|
|
10347
|
+
).action(actApproveCommand);
|
|
10348
|
+
return cmd;
|
|
10349
|
+
}
|
|
10350
|
+
|
|
10351
|
+
// src/commands/publish-analyses.ts
|
|
10352
|
+
import * as fs31 from "fs";
|
|
10353
|
+
import * as path52 from "path";
|
|
10354
|
+
import { Command as Command79 } from "commander";
|
|
10355
|
+
init_dist3();
|
|
10356
|
+
import {
|
|
10357
|
+
renderAnalysisComment,
|
|
10358
|
+
loadPublishedIndex,
|
|
10359
|
+
savePublishedIndex
|
|
10360
|
+
} from "@harness-engineering/orchestrator";
|
|
10361
|
+
function bootstrapTrackerCommand(opts, verb) {
|
|
10362
|
+
const projectPath = path52.resolve(opts.dir);
|
|
10363
|
+
const trackerConfig = loadTrackerSyncConfig(projectPath);
|
|
10364
|
+
if (!trackerConfig) {
|
|
10365
|
+
logger.error(`No tracker config found in harness.config.json. Cannot ${verb}.`);
|
|
10366
|
+
return null;
|
|
10367
|
+
}
|
|
10368
|
+
if (trackerConfig.kind !== "github") {
|
|
10369
|
+
logger.error(
|
|
10370
|
+
`${verb} currently only supports 'github' tracker kind. Found: ${String(trackerConfig.kind)}`
|
|
10371
|
+
);
|
|
10372
|
+
return null;
|
|
10373
|
+
}
|
|
10374
|
+
const projectEnvPath = path52.join(projectPath, ".env");
|
|
10375
|
+
if (fs31.existsSync(projectEnvPath) && !process.env.GITHUB_TOKEN) {
|
|
10376
|
+
__require("dotenv").config({ path: projectEnvPath });
|
|
10377
|
+
}
|
|
10378
|
+
const token = process.env.GITHUB_TOKEN;
|
|
10379
|
+
if (!token) {
|
|
10380
|
+
logger.error("No GITHUB_TOKEN environment variable found.");
|
|
10381
|
+
return null;
|
|
10382
|
+
}
|
|
10383
|
+
return { token, projectPath, trackerConfig };
|
|
10384
|
+
}
|
|
10385
|
+
async function buildNameToExternalIdMap(projectPath) {
|
|
10386
|
+
const mode = loadProjectRoadmapMode(projectPath);
|
|
10387
|
+
if (mode === "file-less") {
|
|
10388
|
+
const trackerCfg = loadTrackerClientConfigFromProject(projectPath);
|
|
10389
|
+
if (!trackerCfg.ok) {
|
|
10390
|
+
logger.error(`File-less mode: ${trackerCfg.error.message}`);
|
|
10391
|
+
return null;
|
|
10392
|
+
}
|
|
10393
|
+
const clientR = createTrackerClient(trackerCfg.value);
|
|
10394
|
+
if (!clientR.ok) {
|
|
10395
|
+
logger.error(`File-less mode: ${clientR.error.message}`);
|
|
10396
|
+
return null;
|
|
10397
|
+
}
|
|
10398
|
+
const r = await clientR.value.fetchAll();
|
|
9915
10399
|
if (!r.ok) {
|
|
9916
10400
|
logger.error(`File-less mode: failed to fetch features from tracker: ${r.error.message}`);
|
|
9917
10401
|
return null;
|
|
@@ -9979,7 +10463,7 @@ async function runPublishAnalyses(opts) {
|
|
|
9979
10463
|
const nameToExternalId = await buildNameToExternalIdMap(projectPath);
|
|
9980
10464
|
if (!nameToExternalId) return process.exit(1);
|
|
9981
10465
|
const { AnalysisArchive } = await import("@harness-engineering/orchestrator");
|
|
9982
|
-
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-
|
|
10466
|
+
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-MCTEX5QH.js");
|
|
9983
10467
|
const archive = new AnalysisArchive(path52.join(projectPath, ".harness", "analyses"));
|
|
9984
10468
|
const analyses = await archive.list();
|
|
9985
10469
|
if (analyses.length === 0) {
|
|
@@ -10002,7 +10486,7 @@ async function runPublishAnalyses(opts) {
|
|
|
10002
10486
|
}
|
|
10003
10487
|
}
|
|
10004
10488
|
function createPublishAnalysesCommand() {
|
|
10005
|
-
const command = new
|
|
10489
|
+
const command = new Command79("publish-analyses").description(
|
|
10006
10490
|
"Publishes locally generated intelligence analyses to the external issue tracker (e.g., GitHub)"
|
|
10007
10491
|
).option("-d, --dir <path>", "Workspace directory", process.cwd()).action(async (opts) => {
|
|
10008
10492
|
try {
|
|
@@ -10018,18 +10502,18 @@ function createPublishAnalysesCommand() {
|
|
|
10018
10502
|
}
|
|
10019
10503
|
|
|
10020
10504
|
// src/commands/pulse/index.ts
|
|
10021
|
-
import { Command as
|
|
10505
|
+
import { Command as Command81 } from "commander";
|
|
10022
10506
|
|
|
10023
10507
|
// src/commands/pulse/run.ts
|
|
10024
10508
|
init_dist3();
|
|
10025
|
-
import { readFileSync as
|
|
10026
|
-
import { dirname as dirname18, join as
|
|
10027
|
-
import { Command as
|
|
10509
|
+
import { readFileSync as readFileSync19, mkdirSync as mkdirSync15, writeFileSync as writeFileSync19 } from "fs";
|
|
10510
|
+
import { dirname as dirname18, join as join42, resolve as resolve36 } from "path";
|
|
10511
|
+
import { Command as Command80 } from "commander";
|
|
10028
10512
|
async function runPulseRunCommand(opts) {
|
|
10029
10513
|
const startedAt = Date.now();
|
|
10030
10514
|
let raw;
|
|
10031
10515
|
try {
|
|
10032
|
-
raw = JSON.parse(
|
|
10516
|
+
raw = JSON.parse(readFileSync19(opts.configPath, "utf-8"));
|
|
10033
10517
|
} catch (err) {
|
|
10034
10518
|
return emit2(
|
|
10035
10519
|
{
|
|
@@ -10055,9 +10539,9 @@ async function runPulseRunCommand(opts) {
|
|
|
10055
10539
|
const report = assembleReport(result, productName, lookback);
|
|
10056
10540
|
const now = /* @__PURE__ */ new Date();
|
|
10057
10541
|
const ts = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}_${String(now.getHours()).padStart(2, "0")}-${String(now.getMinutes()).padStart(2, "0")}`;
|
|
10058
|
-
const filePath =
|
|
10542
|
+
const filePath = join42(opts.outputDir, `${ts}.md`);
|
|
10059
10543
|
mkdirSync15(dirname18(filePath), { recursive: true });
|
|
10060
|
-
|
|
10544
|
+
writeFileSync19(filePath, report);
|
|
10061
10545
|
const headlines = extractHeadlines(report);
|
|
10062
10546
|
return emit2(
|
|
10063
10547
|
{
|
|
@@ -10089,7 +10573,7 @@ function emit2(status, nonInteractive) {
|
|
|
10089
10573
|
return status;
|
|
10090
10574
|
}
|
|
10091
10575
|
function createRunCommand2() {
|
|
10092
|
-
return new
|
|
10576
|
+
return new Command80("run").description("Run a pulse: query configured adapters, sanitize, assemble single-page report").option(
|
|
10093
10577
|
"--lookback <window>",
|
|
10094
10578
|
"Lookback window (e.g. 24h, 7d). Defaults to pulse.lookbackDefault."
|
|
10095
10579
|
).option(
|
|
@@ -10113,13 +10597,13 @@ function createRunCommand2() {
|
|
|
10113
10597
|
|
|
10114
10598
|
// src/commands/pulse/index.ts
|
|
10115
10599
|
function createPulseCommand() {
|
|
10116
|
-
const command = new
|
|
10600
|
+
const command = new Command81("pulse").description("Pulse (read-side observability) commands");
|
|
10117
10601
|
command.addCommand(createRunCommand2());
|
|
10118
10602
|
return command;
|
|
10119
10603
|
}
|
|
10120
10604
|
|
|
10121
10605
|
// src/commands/recommend.ts
|
|
10122
|
-
import { Command as
|
|
10606
|
+
import { Command as Command82 } from "commander";
|
|
10123
10607
|
import chalk13 from "chalk";
|
|
10124
10608
|
async function resolveSnapshot(cwd, noCache) {
|
|
10125
10609
|
if (!noCache) {
|
|
@@ -10140,307 +10624,90 @@ function buildSkillsRecord(cwd) {
|
|
|
10140
10624
|
}
|
|
10141
10625
|
return skills;
|
|
10142
10626
|
}
|
|
10143
|
-
async function runRecommend(options) {
|
|
10144
|
-
const cwd = options.cwd ?? process.cwd();
|
|
10145
|
-
const top = options.top ?? 5;
|
|
10146
|
-
const { snapshot, usedCache } = await resolveSnapshot(cwd, options.noCache);
|
|
10147
|
-
const skills = buildSkillsRecord(cwd);
|
|
10148
|
-
const result = recommend(snapshot, skills, { top });
|
|
10149
|
-
return {
|
|
10150
|
-
...result,
|
|
10151
|
-
snapshotAge: usedCache ? "cached" : "fresh"
|
|
10152
|
-
};
|
|
10153
|
-
}
|
|
10154
|
-
function formatRecommendation(rec) {
|
|
10155
|
-
const lines = [];
|
|
10156
|
-
if (rec.urgency === "critical") {
|
|
10157
|
-
lines.push(` ${chalk13.red("[CRITICAL]")} ${rec.sequence}. ${rec.skillName}`);
|
|
10158
|
-
} else {
|
|
10159
|
-
lines.push(` ${rec.sequence}. ${rec.skillName} (${rec.score.toFixed(2)})`);
|
|
10160
|
-
}
|
|
10161
|
-
for (const reason of rec.reasons) {
|
|
10162
|
-
lines.push(` ${chalk13.dim("\u2192")} ${reason}`);
|
|
10163
|
-
}
|
|
10164
|
-
return lines.join("\n");
|
|
10165
|
-
}
|
|
10166
|
-
function printRecommendations(result) {
|
|
10167
|
-
if (result.recommendations.length === 0) {
|
|
10168
|
-
console.log("");
|
|
10169
|
-
console.log("No recommendations. Codebase health looks good!");
|
|
10170
|
-
console.log("");
|
|
10171
|
-
return;
|
|
10172
|
-
}
|
|
10173
|
-
console.log("");
|
|
10174
|
-
console.log(
|
|
10175
|
-
`Recommended workflow (${result.recommendations.length} skill${result.recommendations.length === 1 ? "" : "s"}):`
|
|
10176
|
-
);
|
|
10177
|
-
console.log("");
|
|
10178
|
-
for (const rec of result.recommendations) {
|
|
10179
|
-
console.log(formatRecommendation(rec));
|
|
10180
|
-
console.log("");
|
|
10181
|
-
}
|
|
10182
|
-
console.log(`Sequence reasoning: ${result.sequenceReasoning}`);
|
|
10183
|
-
console.log("");
|
|
10184
|
-
}
|
|
10185
|
-
function createRecommendCommand() {
|
|
10186
|
-
const command = new Command80("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) => {
|
|
10187
|
-
const globalOpts = cmd.optsWithGlobals();
|
|
10188
|
-
const mode = globalOpts.json ? OutputMode.JSON : OutputMode.TEXT;
|
|
10189
|
-
try {
|
|
10190
|
-
const top = parseInt(opts.top, 10);
|
|
10191
|
-
if (isNaN(top) || top < 1) {
|
|
10192
|
-
logger.error("--top must be a positive integer");
|
|
10193
|
-
process.exit(1);
|
|
10194
|
-
}
|
|
10195
|
-
if (mode === OutputMode.TEXT) {
|
|
10196
|
-
console.log("");
|
|
10197
|
-
console.log("Analyzing codebase health...");
|
|
10198
|
-
}
|
|
10199
|
-
const result = await runRecommend({
|
|
10200
|
-
noCache: opts.cache === false,
|
|
10201
|
-
top
|
|
10202
|
-
});
|
|
10203
|
-
if (mode === OutputMode.JSON) {
|
|
10204
|
-
console.log(JSON.stringify(result, null, 2));
|
|
10205
|
-
} else {
|
|
10206
|
-
printRecommendations(result);
|
|
10207
|
-
}
|
|
10208
|
-
} catch (err) {
|
|
10209
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
10210
|
-
if (mode === OutputMode.JSON) {
|
|
10211
|
-
console.log(JSON.stringify({ error: message }));
|
|
10212
|
-
} else {
|
|
10213
|
-
logger.error(message);
|
|
10214
|
-
}
|
|
10215
|
-
process.exit(1);
|
|
10216
|
-
}
|
|
10217
|
-
});
|
|
10218
|
-
return command;
|
|
10219
|
-
}
|
|
10220
|
-
|
|
10221
|
-
// src/commands/review-ci.ts
|
|
10222
|
-
init_dist3();
|
|
10223
|
-
import { execFileSync as execFileSync6 } from "child_process";
|
|
10224
|
-
import { writeFileSync as writeFileSync19 } from "fs";
|
|
10225
|
-
import { Command as Command81, Option as Option2 } from "commander";
|
|
10226
|
-
|
|
10227
|
-
// src/commands/review-ci-local-adapter.ts
|
|
10228
|
-
import { z } from "zod";
|
|
10229
|
-
import { OpenAICompatibleAnalysisProvider } from "@harness-engineering/intelligence";
|
|
10230
|
-
var FindingSchema = z.object({
|
|
10231
|
-
id: z.string(),
|
|
10232
|
-
file: z.string(),
|
|
10233
|
-
lineRange: z.tuple([z.number(), z.number()]),
|
|
10234
|
-
domain: z.enum(["compliance", "bug", "security", "architecture", "learnings"]),
|
|
10235
|
-
severity: z.enum(["critical", "important", "suggestion"]),
|
|
10236
|
-
title: z.string(),
|
|
10237
|
-
rationale: z.string(),
|
|
10238
|
-
suggestion: z.string().optional(),
|
|
10239
|
-
evidence: z.array(z.string())
|
|
10240
|
-
});
|
|
10241
|
-
var LocalVerdictSchema = z.object({
|
|
10242
|
-
assessment: z.enum(["approve", "comment", "request-changes"]),
|
|
10243
|
-
findings: z.array(FindingSchema)
|
|
10244
|
-
});
|
|
10245
|
-
var MAX_SURFACED_ERROR_LEN = 200;
|
|
10246
|
-
function sanitizeProviderError(err) {
|
|
10247
|
-
const raw = err instanceof Error ? err.message : String(err);
|
|
10248
|
-
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();
|
|
10249
|
-
const capped = redacted.length > MAX_SURFACED_ERROR_LEN ? redacted.slice(0, MAX_SURFACED_ERROR_LEN - 1) + "\u2026" : redacted;
|
|
10250
|
-
return `local runner provider error: ${capped}`;
|
|
10251
|
-
}
|
|
10252
|
-
function createLocalInvoke() {
|
|
10253
|
-
return async ({ endpoint, model, instruction, diff: diff2 }) => {
|
|
10254
|
-
if (!/^https?:\/\//i.test(endpoint)) {
|
|
10255
|
-
throw new Error(
|
|
10256
|
-
`HARNESS_LOCAL_ENDPOINT must be an http(s) URL (got '${endpoint}'); e.g. http://localhost:1234/v1`
|
|
10257
|
-
);
|
|
10258
|
-
}
|
|
10259
|
-
const provider = new OpenAICompatibleAnalysisProvider({
|
|
10260
|
-
apiKey: process.env.HARNESS_LOCAL_API_KEY ?? "local",
|
|
10261
|
-
baseUrl: endpoint,
|
|
10262
|
-
defaultModel: model
|
|
10263
|
-
});
|
|
10264
|
-
try {
|
|
10265
|
-
const { result } = await provider.analyze({
|
|
10266
|
-
prompt: `${instruction}
|
|
10267
|
-
|
|
10268
|
-
---
|
|
10269
|
-
DIFF UNDER REVIEW:
|
|
10270
|
-
${diff2}`,
|
|
10271
|
-
responseSchema: LocalVerdictSchema,
|
|
10272
|
-
model
|
|
10273
|
-
});
|
|
10274
|
-
return JSON.stringify(result);
|
|
10275
|
-
} catch (err) {
|
|
10276
|
-
throw new Error(sanitizeProviderError(err), { cause: err });
|
|
10277
|
-
}
|
|
10278
|
-
};
|
|
10279
|
-
}
|
|
10280
|
-
|
|
10281
|
-
// src/commands/review-ci.ts
|
|
10282
|
-
var KNOWN_RUNNERS = Object.keys(RUNNER_PRESETS);
|
|
10283
|
-
var BLOCK_ON_LEVELS = [...CI_ASSESSMENTS, "none"];
|
|
10284
|
-
function assertKnownRunner(runner) {
|
|
10285
|
-
if (runner === void 0) return;
|
|
10286
|
-
if (!KNOWN_RUNNERS.includes(runner)) {
|
|
10287
|
-
throw new Error(`unknown runner '${runner}' (expected: ${KNOWN_RUNNERS.join("|")})`);
|
|
10288
|
-
}
|
|
10289
|
-
}
|
|
10290
|
-
var defaultRunGit = (args) => execFileSync6("git", args, { encoding: "utf-8" }).toString().trim();
|
|
10291
|
-
function resolveDiffRange(opts) {
|
|
10292
|
-
if (opts.range) return opts.range;
|
|
10293
|
-
const runGit = opts.runGit ?? defaultRunGit;
|
|
10294
|
-
let base = "main";
|
|
10295
|
-
try {
|
|
10296
|
-
const ref = runGit(["symbolic-ref", "refs/remotes/origin/HEAD"]);
|
|
10297
|
-
const m = ref.match(/origin\/(.+)$/);
|
|
10298
|
-
if (m?.[1]) base = m[1];
|
|
10299
|
-
} catch {
|
|
10300
|
-
}
|
|
10301
|
-
return `origin/${base}...HEAD`;
|
|
10302
|
-
}
|
|
10303
|
-
function splitDiffByFile(rawDiff) {
|
|
10304
|
-
const sections = /* @__PURE__ */ new Map();
|
|
10305
|
-
const headerRe = /^diff --git a\/(.+?) b\/(.+?)$/;
|
|
10306
|
-
let currentPath = null;
|
|
10307
|
-
let buffer = [];
|
|
10308
|
-
const flush = () => {
|
|
10309
|
-
if (currentPath !== null) sections.set(currentPath, buffer.join("\n"));
|
|
10310
|
-
};
|
|
10311
|
-
for (const line of rawDiff.split("\n")) {
|
|
10312
|
-
const m = line.match(headerRe);
|
|
10313
|
-
if (m?.[2]) {
|
|
10314
|
-
flush();
|
|
10315
|
-
currentPath = m[2];
|
|
10316
|
-
buffer = [line];
|
|
10317
|
-
} else if (currentPath !== null) {
|
|
10318
|
-
buffer.push(line);
|
|
10319
|
-
}
|
|
10320
|
-
}
|
|
10321
|
-
flush();
|
|
10322
|
-
return sections;
|
|
10323
|
-
}
|
|
10324
|
-
function buildDiffInfo(rawDiff) {
|
|
10325
|
-
const parsed = parseDiff(rawDiff);
|
|
10326
|
-
if (!parsed.ok) throw new Error(`Failed to parse diff: ${parsed.error.message}`);
|
|
10327
|
-
const files = parsed.value.files;
|
|
10328
|
-
const perFile = splitDiffByFile(rawDiff);
|
|
10329
|
-
return {
|
|
10330
|
-
changedFiles: files.map((f) => f.path),
|
|
10331
|
-
newFiles: files.filter((f) => f.status === "added").map((f) => f.path),
|
|
10332
|
-
deletedFiles: files.filter((f) => f.status === "deleted").map((f) => f.path),
|
|
10333
|
-
totalDiffLines: rawDiff ? rawDiff.split("\n").length : 0,
|
|
10334
|
-
// On a path-key miss, fall back to '' (the file contributes no per-file diff)
|
|
10335
|
-
// rather than the ENTIRE raw diff — substituting the whole diff would duplicate
|
|
10336
|
-
// every file's content into one section, inflating the STDIN payload to the LLM
|
|
10337
|
-
// (and risking core's execMaxStdoutBytes). With splitDiffByFile aligned to core's
|
|
10338
|
-
// path extraction, ordinary paths never miss; this is the safe degenerate case.
|
|
10339
|
-
fileDiffs: new Map(files.map((f) => [f.path, perFile.get(f.path) ?? ""]))
|
|
10340
|
-
};
|
|
10341
|
-
}
|
|
10342
|
-
function defaultResolveRaw(range, _cwd, runGit) {
|
|
10343
|
-
return runGit(["diff", range]);
|
|
10344
|
-
}
|
|
10345
|
-
function buildCallOpts(opts, cwd, diff2) {
|
|
10346
|
-
const runner = opts.runner;
|
|
10627
|
+
async function runRecommend(options) {
|
|
10628
|
+
const cwd = options.cwd ?? process.cwd();
|
|
10629
|
+
const top = options.top ?? 5;
|
|
10630
|
+
const { snapshot, usedCache } = await resolveSnapshot(cwd, options.noCache);
|
|
10631
|
+
const skills = buildSkillsRecord(cwd);
|
|
10632
|
+
const result = recommend(snapshot, skills, { top });
|
|
10347
10633
|
return {
|
|
10348
|
-
|
|
10349
|
-
|
|
10350
|
-
...runner ? { runner } : {},
|
|
10351
|
-
...opts.blockOn ? { blockOn: opts.blockOn } : {},
|
|
10352
|
-
...runner === "local" ? { localInvoke: opts.localInvoke ?? createLocalInvoke() } : {}
|
|
10634
|
+
...result,
|
|
10635
|
+
snapshotAge: usedCache ? "cached" : "fresh"
|
|
10353
10636
|
};
|
|
10354
10637
|
}
|
|
10355
|
-
|
|
10356
|
-
|
|
10357
|
-
|
|
10358
|
-
|
|
10359
|
-
|
|
10360
|
-
|
|
10361
|
-
|
|
10362
|
-
|
|
10363
|
-
|
|
10364
|
-
|
|
10365
|
-
|
|
10366
|
-
const callOpts = buildCallOpts(opts, cwd, diff2);
|
|
10367
|
-
return (opts.runCiReviewImpl ?? runCiReview)(callOpts);
|
|
10368
|
-
}
|
|
10369
|
-
function findingLine(f) {
|
|
10370
|
-
const loc = f.lineRange ? `${f.file}:${f.lineRange[0]}` : f.file;
|
|
10371
|
-
return `- \`${f.severity}\` **${loc}** \u2014 ${f.title}`;
|
|
10638
|
+
function formatRecommendation(rec) {
|
|
10639
|
+
const lines = [];
|
|
10640
|
+
if (rec.urgency === "critical") {
|
|
10641
|
+
lines.push(` ${chalk13.red("[CRITICAL]")} ${rec.sequence}. ${rec.skillName}`);
|
|
10642
|
+
} else {
|
|
10643
|
+
lines.push(` ${rec.sequence}. ${rec.skillName} (${rec.score.toFixed(2)})`);
|
|
10644
|
+
}
|
|
10645
|
+
for (const reason of rec.reasons) {
|
|
10646
|
+
lines.push(` ${chalk13.dim("\u2192")} ${reason}`);
|
|
10647
|
+
}
|
|
10648
|
+
return lines.join("\n");
|
|
10372
10649
|
}
|
|
10373
|
-
function
|
|
10374
|
-
|
|
10375
|
-
|
|
10376
|
-
|
|
10377
|
-
|
|
10378
|
-
|
|
10379
|
-
|
|
10380
|
-
|
|
10381
|
-
|
|
10382
|
-
|
|
10383
|
-
|
|
10384
|
-
|
|
10385
|
-
|
|
10386
|
-
|
|
10387
|
-
|
|
10388
|
-
|
|
10650
|
+
function printRecommendations(result) {
|
|
10651
|
+
if (result.recommendations.length === 0) {
|
|
10652
|
+
console.log("");
|
|
10653
|
+
console.log("No recommendations. Codebase health looks good!");
|
|
10654
|
+
console.log("");
|
|
10655
|
+
return;
|
|
10656
|
+
}
|
|
10657
|
+
console.log("");
|
|
10658
|
+
console.log(
|
|
10659
|
+
`Recommended workflow (${result.recommendations.length} skill${result.recommendations.length === 1 ? "" : "s"}):`
|
|
10660
|
+
);
|
|
10661
|
+
console.log("");
|
|
10662
|
+
for (const rec of result.recommendations) {
|
|
10663
|
+
console.log(formatRecommendation(rec));
|
|
10664
|
+
console.log("");
|
|
10665
|
+
}
|
|
10666
|
+
console.log(`Sequence reasoning: ${result.sequenceReasoning}`);
|
|
10667
|
+
console.log("");
|
|
10389
10668
|
}
|
|
10390
|
-
|
|
10391
|
-
|
|
10392
|
-
|
|
10393
|
-
|
|
10394
|
-
encoding: "utf-8"
|
|
10395
|
-
});
|
|
10396
|
-
};
|
|
10397
|
-
function emitReviewCi(result, opts, writeFile2 = (p, d) => writeFileSync19(p, d), log = (m) => process.stdout.write(m + "\n"), postReview = defaultPostReview) {
|
|
10398
|
-
const serialized = JSON.stringify(result.verdict, null, 2);
|
|
10399
|
-
const jsonToStdout = opts.jsonPath === true;
|
|
10400
|
-
if (!jsonToStdout) log(result.terminalOutput);
|
|
10401
|
-
if (typeof opts.jsonPath === "string") writeFile2(opts.jsonPath, serialized);
|
|
10402
|
-
else if (jsonToStdout) log(serialized);
|
|
10403
|
-
if (opts.comment) {
|
|
10669
|
+
function createRecommendCommand() {
|
|
10670
|
+
const command = new Command82("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) => {
|
|
10671
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
10672
|
+
const mode = globalOpts.json ? OutputMode.JSON : OutputMode.TEXT;
|
|
10404
10673
|
try {
|
|
10405
|
-
|
|
10674
|
+
const top = parseInt(opts.top, 10);
|
|
10675
|
+
if (isNaN(top) || top < 1) {
|
|
10676
|
+
logger.error("--top must be a positive integer");
|
|
10677
|
+
process.exit(1);
|
|
10678
|
+
}
|
|
10679
|
+
if (mode === OutputMode.TEXT) {
|
|
10680
|
+
console.log("");
|
|
10681
|
+
console.log("Analyzing codebase health...");
|
|
10682
|
+
}
|
|
10683
|
+
const result = await runRecommend({
|
|
10684
|
+
noCache: opts.cache === false,
|
|
10685
|
+
top
|
|
10686
|
+
});
|
|
10687
|
+
if (mode === OutputMode.JSON) {
|
|
10688
|
+
console.log(JSON.stringify(result, null, 2));
|
|
10689
|
+
} else {
|
|
10690
|
+
printRecommendations(result);
|
|
10691
|
+
}
|
|
10406
10692
|
} catch (err) {
|
|
10407
|
-
|
|
10408
|
-
|
|
10409
|
-
|
|
10693
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
10694
|
+
if (mode === OutputMode.JSON) {
|
|
10695
|
+
console.log(JSON.stringify({ error: message }));
|
|
10696
|
+
} else {
|
|
10697
|
+
logger.error(message);
|
|
10698
|
+
}
|
|
10699
|
+
process.exit(1);
|
|
10410
10700
|
}
|
|
10411
|
-
}
|
|
10412
|
-
}
|
|
10413
|
-
function createReviewCiCommand() {
|
|
10414
|
-
return new Command81("review-ci").description("Run the tiered code-review gate (floor + optional LLM runner) for CI").addOption(
|
|
10415
|
-
new Option2("--runner <runner>", `${KNOWN_RUNNERS.join(" | ")} (omit = floor-only)`).choices(
|
|
10416
|
-
KNOWN_RUNNERS
|
|
10417
|
-
)
|
|
10418
|
-
).addOption(
|
|
10419
|
-
new Option2("--block-on <level>", BLOCK_ON_LEVELS.join(" | ")).choices(BLOCK_ON_LEVELS).default("request-changes")
|
|
10420
|
-
).option("--diff <range>", "git range (default: origin/<base>...HEAD)").option("--comment", "post the verdict as a comment on the current branch's PR via gh").option(
|
|
10421
|
-
"--out <path>",
|
|
10422
|
-
"write the verdict JSON artifact to a file (use the global --json to stream it to stdout instead)"
|
|
10423
|
-
).action(async (opts, cmd) => {
|
|
10424
|
-
const result = await runReviewCi({
|
|
10425
|
-
runner: opts.runner,
|
|
10426
|
-
blockOn: opts.blockOn,
|
|
10427
|
-
diffRange: opts.diff
|
|
10428
|
-
});
|
|
10429
|
-
const streamJson = cmd.optsWithGlobals().json === true;
|
|
10430
|
-
emitReviewCi(result, {
|
|
10431
|
-
jsonPath: typeof opts.out === "string" ? opts.out : streamJson ? true : void 0,
|
|
10432
|
-
comment: opts.comment
|
|
10433
|
-
});
|
|
10434
|
-
process.exit(result.exitCode);
|
|
10435
10701
|
});
|
|
10702
|
+
return command;
|
|
10436
10703
|
}
|
|
10437
10704
|
|
|
10438
10705
|
// src/commands/roadmap/index.ts
|
|
10439
|
-
import { Command as
|
|
10706
|
+
import { Command as Command89 } from "commander";
|
|
10440
10707
|
|
|
10441
10708
|
// src/commands/roadmap/migrate.ts
|
|
10442
10709
|
init_dist3();
|
|
10443
|
-
import { Command as
|
|
10710
|
+
import { Command as Command83 } from "commander";
|
|
10444
10711
|
import * as fs33 from "fs";
|
|
10445
10712
|
import * as path54 from "path";
|
|
10446
10713
|
import chalk14 from "chalk";
|
|
@@ -10850,7 +11117,7 @@ async function runReverseMigrate(opts) {
|
|
|
10850
11117
|
}
|
|
10851
11118
|
}
|
|
10852
11119
|
function createRoadmapMigrateCommand() {
|
|
10853
|
-
return new
|
|
11120
|
+
return new Command83("migrate").description("Migrate the project roadmap to a different storage mode").requiredOption("--to <target>", 'Migration target: "file-less" or "file-backed"').option("--dry-run", "Print the migration plan without making any changes", false).option(
|
|
10854
11121
|
"--format <fmt>",
|
|
10855
11122
|
'Output format: "human" (default) or "json" (single JSON object for CI consumers)',
|
|
10856
11123
|
"human"
|
|
@@ -10885,7 +11152,7 @@ function createRoadmapMigrateCommand() {
|
|
|
10885
11152
|
|
|
10886
11153
|
// src/commands/roadmap/shard.ts
|
|
10887
11154
|
init_dist3();
|
|
10888
|
-
import { Command as
|
|
11155
|
+
import { Command as Command84 } from "commander";
|
|
10889
11156
|
import * as path55 from "path";
|
|
10890
11157
|
|
|
10891
11158
|
// src/commands/roadmap/shard-io.ts
|
|
@@ -10984,7 +11251,7 @@ async function runRoadmapShard(opts = {}) {
|
|
|
10984
11251
|
return Ok(report);
|
|
10985
11252
|
}
|
|
10986
11253
|
function createRoadmapShardCommand() {
|
|
10987
|
-
return new
|
|
11254
|
+
return new Command84("shard").description("Migrate docs/roadmap.md to per-row shards under docs/roadmap.d").option("--cwd <dir>", "Project root (defaults to the current working directory)").option("--dry-run", "Report the migration plan without writing anything", false).option("--force", "Proceed even if docs/roadmap.d already exists", false).option(
|
|
10988
11255
|
"--format <fmt>",
|
|
10989
11256
|
'Output format: "human" (default) or "json" (single JSON object for CI consumers)',
|
|
10990
11257
|
"human"
|
|
@@ -11011,7 +11278,7 @@ function createRoadmapShardCommand() {
|
|
|
11011
11278
|
|
|
11012
11279
|
// src/commands/roadmap/unshard.ts
|
|
11013
11280
|
init_dist3();
|
|
11014
|
-
import { Command as
|
|
11281
|
+
import { Command as Command85 } from "commander";
|
|
11015
11282
|
import * as path56 from "path";
|
|
11016
11283
|
async function runRoadmapUnshard(opts = {}) {
|
|
11017
11284
|
const cwd = opts.cwd ?? process.cwd();
|
|
@@ -11043,7 +11310,7 @@ async function runRoadmapUnshard(opts = {}) {
|
|
|
11043
11310
|
return Ok(report);
|
|
11044
11311
|
}
|
|
11045
11312
|
function createRoadmapUnshardCommand() {
|
|
11046
|
-
return new
|
|
11313
|
+
return new Command85("unshard").description("Reassemble the aggregate from shards and remove docs/roadmap.d").option("--cwd <dir>", "Project root (defaults to the current working directory)").option("--dry-run", "Preview the reassembly without writing or deleting the shard dir", false).option(
|
|
11047
11314
|
"--format <fmt>",
|
|
11048
11315
|
'Output format: "human" (default) or "json" (single JSON object for CI consumers)',
|
|
11049
11316
|
"human"
|
|
@@ -11067,7 +11334,7 @@ function createRoadmapUnshardCommand() {
|
|
|
11067
11334
|
|
|
11068
11335
|
// src/commands/roadmap/regen.ts
|
|
11069
11336
|
init_dist3();
|
|
11070
|
-
import { Command as
|
|
11337
|
+
import { Command as Command86 } from "commander";
|
|
11071
11338
|
import * as path57 from "path";
|
|
11072
11339
|
async function runRoadmapRegen(opts = {}) {
|
|
11073
11340
|
const cwd = opts.cwd ?? process.cwd();
|
|
@@ -11096,7 +11363,7 @@ async function runRoadmapRegen(opts = {}) {
|
|
|
11096
11363
|
return Ok(report);
|
|
11097
11364
|
}
|
|
11098
11365
|
function createRoadmapRegenCommand() {
|
|
11099
|
-
return new
|
|
11366
|
+
return new Command86("regen").description("Regenerate the aggregate from the shard directory (docs/roadmap.d)").option("--cwd <dir>", "Project root (defaults to the current working directory)").option("--dry-run", "Report what would be regenerated without writing anything", false).option(
|
|
11100
11367
|
"--format <fmt>",
|
|
11101
11368
|
'Output format: "human" (default) or "json" (single JSON object for CI consumers)',
|
|
11102
11369
|
"human"
|
|
@@ -11120,7 +11387,7 @@ function createRoadmapRegenCommand() {
|
|
|
11120
11387
|
|
|
11121
11388
|
// src/commands/roadmap/reconcile.ts
|
|
11122
11389
|
init_dist3();
|
|
11123
|
-
import { Command as
|
|
11390
|
+
import { Command as Command87 } from "commander";
|
|
11124
11391
|
import * as fs34 from "fs";
|
|
11125
11392
|
import * as path58 from "path";
|
|
11126
11393
|
async function runRoadmapReconcile(opts = {}) {
|
|
@@ -11253,7 +11520,7 @@ function buildReconcileOptions(options) {
|
|
|
11253
11520
|
return opts;
|
|
11254
11521
|
}
|
|
11255
11522
|
function createRoadmapReconcileCommand() {
|
|
11256
|
-
return new
|
|
11523
|
+
return new Command87("reconcile").description(
|
|
11257
11524
|
"Flip roadmap rows whose linked GitHub issue is closed to done. Offline mode flips only issues closed as 'completed' (a 'not planned'/'wontfix' close is left untouched); the PR-merge auto-done workflow remains authoritative for cross-repo correctness."
|
|
11258
11525
|
).option("--cwd <dir>", "Project root (defaults to the current working directory)").option(
|
|
11259
11526
|
"--from-issues <numbers>",
|
|
@@ -11270,22 +11537,40 @@ function createRoadmapReconcileCommand() {
|
|
|
11270
11537
|
});
|
|
11271
11538
|
}
|
|
11272
11539
|
|
|
11540
|
+
// src/commands/roadmap/referenced-issues.ts
|
|
11541
|
+
init_dist3();
|
|
11542
|
+
import { Command as Command88 } from "commander";
|
|
11543
|
+
function runReferencedIssues(text, print) {
|
|
11544
|
+
for (const n of parseReferencedIssues(text)) print(String(n));
|
|
11545
|
+
}
|
|
11546
|
+
function createRoadmapReferencedIssuesCommand() {
|
|
11547
|
+
return new Command88("referenced-issues").description(
|
|
11548
|
+
"Parse issue references (#N, Closes/Fixes/Resolves #N, owner/repo#N) from stdin text (PR body + title) and print each issue number on its own line. Backstop for auto-done when a PR's closing keyword is malformed."
|
|
11549
|
+
).action(async () => {
|
|
11550
|
+
const chunks = [];
|
|
11551
|
+
for await (const c of process.stdin) chunks.push(c);
|
|
11552
|
+
const text = Buffer.concat(chunks).toString("utf8");
|
|
11553
|
+
runReferencedIssues(text, (line) => process.stdout.write(line + "\n"));
|
|
11554
|
+
});
|
|
11555
|
+
}
|
|
11556
|
+
|
|
11273
11557
|
// src/commands/roadmap/index.ts
|
|
11274
11558
|
function createRoadmapCommand() {
|
|
11275
|
-
const roadmap = new
|
|
11559
|
+
const roadmap = new Command89("roadmap").description("Roadmap management");
|
|
11276
11560
|
roadmap.addCommand(createRoadmapMigrateCommand());
|
|
11277
11561
|
roadmap.addCommand(createRoadmapShardCommand());
|
|
11278
11562
|
roadmap.addCommand(createRoadmapUnshardCommand());
|
|
11279
11563
|
roadmap.addCommand(createRoadmapRegenCommand());
|
|
11280
11564
|
roadmap.addCommand(createRoadmapReconcileCommand());
|
|
11565
|
+
roadmap.addCommand(createRoadmapReferencedIssuesCommand());
|
|
11281
11566
|
return roadmap;
|
|
11282
11567
|
}
|
|
11283
11568
|
|
|
11284
11569
|
// src/commands/routing/index.ts
|
|
11285
|
-
import { Command as
|
|
11570
|
+
import { Command as Command93 } from "commander";
|
|
11286
11571
|
|
|
11287
11572
|
// src/commands/routing/config.ts
|
|
11288
|
-
import { Command as
|
|
11573
|
+
import { Command as Command90 } from "commander";
|
|
11289
11574
|
|
|
11290
11575
|
// src/commands/routing/http-client.ts
|
|
11291
11576
|
function orchestratorBase() {
|
|
@@ -11350,7 +11635,7 @@ function renderHuman(data) {
|
|
|
11350
11635
|
}
|
|
11351
11636
|
}
|
|
11352
11637
|
function createConfigCommand() {
|
|
11353
|
-
return new
|
|
11638
|
+
return new Command90("config").description("Print active routing config and resolved fallback chains").option("--json", "Emit JSON to stdout instead of human-readable text").action(async (opts) => {
|
|
11354
11639
|
const r = await getJson("/api/v1/routing/config");
|
|
11355
11640
|
if (!r.ok) {
|
|
11356
11641
|
if (r.status === 0) {
|
|
@@ -11376,7 +11661,7 @@ function createConfigCommand() {
|
|
|
11376
11661
|
}
|
|
11377
11662
|
|
|
11378
11663
|
// src/commands/routing/trace.ts
|
|
11379
|
-
import { Command as
|
|
11664
|
+
import { Command as Command91 } from "commander";
|
|
11380
11665
|
function buildUseCase(opts) {
|
|
11381
11666
|
if (opts.skill) {
|
|
11382
11667
|
return opts.mode ? { kind: "skill", skillName: opts.skill, cognitiveMode: opts.mode } : { kind: "skill", skillName: opts.skill };
|
|
@@ -11397,7 +11682,7 @@ function renderHuman2(r) {
|
|
|
11397
11682
|
}
|
|
11398
11683
|
}
|
|
11399
11684
|
function createTraceCommand() {
|
|
11400
|
-
return new
|
|
11685
|
+
return new Command91("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) => {
|
|
11401
11686
|
const useCase = buildUseCase(opts);
|
|
11402
11687
|
if (!useCase) {
|
|
11403
11688
|
logger.error("Either --skill <name> or --mode <m> is required");
|
|
@@ -11429,7 +11714,7 @@ function createTraceCommand() {
|
|
|
11429
11714
|
}
|
|
11430
11715
|
|
|
11431
11716
|
// src/commands/routing/decisions.ts
|
|
11432
|
-
import { Command as
|
|
11717
|
+
import { Command as Command92 } from "commander";
|
|
11433
11718
|
function summarizeUseCase(uc) {
|
|
11434
11719
|
switch (uc.kind) {
|
|
11435
11720
|
case "skill":
|
|
@@ -11477,7 +11762,7 @@ function buildQuery(opts) {
|
|
|
11477
11762
|
return q ? `?${q}` : "";
|
|
11478
11763
|
}
|
|
11479
11764
|
function createDecisionsCommand() {
|
|
11480
|
-
return new
|
|
11765
|
+
return new Command92("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(
|
|
11481
11766
|
async (opts) => {
|
|
11482
11767
|
const query = buildQuery(opts);
|
|
11483
11768
|
const r = await getJson(`/api/v1/routing/decisions${query}`);
|
|
@@ -11507,7 +11792,7 @@ function createDecisionsCommand() {
|
|
|
11507
11792
|
|
|
11508
11793
|
// src/commands/routing/index.ts
|
|
11509
11794
|
function createRoutingCommand() {
|
|
11510
|
-
const cmd = new
|
|
11795
|
+
const cmd = new Command93("routing").description(
|
|
11511
11796
|
"Inspect routing config, trace decisions, and read recent dispatches"
|
|
11512
11797
|
);
|
|
11513
11798
|
cmd.addCommand(createConfigCommand());
|
|
@@ -11518,9 +11803,9 @@ function createRoutingCommand() {
|
|
|
11518
11803
|
|
|
11519
11804
|
// src/commands/scan-config.ts
|
|
11520
11805
|
init_dist3();
|
|
11521
|
-
import { Command as
|
|
11522
|
-
import { existsSync as existsSync30, readFileSync as
|
|
11523
|
-
import { join as
|
|
11806
|
+
import { Command as Command94 } from "commander";
|
|
11807
|
+
import { existsSync as existsSync30, readFileSync as readFileSync22, writeFileSync as writeFileSync22 } from "fs";
|
|
11808
|
+
import { join as join49, relative as relative3 } from "path";
|
|
11524
11809
|
var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".gemini/settings.json", "skill.yaml"];
|
|
11525
11810
|
function stripHighSeverityPatterns(content, injectionFindings) {
|
|
11526
11811
|
const highLines = /* @__PURE__ */ new Set();
|
|
@@ -11556,7 +11841,7 @@ function scanSingleFile(filePath, targetDir, scanner, options) {
|
|
|
11556
11841
|
if (!existsSync30(filePath)) return null;
|
|
11557
11842
|
let content;
|
|
11558
11843
|
try {
|
|
11559
|
-
content =
|
|
11844
|
+
content = readFileSync22(filePath, "utf8");
|
|
11560
11845
|
} catch {
|
|
11561
11846
|
return null;
|
|
11562
11847
|
}
|
|
@@ -11577,7 +11862,7 @@ async function runScanConfig(targetDir, options) {
|
|
|
11577
11862
|
const scanner = new SecurityScanner(parseSecurityConfig({}));
|
|
11578
11863
|
const results = [];
|
|
11579
11864
|
for (const configFile of CONFIG_FILES) {
|
|
11580
|
-
const result = scanSingleFile(
|
|
11865
|
+
const result = scanSingleFile(join49(targetDir, configFile), targetDir, scanner, options);
|
|
11581
11866
|
if (result) results.push(result);
|
|
11582
11867
|
}
|
|
11583
11868
|
return { exitCode: computeScanExitCode(results), results };
|
|
@@ -11607,7 +11892,7 @@ function formatTextOutput(result) {
|
|
|
11607
11892
|
}
|
|
11608
11893
|
}
|
|
11609
11894
|
function createScanConfigCommand() {
|
|
11610
|
-
const command = new
|
|
11895
|
+
const command = new Command94("scan-config").description(
|
|
11611
11896
|
"Scan CLAUDE.md, AGENTS.md, .gemini/settings.json, and skill.yaml for prompt injection patterns"
|
|
11612
11897
|
).option("--path <dir>", "Target directory to scan (default: cwd)").option("--fix", "Strip high-severity patterns from files in-place").action(async (opts, cmd) => {
|
|
11613
11898
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -11625,7 +11910,7 @@ function createScanConfigCommand() {
|
|
|
11625
11910
|
|
|
11626
11911
|
// src/commands/search.ts
|
|
11627
11912
|
init_dist();
|
|
11628
|
-
import { Command as
|
|
11913
|
+
import { Command as Command95 } from "commander";
|
|
11629
11914
|
function parseFileKinds(raw) {
|
|
11630
11915
|
if (!raw) return void 0;
|
|
11631
11916
|
const set = /* @__PURE__ */ new Set();
|
|
@@ -11646,7 +11931,7 @@ function fmtBytes(n) {
|
|
|
11646
11931
|
return n.toFixed(0);
|
|
11647
11932
|
}
|
|
11648
11933
|
function createSearchCommand() {
|
|
11649
|
-
return new
|
|
11934
|
+
return new Command95("search").description("Full-text search over archived + live session content.").argument(
|
|
11650
11935
|
"<query>",
|
|
11651
11936
|
"FTS5 query (bare words AND-joined; quotes/AND/OR/NOT/column: for advanced syntax)"
|
|
11652
11937
|
).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(
|
|
@@ -11706,9 +11991,9 @@ function createSearchCommand() {
|
|
|
11706
11991
|
}
|
|
11707
11992
|
|
|
11708
11993
|
// src/commands/security-craft.ts
|
|
11709
|
-
import { Command as
|
|
11994
|
+
import { Command as Command96 } from "commander";
|
|
11710
11995
|
function createSecurityCraftCommand() {
|
|
11711
|
-
return new
|
|
11996
|
+
return new Command96("security-craft").description(
|
|
11712
11997
|
"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."
|
|
11713
11998
|
).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) => {
|
|
11714
11999
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -11774,7 +12059,7 @@ ${file}`);
|
|
|
11774
12059
|
}
|
|
11775
12060
|
|
|
11776
12061
|
// src/commands/setup.ts
|
|
11777
|
-
import { Command as
|
|
12062
|
+
import { Command as Command97 } from "commander";
|
|
11778
12063
|
import * as fs37 from "fs";
|
|
11779
12064
|
import * as os9 from "os";
|
|
11780
12065
|
import * as path61 from "path";
|
|
@@ -12119,7 +12404,7 @@ async function runSetup(cwd) {
|
|
|
12119
12404
|
return { steps, success };
|
|
12120
12405
|
}
|
|
12121
12406
|
function createSetupCommand() {
|
|
12122
|
-
return new
|
|
12407
|
+
return new Command97("setup").description("Configure harness environment: slash commands, MCP, and more").action(async () => {
|
|
12123
12408
|
const cwd = process.cwd();
|
|
12124
12409
|
console.log("");
|
|
12125
12410
|
console.log(` ${chalk16.bold("harness setup")}`);
|
|
@@ -12142,7 +12427,7 @@ function createSetupCommand() {
|
|
|
12142
12427
|
|
|
12143
12428
|
// src/commands/share.ts
|
|
12144
12429
|
init_dist3();
|
|
12145
|
-
import { Command as
|
|
12430
|
+
import { Command as Command98 } from "commander";
|
|
12146
12431
|
import * as fs38 from "fs";
|
|
12147
12432
|
import * as path62 from "path";
|
|
12148
12433
|
import { parse as parseYaml3 } from "yaml";
|
|
@@ -12201,16 +12486,16 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
12201
12486
|
logger.success(`Bundle written to ${outputPath}`);
|
|
12202
12487
|
}
|
|
12203
12488
|
function createShareCommand() {
|
|
12204
|
-
return new
|
|
12489
|
+
return new Command98("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) => {
|
|
12205
12490
|
await runShareAction(projectPath, options);
|
|
12206
12491
|
});
|
|
12207
12492
|
}
|
|
12208
12493
|
|
|
12209
12494
|
// src/commands/skill/index.ts
|
|
12210
|
-
import { Command as
|
|
12495
|
+
import { Command as Command106 } from "commander";
|
|
12211
12496
|
|
|
12212
12497
|
// src/commands/skill/list.ts
|
|
12213
|
-
import { Command as
|
|
12498
|
+
import { Command as Command99 } from "commander";
|
|
12214
12499
|
import * as fs39 from "fs";
|
|
12215
12500
|
import * as path63 from "path";
|
|
12216
12501
|
import { parse } from "yaml";
|
|
@@ -12313,7 +12598,7 @@ function printSkillsVerbose(skills) {
|
|
|
12313
12598
|
for (const s of skills) printSkillEntry(s);
|
|
12314
12599
|
}
|
|
12315
12600
|
function createListCommand3() {
|
|
12316
|
-
return new
|
|
12601
|
+
return new Command99("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) => {
|
|
12317
12602
|
const globalOpts = cmd.optsWithGlobals();
|
|
12318
12603
|
const skills = collectSkills({ filter: resolveFilter(opts) });
|
|
12319
12604
|
if (globalOpts.json) {
|
|
@@ -12328,13 +12613,13 @@ function createListCommand3() {
|
|
|
12328
12613
|
}
|
|
12329
12614
|
|
|
12330
12615
|
// src/commands/skill/run.ts
|
|
12331
|
-
import { Command as
|
|
12616
|
+
import { Command as Command100 } from "commander";
|
|
12332
12617
|
import * as fs40 from "fs";
|
|
12333
12618
|
import * as path64 from "path";
|
|
12334
12619
|
import { parse as parse2 } from "yaml";
|
|
12335
12620
|
|
|
12336
12621
|
// src/skill/complexity.ts
|
|
12337
|
-
import { execFileSync as
|
|
12622
|
+
import { execFileSync as execFileSync8 } from "child_process";
|
|
12338
12623
|
function evaluateSignals(signals) {
|
|
12339
12624
|
if (signals.fileCount >= 3) return "thorough";
|
|
12340
12625
|
if (signals.newDir) return "thorough";
|
|
@@ -12346,17 +12631,17 @@ function evaluateSignals(signals) {
|
|
|
12346
12631
|
}
|
|
12347
12632
|
function detectComplexity(projectPath) {
|
|
12348
12633
|
try {
|
|
12349
|
-
const base =
|
|
12634
|
+
const base = execFileSync8("git", ["merge-base", "HEAD", "main"], {
|
|
12350
12635
|
cwd: projectPath,
|
|
12351
12636
|
encoding: "utf-8",
|
|
12352
12637
|
stdio: ["pipe", "pipe", "pipe"]
|
|
12353
12638
|
}).trim();
|
|
12354
|
-
const diffFiles =
|
|
12639
|
+
const diffFiles = execFileSync8("git", ["diff", "--name-only", base], {
|
|
12355
12640
|
cwd: projectPath,
|
|
12356
12641
|
encoding: "utf-8",
|
|
12357
12642
|
stdio: ["pipe", "pipe", "pipe"]
|
|
12358
12643
|
}).trim().split("\n").filter(Boolean);
|
|
12359
|
-
const diffStat =
|
|
12644
|
+
const diffStat = execFileSync8("git", ["diff", "--stat", base], {
|
|
12360
12645
|
cwd: projectPath,
|
|
12361
12646
|
encoding: "utf-8",
|
|
12362
12647
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -12476,7 +12761,7 @@ function resolvePhaseState(metadata, projectPath, phase) {
|
|
|
12476
12761
|
}
|
|
12477
12762
|
async function appendProjectState(content, metadata, projectPath, hasPathOpt) {
|
|
12478
12763
|
if (!metadata?.state.persistent || !hasPathOpt) return content;
|
|
12479
|
-
const { readHarnessState: readHarnessState2, isEmptyHarnessState } = await import("./state-events-
|
|
12764
|
+
const { readHarnessState: readHarnessState2, isEmptyHarnessState } = await import("./state-events-YY5KBZTC.js");
|
|
12480
12765
|
const stateResult = await readHarnessState2(projectPath);
|
|
12481
12766
|
if (!stateResult.ok || isEmptyHarnessState(stateResult.value)) return content;
|
|
12482
12767
|
const stateContent = JSON.stringify(stateResult.value, null, 2);
|
|
@@ -12542,14 +12827,14 @@ async function runSkill(name, opts) {
|
|
|
12542
12827
|
process.exit(ExitCode.SUCCESS);
|
|
12543
12828
|
}
|
|
12544
12829
|
function createRunCommand3() {
|
|
12545
|
-
return new
|
|
12830
|
+
return new Command100("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(
|
|
12546
12831
|
"--backend <name>",
|
|
12547
12832
|
"Spec B: one-shot routing override forwarded to the orchestrator as HARNESS_BACKEND_OVERRIDE"
|
|
12548
12833
|
).action(async (name, opts) => runSkill(name, opts));
|
|
12549
12834
|
}
|
|
12550
12835
|
|
|
12551
12836
|
// src/commands/skill/validate.ts
|
|
12552
|
-
import { Command as
|
|
12837
|
+
import { Command as Command101 } from "commander";
|
|
12553
12838
|
import * as fs41 from "fs";
|
|
12554
12839
|
import * as path65 from "path";
|
|
12555
12840
|
import { parse as parse3 } from "yaml";
|
|
@@ -12613,7 +12898,7 @@ function validateSkillEntry(name, skillsDir, errors) {
|
|
|
12613
12898
|
}
|
|
12614
12899
|
}
|
|
12615
12900
|
function createValidateCommand2() {
|
|
12616
|
-
return new
|
|
12901
|
+
return new Command101("validate").description("Validate all skill.yaml files and SKILL.md structure").action(async (_opts, cmd) => {
|
|
12617
12902
|
const globalOpts = cmd.optsWithGlobals();
|
|
12618
12903
|
const skillsDir = resolveSkillsDir();
|
|
12619
12904
|
if (!fs41.existsSync(skillsDir)) {
|
|
@@ -12643,7 +12928,7 @@ function createValidateCommand2() {
|
|
|
12643
12928
|
}
|
|
12644
12929
|
|
|
12645
12930
|
// src/commands/skill/info.ts
|
|
12646
|
-
import { Command as
|
|
12931
|
+
import { Command as Command102 } from "commander";
|
|
12647
12932
|
import * as fs42 from "fs";
|
|
12648
12933
|
import * as path66 from "path";
|
|
12649
12934
|
import { parse as parse4 } from "yaml";
|
|
@@ -12688,7 +12973,7 @@ function printSkillInfo(skill) {
|
|
|
12688
12973
|
console.log(`Persistent: ${skill.state.persistent}`);
|
|
12689
12974
|
}
|
|
12690
12975
|
function createInfoCommand() {
|
|
12691
|
-
return new
|
|
12976
|
+
return new Command102("info").description("Show metadata for a skill").argument("<name>", "Skill name (e.g., harness-tdd)").action(async (name, _opts, cmd) => {
|
|
12692
12977
|
const globalOpts = cmd.optsWithGlobals();
|
|
12693
12978
|
const loaded = loadSkillMetadata2(name);
|
|
12694
12979
|
if (!loaded.ok) {
|
|
@@ -12705,7 +12990,7 @@ function createInfoCommand() {
|
|
|
12705
12990
|
}
|
|
12706
12991
|
|
|
12707
12992
|
// src/commands/skill/search.ts
|
|
12708
|
-
import { Command as
|
|
12993
|
+
import { Command as Command103 } from "commander";
|
|
12709
12994
|
async function runSearch(query, opts) {
|
|
12710
12995
|
const results = await searchNpmRegistry(query, opts.registry);
|
|
12711
12996
|
return results.filter((r) => {
|
|
@@ -12750,13 +13035,13 @@ async function runSearchAction(query, opts, globalOpts) {
|
|
|
12750
13035
|
}
|
|
12751
13036
|
}
|
|
12752
13037
|
function createSearchCommand2() {
|
|
12753
|
-
return new
|
|
13038
|
+
return new Command103("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) => {
|
|
12754
13039
|
await runSearchAction(query, opts, cmd.optsWithGlobals());
|
|
12755
13040
|
});
|
|
12756
13041
|
}
|
|
12757
13042
|
|
|
12758
13043
|
// src/commands/skill/create.ts
|
|
12759
|
-
import { Command as
|
|
13044
|
+
import { Command as Command104 } from "commander";
|
|
12760
13045
|
import * as path67 from "path";
|
|
12761
13046
|
import * as fs43 from "fs";
|
|
12762
13047
|
import YAML from "yaml";
|
|
@@ -12876,7 +13161,7 @@ Next steps:`);
|
|
|
12876
13161
|
logger.info(` 3. Run: harness skills publish`);
|
|
12877
13162
|
}
|
|
12878
13163
|
function createCreateCommand() {
|
|
12879
|
-
return new
|
|
13164
|
+
return new Command104("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) => {
|
|
12880
13165
|
const globalOpts = cmd.optsWithGlobals();
|
|
12881
13166
|
try {
|
|
12882
13167
|
const result = runCreate(name, {
|
|
@@ -12899,10 +13184,10 @@ function createCreateCommand() {
|
|
|
12899
13184
|
}
|
|
12900
13185
|
|
|
12901
13186
|
// src/commands/skill/publish.ts
|
|
12902
|
-
import { Command as
|
|
13187
|
+
import { Command as Command105 } from "commander";
|
|
12903
13188
|
import * as fs45 from "fs";
|
|
12904
13189
|
import * as path69 from "path";
|
|
12905
|
-
import { execFileSync as
|
|
13190
|
+
import { execFileSync as execFileSync9 } from "child_process";
|
|
12906
13191
|
|
|
12907
13192
|
// src/registry/validator.ts
|
|
12908
13193
|
import * as fs44 from "fs";
|
|
@@ -13056,7 +13341,7 @@ ${skillMdContent}`;
|
|
|
13056
13341
|
if (opts.registry) {
|
|
13057
13342
|
publishArgs.push("--registry", opts.registry);
|
|
13058
13343
|
}
|
|
13059
|
-
|
|
13344
|
+
execFileSync9("npm", publishArgs, {
|
|
13060
13345
|
cwd: skillDir,
|
|
13061
13346
|
stdio: "pipe",
|
|
13062
13347
|
timeout: 6e4
|
|
@@ -13068,7 +13353,7 @@ ${skillMdContent}`;
|
|
|
13068
13353
|
};
|
|
13069
13354
|
}
|
|
13070
13355
|
function createPublishCommand() {
|
|
13071
|
-
return new
|
|
13356
|
+
return new Command105("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) => {
|
|
13072
13357
|
const globalOpts = cmd.optsWithGlobals();
|
|
13073
13358
|
const skillDir = opts.dir || process.cwd();
|
|
13074
13359
|
try {
|
|
@@ -13093,7 +13378,7 @@ function createPublishCommand() {
|
|
|
13093
13378
|
|
|
13094
13379
|
// src/commands/skill/index.ts
|
|
13095
13380
|
function createSkillCommand() {
|
|
13096
|
-
const command = new
|
|
13381
|
+
const command = new Command106("skill").description("Skill management commands");
|
|
13097
13382
|
command.addCommand(createListCommand3());
|
|
13098
13383
|
command.addCommand(createRunCommand3());
|
|
13099
13384
|
command.addCommand(createValidateCommand2());
|
|
@@ -13106,7 +13391,7 @@ function createSkillCommand() {
|
|
|
13106
13391
|
|
|
13107
13392
|
// src/commands/snapshot.ts
|
|
13108
13393
|
init_dist3();
|
|
13109
|
-
import { Command as
|
|
13394
|
+
import { Command as Command107 } from "commander";
|
|
13110
13395
|
import { execSync as execSync5 } from "child_process";
|
|
13111
13396
|
import chalk17 from "chalk";
|
|
13112
13397
|
function getCommitHash3(cwd) {
|
|
@@ -13288,7 +13573,7 @@ function registerListCommand2(parent) {
|
|
|
13288
13573
|
});
|
|
13289
13574
|
}
|
|
13290
13575
|
function createSnapshotCommand() {
|
|
13291
|
-
const command = new
|
|
13576
|
+
const command = new Command107("snapshot").description("Architecture timeline snapshot commands");
|
|
13292
13577
|
registerCaptureCommand(command);
|
|
13293
13578
|
registerTrendsCommand(command);
|
|
13294
13579
|
registerListCommand2(command);
|
|
@@ -13296,9 +13581,9 @@ function createSnapshotCommand() {
|
|
|
13296
13581
|
}
|
|
13297
13582
|
|
|
13298
13583
|
// src/commands/spec-craft.ts
|
|
13299
|
-
import { Command as
|
|
13584
|
+
import { Command as Command108 } from "commander";
|
|
13300
13585
|
function createSpecCraftCommand() {
|
|
13301
|
-
return new
|
|
13586
|
+
return new Command108("spec-craft").description(
|
|
13302
13587
|
"LLM-judgment critique of spec quality (proposals + ADRs). Second craft-pipeline ceiling skill; 7 seed rubrics from the spec-quality canon. Per-section critique."
|
|
13303
13588
|
).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) => {
|
|
13304
13589
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -13366,7 +13651,7 @@ ${file}`);
|
|
|
13366
13651
|
|
|
13367
13652
|
// src/commands/stale-constraints.ts
|
|
13368
13653
|
init_dist3();
|
|
13369
|
-
import { Command as
|
|
13654
|
+
import { Command as Command109 } from "commander";
|
|
13370
13655
|
import chalk18 from "chalk";
|
|
13371
13656
|
var CATEGORIES = [
|
|
13372
13657
|
"circular-deps",
|
|
@@ -13453,7 +13738,7 @@ async function runStaleConstraintsCommand(options) {
|
|
|
13453
13738
|
);
|
|
13454
13739
|
}
|
|
13455
13740
|
function createStaleConstraintsCommand() {
|
|
13456
|
-
return new
|
|
13741
|
+
return new Command109("stale-constraints").description(
|
|
13457
13742
|
"Detect architectural constraints not violated within a window (candidates for removal)"
|
|
13458
13743
|
).option(
|
|
13459
13744
|
"--window <days>",
|
|
@@ -13471,10 +13756,10 @@ function createStaleConstraintsCommand() {
|
|
|
13471
13756
|
}
|
|
13472
13757
|
|
|
13473
13758
|
// src/commands/state/index.ts
|
|
13474
|
-
import { Command as
|
|
13759
|
+
import { Command as Command114 } from "commander";
|
|
13475
13760
|
|
|
13476
13761
|
// src/commands/state/show.ts
|
|
13477
|
-
import { Command as
|
|
13762
|
+
import { Command as Command110 } from "commander";
|
|
13478
13763
|
import * as path70 from "path";
|
|
13479
13764
|
function printStateText(state, stream) {
|
|
13480
13765
|
if (stream) console.log(`Stream: ${stream}`);
|
|
@@ -13499,7 +13784,7 @@ function printStateProgress(progress) {
|
|
|
13499
13784
|
}
|
|
13500
13785
|
}
|
|
13501
13786
|
function createShowCommand() {
|
|
13502
|
-
return new
|
|
13787
|
+
return new Command110("show").description("Show current project state").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(async (opts, cmd) => {
|
|
13503
13788
|
const globalOpts = cmd.optsWithGlobals();
|
|
13504
13789
|
const result = await readHarnessState(path70.resolve(opts.path), { stream: opts.stream });
|
|
13505
13790
|
if (!result.ok) {
|
|
@@ -13517,7 +13802,7 @@ function createShowCommand() {
|
|
|
13517
13802
|
|
|
13518
13803
|
// src/commands/state/reset.ts
|
|
13519
13804
|
init_dist3();
|
|
13520
|
-
import { Command as
|
|
13805
|
+
import { Command as Command111 } from "commander";
|
|
13521
13806
|
import * as path71 from "path";
|
|
13522
13807
|
import * as readline3 from "readline";
|
|
13523
13808
|
async function confirmReset() {
|
|
@@ -13543,15 +13828,15 @@ async function runReset(opts) {
|
|
|
13543
13828
|
process.exit(ExitCode.SUCCESS);
|
|
13544
13829
|
}
|
|
13545
13830
|
function createResetCommand() {
|
|
13546
|
-
return new
|
|
13831
|
+
return new Command111("reset").description("Reset project state (truncates the event log to a fresh default)").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").option("--yes", "Skip confirmation prompt").action(async (opts) => runReset(opts));
|
|
13547
13832
|
}
|
|
13548
13833
|
|
|
13549
13834
|
// src/commands/state/learn.ts
|
|
13550
13835
|
init_dist3();
|
|
13551
|
-
import { Command as
|
|
13836
|
+
import { Command as Command112 } from "commander";
|
|
13552
13837
|
import * as path72 from "path";
|
|
13553
13838
|
function createLearnCommand() {
|
|
13554
|
-
return new
|
|
13839
|
+
return new Command112("learn").description("Append a learning to .harness/learnings.md").argument("<message>", "The learning to record").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(async (message, opts, _cmd) => {
|
|
13555
13840
|
const projectPath = path72.resolve(opts.path);
|
|
13556
13841
|
const result = await appendLearning(projectPath, message, void 0, void 0, opts.stream);
|
|
13557
13842
|
if (!result.ok) {
|
|
@@ -13566,7 +13851,7 @@ function createLearnCommand() {
|
|
|
13566
13851
|
|
|
13567
13852
|
// src/commands/state/streams.ts
|
|
13568
13853
|
init_dist3();
|
|
13569
|
-
import { Command as
|
|
13854
|
+
import { Command as Command113 } from "commander";
|
|
13570
13855
|
import * as path73 from "path";
|
|
13571
13856
|
async function runListStreams(opts, globalOpts) {
|
|
13572
13857
|
const projectPath = path73.resolve(opts.path);
|
|
@@ -13617,7 +13902,7 @@ async function runActivateStream(name, opts) {
|
|
|
13617
13902
|
process.exit(ExitCode.SUCCESS);
|
|
13618
13903
|
}
|
|
13619
13904
|
function createStreamsCommand() {
|
|
13620
|
-
const command = new
|
|
13905
|
+
const command = new Command113("streams").description("Manage state streams");
|
|
13621
13906
|
command.command("list").description("List all known streams").option("--path <path>", "Project root path", ".").action(async (opts, cmd) => runListStreams(opts, cmd.optsWithGlobals()));
|
|
13622
13907
|
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));
|
|
13623
13908
|
command.command("archive <name>").description("Archive a stream").option("--path <path>", "Project root path", ".").action(async (name, opts) => runArchiveStream(name, opts));
|
|
@@ -13627,7 +13912,7 @@ function createStreamsCommand() {
|
|
|
13627
13912
|
|
|
13628
13913
|
// src/commands/state/index.ts
|
|
13629
13914
|
function createStateCommand() {
|
|
13630
|
-
const command = new
|
|
13915
|
+
const command = new Command114("state").description("Project state management commands");
|
|
13631
13916
|
command.addCommand(createShowCommand());
|
|
13632
13917
|
command.addCommand(createResetCommand());
|
|
13633
13918
|
command.addCommand(createLearnCommand());
|
|
@@ -13638,7 +13923,7 @@ function createStateCommand() {
|
|
|
13638
13923
|
// src/commands/sync-analyses.ts
|
|
13639
13924
|
import * as fs46 from "fs";
|
|
13640
13925
|
import * as path74 from "path";
|
|
13641
|
-
import { Command as
|
|
13926
|
+
import { Command as Command115 } from "commander";
|
|
13642
13927
|
init_dist3();
|
|
13643
13928
|
function isValidRecord(record) {
|
|
13644
13929
|
return typeof record.issueId === "string" && !!record.issueId && typeof record.identifier === "string" && !!record.identifier && typeof record.analyzedAt === "string" && !/[/\\]/.test(record.issueId);
|
|
@@ -13763,7 +14048,7 @@ async function runSyncAnalyses(opts) {
|
|
|
13763
14048
|
return;
|
|
13764
14049
|
}
|
|
13765
14050
|
const { AnalysisArchive, loadPublishedIndex: loadPublishedIndex2, savePublishedIndex: savePublishedIndex2 } = await import("@harness-engineering/orchestrator");
|
|
13766
|
-
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-
|
|
14051
|
+
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-MCTEX5QH.js");
|
|
13767
14052
|
const adapter = new GitHubIssuesSyncAdapter2({ token, config: trackerConfig });
|
|
13768
14053
|
const archive = new AnalysisArchive(path74.join(projectPath, ".harness", "analyses"));
|
|
13769
14054
|
const publishedIndex = loadPublishedIndex2(projectPath);
|
|
@@ -13785,7 +14070,7 @@ async function runSyncAnalyses(opts) {
|
|
|
13785
14070
|
}
|
|
13786
14071
|
}
|
|
13787
14072
|
function createSyncAnalysesCommand() {
|
|
13788
|
-
const command = new
|
|
14073
|
+
const command = new Command115("sync-analyses").description(
|
|
13789
14074
|
"Pull published intelligence analyses from the external issue tracker into the local .harness/analyses/ directory"
|
|
13790
14075
|
).option("-d, --dir <path>", "Workspace directory", process.cwd()).action(async (opts) => {
|
|
13791
14076
|
try {
|
|
@@ -13799,7 +14084,7 @@ function createSyncAnalysesCommand() {
|
|
|
13799
14084
|
}
|
|
13800
14085
|
|
|
13801
14086
|
// src/commands/sync-main.ts
|
|
13802
|
-
import { Command as
|
|
14087
|
+
import { Command as Command116 } from "commander";
|
|
13803
14088
|
import * as path75 from "path";
|
|
13804
14089
|
import {
|
|
13805
14090
|
syncMain as defaultSyncMain
|
|
@@ -13830,7 +14115,7 @@ function formatHuman2(r) {
|
|
|
13830
14115
|
}
|
|
13831
14116
|
}
|
|
13832
14117
|
function createSyncMainCommand() {
|
|
13833
|
-
return new
|
|
14118
|
+
return new Command116("sync-main").description("Fast-forward the local default branch from origin (no-op on conflict)").option("--json", "Emit a SyncMainResult JSON object", false).option("--path <path>", "Project root path", ".").action(async (opts) => {
|
|
13834
14119
|
const cwd = path75.resolve(opts.path ?? ".");
|
|
13835
14120
|
const exitCode = await runSyncMain({ cwd, json: Boolean(opts.json) });
|
|
13836
14121
|
process.exit(exitCode);
|
|
@@ -13839,7 +14124,7 @@ function createSyncMainCommand() {
|
|
|
13839
14124
|
|
|
13840
14125
|
// src/commands/taint.ts
|
|
13841
14126
|
init_dist3();
|
|
13842
|
-
import { Command as
|
|
14127
|
+
import { Command as Command117 } from "commander";
|
|
13843
14128
|
function getProjectRoot() {
|
|
13844
14129
|
return process.cwd();
|
|
13845
14130
|
}
|
|
@@ -13911,18 +14196,18 @@ function registerStatusCommand(taint) {
|
|
|
13911
14196
|
});
|
|
13912
14197
|
}
|
|
13913
14198
|
function createTaintCommand() {
|
|
13914
|
-
const taint = new
|
|
14199
|
+
const taint = new Command117("taint").description("Manage sentinel session taint state");
|
|
13915
14200
|
registerClearCommand(taint);
|
|
13916
14201
|
registerStatusCommand(taint);
|
|
13917
14202
|
return taint;
|
|
13918
14203
|
}
|
|
13919
14204
|
|
|
13920
14205
|
// src/commands/telemetry/index.ts
|
|
13921
|
-
import { Command as
|
|
14206
|
+
import { Command as Command121 } from "commander";
|
|
13922
14207
|
|
|
13923
14208
|
// src/commands/telemetry/identify.ts
|
|
13924
14209
|
init_dist3();
|
|
13925
|
-
import { Command as
|
|
14210
|
+
import { Command as Command118 } from "commander";
|
|
13926
14211
|
import * as fs47 from "fs";
|
|
13927
14212
|
import * as path76 from "path";
|
|
13928
14213
|
function telemetryFilePath(cwd) {
|
|
@@ -13946,7 +14231,7 @@ function printIdentity(identity) {
|
|
|
13946
14231
|
}
|
|
13947
14232
|
}
|
|
13948
14233
|
function createIdentifyCommand() {
|
|
13949
|
-
const cmd = new
|
|
14234
|
+
const cmd = new Command118("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) => {
|
|
13950
14235
|
const cwd = process.cwd();
|
|
13951
14236
|
if (opts.clear) {
|
|
13952
14237
|
writeTelemetryFile(cwd, { identity: {} });
|
|
@@ -13971,7 +14256,7 @@ function createIdentifyCommand() {
|
|
|
13971
14256
|
|
|
13972
14257
|
// src/commands/telemetry/status.ts
|
|
13973
14258
|
init_dist3();
|
|
13974
|
-
import { Command as
|
|
14259
|
+
import { Command as Command119 } from "commander";
|
|
13975
14260
|
function gatherEnvOverrides() {
|
|
13976
14261
|
const overrides = {};
|
|
13977
14262
|
if (process.env.DO_NOT_TRACK) overrides.DO_NOT_TRACK = process.env.DO_NOT_TRACK;
|
|
@@ -14013,7 +14298,7 @@ function printHumanStatus(result) {
|
|
|
14013
14298
|
printEnvOverrides(result.envOverrides);
|
|
14014
14299
|
}
|
|
14015
14300
|
function createStatusCommand() {
|
|
14016
|
-
const cmd = new
|
|
14301
|
+
const cmd = new Command119("status").description("Show current telemetry consent state, install ID, and identity").option("--json", "Output as JSON").action((opts) => {
|
|
14017
14302
|
const cwd = process.cwd();
|
|
14018
14303
|
const envOverrides = gatherEnvOverrides();
|
|
14019
14304
|
const consent = resolveConsent(cwd, void 0);
|
|
@@ -14046,11 +14331,11 @@ function createStatusCommand() {
|
|
|
14046
14331
|
|
|
14047
14332
|
// src/commands/telemetry/test.ts
|
|
14048
14333
|
init_dist3();
|
|
14049
|
-
import { Command as
|
|
14334
|
+
import { Command as Command120 } from "commander";
|
|
14050
14335
|
|
|
14051
14336
|
// src/bin/command-telemetry.ts
|
|
14052
|
-
import { existsSync as existsSync43, readFileSync as
|
|
14053
|
-
import { join as
|
|
14337
|
+
import { existsSync as existsSync43, readFileSync as readFileSync32, mkdirSync as mkdirSync22, appendFileSync, writeFileSync as writeFileSync28 } from "fs";
|
|
14338
|
+
import { join as join63, parse as parsePath, resolve as resolve45 } from "path";
|
|
14054
14339
|
import { spawn as spawn2 } from "child_process";
|
|
14055
14340
|
var POSTHOG_API_KEY = "phc_wNTdCMcfJXZPgdNeDociZW6vwoGGo4nb7vqEfWThFfsG";
|
|
14056
14341
|
var EXCLUDED_COMMANDS = /* @__PURE__ */ new Set(["help", "completion"]);
|
|
@@ -14058,7 +14343,7 @@ function findProjectRoot(cwd) {
|
|
|
14058
14343
|
let dir = resolve45(cwd);
|
|
14059
14344
|
const { root } = parsePath(dir);
|
|
14060
14345
|
while (dir !== root) {
|
|
14061
|
-
if (existsSync43(
|
|
14346
|
+
if (existsSync43(join63(dir, "harness.config.json"))) return dir;
|
|
14062
14347
|
dir = resolve45(dir, "..");
|
|
14063
14348
|
}
|
|
14064
14349
|
return cwd;
|
|
@@ -14096,7 +14381,7 @@ function resolveCommandName(cmd) {
|
|
|
14096
14381
|
}
|
|
14097
14382
|
function writeCommandRecordSync(cwd, command, duration, outcome) {
|
|
14098
14383
|
try {
|
|
14099
|
-
const metricsDir =
|
|
14384
|
+
const metricsDir = join63(cwd, ".harness", "metrics");
|
|
14100
14385
|
mkdirSync22(metricsDir, { recursive: true });
|
|
14101
14386
|
const record = {
|
|
14102
14387
|
skill: command,
|
|
@@ -14106,16 +14391,16 @@ function writeCommandRecordSync(cwd, command, duration, outcome) {
|
|
|
14106
14391
|
outcome,
|
|
14107
14392
|
phasesReached: []
|
|
14108
14393
|
};
|
|
14109
|
-
const adoptionFile =
|
|
14394
|
+
const adoptionFile = join63(metricsDir, "adoption.jsonl");
|
|
14110
14395
|
appendFileSync(adoptionFile, JSON.stringify(record) + "\n");
|
|
14111
14396
|
} catch {
|
|
14112
14397
|
}
|
|
14113
14398
|
}
|
|
14114
14399
|
function isTelemetryDisabledByConfig(cwd) {
|
|
14115
|
-
const configPath =
|
|
14400
|
+
const configPath = join63(cwd, "harness.config.json");
|
|
14116
14401
|
if (!existsSync43(configPath)) return false;
|
|
14117
14402
|
try {
|
|
14118
|
-
const config = JSON.parse(
|
|
14403
|
+
const config = JSON.parse(readFileSync32(configPath, "utf-8"));
|
|
14119
14404
|
return config?.telemetry?.enabled === false;
|
|
14120
14405
|
} catch {
|
|
14121
14406
|
return false;
|
|
@@ -14127,10 +14412,10 @@ function isTelemetryOptedOut(cwd) {
|
|
|
14127
14412
|
}
|
|
14128
14413
|
function flushTelemetryBackground(cwd) {
|
|
14129
14414
|
try {
|
|
14130
|
-
const adoptionFile =
|
|
14415
|
+
const adoptionFile = join63(cwd, ".harness", "metrics", "adoption.jsonl");
|
|
14131
14416
|
if (!existsSync43(adoptionFile)) return;
|
|
14132
14417
|
if (isTelemetryOptedOut(cwd)) return;
|
|
14133
|
-
const reporterPath =
|
|
14418
|
+
const reporterPath = join63(cwd, ".harness", "hooks", "telemetry-reporter.js");
|
|
14134
14419
|
if (!existsSync43(reporterPath)) return;
|
|
14135
14420
|
const child = spawn2(process.execPath, [reporterPath], {
|
|
14136
14421
|
cwd,
|
|
@@ -14168,7 +14453,7 @@ function reportTestSuccess(status, distinctId, identity) {
|
|
|
14168
14453
|
if (identity.team) logger.info(` team: ${identity.team}`);
|
|
14169
14454
|
}
|
|
14170
14455
|
function createTestCommand() {
|
|
14171
|
-
return new
|
|
14456
|
+
return new Command120("test").description("Send a test event to PostHog and verify connectivity").action(async () => {
|
|
14172
14457
|
const cwd = process.cwd();
|
|
14173
14458
|
const consent = resolveConsent(cwd, void 0);
|
|
14174
14459
|
if (!consent.allowed) {
|
|
@@ -14205,7 +14490,7 @@ function createTestCommand() {
|
|
|
14205
14490
|
|
|
14206
14491
|
// src/commands/telemetry/index.ts
|
|
14207
14492
|
function createTelemetryCommand() {
|
|
14208
|
-
const command = new
|
|
14493
|
+
const command = new Command121("telemetry").description("Telemetry identity and status management");
|
|
14209
14494
|
command.addCommand(createIdentifyCommand());
|
|
14210
14495
|
command.addCommand(createStatusCommand());
|
|
14211
14496
|
command.addCommand(createTestCommand());
|
|
@@ -14213,9 +14498,9 @@ function createTelemetryCommand() {
|
|
|
14213
14498
|
}
|
|
14214
14499
|
|
|
14215
14500
|
// src/commands/test-craft.ts
|
|
14216
|
-
import { Command as
|
|
14501
|
+
import { Command as Command122 } from "commander";
|
|
14217
14502
|
function createTestCraftCommand() {
|
|
14218
|
-
return new
|
|
14503
|
+
return new Command122("test-craft").description(
|
|
14219
14504
|
"LLM-judgment critique of test quality across vitest/jest/mocha/playwright. Fourth craft-pipeline ceiling skill. Per-test critique with best-effort source pairing."
|
|
14220
14505
|
).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) => {
|
|
14221
14506
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -14285,7 +14570,7 @@ ${file}`);
|
|
|
14285
14570
|
}
|
|
14286
14571
|
|
|
14287
14572
|
// src/commands/traceability.ts
|
|
14288
|
-
import { Command as
|
|
14573
|
+
import { Command as Command123 } from "commander";
|
|
14289
14574
|
import chalk19 from "chalk";
|
|
14290
14575
|
function confidenceLabel(maxConfidence) {
|
|
14291
14576
|
if (maxConfidence >= 0.8) return "explicit";
|
|
@@ -14327,9 +14612,11 @@ function buildFilterOptions(options) {
|
|
|
14327
14612
|
}
|
|
14328
14613
|
function handleNoStore2(mode) {
|
|
14329
14614
|
if (mode === OutputMode.JSON) {
|
|
14330
|
-
console.log(
|
|
14615
|
+
console.log(
|
|
14616
|
+
JSON.stringify({ error: "No knowledge graph found. Run `harness graph scan` first." })
|
|
14617
|
+
);
|
|
14331
14618
|
} else {
|
|
14332
|
-
logger.error("No knowledge graph found. Run `harness scan` first.");
|
|
14619
|
+
logger.error("No knowledge graph found. Run `harness graph scan` first.");
|
|
14333
14620
|
}
|
|
14334
14621
|
process.exit(ExitCode.ERROR);
|
|
14335
14622
|
}
|
|
@@ -14338,11 +14625,13 @@ function handleEmptyResults(mode) {
|
|
|
14338
14625
|
console.log(
|
|
14339
14626
|
JSON.stringify({
|
|
14340
14627
|
results: [],
|
|
14341
|
-
message: "No requirements found in graph. Run `harness scan` to ingest spec requirements."
|
|
14628
|
+
message: "No requirements found in graph. Run `harness graph scan` to ingest spec requirements."
|
|
14342
14629
|
})
|
|
14343
14630
|
);
|
|
14344
14631
|
} else if (mode !== OutputMode.QUIET) {
|
|
14345
|
-
logger.info(
|
|
14632
|
+
logger.info(
|
|
14633
|
+
"No requirements found in graph. Run `harness graph scan` to ingest spec requirements."
|
|
14634
|
+
);
|
|
14346
14635
|
}
|
|
14347
14636
|
process.exit(ExitCode.SUCCESS);
|
|
14348
14637
|
}
|
|
@@ -14421,7 +14710,7 @@ async function runTraceability(options) {
|
|
|
14421
14710
|
process.exit(ExitCode.SUCCESS);
|
|
14422
14711
|
}
|
|
14423
14712
|
function createTraceabilityCommand() {
|
|
14424
|
-
return new
|
|
14713
|
+
return new Command123("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) => {
|
|
14425
14714
|
const globalOpts = cmd.optsWithGlobals();
|
|
14426
14715
|
await runTraceability({
|
|
14427
14716
|
spec: opts.spec,
|
|
@@ -14435,7 +14724,7 @@ function createTraceabilityCommand() {
|
|
|
14435
14724
|
|
|
14436
14725
|
// src/commands/uninstall.ts
|
|
14437
14726
|
import * as path77 from "path";
|
|
14438
|
-
import { Command as
|
|
14727
|
+
import { Command as Command124 } from "commander";
|
|
14439
14728
|
async function runUninstall(skillName, options) {
|
|
14440
14729
|
const packageName = resolvePackageName(skillName);
|
|
14441
14730
|
const shortName = extractSkillName(packageName);
|
|
@@ -14472,7 +14761,7 @@ async function runUninstall(skillName, options) {
|
|
|
14472
14761
|
return result;
|
|
14473
14762
|
}
|
|
14474
14763
|
function createUninstallCommand() {
|
|
14475
|
-
const cmd = new
|
|
14764
|
+
const cmd = new Command124("uninstall");
|
|
14476
14765
|
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) => {
|
|
14477
14766
|
try {
|
|
14478
14767
|
const result = await runUninstall(skill, opts);
|
|
@@ -14494,7 +14783,7 @@ function createUninstallCommand() {
|
|
|
14494
14783
|
init_dist3();
|
|
14495
14784
|
import * as fs48 from "fs/promises";
|
|
14496
14785
|
import * as path78 from "path";
|
|
14497
|
-
import { Command as
|
|
14786
|
+
import { Command as Command125 } from "commander";
|
|
14498
14787
|
async function runUninstallConstraints(options) {
|
|
14499
14788
|
const { packageName, configPath, lockfilePath } = options;
|
|
14500
14789
|
const lockfileResult = await readLockfile(lockfilePath);
|
|
@@ -14551,7 +14840,7 @@ async function runUninstallConstraints(options) {
|
|
|
14551
14840
|
};
|
|
14552
14841
|
}
|
|
14553
14842
|
function createUninstallConstraintsCommand() {
|
|
14554
|
-
const cmd = new
|
|
14843
|
+
const cmd = new Command125("uninstall-constraints");
|
|
14555
14844
|
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) => {
|
|
14556
14845
|
let configPath;
|
|
14557
14846
|
if (opts.config) {
|
|
@@ -14591,10 +14880,10 @@ function createUninstallConstraintsCommand() {
|
|
|
14591
14880
|
|
|
14592
14881
|
// src/commands/update.ts
|
|
14593
14882
|
init_dist3();
|
|
14594
|
-
import { Command as
|
|
14595
|
-
import { execFile as execFile2, execFileSync as
|
|
14596
|
-
import { realpathSync, existsSync as existsSync44, readFileSync as
|
|
14597
|
-
import { dirname as dirname24, join as
|
|
14883
|
+
import { Command as Command126 } from "commander";
|
|
14884
|
+
import { execFile as execFile2, execFileSync as execFileSync10 } from "child_process";
|
|
14885
|
+
import { realpathSync, existsSync as existsSync44, readFileSync as readFileSync33 } from "fs";
|
|
14886
|
+
import { dirname as dirname24, join as join66 } from "path";
|
|
14598
14887
|
import { promisify as promisify2 } from "util";
|
|
14599
14888
|
import readline4 from "readline";
|
|
14600
14889
|
import chalk20 from "chalk";
|
|
@@ -14634,7 +14923,7 @@ async function getLatestVersionAsync(pkg) {
|
|
|
14634
14923
|
function getInstalledVersions(pm, packages) {
|
|
14635
14924
|
const versions = {};
|
|
14636
14925
|
try {
|
|
14637
|
-
const output =
|
|
14926
|
+
const output = execFileSync10(pm, ["list", "-g", "--json"], {
|
|
14638
14927
|
encoding: "utf-8",
|
|
14639
14928
|
timeout: 15e3
|
|
14640
14929
|
});
|
|
@@ -14655,7 +14944,7 @@ function getInstalledVersions(pm, packages) {
|
|
|
14655
14944
|
}
|
|
14656
14945
|
function getInstalledPackages(pm) {
|
|
14657
14946
|
try {
|
|
14658
|
-
const output =
|
|
14947
|
+
const output = execFileSync10(pm, ["list", "-g", "--json"], {
|
|
14659
14948
|
encoding: "utf-8",
|
|
14660
14949
|
timeout: 15e3
|
|
14661
14950
|
});
|
|
@@ -14685,9 +14974,9 @@ function prompt3(question) {
|
|
|
14685
14974
|
function findPackageDir(resolvedBin) {
|
|
14686
14975
|
let dir = dirname24(resolvedBin);
|
|
14687
14976
|
for (let i = 0; i < 6; i++) {
|
|
14688
|
-
if (existsSync44(
|
|
14977
|
+
if (existsSync44(join66(dir, "package.json"))) {
|
|
14689
14978
|
try {
|
|
14690
|
-
const pkg = JSON.parse(
|
|
14979
|
+
const pkg = JSON.parse(readFileSync33(join66(dir, "package.json"), "utf-8"));
|
|
14691
14980
|
if (pkg?.name === CLI_PACKAGE) return dir;
|
|
14692
14981
|
} catch {
|
|
14693
14982
|
}
|
|
@@ -14710,7 +14999,7 @@ function findAllInstalls() {
|
|
|
14710
14999
|
const args = isWindows ? ["harness"] : ["-a", "harness"];
|
|
14711
15000
|
let output;
|
|
14712
15001
|
try {
|
|
14713
|
-
const raw =
|
|
15002
|
+
const raw = execFileSync10(cmd, args, { encoding: "utf-8", timeout: 5e3 });
|
|
14714
15003
|
if (typeof raw !== "string") return [];
|
|
14715
15004
|
output = raw;
|
|
14716
15005
|
} catch {
|
|
@@ -14732,7 +15021,7 @@ function findAllInstalls() {
|
|
|
14732
15021
|
if (!packageDir) continue;
|
|
14733
15022
|
let version = null;
|
|
14734
15023
|
try {
|
|
14735
|
-
const pkg = JSON.parse(
|
|
15024
|
+
const pkg = JSON.parse(readFileSync33(join66(packageDir, "package.json"), "utf-8"));
|
|
14736
15025
|
if (typeof pkg?.version === "string") version = pkg.version;
|
|
14737
15026
|
} catch {
|
|
14738
15027
|
}
|
|
@@ -14787,7 +15076,7 @@ async function offerCleanupOfOtherInstalls(activeDir) {
|
|
|
14787
15076
|
}
|
|
14788
15077
|
try {
|
|
14789
15078
|
logger.info(`Uninstalling: ${cmd.display}`);
|
|
14790
|
-
|
|
15079
|
+
execFileSync10(cmd.bin, cmd.args, { stdio: "inherit", timeout: 12e4 });
|
|
14791
15080
|
} catch {
|
|
14792
15081
|
logger.warn(`Uninstall failed for ${cmd.install.binPath}.`);
|
|
14793
15082
|
console.log(` Run manually: ${chalk20.cyan(cmd.display)}`);
|
|
@@ -14825,12 +15114,12 @@ async function ensureTelemetryIfNeeded() {
|
|
|
14825
15114
|
}
|
|
14826
15115
|
function refreshHooks() {
|
|
14827
15116
|
const cwd = process.cwd();
|
|
14828
|
-
const configPath =
|
|
15117
|
+
const configPath = join66(cwd, "harness.config.json");
|
|
14829
15118
|
if (!existsSync44(configPath)) return;
|
|
14830
15119
|
let profile = "standard";
|
|
14831
|
-
const profilePath =
|
|
15120
|
+
const profilePath = join66(cwd, ".harness", "hooks", "profile.json");
|
|
14832
15121
|
try {
|
|
14833
|
-
const data = JSON.parse(
|
|
15122
|
+
const data = JSON.parse(readFileSync33(profilePath, "utf-8"));
|
|
14834
15123
|
if (data.profile && ["minimal", "standard", "strict"].includes(data.profile)) {
|
|
14835
15124
|
profile = data.profile;
|
|
14836
15125
|
}
|
|
@@ -14847,7 +15136,7 @@ function refreshHooks() {
|
|
|
14847
15136
|
function runLocalGraphScan() {
|
|
14848
15137
|
try {
|
|
14849
15138
|
logger.info("Scanning codebase to rebuild knowledge graph...");
|
|
14850
|
-
|
|
15139
|
+
execFileSync10("harness", ["graph", "scan", "."], { stdio: "inherit" });
|
|
14851
15140
|
} catch {
|
|
14852
15141
|
logger.warn("Graph scan failed. Run manually:");
|
|
14853
15142
|
console.log(` ${chalk20.cyan("harness graph scan .")}`);
|
|
@@ -14862,7 +15151,7 @@ async function offerRegeneration() {
|
|
|
14862
15151
|
const scopeAnswer = await prompt3("Generate for (G)lobal or (l)ocal project? (G/l) ");
|
|
14863
15152
|
const isGlobal = scopeAnswer !== "l" && scopeAnswer !== "local";
|
|
14864
15153
|
try {
|
|
14865
|
-
|
|
15154
|
+
execFileSync10("harness", ["generate", ...isGlobal ? ["--global"] : []], {
|
|
14866
15155
|
stdio: "inherit"
|
|
14867
15156
|
});
|
|
14868
15157
|
} catch {
|
|
@@ -14942,7 +15231,7 @@ async function runUpdateAction(opts, globalOpts) {
|
|
|
14942
15231
|
}
|
|
14943
15232
|
try {
|
|
14944
15233
|
logger.info("Updating packages...");
|
|
14945
|
-
|
|
15234
|
+
execFileSync10(pm, ["install", "-g", ...installPkgs], { stdio: "inherit", timeout: 12e4 });
|
|
14946
15235
|
console.log("");
|
|
14947
15236
|
logger.success("Update complete");
|
|
14948
15237
|
invalidateCheckState();
|
|
@@ -14959,7 +15248,7 @@ async function runUpdateAction(opts, globalOpts) {
|
|
|
14959
15248
|
process.exit(ExitCode.SUCCESS);
|
|
14960
15249
|
}
|
|
14961
15250
|
function createUpdateCommand() {
|
|
14962
|
-
return new
|
|
15251
|
+
return new Command126("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(
|
|
14963
15252
|
"--regenerate",
|
|
14964
15253
|
"Only regenerate slash commands and agent definitions (skip package updates)"
|
|
14965
15254
|
).action(async (opts, cmd) => {
|
|
@@ -14969,9 +15258,9 @@ function createUpdateCommand() {
|
|
|
14969
15258
|
}
|
|
14970
15259
|
|
|
14971
15260
|
// src/commands/usage.ts
|
|
14972
|
-
import { Command as
|
|
15261
|
+
import { Command as Command127 } from "commander";
|
|
14973
15262
|
async function loadAndPriceRecords(cwd, includeClaudeSessions = false) {
|
|
14974
|
-
const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-
|
|
15263
|
+
const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-MCTEX5QH.js");
|
|
14975
15264
|
const records = readCostRecords(cwd);
|
|
14976
15265
|
if (includeClaudeSessions) {
|
|
14977
15266
|
const ccRecords = parseCCRecords();
|
|
@@ -15064,7 +15353,7 @@ function registerDailyCommand(usage) {
|
|
|
15064
15353
|
else logger.info("No usage data found. Run some harness sessions first.");
|
|
15065
15354
|
return;
|
|
15066
15355
|
}
|
|
15067
|
-
const { aggregateByDay } = await import("./dist-
|
|
15356
|
+
const { aggregateByDay } = await import("./dist-MCTEX5QH.js");
|
|
15068
15357
|
const limited = aggregateByDay(records).slice(0, days);
|
|
15069
15358
|
if (globalOpts.json) {
|
|
15070
15359
|
console.log(
|
|
@@ -15093,7 +15382,7 @@ function registerSessionsCommand(usage) {
|
|
|
15093
15382
|
}
|
|
15094
15383
|
return;
|
|
15095
15384
|
}
|
|
15096
|
-
const { aggregateBySession } = await import("./dist-
|
|
15385
|
+
const { aggregateBySession } = await import("./dist-MCTEX5QH.js");
|
|
15097
15386
|
const sessionData = aggregateBySession(records);
|
|
15098
15387
|
const limited = sessionData.slice(0, limit);
|
|
15099
15388
|
if (globalOpts.json) {
|
|
@@ -15173,7 +15462,7 @@ function registerSessionCommand(usage) {
|
|
|
15173
15462
|
cwd,
|
|
15174
15463
|
globalOpts.includeClaudeSessions
|
|
15175
15464
|
);
|
|
15176
|
-
const { aggregateBySession } = await import("./dist-
|
|
15465
|
+
const { aggregateBySession } = await import("./dist-MCTEX5QH.js");
|
|
15177
15466
|
const sessionData = aggregateBySession(records);
|
|
15178
15467
|
const match = sessionData.find((s) => s.sessionId === id);
|
|
15179
15468
|
if (!match) {
|
|
@@ -15208,7 +15497,7 @@ function registerLatestCommand(usage) {
|
|
|
15208
15497
|
}
|
|
15209
15498
|
return;
|
|
15210
15499
|
}
|
|
15211
|
-
const { aggregateBySession } = await import("./dist-
|
|
15500
|
+
const { aggregateBySession } = await import("./dist-MCTEX5QH.js");
|
|
15212
15501
|
const sessionData = aggregateBySession(records);
|
|
15213
15502
|
const latest = sessionData[0];
|
|
15214
15503
|
if (!latest) {
|
|
@@ -15234,7 +15523,7 @@ function registerLatestCommand(usage) {
|
|
|
15234
15523
|
});
|
|
15235
15524
|
}
|
|
15236
15525
|
function createUsageCommand() {
|
|
15237
|
-
const usage = new
|
|
15526
|
+
const usage = new Command127("usage").description("Token usage and cost tracking");
|
|
15238
15527
|
usage.option(
|
|
15239
15528
|
"--include-claude-sessions",
|
|
15240
15529
|
"Include Claude Code session data from ~/.claude/projects/"
|
|
@@ -15249,10 +15538,10 @@ function createUsageCommand() {
|
|
|
15249
15538
|
// src/commands/validate.ts
|
|
15250
15539
|
init_dist3();
|
|
15251
15540
|
init_dist3();
|
|
15252
|
-
import { Command as
|
|
15541
|
+
import { Command as Command128 } from "commander";
|
|
15253
15542
|
import * as fs49 from "fs";
|
|
15254
15543
|
import * as path79 from "path";
|
|
15255
|
-
import { execFileSync as
|
|
15544
|
+
import { execFileSync as execFileSync11 } from "child_process";
|
|
15256
15545
|
async function runValidate(options) {
|
|
15257
15546
|
const configResult = resolveConfig(options.configPath);
|
|
15258
15547
|
if (!configResult.ok) {
|
|
@@ -15371,7 +15660,7 @@ async function runValidate(options) {
|
|
|
15371
15660
|
const gitattributesContent = fs49.readFileSync(gitattributesPath, "utf-8");
|
|
15372
15661
|
let driverConfigured = false;
|
|
15373
15662
|
try {
|
|
15374
|
-
const out =
|
|
15663
|
+
const out = execFileSync11("git", ["config", "--get", "merge.ours.driver"], {
|
|
15375
15664
|
cwd,
|
|
15376
15665
|
encoding: "utf-8",
|
|
15377
15666
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -15557,7 +15846,7 @@ function resolveValidateMode(globalOpts) {
|
|
|
15557
15846
|
return OutputMode.TEXT;
|
|
15558
15847
|
}
|
|
15559
15848
|
async function printCrossCheckWarnings(mode) {
|
|
15560
|
-
const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-
|
|
15849
|
+
const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-OYKEDCST.js");
|
|
15561
15850
|
const cwd = process.cwd();
|
|
15562
15851
|
const crossResult = await runCrossCheck2({
|
|
15563
15852
|
specsDir: path79.join(cwd, "docs", "specs"),
|
|
@@ -15573,7 +15862,7 @@ async function printCrossCheckWarnings(mode) {
|
|
|
15573
15862
|
${crossResult.value.warnings} warnings`);
|
|
15574
15863
|
}
|
|
15575
15864
|
function createValidateCommand3() {
|
|
15576
|
-
const command = new
|
|
15865
|
+
const command = new Command128("validate").description("Run all validation checks").option("--cross-check", "Run cross-artifact consistency validation").option(
|
|
15577
15866
|
"--agent-configs",
|
|
15578
15867
|
"Validate agent configs (CLAUDE.md, hooks, skills) via agnix or built-in fallback rules"
|
|
15579
15868
|
).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()));
|
|
@@ -15622,7 +15911,7 @@ Agent configs checked via ${engineLabel}${note}`);
|
|
|
15622
15911
|
|
|
15623
15912
|
// src/commands/verify.ts
|
|
15624
15913
|
init_dist3();
|
|
15625
|
-
import { Command as
|
|
15914
|
+
import { Command as Command129 } from "commander";
|
|
15626
15915
|
import { execSync as execSync6 } from "child_process";
|
|
15627
15916
|
function loadBranchingConfig(configPath) {
|
|
15628
15917
|
const shouldLoad = configPath !== void 0 || findConfigFile().ok;
|
|
@@ -15685,7 +15974,7 @@ async function runVerify(options) {
|
|
|
15685
15974
|
process.exit(ExitCode.VALIDATION_FAILED);
|
|
15686
15975
|
}
|
|
15687
15976
|
function createVerifyCommand() {
|
|
15688
|
-
return new
|
|
15977
|
+
return new Command129("verify").description(
|
|
15689
15978
|
"Verify project conventions (currently: branch naming). Works with or without a harness.config.json."
|
|
15690
15979
|
).option(
|
|
15691
15980
|
"--branch <name>",
|
|
@@ -15736,7 +16025,6 @@ var commandCreators = [
|
|
|
15736
16025
|
createGraphCommand,
|
|
15737
16026
|
createHooksCommand,
|
|
15738
16027
|
createImpactPreviewCommand,
|
|
15739
|
-
createIngestCommand,
|
|
15740
16028
|
createInitCommand3,
|
|
15741
16029
|
createInsightsCommand,
|
|
15742
16030
|
createInstallCommand,
|
|
@@ -15757,15 +16045,14 @@ var commandCreators = [
|
|
|
15757
16045
|
createPerfCommand,
|
|
15758
16046
|
createPersonaCommand,
|
|
15759
16047
|
createPredictCommand,
|
|
16048
|
+
createPreMergeBriefCommand,
|
|
15760
16049
|
createProposalsCommand,
|
|
15761
16050
|
createPublishAnalysesCommand,
|
|
15762
16051
|
createPulseCommand,
|
|
15763
|
-
createQueryCommand,
|
|
15764
16052
|
createRecommendCommand,
|
|
15765
16053
|
createReviewCiCommand,
|
|
15766
16054
|
createRoadmapCommand,
|
|
15767
16055
|
createRoutingCommand,
|
|
15768
|
-
createScanCommand,
|
|
15769
16056
|
createScanConfigCommand,
|
|
15770
16057
|
createSearchCommand,
|
|
15771
16058
|
createSecurityCraftCommand,
|
|
@@ -15791,13 +16078,35 @@ var commandCreators = [
|
|
|
15791
16078
|
createVerifyCommand
|
|
15792
16079
|
];
|
|
15793
16080
|
|
|
16081
|
+
// src/commands/graph/deprecated-aliases.ts
|
|
16082
|
+
var DEPRECATED_GRAPH_ALIASES = [
|
|
16083
|
+
{ factory: createScanCommand, canonical: "harness graph scan" },
|
|
16084
|
+
{ factory: createQueryCommand, canonical: "harness graph query" },
|
|
16085
|
+
{ factory: createIngestCommand, canonical: "harness graph ingest" }
|
|
16086
|
+
];
|
|
16087
|
+
function registerDeprecatedGraphAliases(program) {
|
|
16088
|
+
for (const { factory, canonical } of DEPRECATED_GRAPH_ALIASES) {
|
|
16089
|
+
const alias = factory();
|
|
16090
|
+
alias.hook("preAction", (thisCommand) => {
|
|
16091
|
+
const globalOpts = thisCommand.optsWithGlobals();
|
|
16092
|
+
if (globalOpts.quiet) return;
|
|
16093
|
+
process.stderr.write(
|
|
16094
|
+
`\u26A0 "harness ${alias.name()}" is deprecated and will be removed in the next major release. Use "${canonical}" instead.
|
|
16095
|
+
`
|
|
16096
|
+
);
|
|
16097
|
+
});
|
|
16098
|
+
program.addCommand(alias, { hidden: true });
|
|
16099
|
+
}
|
|
16100
|
+
}
|
|
16101
|
+
|
|
15794
16102
|
// src/index.ts
|
|
15795
16103
|
function createProgram() {
|
|
15796
|
-
const program = new
|
|
16104
|
+
const program = new Command130();
|
|
15797
16105
|
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");
|
|
15798
16106
|
for (const creator of commandCreators) {
|
|
15799
16107
|
program.addCommand(creator());
|
|
15800
16108
|
}
|
|
16109
|
+
registerDeprecatedGraphAliases(program);
|
|
15801
16110
|
return program;
|
|
15802
16111
|
}
|
|
15803
16112
|
|