@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.
Files changed (64) hide show
  1. package/dist/agents/skills/claude-code/harness-autopilot/SKILL.md +9 -8
  2. package/dist/agents/skills/claude-code/pre-merge-brief/SKILL.md +86 -0
  3. package/dist/agents/skills/claude-code/pre-merge-brief/skill.yaml +67 -0
  4. package/dist/agents/skills/codex/harness-autopilot/SKILL.md +9 -8
  5. package/dist/agents/skills/codex/pre-merge-brief/SKILL.md +86 -0
  6. package/dist/agents/skills/codex/pre-merge-brief/skill.yaml +67 -0
  7. package/dist/agents/skills/cursor/harness-autopilot/SKILL.md +9 -8
  8. package/dist/agents/skills/cursor/pre-merge-brief/SKILL.md +86 -0
  9. package/dist/agents/skills/cursor/pre-merge-brief/skill.yaml +67 -0
  10. package/dist/agents/skills/gemini-cli/harness-autopilot/SKILL.md +9 -8
  11. package/dist/agents/skills/gemini-cli/pre-merge-brief/SKILL.md +86 -0
  12. package/dist/agents/skills/gemini-cli/pre-merge-brief/skill.yaml +67 -0
  13. package/dist/{agents-md-VJLYEH2S.js → agents-md-7L4PKKOM.js} +3 -3
  14. package/dist/analyzer-XRCV63KC-HLTEIKAJ.js +26 -0
  15. package/dist/{architecture-YQH2NYXY.js → architecture-RCHFDI7E.js} +4 -4
  16. package/dist/{assess-project-KOTAPAHD.js → assess-project-YUDPNRS6.js} +5 -3
  17. package/dist/bin/harness-mcp.js +14 -14
  18. package/dist/bin/harness.js +24 -24
  19. package/dist/{check-phase-gate-CHU2E3OU.js → check-phase-gate-56GS3A72.js} +4 -4
  20. package/dist/{chunk-JPVOI7GP.js → chunk-4SKWGVYD.js} +2 -2
  21. package/dist/{chunk-GHBFNLV2.js → chunk-ABRYIEWJ.js} +923 -614
  22. package/dist/{chunk-36GQREOE.js → chunk-AU4YPEIE.js} +3 -3
  23. package/dist/{chunk-OQ6KOQEV.js → chunk-DGVT4DBI.js} +3 -3
  24. package/dist/{chunk-I3CGCZJF.js → chunk-GLHLXTKC.js} +5 -5
  25. package/dist/{chunk-4VXFZSYF.js → chunk-GSYBGOY3.js} +1 -1
  26. package/dist/{chunk-UWVMJEUJ.js → chunk-IGWJRAJT.js} +1 -1
  27. package/dist/{chunk-YANR2RL7.js → chunk-N4AI2WFH.js} +1 -1
  28. package/dist/{chunk-5RSXUAMN.js → chunk-NCMJXBEO.js} +1 -1
  29. package/dist/{chunk-SEA2PFVI.js → chunk-OZSKLJQW.js} +1 -1
  30. package/dist/{chunk-AX5KGRM6.js → chunk-P4NA5OQX.js} +1 -1
  31. package/dist/{chunk-7PJCK7UE.js → chunk-RY2U77OY.js} +6 -6
  32. package/dist/{chunk-6SCYTIF4.js → chunk-RZQZXWEL.js} +32 -7
  33. package/dist/{chunk-MMLQPHZ5.js → chunk-UACYZCW4.js} +31 -13
  34. package/dist/{chunk-T746REAE.js → chunk-VG4G3R2V.js} +1 -1
  35. package/dist/{chunk-7TIJXWG3.js → chunk-XW5XVVMI.js} +2 -2
  36. package/dist/{chunk-E5EW5HVJ.js → chunk-XZNCONFG.js} +1 -1
  37. package/dist/{chunk-SDMV4QHM.js → chunk-Y33P35U3.js} +24 -6
  38. package/dist/{chunk-XAVMTAUT.js → chunk-Y5HPJMTM.js} +18 -2
  39. package/dist/{chunk-6YFHUIGT.js → chunk-YJYUJSWK.js} +58 -20
  40. package/dist/{chunk-AAUY53CC.js → chunk-YWRPQZL7.js} +1 -1
  41. package/dist/{chunk-M7F2PPN3.js → chunk-YXOPQEFZ.js} +2 -2
  42. package/dist/{chunk-RG3TNIUB.js → chunk-Z4XWWNYX.js} +86 -86
  43. package/dist/{chunk-EYMOPFSD.js → chunk-ZU6T7W3U.js} +2 -2
  44. package/dist/{ci-workflow-HO4WKGNG.js → ci-workflow-TFAA5BK7.js} +3 -3
  45. package/dist/{dist-6D2F2J57.js → dist-MCTEX5QH.js} +4 -2
  46. package/dist/{docs-FP47JONV.js → docs-BBMAN3CY.js} +4 -4
  47. package/dist/{engine-YUA6IYOG.js → engine-R2YH74AL.js} +3 -3
  48. package/dist/{entropy-DBW2INZU.js → entropy-5UP5N6AF.js} +6 -3
  49. package/dist/{feedback-BPHADTRQ.js → feedback-6MDCCFZL.js} +1 -1
  50. package/dist/{generate-agent-definitions-KPVGBGDY.js → generate-agent-definitions-KKAN5DB5.js} +4 -4
  51. package/dist/hooks/sentinel-pre.js +15 -138
  52. package/dist/index.d.ts +61 -0
  53. package/dist/index.js +24 -24
  54. package/dist/{loader-QOJQ6NZM.js → loader-2WT2ZE2T.js} +3 -3
  55. package/dist/{mcp-ZPX6O767.js → mcp-5V4LPMB3.js} +14 -14
  56. package/dist/{performance-FXYTGJGQ.js → performance-2N3CFXLR.js} +4 -4
  57. package/dist/{review-pipeline-2TFQ5OAE.js → review-pipeline-XZIGBU3Q.js} +3 -3
  58. package/dist/{runtime-XK6ZCHKE.js → runtime-POME3GTR.js} +3 -3
  59. package/dist/{security-4W2P5ZGI.js → security-QLJLMSIW.js} +1 -1
  60. package/dist/{state-events-ECQAIZLU.js → state-events-YY5KBZTC.js} +3 -3
  61. package/dist/{validate-64KTEGIE.js → validate-VZORVK43.js} +4 -4
  62. package/dist/{validate-cross-check-J5PVGICB.js → validate-cross-check-OYKEDCST.js} +3 -3
  63. package/package.json +5 -4
  64. package/dist/analyzer-V633GUHY-OV335HGM.js +0 -26
@@ -1,22 +1,22 @@
1
1
  import {
2
2
  generateRuntime
3
- } from "./chunk-YANR2RL7.js";
3
+ } from "./chunk-N4AI2WFH.js";
4
4
  import {
5
5
  generateAgentsMd
6
- } from "./chunk-T746REAE.js";
6
+ } from "./chunk-VG4G3R2V.js";
7
7
  import {
8
8
  generateCIWorkflow
9
- } from "./chunk-AX5KGRM6.js";
9
+ } from "./chunk-P4NA5OQX.js";
10
10
  import {
11
11
  readHarnessState
12
- } from "./chunk-AAUY53CC.js";
12
+ } from "./chunk-YWRPQZL7.js";
13
13
  import {
14
14
  runCrossCheck
15
- } from "./chunk-4VXFZSYF.js";
15
+ } from "./chunk-GSYBGOY3.js";
16
16
  import {
17
17
  createGenerateAgentDefinitionsCommand,
18
18
  generateAgentDefinitions
19
- } from "./chunk-E5EW5HVJ.js";
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-UWVMJEUJ.js";
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-5RSXUAMN.js";
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-7TIJXWG3.js";
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-RG3TNIUB.js";
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-XAVMTAUT.js";
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-MMLQPHZ5.js";
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-6YFHUIGT.js";
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 Command128 } from "commander";
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-6D2F2J57.js");
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-6D2F2J57.js");
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-6D2F2J57.js");
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: type === "all" || type === "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-6D2F2J57.js");
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-ZPX6O767.js"),
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-6D2F2J57.js");
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-6D2F2J57.js");
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-6D2F2J57.js");
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/proposals.ts
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 { Command as Command76 } from "commander";
9759
- import { resolve as resolve34 } from "path";
9760
- function projectRoot() {
9761
- return resolve34(process.env["HARNESS_PROJECT_ROOT"] ?? process.cwd());
9762
- }
9763
- function summarizeProposal(p) {
9764
- return {
9765
- id: p.id,
9766
- kind: p.kind,
9767
- targetSkill: p.targetSkill,
9768
- name: p.content.name,
9769
- status: p.status,
9770
- proposedBy: p.proposedBy,
9771
- createdAt: p.createdAt,
9772
- gateLastRunAt: p.gate?.lastRunAt,
9773
- findings: p.gate?.findings?.length ?? 0
9774
- };
9775
- }
9776
- async function runProposalsList(status) {
9777
- const opts = {};
9778
- if (status) opts.status = status;
9779
- const proposals = await listProposals(projectRoot(), opts);
9780
- return proposals.map(summarizeProposal);
9781
- }
9782
- async function runProposalsShow(id) {
9783
- return getProposal(projectRoot(), id);
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
- async function runProposalsReject(id, reason) {
9786
- const decision = {
9787
- decidedAt: (/* @__PURE__ */ new Date()).toISOString(),
9788
- decidedBy: process.env["USER"] ?? "cli",
9789
- action: "rejected",
9790
- reason
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
- async function actShowCommand(id) {
9816
- const proposal = await runProposalsShow(id);
9817
- if (!proposal) {
9818
- fail(`No such proposal: ${id}`);
9819
- return;
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
- async function actRejectCommand(id, opts) {
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 updated = await runProposalsReject(id, opts.reason);
9826
- console.log(JSON.stringify(summarizeProposal(updated), null, 2));
9827
- } catch (err) {
9828
- fail(err instanceof Error ? err.message : String(err));
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
- async function actApproveCommand(id) {
9832
- const orchestratorUrl = process.env["HARNESS_ORCHESTRATOR_URL"] ?? "http://127.0.0.1:4577";
9833
- const token = process.env["HARNESS_ADMIN_TOKEN"];
9834
- if (!token) {
9835
- fail("HARNESS_ADMIN_TOKEN is required to approve proposals (manage-proposals scope).");
9836
- return;
9837
- }
9838
- try {
9839
- const res = await fetch(`${orchestratorUrl}/api/v1/proposals/${id}/approve`, {
9840
- method: "POST",
9841
- headers: { authorization: `Bearer ${token}` }
9842
- });
9843
- if (!res.ok) {
9844
- fail(`HTTP ${res.status}: ${await res.text()}`);
9845
- return;
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 createProposalsCommand() {
9853
- const cmd = new Command76("proposals").description("Skill-proposal review queue");
9854
- cmd.command("list").description("List skill proposals in the local queue").option(
9855
- "--status <status>",
9856
- `Filter by status \u2014 one of ${ALLOWED_STATUSES.join(" | ")}`,
9857
- "open"
9858
- ).action(actListCommand);
9859
- cmd.command("show <id>").description("Show a single proposal in full").action(actShowCommand);
9860
- cmd.command("reject <id>").description("Reject a proposal with a one-line reason").requiredOption("--reason <text>", "Why the proposal is being rejected").action(actRejectCommand);
9861
- cmd.command("approve <id>").description(
9862
- "Approve a proposal (runs the soundness-review gate then promotes). Requires the orchestrator to be running."
9863
- ).action(actApproveCommand);
9864
- return cmd;
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
- // src/commands/publish-analyses.ts
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
- async function buildNameToExternalIdMap(projectPath) {
9902
- const mode = loadProjectRoadmapMode(projectPath);
9903
- if (mode === "file-less") {
9904
- const trackerCfg = loadTrackerClientConfigFromProject(projectPath);
9905
- if (!trackerCfg.ok) {
9906
- logger.error(`File-less mode: ${trackerCfg.error.message}`);
9907
- return null;
9908
- }
9909
- const clientR = createTrackerClient(trackerCfg.value);
9910
- if (!clientR.ok) {
9911
- logger.error(`File-less mode: ${clientR.error.message}`);
9912
- return null;
9913
- }
9914
- const r = await clientR.value.fetchAll();
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-6D2F2J57.js");
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 Command77("publish-analyses").description(
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 Command79 } from "commander";
10505
+ import { Command as Command81 } from "commander";
10022
10506
 
10023
10507
  // src/commands/pulse/run.ts
10024
10508
  init_dist3();
10025
- import { readFileSync as readFileSync18, mkdirSync as mkdirSync15, writeFileSync as writeFileSync18 } from "fs";
10026
- import { dirname as dirname18, join as join41, resolve as resolve36 } from "path";
10027
- import { Command as Command78 } from "commander";
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(readFileSync18(opts.configPath, "utf-8"));
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 = join41(opts.outputDir, `${ts}.md`);
10542
+ const filePath = join42(opts.outputDir, `${ts}.md`);
10059
10543
  mkdirSync15(dirname18(filePath), { recursive: true });
10060
- writeFileSync18(filePath, report);
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 Command78("run").description("Run a pulse: query configured adapters, sanitize, assemble single-page report").option(
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 Command79("pulse").description("Pulse (read-side observability) commands");
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 Command80 } from "commander";
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
- projectRoot: cwd,
10349
- diff: diff2,
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
- async function runReviewCi(opts) {
10356
- assertKnownRunner(opts.runner);
10357
- const cwd = opts.cwd ?? process.cwd();
10358
- const runGit = opts.runGit ?? defaultRunGit;
10359
- const range = resolveDiffRange({
10360
- ...opts.diffRange ? { range: opts.diffRange } : {},
10361
- cwd,
10362
- runGit
10363
- });
10364
- const rawDiff = (opts.resolveRaw ?? defaultResolveRaw)(range, cwd, runGit);
10365
- const diff2 = buildDiffInfo(rawDiff);
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 buildReviewBody(verdict) {
10374
- const findings = verdict.findings ?? [];
10375
- const blocking = verdict.blockingFindings ?? [];
10376
- const icon = verdict.assessment === "approve" ? "\u2705" : verdict.assessment === "request-changes" ? "\u{1F6D1}" : "\u{1F4AC}";
10377
- const out = [
10378
- `## ${icon} harness review-ci \u2014 ${verdict.assessment}`,
10379
- "",
10380
- `**Runner:** \`${verdict.runner}\` \u2022 **Findings:** ${findings.length} (blocking: ${blocking.length})`
10381
- ];
10382
- if (verdict.skipped) out.push("", `> \u26A0\uFE0F ${verdict.skipReason ?? "A review tier was skipped."}`);
10383
- if (blocking.length) out.push("", "### Blocking", ...blocking.map(findingLine));
10384
- const nonBlocking = findings.filter((f) => !blocking.some((b) => b.id === f.id));
10385
- if (nonBlocking.length) out.push("", "### Other findings", ...nonBlocking.map(findingLine));
10386
- if (!findings.length) out.push("", "No findings. \u{1F389}");
10387
- out.push("", "<sub>Posted by `harness review-ci`. The CI exit code is authoritative.</sub>");
10388
- return out.join("\n");
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
- var defaultPostReview = (verdict) => {
10391
- execFileSync6("gh", ["pr", "comment", "--body-file", "-"], {
10392
- input: buildReviewBody(verdict),
10393
- stdio: ["pipe", "pipe", "pipe"],
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
- postReview(result.verdict);
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
- logger.warn(
10408
- `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.`
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 Command87 } from "commander";
10706
+ import { Command as Command89 } from "commander";
10440
10707
 
10441
10708
  // src/commands/roadmap/migrate.ts
10442
10709
  init_dist3();
10443
- import { Command as Command82 } from "commander";
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 Command82("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(
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 Command83 } from "commander";
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 Command83("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(
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 Command84 } from "commander";
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 Command84("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(
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 Command85 } from "commander";
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 Command85("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(
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 Command86 } from "commander";
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 Command86("reconcile").description(
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 Command87("roadmap").description("Roadmap management");
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 Command91 } from "commander";
11570
+ import { Command as Command93 } from "commander";
11286
11571
 
11287
11572
  // src/commands/routing/config.ts
11288
- import { Command as Command88 } from "commander";
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 Command88("config").description("Print active routing config and resolved fallback chains").option("--json", "Emit JSON to stdout instead of human-readable text").action(async (opts) => {
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 Command89 } from "commander";
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 Command89("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) => {
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 Command90 } from "commander";
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 Command90("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(
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 Command91("routing").description(
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 Command92 } from "commander";
11522
- import { existsSync as existsSync30, readFileSync as readFileSync21, writeFileSync as writeFileSync22 } from "fs";
11523
- import { join as join48, relative as relative3 } from "path";
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 = readFileSync21(filePath, "utf8");
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(join48(targetDir, configFile), targetDir, scanner, options);
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 Command92("scan-config").description(
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 Command93 } from "commander";
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 Command93("search").description("Full-text search over archived + live session content.").argument(
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 Command94 } from "commander";
11994
+ import { Command as Command96 } from "commander";
11710
11995
  function createSecurityCraftCommand() {
11711
- return new Command94("security-craft").description(
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 Command95 } from "commander";
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 Command95("setup").description("Configure harness environment: slash commands, MCP, and more").action(async () => {
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 Command96 } from "commander";
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 Command96("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) => {
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 Command104 } from "commander";
12495
+ import { Command as Command106 } from "commander";
12211
12496
 
12212
12497
  // src/commands/skill/list.ts
12213
- import { Command as Command97 } from "commander";
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 Command97("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) => {
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 Command98 } from "commander";
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 execFileSync7 } from "child_process";
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 = execFileSync7("git", ["merge-base", "HEAD", "main"], {
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 = execFileSync7("git", ["diff", "--name-only", base], {
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 = execFileSync7("git", ["diff", "--stat", base], {
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-ECQAIZLU.js");
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 Command98("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(
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 Command99 } from "commander";
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 Command99("validate").description("Validate all skill.yaml files and SKILL.md structure").action(async (_opts, cmd) => {
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 Command100 } from "commander";
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 Command100("info").description("Show metadata for a skill").argument("<name>", "Skill name (e.g., harness-tdd)").action(async (name, _opts, cmd) => {
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 Command101 } from "commander";
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 Command101("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) => {
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 Command102 } from "commander";
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 Command102("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) => {
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 Command103 } from "commander";
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 execFileSync8 } from "child_process";
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
- execFileSync8("npm", publishArgs, {
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 Command103("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) => {
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 Command104("skill").description("Skill management commands");
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 Command105 } from "commander";
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 Command105("snapshot").description("Architecture timeline snapshot commands");
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 Command106 } from "commander";
13584
+ import { Command as Command108 } from "commander";
13300
13585
  function createSpecCraftCommand() {
13301
- return new Command106("spec-craft").description(
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 Command107 } from "commander";
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 Command107("stale-constraints").description(
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 Command112 } from "commander";
13759
+ import { Command as Command114 } from "commander";
13475
13760
 
13476
13761
  // src/commands/state/show.ts
13477
- import { Command as Command108 } from "commander";
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 Command108("show").description("Show current project state").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(async (opts, cmd) => {
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 Command109 } from "commander";
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 Command109("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));
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 Command110 } from "commander";
13836
+ import { Command as Command112 } from "commander";
13552
13837
  import * as path72 from "path";
13553
13838
  function createLearnCommand() {
13554
- return new Command110("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) => {
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 Command111 } from "commander";
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 Command111("streams").description("Manage state streams");
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 Command112("state").description("Project state management commands");
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 Command113 } from "commander";
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-6D2F2J57.js");
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 Command113("sync-analyses").description(
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 Command114 } from "commander";
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 Command114("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) => {
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 Command115 } from "commander";
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 Command115("taint").description("Manage sentinel session taint state");
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 Command119 } from "commander";
14206
+ import { Command as Command121 } from "commander";
13922
14207
 
13923
14208
  // src/commands/telemetry/identify.ts
13924
14209
  init_dist3();
13925
- import { Command as Command116 } from "commander";
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 Command116("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) => {
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 Command117 } from "commander";
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 Command117("status").description("Show current telemetry consent state, install ID, and identity").option("--json", "Output as JSON").action((opts) => {
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 Command118 } from "commander";
14334
+ import { Command as Command120 } from "commander";
14050
14335
 
14051
14336
  // src/bin/command-telemetry.ts
14052
- import { existsSync as existsSync43, readFileSync as readFileSync31, mkdirSync as mkdirSync22, appendFileSync, writeFileSync as writeFileSync28 } from "fs";
14053
- import { join as join62, parse as parsePath, resolve as resolve45 } from "path";
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(join62(dir, "harness.config.json"))) return dir;
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 = join62(cwd, ".harness", "metrics");
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 = join62(metricsDir, "adoption.jsonl");
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 = join62(cwd, "harness.config.json");
14400
+ const configPath = join63(cwd, "harness.config.json");
14116
14401
  if (!existsSync43(configPath)) return false;
14117
14402
  try {
14118
- const config = JSON.parse(readFileSync31(configPath, "utf-8"));
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 = join62(cwd, ".harness", "metrics", "adoption.jsonl");
14415
+ const adoptionFile = join63(cwd, ".harness", "metrics", "adoption.jsonl");
14131
14416
  if (!existsSync43(adoptionFile)) return;
14132
14417
  if (isTelemetryOptedOut(cwd)) return;
14133
- const reporterPath = join62(cwd, ".harness", "hooks", "telemetry-reporter.js");
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 Command118("test").description("Send a test event to PostHog and verify connectivity").action(async () => {
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 Command119("telemetry").description("Telemetry identity and status management");
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 Command120 } from "commander";
14501
+ import { Command as Command122 } from "commander";
14217
14502
  function createTestCraftCommand() {
14218
- return new Command120("test-craft").description(
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 Command121 } from "commander";
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(JSON.stringify({ error: "No knowledge graph found. Run `harness scan` first." }));
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("No requirements found in graph. Run `harness scan` to ingest spec requirements.");
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 Command121("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) => {
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 Command122 } from "commander";
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 Command122("uninstall");
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 Command123 } from "commander";
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 Command123("uninstall-constraints");
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 Command124 } from "commander";
14595
- import { execFile as execFile2, execFileSync as execFileSync9 } from "child_process";
14596
- import { realpathSync, existsSync as existsSync44, readFileSync as readFileSync32 } from "fs";
14597
- import { dirname as dirname24, join as join65 } from "path";
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 = execFileSync9(pm, ["list", "-g", "--json"], {
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 = execFileSync9(pm, ["list", "-g", "--json"], {
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(join65(dir, "package.json"))) {
14977
+ if (existsSync44(join66(dir, "package.json"))) {
14689
14978
  try {
14690
- const pkg = JSON.parse(readFileSync32(join65(dir, "package.json"), "utf-8"));
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 = execFileSync9(cmd, args, { encoding: "utf-8", timeout: 5e3 });
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(readFileSync32(join65(packageDir, "package.json"), "utf-8"));
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
- execFileSync9(cmd.bin, cmd.args, { stdio: "inherit", timeout: 12e4 });
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 = join65(cwd, "harness.config.json");
15117
+ const configPath = join66(cwd, "harness.config.json");
14829
15118
  if (!existsSync44(configPath)) return;
14830
15119
  let profile = "standard";
14831
- const profilePath = join65(cwd, ".harness", "hooks", "profile.json");
15120
+ const profilePath = join66(cwd, ".harness", "hooks", "profile.json");
14832
15121
  try {
14833
- const data = JSON.parse(readFileSync32(profilePath, "utf-8"));
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
- execFileSync9("harness", ["graph", "scan", "."], { stdio: "inherit" });
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
- execFileSync9("harness", ["generate", ...isGlobal ? ["--global"] : []], {
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
- execFileSync9(pm, ["install", "-g", ...installPkgs], { stdio: "inherit", timeout: 12e4 });
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 Command124("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(
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 Command125 } from "commander";
15261
+ import { Command as Command127 } from "commander";
14973
15262
  async function loadAndPriceRecords(cwd, includeClaudeSessions = false) {
14974
- const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-6D2F2J57.js");
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-6D2F2J57.js");
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-6D2F2J57.js");
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-6D2F2J57.js");
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-6D2F2J57.js");
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 Command125("usage").description("Token usage and cost tracking");
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 Command126 } from "commander";
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 execFileSync10 } from "child_process";
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 = execFileSync10("git", ["config", "--get", "merge.ours.driver"], {
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-J5PVGICB.js");
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 Command126("validate").description("Run all validation checks").option("--cross-check", "Run cross-artifact consistency validation").option(
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 Command127 } from "commander";
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 Command127("verify").description(
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 Command128();
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