@harness-engineering/cli 1.24.3 → 1.25.1

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 (116) hide show
  1. package/dist/agents/skills/claude-code/harness-architecture-advisor/SKILL.md +10 -6
  2. package/dist/agents/skills/claude-code/harness-code-review/SKILL.md +40 -5
  3. package/dist/agents/skills/claude-code/harness-git-workflow/SKILL.md +32 -11
  4. package/dist/agents/skills/claude-code/harness-planning/SKILL.md +19 -0
  5. package/dist/agents/skills/claude-code/harness-skill-authoring/SKILL.md +42 -7
  6. package/dist/agents/skills/claude-code/harness-verification/SKILL.md +25 -0
  7. package/dist/agents/skills/codex/harness-architecture-advisor/SKILL.md +10 -6
  8. package/dist/agents/skills/codex/harness-code-review/SKILL.md +40 -5
  9. package/dist/agents/skills/codex/harness-git-workflow/SKILL.md +32 -11
  10. package/dist/agents/skills/codex/harness-planning/SKILL.md +19 -0
  11. package/dist/agents/skills/codex/harness-skill-authoring/SKILL.md +42 -7
  12. package/dist/agents/skills/codex/harness-verification/SKILL.md +25 -0
  13. package/dist/agents/skills/cursor/harness-architecture-advisor/SKILL.md +10 -6
  14. package/dist/agents/skills/cursor/harness-code-review/SKILL.md +40 -5
  15. package/dist/agents/skills/cursor/harness-git-workflow/SKILL.md +32 -11
  16. package/dist/agents/skills/cursor/harness-planning/SKILL.md +19 -0
  17. package/dist/agents/skills/cursor/harness-skill-authoring/SKILL.md +42 -7
  18. package/dist/agents/skills/cursor/harness-verification/SKILL.md +25 -0
  19. package/dist/agents/skills/gemini-cli/harness-architecture-advisor/SKILL.md +10 -6
  20. package/dist/agents/skills/gemini-cli/harness-code-review/SKILL.md +40 -5
  21. package/dist/agents/skills/gemini-cli/harness-git-workflow/SKILL.md +32 -11
  22. package/dist/agents/skills/gemini-cli/harness-planning/SKILL.md +19 -0
  23. package/dist/agents/skills/gemini-cli/harness-skill-authoring/SKILL.md +42 -7
  24. package/dist/agents/skills/gemini-cli/harness-verification/SKILL.md +25 -0
  25. package/dist/agents-md-MCUM4SIZ.js +10 -0
  26. package/dist/{architecture-FBSLURIB.js → architecture-HNIO6AUX.js} +6 -5
  27. package/dist/{assess-project-74UVWPMB.js → assess-project-6MV5TNY3.js} +2 -1
  28. package/dist/bin/harness-mcp.js +18 -17
  29. package/dist/bin/harness.js +47 -31
  30. package/dist/{check-phase-gate-WY6UICCL.js → check-phase-gate-VCBQHQAC.js} +6 -5
  31. package/dist/{chunk-Q3XYV5UC.js → chunk-47N6R2F4.js} +5 -1
  32. package/dist/{chunk-3XAPHB2Z.js → chunk-4NK7Z3BP.js} +1 -1
  33. package/dist/{chunk-I4QR3HNH.js → chunk-5BQ5BOJL.js} +5 -1
  34. package/dist/{chunk-LM5Z2WCA.js → chunk-6VZQJ5CX.js} +5 -1
  35. package/dist/{chunk-NV4FPO7J.js → chunk-AT74HEQM.js} +1 -1
  36. package/dist/{chunk-TDLOFNNQ.js → chunk-BOQRTARD.js} +1 -1
  37. package/dist/{chunk-ZAMT24QN.js → chunk-CJXVNDZZ.js} +1537 -737
  38. package/dist/{chunk-A737JDL4.js → chunk-CXJTVICF.js} +4 -4
  39. package/dist/{chunk-LOUH2LIC.js → chunk-EHRZMOQ2.js} +5 -1
  40. package/dist/{chunk-L5UONZ53.js → chunk-F23H3U5U.js} +6 -2
  41. package/dist/{chunk-YPKKEP3O.js → chunk-FES2YEQU.js} +9 -9
  42. package/dist/{chunk-SPTKLCKC.js → chunk-FIQL2HND.js} +6 -2
  43. package/dist/{chunk-FP53DDB5.js → chunk-FSNPBT74.js} +5 -1
  44. package/dist/{chunk-TB427QOK.js → chunk-GEEYCQDS.js} +13 -9
  45. package/dist/{chunk-ZOVATVQC.js → chunk-HIK6OEUF.js} +6 -1
  46. package/dist/chunk-K2SON7TI.js +5382 -0
  47. package/dist/chunk-KFQGP6VL.js +33 -0
  48. package/dist/{chunk-V2FGX2KD.js → chunk-M55DGGF3.js} +9 -5
  49. package/dist/{chunk-H4U2QNY2.js → chunk-MI6MA6OP.js} +16888 -13143
  50. package/dist/{chunk-YDRB55Q4.js → chunk-P7PANON5.js} +5 -1
  51. package/dist/chunk-RL4VMEXL.js +53 -0
  52. package/dist/{chunk-XNEMDKV5.js → chunk-RRHUCDRD.js} +1 -1
  53. package/dist/{chunk-O6UF33QH.js → chunk-RX7TUMBR.js} +320 -136
  54. package/dist/{chunk-3VYWO6QN.js → chunk-TYV4EUAD.js} +12 -8
  55. package/dist/{chunk-FJYP32IV.js → chunk-UQZBZINS.js} +6 -6
  56. package/dist/{chunk-UFQQBGC3.js → chunk-UV3BZMGT.js} +64 -3
  57. package/dist/{chunk-5PMRARB5.js → chunk-WIQA4BSH.js} +12 -5
  58. package/dist/{chunk-UQEUYRBP.js → chunk-WWACIIBA.js} +1 -1
  59. package/dist/{chunk-LVYPPKZI.js → chunk-XTITAVUR.js} +5 -1
  60. package/dist/{chunk-WEN5Z7CL.js → chunk-YTP2UDPV.js} +3 -3
  61. package/dist/ci-workflow-RTM7VVTD.js +10 -0
  62. package/dist/{constants-5JGUXPEK.js → constants-P4M3C2T3.js} +1 -0
  63. package/dist/{create-skill-QCXINA5Q.js → create-skill-6QWJHQYS.js} +3 -2
  64. package/dist/{dist-666AAZQ6.js → dist-3EWNRFFQ.js} +6 -1
  65. package/dist/{dist-RRMRZRV7.js → dist-ABTKRYZH.js} +1 -0
  66. package/dist/{dist-QW5G3GX3.js → dist-RADHOOXG.js} +4 -1
  67. package/dist/{dist-7EBSGAHX.js → dist-WCSJHQPK.js} +106 -3
  68. package/dist/{docs-H34GBVRS.js → docs-UBOGGHTY.js} +6 -5
  69. package/dist/engine-MJJAP5CH.js +10 -0
  70. package/dist/{entropy-ZAY73R6A.js → entropy-EMSXF2PX.js} +5 -4
  71. package/dist/{feedback-TMEGYMWU.js → feedback-ZLUX72HD.js} +2 -1
  72. package/dist/{generate-agent-definitions-PQPG6SX5.js → generate-agent-definitions-AWLPJ27C.js} +6 -5
  73. package/dist/{glob-helper-SRXMZPKM.js → glob-helper-VCQXK5XY.js} +2 -0
  74. package/dist/{graph-loader-M6FXJAKK.js → graph-loader-JHQVQRUS.js} +2 -1
  75. package/dist/hooks/adoption-tracker.js +189 -0
  76. package/dist/hooks/block-no-verify.js +50 -0
  77. package/dist/hooks/cost-tracker.js +66 -0
  78. package/dist/hooks/pre-compact-state.js +115 -0
  79. package/dist/hooks/profiles.ts +48 -0
  80. package/dist/hooks/protect-config.js +75 -0
  81. package/dist/hooks/quality-gate.js +131 -0
  82. package/dist/hooks/sentinel-post.js +159 -0
  83. package/dist/hooks/sentinel-pre.js +244 -0
  84. package/dist/hooks/telemetry-reporter.js +248 -0
  85. package/dist/index.d.ts +13 -7
  86. package/dist/index.js +30 -29
  87. package/dist/loader-JVSJZSWZ.js +12 -0
  88. package/dist/mcp-2553PNUC.js +38 -0
  89. package/dist/{performance-N67YJJDG.js → performance-7AGWJUY4.js} +6 -5
  90. package/dist/review-pipeline-ZWVQJTJX.js +13 -0
  91. package/dist/{runner-TY7DJGQV.js → runner-AAEF2TXY.js} +1 -0
  92. package/dist/runtime-D6YUQPP2.js +11 -0
  93. package/dist/{scan-U67OKDRS.js → scan-MPJ6JHUY.js} +2 -1
  94. package/dist/security-JLZUAQYT.js +14 -0
  95. package/dist/skill-executor-MOCUIAYS.js +9 -0
  96. package/dist/templates/base/.agnix.toml +16 -0
  97. package/dist/templates/orchestrator/WORKFLOW.md +55 -7
  98. package/dist/templates/orchestrator/template.json +1 -0
  99. package/dist/tool-tiers-7QGZ3FKY.js +98 -0
  100. package/dist/validate-TIIHRPMA.js +14 -0
  101. package/dist/validate-cross-check-ZOWFA3DB.js +10 -0
  102. package/dist/{version-KFFPOQAX.js → version-CUI434OP.js} +1 -0
  103. package/package.json +4 -4
  104. package/dist/agents-md-PBKKTSQY.js +0 -9
  105. package/dist/chunk-5LMZA5LZ.js +0 -38
  106. package/dist/chunk-OZIHPMA7.js +0 -5171
  107. package/dist/ci-workflow-QZRHAIO2.js +0 -9
  108. package/dist/engine-VUQEAJFZ.js +0 -9
  109. package/dist/loader-Y6A42WBD.js +0 -11
  110. package/dist/mcp-LCHC4NZ5.js +0 -37
  111. package/dist/review-pipeline-YXF5ITL2.js +0 -12
  112. package/dist/runtime-XNJUJCSG.js +0 -10
  113. package/dist/security-L2YN3CTI.js +0 -9
  114. package/dist/skill-executor-GA7BDX3F.js +0 -8
  115. package/dist/validate-MNE25KLZ.js +0 -13
  116. package/dist/validate-cross-check-Y4PDR63C.js +0 -9
@@ -1,25 +1,25 @@
1
1
  import {
2
2
  generateAgentsMd
3
- } from "./chunk-I4QR3HNH.js";
3
+ } from "./chunk-5BQ5BOJL.js";
4
4
  import {
5
5
  generateCIWorkflow
6
- } from "./chunk-LOUH2LIC.js";
6
+ } from "./chunk-EHRZMOQ2.js";
7
7
  import {
8
8
  TemplateEngine
9
- } from "./chunk-FP53DDB5.js";
9
+ } from "./chunk-FSNPBT74.js";
10
10
  import {
11
11
  generate,
12
12
  validate
13
13
  } from "./chunk-CQ553GZO.js";
14
14
  import {
15
15
  generateRuntime
16
- } from "./chunk-LM5Z2WCA.js";
16
+ } from "./chunk-6VZQJ5CX.js";
17
17
  import {
18
18
  runPersona
19
19
  } from "./chunk-GISMXMVL.js";
20
20
  import {
21
21
  executeSkill
22
- } from "./chunk-UQEUYRBP.js";
22
+ } from "./chunk-WWACIIBA.js";
23
23
  import {
24
24
  ALLOWED_PERSONA_COMMANDS
25
25
  } from "./chunk-TEFCFC4H.js";
@@ -28,21 +28,21 @@ import {
28
28
  OutputMode,
29
29
  createCheckPhaseGateCommand,
30
30
  findFiles
31
- } from "./chunk-SPTKLCKC.js";
31
+ } from "./chunk-FIQL2HND.js";
32
32
  import {
33
33
  createGenerateAgentDefinitionsCommand,
34
34
  generateAgentDefinitions
35
- } from "./chunk-NV4FPO7J.js";
35
+ } from "./chunk-AT74HEQM.js";
36
36
  import {
37
37
  toKebabCase
38
38
  } from "./chunk-KET4QQZB.js";
39
39
  import {
40
40
  listPersonas,
41
41
  loadPersona
42
- } from "./chunk-YDRB55Q4.js";
42
+ } from "./chunk-P7PANON5.js";
43
43
  import {
44
44
  createScanCommand
45
- } from "./chunk-3XAPHB2Z.js";
45
+ } from "./chunk-4NK7Z3BP.js";
46
46
  import {
47
47
  appendFrameworkAgents,
48
48
  captureHealthSnapshot,
@@ -56,11 +56,11 @@ import {
56
56
  loadOrRebuildIndex,
57
57
  persistToolingConfig,
58
58
  recommend
59
- } from "./chunk-O6UF33QH.js";
59
+ } from "./chunk-RX7TUMBR.js";
60
60
  import {
61
61
  findConfigFile,
62
62
  resolveConfig
63
- } from "./chunk-5PMRARB5.js";
63
+ } from "./chunk-WIQA4BSH.js";
64
64
  import {
65
65
  VALID_PLATFORMS
66
66
  } from "./chunk-3ISINLYT.js";
@@ -74,7 +74,7 @@ import {
74
74
  } from "./chunk-6B6UN6SG.js";
75
75
  import {
76
76
  loadGraphStore
77
- } from "./chunk-TDLOFNNQ.js";
77
+ } from "./chunk-BOQRTARD.js";
78
78
  import {
79
79
  CLI_VERSION
80
80
  } from "./chunk-BM3PWGXQ.js";
@@ -84,11 +84,14 @@ import {
84
84
  BaselineManager,
85
85
  BlueprintGenerator,
86
86
  BundleSchema,
87
+ CINotifier,
87
88
  CriticalPathResolver,
88
89
  EntropyAnalyzer,
90
+ GitHubIssuesSyncAdapter,
89
91
  PredictionEngine,
90
92
  ProjectScanner,
91
93
  SecurityScanner,
94
+ SecurityTimelineManager,
92
95
  SpecImpactEstimator,
93
96
  TimelineManager,
94
97
  TypeScriptParser,
@@ -110,16 +113,20 @@ import {
110
113
  detectDeadCode,
111
114
  detectDocDrift,
112
115
  diff,
116
+ dist_exports,
113
117
  extractBundle,
114
118
  generateSuggestions,
115
119
  getOrCreateInstallId,
120
+ init_dist,
116
121
  listStreams,
117
122
  listTaintedSessions,
118
123
  loadState,
119
124
  loadStreamIndex,
125
+ loadTrackerSyncConfig,
120
126
  mapInjectionFindings,
121
127
  mapSecurityFindings,
122
128
  parseDiff,
129
+ parseFileRegions,
123
130
  parseManifest,
124
131
  parseSecurityConfig,
125
132
  pruneLearnings,
@@ -135,35 +142,41 @@ import {
135
142
  runReviewPipeline,
136
143
  scanForInjection,
137
144
  setActiveStream,
145
+ validateAgentConfigs,
138
146
  validateAgentsMap,
139
147
  validateDependencies,
140
148
  validateKnowledgeMap,
141
149
  writeConfig,
142
150
  writeLockfile
143
- } from "./chunk-H4U2QNY2.js";
151
+ } from "./chunk-MI6MA6OP.js";
144
152
  import {
145
153
  Err,
146
154
  Ok
147
- } from "./chunk-5LMZA5LZ.js";
155
+ } from "./chunk-RL4VMEXL.js";
148
156
  import {
149
157
  createCreateSkillCommand
150
- } from "./chunk-XNEMDKV5.js";
158
+ } from "./chunk-RRHUCDRD.js";
151
159
  import {
152
160
  logger
153
161
  } from "./chunk-EBJQ6N4M.js";
154
162
  import {
155
163
  SkillMetadataSchema
156
- } from "./chunk-ZOVATVQC.js";
164
+ } from "./chunk-HIK6OEUF.js";
157
165
  import {
158
166
  CLIError,
159
167
  ExitCode,
160
168
  handleError
161
169
  } from "./chunk-3WGJMBKH.js";
170
+ import {
171
+ __require,
172
+ __toCommonJS
173
+ } from "./chunk-KFQGP6VL.js";
162
174
 
163
175
  // src/index.ts
164
- import { Command as Command79 } from "commander";
176
+ import { Command as Command83 } from "commander";
165
177
 
166
178
  // src/commands/add.ts
179
+ init_dist();
167
180
  import { Command } from "commander";
168
181
  import * as fs from "fs";
169
182
  import * as path from "path";
@@ -251,7 +264,7 @@ function addDoc(name, cwd, configResult, created) {
251
264
  return Ok(void 0);
252
265
  }
253
266
  async function addSkill(name, cwd, created) {
254
- const { generateSkillFiles: generateSkillFiles2 } = await import("./create-skill-QCXINA5Q.js");
267
+ const { generateSkillFiles: generateSkillFiles2 } = await import("./create-skill-6QWJHQYS.js");
255
268
  generateSkillFiles2({
256
269
  name,
257
270
  description: `${name} skill`,
@@ -349,7 +362,7 @@ function registerSkillsCommand(adoption) {
349
362
  const globalOpts = cmd.optsWithGlobals();
350
363
  const limit = Math.max(parseInt(opts.limit, 10) || 20, 1);
351
364
  const cwd = process.cwd();
352
- const { readAdoptionRecords, aggregateBySkill } = await import("./dist-7EBSGAHX.js");
365
+ const { readAdoptionRecords, aggregateBySkill } = await import("./dist-WCSJHQPK.js");
353
366
  const records = readAdoptionRecords(cwd);
354
367
  if (records.length === 0) {
355
368
  if (globalOpts.json) {
@@ -385,7 +398,7 @@ function registerRecentCommand(adoption) {
385
398
  const globalOpts = cmd.optsWithGlobals();
386
399
  const limit = Math.max(parseInt(opts.limit, 10) || 20, 1);
387
400
  const cwd = process.cwd();
388
- const { readAdoptionRecords } = await import("./dist-7EBSGAHX.js");
401
+ const { readAdoptionRecords } = await import("./dist-WCSJHQPK.js");
389
402
  const records = readAdoptionRecords(cwd);
390
403
  if (records.length === 0) {
391
404
  if (globalOpts.json) {
@@ -422,7 +435,7 @@ function registerSkillCommand(adoption) {
422
435
  adoption.command("skill <name>").description("Show detail for a specific skill").action(async (name, _opts, cmd) => {
423
436
  const globalOpts = cmd.optsWithGlobals();
424
437
  const cwd = process.cwd();
425
- const { readAdoptionRecords, aggregateBySkill } = await import("./dist-7EBSGAHX.js");
438
+ const { readAdoptionRecords, aggregateBySkill } = await import("./dist-WCSJHQPK.js");
426
439
  const records = readAdoptionRecords(cwd);
427
440
  const skillRecords = records.filter((r) => r.skill === name);
428
441
  if (skillRecords.length === 0) {
@@ -488,6 +501,8 @@ function createAdoptionCommand() {
488
501
  import { Command as Command5 } from "commander";
489
502
 
490
503
  // src/commands/agent/run.ts
504
+ init_dist();
505
+ init_dist();
491
506
  import { Command as Command3 } from "commander";
492
507
  import * as path2 from "path";
493
508
  import * as childProcess from "child_process";
@@ -609,6 +624,7 @@ function createRunCommand() {
609
624
  }
610
625
 
611
626
  // src/commands/agent/review.ts
627
+ init_dist();
612
628
  import { Command as Command4 } from "commander";
613
629
  import { execSync } from "child_process";
614
630
  async function runAgentReview(options) {
@@ -657,7 +673,9 @@ async function runAgentReview(options) {
657
673
  comment: options.comment ?? false,
658
674
  ci: options.ci ?? false,
659
675
  deep: options.deep ?? false,
660
- noMechanical: options.noMechanical ?? false
676
+ noMechanical: options.noMechanical ?? false,
677
+ ...options.thorough ? { thorough: true } : {},
678
+ ...options.isolated ? { isolated: true } : {}
661
679
  },
662
680
  config
663
681
  });
@@ -703,7 +721,10 @@ function printReviewResult(result, mode) {
703
721
  }
704
722
  }
705
723
  function createReviewCommand() {
706
- return new Command4("review").description("Run unified code review pipeline on current changes").option("--comment", "Post inline comments to GitHub PR").option("--ci", "Enable eligibility gate, non-interactive output").option("--deep", "Add threat modeling pass to security agent").option("--no-mechanical", "Skip mechanical checks").action(async (opts, cmd) => {
724
+ return new Command4("review").description("Run unified code review pipeline on current changes").option("--comment", "Post inline comments to GitHub PR").option("--ci", "Enable eligibility gate, non-interactive output").option("--deep", "Add threat modeling pass to security agent").option("--no-mechanical", "Skip mechanical checks").option("--thorough", "Generate task-specific rubric before reading implementation").option(
725
+ "--isolated",
726
+ "Two-stage review: spec-compliance then code-quality with disjoint context"
727
+ ).action(async (opts, cmd) => {
707
728
  const globalOpts = cmd.optsWithGlobals();
708
729
  const mode = resolveReviewMode(globalOpts);
709
730
  const result = await runAgentReview({
@@ -714,7 +735,9 @@ function createReviewCommand() {
714
735
  comment: opts.comment,
715
736
  ci: opts.ci,
716
737
  deep: opts.deep,
717
- noMechanical: opts.mechanical === false
738
+ noMechanical: opts.mechanical === false,
739
+ thorough: opts.thorough,
740
+ isolated: opts.isolated
718
741
  });
719
742
  if (!result.ok) {
720
743
  if (mode === OutputMode.JSON) console.log(JSON.stringify({ error: result.error.message }));
@@ -737,21 +760,130 @@ function createAgentCommand() {
737
760
  return command;
738
761
  }
739
762
 
740
- // src/commands/blueprint.ts
763
+ // src/commands/audit-protected.ts
764
+ init_dist();
741
765
  import { Command as Command6 } from "commander";
742
766
  import * as path3 from "path";
767
+ import * as fs2 from "fs";
768
+
769
+ // src/utils/output.ts
770
+ function resolveOutputMode(globalOpts) {
771
+ if (globalOpts.json) return OutputMode.JSON;
772
+ if (globalOpts.quiet) return OutputMode.QUIET;
773
+ if (globalOpts.verbose) return OutputMode.VERBOSE;
774
+ return OutputMode.TEXT;
775
+ }
776
+
777
+ // src/commands/audit-protected.ts
778
+ import { glob } from "glob";
779
+ async function runAuditProtected(options) {
780
+ const cwd = options.cwd ?? process.cwd();
781
+ const configResult = resolveConfig(options.configPath);
782
+ if (!configResult.ok) {
783
+ return Err(configResult.error);
784
+ }
785
+ const config = configResult.value;
786
+ const rootDir = path3.resolve(cwd, config.rootDir);
787
+ const excludePatterns = config.entropy?.excludePatterns ?? ["**/node_modules/**", "**/*.test.ts"];
788
+ let files;
789
+ try {
790
+ files = await glob("**/*.{ts,tsx,js,jsx,py,sh}", {
791
+ cwd: rootDir,
792
+ ignore: excludePatterns
793
+ });
794
+ } catch (e) {
795
+ return Err(new CLIError(`Failed to scan files: ${e.message}`, ExitCode.ERROR));
796
+ }
797
+ const allRegions = [];
798
+ const allIssues = [];
799
+ for (const relativePath of files) {
800
+ const absolutePath = path3.resolve(rootDir, relativePath);
801
+ let content;
802
+ try {
803
+ content = fs2.readFileSync(absolutePath, "utf-8");
804
+ } catch {
805
+ continue;
806
+ }
807
+ const { regions, issues } = parseFileRegions(relativePath, content);
808
+ allRegions.push(...regions);
809
+ allIssues.push(...issues);
810
+ }
811
+ return Ok({
812
+ regions: allRegions,
813
+ issues: allIssues,
814
+ fileCount: files.length
815
+ });
816
+ }
817
+ function formatRegion(region) {
818
+ const lineRange = region.startLine === region.endLine ? `${region.startLine}` : `${region.startLine}-${region.endLine}`;
819
+ const scopes = region.scopes.join(",");
820
+ const reason = region.reason ? ` ${region.reason}` : "";
821
+ return ` ${region.file}:${lineRange} [${scopes}]${reason}`;
822
+ }
823
+ function printAuditResult(result, formatter) {
824
+ const regionCount = result.regions.length;
825
+ const fileSet = new Set(result.regions.map((r) => r.file));
826
+ console.log(
827
+ formatter.formatSummary("Protected regions", `${regionCount} in ${fileSet.size} file(s)`, true)
828
+ );
829
+ if (regionCount > 0) {
830
+ console.log(`
831
+ Found ${regionCount} protected region(s) in ${fileSet.size} file(s):
832
+ `);
833
+ for (const region of result.regions) {
834
+ console.log(formatRegion(region));
835
+ }
836
+ }
837
+ if (result.issues.length > 0) {
838
+ console.log(`
839
+ Issues (${result.issues.length}):`);
840
+ for (const issue of result.issues) {
841
+ console.log(` ${issue.file}:${issue.line} \u2014 ${issue.message}`);
842
+ }
843
+ }
844
+ }
845
+ function createAuditProtectedCommand() {
846
+ const command = new Command6("audit-protected").description("Report all harness-ignore protected code regions").action(async (_opts, cmd) => {
847
+ const globalOpts = cmd.optsWithGlobals();
848
+ const mode = resolveOutputMode(globalOpts);
849
+ const formatter = new OutputFormatter(mode);
850
+ const result = await runAuditProtected({
851
+ configPath: globalOpts.config
852
+ });
853
+ if (!result.ok) {
854
+ if (mode === OutputMode.JSON) {
855
+ console.log(JSON.stringify({ error: result.error.message }));
856
+ } else {
857
+ logger.error(result.error.message);
858
+ }
859
+ process.exit(result.error.exitCode);
860
+ }
861
+ if (mode === OutputMode.JSON) {
862
+ console.log(JSON.stringify(result.value, null, 2));
863
+ } else if (mode !== OutputMode.QUIET || result.value.regions.length > 0) {
864
+ printAuditResult(result.value, formatter);
865
+ }
866
+ process.exit(result.value.issues.length > 0 ? ExitCode.VALIDATION_FAILED : ExitCode.SUCCESS);
867
+ });
868
+ return command;
869
+ }
870
+
871
+ // src/commands/blueprint.ts
872
+ init_dist();
873
+ import { Command as Command7 } from "commander";
874
+ import * as path4 from "path";
743
875
  function createBlueprintCommand() {
744
- return new Command6("blueprint").description("Generate a self-contained, interactive blueprint of the codebase").argument("[path]", "Path to the project root", ".").option("-o, --output <dir>", "Output directory", "docs/blueprint").action(async (projectPath, options) => {
876
+ return new Command7("blueprint").description("Generate a self-contained, interactive blueprint of the codebase").argument("[path]", "Path to the project root", ".").option("-o, --output <dir>", "Output directory", "docs/blueprint").action(async (projectPath, options) => {
745
877
  try {
746
- const rootDir = path3.resolve(projectPath);
747
- const outputDir = path3.resolve(options.output);
878
+ const rootDir = path4.resolve(projectPath);
879
+ const outputDir = path4.resolve(options.output);
748
880
  logger.info(`Scanning project at ${rootDir}...`);
749
881
  const scanner = new ProjectScanner(rootDir);
750
882
  const data = await scanner.scan();
751
883
  logger.info(`Generating blueprint to ${outputDir}...`);
752
884
  const generator = new BlueprintGenerator();
753
885
  await generator.generate(data, { outputDir });
754
- logger.success(`Blueprint generated successfully at ${path3.join(outputDir, "index.html")}`);
886
+ logger.success(`Blueprint generated successfully at ${path4.join(outputDir, "index.html")}`);
755
887
  } catch (error) {
756
888
  logger.error(
757
889
  `Failed to generate blueprint: ${error instanceof Error ? error.message : String(error)}`
@@ -762,7 +894,8 @@ function createBlueprintCommand() {
762
894
  }
763
895
 
764
896
  // src/commands/check-arch.ts
765
- import { Command as Command7 } from "commander";
897
+ init_dist();
898
+ import { Command as Command8 } from "commander";
766
899
  import { execSync as execSync2 } from "child_process";
767
900
  function getCommitHash(cwd) {
768
901
  try {
@@ -856,7 +989,7 @@ async function runCheckArch(options) {
856
989
  thresholdViolations
857
990
  });
858
991
  }
859
- function resolveOutputMode(globalOpts) {
992
+ function resolveOutputMode2(globalOpts) {
860
993
  if (globalOpts.json) return OutputMode.JSON;
861
994
  if (globalOpts.quiet) return OutputMode.QUIET;
862
995
  if (globalOpts.verbose) return OutputMode.VERBOSE;
@@ -892,9 +1025,9 @@ function printArchResult(value, mode, formatter) {
892
1025
  if (output) console.log(output);
893
1026
  }
894
1027
  function createCheckArchCommand() {
895
- const command = new Command7("check-arch").description("Check architecture assertions against baseline and thresholds").option("--update-baseline", "Capture current state as new baseline").option("--module <path>", "Check a single module").action(async (opts, cmd) => {
1028
+ const command = new Command8("check-arch").description("Check architecture assertions against baseline and thresholds").option("--update-baseline", "Capture current state as new baseline").option("--module <path>", "Check a single module").action(async (opts, cmd) => {
896
1029
  const globalOpts = cmd.optsWithGlobals();
897
- const mode = resolveOutputMode(globalOpts);
1030
+ const mode = resolveOutputMode2(globalOpts);
898
1031
  const formatter = new OutputFormatter(mode);
899
1032
  const result = await runCheckArch({
900
1033
  configPath: globalOpts.config,
@@ -922,8 +1055,10 @@ function createCheckArchCommand() {
922
1055
  }
923
1056
 
924
1057
  // src/commands/check-deps.ts
925
- import { Command as Command8 } from "commander";
926
- import * as path4 from "path";
1058
+ init_dist();
1059
+ init_dist();
1060
+ import { Command as Command9 } from "commander";
1061
+ import * as path5 from "path";
927
1062
  async function runCheckDeps(options) {
928
1063
  const cwd = options.cwd ?? process.cwd();
929
1064
  const configResult = resolveConfig(options.configPath);
@@ -939,7 +1074,7 @@ async function runCheckDeps(options) {
939
1074
  if (!config.layers || config.layers.length === 0) {
940
1075
  return Ok(result);
941
1076
  }
942
- const rootDir = path4.resolve(cwd, config.rootDir);
1077
+ const rootDir = path5.resolve(cwd, config.rootDir);
943
1078
  const parser = new TypeScriptParser();
944
1079
  const layers = config.layers.map((l) => defineLayer(l.name, [l.pattern], l.allowedDependencies));
945
1080
  const layerConfig = {
@@ -1014,25 +1149,17 @@ async function runCheckDepsAction(globalOpts) {
1014
1149
  process.exit(result.value.valid ? ExitCode.SUCCESS : ExitCode.VALIDATION_FAILED);
1015
1150
  }
1016
1151
  function createCheckDepsCommand() {
1017
- const command = new Command8("check-deps").description("Validate dependency layers and detect circular dependencies").action(async (_opts, cmd) => {
1152
+ const command = new Command9("check-deps").description("Validate dependency layers and detect circular dependencies").action(async (_opts, cmd) => {
1018
1153
  await runCheckDepsAction(cmd.optsWithGlobals());
1019
1154
  });
1020
1155
  return command;
1021
1156
  }
1022
1157
 
1023
1158
  // src/commands/check-docs.ts
1024
- import { Command as Command9 } from "commander";
1025
- import * as path5 from "path";
1026
-
1027
- // src/utils/output.ts
1028
- function resolveOutputMode2(globalOpts) {
1029
- if (globalOpts.json) return OutputMode.JSON;
1030
- if (globalOpts.quiet) return OutputMode.QUIET;
1031
- if (globalOpts.verbose) return OutputMode.VERBOSE;
1032
- return OutputMode.TEXT;
1033
- }
1034
-
1035
- // src/commands/check-docs.ts
1159
+ init_dist();
1160
+ init_dist();
1161
+ import { Command as Command10 } from "commander";
1162
+ import * as path6 from "path";
1036
1163
  async function runCheckDocs(options) {
1037
1164
  const cwd = options.cwd ?? process.cwd();
1038
1165
  const minCoverage = options.minCoverage ?? 80;
@@ -1041,8 +1168,8 @@ async function runCheckDocs(options) {
1041
1168
  return configResult;
1042
1169
  }
1043
1170
  const config = configResult.value;
1044
- const docsDir = path5.resolve(cwd, config.docsDir);
1045
- const sourceDir = path5.resolve(cwd, config.rootDir);
1171
+ const docsDir = path6.resolve(cwd, config.docsDir);
1172
+ const sourceDir = path6.resolve(cwd, config.rootDir);
1046
1173
  const coverageResult = await checkDocCoverage("project", {
1047
1174
  docsDir,
1048
1175
  sourceDir,
@@ -1109,9 +1236,9 @@ function printCheckDocsResult(value, mode, formatter) {
1109
1236
  }
1110
1237
  }
1111
1238
  function createCheckDocsCommand() {
1112
- const command = new Command9("check-docs").description("Check documentation coverage").option("--min-coverage <percent>", "Minimum coverage percentage", "80").action(async (opts, cmd) => {
1239
+ const command = new Command10("check-docs").description("Check documentation coverage").option("--min-coverage <percent>", "Minimum coverage percentage", "80").action(async (opts, cmd) => {
1113
1240
  const globalOpts = cmd.optsWithGlobals();
1114
- const mode = resolveOutputMode2(globalOpts);
1241
+ const mode = resolveOutputMode(globalOpts);
1115
1242
  const formatter = new OutputFormatter(mode);
1116
1243
  const result = await runCheckDocs({
1117
1244
  configPath: globalOpts.config,
@@ -1139,12 +1266,13 @@ function createCheckDocsCommand() {
1139
1266
  }
1140
1267
 
1141
1268
  // src/commands/check-perf.ts
1142
- import { Command as Command10 } from "commander";
1143
- import * as path6 from "path";
1269
+ init_dist();
1270
+ import { Command as Command11 } from "commander";
1271
+ import * as path7 from "path";
1144
1272
  async function runCheckPerf(cwd, options) {
1145
1273
  const runAll2 = !options.structural && !options.size && !options.coupling;
1146
1274
  const analyzer = new EntropyAnalyzer({
1147
- rootDir: path6.resolve(cwd),
1275
+ rootDir: path7.resolve(cwd),
1148
1276
  analyze: {
1149
1277
  complexity: runAll2 || !!options.structural,
1150
1278
  coupling: runAll2 || !!options.coupling,
@@ -1256,15 +1384,16 @@ async function runCheckPerfAction(opts, globalOpts) {
1256
1384
  process.exit(result.value.valid ? ExitCode.SUCCESS : ExitCode.VALIDATION_FAILED);
1257
1385
  }
1258
1386
  function createCheckPerfCommand() {
1259
- const command = new Command10("check-perf").description("Run performance checks: structural complexity, coupling, and size budgets").option("--structural", "Run structural complexity checks only").option("--coupling", "Run coupling metric checks only").option("--size", "Run size budget checks only").action(async (opts, cmd) => {
1387
+ const command = new Command11("check-perf").description("Run performance checks: structural complexity, coupling, and size budgets").option("--structural", "Run structural complexity checks only").option("--coupling", "Run coupling metric checks only").option("--size", "Run size budget checks only").action(async (opts, cmd) => {
1260
1388
  await runCheckPerfAction(opts, cmd.optsWithGlobals());
1261
1389
  });
1262
1390
  return command;
1263
1391
  }
1264
1392
 
1265
1393
  // src/commands/check-security.ts
1266
- import { Command as Command11 } from "commander";
1267
- import * as path7 from "path";
1394
+ init_dist();
1395
+ import { Command as Command12 } from "commander";
1396
+ import * as path8 from "path";
1268
1397
  import { execSync as execSync3 } from "child_process";
1269
1398
  var SEVERITY_RANK = {
1270
1399
  error: 3,
@@ -1277,19 +1406,19 @@ function getChangedFiles(cwd) {
1277
1406
  cwd,
1278
1407
  encoding: "utf-8"
1279
1408
  });
1280
- return output.trim().split("\n").filter((f) => f.length > 0).map((f) => path7.resolve(cwd, f));
1409
+ return output.trim().split("\n").filter((f) => f.length > 0).map((f) => path8.resolve(cwd, f));
1281
1410
  } catch {
1282
1411
  return [];
1283
1412
  }
1284
1413
  }
1285
1414
  async function runCheckSecurity(cwd, options) {
1286
- const projectRoot = path7.resolve(cwd);
1415
+ const projectRoot = path8.resolve(cwd);
1287
1416
  let configData = {};
1288
1417
  try {
1289
- const fs36 = await import("fs");
1290
- const configPath = path7.join(projectRoot, "harness.config.json");
1291
- if (fs36.existsSync(configPath)) {
1292
- const raw = fs36.readFileSync(configPath, "utf-8");
1418
+ const fs41 = await import("fs");
1419
+ const configPath = path8.join(projectRoot, "harness.config.json");
1420
+ if (fs41.existsSync(configPath)) {
1421
+ const raw = fs41.readFileSync(configPath, "utf-8");
1293
1422
  const parsed = JSON.parse(raw);
1294
1423
  configData = parsed.security ?? {};
1295
1424
  }
@@ -1302,7 +1431,7 @@ async function runCheckSecurity(cwd, options) {
1302
1431
  if (options.changedOnly) {
1303
1432
  filesToScan = getChangedFiles(projectRoot);
1304
1433
  } else {
1305
- const { glob } = await import("glob");
1434
+ const { glob: glob2 } = await import("glob");
1306
1435
  const pattern = "**/*.{ts,tsx,js,jsx,go,py,java,rb}";
1307
1436
  const ignore = securityConfig.exclude ?? [
1308
1437
  "**/node_modules/**",
@@ -1310,9 +1439,19 @@ async function runCheckSecurity(cwd, options) {
1310
1439
  "**/*.test.ts",
1311
1440
  "**/fixtures/**"
1312
1441
  ];
1313
- filesToScan = await glob(pattern, { cwd: projectRoot, absolute: true, ignore });
1442
+ filesToScan = await glob2(pattern, { cwd: projectRoot, absolute: true, ignore });
1314
1443
  }
1315
1444
  const result = await scanner.scanFiles(filesToScan);
1445
+ try {
1446
+ const commitHash = execSync3("git rev-parse HEAD", {
1447
+ cwd: projectRoot,
1448
+ encoding: "utf-8"
1449
+ }).trim();
1450
+ const timelineManager = new SecurityTimelineManager(projectRoot);
1451
+ timelineManager.capture(result, commitHash);
1452
+ timelineManager.updateLifecycles(result.findings, commitHash);
1453
+ } catch {
1454
+ }
1316
1455
  const threshold = options.severity ?? "warning";
1317
1456
  const thresholdRank = SEVERITY_RANK[threshold];
1318
1457
  const filtered = result.findings.filter((f) => SEVERITY_RANK[f.severity] >= thresholdRank);
@@ -1358,7 +1497,7 @@ async function runCheckSecurityAction(opts, globalOpts) {
1358
1497
  process.exit(result.value.valid ? ExitCode.SUCCESS : ExitCode.VALIDATION_FAILED);
1359
1498
  }
1360
1499
  function createCheckSecurityCommand() {
1361
- const command = new Command11("check-security").description("Run lightweight security scan: secrets, injection, XSS, weak crypto").option("--severity <level>", "Minimum severity threshold", "warning").hook("preAction", (thisCommand) => {
1500
+ const command = new Command12("check-security").description("Run lightweight security scan: secrets, injection, XSS, weak crypto").option("--severity <level>", "Minimum severity threshold", "warning").hook("preAction", (thisCommand) => {
1362
1501
  const severity = thisCommand.opts().severity;
1363
1502
  if (!["error", "warning", "info"].includes(severity)) {
1364
1503
  logger.error(`Invalid severity: "${severity}". Must be one of: error, warning, info`);
@@ -1371,10 +1510,11 @@ function createCheckSecurityCommand() {
1371
1510
  }
1372
1511
 
1373
1512
  // src/commands/ci/index.ts
1374
- import { Command as Command14 } from "commander";
1513
+ import { Command as Command16 } from "commander";
1375
1514
 
1376
1515
  // src/commands/ci/check.ts
1377
- import { Command as Command12 } from "commander";
1516
+ init_dist();
1517
+ import { Command as Command13 } from "commander";
1378
1518
  var VALID_CHECKS = [
1379
1519
  "validate",
1380
1520
  "deps",
@@ -1438,7 +1578,7 @@ function printCheckReport(report) {
1438
1578
  }
1439
1579
  }
1440
1580
  async function runCheckAction(opts, globalOpts) {
1441
- const mode = resolveOutputMode2(globalOpts);
1581
+ const mode = resolveOutputMode(globalOpts);
1442
1582
  const skip = parseSkip(opts.skip);
1443
1583
  const failOn = parseFailOn(opts.failOn);
1444
1584
  const opts2 = {
@@ -1464,15 +1604,16 @@ async function runCheckAction(opts, globalOpts) {
1464
1604
  process.exit(report.exitCode);
1465
1605
  }
1466
1606
  function createCheckCommand() {
1467
- return new Command12("check").description("Run all harness checks for CI (validate, deps, docs, entropy, phase-gate, arch)").option("--skip <checks>", "Comma-separated checks to skip (e.g., entropy,docs)").option("--fail-on <severity>", "Fail on severity level: error (default) or warning", "error").action(async (opts, cmd) => {
1607
+ return new Command13("check").description("Run all harness checks for CI (validate, deps, docs, entropy, phase-gate, arch)").option("--skip <checks>", "Comma-separated checks to skip (e.g., entropy,docs)").option("--fail-on <severity>", "Fail on severity level: error (default) or warning", "error").action(async (opts, cmd) => {
1468
1608
  await runCheckAction(opts, cmd.optsWithGlobals());
1469
1609
  });
1470
1610
  }
1471
1611
 
1472
1612
  // src/commands/ci/init.ts
1473
- import { Command as Command13 } from "commander";
1474
- import * as fs2 from "fs";
1475
- import * as path8 from "path";
1613
+ init_dist();
1614
+ import { Command as Command14 } from "commander";
1615
+ import * as fs3 from "fs";
1616
+ import * as path9 from "path";
1476
1617
  var ALL_CHECKS = [
1477
1618
  "validate",
1478
1619
  "deps",
@@ -1574,17 +1715,17 @@ function generateCIConfig(options) {
1574
1715
  });
1575
1716
  }
1576
1717
  function detectPlatform() {
1577
- if (fs2.existsSync(".github")) return "github";
1578
- if (fs2.existsSync(".gitlab-ci.yml")) return "gitlab";
1718
+ if (fs3.existsSync(".github")) return "github";
1719
+ if (fs3.existsSync(".gitlab-ci.yml")) return "gitlab";
1579
1720
  return null;
1580
1721
  }
1581
1722
  function writeGeneratedConfig(filename, content, platform) {
1582
- const targetPath = path8.resolve(filename);
1583
- const dir = path8.dirname(targetPath);
1584
- fs2.mkdirSync(dir, { recursive: true });
1585
- fs2.writeFileSync(targetPath, content);
1723
+ const targetPath = path9.resolve(filename);
1724
+ const dir = path9.dirname(targetPath);
1725
+ fs3.mkdirSync(dir, { recursive: true });
1726
+ fs3.writeFileSync(targetPath, content);
1586
1727
  if (platform === "generic" && process.platform !== "win32") {
1587
- fs2.chmodSync(targetPath, "755");
1728
+ fs3.chmodSync(targetPath, "755");
1588
1729
  }
1589
1730
  }
1590
1731
  async function runInitAction(opts, globalOpts) {
@@ -1607,22 +1748,128 @@ async function runInitAction(opts, globalOpts) {
1607
1748
  }
1608
1749
  }
1609
1750
  function createInitCommand() {
1610
- return new Command13("init").description("Generate CI configuration for harness checks").option("--platform <platform>", "CI platform: github, gitlab, or generic").option("--checks <list>", "Comma-separated list of checks to include").action(async (opts, cmd) => {
1751
+ return new Command14("init").description("Generate CI configuration for harness checks").option("--platform <platform>", "CI platform: github, gitlab, or generic").option("--checks <list>", "Comma-separated list of checks to include").action(async (opts, cmd) => {
1611
1752
  await runInitAction(opts, cmd.optsWithGlobals());
1612
1753
  });
1613
1754
  }
1614
1755
 
1756
+ // src/commands/ci/notify.ts
1757
+ init_dist();
1758
+ import { Command as Command15 } from "commander";
1759
+ import * as fs4 from "fs";
1760
+ function loadReport(reportPath) {
1761
+ try {
1762
+ const raw = fs4.readFileSync(reportPath, "utf-8");
1763
+ return JSON.parse(raw);
1764
+ } catch {
1765
+ return null;
1766
+ }
1767
+ }
1768
+ function resolveToken() {
1769
+ return process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN ?? null;
1770
+ }
1771
+ function resolveTrackerConfig(config) {
1772
+ const roadmap = config.roadmap;
1773
+ const tracker = roadmap?.tracker;
1774
+ if (!tracker || tracker.kind !== "github") return null;
1775
+ return tracker;
1776
+ }
1777
+ async function runNotifyAction(reportPath, opts, globalOpts) {
1778
+ const mode = resolveOutputMode(globalOpts);
1779
+ const report = loadReport(reportPath);
1780
+ if (!report) {
1781
+ logger.error(`Failed to read or parse report: ${reportPath}`);
1782
+ process.exit(ExitCode.ERROR);
1783
+ }
1784
+ const configResult = resolveConfig(
1785
+ typeof globalOpts.config === "string" ? globalOpts.config : void 0
1786
+ );
1787
+ if (!configResult.ok) {
1788
+ logger.error(configResult.error.message);
1789
+ process.exit(ExitCode.ERROR);
1790
+ }
1791
+ const config = configResult.value;
1792
+ const trackerConfig = resolveTrackerConfig(config);
1793
+ if (!trackerConfig || !trackerConfig.repo) {
1794
+ logger.error(
1795
+ 'No GitHub tracker configured. Set roadmap.tracker in harness.config.json with kind: "github" and repo: "owner/repo".'
1796
+ );
1797
+ process.exit(ExitCode.ERROR);
1798
+ }
1799
+ const token = resolveToken();
1800
+ if (!token) {
1801
+ logger.error("No GitHub token found. Set GITHUB_TOKEN or GH_TOKEN environment variable.");
1802
+ process.exit(ExitCode.ERROR);
1803
+ }
1804
+ const adapter = new GitHubIssuesSyncAdapter({ token, config: trackerConfig });
1805
+ const notifier = new CINotifier(adapter, trackerConfig.repo);
1806
+ const target = opts.target;
1807
+ if (target === "pr-comment") {
1808
+ if (!opts.pr) {
1809
+ logger.error("--pr <number> is required when target is pr-comment");
1810
+ process.exit(ExitCode.ERROR);
1811
+ }
1812
+ const prNumber = parseInt(opts.pr, 10);
1813
+ if (isNaN(prNumber)) {
1814
+ logger.error(`Invalid PR number: ${opts.pr}`);
1815
+ process.exit(ExitCode.ERROR);
1816
+ }
1817
+ const result = await notifier.notifyPR(report, prNumber);
1818
+ if (!result.ok) {
1819
+ logger.error(`Failed to post PR comment: ${result.error.message}`);
1820
+ process.exit(ExitCode.ERROR);
1821
+ }
1822
+ if (mode === OutputMode.JSON) {
1823
+ console.log(JSON.stringify({ target, pr: prNumber, status: "posted" }));
1824
+ } else if (mode !== OutputMode.QUIET) {
1825
+ logger.success(`Posted CI report as comment on PR #${prNumber}`);
1826
+ }
1827
+ } else if (target === "issue") {
1828
+ if (report.exitCode === 0) {
1829
+ if (mode === OutputMode.JSON) {
1830
+ console.log(JSON.stringify({ target, status: "skipped", reason: "no failures" }));
1831
+ } else if (mode !== OutputMode.QUIET) {
1832
+ logger.dim("No failures \u2014 skipping issue creation");
1833
+ }
1834
+ return;
1835
+ }
1836
+ const issueOpts = {};
1837
+ if (opts.title) issueOpts.issueTitle = opts.title;
1838
+ if (opts.labels) issueOpts.labels = opts.labels.split(",").map((l) => l.trim());
1839
+ const result = await notifier.notifyIssue(report, issueOpts);
1840
+ if (!result.ok) {
1841
+ logger.error(`Failed to create issue: ${result.error.message}`);
1842
+ process.exit(ExitCode.ERROR);
1843
+ }
1844
+ if (mode === OutputMode.JSON) {
1845
+ console.log(JSON.stringify({ target, ...result.value, status: "created" }));
1846
+ } else if (mode !== OutputMode.QUIET) {
1847
+ logger.success(`Created issue: ${result.value.url}`);
1848
+ }
1849
+ } else {
1850
+ logger.error(`Unknown target: ${target}. Use "pr-comment" or "issue".`);
1851
+ process.exit(ExitCode.ERROR);
1852
+ }
1853
+ }
1854
+ function createNotifyCommand() {
1855
+ return new Command15("notify").description("Post CI check results to GitHub (PR comment or issue)").argument("<report>", "Path to CI check report JSON file (from harness ci check --json)").requiredOption("--target <target>", "Notification target: pr-comment or issue").option("--pr <number>", "PR number (required for pr-comment target)").option("--title <title>", "Custom issue title (for issue target)").option("--labels <labels>", "Comma-separated labels for created issues").action(async (reportPath, opts, cmd) => {
1856
+ await runNotifyAction(reportPath, opts, cmd.optsWithGlobals());
1857
+ });
1858
+ }
1859
+
1615
1860
  // src/commands/ci/index.ts
1616
1861
  function createCICommand() {
1617
- const command = new Command14("ci").description("CI/CD integration commands");
1862
+ const command = new Command16("ci").description("CI/CD integration commands");
1618
1863
  command.addCommand(createCheckCommand());
1619
1864
  command.addCommand(createInitCommand());
1865
+ command.addCommand(createNotifyCommand());
1620
1866
  return command;
1621
1867
  }
1622
1868
 
1623
1869
  // src/commands/cleanup.ts
1624
- import { Command as Command15 } from "commander";
1625
- import * as path9 from "path";
1870
+ init_dist();
1871
+ import { Command as Command17 } from "commander";
1872
+ import * as path10 from "path";
1626
1873
  async function runCleanup(options) {
1627
1874
  const cwd = options.cwd ?? process.cwd();
1628
1875
  const type = options.type ?? "all";
@@ -1637,11 +1884,11 @@ async function runCleanup(options) {
1637
1884
  patternViolations: [],
1638
1885
  totalIssues: 0
1639
1886
  };
1640
- const rootDir = path9.resolve(cwd, config.rootDir);
1641
- const docsDir = path9.resolve(cwd, config.docsDir);
1887
+ const rootDir = path10.resolve(cwd, config.rootDir);
1888
+ const docsDir = path10.resolve(cwd, config.docsDir);
1642
1889
  const entropyConfig = {
1643
1890
  rootDir,
1644
- entryPoints: [path9.join(rootDir, "src/index.ts")],
1891
+ ...config.entropy?.entryPoints && { entryPoints: config.entropy.entryPoints },
1645
1892
  docPaths: [docsDir],
1646
1893
  analyze: {
1647
1894
  drift: type === "all" || type === "drift",
@@ -1710,9 +1957,9 @@ function printCleanupResult(value, formatter) {
1710
1957
  }
1711
1958
  }
1712
1959
  function createCleanupCommand() {
1713
- const command = new Command15("cleanup").description("Detect entropy issues (doc drift, dead code, patterns)").option("-t, --type <type>", "Issue type: drift, dead-code, patterns, all", "all").action(async (opts, cmd) => {
1960
+ const command = new Command17("cleanup").description("Detect entropy issues (doc drift, dead code, patterns)").option("-t, --type <type>", "Issue type: drift, dead-code, patterns, all", "all").action(async (opts, cmd) => {
1714
1961
  const globalOpts = cmd.optsWithGlobals();
1715
- const mode = resolveOutputMode2(globalOpts);
1962
+ const mode = resolveOutputMode(globalOpts);
1716
1963
  const formatter = new OutputFormatter(mode);
1717
1964
  const result = await runCleanup({
1718
1965
  configPath: globalOpts.config,
@@ -1740,20 +1987,21 @@ function createCleanupCommand() {
1740
1987
  }
1741
1988
 
1742
1989
  // src/commands/cleanup-sessions.ts
1743
- import { Command as Command16 } from "commander";
1744
- import * as fs3 from "fs";
1745
- import * as path10 from "path";
1990
+ init_dist();
1991
+ import { Command as Command18 } from "commander";
1992
+ import * as fs5 from "fs";
1993
+ import * as path11 from "path";
1746
1994
  var STALE_TTL_MS = 24 * 60 * 60 * 1e3;
1747
1995
  function getMostRecentMtime(dirPath) {
1748
1996
  let latest = 0;
1749
1997
  try {
1750
- const entries = fs3.readdirSync(dirPath, { withFileTypes: true });
1998
+ const entries = fs5.readdirSync(dirPath, { withFileTypes: true });
1751
1999
  for (const entry of entries) {
1752
- const fullPath = path10.join(dirPath, entry.name);
1753
- const stat = fs3.statSync(fullPath);
2000
+ const fullPath = path11.join(dirPath, entry.name);
2001
+ const stat = fs5.statSync(fullPath);
1754
2002
  if (stat.mtimeMs > latest) latest = stat.mtimeMs;
1755
2003
  }
1756
- const dirStat = fs3.statSync(dirPath);
2004
+ const dirStat = fs5.statSync(dirPath);
1757
2005
  if (dirStat.mtimeMs > latest) latest = dirStat.mtimeMs;
1758
2006
  } catch {
1759
2007
  }
@@ -1762,14 +2010,14 @@ function getMostRecentMtime(dirPath) {
1762
2010
  async function runCleanupSessions(options) {
1763
2011
  const cwd = options.cwd ?? process.cwd();
1764
2012
  const dryRun = options.dryRun ?? false;
1765
- const sessionsDir = path10.join(cwd, ".harness", "sessions");
2013
+ const sessionsDir = path11.join(cwd, ".harness", "sessions");
1766
2014
  const result = { removed: [], kept: [] };
1767
- if (!fs3.existsSync(sessionsDir)) {
2015
+ if (!fs5.existsSync(sessionsDir)) {
1768
2016
  return Ok(result);
1769
2017
  }
1770
2018
  let entries;
1771
2019
  try {
1772
- entries = fs3.readdirSync(sessionsDir, { withFileTypes: true });
2020
+ entries = fs5.readdirSync(sessionsDir, { withFileTypes: true });
1773
2021
  } catch (err) {
1774
2022
  return Err(
1775
2023
  new CLIError(
@@ -1781,14 +2029,14 @@ async function runCleanupSessions(options) {
1781
2029
  const now = Date.now();
1782
2030
  for (const entry of entries) {
1783
2031
  if (!entry.isDirectory()) continue;
1784
- const sessionPath = path10.join(sessionsDir, entry.name);
2032
+ const sessionPath = path11.join(sessionsDir, entry.name);
1785
2033
  const mostRecent = getMostRecentMtime(sessionPath);
1786
2034
  const ageMs = now - mostRecent;
1787
2035
  if (ageMs > STALE_TTL_MS) {
1788
2036
  result.removed.push(entry.name);
1789
2037
  if (!dryRun) {
1790
2038
  try {
1791
- fs3.rmSync(sessionPath, { recursive: true, force: true });
2039
+ fs5.rmSync(sessionPath, { recursive: true, force: true });
1792
2040
  } catch (err) {
1793
2041
  return Err(
1794
2042
  new CLIError(
@@ -1831,9 +2079,9 @@ Cleaned up ${removed.length} stale session(s).`);
1831
2079
  }
1832
2080
  }
1833
2081
  function createCleanupSessionsCommand() {
1834
- const command = new Command16("cleanup-sessions").description("Remove stale session directories from .harness/sessions/ (no write in 24h)").option("--dry-run", "List stale sessions without deleting them", false).option("--path <path>", "Project root path", ".").action(async (opts, cmd) => {
2082
+ const command = new Command18("cleanup-sessions").description("Remove stale session directories from .harness/sessions/ (no write in 24h)").option("--dry-run", "List stale sessions without deleting them", false).option("--path <path>", "Project root path", ".").action(async (opts, cmd) => {
1835
2083
  const globalOpts = cmd.optsWithGlobals();
1836
- const cwd = path10.resolve(opts.path);
2084
+ const cwd = path11.resolve(opts.path);
1837
2085
  const result = await runCleanupSessions({ cwd, dryRun: opts.dryRun });
1838
2086
  if (!result.ok) {
1839
2087
  logger.error(result.error.message);
@@ -1847,11 +2095,11 @@ function createCleanupSessionsCommand() {
1847
2095
  }
1848
2096
 
1849
2097
  // src/commands/dashboard.ts
1850
- import { Command as Command17 } from "commander";
2098
+ import { Command as Command19 } from "commander";
1851
2099
  import { spawn } from "child_process";
1852
2100
  import { existsSync as existsSync4 } from "fs";
1853
2101
  import { createRequire } from "module";
1854
- import { dirname as dirname2, join as join7, resolve as resolve10 } from "path";
2102
+ import { dirname as dirname2, join as join6, resolve as resolve11 } from "path";
1855
2103
  import { setTimeout } from "timers";
1856
2104
  import { fileURLToPath } from "url";
1857
2105
  var __filename = fileURLToPath(import.meta.url);
@@ -1867,20 +2115,20 @@ function resolveServerScript() {
1867
2115
  try {
1868
2116
  const pkgPath = require2.resolve("@harness-engineering/dashboard/package.json");
1869
2117
  const pkgDir = dirname2(pkgPath);
1870
- const built2 = join7(pkgDir, "dist", "server", "serve.js");
2118
+ const built2 = join6(pkgDir, "dist", "server", "serve.js");
1871
2119
  if (existsSync4(built2)) return { script: built2, dev: false };
1872
- const dev2 = join7(pkgDir, "src", "server", "serve.ts");
2120
+ const dev2 = join6(pkgDir, "src", "server", "serve.ts");
1873
2121
  if (existsSync4(dev2)) return { script: dev2, dev: true };
1874
2122
  } catch {
1875
2123
  }
1876
2124
  const built = [
1877
- join7(__dirname, "..", "..", "..", "dashboard", "dist", "server", "serve.js"),
1878
- join7(__dirname, "..", "..", "..", "..", "packages", "dashboard", "dist", "server", "serve.js")
2125
+ join6(__dirname, "..", "..", "..", "dashboard", "dist", "server", "serve.js"),
2126
+ join6(__dirname, "..", "..", "..", "..", "packages", "dashboard", "dist", "server", "serve.js")
1879
2127
  ].find((p) => existsSync4(p));
1880
2128
  if (built) return { script: built, dev: false };
1881
2129
  const dev = [
1882
- join7(__dirname, "..", "..", "..", "dashboard", "src", "server", "serve.ts"),
1883
- join7(__dirname, "..", "..", "..", "..", "packages", "dashboard", "src", "server", "serve.ts")
2130
+ join6(__dirname, "..", "..", "..", "dashboard", "src", "server", "serve.ts"),
2131
+ join6(__dirname, "..", "..", "..", "..", "packages", "dashboard", "src", "server", "serve.ts")
1884
2132
  ].find((p) => existsSync4(p));
1885
2133
  return dev ? { script: dev, dev: true } : null;
1886
2134
  }
@@ -1901,7 +2149,7 @@ function spawnDashboardServer(server, env) {
1901
2149
  function runDashboard(opts) {
1902
2150
  const clientPort = Number(opts.port ?? DEFAULT_CLIENT_PORT);
1903
2151
  const apiPort = Number(opts.apiPort ?? DEFAULT_API_PORT);
1904
- const projectPath = resolve10(opts.cwd ?? process.cwd());
2152
+ const projectPath = resolve11(opts.cwd ?? process.cwd());
1905
2153
  const url = `http://localhost:${apiPort}`;
1906
2154
  const server = resolveServerScript();
1907
2155
  if (!server) {
@@ -1922,14 +2170,14 @@ function runDashboard(opts) {
1922
2170
  }
1923
2171
  }
1924
2172
  function createDashboardCommand() {
1925
- return new Command17("dashboard").description("Start the Harness local web dashboard").option("--port <port>", "Client dev server port", String(DEFAULT_CLIENT_PORT)).option("--api-port <port>", "API server port", String(DEFAULT_API_PORT)).option("--no-open", "Do not automatically open browser").option("--cwd <path>", "Project directory (defaults to cwd)").action((opts) => runDashboard(opts));
2173
+ return new Command19("dashboard").description("Start the Harness local web dashboard").option("--port <port>", "Client dev server port", String(DEFAULT_CLIENT_PORT)).option("--api-port <port>", "API server port", String(DEFAULT_API_PORT)).option("--no-open", "Do not automatically open browser").option("--cwd <path>", "Project directory (defaults to cwd)").action((opts) => runDashboard(opts));
1926
2174
  }
1927
2175
 
1928
2176
  // src/commands/doctor.ts
1929
- import { Command as Command18 } from "commander";
1930
- import * as fs5 from "fs";
2177
+ import { Command as Command20 } from "commander";
2178
+ import * as fs7 from "fs";
1931
2179
  import * as os from "os";
1932
- import * as path12 from "path";
2180
+ import * as path13 from "path";
1933
2181
  import chalk from "chalk";
1934
2182
 
1935
2183
  // src/utils/node-version.ts
@@ -2012,24 +2260,24 @@ var INTEGRATION_REGISTRY = [
2012
2260
  ];
2013
2261
 
2014
2262
  // src/integrations/config.ts
2015
- import * as fs4 from "fs";
2016
- import * as path11 from "path";
2263
+ import * as fs6 from "fs";
2264
+ import * as path12 from "path";
2017
2265
  function readJsonSafe(filePath) {
2018
- if (!fs4.existsSync(filePath)) return null;
2266
+ if (!fs6.existsSync(filePath)) return null;
2019
2267
  try {
2020
- return JSON.parse(fs4.readFileSync(filePath, "utf-8"));
2268
+ return JSON.parse(fs6.readFileSync(filePath, "utf-8"));
2021
2269
  } catch {
2022
2270
  return null;
2023
2271
  }
2024
2272
  }
2025
2273
  function writeJson(filePath, data) {
2026
- const dir = path11.dirname(filePath);
2027
- if (!fs4.existsSync(dir)) {
2028
- fs4.mkdirSync(dir, { recursive: true });
2274
+ const dir = path12.dirname(filePath);
2275
+ if (!fs6.existsSync(dir)) {
2276
+ fs6.mkdirSync(dir, { recursive: true });
2029
2277
  }
2030
2278
  const tmp = filePath + ".tmp";
2031
- fs4.writeFileSync(tmp, JSON.stringify(data, null, 2) + "\n");
2032
- fs4.renameSync(tmp, filePath);
2279
+ fs6.writeFileSync(tmp, JSON.stringify(data, null, 2) + "\n");
2280
+ fs6.renameSync(tmp, filePath);
2033
2281
  }
2034
2282
  function readMcpConfig(filePath) {
2035
2283
  const config = readJsonSafe(filePath);
@@ -2043,7 +2291,7 @@ function writeMcpEntry(filePath, name, entry) {
2043
2291
  writeJson(filePath, config);
2044
2292
  }
2045
2293
  function removeMcpEntry(filePath, name) {
2046
- if (!fs4.existsSync(filePath)) return;
2294
+ if (!fs6.existsSync(filePath)) return;
2047
2295
  const config = readMcpConfig(filePath);
2048
2296
  delete config.mcpServers[name];
2049
2297
  writeJson(filePath, config);
@@ -2082,7 +2330,7 @@ function checkNodeVersion2() {
2082
2330
  }
2083
2331
  function countCommandFiles(dir, ext) {
2084
2332
  try {
2085
- return fs5.readdirSync(dir).filter((f) => f.endsWith(ext)).length;
2333
+ return fs7.readdirSync(dir).filter((f) => f.endsWith(ext)).length;
2086
2334
  } catch {
2087
2335
  return 0;
2088
2336
  }
@@ -2091,13 +2339,13 @@ function checkSlashCommands() {
2091
2339
  const platforms = [
2092
2340
  {
2093
2341
  name: "Claude Code",
2094
- dir: path12.join(os.homedir(), ".claude", "commands", "harness"),
2342
+ dir: path13.join(os.homedir(), ".claude", "commands", "harness"),
2095
2343
  ext: ".md",
2096
2344
  client: "claude-code"
2097
2345
  },
2098
2346
  {
2099
2347
  name: "Gemini CLI",
2100
- dir: path12.join(os.homedir(), ".gemini", "commands", "harness"),
2348
+ dir: path13.join(os.homedir(), ".gemini", "commands", "harness"),
2101
2349
  ext: ".toml",
2102
2350
  client: "gemini-cli"
2103
2351
  }
@@ -2121,7 +2369,7 @@ function checkSlashCommands() {
2121
2369
  }
2122
2370
  function checkMcpConfig(cwd) {
2123
2371
  const results = [];
2124
- const claudeConfig = readMcpConfig(path12.join(cwd, ".mcp.json"));
2372
+ const claudeConfig = readMcpConfig(path13.join(cwd, ".mcp.json"));
2125
2373
  if (claudeConfig.mcpServers?.["harness"]) {
2126
2374
  results.push({
2127
2375
  name: "mcp-claude",
@@ -2136,9 +2384,9 @@ function checkMcpConfig(cwd) {
2136
2384
  fix: "Run: harness setup-mcp --client claude"
2137
2385
  });
2138
2386
  }
2139
- const geminiDir = path12.join(cwd, ".gemini");
2140
- if (fs5.existsSync(geminiDir)) {
2141
- const geminiConfig = readMcpConfig(path12.join(geminiDir, "settings.json"));
2387
+ const geminiDir = path13.join(cwd, ".gemini");
2388
+ if (fs7.existsSync(geminiDir)) {
2389
+ const geminiConfig = readMcpConfig(path13.join(geminiDir, "settings.json"));
2142
2390
  if (geminiConfig.mcpServers?.["harness"]) {
2143
2391
  results.push({
2144
2392
  name: "mcp-gemini",
@@ -2157,12 +2405,12 @@ function checkMcpConfig(cwd) {
2157
2405
  return results;
2158
2406
  }
2159
2407
  function loadMcpPresence(cwd) {
2160
- const mcpPath = path12.join(cwd, ".mcp.json");
2161
- const geminiDir = path12.join(cwd, ".gemini");
2162
- const hasGemini = fs5.existsSync(geminiDir);
2408
+ const mcpPath = path13.join(cwd, ".mcp.json");
2409
+ const geminiDir = path13.join(cwd, ".gemini");
2410
+ const hasGemini = fs7.existsSync(geminiDir);
2163
2411
  return {
2164
2412
  mcpConfig: readMcpConfig(mcpPath),
2165
- geminiConfig: hasGemini ? readMcpConfig(path12.join(geminiDir, "settings.json")) : null,
2413
+ geminiConfig: hasGemini ? readMcpConfig(path13.join(geminiDir, "settings.json")) : null,
2166
2414
  hasGemini
2167
2415
  };
2168
2416
  }
@@ -2194,7 +2442,7 @@ function checkTier0Presence(def, presence) {
2194
2442
  function checkIntegrations(cwd) {
2195
2443
  const results = [];
2196
2444
  const presence = loadMcpPresence(cwd);
2197
- const configPath = path12.join(cwd, "harness.config.json");
2445
+ const configPath = path13.join(cwd, "harness.config.json");
2198
2446
  const integrationsConfig = readIntegrationsConfig(configPath);
2199
2447
  for (const def of INTEGRATION_REGISTRY.filter((d) => d.tier === 0)) {
2200
2448
  results.push(checkTier0Presence(def, presence));
@@ -2244,7 +2492,7 @@ function formatCheck(check) {
2244
2492
  return line;
2245
2493
  }
2246
2494
  function createDoctorCommand() {
2247
- return new Command18("doctor").description("Check environment health: Node version, slash commands, MCP configuration").action((_opts, cmd) => {
2495
+ return new Command20("doctor").description("Check environment health: Node version, slash commands, MCP configuration").action((_opts, cmd) => {
2248
2496
  const globalOpts = cmd.optsWithGlobals();
2249
2497
  const cwd = process.cwd();
2250
2498
  const useJson = globalOpts.json;
@@ -2269,8 +2517,9 @@ function createDoctorCommand() {
2269
2517
  }
2270
2518
 
2271
2519
  // src/commands/fix-drift.ts
2272
- import { Command as Command19 } from "commander";
2273
- import * as path13 from "path";
2520
+ init_dist();
2521
+ import { Command as Command21 } from "commander";
2522
+ import * as path14 from "path";
2274
2523
  async function runFixDrift(options) {
2275
2524
  const cwd = options.cwd ?? process.cwd();
2276
2525
  const dryRun = options.dryRun !== false;
@@ -2279,11 +2528,11 @@ async function runFixDrift(options) {
2279
2528
  return Err(configResult.error);
2280
2529
  }
2281
2530
  const config = configResult.value;
2282
- const rootDir = path13.resolve(cwd, config.rootDir);
2283
- const docsDir = path13.resolve(cwd, config.docsDir);
2531
+ const rootDir = path14.resolve(cwd, config.rootDir);
2532
+ const docsDir = path14.resolve(cwd, config.docsDir);
2284
2533
  const entropyConfig = {
2285
2534
  rootDir,
2286
- entryPoints: [path13.join(rootDir, "src/index.ts")],
2535
+ ...config.entropy?.entryPoints && { entryPoints: config.entropy.entryPoints },
2287
2536
  docPaths: [docsDir],
2288
2537
  analyze: {
2289
2538
  drift: true,
@@ -2402,9 +2651,9 @@ function printFixDriftResult(value, mode, formatter) {
2402
2651
  }
2403
2652
  }
2404
2653
  function createFixDriftCommand() {
2405
- const command = new Command19("fix-drift").description("Auto-fix entropy issues (doc drift, dead code)").option("--no-dry-run", "Actually apply fixes (default is dry-run mode)").action(async (opts, cmd) => {
2654
+ const command = new Command21("fix-drift").description("Auto-fix entropy issues (doc drift, dead code)").option("--no-dry-run", "Actually apply fixes (default is dry-run mode)").action(async (opts, cmd) => {
2406
2655
  const globalOpts = cmd.optsWithGlobals();
2407
- const mode = resolveOutputMode2(globalOpts);
2656
+ const mode = resolveOutputMode(globalOpts);
2408
2657
  const formatter = new OutputFormatter(mode);
2409
2658
  const result = await runFixDrift({
2410
2659
  configPath: globalOpts.config,
@@ -2432,7 +2681,7 @@ function createFixDriftCommand() {
2432
2681
  }
2433
2682
 
2434
2683
  // src/commands/generate.ts
2435
- import { Command as Command20 } from "commander";
2684
+ import { Command as Command22 } from "commander";
2436
2685
  function validatePlatforms(platforms) {
2437
2686
  for (const p of platforms) {
2438
2687
  if (!VALID_PLATFORMS.includes(p)) {
@@ -2492,27 +2741,27 @@ async function runGenerateAction(opts, globalOpts) {
2492
2741
  }
2493
2742
  }
2494
2743
  function createGenerateCommand() {
2495
- return new Command20("generate").description("Generate all platform integrations (slash commands + agent definitions)").option("--platforms <list>", "Target platforms (comma-separated)", "claude-code,gemini-cli").option("--global", "Write to global directories", false).option("--include-global", "Include built-in global skills", false).option("--output <dir>", "Custom output directory").option("--dry-run", "Show what would change without writing", false).option("--yes", "Skip deletion confirmation prompts", false).action(async (opts, cmd) => {
2744
+ return new Command22("generate").description("Generate all platform integrations (slash commands + agent definitions)").option("--platforms <list>", "Target platforms (comma-separated)", "claude-code,gemini-cli").option("--global", "Write to global directories", false).option("--include-global", "Include built-in global skills", false).option("--output <dir>", "Custom output directory").option("--dry-run", "Show what would change without writing", false).option("--yes", "Skip deletion confirmation prompts", false).action(async (opts, cmd) => {
2496
2745
  await runGenerateAction(opts, cmd.optsWithGlobals());
2497
2746
  });
2498
2747
  }
2499
2748
 
2500
2749
  // src/commands/graph/index.ts
2501
- import { Command as Command23 } from "commander";
2750
+ import { Command as Command25 } from "commander";
2502
2751
 
2503
2752
  // src/commands/graph/status.ts
2504
- import * as path14 from "path";
2753
+ import * as path15 from "path";
2505
2754
  async function runGraphStatus(projectPath) {
2506
- const { GraphStore } = await import("./dist-QW5G3GX3.js");
2507
- const graphDir = path14.join(projectPath, ".harness", "graph");
2755
+ const { GraphStore } = await import("./dist-RADHOOXG.js");
2756
+ const graphDir = path15.join(projectPath, ".harness", "graph");
2508
2757
  const store = new GraphStore();
2509
2758
  const loaded = await store.load(graphDir);
2510
2759
  if (!loaded) return { status: "no_graph", message: "No graph found. Run `harness scan` first." };
2511
- const fs36 = await import("fs/promises");
2512
- const metaPath = path14.join(graphDir, "metadata.json");
2760
+ const fs41 = await import("fs/promises");
2761
+ const metaPath = path15.join(graphDir, "metadata.json");
2513
2762
  let lastScan = "unknown";
2514
2763
  try {
2515
- const meta = JSON.parse(await fs36.readFile(metaPath, "utf-8"));
2764
+ const meta = JSON.parse(await fs41.readFile(metaPath, "utf-8"));
2516
2765
  lastScan = meta.lastScanTimestamp;
2517
2766
  } catch {
2518
2767
  }
@@ -2523,8 +2772,8 @@ async function runGraphStatus(projectPath) {
2523
2772
  }
2524
2773
  let connectorSyncStatus = {};
2525
2774
  try {
2526
- const syncMetaPath = path14.join(graphDir, "sync-metadata.json");
2527
- const syncMeta = JSON.parse(await fs36.readFile(syncMetaPath, "utf-8"));
2775
+ const syncMetaPath = path15.join(graphDir, "sync-metadata.json");
2776
+ const syncMeta = JSON.parse(await fs41.readFile(syncMetaPath, "utf-8"));
2528
2777
  for (const [name, data] of Object.entries(syncMeta.connectors ?? {})) {
2529
2778
  connectorSyncStatus[name] = data.lastSyncTimestamp;
2530
2779
  }
@@ -2541,10 +2790,10 @@ async function runGraphStatus(projectPath) {
2541
2790
  }
2542
2791
 
2543
2792
  // src/commands/graph/export.ts
2544
- import * as path15 from "path";
2793
+ import * as path16 from "path";
2545
2794
  async function runGraphExport(projectPath, format) {
2546
- const { GraphStore } = await import("./dist-QW5G3GX3.js");
2547
- const graphDir = path15.join(projectPath, ".harness", "graph");
2795
+ const { GraphStore } = await import("./dist-RADHOOXG.js");
2796
+ const graphDir = path16.join(projectPath, ".harness", "graph");
2548
2797
  const store = new GraphStore();
2549
2798
  const loaded = await store.load(graphDir);
2550
2799
  if (!loaded) throw new Error("No graph found. Run `harness scan` first.");
@@ -2573,15 +2822,15 @@ async function runGraphExport(projectPath, format) {
2573
2822
  }
2574
2823
 
2575
2824
  // src/commands/graph/index.ts
2576
- import * as path18 from "path";
2825
+ import * as path19 from "path";
2577
2826
 
2578
2827
  // src/commands/graph/query.ts
2579
- import { Command as Command21 } from "commander";
2580
- import * as path16 from "path";
2828
+ import { Command as Command23 } from "commander";
2829
+ import * as path17 from "path";
2581
2830
  async function runQuery(projectPath, rootNodeId, opts) {
2582
- const { GraphStore, ContextQL } = await import("./dist-QW5G3GX3.js");
2831
+ const { GraphStore, ContextQL } = await import("./dist-RADHOOXG.js");
2583
2832
  const store = new GraphStore();
2584
- const graphDir = path16.join(projectPath, ".harness", "graph");
2833
+ const graphDir = path17.join(projectPath, ".harness", "graph");
2585
2834
  const loaded = await store.load(graphDir);
2586
2835
  if (!loaded) throw new Error("No graph found. Run `harness scan` first.");
2587
2836
  const params = {
@@ -2603,7 +2852,7 @@ function printQueryResult(result) {
2603
2852
  }
2604
2853
  }
2605
2854
  async function runQueryAction(rootNodeId, opts, globalOpts) {
2606
- const projectPath = path16.resolve(globalOpts.config ? path16.dirname(globalOpts.config) : ".");
2855
+ const projectPath = path17.resolve(globalOpts.config ? path17.dirname(globalOpts.config) : ".");
2607
2856
  try {
2608
2857
  const result = await runQuery(projectPath, rootNodeId, {
2609
2858
  depth: parseInt(opts.depth),
@@ -2622,19 +2871,19 @@ async function runQueryAction(rootNodeId, opts, globalOpts) {
2622
2871
  }
2623
2872
  }
2624
2873
  function createQueryCommand() {
2625
- return new Command21("query").description("Query the knowledge graph").argument("<rootNodeId>", "Starting node ID").option("--depth <n>", "Max traversal depth", "3").option("--types <types>", "Comma-separated node types to include").option("--edges <edges>", "Comma-separated edge types to include").option("--bidirectional", "Traverse both directions").action(async (rootNodeId, opts, cmd) => {
2874
+ return new Command23("query").description("Query the knowledge graph").argument("<rootNodeId>", "Starting node ID").option("--depth <n>", "Max traversal depth", "3").option("--types <types>", "Comma-separated node types to include").option("--edges <edges>", "Comma-separated edge types to include").option("--bidirectional", "Traverse both directions").action(async (rootNodeId, opts, cmd) => {
2626
2875
  await runQueryAction(rootNodeId, opts, cmd.optsWithGlobals());
2627
2876
  });
2628
2877
  }
2629
2878
 
2630
2879
  // src/commands/graph/ingest.ts
2631
- import { Command as Command22 } from "commander";
2632
- import * as path17 from "path";
2880
+ import { Command as Command24 } from "commander";
2881
+ import * as path18 from "path";
2633
2882
  async function loadConnectorConfig(projectPath, source) {
2634
2883
  try {
2635
- const fs36 = await import("fs/promises");
2636
- const configPath = path17.join(projectPath, "harness.config.json");
2637
- const config = JSON.parse(await fs36.readFile(configPath, "utf-8"));
2884
+ const fs41 = await import("fs/promises");
2885
+ const configPath = path18.join(projectPath, "harness.config.json");
2886
+ const config = JSON.parse(await fs41.readFile(configPath, "utf-8"));
2638
2887
  const connector = config.graph?.connectors?.[source];
2639
2888
  return connector ?? {};
2640
2889
  } catch {
@@ -2673,8 +2922,8 @@ async function runIngest(projectPath, source, opts) {
2673
2922
  SlackConnector,
2674
2923
  CIConnector,
2675
2924
  ConfluenceConnector
2676
- } = await import("./dist-QW5G3GX3.js");
2677
- const graphDir = path17.join(projectPath, ".harness", "graph");
2925
+ } = await import("./dist-RADHOOXG.js");
2926
+ const graphDir = path18.join(projectPath, ".harness", "graph");
2678
2927
  const store = new GraphStore();
2679
2928
  await store.load(graphDir);
2680
2929
  if (opts?.all) {
@@ -2741,7 +2990,7 @@ async function runIngest(projectPath, source, opts) {
2741
2990
  return result;
2742
2991
  }
2743
2992
  function createIngestCommand() {
2744
- return new Command22("ingest").description("Ingest data into the knowledge graph").option(
2993
+ return new Command24("ingest").description("Ingest data into the knowledge graph").option(
2745
2994
  "--source <name>",
2746
2995
  "Source to ingest (code, knowledge, git, jira, slack, ci, confluence)"
2747
2996
  ).option("--all", "Run all sources (code, knowledge, git, and configured connectors)").option("--full", "Force full re-ingestion").action(async (opts, cmd) => {
@@ -2750,7 +2999,7 @@ function createIngestCommand() {
2750
2999
  process.exit(1);
2751
3000
  }
2752
3001
  const globalOpts = cmd.optsWithGlobals();
2753
- const projectPath = path17.resolve(globalOpts.config ? path17.dirname(globalOpts.config) : ".");
3002
+ const projectPath = path18.resolve(globalOpts.config ? path18.dirname(globalOpts.config) : ".");
2754
3003
  try {
2755
3004
  const result = await runIngest(projectPath, opts.source ?? "", {
2756
3005
  full: opts.full,
@@ -2773,7 +3022,7 @@ function createIngestCommand() {
2773
3022
 
2774
3023
  // src/commands/graph/index.ts
2775
3024
  function resolveProjectPath(globalOpts) {
2776
- return path18.resolve(globalOpts.config ? path18.dirname(globalOpts.config) : ".");
3025
+ return path19.resolve(globalOpts.config ? path19.dirname(globalOpts.config) : ".");
2777
3026
  }
2778
3027
  function printGraphStatus(result) {
2779
3028
  if (result.status === "no_graph") {
@@ -2817,19 +3066,19 @@ async function runExportAction(opts, cmd) {
2817
3066
  }
2818
3067
  }
2819
3068
  function createGraphCommand() {
2820
- const graph = new Command23("graph").description("Knowledge graph management");
3069
+ const graph = new Command25("graph").description("Knowledge graph management");
2821
3070
  graph.command("status").description("Show graph statistics").action(runStatusAction);
2822
3071
  graph.command("export").description("Export graph").requiredOption("--format <format>", "Output format (json, mermaid)").action(runExportAction);
2823
3072
  return graph;
2824
3073
  }
2825
3074
 
2826
3075
  // src/commands/hooks/index.ts
2827
- import { Command as Command28 } from "commander";
3076
+ import { Command as Command30 } from "commander";
2828
3077
 
2829
3078
  // src/commands/hooks/init.ts
2830
- import { Command as Command24 } from "commander";
2831
- import * as fs6 from "fs";
2832
- import * as path19 from "path";
3079
+ import { Command as Command26 } from "commander";
3080
+ import * as fs8 from "fs";
3081
+ import * as path20 from "path";
2833
3082
  import { fileURLToPath as fileURLToPath2 } from "url";
2834
3083
 
2835
3084
  // src/hooks/profiles.ts
@@ -2859,14 +3108,24 @@ var PROFILES = {
2859
3108
 
2860
3109
  // src/commands/hooks/init.ts
2861
3110
  var __filename2 = fileURLToPath2(import.meta.url);
2862
- var __dirname2 = path19.dirname(__filename2);
3111
+ var __dirname2 = path20.dirname(__filename2);
2863
3112
  var VALID_PROFILES = ["minimal", "standard", "strict"];
2864
3113
  function resolveHookSourceDir() {
2865
- const candidate = path19.resolve(__dirname2, "..", "..", "hooks");
2866
- if (fs6.existsSync(candidate)) {
2867
- return candidate;
3114
+ const candidates = [
3115
+ // Dev layout: src/commands/hooks/ → ../../hooks/
3116
+ path20.resolve(__dirname2, "..", "..", "hooks"),
3117
+ // Bundled layout: dist/ → ./hooks/ (copied by copy-assets.mjs)
3118
+ path20.resolve(__dirname2, "hooks")
3119
+ ];
3120
+ for (const candidate of candidates) {
3121
+ if (fs8.existsSync(candidate)) {
3122
+ return candidate;
3123
+ }
2868
3124
  }
2869
- throw new Error(`Cannot locate hook scripts directory. Expected at: ${candidate}`);
3125
+ throw new Error(
3126
+ `Cannot locate hook scripts directory. Searched:
3127
+ ${candidates.map((c) => ` - ${c}`).join("\n")}`
3128
+ );
2870
3129
  }
2871
3130
  function buildSettingsHooks(profile) {
2872
3131
  const activeHookNames = PROFILES[profile];
@@ -2891,12 +3150,12 @@ function mergeSettings(existing, hooksConfig) {
2891
3150
  }
2892
3151
  function initHooks(options) {
2893
3152
  const { profile, projectDir } = options;
2894
- const hooksDestDir = path19.join(projectDir, ".harness", "hooks");
2895
- fs6.mkdirSync(hooksDestDir, { recursive: true });
2896
- if (fs6.existsSync(hooksDestDir)) {
2897
- for (const entry of fs6.readdirSync(hooksDestDir)) {
3153
+ const hooksDestDir = path20.join(projectDir, ".harness", "hooks");
3154
+ fs8.mkdirSync(hooksDestDir, { recursive: true });
3155
+ if (fs8.existsSync(hooksDestDir)) {
3156
+ for (const entry of fs8.readdirSync(hooksDestDir)) {
2898
3157
  if (entry.endsWith(".js")) {
2899
- fs6.unlinkSync(path19.join(hooksDestDir, entry));
3158
+ fs8.unlinkSync(path20.join(hooksDestDir, entry));
2900
3159
  }
2901
3160
  }
2902
3161
  }
@@ -2905,22 +3164,22 @@ function initHooks(options) {
2905
3164
  const activeNames = PROFILES[profile];
2906
3165
  const activeScripts = HOOK_SCRIPTS.filter((h) => activeNames.includes(h.name));
2907
3166
  for (const script of activeScripts) {
2908
- const srcFile = path19.join(sourceDir, `${script.name}.js`);
2909
- const destFile = path19.join(hooksDestDir, `${script.name}.js`);
2910
- if (fs6.existsSync(srcFile)) {
2911
- fs6.copyFileSync(srcFile, destFile);
3167
+ const srcFile = path20.join(sourceDir, `${script.name}.js`);
3168
+ const destFile = path20.join(hooksDestDir, `${script.name}.js`);
3169
+ if (fs8.existsSync(srcFile)) {
3170
+ fs8.copyFileSync(srcFile, destFile);
2912
3171
  copiedScripts.push(script.name);
2913
3172
  }
2914
3173
  }
2915
- const profilePath = path19.join(hooksDestDir, "profile.json");
2916
- fs6.writeFileSync(profilePath, JSON.stringify({ profile }, null, 2) + "\n");
2917
- const claudeDir = path19.join(projectDir, ".claude");
2918
- fs6.mkdirSync(claudeDir, { recursive: true });
2919
- const settingsPath = path19.join(claudeDir, "settings.json");
3174
+ const profilePath = path20.join(hooksDestDir, "profile.json");
3175
+ fs8.writeFileSync(profilePath, JSON.stringify({ profile }, null, 2) + "\n");
3176
+ const claudeDir = path20.join(projectDir, ".claude");
3177
+ fs8.mkdirSync(claudeDir, { recursive: true });
3178
+ const settingsPath = path20.join(claudeDir, "settings.json");
2920
3179
  let existing = {};
2921
- if (fs6.existsSync(settingsPath)) {
3180
+ if (fs8.existsSync(settingsPath)) {
2922
3181
  try {
2923
- existing = JSON.parse(fs6.readFileSync(settingsPath, "utf-8"));
3182
+ existing = JSON.parse(fs8.readFileSync(settingsPath, "utf-8"));
2924
3183
  } catch (e) {
2925
3184
  throw new Error(
2926
3185
  `Malformed .claude/settings.json \u2014 fix the JSON syntax before running hooks init. Parse error: ${e instanceof Error ? e.message : String(e)}`,
@@ -2930,11 +3189,11 @@ function initHooks(options) {
2930
3189
  }
2931
3190
  const hooksConfig = buildSettingsHooks(profile);
2932
3191
  const merged = mergeSettings(existing, hooksConfig);
2933
- fs6.writeFileSync(settingsPath, JSON.stringify(merged, null, 2) + "\n");
3192
+ fs8.writeFileSync(settingsPath, JSON.stringify(merged, null, 2) + "\n");
2934
3193
  return { copiedScripts, settingsPath, profilePath };
2935
3194
  }
2936
3195
  function createInitCommand2() {
2937
- return new Command24("init").description("Install Claude Code hook configurations into the current project").option("--profile <profile>", "Hook profile: minimal, standard, or strict", "standard").action(async (opts, cmd) => {
3196
+ return new Command26("init").description("Install Claude Code hook configurations into the current project").option("--profile <profile>", "Hook profile: minimal, standard, or strict", "standard").action(async (opts, cmd) => {
2938
3197
  const globalOpts = cmd.optsWithGlobals();
2939
3198
  const profile = opts.profile;
2940
3199
  if (!VALID_PROFILES.includes(profile)) {
@@ -2958,7 +3217,9 @@ function createInitCommand2() {
2958
3217
  `Installed ${result.copiedScripts.length} hook scripts to .harness/hooks/`
2959
3218
  );
2960
3219
  logger.info(`Profile: ${profile}`);
2961
- logger.info(`Settings: ${path19.relative(projectDir, result.settingsPath)}`);
3220
+ logger.info(
3221
+ `Settings: ${path20.relative(projectDir, result.settingsPath).replaceAll("\\", "/")}`
3222
+ );
2962
3223
  logger.dim("Run 'harness hooks list' to see installed hooks");
2963
3224
  }
2964
3225
  } catch (err) {
@@ -2970,19 +3231,19 @@ function createInitCommand2() {
2970
3231
  }
2971
3232
 
2972
3233
  // src/commands/hooks/list.ts
2973
- import { Command as Command25 } from "commander";
2974
- import * as fs7 from "fs";
2975
- import * as path20 from "path";
3234
+ import { Command as Command27 } from "commander";
3235
+ import * as fs9 from "fs";
3236
+ import * as path21 from "path";
2976
3237
  function listHooks(projectDir) {
2977
- const hooksDir = path20.join(projectDir, ".harness", "hooks");
2978
- const profilePath = path20.join(hooksDir, "profile.json");
2979
- if (!fs7.existsSync(profilePath)) {
3238
+ const hooksDir = path21.join(projectDir, ".harness", "hooks");
3239
+ const profilePath = path21.join(hooksDir, "profile.json");
3240
+ if (!fs9.existsSync(profilePath)) {
2980
3241
  return { installed: false, profile: null, hooks: [] };
2981
3242
  }
2982
3243
  let profile = "standard";
2983
3244
  let warning;
2984
3245
  try {
2985
- const data = JSON.parse(fs7.readFileSync(profilePath, "utf-8"));
3246
+ const data = JSON.parse(fs9.readFileSync(profilePath, "utf-8"));
2986
3247
  if (data.profile && ["minimal", "standard", "strict"].includes(data.profile)) {
2987
3248
  profile = data.profile;
2988
3249
  }
@@ -2994,7 +3255,7 @@ function listHooks(projectDir) {
2994
3255
  name: h.name,
2995
3256
  event: h.event,
2996
3257
  matcher: h.matcher,
2997
- scriptPath: path20.join(".harness", "hooks", `${h.name}.js`)
3258
+ scriptPath: path21.join(".harness", "hooks", `${h.name}.js`)
2998
3259
  }));
2999
3260
  const result = { installed: true, profile, hooks };
3000
3261
  if (warning) {
@@ -3003,7 +3264,7 @@ function listHooks(projectDir) {
3003
3264
  return result;
3004
3265
  }
3005
3266
  function createListCommand() {
3006
- return new Command25("list").description("Show installed hooks and active profile").action(async (_opts, cmd) => {
3267
+ return new Command27("list").description("Show installed hooks and active profile").action(async (_opts, cmd) => {
3007
3268
  const globalOpts = cmd.optsWithGlobals();
3008
3269
  const projectDir = process.cwd();
3009
3270
  const result = listHooks(projectDir);
@@ -3024,28 +3285,28 @@ function createListCommand() {
3024
3285
  }
3025
3286
 
3026
3287
  // src/commands/hooks/remove.ts
3027
- import { Command as Command26 } from "commander";
3028
- import * as fs8 from "fs";
3029
- import * as path21 from "path";
3288
+ import { Command as Command28 } from "commander";
3289
+ import * as fs10 from "fs";
3290
+ import * as path22 from "path";
3030
3291
  function removeHooks(projectDir) {
3031
- const hooksDir = path21.join(projectDir, ".harness", "hooks");
3032
- const settingsPath = path21.join(projectDir, ".claude", "settings.json");
3292
+ const hooksDir = path22.join(projectDir, ".harness", "hooks");
3293
+ const settingsPath = path22.join(projectDir, ".claude", "settings.json");
3033
3294
  let removed = false;
3034
3295
  let settingsCleaned = false;
3035
- if (fs8.existsSync(hooksDir)) {
3036
- fs8.rmSync(hooksDir, { recursive: true, force: true });
3296
+ if (fs10.existsSync(hooksDir)) {
3297
+ fs10.rmSync(hooksDir, { recursive: true, force: true });
3037
3298
  removed = true;
3038
3299
  }
3039
- if (fs8.existsSync(settingsPath)) {
3300
+ if (fs10.existsSync(settingsPath)) {
3040
3301
  try {
3041
- const settings = JSON.parse(fs8.readFileSync(settingsPath, "utf-8"));
3302
+ const settings = JSON.parse(fs10.readFileSync(settingsPath, "utf-8"));
3042
3303
  if (settings.hooks !== void 0) {
3043
3304
  delete settings.hooks;
3044
3305
  settingsCleaned = true;
3045
3306
  if (Object.keys(settings).length === 0) {
3046
- fs8.unlinkSync(settingsPath);
3307
+ fs10.unlinkSync(settingsPath);
3047
3308
  } else {
3048
- fs8.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
3309
+ fs10.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
3049
3310
  }
3050
3311
  }
3051
3312
  } catch {
@@ -3054,7 +3315,7 @@ function removeHooks(projectDir) {
3054
3315
  return { removed, hooksDir, settingsCleaned };
3055
3316
  }
3056
3317
  function createRemoveCommand() {
3057
- return new Command26("remove").description("Remove harness-managed hooks from the current project").action(async (_opts, cmd) => {
3318
+ return new Command28("remove").description("Remove harness-managed hooks from the current project").action(async (_opts, cmd) => {
3058
3319
  const globalOpts = cmd.optsWithGlobals();
3059
3320
  const projectDir = process.cwd();
3060
3321
  const result = removeHooks(projectDir);
@@ -3076,21 +3337,21 @@ function createRemoveCommand() {
3076
3337
  }
3077
3338
 
3078
3339
  // src/commands/hooks/add.ts
3079
- import { Command as Command27 } from "commander";
3080
- import * as fs9 from "fs";
3081
- import * as path22 from "path";
3340
+ import { Command as Command29 } from "commander";
3341
+ import * as fs11 from "fs";
3342
+ import * as path23 from "path";
3082
3343
  import { fileURLToPath as fileURLToPath3 } from "url";
3083
- var __dirname3 = path22.dirname(fileURLToPath3(import.meta.url));
3344
+ var __dirname3 = path23.dirname(fileURLToPath3(import.meta.url));
3084
3345
  var ALIASES = {
3085
3346
  sentinel: ["sentinel-pre", "sentinel-post"]
3086
3347
  };
3087
3348
  function hookSourceDir() {
3088
- const d = path22.resolve(__dirname3, "..", "..", "hooks");
3089
- if (fs9.existsSync(d)) return d;
3349
+ const d = path23.resolve(__dirname3, "..", "..", "hooks");
3350
+ if (fs11.existsSync(d)) return d;
3090
3351
  throw new Error(`Hook scripts not found: ${d}`);
3091
3352
  }
3092
3353
  function readJson(p) {
3093
- return fs9.existsSync(p) ? JSON.parse(fs9.readFileSync(p, "utf-8")) : {};
3354
+ return fs11.existsSync(p) ? JSON.parse(fs11.readFileSync(p, "utf-8")) : {};
3094
3355
  }
3095
3356
  function registerHook(s, ev, matcher, name) {
3096
3357
  if (!s.hooks[ev]) s.hooks[ev] = [];
@@ -3103,11 +3364,11 @@ function addHooks(hookName, projectDir) {
3103
3364
  const names = ALIASES[hookName] ?? [hookName];
3104
3365
  const result = { added: [], alreadyInstalled: [], notFound: [] };
3105
3366
  const srcDir = hookSourceDir();
3106
- const destDir = path22.join(projectDir, ".harness", "hooks");
3107
- fs9.mkdirSync(destDir, { recursive: true });
3108
- const claudeDir = path22.join(projectDir, ".claude");
3109
- fs9.mkdirSync(claudeDir, { recursive: true });
3110
- const settingsPath = path22.join(claudeDir, "settings.json");
3367
+ const destDir = path23.join(projectDir, ".harness", "hooks");
3368
+ fs11.mkdirSync(destDir, { recursive: true });
3369
+ const claudeDir = path23.join(projectDir, ".claude");
3370
+ fs11.mkdirSync(claudeDir, { recursive: true });
3371
+ const settingsPath = path23.join(claudeDir, "settings.json");
3111
3372
  const settings = readJson(settingsPath);
3112
3373
  if (!settings.hooks) settings.hooks = {};
3113
3374
  for (const name of names) {
@@ -3116,24 +3377,24 @@ function addHooks(hookName, projectDir) {
3116
3377
  result.notFound.push(name);
3117
3378
  continue;
3118
3379
  }
3119
- const src = path22.join(srcDir, `${name}.js`);
3120
- const dest = path22.join(destDir, `${name}.js`);
3121
- if (fs9.existsSync(dest)) {
3380
+ const src = path23.join(srcDir, `${name}.js`);
3381
+ const dest = path23.join(destDir, `${name}.js`);
3382
+ if (fs11.existsSync(dest)) {
3122
3383
  result.alreadyInstalled.push(name);
3123
- } else if (!fs9.existsSync(src)) {
3384
+ } else if (!fs11.existsSync(src)) {
3124
3385
  result.notFound.push(name);
3125
3386
  continue;
3126
3387
  } else {
3127
- fs9.copyFileSync(src, dest);
3388
+ fs11.copyFileSync(src, dest);
3128
3389
  result.added.push(name);
3129
3390
  }
3130
3391
  registerHook(settings, def.event, def.matcher, name);
3131
3392
  }
3132
- fs9.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
3393
+ fs11.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
3133
3394
  return result;
3134
3395
  }
3135
3396
  function createAddCommand2() {
3136
- return new Command27("add").argument("<hook-name>", "Hook name or alias (e.g., sentinel)").description("Add a hook without changing the profile").action(async (hookName, _opts, cmd) => {
3397
+ return new Command29("add").argument("<hook-name>", "Hook name or alias (e.g., sentinel)").description("Add a hook without changing the profile").action(async (hookName, _opts, cmd) => {
3137
3398
  const projectDir = process.cwd();
3138
3399
  try {
3139
3400
  const res = addHooks(hookName, projectDir);
@@ -3158,7 +3419,7 @@ function createAddCommand2() {
3158
3419
 
3159
3420
  // src/commands/hooks/index.ts
3160
3421
  function createHooksCommand() {
3161
- const command = new Command28("hooks").description("Manage Claude Code hook configurations");
3422
+ const command = new Command30("hooks").description("Manage Claude Code hook configurations");
3162
3423
  command.addCommand(createInitCommand2());
3163
3424
  command.addCommand(createListCommand());
3164
3425
  command.addCommand(createRemoveCommand());
@@ -3167,10 +3428,10 @@ function createHooksCommand() {
3167
3428
  }
3168
3429
 
3169
3430
  // src/commands/impact-preview.ts
3170
- import { Command as Command29 } from "commander";
3431
+ import { Command as Command31 } from "commander";
3171
3432
  import { execSync as execSync4 } from "child_process";
3172
- import * as path23 from "path";
3173
- import * as fs10 from "fs";
3433
+ import * as path24 from "path";
3434
+ import * as fs12 from "fs";
3174
3435
  function getStagedFiles(cwd) {
3175
3436
  try {
3176
3437
  const output = execSync4("git diff --cached --name-only", {
@@ -3184,7 +3445,7 @@ function getStagedFiles(cwd) {
3184
3445
  }
3185
3446
  function graphExists(projectPath) {
3186
3447
  try {
3187
- return fs10.existsSync(path23.join(projectPath, ".harness", "graph", "graph.json"));
3448
+ return fs12.existsSync(path24.join(projectPath, ".harness", "graph", "graph.json"));
3188
3449
  } catch {
3189
3450
  return false;
3190
3451
  }
@@ -3193,7 +3454,7 @@ function extractNodeName(id) {
3193
3454
  const parts = id.split(":");
3194
3455
  if (parts.length > 1) {
3195
3456
  const fullPath = parts.slice(1).join(":");
3196
- return path23.basename(fullPath);
3457
+ return path24.basename(fullPath);
3197
3458
  }
3198
3459
  return id;
3199
3460
  }
@@ -3348,7 +3609,7 @@ function formatImpactOutput(stagedFiles, acc, options) {
3348
3609
  return options.detailed ? formatDetailed(stagedFiles.length, merged, acc.aggregateCounts) : formatCompact(stagedFiles.length, merged, acc.aggregateCounts);
3349
3610
  }
3350
3611
  async function runImpactPreview(options) {
3351
- const projectPath = path23.resolve(options.path ?? process.cwd());
3612
+ const projectPath = path24.resolve(options.path ?? process.cwd());
3352
3613
  const stagedFiles = getStagedFiles(projectPath);
3353
3614
  if (stagedFiles.length === 0) return "Impact Preview: no staged changes";
3354
3615
  if (!graphExists(projectPath)) {
@@ -3366,7 +3627,7 @@ async function runImpactPreview(options) {
3366
3627
  return formatImpactOutput(stagedFiles, acc, options);
3367
3628
  }
3368
3629
  function createImpactPreviewCommand() {
3369
- const command = new Command29("impact-preview").description("Show blast radius of staged changes using the knowledge graph").option("--detailed", "Show all affected files instead of top items").option("--per-file", "Show impact per staged file instead of aggregate").option("--path <dir>", "Project root (default: cwd)").action(async (opts) => {
3630
+ const command = new Command31("impact-preview").description("Show blast radius of staged changes using the knowledge graph").option("--detailed", "Show all affected files instead of top items").option("--per-file", "Show impact per staged file instead of aggregate").option("--path <dir>", "Project root (default: cwd)").action(async (opts) => {
3370
3631
  const output = await runImpactPreview({
3371
3632
  detailed: opts.detailed,
3372
3633
  perFile: opts.perFile,
@@ -3379,28 +3640,29 @@ function createImpactPreviewCommand() {
3379
3640
  }
3380
3641
 
3381
3642
  // src/commands/init.ts
3382
- import { Command as Command31 } from "commander";
3643
+ init_dist();
3644
+ import { Command as Command33 } from "commander";
3383
3645
  import chalk3 from "chalk";
3384
- import * as fs13 from "fs";
3385
- import * as path26 from "path";
3646
+ import * as fs15 from "fs";
3647
+ import * as path27 from "path";
3386
3648
 
3387
3649
  // src/commands/setup-mcp.ts
3388
- import { Command as Command30 } from "commander";
3389
- import * as fs12 from "fs";
3390
- import * as path25 from "path";
3650
+ import { Command as Command32 } from "commander";
3651
+ import * as fs14 from "fs";
3652
+ import * as path26 from "path";
3391
3653
  import * as os2 from "os";
3392
3654
  import chalk2 from "chalk";
3393
3655
  import * as clack from "@clack/prompts";
3394
3656
 
3395
3657
  // src/integrations/toml.ts
3396
- import * as fs11 from "fs";
3397
- import * as path24 from "path";
3658
+ import * as fs13 from "fs";
3659
+ import * as path25 from "path";
3398
3660
  function writeTomlMcpEntry(filePath, name, entry) {
3399
- const dir = path24.dirname(filePath);
3400
- if (!fs11.existsSync(dir)) {
3401
- fs11.mkdirSync(dir, { recursive: true });
3661
+ const dir = path25.dirname(filePath);
3662
+ if (!fs13.existsSync(dir)) {
3663
+ fs13.mkdirSync(dir, { recursive: true });
3402
3664
  }
3403
- const existing = fs11.existsSync(filePath) ? fs11.readFileSync(filePath, "utf-8") : "";
3665
+ const existing = fs13.existsSync(filePath) ? fs13.readFileSync(filePath, "utf-8") : "";
3404
3666
  const blockHeader = `[mcp_servers.${name}]`;
3405
3667
  const newBlock = serializeTomlMcpBlock(name, entry);
3406
3668
  let updated;
@@ -3411,8 +3673,8 @@ function writeTomlMcpEntry(filePath, name, entry) {
3411
3673
  updated = existing + separator + newBlock;
3412
3674
  }
3413
3675
  const tmp = filePath + ".tmp";
3414
- fs11.writeFileSync(tmp, updated);
3415
- fs11.renameSync(tmp, filePath);
3676
+ fs13.writeFileSync(tmp, updated);
3677
+ fs13.renameSync(tmp, filePath);
3416
3678
  }
3417
3679
  function serializeTomlMcpBlock(name, entry) {
3418
3680
  const lines = [`[mcp_servers.${name}]`];
@@ -3470,6 +3732,7 @@ var CURSOR_CURATED_TOOLS = [
3470
3732
  "get_impact",
3471
3733
  "detect_entropy",
3472
3734
  "run_security_scan",
3735
+ "get_security_trends",
3473
3736
  "assess_project",
3474
3737
  "manage_state",
3475
3738
  "create_self_review",
@@ -3514,6 +3777,7 @@ var ALL_MCP_TOOLS = [
3514
3777
  "ingest_source",
3515
3778
  "generate_agent_definitions",
3516
3779
  "run_security_scan",
3780
+ "get_security_trends",
3517
3781
  "check_performance",
3518
3782
  "get_perf_baselines",
3519
3783
  "update_perf_baselines",
@@ -3540,7 +3804,8 @@ var ALL_MCP_TOOLS = [
3540
3804
  "recommend_skills",
3541
3805
  "compute_blast_radius",
3542
3806
  "dispatch_skills",
3543
- "compact"
3807
+ "compact",
3808
+ "detect_constraint_emergence"
3544
3809
  ];
3545
3810
  async function runCursorToolPicker() {
3546
3811
  try {
@@ -3568,20 +3833,20 @@ function writeCursorMcpEntryWithTools(configPath, tools) {
3568
3833
  });
3569
3834
  }
3570
3835
  function readJsonFile(filePath) {
3571
- if (!fs12.existsSync(filePath)) return null;
3836
+ if (!fs14.existsSync(filePath)) return null;
3572
3837
  try {
3573
- return JSON.parse(fs12.readFileSync(filePath, "utf-8"));
3838
+ return JSON.parse(fs14.readFileSync(filePath, "utf-8"));
3574
3839
  } catch {
3575
- fs12.copyFileSync(filePath, filePath + ".bak");
3840
+ fs14.copyFileSync(filePath, filePath + ".bak");
3576
3841
  return null;
3577
3842
  }
3578
3843
  }
3579
3844
  function writeJsonFile(filePath, data) {
3580
- const dir = path25.dirname(filePath);
3581
- if (!fs12.existsSync(dir)) {
3582
- fs12.mkdirSync(dir, { recursive: true });
3845
+ const dir = path26.dirname(filePath);
3846
+ if (!fs14.existsSync(dir)) {
3847
+ fs14.mkdirSync(dir, { recursive: true });
3583
3848
  }
3584
- fs12.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
3849
+ fs14.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
3585
3850
  }
3586
3851
  function configureMcpServer(configPath) {
3587
3852
  const config = readJsonFile(configPath) ?? {};
@@ -3596,7 +3861,7 @@ function configureMcpServer(configPath) {
3596
3861
  return true;
3597
3862
  }
3598
3863
  function addGeminiTrustedFolder(cwd) {
3599
- const trustedPath = path25.join(os2.homedir(), ".gemini", "trustedFolders.json");
3864
+ const trustedPath = path26.join(os2.homedir(), ".gemini", "trustedFolders.json");
3600
3865
  const folders = readJsonFile(trustedPath) ?? {};
3601
3866
  if (folders[cwd] === "TRUST_FOLDER") {
3602
3867
  return false;
@@ -3610,7 +3875,7 @@ function setupMcp(cwd, client) {
3610
3875
  const skipped = [];
3611
3876
  let trustedFolder = false;
3612
3877
  if (client === "all" || client === "claude") {
3613
- const configPath = path25.join(cwd, ".mcp.json");
3878
+ const configPath = path26.join(cwd, ".mcp.json");
3614
3879
  if (configureMcpServer(configPath)) {
3615
3880
  configured.push("Claude Code");
3616
3881
  } else {
@@ -3618,7 +3883,7 @@ function setupMcp(cwd, client) {
3618
3883
  }
3619
3884
  }
3620
3885
  if (client === "all" || client === "gemini") {
3621
- const configPath = path25.join(cwd, ".gemini", "settings.json");
3886
+ const configPath = path26.join(cwd, ".gemini", "settings.json");
3622
3887
  if (configureMcpServer(configPath)) {
3623
3888
  configured.push("Gemini CLI");
3624
3889
  } else {
@@ -3627,10 +3892,10 @@ function setupMcp(cwd, client) {
3627
3892
  trustedFolder = addGeminiTrustedFolder(cwd);
3628
3893
  }
3629
3894
  if (client === "all" || client === "codex") {
3630
- const configPath = path25.join(cwd, ".codex", "config.toml");
3895
+ const configPath = path26.join(cwd, ".codex", "config.toml");
3631
3896
  const alreadyConfigured = (() => {
3632
- if (!fs12.existsSync(configPath)) return false;
3633
- const content = fs12.readFileSync(configPath, "utf-8");
3897
+ if (!fs14.existsSync(configPath)) return false;
3898
+ const content = fs14.readFileSync(configPath, "utf-8");
3634
3899
  return content.includes("[mcp_servers.harness]");
3635
3900
  })();
3636
3901
  if (alreadyConfigured) {
@@ -3645,7 +3910,7 @@ function setupMcp(cwd, client) {
3645
3910
  }
3646
3911
  }
3647
3912
  if (client === "all" || client === "cursor") {
3648
- const configPath = path25.join(cwd, ".cursor", "mcp.json");
3913
+ const configPath = path26.join(cwd, ".cursor", "mcp.json");
3649
3914
  const existing = readJsonFile(configPath);
3650
3915
  if (existing?.mcpServers?.["harness"]) {
3651
3916
  skipped.push("Cursor");
@@ -3659,7 +3924,7 @@ function setupMcp(cwd, client) {
3659
3924
  async function resolveCursorWithPicker(cwd, pick) {
3660
3925
  const configured = [];
3661
3926
  const skipped = [];
3662
- const cursorConfigPath = path25.join(cwd, ".cursor", "mcp.json");
3927
+ const cursorConfigPath = path26.join(cwd, ".cursor", "mcp.json");
3663
3928
  const existing = readJsonFile(cursorConfigPath);
3664
3929
  if (existing?.mcpServers?.["harness"] && !pick) {
3665
3930
  skipped.push("Cursor");
@@ -3703,7 +3968,7 @@ function printMcpResult(configured, skipped, trustedFolder) {
3703
3968
  console.log("");
3704
3969
  }
3705
3970
  function createSetupMcpCommand() {
3706
- return new Command30("setup-mcp").description("Configure MCP server for AI agent integration").option("--client <client>", "Client to configure (claude, gemini, codex, cursor, all)", "all").option("--pick", "Launch interactive tool picker (Cursor only)").option("--yes", "Bypass interactive picker and use curated 25-tool set (Cursor only)").action(async (opts, cmd) => {
3971
+ return new Command32("setup-mcp").description("Configure MCP server for AI agent integration").option("--client <client>", "Client to configure (claude, gemini, codex, cursor, all)", "all").option("--pick", "Launch interactive tool picker (Cursor only)").option("--yes", "Bypass interactive picker and use curated 25-tool set (Cursor only)").action(async (opts, cmd) => {
3707
3972
  const globalOpts = cmd.optsWithGlobals();
3708
3973
  const cwd = process.cwd();
3709
3974
  let configured;
@@ -3730,12 +3995,12 @@ function loadEngineAndTemplates(_options) {
3730
3995
  }
3731
3996
  function resolveInitDefaults(options) {
3732
3997
  const cwd = options.cwd ?? process.cwd();
3733
- return { cwd, name: options.name ?? path26.basename(cwd), force: options.force ?? false };
3998
+ return { cwd, name: options.name ?? path27.basename(cwd), force: options.force ?? false };
3734
3999
  }
3735
4000
  async function runInit(options) {
3736
4001
  const { cwd, name, force } = resolveInitDefaults(options);
3737
- const configPath = path26.join(cwd, "harness.config.json");
3738
- if (!force && fs13.existsSync(configPath)) {
4002
+ const configPath = path27.join(cwd, "harness.config.json");
4003
+ if (!force && fs15.existsSync(configPath)) {
3739
4004
  return Err(
3740
4005
  new CLIError("Project already initialized. Use --force to overwrite.", ExitCode.ERROR)
3741
4006
  );
@@ -3843,11 +4108,11 @@ async function runInitAction2(opts) {
3843
4108
  process.exit(ExitCode.SUCCESS);
3844
4109
  }
3845
4110
  function createInitCommand3() {
3846
- const command = new Command31("init").description("Initialize a new harness-engineering project").option("-n, --name <name>", "Project name").option("-l, --level <level>", "Adoption level (basic, intermediate, advanced)", "basic").option("--framework <framework>", "Framework overlay (nextjs)").option("--language <language>", "Target language (typescript, python, go, rust, java)").option("-f, --force", "Overwrite existing files").option("-y, --yes", "Use defaults without prompting").action(async (opts, cmd) => {
4111
+ const command = new Command33("init").description("Initialize a new harness-engineering project").option("-n, --name <name>", "Project name").option("-l, --level <level>", "Adoption level (basic, intermediate, advanced)", "basic").option("-t, --template <template>", "Specific template name (e.g. orchestrator)").option("--framework <framework>", "Framework overlay (nextjs)").option("--language <language>", "Target language (typescript, python, go, rust, java)").option("-f, --force", "Overwrite existing files").option("-y, --yes", "Use defaults without prompting").action(async (opts, cmd) => {
3847
4112
  const globalOpts = cmd.optsWithGlobals();
3848
4113
  await runInitAction2({
3849
4114
  name: opts.name,
3850
- level: opts.level,
4115
+ level: opts.template ?? opts.level,
3851
4116
  framework: opts.framework,
3852
4117
  language: opts.language,
3853
4118
  force: opts.force,
@@ -3858,16 +4123,16 @@ function createInitCommand3() {
3858
4123
  }
3859
4124
 
3860
4125
  // src/commands/install.ts
3861
- import * as fs18 from "fs";
4126
+ import * as fs20 from "fs";
3862
4127
  import * as os5 from "os";
3863
- import * as path31 from "path";
4128
+ import * as path32 from "path";
3864
4129
  import { execFileSync as execFileSync3 } from "child_process";
3865
- import { Command as Command32 } from "commander";
4130
+ import { Command as Command34 } from "commander";
3866
4131
  import { parse as yamlParse } from "yaml";
3867
4132
 
3868
4133
  // src/registry/npm-client.ts
3869
- import * as fs14 from "fs";
3870
- import * as path27 from "path";
4134
+ import * as fs16 from "fs";
4135
+ import * as path28 from "path";
3871
4136
  import * as os3 from "os";
3872
4137
  var NPM_REGISTRY = "https://registry.npmjs.org";
3873
4138
  var FETCH_TIMEOUT_MS = 3e4;
@@ -3890,10 +4155,10 @@ function extractSkillName(packageName) {
3890
4155
  function readNpmrcToken(registryUrl) {
3891
4156
  const { hostname, pathname } = new URL(registryUrl);
3892
4157
  const registryPath = `//${hostname}${pathname.replace(/\/$/, "")}/:_authToken=`;
3893
- const candidates = [path27.join(process.cwd(), ".npmrc"), path27.join(os3.homedir(), ".npmrc")];
4158
+ const candidates = [path28.join(process.cwd(), ".npmrc"), path28.join(os3.homedir(), ".npmrc")];
3894
4159
  for (const npmrcPath of candidates) {
3895
4160
  try {
3896
- const content = fs14.readFileSync(npmrcPath, "utf-8");
4161
+ const content = fs16.readFileSync(npmrcPath, "utf-8");
3897
4162
  for (const line of content.split("\n")) {
3898
4163
  const trimmed = line.trim();
3899
4164
  if (trimmed.startsWith(registryPath)) {
@@ -3988,19 +4253,19 @@ async function searchNpmRegistry(query, registryUrl) {
3988
4253
  }
3989
4254
 
3990
4255
  // src/registry/tarball.ts
3991
- import * as fs15 from "fs";
3992
- import * as path28 from "path";
4256
+ import * as fs17 from "fs";
4257
+ import * as path29 from "path";
3993
4258
  import * as os4 from "os";
3994
4259
  import { execFileSync as execFileSync2 } from "child_process";
3995
4260
  function extractTarball(tarballBuffer) {
3996
- const tmpDir = fs15.mkdtempSync(path28.join(os4.tmpdir(), "harness-skill-install-"));
3997
- const tarballPath = path28.join(tmpDir, "package.tgz");
4261
+ const tmpDir = fs17.mkdtempSync(path29.join(os4.tmpdir(), "harness-skill-install-"));
4262
+ const tarballPath = path29.join(tmpDir, "package.tgz");
3998
4263
  try {
3999
- fs15.writeFileSync(tarballPath, tarballBuffer);
4264
+ fs17.writeFileSync(tarballPath, tarballBuffer);
4000
4265
  execFileSync2("tar", ["-xzf", tarballPath, "-C", tmpDir], {
4001
4266
  timeout: 3e4
4002
4267
  });
4003
- fs15.unlinkSync(tarballPath);
4268
+ fs17.unlinkSync(tarballPath);
4004
4269
  } catch (err) {
4005
4270
  cleanupTempDir(tmpDir);
4006
4271
  throw new Error(
@@ -4011,37 +4276,37 @@ function extractTarball(tarballBuffer) {
4011
4276
  return tmpDir;
4012
4277
  }
4013
4278
  function placeSkillContent(extractedPkgDir, communityBaseDir, skillName, platforms) {
4014
- const files = fs15.readdirSync(extractedPkgDir);
4279
+ const files = fs17.readdirSync(extractedPkgDir);
4015
4280
  for (const platform of platforms) {
4016
- const targetDir = path28.join(communityBaseDir, platform, skillName);
4017
- if (fs15.existsSync(targetDir)) {
4018
- fs15.rmSync(targetDir, { recursive: true, force: true });
4281
+ const targetDir = path29.join(communityBaseDir, platform, skillName);
4282
+ if (fs17.existsSync(targetDir)) {
4283
+ fs17.rmSync(targetDir, { recursive: true, force: true });
4019
4284
  }
4020
- fs15.mkdirSync(targetDir, { recursive: true });
4285
+ fs17.mkdirSync(targetDir, { recursive: true });
4021
4286
  for (const file of files) {
4022
4287
  if (file === "package.json" || file === "node_modules") continue;
4023
- const srcPath = path28.join(extractedPkgDir, file);
4024
- const destPath = path28.join(targetDir, file);
4025
- const stat = fs15.statSync(srcPath);
4288
+ const srcPath = path29.join(extractedPkgDir, file);
4289
+ const destPath = path29.join(targetDir, file);
4290
+ const stat = fs17.statSync(srcPath);
4026
4291
  if (stat.isDirectory()) {
4027
- fs15.cpSync(srcPath, destPath, { recursive: true });
4292
+ fs17.cpSync(srcPath, destPath, { recursive: true });
4028
4293
  } else {
4029
- fs15.copyFileSync(srcPath, destPath);
4294
+ fs17.copyFileSync(srcPath, destPath);
4030
4295
  }
4031
4296
  }
4032
4297
  }
4033
4298
  }
4034
4299
  function removeSkillContent(communityBaseDir, skillName, platforms) {
4035
4300
  for (const platform of platforms) {
4036
- const targetDir = path28.join(communityBaseDir, platform, skillName);
4037
- if (fs15.existsSync(targetDir)) {
4038
- fs15.rmSync(targetDir, { recursive: true, force: true });
4301
+ const targetDir = path29.join(communityBaseDir, platform, skillName);
4302
+ if (fs17.existsSync(targetDir)) {
4303
+ fs17.rmSync(targetDir, { recursive: true, force: true });
4039
4304
  }
4040
4305
  }
4041
4306
  }
4042
4307
  function cleanupTempDir(dirPath) {
4043
4308
  try {
4044
- fs15.rmSync(dirPath, { recursive: true, force: true });
4309
+ fs17.rmSync(dirPath, { recursive: true, force: true });
4045
4310
  } catch {
4046
4311
  }
4047
4312
  }
@@ -4083,8 +4348,8 @@ function findDependentsOf(lockfile, targetPackageName) {
4083
4348
  }
4084
4349
 
4085
4350
  // src/registry/lockfile.ts
4086
- import * as fs16 from "fs";
4087
- import * as path29 from "path";
4351
+ import * as fs18 from "fs";
4352
+ import * as path30 from "path";
4088
4353
  function createEmptyLockfile() {
4089
4354
  return { version: 1, skills: {} };
4090
4355
  }
@@ -4104,10 +4369,10 @@ function sortedStringify(obj) {
4104
4369
  );
4105
4370
  }
4106
4371
  function readLockfile2(filePath) {
4107
- if (!fs16.existsSync(filePath)) {
4372
+ if (!fs18.existsSync(filePath)) {
4108
4373
  return createEmptyLockfile();
4109
4374
  }
4110
- const raw = fs16.readFileSync(filePath, "utf-8");
4375
+ const raw = fs18.readFileSync(filePath, "utf-8");
4111
4376
  let parsed;
4112
4377
  try {
4113
4378
  parsed = JSON.parse(raw);
@@ -4124,9 +4389,9 @@ function readLockfile2(filePath) {
4124
4389
  return parsed;
4125
4390
  }
4126
4391
  function writeLockfile2(filePath, lockfile) {
4127
- const dir = path29.dirname(filePath);
4128
- fs16.mkdirSync(dir, { recursive: true });
4129
- fs16.writeFileSync(filePath, sortedStringify(lockfile) + "\n", "utf-8");
4392
+ const dir = path30.dirname(filePath);
4393
+ fs18.mkdirSync(dir, { recursive: true });
4394
+ fs18.writeFileSync(filePath, sortedStringify(lockfile) + "\n", "utf-8");
4130
4395
  }
4131
4396
  function updateLockfileEntry(lockfile, name, entry) {
4132
4397
  return {
@@ -4149,17 +4414,17 @@ function removeLockfileEntry(lockfile, name) {
4149
4414
  }
4150
4415
 
4151
4416
  // src/registry/bundled-skills.ts
4152
- import * as fs17 from "fs";
4153
- import * as path30 from "path";
4417
+ import * as fs19 from "fs";
4418
+ import * as path31 from "path";
4154
4419
  function getBundledSkillNames(bundledSkillsDir) {
4155
- if (!fs17.existsSync(bundledSkillsDir)) {
4420
+ if (!fs19.existsSync(bundledSkillsDir)) {
4156
4421
  return /* @__PURE__ */ new Set();
4157
4422
  }
4158
- const entries = fs17.readdirSync(bundledSkillsDir);
4423
+ const entries = fs19.readdirSync(bundledSkillsDir);
4159
4424
  const names = /* @__PURE__ */ new Set();
4160
4425
  for (const entry of entries) {
4161
4426
  try {
4162
- const stat = fs17.statSync(path30.join(bundledSkillsDir, String(entry)));
4427
+ const stat = fs19.statSync(path31.join(bundledSkillsDir, String(entry)));
4163
4428
  if (stat.isDirectory()) {
4164
4429
  names.add(String(entry));
4165
4430
  }
@@ -4186,12 +4451,12 @@ function validateSkillYaml(parsed) {
4186
4451
  function resolveCommunityBase(global) {
4187
4452
  if (global) {
4188
4453
  const communityBase2 = resolveGlobalCommunityBaseDir();
4189
- return { communityBase: communityBase2, lockfilePath: path31.join(communityBase2, "skills-lock.json") };
4454
+ return { communityBase: communityBase2, lockfilePath: path32.join(communityBase2, "skills-lock.json") };
4190
4455
  }
4191
4456
  const globalDir = resolveGlobalSkillsDir();
4192
- const skillsDir = path31.dirname(globalDir);
4193
- const communityBase = path31.join(skillsDir, "community");
4194
- return { communityBase, lockfilePath: path31.join(communityBase, "skills-lock.json") };
4457
+ const skillsDir = path32.dirname(globalDir);
4458
+ const communityBase = path32.join(skillsDir, "community");
4459
+ return { communityBase, lockfilePath: path32.join(communityBase, "skills-lock.json") };
4195
4460
  }
4196
4461
  function parseGitHubRef(from) {
4197
4462
  const ghPrefix = from.match(/^github:([^/]+)\/([^#]+?)(?:#(.+))?$/);
@@ -4207,7 +4472,7 @@ function parseGitHubRef(from) {
4207
4472
  return null;
4208
4473
  }
4209
4474
  function cloneGitHubRepo(owner, repo, ref) {
4210
- const tmpDir = fs18.mkdtempSync(path31.join(os5.tmpdir(), "harness-gh-install-"));
4475
+ const tmpDir = fs20.mkdtempSync(path32.join(os5.tmpdir(), "harness-gh-install-"));
4211
4476
  const url = `https://github.com/${owner}/${repo}.git`;
4212
4477
  try {
4213
4478
  const cloneArgs = ["clone", "--depth", "1"];
@@ -4228,43 +4493,43 @@ function discoverSkillDirs(rootDir) {
4228
4493
  const skillDirs = [];
4229
4494
  function scan(dir, depth) {
4230
4495
  if (depth > 3) return;
4231
- if (!fs18.existsSync(dir)) return;
4232
- if (fs18.existsSync(path31.join(dir, "skill.yaml"))) {
4496
+ if (!fs20.existsSync(dir)) return;
4497
+ if (fs20.existsSync(path32.join(dir, "skill.yaml"))) {
4233
4498
  skillDirs.push(dir);
4234
4499
  return;
4235
4500
  }
4236
- const entries = fs18.readdirSync(dir, { withFileTypes: true });
4501
+ const entries = fs20.readdirSync(dir, { withFileTypes: true });
4237
4502
  for (const entry of entries) {
4238
4503
  if (!entry.isDirectory()) continue;
4239
4504
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
4240
- scan(path31.join(dir, entry.name), depth + 1);
4505
+ scan(path32.join(dir, entry.name), depth + 1);
4241
4506
  }
4242
4507
  }
4243
4508
  scan(rootDir, 0);
4244
4509
  return skillDirs;
4245
4510
  }
4246
4511
  function resolveLocalPkgDir(fromPath) {
4247
- const resolvedPath = path31.resolve(fromPath);
4248
- if (!fs18.existsSync(resolvedPath)) {
4512
+ const resolvedPath = path32.resolve(fromPath);
4513
+ if (!fs20.existsSync(resolvedPath)) {
4249
4514
  throw new Error(`--from path does not exist: ${resolvedPath}`);
4250
4515
  }
4251
- const stat = fs18.statSync(resolvedPath);
4516
+ const stat = fs20.statSync(resolvedPath);
4252
4517
  if (stat.isDirectory()) {
4253
4518
  return { pkgDir: resolvedPath, extractDir: null };
4254
4519
  }
4255
4520
  if (resolvedPath.endsWith(".tgz") || resolvedPath.endsWith(".tar.gz")) {
4256
- const tarballBuffer = fs18.readFileSync(resolvedPath);
4521
+ const tarballBuffer = fs20.readFileSync(resolvedPath);
4257
4522
  const extractDir = extractTarball(tarballBuffer);
4258
- return { pkgDir: path31.join(extractDir, "package"), extractDir };
4523
+ return { pkgDir: path32.join(extractDir, "package"), extractDir };
4259
4524
  }
4260
4525
  throw new Error(`--from path must be a directory or .tgz file. Got: ${resolvedPath}`);
4261
4526
  }
4262
4527
  function installSkillDir(pkgDir, resolvedPath, options) {
4263
- const skillYamlPath = path31.join(pkgDir, "skill.yaml");
4264
- if (!fs18.existsSync(skillYamlPath)) {
4528
+ const skillYamlPath = path32.join(pkgDir, "skill.yaml");
4529
+ if (!fs20.existsSync(skillYamlPath)) {
4265
4530
  throw new Error(`No skill.yaml found at ${skillYamlPath}`);
4266
4531
  }
4267
- const skillYaml = validateSkillYaml(yamlParse(fs18.readFileSync(skillYamlPath, "utf-8")));
4532
+ const skillYaml = validateSkillYaml(yamlParse(fs20.readFileSync(skillYamlPath, "utf-8")));
4268
4533
  const shortName = skillYaml.name;
4269
4534
  const { communityBase, lockfilePath } = resolveCommunityBase(options.global ?? false);
4270
4535
  if (!options.global) {
@@ -4292,7 +4557,7 @@ function installSkillDir(pkgDir, resolvedPath, options) {
4292
4557
  async function runLocalInstall(fromPath, options) {
4293
4558
  const { pkgDir, extractDir } = resolveLocalPkgDir(fromPath);
4294
4559
  try {
4295
- return installSkillDir(pkgDir, path31.resolve(fromPath), options);
4560
+ return installSkillDir(pkgDir, path32.resolve(fromPath), options);
4296
4561
  } finally {
4297
4562
  if (extractDir) cleanupTempDir(extractDir);
4298
4563
  }
@@ -4335,8 +4600,8 @@ async function runInstall(skillName, options) {
4335
4600
  version: installed.map((r) => r.version).join(", ")
4336
4601
  };
4337
4602
  }
4338
- const resolvedFrom = path31.resolve(options.from);
4339
- if (fs18.existsSync(resolvedFrom) && fs18.statSync(resolvedFrom).isDirectory() && !fs18.existsSync(path31.join(resolvedFrom, "skill.yaml"))) {
4603
+ const resolvedFrom = path32.resolve(options.from);
4604
+ if (fs20.existsSync(resolvedFrom) && fs20.statSync(resolvedFrom).isDirectory() && !fs20.existsSync(path32.join(resolvedFrom, "skill.yaml"))) {
4340
4605
  const results = await runBulkInstall(resolvedFrom, options);
4341
4606
  const installed = results.filter((r) => r.installed);
4342
4607
  return {
@@ -4377,12 +4642,12 @@ async function runInstall(skillName, options) {
4377
4642
  const extractDir = extractTarball(tarballBuffer);
4378
4643
  let skillYaml;
4379
4644
  try {
4380
- const extractedPkgDir = path31.join(extractDir, "package");
4381
- const skillYamlPath = path31.join(extractedPkgDir, "skill.yaml");
4382
- if (!fs18.existsSync(skillYamlPath)) {
4645
+ const extractedPkgDir = path32.join(extractDir, "package");
4646
+ const skillYamlPath = path32.join(extractedPkgDir, "skill.yaml");
4647
+ if (!fs20.existsSync(skillYamlPath)) {
4383
4648
  throw new Error(`contains invalid skill.yaml: file not found in package`);
4384
4649
  }
4385
- const rawYaml = fs18.readFileSync(skillYamlPath, "utf-8");
4650
+ const rawYaml = fs20.readFileSync(skillYamlPath, "utf-8");
4386
4651
  const parsed = yamlParse(rawYaml);
4387
4652
  skillYaml = validateSkillYaml(parsed);
4388
4653
  placeSkillContent(extractedPkgDir, communityBase, shortName, skillYaml.platforms);
@@ -4422,7 +4687,7 @@ async function runInstall(skillName, options) {
4422
4687
  return result;
4423
4688
  }
4424
4689
  function createInstallCommand() {
4425
- const cmd = new Command32("install");
4690
+ const cmd = new Command34("install");
4426
4691
  cmd.description("Install skills from npm registry, local directory, or GitHub repository").argument("<skill>", 'Skill name, @harness-skills/scoped package, or "." for bulk install').option("--version <range>", "Semver range or exact version to install").option("--force", "Force reinstall even if same version is already installed").option(
4427
4692
  "--from <source>",
4428
4693
  "Install from local path, directory, or GitHub (github:owner/repo, https://github.com/owner/repo)"
@@ -4455,15 +4720,16 @@ function createInstallCommand() {
4455
4720
  }
4456
4721
 
4457
4722
  // src/commands/install-constraints.ts
4458
- import * as fs19 from "fs/promises";
4459
- import * as path32 from "path";
4460
- import { Command as Command33 } from "commander";
4723
+ init_dist();
4724
+ import * as fs21 from "fs/promises";
4725
+ import * as path33 from "path";
4726
+ import { Command as Command35 } from "commander";
4461
4727
  import semver3 from "semver";
4462
4728
  async function runInstallConstraints(options) {
4463
4729
  const { source, configPath, lockfilePath } = options;
4464
4730
  let rawBundle;
4465
4731
  try {
4466
- rawBundle = await fs19.readFile(source, "utf-8");
4732
+ rawBundle = await fs21.readFile(source, "utf-8");
4467
4733
  } catch (err) {
4468
4734
  if (isNodeError(err) && err.code === "ENOENT") {
4469
4735
  return { ok: false, error: `Bundle file not found: ${source}` };
@@ -4506,7 +4772,7 @@ async function runInstallConstraints(options) {
4506
4772
  }
4507
4773
  let localConfig;
4508
4774
  try {
4509
- const raw = await fs19.readFile(configPath, "utf-8");
4775
+ const raw = await fs21.readFile(configPath, "utf-8");
4510
4776
  localConfig = JSON.parse(raw);
4511
4777
  } catch (err) {
4512
4778
  return {
@@ -4654,7 +4920,7 @@ function isNodeError(err) {
4654
4920
  return err instanceof Error && "code" in err;
4655
4921
  }
4656
4922
  function resolveConfigPath(opts) {
4657
- if (opts.config) return path32.resolve(opts.config);
4923
+ if (opts.config) return path33.resolve(opts.config);
4658
4924
  const found = findConfigFile();
4659
4925
  if (!found.ok) {
4660
4926
  logger.error(found.error.message);
@@ -4689,9 +4955,9 @@ function logInstallResult(val, opts) {
4689
4955
  }
4690
4956
  async function handleInstallConstraints(source, opts) {
4691
4957
  const configPath = resolveConfigPath(opts);
4692
- const projectRoot = path32.dirname(configPath);
4693
- const lockfilePath = path32.join(projectRoot, ".harness", "constraints.lock.json");
4694
- const resolvedSource = path32.resolve(source);
4958
+ const projectRoot = path33.dirname(configPath);
4959
+ const lockfilePath = path33.join(projectRoot, ".harness", "constraints.lock.json");
4960
+ const resolvedSource = path33.resolve(source);
4695
4961
  if (opts.forceLocal && opts.forcePackage) {
4696
4962
  logger.error("Cannot use both --force-local and --force-package.");
4697
4963
  process.exit(1);
@@ -4711,18 +4977,19 @@ async function handleInstallConstraints(source, opts) {
4711
4977
  logInstallResult(result.value, opts);
4712
4978
  }
4713
4979
  function createInstallConstraintsCommand() {
4714
- const cmd = new Command33("install-constraints");
4980
+ const cmd = new Command35("install-constraints");
4715
4981
  cmd.description("Install a constraints bundle into the local harness config").argument("<source>", "Path to a .harness-constraints.json bundle file").option("--force-local", "Resolve all conflicts by keeping local values").option("--force-package", "Resolve all conflicts by using package values").option("--dry-run", "Show what would change without writing files").option("-c, --config <path>", "Path to harness.config.json").action(handleInstallConstraints);
4716
4982
  return cmd;
4717
4983
  }
4718
4984
 
4719
4985
  // src/commands/integrations/index.ts
4720
- import { Command as Command38 } from "commander";
4986
+ import { Command as Command40 } from "commander";
4721
4987
 
4722
4988
  // src/commands/integrations/add.ts
4723
- import { Command as Command34 } from "commander";
4724
- import * as fs20 from "fs";
4725
- import * as path33 from "path";
4989
+ init_dist();
4990
+ import { Command as Command36 } from "commander";
4991
+ import * as fs22 from "fs";
4992
+ import * as path34 from "path";
4726
4993
  import chalk4 from "chalk";
4727
4994
  function buildMcpEntry(def) {
4728
4995
  const entry = { command: def.mcpConfig.command };
@@ -4731,14 +4998,14 @@ function buildMcpEntry(def) {
4731
4998
  return entry;
4732
4999
  }
4733
5000
  function writeMcpEntries(cwd, defName, mcpEntry) {
4734
- writeMcpEntry(path33.join(cwd, ".mcp.json"), defName, mcpEntry);
4735
- const geminiDir = path33.join(cwd, ".gemini");
4736
- if (fs20.existsSync(geminiDir)) {
4737
- writeMcpEntry(path33.join(geminiDir, "settings.json"), defName, mcpEntry);
5001
+ writeMcpEntry(path34.join(cwd, ".mcp.json"), defName, mcpEntry);
5002
+ const geminiDir = path34.join(cwd, ".gemini");
5003
+ if (fs22.existsSync(geminiDir)) {
5004
+ writeMcpEntry(path34.join(geminiDir, "settings.json"), defName, mcpEntry);
4738
5005
  }
4739
5006
  }
4740
5007
  function updateIntegrationsConfig(cwd, defName) {
4741
- const configPath = path33.join(cwd, "harness.config.json");
5008
+ const configPath = path34.join(cwd, "harness.config.json");
4742
5009
  const integConfig = readIntegrationsConfig(configPath);
4743
5010
  if (!integConfig.enabled.includes(defName)) integConfig.enabled.push(defName);
4744
5011
  integConfig.dismissed = integConfig.dismissed.filter((d) => d !== defName);
@@ -4784,7 +5051,7 @@ function printAddSuccess(value) {
4784
5051
  }
4785
5052
  }
4786
5053
  function createAddIntegrationCommand() {
4787
- return new Command34("add").description("Enable an MCP integration").argument("<name>", "Integration name (e.g. perplexity, augment-code)").action(async (name, _opts, cmd) => {
5054
+ return new Command36("add").description("Enable an MCP integration").argument("<name>", "Integration name (e.g. perplexity, augment-code)").action(async (name, _opts, cmd) => {
4788
5055
  const globalOpts = cmd.optsWithGlobals();
4789
5056
  const result = addIntegration(process.cwd(), name);
4790
5057
  if (!result.ok) {
@@ -4798,8 +5065,8 @@ function createAddIntegrationCommand() {
4798
5065
  }
4799
5066
 
4800
5067
  // src/commands/integrations/list.ts
4801
- import { Command as Command35 } from "commander";
4802
- import * as path34 from "path";
5068
+ import { Command as Command37 } from "commander";
5069
+ import * as path35 from "path";
4803
5070
  import chalk5 from "chalk";
4804
5071
  function printTier0Integrations(tier0, mcpServers) {
4805
5072
  console.log(" Tier 0 (zero-config):");
@@ -4823,8 +5090,8 @@ function printTier1Integrations(tier1, mcpServers, dismissed) {
4823
5090
  }
4824
5091
  async function runListIntegrations(globalOpts) {
4825
5092
  const cwd = process.cwd();
4826
- const mcpConfig = readMcpConfig(path34.join(cwd, ".mcp.json"));
4827
- const integConfig = readIntegrationsConfig(path34.join(cwd, "harness.config.json"));
5093
+ const mcpConfig = readMcpConfig(path35.join(cwd, ".mcp.json"));
5094
+ const integConfig = readIntegrationsConfig(path35.join(cwd, "harness.config.json"));
4828
5095
  const mcpServers = mcpConfig.mcpServers ?? {};
4829
5096
  if (globalOpts.json) {
4830
5097
  const entries = INTEGRATION_REGISTRY.map((i) => ({
@@ -4855,13 +5122,14 @@ async function runListIntegrations(globalOpts) {
4855
5122
  process.exit(ExitCode.SUCCESS);
4856
5123
  }
4857
5124
  function createListIntegrationsCommand() {
4858
- return new Command35("list").description("Show all MCP integrations with status").action(async (_opts, cmd) => runListIntegrations(cmd.optsWithGlobals()));
5125
+ return new Command37("list").description("Show all MCP integrations with status").action(async (_opts, cmd) => runListIntegrations(cmd.optsWithGlobals()));
4859
5126
  }
4860
5127
 
4861
5128
  // src/commands/integrations/remove.ts
4862
- import { Command as Command36 } from "commander";
4863
- import * as fs21 from "fs";
4864
- import * as path35 from "path";
5129
+ init_dist();
5130
+ import { Command as Command38 } from "commander";
5131
+ import * as fs23 from "fs";
5132
+ import * as path36 from "path";
4865
5133
  function removeIntegration(cwd, name) {
4866
5134
  const def = INTEGRATION_REGISTRY.find((i) => i.name === name);
4867
5135
  if (!def) {
@@ -4872,21 +5140,21 @@ function removeIntegration(cwd, name) {
4872
5140
  )
4873
5141
  );
4874
5142
  }
4875
- const mcpPath = path35.join(cwd, ".mcp.json");
5143
+ const mcpPath = path36.join(cwd, ".mcp.json");
4876
5144
  removeMcpEntry(mcpPath, def.name);
4877
- const geminiDir = path35.join(cwd, ".gemini");
4878
- if (fs21.existsSync(geminiDir)) {
4879
- const geminiPath = path35.join(geminiDir, "settings.json");
5145
+ const geminiDir = path36.join(cwd, ".gemini");
5146
+ if (fs23.existsSync(geminiDir)) {
5147
+ const geminiPath = path36.join(geminiDir, "settings.json");
4880
5148
  removeMcpEntry(geminiPath, def.name);
4881
5149
  }
4882
- const configPath = path35.join(cwd, "harness.config.json");
5150
+ const configPath = path36.join(cwd, "harness.config.json");
4883
5151
  const integConfig = readIntegrationsConfig(configPath);
4884
5152
  integConfig.enabled = integConfig.enabled.filter((e) => e !== def.name);
4885
5153
  writeIntegrationsConfig(configPath, integConfig);
4886
5154
  return Ok(def.displayName);
4887
5155
  }
4888
5156
  function createRemoveIntegrationCommand() {
4889
- return new Command36("remove").description("Remove an MCP integration").argument("<name>", "Integration name (e.g. perplexity, augment-code)").action(async (name, _opts, cmd) => {
5157
+ return new Command38("remove").description("Remove an MCP integration").argument("<name>", "Integration name (e.g. perplexity, augment-code)").action(async (name, _opts, cmd) => {
4890
5158
  const globalOpts = cmd.optsWithGlobals();
4891
5159
  const cwd = process.cwd();
4892
5160
  const result = removeIntegration(cwd, name);
@@ -4907,8 +5175,9 @@ function createRemoveIntegrationCommand() {
4907
5175
  }
4908
5176
 
4909
5177
  // src/commands/integrations/dismiss.ts
4910
- import { Command as Command37 } from "commander";
4911
- import * as path36 from "path";
5178
+ init_dist();
5179
+ import { Command as Command39 } from "commander";
5180
+ import * as path37 from "path";
4912
5181
  function dismissIntegration(cwd, name) {
4913
5182
  const def = INTEGRATION_REGISTRY.find((i) => i.name === name);
4914
5183
  if (!def) {
@@ -4919,7 +5188,7 @@ function dismissIntegration(cwd, name) {
4919
5188
  )
4920
5189
  );
4921
5190
  }
4922
- const configPath = path36.join(cwd, "harness.config.json");
5191
+ const configPath = path37.join(cwd, "harness.config.json");
4923
5192
  const integConfig = readIntegrationsConfig(configPath);
4924
5193
  if (!integConfig.dismissed.includes(def.name)) {
4925
5194
  integConfig.dismissed.push(def.name);
@@ -4929,7 +5198,7 @@ function dismissIntegration(cwd, name) {
4929
5198
  return Ok(def.displayName);
4930
5199
  }
4931
5200
  function createDismissIntegrationCommand() {
4932
- return new Command37("dismiss").description("Suppress doctor recommendations for an integration").argument("<name>", "Integration name (e.g. perplexity, augment-code)").action(async (name, _opts, cmd) => {
5201
+ return new Command39("dismiss").description("Suppress doctor recommendations for an integration").argument("<name>", "Integration name (e.g. perplexity, augment-code)").action(async (name, _opts, cmd) => {
4933
5202
  const globalOpts = cmd.optsWithGlobals();
4934
5203
  const cwd = process.cwd();
4935
5204
  const result = dismissIntegration(cwd, name);
@@ -4953,7 +5222,7 @@ function createDismissIntegrationCommand() {
4953
5222
 
4954
5223
  // src/commands/integrations/index.ts
4955
5224
  function createIntegrationsCommand() {
4956
- const command = new Command38("integrations").description(
5225
+ const command = new Command40("integrations").description(
4957
5226
  "Manage MCP peer integrations (add, list, remove, dismiss)"
4958
5227
  );
4959
5228
  command.addCommand(createListIntegrationsCommand());
@@ -4964,13 +5233,14 @@ function createIntegrationsCommand() {
4964
5233
  }
4965
5234
 
4966
5235
  // src/commands/learnings/index.ts
4967
- import { Command as Command40 } from "commander";
5236
+ import { Command as Command42 } from "commander";
4968
5237
 
4969
5238
  // src/commands/learnings/prune.ts
4970
- import { Command as Command39 } from "commander";
4971
- import * as path37 from "path";
5239
+ init_dist();
5240
+ import { Command as Command41 } from "commander";
5241
+ import * as path38 from "path";
4972
5242
  async function handlePrune(opts) {
4973
- const projectPath = path37.resolve(opts.path);
5243
+ const projectPath = path38.resolve(opts.path);
4974
5244
  const result = await pruneLearnings(projectPath, opts.stream);
4975
5245
  if (!result.ok) {
4976
5246
  logger.error(result.error.message);
@@ -5009,23 +5279,23 @@ function printPatternProposals(patterns) {
5009
5279
  );
5010
5280
  }
5011
5281
  function createPruneCommand() {
5012
- return new Command39("prune").description(
5282
+ return new Command41("prune").description(
5013
5283
  "Analyze global learnings for patterns, present improvement proposals, and archive old entries"
5014
5284
  ).option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(handlePrune);
5015
5285
  }
5016
5286
 
5017
5287
  // src/commands/learnings/index.ts
5018
5288
  function createLearningsCommand() {
5019
- const command = new Command40("learnings").description("Learnings management commands");
5289
+ const command = new Command42("learnings").description("Learnings management commands");
5020
5290
  command.addCommand(createPruneCommand());
5021
5291
  return command;
5022
5292
  }
5023
5293
 
5024
5294
  // src/commands/linter/index.ts
5025
- import { Command as Command43 } from "commander";
5295
+ import { Command as Command45 } from "commander";
5026
5296
 
5027
5297
  // src/commands/linter/generate.ts
5028
- import { Command as Command41 } from "commander";
5298
+ import { Command as Command43 } from "commander";
5029
5299
  function formatGenerateError(e) {
5030
5300
  switch (e.type) {
5031
5301
  case "parse":
@@ -5069,7 +5339,7 @@ function handleSuccess(result, useJson) {
5069
5339
  Generated ${result.rulesGenerated.length} rules to ${result.outputDir}`);
5070
5340
  }
5071
5341
  function createGenerateCommand2() {
5072
- return new Command41("generate").description("Generate ESLint rules from harness-linter.yml").option("-c, --config <path>", "Path to harness-linter.yml", "./harness-linter.yml").option("-o, --output <dir>", "Override output directory").option("--clean", "Remove existing files before generating").option("--dry-run", "Preview without writing files").option("--json", "Output as JSON").option("--verbose", "Show detailed output").action(async (options) => {
5342
+ return new Command43("generate").description("Generate ESLint rules from harness-linter.yml").option("-c, --config <path>", "Path to harness-linter.yml", "./harness-linter.yml").option("-o, --output <dir>", "Override output directory").option("--clean", "Remove existing files before generating").option("--dry-run", "Preview without writing files").option("--json", "Output as JSON").option("--verbose", "Show detailed output").action(async (options) => {
5073
5343
  try {
5074
5344
  if (options.verbose) logger.info(`Parsing config: ${options.config}`);
5075
5345
  const result = await generate({
@@ -5091,9 +5361,9 @@ function createGenerateCommand2() {
5091
5361
  }
5092
5362
 
5093
5363
  // src/commands/linter/validate.ts
5094
- import { Command as Command42 } from "commander";
5364
+ import { Command as Command44 } from "commander";
5095
5365
  function createValidateCommand() {
5096
- return new Command42("validate").description("Validate harness-linter.yml config").option("-c, --config <path>", "Path to harness-linter.yml", "./harness-linter.yml").option("--json", "Output as JSON").action(async (options) => {
5366
+ return new Command44("validate").description("Validate harness-linter.yml config").option("-c, --config <path>", "Path to harness-linter.yml", "./harness-linter.yml").option("--json", "Output as JSON").action(async (options) => {
5097
5367
  try {
5098
5368
  const result = await validate({ configPath: options.config });
5099
5369
  if (options.json) {
@@ -5112,7 +5382,7 @@ function createValidateCommand() {
5112
5382
 
5113
5383
  // src/commands/linter/index.ts
5114
5384
  function createLinterCommand() {
5115
- const linter = new Command43("linter").description(
5385
+ const linter = new Command45("linter").description(
5116
5386
  "Generate and validate ESLint rules from YAML config"
5117
5387
  );
5118
5388
  linter.addCommand(createGenerateCommand2());
@@ -5121,22 +5391,61 @@ function createLinterCommand() {
5121
5391
  }
5122
5392
 
5123
5393
  // src/commands/mcp.ts
5124
- import { Command as Command44 } from "commander";
5394
+ import { Command as Command46 } from "commander";
5395
+ var VALID_TIERS = /* @__PURE__ */ new Set(["core", "standard", "full"]);
5396
+ function parseTier(value) {
5397
+ const lower = value.toLowerCase();
5398
+ if (!VALID_TIERS.has(lower)) {
5399
+ throw new Error(`Invalid tier "${value}". Expected one of: core, standard, full.`);
5400
+ }
5401
+ return lower;
5402
+ }
5403
+ function parseBudget(value) {
5404
+ const n = Number.parseInt(value, 10);
5405
+ if (!Number.isFinite(n) || n < 0) {
5406
+ throw new Error(`Invalid --budget-tokens "${value}". Expected a non-negative integer.`);
5407
+ }
5408
+ return n;
5409
+ }
5125
5410
  function createMcpCommand() {
5126
- return new Command44("mcp").description("Start the MCP (Model Context Protocol) server on stdio").option("--tools <tools...>", "Only register the specified tools (used by Cursor integration)").action(async (opts) => {
5127
- const { startServer: startServer2 } = await import("./mcp-LCHC4NZ5.js");
5128
- await startServer2(opts.tools);
5411
+ return new Command46("mcp").description("Start the MCP (Model Context Protocol) server on stdio").option("--tools <tools...>", "Only register the specified tools (used by Cursor integration)").option(
5412
+ "--tier <core|standard|full>",
5413
+ "Load a preset tool tier instead of all tools",
5414
+ parseTier
5415
+ ).option(
5416
+ "--budget-tokens <n>",
5417
+ "Auto-select tier to fit this baseline token budget",
5418
+ parseBudget
5419
+ ).action(async (opts) => {
5420
+ const [{ startServer: startServer2, getToolDefinitions: getToolDefinitions2 }, { selectTier }] = await Promise.all([
5421
+ import("./mcp-2553PNUC.js"),
5422
+ import("./tool-tiers-7QGZ3FKY.js")
5423
+ ]);
5424
+ if (opts.tools && opts.tools.length > 0) {
5425
+ await startServer2(opts.tools);
5426
+ return;
5427
+ }
5428
+ if (opts.tier || opts.budgetTokens !== void 0) {
5429
+ const definitions = getToolDefinitions2();
5430
+ const selection = selectTier(definitions, {
5431
+ ...opts.tier ? { overrideTier: opts.tier } : {},
5432
+ ...opts.budgetTokens !== void 0 ? { tokenBudget: opts.budgetTokens } : {}
5433
+ });
5434
+ await startServer2(selection.filter);
5435
+ return;
5436
+ }
5437
+ await startServer2();
5129
5438
  });
5130
5439
  }
5131
5440
 
5132
5441
  // src/commands/orchestrator.ts
5133
- import { Command as Command45 } from "commander";
5134
- import * as path38 from "path";
5442
+ import { Command as Command47 } from "commander";
5443
+ import * as path39 from "path";
5135
5444
  import { Orchestrator, WorkflowLoader, launchTUI } from "@harness-engineering/orchestrator";
5136
5445
  function createOrchestratorCommand() {
5137
- const orchestrator = new Command45("orchestrator");
5138
- orchestrator.command("run").description("Run the orchestrator daemon").option("-w, --workflow <path>", "Path to WORKFLOW.md", "WORKFLOW.md").action(async (opts) => {
5139
- const workflowPath = path38.resolve(process.cwd(), opts.workflow);
5446
+ const orchestrator = new Command47("orchestrator");
5447
+ orchestrator.command("run").description("Run the orchestrator daemon").option("-w, --workflow <path>", "Path to WORKFLOW.md", "WORKFLOW.md").option("--headless", "Run without TUI (server-only mode for use with web dashboard)").action(async (opts) => {
5448
+ const workflowPath = path39.resolve(process.cwd(), opts.workflow);
5140
5449
  const loader = new WorkflowLoader();
5141
5450
  const result = await loader.loadWorkflow(workflowPath);
5142
5451
  if (!result.ok) {
@@ -5152,23 +5461,32 @@ function createOrchestratorCommand() {
5152
5461
  process.on("SIGINT", shutdown);
5153
5462
  process.on("SIGTERM", shutdown);
5154
5463
  daemon.start();
5155
- const { waitUntilExit } = launchTUI(daemon);
5156
- await waitUntilExit();
5464
+ if (opts.headless) {
5465
+ logger.info(
5466
+ "Orchestrator running in headless mode (no TUI). Use the web dashboard or Ctrl+C to stop."
5467
+ );
5468
+ await new Promise(() => {
5469
+ });
5470
+ } else {
5471
+ const { waitUntilExit } = launchTUI(daemon);
5472
+ await waitUntilExit();
5473
+ }
5157
5474
  process.exit(ExitCode.SUCCESS);
5158
5475
  });
5159
5476
  return orchestrator;
5160
5477
  }
5161
5478
 
5162
5479
  // src/commands/perf.ts
5163
- import { Command as Command46 } from "commander";
5164
- import * as path39 from "path";
5480
+ init_dist();
5481
+ import { Command as Command48 } from "commander";
5482
+ import * as path40 from "path";
5165
5483
  function registerBenchCommand(perf) {
5166
- perf.command("bench [glob]").description("Run benchmarks via vitest bench").action(async (glob, _opts, cmd) => {
5484
+ perf.command("bench [glob]").description("Run benchmarks via vitest bench").action(async (glob2, _opts, cmd) => {
5167
5485
  const globalOpts = cmd.optsWithGlobals();
5168
5486
  const cwd = process.cwd();
5169
- const { BenchmarkRunner } = await import("./dist-7EBSGAHX.js");
5487
+ const { BenchmarkRunner } = await import("./dist-WCSJHQPK.js");
5170
5488
  const runner = new BenchmarkRunner();
5171
- const benchFiles = runner.discover(cwd, glob);
5489
+ const benchFiles = runner.discover(cwd, glob2);
5172
5490
  if (benchFiles.length === 0) {
5173
5491
  if (globalOpts.json) {
5174
5492
  console.log(JSON.stringify({ benchFiles: [], message: "No .bench.ts files found" }));
@@ -5178,7 +5496,7 @@ function registerBenchCommand(perf) {
5178
5496
  return;
5179
5497
  }
5180
5498
  logBenchDiscovery(globalOpts.json, benchFiles);
5181
- const result = await runner.run(glob ? { cwd, glob } : { cwd });
5499
+ const result = await runner.run(glob2 ? { cwd, glob: glob2 } : { cwd });
5182
5500
  outputBenchResults(globalOpts.json, result);
5183
5501
  });
5184
5502
  }
@@ -5230,7 +5548,7 @@ async function runBaselinesShow(globalOpts) {
5230
5548
  }
5231
5549
  async function runBaselinesUpdate(globalOpts) {
5232
5550
  const cwd = process.cwd();
5233
- const { BenchmarkRunner } = await import("./dist-7EBSGAHX.js");
5551
+ const { BenchmarkRunner } = await import("./dist-WCSJHQPK.js");
5234
5552
  const runner = new BenchmarkRunner();
5235
5553
  const manager = new BaselineManager(cwd);
5236
5554
  logger.info("Running benchmarks to update baselines...");
@@ -5271,9 +5589,9 @@ function registerReportCommand(perf) {
5271
5589
  perf.command("report").description("Full performance report with metrics, trends, and hotspots").action(async (_opts, cmd) => {
5272
5590
  const globalOpts = cmd.optsWithGlobals();
5273
5591
  const cwd = process.cwd();
5274
- const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-7EBSGAHX.js");
5592
+ const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-WCSJHQPK.js");
5275
5593
  const analyzer = new EntropyAnalyzer2({
5276
- rootDir: path39.resolve(cwd),
5594
+ rootDir: path40.resolve(cwd),
5277
5595
  analyze: { complexity: true, coupling: true }
5278
5596
  });
5279
5597
  const result = await analyzer.analyze();
@@ -5332,7 +5650,7 @@ function registerCriticalPathsCommand(perf) {
5332
5650
  });
5333
5651
  }
5334
5652
  function createPerfCommand() {
5335
- const perf = new Command46("perf").description("Performance benchmark and baseline management");
5653
+ const perf = new Command48("perf").description("Performance benchmark and baseline management");
5336
5654
  registerBenchCommand(perf);
5337
5655
  registerBaselinesCommands(perf);
5338
5656
  registerReportCommand(perf);
@@ -5341,10 +5659,10 @@ function createPerfCommand() {
5341
5659
  }
5342
5660
 
5343
5661
  // src/commands/persona/index.ts
5344
- import { Command as Command49 } from "commander";
5662
+ import { Command as Command51 } from "commander";
5345
5663
 
5346
5664
  // src/commands/persona/list.ts
5347
- import { Command as Command47 } from "commander";
5665
+ import { Command as Command49 } from "commander";
5348
5666
  function printPersonaList(personas) {
5349
5667
  if (personas.length === 0) {
5350
5668
  logger.info("No personas found.");
@@ -5358,7 +5676,7 @@ function printPersonaList(personas) {
5358
5676
  }
5359
5677
  }
5360
5678
  function createListCommand2() {
5361
- return new Command47("list").description("List available agent personas").action(async (_opts, cmd) => {
5679
+ return new Command49("list").description("List available agent personas").action(async (_opts, cmd) => {
5362
5680
  const globalOpts = cmd.optsWithGlobals();
5363
5681
  const personasDir = resolvePersonasDir();
5364
5682
  const result = listPersonas(personasDir);
@@ -5378,51 +5696,51 @@ function createListCommand2() {
5378
5696
  }
5379
5697
 
5380
5698
  // src/commands/persona/generate.ts
5381
- import { Command as Command48 } from "commander";
5382
- import * as fs22 from "fs";
5383
- import * as path40 from "path";
5699
+ import { Command as Command50 } from "commander";
5700
+ import * as fs24 from "fs";
5701
+ import * as path41 from "path";
5384
5702
  function generatePersonaArtifacts(persona, outputDir, only) {
5385
5703
  const slug = toKebabCase(persona.name);
5386
5704
  const generated = [];
5387
5705
  if (!only || only === "runtime") {
5388
5706
  const result = generateRuntime(persona);
5389
5707
  if (result.ok) {
5390
- const outPath = path40.join(outputDir, `${slug}.runtime.json`);
5391
- fs22.mkdirSync(path40.dirname(outPath), { recursive: true });
5392
- fs22.writeFileSync(outPath, result.value);
5708
+ const outPath = path41.join(outputDir, `${slug}.runtime.json`);
5709
+ fs24.mkdirSync(path41.dirname(outPath), { recursive: true });
5710
+ fs24.writeFileSync(outPath, result.value);
5393
5711
  generated.push(outPath);
5394
5712
  }
5395
5713
  }
5396
5714
  if (!only || only === "agents-md") {
5397
5715
  const result = generateAgentsMd(persona);
5398
5716
  if (result.ok) {
5399
- const outPath = path40.join(outputDir, `${slug}.agents.md`);
5400
- fs22.writeFileSync(outPath, result.value);
5717
+ const outPath = path41.join(outputDir, `${slug}.agents.md`);
5718
+ fs24.writeFileSync(outPath, result.value);
5401
5719
  generated.push(outPath);
5402
5720
  }
5403
5721
  }
5404
5722
  if (!only || only === "ci") {
5405
5723
  const result = generateCIWorkflow(persona, "github");
5406
5724
  if (result.ok) {
5407
- const outPath = path40.join(outputDir, ".github", "workflows", `${slug}.yml`);
5408
- fs22.mkdirSync(path40.dirname(outPath), { recursive: true });
5409
- fs22.writeFileSync(outPath, result.value);
5725
+ const outPath = path41.join(outputDir, ".github", "workflows", `${slug}.yml`);
5726
+ fs24.mkdirSync(path41.dirname(outPath), { recursive: true });
5727
+ fs24.writeFileSync(outPath, result.value);
5410
5728
  generated.push(outPath);
5411
5729
  }
5412
5730
  }
5413
5731
  return generated;
5414
5732
  }
5415
5733
  function createGenerateCommand3() {
5416
- return new Command48("generate").description("Generate artifacts from a persona config").argument("<name>", "Persona name (e.g., architecture-enforcer)").option("--output-dir <dir>", "Output directory", ".").option("--only <type>", "Generate only: ci, agents-md, runtime").action(async (name, opts, cmd) => {
5734
+ return new Command50("generate").description("Generate artifacts from a persona config").argument("<name>", "Persona name (e.g., architecture-enforcer)").option("--output-dir <dir>", "Output directory", ".").option("--only <type>", "Generate only: ci, agents-md, runtime").action(async (name, opts, cmd) => {
5417
5735
  const globalOpts = cmd.optsWithGlobals();
5418
- const personaResult = loadPersona(path40.join(resolvePersonasDir(), `${name}.yaml`));
5736
+ const personaResult = loadPersona(path41.join(resolvePersonasDir(), `${name}.yaml`));
5419
5737
  if (!personaResult.ok) {
5420
5738
  logger.error(personaResult.error.message);
5421
5739
  process.exit(ExitCode.ERROR);
5422
5740
  }
5423
5741
  const generated = generatePersonaArtifacts(
5424
5742
  personaResult.value,
5425
- path40.resolve(opts.outputDir),
5743
+ path41.resolve(opts.outputDir),
5426
5744
  opts.only
5427
5745
  );
5428
5746
  if (!globalOpts.quiet) {
@@ -5435,14 +5753,15 @@ function createGenerateCommand3() {
5435
5753
 
5436
5754
  // src/commands/persona/index.ts
5437
5755
  function createPersonaCommand() {
5438
- const command = new Command49("persona").description("Agent persona management commands");
5756
+ const command = new Command51("persona").description("Agent persona management commands");
5439
5757
  command.addCommand(createListCommand2());
5440
5758
  command.addCommand(createGenerateCommand3());
5441
5759
  return command;
5442
5760
  }
5443
5761
 
5444
5762
  // src/commands/predict.ts
5445
- import { Command as Command50 } from "commander";
5763
+ init_dist();
5764
+ import { Command as Command52 } from "commander";
5446
5765
  import chalk6 from "chalk";
5447
5766
  var CATEGORY_ORDER = [
5448
5767
  "circular-deps",
@@ -5548,7 +5867,7 @@ function handlePredictError(err, mode) {
5548
5867
  process.exit(ExitCode.ERROR);
5549
5868
  }
5550
5869
  function createPredictCommand() {
5551
- const command = new Command50("predict").description("Predict which architectural constraints will break and when").option("--category <name>", "Filter to a single metric category").option("--no-roadmap", "Baseline only \u2014 skip roadmap spec impact").option("--horizon <weeks>", "Forecast horizon in weeks (default: 12)", "12").action(async (opts, cmd) => {
5870
+ const command = new Command52("predict").description("Predict which architectural constraints will break and when").option("--category <name>", "Filter to a single metric category").option("--no-roadmap", "Baseline only \u2014 skip roadmap spec impact").option("--horizon <weeks>", "Forecast horizon in weeks (default: 12)", "12").action(async (opts, cmd) => {
5552
5871
  const globalOpts = cmd.optsWithGlobals();
5553
5872
  const mode = globalOpts.json ? OutputMode.JSON : OutputMode.TEXT;
5554
5873
  try {
@@ -5570,8 +5889,137 @@ function createPredictCommand() {
5570
5889
  return command;
5571
5890
  }
5572
5891
 
5892
+ // src/commands/publish-analyses.ts
5893
+ import * as fs25 from "fs";
5894
+ import * as path42 from "path";
5895
+ import { Command as Command53 } from "commander";
5896
+ init_dist();
5897
+ import {
5898
+ renderAnalysisComment,
5899
+ loadPublishedIndex,
5900
+ savePublishedIndex
5901
+ } from "@harness-engineering/orchestrator";
5902
+ function bootstrapTrackerCommand(opts, verb) {
5903
+ const projectPath = path42.resolve(opts.dir);
5904
+ const trackerConfig = loadTrackerSyncConfig(projectPath);
5905
+ if (!trackerConfig) {
5906
+ logger.error(`No tracker config found in harness.config.json. Cannot ${verb}.`);
5907
+ return null;
5908
+ }
5909
+ if (trackerConfig.kind !== "github") {
5910
+ logger.error(
5911
+ `${verb} currently only supports 'github' tracker kind. Found: ${String(trackerConfig.kind)}`
5912
+ );
5913
+ return null;
5914
+ }
5915
+ const projectEnvPath = path42.join(projectPath, ".env");
5916
+ if (fs25.existsSync(projectEnvPath) && !process.env.GITHUB_TOKEN) {
5917
+ __require("dotenv").config({ path: projectEnvPath });
5918
+ }
5919
+ const token = process.env.GITHUB_TOKEN;
5920
+ if (!token) {
5921
+ logger.error("No GITHUB_TOKEN environment variable found.");
5922
+ return null;
5923
+ }
5924
+ return { token, projectPath, trackerConfig };
5925
+ }
5926
+ function buildNameToExternalIdMap(projectPath) {
5927
+ const roadmapFile = path42.join(projectPath, "docs", "roadmap.md");
5928
+ if (!fs25.existsSync(roadmapFile)) {
5929
+ logger.error("No docs/roadmap.md found. Cannot map issue hashes to external IDs.");
5930
+ return null;
5931
+ }
5932
+ const { parseRoadmap } = (init_dist(), __toCommonJS(dist_exports));
5933
+ const roadmapParsed = parseRoadmap(fs25.readFileSync(roadmapFile, "utf-8"));
5934
+ if (!roadmapParsed.ok) {
5935
+ logger.error("Failed to parse docs/roadmap.md");
5936
+ return null;
5937
+ }
5938
+ const map = /* @__PURE__ */ new Map();
5939
+ for (const milestone of roadmapParsed.value.milestones) {
5940
+ for (const feature of milestone.features) {
5941
+ if (feature.externalId) {
5942
+ map.set(feature.name.toLowerCase(), feature.externalId);
5943
+ }
5944
+ }
5945
+ }
5946
+ return map;
5947
+ }
5948
+ function resolveExternalId(record, nameToExternalId) {
5949
+ if (record.externalId) return record.externalId;
5950
+ for (const [featName, extId] of nameToExternalId) {
5951
+ const prefix = featName.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 20);
5952
+ if (record.identifier.startsWith(prefix)) return extId;
5953
+ }
5954
+ return null;
5955
+ }
5956
+ async function publishUnpublishedAnalyses(analyses, adapter, nameToExternalId, publishedIndex) {
5957
+ let publishedCount = 0;
5958
+ for (const record of analyses) {
5959
+ if (publishedIndex[record.issueId]) continue;
5960
+ const externalId = resolveExternalId(record, nameToExternalId);
5961
+ if (!externalId) {
5962
+ logger.dim(`Skipping ${record.identifier}: could not resolve externalId from roadmap`);
5963
+ continue;
5964
+ }
5965
+ logger.info(`Publishing analysis for ${record.identifier} to ${externalId}...`);
5966
+ const result = await adapter.addComment(externalId, renderAnalysisComment(record));
5967
+ if (result.ok) {
5968
+ publishedIndex[record.issueId] = (/* @__PURE__ */ new Date()).toISOString();
5969
+ publishedCount++;
5970
+ } else {
5971
+ logger.error(`Failed to publish ${record.identifier}: ${result.error.message}`);
5972
+ }
5973
+ }
5974
+ return publishedCount;
5975
+ }
5976
+ async function runPublishAnalyses(opts) {
5977
+ const bootstrap = bootstrapTrackerCommand(opts, "publish");
5978
+ if (!bootstrap) return process.exit(1);
5979
+ const { token, projectPath, trackerConfig } = bootstrap;
5980
+ const nameToExternalId = buildNameToExternalIdMap(projectPath);
5981
+ if (!nameToExternalId) return process.exit(1);
5982
+ const { AnalysisArchive } = await import("@harness-engineering/orchestrator");
5983
+ const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-WCSJHQPK.js");
5984
+ const archive = new AnalysisArchive(path42.join(projectPath, ".harness", "analyses"));
5985
+ const analyses = await archive.list();
5986
+ if (analyses.length === 0) {
5987
+ logger.info("No analyses found to publish.");
5988
+ return;
5989
+ }
5990
+ const publishedIndex = loadPublishedIndex(projectPath);
5991
+ const adapter = new GitHubIssuesSyncAdapter2({ token, config: trackerConfig });
5992
+ const publishedCount = await publishUnpublishedAnalyses(
5993
+ analyses,
5994
+ adapter,
5995
+ nameToExternalId,
5996
+ publishedIndex
5997
+ );
5998
+ if (publishedCount > 0) {
5999
+ savePublishedIndex(projectPath, publishedIndex);
6000
+ logger.success(`Successfully published ${publishedCount} analyses.`);
6001
+ } else {
6002
+ logger.info("All analyses are already up to date on external tracker.");
6003
+ }
6004
+ }
6005
+ function createPublishAnalysesCommand() {
6006
+ const command = new Command53("publish-analyses").description(
6007
+ "Publishes locally generated intelligence analyses to the external issue tracker (e.g., GitHub)"
6008
+ ).option("-d, --dir <path>", "Workspace directory", process.cwd()).action(async (opts) => {
6009
+ try {
6010
+ await runPublishAnalyses(opts);
6011
+ } catch (err) {
6012
+ logger.error(
6013
+ `Error publishing analyses: ${err instanceof Error ? err.message : String(err)}`
6014
+ );
6015
+ process.exit(1);
6016
+ }
6017
+ });
6018
+ return command;
6019
+ }
6020
+
5573
6021
  // src/commands/recommend.ts
5574
- import { Command as Command51 } from "commander";
6022
+ import { Command as Command54 } from "commander";
5575
6023
  import chalk7 from "chalk";
5576
6024
  async function resolveSnapshot(cwd, noCache) {
5577
6025
  if (!noCache) {
@@ -5635,7 +6083,7 @@ function printRecommendations(result) {
5635
6083
  console.log("");
5636
6084
  }
5637
6085
  function createRecommendCommand() {
5638
- const command = new Command51("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) => {
6086
+ const command = new Command54("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) => {
5639
6087
  const globalOpts = cmd.optsWithGlobals();
5640
6088
  const mode = globalOpts.json ? OutputMode.JSON : OutputMode.TEXT;
5641
6089
  try {
@@ -5671,9 +6119,10 @@ function createRecommendCommand() {
5671
6119
  }
5672
6120
 
5673
6121
  // src/commands/scan-config.ts
5674
- import { Command as Command52 } from "commander";
5675
- import { existsSync as existsSync21, readFileSync as readFileSync11, writeFileSync as writeFileSync12 } from "fs";
5676
- import { join as join31, relative as relative2 } from "path";
6122
+ init_dist();
6123
+ import { Command as Command55 } from "commander";
6124
+ import { existsSync as existsSync22, readFileSync as readFileSync14, writeFileSync as writeFileSync12 } from "fs";
6125
+ import { join as join30, relative as relative2 } from "path";
5677
6126
  var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".gemini/settings.json", "skill.yaml"];
5678
6127
  function stripHighSeverityPatterns(content, injectionFindings) {
5679
6128
  const highLines = /* @__PURE__ */ new Set();
@@ -5706,10 +6155,10 @@ function applyFix(filePath, targetDir, content, injectionFindings) {
5706
6155
  }
5707
6156
  }
5708
6157
  function scanSingleFile(filePath, targetDir, scanner, options) {
5709
- if (!existsSync21(filePath)) return null;
6158
+ if (!existsSync22(filePath)) return null;
5710
6159
  let content;
5711
6160
  try {
5712
- content = readFileSync11(filePath, "utf8");
6161
+ content = readFileSync14(filePath, "utf8");
5713
6162
  } catch {
5714
6163
  return null;
5715
6164
  }
@@ -5730,7 +6179,7 @@ async function runScanConfig(targetDir, options) {
5730
6179
  const scanner = new SecurityScanner(parseSecurityConfig({}));
5731
6180
  const results = [];
5732
6181
  for (const configFile of CONFIG_FILES) {
5733
- const result = scanSingleFile(join31(targetDir, configFile), targetDir, scanner, options);
6182
+ const result = scanSingleFile(join30(targetDir, configFile), targetDir, scanner, options);
5734
6183
  if (result) results.push(result);
5735
6184
  }
5736
6185
  return { exitCode: computeScanExitCode(results), results };
@@ -5760,7 +6209,7 @@ function formatTextOutput(result) {
5760
6209
  }
5761
6210
  }
5762
6211
  function createScanConfigCommand() {
5763
- const command = new Command52("scan-config").description(
6212
+ const command = new Command55("scan-config").description(
5764
6213
  "Scan CLAUDE.md, AGENTS.md, .gemini/settings.json, and skill.yaml for prompt injection patterns"
5765
6214
  ).option("--path <dir>", "Target directory to scan (default: cwd)").option("--fix", "Strip high-severity patterns from files in-place").action(async (opts, cmd) => {
5766
6215
  const globalOpts = cmd.optsWithGlobals();
@@ -5777,24 +6226,24 @@ function createScanConfigCommand() {
5777
6226
  }
5778
6227
 
5779
6228
  // src/commands/setup.ts
5780
- import { Command as Command53 } from "commander";
5781
- import * as fs24 from "fs";
6229
+ import { Command as Command56 } from "commander";
6230
+ import * as fs28 from "fs";
5782
6231
  import * as os7 from "os";
5783
- import * as path42 from "path";
5784
- import chalk8 from "chalk";
6232
+ import * as path45 from "path";
6233
+ import chalk9 from "chalk";
5785
6234
 
5786
6235
  // src/utils/first-run.ts
5787
- import * as fs23 from "fs";
6236
+ import * as fs26 from "fs";
5788
6237
  import * as os6 from "os";
5789
- import * as path41 from "path";
5790
- var HARNESS_DIR = path41.join(os6.homedir(), ".harness");
5791
- var MARKER_FILE = path41.join(HARNESS_DIR, ".setup-complete");
6238
+ import * as path43 from "path";
6239
+ var HARNESS_DIR = path43.join(os6.homedir(), ".harness");
6240
+ var MARKER_FILE = path43.join(HARNESS_DIR, ".setup-complete");
5792
6241
  function isFirstRun() {
5793
- return !fs23.existsSync(MARKER_FILE);
6242
+ return !fs26.existsSync(MARKER_FILE);
5794
6243
  }
5795
6244
  function markSetupComplete() {
5796
- fs23.mkdirSync(HARNESS_DIR, { recursive: true });
5797
- fs23.writeFileSync(MARKER_FILE, "", "utf-8");
6245
+ fs26.mkdirSync(HARNESS_DIR, { recursive: true });
6246
+ fs26.writeFileSync(MARKER_FILE, "", "utf-8");
5798
6247
  }
5799
6248
  function printFirstRunWelcome() {
5800
6249
  try {
@@ -5806,6 +6255,119 @@ function printFirstRunWelcome() {
5806
6255
  }
5807
6256
  }
5808
6257
 
6258
+ // src/commands/telemetry-wizard.ts
6259
+ import * as fs27 from "fs";
6260
+ import * as path44 from "path";
6261
+ import readline from "readline";
6262
+ import chalk8 from "chalk";
6263
+ function prompt(question) {
6264
+ const rl = readline.createInterface({
6265
+ input: process.stdin,
6266
+ output: process.stdout
6267
+ });
6268
+ return new Promise((resolve35) => {
6269
+ rl.question(question, (answer) => {
6270
+ rl.close();
6271
+ resolve35(answer.trim().toLowerCase());
6272
+ });
6273
+ });
6274
+ }
6275
+ function isNo(answer) {
6276
+ return answer === "n" || answer === "no";
6277
+ }
6278
+ function isTelemetryConfigured(cwd) {
6279
+ const configPath = path44.join(cwd, "harness.config.json");
6280
+ try {
6281
+ const raw = JSON.parse(fs27.readFileSync(configPath, "utf-8"));
6282
+ return raw.telemetry !== void 0 && raw.adoption !== void 0;
6283
+ } catch {
6284
+ return false;
6285
+ }
6286
+ }
6287
+ function writeTelemetryConfig(cwd, result) {
6288
+ const configPath = path44.join(cwd, "harness.config.json");
6289
+ try {
6290
+ const raw = JSON.parse(fs27.readFileSync(configPath, "utf-8"));
6291
+ raw.telemetry = { enabled: result.telemetryEnabled };
6292
+ raw.adoption = { enabled: result.adoptionEnabled };
6293
+ fs27.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
6294
+ } catch (err) {
6295
+ console.warn(
6296
+ `\u26A0 Could not update ${configPath}: ${err instanceof Error ? err.message : String(err)}`
6297
+ );
6298
+ }
6299
+ const hasIdentity = result.identity.project || result.identity.team || result.identity.alias;
6300
+ if (hasIdentity) {
6301
+ const harnessDir = path44.join(cwd, ".harness");
6302
+ fs27.mkdirSync(harnessDir, { recursive: true });
6303
+ const telemetryFile = path44.join(harnessDir, "telemetry.json");
6304
+ fs27.writeFileSync(telemetryFile, JSON.stringify({ identity: result.identity }, null, 2) + "\n");
6305
+ }
6306
+ }
6307
+ async function promptIdentity() {
6308
+ console.log(
6309
+ ` ${chalk8.cyan("Identity")} (optional) \u2014 tag telemetry with project/team/alias for filtering.`
6310
+ );
6311
+ console.log(" Leave blank to keep anonymous.");
6312
+ const answer = await prompt(" Set identity fields? (y/N) ");
6313
+ const identity = {};
6314
+ if (answer === "y" || answer === "yes") {
6315
+ const projectName = await prompt(" Project name: ");
6316
+ if (projectName) identity.project = projectName;
6317
+ const teamName = await prompt(" Team name: ");
6318
+ if (teamName) identity.team = teamName;
6319
+ const alias = await prompt(" Alias: ");
6320
+ if (alias) identity.alias = alias;
6321
+ }
6322
+ return identity;
6323
+ }
6324
+ async function runTelemetryWizard() {
6325
+ if (!process.stdin.isTTY) return null;
6326
+ console.log("");
6327
+ console.log(` ${chalk8.bold("Telemetry Configuration")}`);
6328
+ console.log("");
6329
+ console.log(" Harness can collect anonymous usage data to help improve the tool.");
6330
+ console.log(" You can change these settings anytime in harness.config.json.");
6331
+ console.log("");
6332
+ console.log(
6333
+ ` ${chalk8.cyan("Anonymous telemetry")} \u2014 error rates, command usage, performance metrics.`
6334
+ );
6335
+ console.log(" No code, file contents, or personally identifiable info is sent.");
6336
+ const telemetryAnswer = await prompt(" Enable anonymous telemetry? (Y/n) ");
6337
+ const telemetryEnabled = !isNo(telemetryAnswer);
6338
+ console.log("");
6339
+ console.log(
6340
+ ` ${chalk8.cyan("Adoption tracking")} \u2014 which skills are used, how often, and success rates.`
6341
+ );
6342
+ console.log(" Stored locally in .harness/adoption/. Never sent externally.");
6343
+ const adoptionAnswer = await prompt(" Enable local adoption tracking? (Y/n) ");
6344
+ const adoptionEnabled = !isNo(adoptionAnswer);
6345
+ console.log("");
6346
+ const identity = await promptIdentity();
6347
+ return { telemetryEnabled, adoptionEnabled, identity };
6348
+ }
6349
+ async function ensureTelemetryConfigured(cwd) {
6350
+ const configPath = path44.join(cwd, "harness.config.json");
6351
+ if (!fs27.existsSync(configPath)) {
6352
+ return { status: "warn", message: "Not a harness project \u2014 skipped telemetry configuration" };
6353
+ }
6354
+ if (isTelemetryConfigured(cwd)) {
6355
+ return { status: "pass", message: "Telemetry already configured" };
6356
+ }
6357
+ const result = await runTelemetryWizard();
6358
+ if (!result) {
6359
+ return { status: "warn", message: "Non-interactive mode \u2014 skipped telemetry configuration" };
6360
+ }
6361
+ writeTelemetryConfig(cwd, result);
6362
+ const parts = [];
6363
+ parts.push(`telemetry ${result.telemetryEnabled ? "enabled" : "disabled"}`);
6364
+ parts.push(`adoption ${result.adoptionEnabled ? "enabled" : "disabled"}`);
6365
+ if (result.identity.project || result.identity.team || result.identity.alias) {
6366
+ parts.push("identity set");
6367
+ }
6368
+ return { status: "pass", message: `Telemetry configured: ${parts.join(", ")}` };
6369
+ }
6370
+
5809
6371
  // src/commands/setup.ts
5810
6372
  function checkNodeVersion3() {
5811
6373
  const result = checkNodeVersion();
@@ -5832,7 +6394,7 @@ function runSlashCommandGeneration() {
5832
6394
  }
5833
6395
  }
5834
6396
  function detectClient(dirName) {
5835
- return fs24.existsSync(path42.join(os7.homedir(), dirName));
6397
+ return fs28.existsSync(path45.join(os7.homedir(), dirName));
5836
6398
  }
5837
6399
  async function runMcpSetup(cwd) {
5838
6400
  const results = [];
@@ -5869,12 +6431,12 @@ async function runMcpSetup(cwd) {
5869
6431
  return results;
5870
6432
  }
5871
6433
  function formatStep(result) {
5872
- const icon = result.status === "pass" ? chalk8.green("\u2713") : result.status === "warn" ? chalk8.yellow("\u26A0") : chalk8.red("\u2717");
6434
+ const icon = result.status === "pass" ? chalk9.green("\u2713") : result.status === "warn" ? chalk9.yellow("\u26A0") : chalk9.red("\u2717");
5873
6435
  return ` ${icon} ${result.message}`;
5874
6436
  }
5875
6437
  function configureTier0Integrations(cwd) {
5876
6438
  try {
5877
- const mcpPath = path42.join(cwd, ".mcp.json");
6439
+ const mcpPath = path45.join(cwd, ".mcp.json");
5878
6440
  const config = readMcpConfig(mcpPath);
5879
6441
  const tier0 = INTEGRATION_REGISTRY.filter((i) => i.tier === 0);
5880
6442
  const added = [];
@@ -5883,9 +6445,9 @@ function configureTier0Integrations(cwd) {
5883
6445
  writeMcpEntry(mcpPath, integration.name, integration.mcpConfig);
5884
6446
  added.push(integration.displayName);
5885
6447
  }
5886
- const geminiDir = path42.join(cwd, ".gemini");
5887
- if (fs24.existsSync(geminiDir)) {
5888
- const geminiPath = path42.join(geminiDir, "settings.json");
6448
+ const geminiDir = path45.join(cwd, ".gemini");
6449
+ if (fs28.existsSync(geminiDir)) {
6450
+ const geminiPath = path45.join(geminiDir, "settings.json");
5889
6451
  const geminiConfig = readMcpConfig(geminiPath);
5890
6452
  for (const integration of tier0) {
5891
6453
  if (!geminiConfig.mcpServers[integration.name]) {
@@ -5906,14 +6468,14 @@ function configureTier0Integrations(cwd) {
5906
6468
  }
5907
6469
  }
5908
6470
  function ensureHooks(cwd) {
5909
- const configPath = path42.join(cwd, "harness.config.json");
5910
- if (!fs24.existsSync(configPath)) {
6471
+ const configPath = path45.join(cwd, "harness.config.json");
6472
+ if (!fs28.existsSync(configPath)) {
5911
6473
  return { status: "warn", message: "Not a harness project \u2014 skipped hook installation" };
5912
6474
  }
5913
6475
  let profile = "standard";
5914
- const profilePath = path42.join(cwd, ".harness", "hooks", "profile.json");
6476
+ const profilePath = path45.join(cwd, ".harness", "hooks", "profile.json");
5915
6477
  try {
5916
- const data = JSON.parse(fs24.readFileSync(profilePath, "utf-8"));
6478
+ const data = JSON.parse(fs28.readFileSync(profilePath, "utf-8"));
5917
6479
  if (data.profile && ["minimal", "standard", "strict"].includes(data.profile)) {
5918
6480
  profile = data.profile;
5919
6481
  }
@@ -5932,7 +6494,7 @@ function ensureHooks(cwd) {
5932
6494
  }
5933
6495
  async function runInitialGraphScan(cwd) {
5934
6496
  try {
5935
- const { runScan: runScan2 } = await import("./scan-U67OKDRS.js");
6497
+ const { runScan: runScan2 } = await import("./scan-MPJ6JHUY.js");
5936
6498
  const result = await runScan2(cwd);
5937
6499
  return {
5938
6500
  status: "pass",
@@ -5958,6 +6520,8 @@ async function runSetup(cwd) {
5958
6520
  steps.push(tier0Result);
5959
6521
  const hooksResult = ensureHooks(cwd);
5960
6522
  steps.push(hooksResult);
6523
+ const telemetryResult = await ensureTelemetryConfigured(cwd);
6524
+ steps.push(telemetryResult);
5961
6525
  const graphResult = await runInitialGraphScan(cwd);
5962
6526
  steps.push(graphResult);
5963
6527
  const success = steps.every((s) => s.status !== "fail");
@@ -5967,10 +6531,10 @@ async function runSetup(cwd) {
5967
6531
  return { steps, success };
5968
6532
  }
5969
6533
  function createSetupCommand() {
5970
- return new Command53("setup").description("Configure harness environment: slash commands, MCP, and more").action(async () => {
6534
+ return new Command56("setup").description("Configure harness environment: slash commands, MCP, and more").action(async () => {
5971
6535
  const cwd = process.cwd();
5972
6536
  console.log("");
5973
- console.log(` ${chalk8.bold("harness setup")}`);
6537
+ console.log(` ${chalk9.bold("harness setup")}`);
5974
6538
  console.log("");
5975
6539
  const { steps, success } = await runSetup(cwd);
5976
6540
  for (const step of steps) {
@@ -5989,15 +6553,16 @@ function createSetupCommand() {
5989
6553
  }
5990
6554
 
5991
6555
  // src/commands/share.ts
5992
- import { Command as Command54 } from "commander";
5993
- import * as fs25 from "fs";
5994
- import * as path43 from "path";
6556
+ init_dist();
6557
+ import { Command as Command57 } from "commander";
6558
+ import * as fs29 from "fs";
6559
+ import * as path46 from "path";
5995
6560
  import { parse as parseYaml } from "yaml";
5996
6561
  var MANIFEST_FILENAME = "constraints.yaml";
5997
6562
  async function runShareAction(projectPath, options) {
5998
- const rootDir = path43.resolve(projectPath);
5999
- const manifestPath = path43.join(rootDir, MANIFEST_FILENAME);
6000
- if (!fs25.existsSync(manifestPath)) {
6563
+ const rootDir = path46.resolve(projectPath);
6564
+ const manifestPath = path46.join(rootDir, MANIFEST_FILENAME);
6565
+ if (!fs29.existsSync(manifestPath)) {
6001
6566
  logger.error(
6002
6567
  `No ${MANIFEST_FILENAME} found at ${manifestPath}.
6003
6568
  Create a constraints.yaml in your project root to define what to share.`
@@ -6006,7 +6571,7 @@ Create a constraints.yaml in your project root to define what to share.`
6006
6571
  }
6007
6572
  let parsed;
6008
6573
  try {
6009
- const raw = fs25.readFileSync(manifestPath, "utf-8");
6574
+ const raw = fs29.readFileSync(manifestPath, "utf-8");
6010
6575
  parsed = parseYaml(raw);
6011
6576
  } catch (err) {
6012
6577
  logger.error(
@@ -6020,7 +6585,7 @@ Create a constraints.yaml in your project root to define what to share.`
6020
6585
  process.exit(1);
6021
6586
  }
6022
6587
  const manifest = manifestResult.value;
6023
- const configResult = resolveConfig(path43.join(rootDir, "harness.config.json"));
6588
+ const configResult = resolveConfig(path46.join(rootDir, "harness.config.json"));
6024
6589
  if (!configResult.ok) {
6025
6590
  logger.error(configResult.error.message);
6026
6591
  process.exit(1);
@@ -6038,8 +6603,8 @@ Create a constraints.yaml in your project root to define what to share.`
6038
6603
  );
6039
6604
  process.exit(1);
6040
6605
  }
6041
- const outputDir = path43.resolve(options.output);
6042
- const outputPath = path43.join(outputDir, `${manifest.name}.harness-constraints.json`);
6606
+ const outputDir = path46.resolve(options.output);
6607
+ const outputPath = path46.join(outputDir, `${manifest.name}.harness-constraints.json`);
6043
6608
  const writeResult = await writeConfig(outputPath, bundle);
6044
6609
  if (!writeResult.ok) {
6045
6610
  logger.error(`Failed to write bundle: ${writeResult.error.message}`);
@@ -6048,28 +6613,28 @@ Create a constraints.yaml in your project root to define what to share.`
6048
6613
  logger.success(`Bundle written to ${outputPath}`);
6049
6614
  }
6050
6615
  function createShareCommand() {
6051
- return new Command54("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) => {
6616
+ return new Command57("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) => {
6052
6617
  await runShareAction(projectPath, options);
6053
6618
  });
6054
6619
  }
6055
6620
 
6056
6621
  // src/commands/skill/index.ts
6057
- import { Command as Command62 } from "commander";
6622
+ import { Command as Command65 } from "commander";
6058
6623
 
6059
6624
  // src/commands/skill/list.ts
6060
- import { Command as Command55 } from "commander";
6061
- import * as fs26 from "fs";
6062
- import * as path44 from "path";
6625
+ import { Command as Command58 } from "commander";
6626
+ import * as fs30 from "fs";
6627
+ import * as path47 from "path";
6063
6628
  import { parse } from "yaml";
6064
6629
  function scanDirectory(dirPath, source) {
6065
- if (!fs26.existsSync(dirPath)) return [];
6066
- const entries = fs26.readdirSync(dirPath, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
6630
+ if (!fs30.existsSync(dirPath)) return [];
6631
+ const entries = fs30.readdirSync(dirPath, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
6067
6632
  const skills = [];
6068
6633
  for (const name of entries) {
6069
- const yamlPath = path44.join(dirPath, name, "skill.yaml");
6070
- if (!fs26.existsSync(yamlPath)) continue;
6634
+ const yamlPath = path47.join(dirPath, name, "skill.yaml");
6635
+ if (!fs30.existsSync(yamlPath)) continue;
6071
6636
  try {
6072
- const raw = fs26.readFileSync(yamlPath, "utf-8");
6637
+ const raw = fs30.readFileSync(yamlPath, "utf-8");
6073
6638
  const parsed = parse(raw);
6074
6639
  const result = SkillMetadataSchema.safeParse(parsed);
6075
6640
  if (result.success) {
@@ -6087,10 +6652,10 @@ function scanDirectory(dirPath, source) {
6087
6652
  }
6088
6653
  function collectCommunitySkills(seen, allSkills) {
6089
6654
  const globalDir = resolveGlobalSkillsDir();
6090
- const skillsDir = path44.dirname(globalDir);
6091
- const communityBase = path44.join(skillsDir, "community");
6092
- const communityPlatformDir = path44.join(communityBase, "claude-code");
6093
- const lockfilePath = path44.join(communityBase, "skills-lock.json");
6655
+ const skillsDir = path47.dirname(globalDir);
6656
+ const communityBase = path47.join(skillsDir, "community");
6657
+ const communityPlatformDir = path47.join(communityBase, "claude-code");
6658
+ const lockfilePath = path47.join(communityBase, "skills-lock.json");
6094
6659
  const lockfile = readLockfile2(lockfilePath);
6095
6660
  const communitySkills = scanDirectory(communityPlatformDir, "community");
6096
6661
  for (const skill of communitySkills) {
@@ -6160,7 +6725,7 @@ function printSkillsVerbose(skills) {
6160
6725
  for (const s of skills) printSkillEntry(s);
6161
6726
  }
6162
6727
  function createListCommand3() {
6163
- return new Command55("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) => {
6728
+ return new Command58("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) => {
6164
6729
  const globalOpts = cmd.optsWithGlobals();
6165
6730
  const skills = collectSkills({ filter: resolveFilter(opts) });
6166
6731
  if (globalOpts.json) {
@@ -6175,9 +6740,9 @@ function createListCommand3() {
6175
6740
  }
6176
6741
 
6177
6742
  // src/commands/skill/run.ts
6178
- import { Command as Command56 } from "commander";
6179
- import * as fs27 from "fs";
6180
- import * as path45 from "path";
6743
+ import { Command as Command59 } from "commander";
6744
+ import * as fs31 from "fs";
6745
+ import * as path48 from "path";
6181
6746
  import { parse as parse2 } from "yaml";
6182
6747
 
6183
6748
  // src/skill/complexity.ts
@@ -6273,10 +6838,10 @@ ${options.principles}`);
6273
6838
 
6274
6839
  // src/commands/skill/run.ts
6275
6840
  function loadSkillMetadata(skillDir) {
6276
- const yamlPath = path45.join(skillDir, "skill.yaml");
6277
- if (!fs27.existsSync(yamlPath)) return null;
6841
+ const yamlPath = path48.join(skillDir, "skill.yaml");
6842
+ if (!fs31.existsSync(yamlPath)) return null;
6278
6843
  try {
6279
- const result = SkillMetadataSchema.safeParse(parse2(fs27.readFileSync(yamlPath, "utf-8")));
6844
+ const result = SkillMetadataSchema.safeParse(parse2(fs31.readFileSync(yamlPath, "utf-8")));
6280
6845
  return result.success ? result.data : null;
6281
6846
  } catch {
6282
6847
  return null;
@@ -6288,22 +6853,22 @@ function resolveComplexity(metadata, requested, projectPath) {
6288
6853
  return requested;
6289
6854
  }
6290
6855
  function loadPrinciples(projectPath) {
6291
- const principlesPath = path45.join(projectPath, "docs", "principles.md");
6292
- return fs27.existsSync(principlesPath) ? fs27.readFileSync(principlesPath, "utf-8") : void 0;
6856
+ const principlesPath = path48.join(projectPath, "docs", "principles.md");
6857
+ return fs31.existsSync(principlesPath) ? fs31.readFileSync(principlesPath, "utf-8") : void 0;
6293
6858
  }
6294
6859
  function readMostRecentFileInDir(dirPath) {
6295
- const files = fs27.readdirSync(dirPath).map((f) => ({ name: f, mtime: fs27.statSync(path45.join(dirPath, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
6296
- if (files.length > 0) return fs27.readFileSync(path45.join(dirPath, files[0].name), "utf-8");
6860
+ const files = fs31.readdirSync(dirPath).map((f) => ({ name: f, mtime: fs31.statSync(path48.join(dirPath, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
6861
+ if (files.length > 0) return fs31.readFileSync(path48.join(dirPath, files[0].name), "utf-8");
6297
6862
  return void 0;
6298
6863
  }
6299
6864
  function loadPriorState(metadata, projectPath) {
6300
6865
  if (!metadata?.state.persistent || metadata.state.files.length === 0) return void 0;
6301
6866
  for (const stateFilePath of metadata.state.files) {
6302
- const fullPath = path45.join(projectPath, stateFilePath);
6303
- if (!fs27.existsSync(fullPath)) continue;
6304
- const stat = fs27.statSync(fullPath);
6867
+ const fullPath = path48.join(projectPath, stateFilePath);
6868
+ if (!fs31.existsSync(fullPath)) continue;
6869
+ const stat = fs31.statSync(fullPath);
6305
6870
  if (stat.isDirectory()) return readMostRecentFileInDir(fullPath);
6306
- return fs27.readFileSync(fullPath, "utf-8");
6871
+ return fs31.readFileSync(fullPath, "utf-8");
6307
6872
  }
6308
6873
  return void 0;
6309
6874
  }
@@ -6323,9 +6888,9 @@ function resolvePhaseState(metadata, projectPath, phase) {
6323
6888
  }
6324
6889
  function appendProjectState(content, metadata, projectPath, hasPathOpt) {
6325
6890
  if (!metadata?.state.persistent || !hasPathOpt) return content;
6326
- const stateFile = path45.join(projectPath, ".harness", "state.json");
6327
- if (!fs27.existsSync(stateFile)) return content;
6328
- const stateContent = fs27.readFileSync(stateFile, "utf-8");
6891
+ const stateFile = path48.join(projectPath, ".harness", "state.json");
6892
+ if (!fs31.existsSync(stateFile)) return content;
6893
+ const stateContent = fs31.readFileSync(stateFile, "utf-8");
6329
6894
  return content + `
6330
6895
 
6331
6896
  ---
@@ -6337,14 +6902,14 @@ ${stateContent}
6337
6902
  }
6338
6903
  async function runSkill(name, opts) {
6339
6904
  const skillsDir = resolveSkillsDir();
6340
- const skillDir = path45.join(skillsDir, name);
6341
- if (!fs27.existsSync(skillDir)) {
6905
+ const skillDir = path48.join(skillsDir, name);
6906
+ if (!fs31.existsSync(skillDir)) {
6342
6907
  logger.error(`Skill not found: ${name}`);
6343
6908
  process.exit(ExitCode.ERROR);
6344
6909
  return;
6345
6910
  }
6346
6911
  const metadata = loadSkillMetadata(skillDir);
6347
- const projectPath = opts.path ? path45.resolve(opts.path) : process.cwd();
6912
+ const projectPath = opts.path ? path48.resolve(opts.path) : process.cwd();
6348
6913
  const complexity = resolveComplexity(
6349
6914
  metadata,
6350
6915
  opts.complexity ?? "standard",
@@ -6371,14 +6936,14 @@ async function runSkill(name, opts) {
6371
6936
  ...stateWarning !== void 0 && { stateWarning },
6372
6937
  ...opts.party !== void 0 && { party: opts.party }
6373
6938
  });
6374
- const skillMdPath = path45.join(skillDir, "SKILL.md");
6375
- if (!fs27.existsSync(skillMdPath)) {
6939
+ const skillMdPath = path48.join(skillDir, "SKILL.md");
6940
+ if (!fs31.existsSync(skillMdPath)) {
6376
6941
  logger.error(`SKILL.md not found for skill: ${name}`);
6377
6942
  process.exit(ExitCode.ERROR);
6378
6943
  return;
6379
6944
  }
6380
6945
  const content = appendProjectState(
6381
- fs27.readFileSync(skillMdPath, "utf-8"),
6946
+ fs31.readFileSync(skillMdPath, "utf-8"),
6382
6947
  metadata,
6383
6948
  projectPath,
6384
6949
  !!opts.path
@@ -6387,13 +6952,13 @@ async function runSkill(name, opts) {
6387
6952
  process.exit(ExitCode.SUCCESS);
6388
6953
  }
6389
6954
  function createRunCommand2() {
6390
- return new Command56("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").action(async (name, opts) => runSkill(name, opts));
6955
+ return new Command59("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").action(async (name, opts) => runSkill(name, opts));
6391
6956
  }
6392
6957
 
6393
6958
  // src/commands/skill/validate.ts
6394
- import { Command as Command57 } from "commander";
6395
- import * as fs28 from "fs";
6396
- import * as path46 from "path";
6959
+ import { Command as Command60 } from "commander";
6960
+ import * as fs32 from "fs";
6961
+ import * as path49 from "path";
6397
6962
  import { parse as parse3 } from "yaml";
6398
6963
  var BEHAVIORAL_REQUIRED_SECTIONS = [
6399
6964
  "## When to Use",
@@ -6405,11 +6970,11 @@ var BEHAVIORAL_REQUIRED_SECTIONS = [
6405
6970
  ];
6406
6971
  var KNOWLEDGE_REQUIRED_SECTIONS = ["## Instructions"];
6407
6972
  function validateSkillMd(name, skillMdPath, skillType, errors) {
6408
- if (!fs28.existsSync(skillMdPath)) {
6973
+ if (!fs32.existsSync(skillMdPath)) {
6409
6974
  errors.push(`${name}: missing SKILL.md`);
6410
6975
  return;
6411
6976
  }
6412
- const mdContent = fs28.readFileSync(skillMdPath, "utf-8");
6977
+ const mdContent = fs32.readFileSync(skillMdPath, "utf-8");
6413
6978
  if (!mdContent.trim().startsWith("# ")) {
6414
6979
  errors.push(`${name}/SKILL.md: must start with an h1 heading`);
6415
6980
  }
@@ -6434,20 +6999,20 @@ function validateSkillMd(name, skillMdPath, skillType, errors) {
6434
6999
  }
6435
7000
  }
6436
7001
  function validateSkillEntry(name, skillsDir, errors) {
6437
- const skillDir = path46.join(skillsDir, name);
6438
- const yamlPath = path46.join(skillDir, "skill.yaml");
6439
- if (!fs28.existsSync(yamlPath)) {
7002
+ const skillDir = path49.join(skillsDir, name);
7003
+ const yamlPath = path49.join(skillDir, "skill.yaml");
7004
+ if (!fs32.existsSync(yamlPath)) {
6440
7005
  errors.push(`${name}: missing skill.yaml`);
6441
7006
  return false;
6442
7007
  }
6443
7008
  try {
6444
- const raw = fs28.readFileSync(yamlPath, "utf-8");
7009
+ const raw = fs32.readFileSync(yamlPath, "utf-8");
6445
7010
  const result = SkillMetadataSchema.safeParse(parse3(raw));
6446
7011
  if (!result.success) {
6447
7012
  errors.push(`${name}/skill.yaml: ${result.error.message}`);
6448
7013
  return false;
6449
7014
  }
6450
- validateSkillMd(name, path46.join(skillDir, "SKILL.md"), result.data.type, errors);
7015
+ validateSkillMd(name, path49.join(skillDir, "SKILL.md"), result.data.type, errors);
6451
7016
  return true;
6452
7017
  } catch (e) {
6453
7018
  errors.push(`${name}: parse error \u2014 ${e instanceof Error ? e.message : String(e)}`);
@@ -6455,15 +7020,15 @@ function validateSkillEntry(name, skillsDir, errors) {
6455
7020
  }
6456
7021
  }
6457
7022
  function createValidateCommand2() {
6458
- return new Command57("validate").description("Validate all skill.yaml files and SKILL.md structure").action(async (_opts, cmd) => {
7023
+ return new Command60("validate").description("Validate all skill.yaml files and SKILL.md structure").action(async (_opts, cmd) => {
6459
7024
  const globalOpts = cmd.optsWithGlobals();
6460
7025
  const skillsDir = resolveSkillsDir();
6461
- if (!fs28.existsSync(skillsDir)) {
7026
+ if (!fs32.existsSync(skillsDir)) {
6462
7027
  logger.info("No skills directory found.");
6463
7028
  process.exit(ExitCode.SUCCESS);
6464
7029
  return;
6465
7030
  }
6466
- const entries = fs28.readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
7031
+ const entries = fs32.readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
6467
7032
  const errors = [];
6468
7033
  let validated = 0;
6469
7034
  for (const name of entries) {
@@ -6485,24 +7050,24 @@ function createValidateCommand2() {
6485
7050
  }
6486
7051
 
6487
7052
  // src/commands/skill/info.ts
6488
- import { Command as Command58 } from "commander";
6489
- import * as fs29 from "fs";
6490
- import * as path47 from "path";
7053
+ import { Command as Command61 } from "commander";
7054
+ import * as fs33 from "fs";
7055
+ import * as path50 from "path";
6491
7056
  import { parse as parse4 } from "yaml";
6492
7057
  function loadSkillMetadata2(name) {
6493
7058
  const skillsDir = resolveSkillsDir();
6494
- const skillDir = path47.join(skillsDir, name);
6495
- if (!fs29.existsSync(skillDir)) {
7059
+ const skillDir = path50.join(skillsDir, name);
7060
+ if (!fs33.existsSync(skillDir)) {
6496
7061
  logger.error(`Skill not found: ${name}`);
6497
7062
  return { ok: false, exitCode: ExitCode.ERROR };
6498
7063
  }
6499
- const yamlPath = path47.join(skillDir, "skill.yaml");
6500
- if (!fs29.existsSync(yamlPath)) {
7064
+ const yamlPath = path50.join(skillDir, "skill.yaml");
7065
+ if (!fs33.existsSync(yamlPath)) {
6501
7066
  logger.error(`skill.yaml not found for skill: ${name}`);
6502
7067
  return { ok: false, exitCode: ExitCode.ERROR };
6503
7068
  }
6504
7069
  try {
6505
- const raw = fs29.readFileSync(yamlPath, "utf-8");
7070
+ const raw = fs33.readFileSync(yamlPath, "utf-8");
6506
7071
  const parsed = parse4(raw);
6507
7072
  const result = SkillMetadataSchema.safeParse(parsed);
6508
7073
  if (!result.success) {
@@ -6531,7 +7096,7 @@ function printSkillInfo(skill) {
6531
7096
  console.log(`Persistent: ${skill.state.persistent}`);
6532
7097
  }
6533
7098
  function createInfoCommand() {
6534
- return new Command58("info").description("Show metadata for a skill").argument("<name>", "Skill name (e.g., harness-tdd)").action(async (name, _opts, cmd) => {
7099
+ return new Command61("info").description("Show metadata for a skill").argument("<name>", "Skill name (e.g., harness-tdd)").action(async (name, _opts, cmd) => {
6535
7100
  const globalOpts = cmd.optsWithGlobals();
6536
7101
  const loaded = loadSkillMetadata2(name);
6537
7102
  if (!loaded.ok) {
@@ -6548,7 +7113,7 @@ function createInfoCommand() {
6548
7113
  }
6549
7114
 
6550
7115
  // src/commands/skill/search.ts
6551
- import { Command as Command59 } from "commander";
7116
+ import { Command as Command62 } from "commander";
6552
7117
  async function runSearch(query, opts) {
6553
7118
  const results = await searchNpmRegistry(query, opts.registry);
6554
7119
  return results.filter((r) => {
@@ -6593,15 +7158,15 @@ async function runSearchAction(query, opts, globalOpts) {
6593
7158
  }
6594
7159
  }
6595
7160
  function createSearchCommand() {
6596
- return new Command59("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) => {
7161
+ return new Command62("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) => {
6597
7162
  await runSearchAction(query, opts, cmd.optsWithGlobals());
6598
7163
  });
6599
7164
  }
6600
7165
 
6601
7166
  // src/commands/skill/create.ts
6602
- import { Command as Command60 } from "commander";
6603
- import * as path48 from "path";
6604
- import * as fs30 from "fs";
7167
+ import { Command as Command63 } from "commander";
7168
+ import * as path51 from "path";
7169
+ import * as fs34 from "fs";
6605
7170
  import YAML from "yaml";
6606
7171
  var KEBAB_CASE_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
6607
7172
  function buildReadme(name, description) {
@@ -6685,22 +7250,22 @@ function runCreate(name, opts) {
6685
7250
  if (!KEBAB_CASE_RE.test(name)) {
6686
7251
  throw new Error(`Invalid skill name "${name}". Must be kebab-case (e.g., my-skill).`);
6687
7252
  }
6688
- const baseDir = opts.outputDir ?? path48.join(process.cwd(), "agents", "skills", "claude-code");
6689
- const skillDir = path48.join(baseDir, name);
6690
- if (fs30.existsSync(skillDir)) {
7253
+ const baseDir = opts.outputDir ?? path51.join(process.cwd(), "agents", "skills", "claude-code");
7254
+ const skillDir = path51.join(baseDir, name);
7255
+ if (fs34.existsSync(skillDir)) {
6691
7256
  throw new Error(`Skill directory already exists: ${skillDir}`);
6692
7257
  }
6693
- fs30.mkdirSync(skillDir, { recursive: true });
7258
+ fs34.mkdirSync(skillDir, { recursive: true });
6694
7259
  const description = opts.description || `A community skill: ${name}`;
6695
7260
  const skillYaml = buildSkillYaml(name, opts);
6696
- const skillYamlPath = path48.join(skillDir, "skill.yaml");
6697
- fs30.writeFileSync(skillYamlPath, YAML.stringify(skillYaml));
7261
+ const skillYamlPath = path51.join(skillDir, "skill.yaml");
7262
+ fs34.writeFileSync(skillYamlPath, YAML.stringify(skillYaml));
6698
7263
  const skillMd = buildSkillMd(name, description);
6699
- const skillMdPath = path48.join(skillDir, "SKILL.md");
6700
- fs30.writeFileSync(skillMdPath, skillMd);
7264
+ const skillMdPath = path51.join(skillDir, "SKILL.md");
7265
+ fs34.writeFileSync(skillMdPath, skillMd);
6701
7266
  const readme = buildReadme(name, description);
6702
- const readmePath = path48.join(skillDir, "README.md");
6703
- fs30.writeFileSync(readmePath, readme);
7267
+ const readmePath = path51.join(skillDir, "README.md");
7268
+ fs34.writeFileSync(readmePath, readme);
6704
7269
  return {
6705
7270
  name,
6706
7271
  directory: skillDir,
@@ -6714,12 +7279,12 @@ function printCreateResult(name, result) {
6714
7279
  }
6715
7280
  logger.info(`
6716
7281
  Next steps:`);
6717
- logger.info(` 1. Edit ${path48.join(result.directory, "SKILL.md")} with your skill content`);
7282
+ logger.info(` 1. Edit ${path51.join(result.directory, "SKILL.md")} with your skill content`);
6718
7283
  logger.info(` 2. Run: harness skill validate ${name}`);
6719
7284
  logger.info(` 3. Run: harness skills publish`);
6720
7285
  }
6721
7286
  function createCreateCommand() {
6722
- return new Command60("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) => {
7287
+ return new Command63("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) => {
6723
7288
  const globalOpts = cmd.optsWithGlobals();
6724
7289
  try {
6725
7290
  const result = runCreate(name, {
@@ -6742,26 +7307,26 @@ function createCreateCommand() {
6742
7307
  }
6743
7308
 
6744
7309
  // src/commands/skill/publish.ts
6745
- import { Command as Command61 } from "commander";
6746
- import * as fs32 from "fs";
6747
- import * as path50 from "path";
7310
+ import { Command as Command64 } from "commander";
7311
+ import * as fs36 from "fs";
7312
+ import * as path53 from "path";
6748
7313
  import { execFileSync as execFileSync5 } from "child_process";
6749
7314
 
6750
7315
  // src/registry/validator.ts
6751
- import * as fs31 from "fs";
6752
- import * as path49 from "path";
7316
+ import * as fs35 from "fs";
7317
+ import * as path52 from "path";
6753
7318
  import { parse as parse5 } from "yaml";
6754
7319
  import semver4 from "semver";
6755
7320
  async function validateForPublish(skillDir, registryUrl) {
6756
7321
  const errors = [];
6757
- const skillYamlPath = path49.join(skillDir, "skill.yaml");
6758
- if (!fs31.existsSync(skillYamlPath)) {
7322
+ const skillYamlPath = path52.join(skillDir, "skill.yaml");
7323
+ if (!fs35.existsSync(skillYamlPath)) {
6759
7324
  errors.push("skill.yaml not found. Create one with: harness skill create <name>");
6760
7325
  return { valid: false, errors };
6761
7326
  }
6762
7327
  let skillMeta;
6763
7328
  try {
6764
- const raw = fs31.readFileSync(skillYamlPath, "utf-8");
7329
+ const raw = fs35.readFileSync(skillYamlPath, "utf-8");
6765
7330
  const parsed = parse5(raw);
6766
7331
  const result = SkillMetadataSchema.safeParse(parsed);
6767
7332
  if (!result.success) {
@@ -6783,11 +7348,11 @@ async function validateForPublish(skillDir, registryUrl) {
6783
7348
  if (!skillMeta.triggers || skillMeta.triggers.length === 0) {
6784
7349
  errors.push("At least one trigger is required. Add triggers to skill.yaml.");
6785
7350
  }
6786
- const skillMdPath = path49.join(skillDir, "SKILL.md");
6787
- if (!fs31.existsSync(skillMdPath)) {
7351
+ const skillMdPath = path52.join(skillDir, "SKILL.md");
7352
+ if (!fs35.existsSync(skillMdPath)) {
6788
7353
  errors.push("SKILL.md not found. Create it with content describing your skill.");
6789
7354
  } else {
6790
- const content = fs31.readFileSync(skillMdPath, "utf-8");
7355
+ const content = fs35.readFileSync(skillMdPath, "utf-8");
6791
7356
  if (!content.includes("## When to Use")) {
6792
7357
  errors.push('SKILL.md must contain a "## When to Use" section.');
6793
7358
  }
@@ -6867,11 +7432,11 @@ ${errorList}`);
6867
7432
  }
6868
7433
  const meta = validation.skillMeta;
6869
7434
  const pkg = derivePackageJson(meta);
6870
- const pkgPath = path50.join(skillDir, "package.json");
6871
- fs32.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
6872
- const readmePath = path50.join(skillDir, "README.md");
6873
- if (!fs32.existsSync(readmePath)) {
6874
- const skillMdContent = fs32.readFileSync(path50.join(skillDir, "SKILL.md"), "utf-8");
7435
+ const pkgPath = path53.join(skillDir, "package.json");
7436
+ fs36.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
7437
+ const readmePath = path53.join(skillDir, "README.md");
7438
+ if (!fs36.existsSync(readmePath)) {
7439
+ const skillMdContent = fs36.readFileSync(path53.join(skillDir, "SKILL.md"), "utf-8");
6875
7440
  const readme = `# ${pkg.name}
6876
7441
 
6877
7442
  ${meta.description}
@@ -6885,7 +7450,7 @@ harness install ${meta.name}
6885
7450
  ---
6886
7451
 
6887
7452
  ${skillMdContent}`;
6888
- fs32.writeFileSync(readmePath, readme);
7453
+ fs36.writeFileSync(readmePath, readme);
6889
7454
  }
6890
7455
  if (opts.dryRun) {
6891
7456
  return {
@@ -6911,7 +7476,7 @@ ${skillMdContent}`;
6911
7476
  };
6912
7477
  }
6913
7478
  function createPublishCommand() {
6914
- return new Command61("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) => {
7479
+ return new Command64("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) => {
6915
7480
  const globalOpts = cmd.optsWithGlobals();
6916
7481
  const skillDir = opts.dir || process.cwd();
6917
7482
  try {
@@ -6936,7 +7501,7 @@ function createPublishCommand() {
6936
7501
 
6937
7502
  // src/commands/skill/index.ts
6938
7503
  function createSkillCommand() {
6939
- const command = new Command62("skill").description("Skill management commands");
7504
+ const command = new Command65("skill").description("Skill management commands");
6940
7505
  command.addCommand(createListCommand3());
6941
7506
  command.addCommand(createRunCommand2());
6942
7507
  command.addCommand(createValidateCommand2());
@@ -6948,9 +7513,10 @@ function createSkillCommand() {
6948
7513
  }
6949
7514
 
6950
7515
  // src/commands/snapshot.ts
6951
- import { Command as Command63 } from "commander";
7516
+ init_dist();
7517
+ import { Command as Command66 } from "commander";
6952
7518
  import { execSync as execSync5 } from "child_process";
6953
- import chalk9 from "chalk";
7519
+ import chalk10 from "chalk";
6954
7520
  function getCommitHash3(cwd) {
6955
7521
  try {
6956
7522
  return execSync5("git rev-parse --short HEAD", { cwd, encoding: "utf-8" }).toString().trim();
@@ -6975,9 +7541,9 @@ function formatDelta(delta) {
6975
7541
  function directionSymbol(direction) {
6976
7542
  switch (direction) {
6977
7543
  case "improving":
6978
- return chalk9.green("improving");
7544
+ return chalk10.green("improving");
6979
7545
  case "declining":
6980
- return chalk9.red("declining");
7546
+ return chalk10.red("declining");
6981
7547
  case "stable":
6982
7548
  return "=";
6983
7549
  }
@@ -7130,7 +7696,7 @@ function registerListCommand(parent) {
7130
7696
  });
7131
7697
  }
7132
7698
  function createSnapshotCommand() {
7133
- const command = new Command63("snapshot").description("Architecture timeline snapshot commands");
7699
+ const command = new Command66("snapshot").description("Architecture timeline snapshot commands");
7134
7700
  registerCaptureCommand(command);
7135
7701
  registerTrendsCommand(command);
7136
7702
  registerListCommand(command);
@@ -7138,11 +7704,12 @@ function createSnapshotCommand() {
7138
7704
  }
7139
7705
 
7140
7706
  // src/commands/state/index.ts
7141
- import { Command as Command68 } from "commander";
7707
+ import { Command as Command71 } from "commander";
7142
7708
 
7143
7709
  // src/commands/state/show.ts
7144
- import { Command as Command64 } from "commander";
7145
- import * as path51 from "path";
7710
+ init_dist();
7711
+ import { Command as Command67 } from "commander";
7712
+ import * as path54 from "path";
7146
7713
  function printStateText(state, stream) {
7147
7714
  if (stream) console.log(`Stream: ${stream}`);
7148
7715
  console.log(`Schema Version: ${state.schemaVersion}`);
@@ -7166,9 +7733,9 @@ function printStateProgress(progress) {
7166
7733
  }
7167
7734
  }
7168
7735
  function createShowCommand() {
7169
- return new Command64("show").description("Show current project state").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(async (opts, cmd) => {
7736
+ return new Command67("show").description("Show current project state").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(async (opts, cmd) => {
7170
7737
  const globalOpts = cmd.optsWithGlobals();
7171
- const result = await loadState(path51.resolve(opts.path), opts.stream);
7738
+ const result = await loadState(path54.resolve(opts.path), opts.stream);
7172
7739
  if (!result.ok) {
7173
7740
  logger.error(result.error.message);
7174
7741
  process.exit(ExitCode.ERROR);
@@ -7183,10 +7750,11 @@ function createShowCommand() {
7183
7750
  }
7184
7751
 
7185
7752
  // src/commands/state/reset.ts
7186
- import { Command as Command65 } from "commander";
7187
- import * as fs33 from "fs";
7188
- import * as path52 from "path";
7189
- import * as readline from "readline";
7753
+ init_dist();
7754
+ import { Command as Command68 } from "commander";
7755
+ import * as fs37 from "fs";
7756
+ import * as path55 from "path";
7757
+ import * as readline2 from "readline";
7190
7758
  async function resolveStatePath(projectPath, stream) {
7191
7759
  if (stream) {
7192
7760
  const streamResult = await resolveStreamPath(projectPath, { stream });
@@ -7194,22 +7762,22 @@ async function resolveStatePath(projectPath, stream) {
7194
7762
  logger.error(streamResult.error.message);
7195
7763
  process.exit(ExitCode.ERROR);
7196
7764
  }
7197
- return path52.join(streamResult.value, "state.json");
7765
+ return path55.join(streamResult.value, "state.json");
7198
7766
  }
7199
- return path52.join(projectPath, ".harness", "state.json");
7767
+ return path55.join(projectPath, ".harness", "state.json");
7200
7768
  }
7201
7769
  async function confirmReset() {
7202
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
7203
- const answer = await new Promise((resolve32) => {
7204
- rl.question("Reset project state? This cannot be undone. [y/N] ", resolve32);
7770
+ const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
7771
+ const answer = await new Promise((resolve35) => {
7772
+ rl.question("Reset project state? This cannot be undone. [y/N] ", resolve35);
7205
7773
  });
7206
7774
  rl.close();
7207
7775
  return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
7208
7776
  }
7209
7777
  async function runReset(opts) {
7210
- const projectPath = path52.resolve(opts.path);
7778
+ const projectPath = path55.resolve(opts.path);
7211
7779
  const statePath = await resolveStatePath(projectPath, opts.stream);
7212
- if (!fs33.existsSync(statePath)) {
7780
+ if (!fs37.existsSync(statePath)) {
7213
7781
  logger.info("No state file found. Nothing to reset.");
7214
7782
  process.exit(ExitCode.SUCCESS);
7215
7783
  }
@@ -7218,7 +7786,7 @@ async function runReset(opts) {
7218
7786
  process.exit(ExitCode.SUCCESS);
7219
7787
  }
7220
7788
  try {
7221
- fs33.unlinkSync(statePath);
7789
+ fs37.unlinkSync(statePath);
7222
7790
  logger.success("Project state reset.");
7223
7791
  } catch (e) {
7224
7792
  logger.error(`Failed to reset state: ${e instanceof Error ? e.message : String(e)}`);
@@ -7227,15 +7795,16 @@ async function runReset(opts) {
7227
7795
  process.exit(ExitCode.SUCCESS);
7228
7796
  }
7229
7797
  function createResetCommand() {
7230
- return new Command65("reset").description("Reset project state (deletes .harness/state.json)").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").option("--yes", "Skip confirmation prompt").action(async (opts) => runReset(opts));
7798
+ return new Command68("reset").description("Reset project state (deletes .harness/state.json)").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").option("--yes", "Skip confirmation prompt").action(async (opts) => runReset(opts));
7231
7799
  }
7232
7800
 
7233
7801
  // src/commands/state/learn.ts
7234
- import { Command as Command66 } from "commander";
7235
- import * as path53 from "path";
7802
+ init_dist();
7803
+ import { Command as Command69 } from "commander";
7804
+ import * as path56 from "path";
7236
7805
  function createLearnCommand() {
7237
- return new Command66("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) => {
7238
- const projectPath = path53.resolve(opts.path);
7806
+ return new Command69("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) => {
7807
+ const projectPath = path56.resolve(opts.path);
7239
7808
  const result = await appendLearning(projectPath, message, void 0, void 0, opts.stream);
7240
7809
  if (!result.ok) {
7241
7810
  logger.error(result.error.message);
@@ -7248,10 +7817,11 @@ function createLearnCommand() {
7248
7817
  }
7249
7818
 
7250
7819
  // src/commands/state/streams.ts
7251
- import { Command as Command67 } from "commander";
7252
- import * as path54 from "path";
7820
+ init_dist();
7821
+ import { Command as Command70 } from "commander";
7822
+ import * as path57 from "path";
7253
7823
  async function runListStreams(opts, globalOpts) {
7254
- const projectPath = path54.resolve(opts.path);
7824
+ const projectPath = path57.resolve(opts.path);
7255
7825
  const indexResult = await loadStreamIndex(projectPath);
7256
7826
  const result = await listStreams(projectPath);
7257
7827
  if (!result.ok) {
@@ -7272,7 +7842,7 @@ async function runListStreams(opts, globalOpts) {
7272
7842
  process.exit(ExitCode.SUCCESS);
7273
7843
  }
7274
7844
  async function runCreateStream(name, opts) {
7275
- const result = await createStream(path54.resolve(opts.path), name, opts.branch);
7845
+ const result = await createStream(path57.resolve(opts.path), name, opts.branch);
7276
7846
  if (!result.ok) {
7277
7847
  logger.error(result.error.message);
7278
7848
  process.exit(ExitCode.ERROR);
@@ -7281,7 +7851,7 @@ async function runCreateStream(name, opts) {
7281
7851
  process.exit(ExitCode.SUCCESS);
7282
7852
  }
7283
7853
  async function runArchiveStream(name, opts) {
7284
- const result = await archiveStream(path54.resolve(opts.path), name);
7854
+ const result = await archiveStream(path57.resolve(opts.path), name);
7285
7855
  if (!result.ok) {
7286
7856
  logger.error(result.error.message);
7287
7857
  process.exit(ExitCode.ERROR);
@@ -7290,7 +7860,7 @@ async function runArchiveStream(name, opts) {
7290
7860
  process.exit(ExitCode.SUCCESS);
7291
7861
  }
7292
7862
  async function runActivateStream(name, opts) {
7293
- const result = await setActiveStream(path54.resolve(opts.path), name);
7863
+ const result = await setActiveStream(path57.resolve(opts.path), name);
7294
7864
  if (!result.ok) {
7295
7865
  logger.error(result.error.message);
7296
7866
  process.exit(ExitCode.ERROR);
@@ -7299,7 +7869,7 @@ async function runActivateStream(name, opts) {
7299
7869
  process.exit(ExitCode.SUCCESS);
7300
7870
  }
7301
7871
  function createStreamsCommand() {
7302
- const command = new Command67("streams").description("Manage state streams");
7872
+ const command = new Command70("streams").description("Manage state streams");
7303
7873
  command.command("list").description("List all known streams").option("--path <path>", "Project root path", ".").action(async (opts, cmd) => runListStreams(opts, cmd.optsWithGlobals()));
7304
7874
  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));
7305
7875
  command.command("archive <name>").description("Archive a stream").option("--path <path>", "Project root path", ".").action(async (name, opts) => runArchiveStream(name, opts));
@@ -7309,7 +7879,7 @@ function createStreamsCommand() {
7309
7879
 
7310
7880
  // src/commands/state/index.ts
7311
7881
  function createStateCommand() {
7312
- const command = new Command68("state").description("Project state management commands");
7882
+ const command = new Command71("state").description("Project state management commands");
7313
7883
  command.addCommand(createShowCommand());
7314
7884
  command.addCommand(createResetCommand());
7315
7885
  command.addCommand(createLearnCommand());
@@ -7317,8 +7887,154 @@ function createStateCommand() {
7317
7887
  return command;
7318
7888
  }
7319
7889
 
7890
+ // src/commands/sync-analyses.ts
7891
+ import * as fs38 from "fs";
7892
+ import * as path58 from "path";
7893
+ import { Command as Command72 } from "commander";
7894
+ init_dist();
7895
+ function isValidRecord(record) {
7896
+ return typeof record.issueId === "string" && !!record.issueId && typeof record.identifier === "string" && !!record.identifier && typeof record.analyzedAt === "string" && !/[/\\]/.test(record.issueId);
7897
+ }
7898
+ function extractAnalysisFromComments(comments) {
7899
+ const sorted = [...comments].sort(
7900
+ (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
7901
+ );
7902
+ for (const comment of sorted) {
7903
+ const fenceRegex = /```json\s*\r?\n([\s\S]*?)\r?\n```/g;
7904
+ let match;
7905
+ while ((match = fenceRegex.exec(comment.body)) !== null) {
7906
+ try {
7907
+ const parsed = JSON.parse(match[1]);
7908
+ if (parsed._harness_analysis !== true) continue;
7909
+ const { _harness_analysis, _version, ...record } = parsed;
7910
+ if (isValidRecord(record)) return record;
7911
+ } catch {
7912
+ continue;
7913
+ }
7914
+ }
7915
+ }
7916
+ return null;
7917
+ }
7918
+ function bootstrapTrackerCommand2(opts, verb) {
7919
+ const projectPath = path58.resolve(opts.dir);
7920
+ const trackerConfig = loadTrackerSyncConfig(projectPath);
7921
+ if (!trackerConfig) {
7922
+ logger.error(`No tracker config found in harness.config.json. Cannot ${verb}.`);
7923
+ return null;
7924
+ }
7925
+ if (trackerConfig.kind !== "github") {
7926
+ logger.error(
7927
+ `${verb} currently only supports 'github' tracker kind. Found: ${String(trackerConfig.kind)}`
7928
+ );
7929
+ return null;
7930
+ }
7931
+ const projectEnvPath = path58.join(projectPath, ".env");
7932
+ if (fs38.existsSync(projectEnvPath) && !process.env.GITHUB_TOKEN) {
7933
+ __require("dotenv").config({ path: projectEnvPath });
7934
+ }
7935
+ const token = process.env.GITHUB_TOKEN;
7936
+ if (!token) {
7937
+ logger.error("No GITHUB_TOKEN environment variable found.");
7938
+ return null;
7939
+ }
7940
+ return { token, projectPath, trackerConfig };
7941
+ }
7942
+ function loadRoadmapFeatures(projectPath) {
7943
+ const roadmapFile = path58.join(projectPath, "docs", "roadmap.md");
7944
+ if (!fs38.existsSync(roadmapFile)) {
7945
+ logger.error("No docs/roadmap.md found. Cannot discover features with externalIds.");
7946
+ return null;
7947
+ }
7948
+ const { parseRoadmap } = (init_dist(), __toCommonJS(dist_exports));
7949
+ const roadmapParsed = parseRoadmap(fs38.readFileSync(roadmapFile, "utf-8"));
7950
+ if (!roadmapParsed.ok) {
7951
+ logger.error("Failed to parse docs/roadmap.md");
7952
+ return null;
7953
+ }
7954
+ const features = [];
7955
+ for (const milestone of roadmapParsed.value.milestones) {
7956
+ for (const feature of milestone.features) {
7957
+ if (feature.externalId) {
7958
+ features.push({ name: feature.name, externalId: feature.externalId });
7959
+ }
7960
+ }
7961
+ }
7962
+ return features;
7963
+ }
7964
+ async function syncFeatureAnalyses(features, adapter, archive, publishedIndex) {
7965
+ let syncedCount = 0;
7966
+ let skippedCount = 0;
7967
+ let warnCount = 0;
7968
+ for (const { name, externalId } of features) {
7969
+ const commentsResult = await adapter.fetchComments(externalId);
7970
+ if (!commentsResult.ok) {
7971
+ logger.warn(
7972
+ `Failed to fetch comments for "${name}" (${externalId}): ${commentsResult.error.message}`
7973
+ );
7974
+ warnCount++;
7975
+ continue;
7976
+ }
7977
+ const record = extractAnalysisFromComments(commentsResult.value);
7978
+ if (!record) {
7979
+ skippedCount++;
7980
+ continue;
7981
+ }
7982
+ await archive.save(record);
7983
+ publishedIndex[record.issueId] = record.analyzedAt;
7984
+ syncedCount++;
7985
+ logger.info(`Synced analysis for "${name}" (${record.identifier})`);
7986
+ }
7987
+ return { syncedCount, skippedCount, warnCount };
7988
+ }
7989
+ async function runSyncAnalyses(opts) {
7990
+ const bootstrap = bootstrapTrackerCommand2(opts, "sync");
7991
+ if (!bootstrap) return process.exit(1);
7992
+ const { token, projectPath, trackerConfig } = bootstrap;
7993
+ const features = loadRoadmapFeatures(projectPath);
7994
+ if (!features) return process.exit(1);
7995
+ if (features.length === 0) {
7996
+ logger.info("No roadmap features have externalIds. Nothing to sync.");
7997
+ return;
7998
+ }
7999
+ const { AnalysisArchive, loadPublishedIndex: loadPublishedIndex2, savePublishedIndex: savePublishedIndex2 } = await import("@harness-engineering/orchestrator");
8000
+ const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-WCSJHQPK.js");
8001
+ const adapter = new GitHubIssuesSyncAdapter2({ token, config: trackerConfig });
8002
+ const archive = new AnalysisArchive(path58.join(projectPath, ".harness", "analyses"));
8003
+ const publishedIndex = loadPublishedIndex2(projectPath);
8004
+ const { syncedCount, skippedCount, warnCount } = await syncFeatureAnalyses(
8005
+ features,
8006
+ adapter,
8007
+ archive,
8008
+ publishedIndex
8009
+ );
8010
+ if (syncedCount > 0) {
8011
+ savePublishedIndex2(projectPath, publishedIndex);
8012
+ logger.success(
8013
+ `Synced ${syncedCount} analysis record(s). Skipped ${skippedCount} (no analysis). Warnings: ${warnCount}.`
8014
+ );
8015
+ } else {
8016
+ logger.info(
8017
+ `No new analyses found on tracker. Skipped ${skippedCount} feature(s) with no analysis comments.`
8018
+ );
8019
+ }
8020
+ }
8021
+ function createSyncAnalysesCommand() {
8022
+ const command = new Command72("sync-analyses").description(
8023
+ "Pull published intelligence analyses from the external issue tracker into the local .harness/analyses/ directory"
8024
+ ).option("-d, --dir <path>", "Workspace directory", process.cwd()).action(async (opts) => {
8025
+ try {
8026
+ await runSyncAnalyses(opts);
8027
+ } catch (err) {
8028
+ logger.error(`Error syncing analyses: ${err instanceof Error ? err.message : String(err)}`);
8029
+ process.exit(1);
8030
+ }
8031
+ });
8032
+ return command;
8033
+ }
8034
+
7320
8035
  // src/commands/taint.ts
7321
- import { Command as Command69 } from "commander";
8036
+ init_dist();
8037
+ import { Command as Command73 } from "commander";
7322
8038
  function getProjectRoot() {
7323
8039
  return process.cwd();
7324
8040
  }
@@ -7390,27 +8106,28 @@ function registerStatusCommand(taint) {
7390
8106
  });
7391
8107
  }
7392
8108
  function createTaintCommand() {
7393
- const taint = new Command69("taint").description("Manage sentinel session taint state");
8109
+ const taint = new Command73("taint").description("Manage sentinel session taint state");
7394
8110
  registerClearCommand(taint);
7395
8111
  registerStatusCommand(taint);
7396
8112
  return taint;
7397
8113
  }
7398
8114
 
7399
8115
  // src/commands/telemetry/index.ts
7400
- import { Command as Command72 } from "commander";
8116
+ import { Command as Command76 } from "commander";
7401
8117
 
7402
8118
  // src/commands/telemetry/identify.ts
7403
- import { Command as Command70 } from "commander";
7404
- import * as fs34 from "fs";
7405
- import * as path55 from "path";
8119
+ init_dist();
8120
+ import { Command as Command74 } from "commander";
8121
+ import * as fs39 from "fs";
8122
+ import * as path59 from "path";
7406
8123
  function telemetryFilePath(cwd) {
7407
- return path55.join(cwd, ".harness", "telemetry.json");
8124
+ return path59.join(cwd, ".harness", "telemetry.json");
7408
8125
  }
7409
8126
  function writeTelemetryFile(cwd, data) {
7410
8127
  const filePath = telemetryFilePath(cwd);
7411
- const dir = path55.dirname(filePath);
7412
- fs34.mkdirSync(dir, { recursive: true });
7413
- fs34.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
8128
+ const dir = path59.dirname(filePath);
8129
+ fs39.mkdirSync(dir, { recursive: true });
8130
+ fs39.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
7414
8131
  }
7415
8132
  var IDENTITY_FIELDS = ["project", "team", "alias"];
7416
8133
  function applyIdentityFields(opts, identity) {
@@ -7424,7 +8141,7 @@ function printIdentity(identity) {
7424
8141
  }
7425
8142
  }
7426
8143
  function createIdentifyCommand() {
7427
- const cmd = new Command70("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) => {
8144
+ const cmd = new Command74("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) => {
7428
8145
  const cwd = process.cwd();
7429
8146
  if (opts.clear) {
7430
8147
  writeTelemetryFile(cwd, { identity: {} });
@@ -7448,7 +8165,8 @@ function createIdentifyCommand() {
7448
8165
  }
7449
8166
 
7450
8167
  // src/commands/telemetry/status.ts
7451
- import { Command as Command71 } from "commander";
8168
+ init_dist();
8169
+ import { Command as Command75 } from "commander";
7452
8170
  function gatherEnvOverrides() {
7453
8171
  const overrides = {};
7454
8172
  if (process.env.DO_NOT_TRACK) overrides.DO_NOT_TRACK = process.env.DO_NOT_TRACK;
@@ -7486,7 +8204,7 @@ function printHumanStatus(result) {
7486
8204
  }
7487
8205
  }
7488
8206
  function createStatusCommand() {
7489
- const cmd = new Command71("status").description("Show current telemetry consent state, install ID, and identity").option("--json", "Output as JSON").action((opts) => {
8207
+ const cmd = new Command75("status").description("Show current telemetry consent state, install ID, and identity").option("--json", "Output as JSON").action((opts) => {
7490
8208
  const cwd = process.cwd();
7491
8209
  const envOverrides = gatherEnvOverrides();
7492
8210
  const consent = resolveConsent(cwd, void 0);
@@ -7519,15 +8237,15 @@ function createStatusCommand() {
7519
8237
 
7520
8238
  // src/commands/telemetry/index.ts
7521
8239
  function createTelemetryCommand() {
7522
- const command = new Command72("telemetry").description("Telemetry identity and status management");
8240
+ const command = new Command76("telemetry").description("Telemetry identity and status management");
7523
8241
  command.addCommand(createIdentifyCommand());
7524
8242
  command.addCommand(createStatusCommand());
7525
8243
  return command;
7526
8244
  }
7527
8245
 
7528
8246
  // src/commands/traceability.ts
7529
- import { Command as Command73 } from "commander";
7530
- import chalk10 from "chalk";
8247
+ import { Command as Command77 } from "commander";
8248
+ import chalk11 from "chalk";
7531
8249
  function confidenceLabel(maxConfidence) {
7532
8250
  if (maxConfidence >= 0.8) return "explicit";
7533
8251
  if (maxConfidence > 0) return "inferred";
@@ -7536,13 +8254,13 @@ function confidenceLabel(maxConfidence) {
7536
8254
  function statusIcon(status) {
7537
8255
  switch (status) {
7538
8256
  case "full":
7539
- return chalk10.green("\u2713 full");
8257
+ return chalk11.green("\u2713 full");
7540
8258
  case "code-only":
7541
- return chalk10.yellow("\u25D0 code-only");
8259
+ return chalk11.yellow("\u25D0 code-only");
7542
8260
  case "test-only":
7543
- return chalk10.yellow("\u25D0 test-only");
8261
+ return chalk11.yellow("\u25D0 test-only");
7544
8262
  case "none":
7545
- return chalk10.red("\u2717 none");
8263
+ return chalk11.red("\u2717 none");
7546
8264
  default:
7547
8265
  return status;
7548
8266
  }
@@ -7584,14 +8302,14 @@ function handleEmptyResults(mode) {
7584
8302
  }
7585
8303
  function printResultTable(result, mode) {
7586
8304
  const specLabel = result.specPath || result.featureName || "Unknown";
7587
- console.log(` ${chalk10.cyan(specLabel)} ${chalk10.dim(`(${result.summary.total} requirements)`)}`);
8305
+ console.log(` ${chalk11.cyan(specLabel)} ${chalk11.dim(`(${result.summary.total} requirements)`)}`);
7588
8306
  console.log("");
7589
8307
  const numWidth = 4;
7590
8308
  const nameWidth = mode === OutputMode.VERBOSE ? 44 : 36;
7591
8309
  const codeWidth = 6;
7592
8310
  const testWidth = 7;
7593
8311
  const confWidth = 12;
7594
- const header = chalk10.dim(
8312
+ const header = chalk11.dim(
7595
8313
  ` ${pad("#", numWidth)}${pad("Requirement", nameWidth)}${pad("Code", codeWidth)}${pad("Tests", testWidth)}${pad("Confidence", confWidth)}Status`
7596
8314
  );
7597
8315
  console.log(header);
@@ -7620,10 +8338,10 @@ function printRequirementRows(requirements, mode, cols) {
7620
8338
  );
7621
8339
  if (mode === OutputMode.VERBOSE) {
7622
8340
  for (const f of req.codeFiles) {
7623
- console.log(chalk10.dim(` code: ${f.path} (${f.method})`));
8341
+ console.log(chalk11.dim(` code: ${f.path} (${f.method})`));
7624
8342
  }
7625
8343
  for (const f of req.testFiles) {
7626
- console.log(chalk10.dim(` test: ${f.path} (${f.method})`));
8344
+ console.log(chalk11.dim(` test: ${f.path} (${f.method})`));
7627
8345
  }
7628
8346
  }
7629
8347
  }
@@ -7634,7 +8352,7 @@ function printCoverageSummary(summary) {
7634
8352
  const codePct = s.total > 0 ? Math.round(s.withCode / s.total * 100) : 0;
7635
8353
  const testPct = s.total > 0 ? Math.round(s.withTests / s.total * 100) : 0;
7636
8354
  console.log(
7637
- ` ${chalk10.bold("Coverage:")} ${fullyPct}% fully traced (${s.fullyTraced}/${s.total}), ${codePct}% with code (${s.withCode}/${s.total}), ${testPct}% with tests (${s.withTests}/${s.total})`
8355
+ ` ${chalk11.bold("Coverage:")} ${fullyPct}% fully traced (${s.fullyTraced}/${s.total}), ${codePct}% with code (${s.withCode}/${s.total}), ${testPct}% with tests (${s.withTests}/${s.total})`
7638
8356
  );
7639
8357
  }
7640
8358
  async function runTraceability(options) {
@@ -7642,7 +8360,7 @@ async function runTraceability(options) {
7642
8360
  const projectPath = process.cwd();
7643
8361
  const store = await loadGraphStore(projectPath);
7644
8362
  if (!store) handleNoStore(mode);
7645
- const graphModule = await import("./dist-QW5G3GX3.js");
8363
+ const graphModule = await import("./dist-RADHOOXG.js");
7646
8364
  const filterOptions = buildFilterOptions(options);
7647
8365
  const results = graphModule.queryTraceability(store, filterOptions);
7648
8366
  if (results.length === 0) handleEmptyResults(mode);
@@ -7650,14 +8368,14 @@ async function runTraceability(options) {
7650
8368
  console.log(JSON.stringify(results, null, 2));
7651
8369
  } else {
7652
8370
  console.log("");
7653
- console.log(chalk10.bold("Spec-to-Implementation Traceability"));
8371
+ console.log(chalk11.bold("Spec-to-Implementation Traceability"));
7654
8372
  console.log("");
7655
8373
  for (const result of results) printResultTable(result, mode);
7656
8374
  }
7657
8375
  process.exit(ExitCode.SUCCESS);
7658
8376
  }
7659
8377
  function createTraceabilityCommand() {
7660
- return new Command73("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) => {
8378
+ return new Command77("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) => {
7661
8379
  const globalOpts = cmd.optsWithGlobals();
7662
8380
  await runTraceability({
7663
8381
  spec: opts.spec,
@@ -7670,15 +8388,15 @@ function createTraceabilityCommand() {
7670
8388
  }
7671
8389
 
7672
8390
  // src/commands/uninstall.ts
7673
- import * as path56 from "path";
7674
- import { Command as Command74 } from "commander";
8391
+ import * as path60 from "path";
8392
+ import { Command as Command78 } from "commander";
7675
8393
  async function runUninstall(skillName, options) {
7676
8394
  const packageName = resolvePackageName(skillName);
7677
8395
  const shortName = extractSkillName(packageName);
7678
8396
  const globalDir = resolveGlobalSkillsDir();
7679
- const skillsDir = path56.dirname(globalDir);
7680
- const communityBase = path56.join(skillsDir, "community");
7681
- const lockfilePath = path56.join(communityBase, "skills-lock.json");
8397
+ const skillsDir = path60.dirname(globalDir);
8398
+ const communityBase = path60.join(skillsDir, "community");
8399
+ const lockfilePath = path60.join(communityBase, "skills-lock.json");
7682
8400
  const lockfile = readLockfile2(lockfilePath);
7683
8401
  const entry = lockfile.skills[packageName];
7684
8402
  if (!entry) {
@@ -7708,7 +8426,7 @@ async function runUninstall(skillName, options) {
7708
8426
  return result;
7709
8427
  }
7710
8428
  function createUninstallCommand() {
7711
- const cmd = new Command74("uninstall");
8429
+ const cmd = new Command78("uninstall");
7712
8430
  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) => {
7713
8431
  try {
7714
8432
  const result = await runUninstall(skill, opts);
@@ -7727,9 +8445,10 @@ function createUninstallCommand() {
7727
8445
  }
7728
8446
 
7729
8447
  // src/commands/uninstall-constraints.ts
7730
- import * as fs35 from "fs/promises";
7731
- import * as path57 from "path";
7732
- import { Command as Command75 } from "commander";
8448
+ init_dist();
8449
+ import * as fs40 from "fs/promises";
8450
+ import * as path61 from "path";
8451
+ import { Command as Command79 } from "commander";
7733
8452
  async function runUninstallConstraints(options) {
7734
8453
  const { packageName, configPath, lockfilePath } = options;
7735
8454
  const lockfileResult = await readLockfile(lockfilePath);
@@ -7749,7 +8468,7 @@ async function runUninstallConstraints(options) {
7749
8468
  }
7750
8469
  let localConfig;
7751
8470
  try {
7752
- const raw = await fs35.readFile(configPath, "utf-8");
8471
+ const raw = await fs40.readFile(configPath, "utf-8");
7753
8472
  localConfig = JSON.parse(raw);
7754
8473
  } catch (err) {
7755
8474
  return {
@@ -7786,11 +8505,11 @@ async function runUninstallConstraints(options) {
7786
8505
  };
7787
8506
  }
7788
8507
  function createUninstallConstraintsCommand() {
7789
- const cmd = new Command75("uninstall-constraints");
8508
+ const cmd = new Command79("uninstall-constraints");
7790
8509
  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) => {
7791
8510
  let configPath;
7792
8511
  if (opts.config) {
7793
- configPath = path57.resolve(opts.config);
8512
+ configPath = path61.resolve(opts.config);
7794
8513
  } else {
7795
8514
  const found = findConfigFile();
7796
8515
  if (!found.ok) {
@@ -7799,8 +8518,8 @@ function createUninstallConstraintsCommand() {
7799
8518
  }
7800
8519
  configPath = found.value;
7801
8520
  }
7802
- const projectRoot = path57.dirname(configPath);
7803
- const lockfilePath = path57.join(projectRoot, ".harness", "constraints.lock.json");
8521
+ const projectRoot = path61.dirname(configPath);
8522
+ const lockfilePath = path61.join(projectRoot, ".harness", "constraints.lock.json");
7804
8523
  const result = await runUninstallConstraints({
7805
8524
  packageName: name,
7806
8525
  configPath,
@@ -7825,13 +8544,13 @@ function createUninstallConstraintsCommand() {
7825
8544
  }
7826
8545
 
7827
8546
  // src/commands/update.ts
7828
- import { Command as Command76 } from "commander";
8547
+ import { Command as Command80 } from "commander";
7829
8548
  import { execFile, execFileSync as execFileSync6 } from "child_process";
7830
- import { realpathSync, existsSync as existsSync33, readFileSync as readFileSync20 } from "fs";
7831
- import { join as join46 } from "path";
8549
+ import { realpathSync, existsSync as existsSync36, readFileSync as readFileSync25 } from "fs";
8550
+ import { join as join47 } from "path";
7832
8551
  import { promisify } from "util";
7833
- import readline2 from "readline";
7834
- import chalk11 from "chalk";
8552
+ import readline3 from "readline";
8553
+ import chalk12 from "chalk";
7835
8554
  function detectPackageManager() {
7836
8555
  try {
7837
8556
  const argv1 = process.argv[1];
@@ -7889,26 +8608,35 @@ function getInstalledPackages(pm) {
7889
8608
  return ["@harness-engineering/cli", "@harness-engineering/core"];
7890
8609
  }
7891
8610
  }
7892
- function prompt(question) {
7893
- const rl = readline2.createInterface({
8611
+ function prompt2(question) {
8612
+ const rl = readline3.createInterface({
7894
8613
  input: process.stdin,
7895
8614
  output: process.stdout
7896
8615
  });
7897
- return new Promise((resolve32) => {
8616
+ return new Promise((resolve35) => {
7898
8617
  rl.question(question, (answer) => {
7899
8618
  rl.close();
7900
- resolve32(answer.trim().toLowerCase());
8619
+ resolve35(answer.trim().toLowerCase());
7901
8620
  });
7902
8621
  });
7903
8622
  }
8623
+ async function ensureTelemetryIfNeeded() {
8624
+ const cwd = process.cwd();
8625
+ const result = await ensureTelemetryConfigured(cwd);
8626
+ if (result.status === "pass") {
8627
+ logger.success(result.message);
8628
+ } else if (result.status === "warn") {
8629
+ logger.warn(result.message);
8630
+ }
8631
+ }
7904
8632
  function refreshHooks() {
7905
8633
  const cwd = process.cwd();
7906
- const configPath = join46(cwd, "harness.config.json");
7907
- if (!existsSync33(configPath)) return;
8634
+ const configPath = join47(cwd, "harness.config.json");
8635
+ if (!existsSync36(configPath)) return;
7908
8636
  let profile = "standard";
7909
- const profilePath = join46(cwd, ".harness", "hooks", "profile.json");
8637
+ const profilePath = join47(cwd, ".harness", "hooks", "profile.json");
7910
8638
  try {
7911
- const data = JSON.parse(readFileSync20(profilePath, "utf-8"));
8639
+ const data = JSON.parse(readFileSync25(profilePath, "utf-8"));
7912
8640
  if (data.profile && ["minimal", "standard", "strict"].includes(data.profile)) {
7913
8641
  profile = data.profile;
7914
8642
  }
@@ -7928,16 +8656,16 @@ function runLocalGraphScan() {
7928
8656
  execFileSync6("harness", ["graph", "scan", "."], { stdio: "inherit" });
7929
8657
  } catch {
7930
8658
  logger.warn("Graph scan failed. Run manually:");
7931
- console.log(` ${chalk11.cyan("harness graph scan .")}`);
8659
+ console.log(` ${chalk12.cyan("harness graph scan .")}`);
7932
8660
  }
7933
8661
  }
7934
8662
  async function offerRegeneration() {
7935
8663
  console.log("");
7936
- const regenAnswer = await prompt(
8664
+ const regenAnswer = await prompt2(
7937
8665
  "Regenerate slash commands, agent definitions, and knowledge graph? (Y/n) "
7938
8666
  );
7939
8667
  if (regenAnswer === "n" || regenAnswer === "no") return;
7940
- const scopeAnswer = await prompt("Generate for (G)lobal or (l)ocal project? (G/l) ");
8668
+ const scopeAnswer = await prompt2("Generate for (G)lobal or (l)ocal project? (G/l) ");
7941
8669
  const isGlobal = scopeAnswer !== "l" && scopeAnswer !== "local";
7942
8670
  try {
7943
8671
  execFileSync6("harness", ["generate", ...isGlobal ? ["--global"] : []], {
@@ -7945,7 +8673,7 @@ async function offerRegeneration() {
7945
8673
  });
7946
8674
  } catch {
7947
8675
  logger.warn("Generation failed. Run manually:");
7948
- console.log(` ${chalk11.cyan(`harness generate${isGlobal ? " --global" : ""}`)}`);
8676
+ console.log(` ${chalk12.cyan(`harness generate${isGlobal ? " --global" : ""}`)}`);
7949
8677
  }
7950
8678
  if (!isGlobal) {
7951
8679
  runLocalGraphScan();
@@ -8001,14 +8729,15 @@ async function runUpdateAction(opts, globalOpts) {
8001
8729
  if (!hasUpdates) {
8002
8730
  logger.success("All packages are up to date");
8003
8731
  refreshHooks();
8732
+ await ensureTelemetryIfNeeded();
8004
8733
  await offerRegeneration();
8005
8734
  process.exit(ExitCode.SUCCESS);
8006
8735
  }
8007
8736
  console.log("");
8008
8737
  for (const { pkg, current, latest } of outdated) {
8009
8738
  const shortName = pkg.replace("@harness-engineering/", "");
8010
- const currentStr = current ? chalk11.dim(`v${current}`) : chalk11.dim("not installed");
8011
- logger.info(`${shortName}: ${currentStr} \u2192 ${chalk11.green(`v${latest}`)}`);
8739
+ const currentStr = current ? chalk12.dim(`v${current}`) : chalk12.dim("not installed");
8740
+ logger.info(`${shortName}: ${currentStr} \u2192 ${chalk12.green(`v${latest}`)}`);
8012
8741
  }
8013
8742
  console.log("");
8014
8743
  }
@@ -8024,15 +8753,16 @@ async function runUpdateAction(opts, globalOpts) {
8024
8753
  } catch {
8025
8754
  console.log("");
8026
8755
  logger.error("Update failed. You can try manually:");
8027
- console.log(` ${chalk11.cyan(installCmd)}`);
8756
+ console.log(` ${chalk12.cyan(installCmd)}`);
8028
8757
  process.exit(ExitCode.ERROR);
8029
8758
  }
8030
8759
  refreshHooks();
8760
+ await ensureTelemetryIfNeeded();
8031
8761
  await offerRegeneration();
8032
8762
  process.exit(ExitCode.SUCCESS);
8033
8763
  }
8034
8764
  function createUpdateCommand() {
8035
- return new Command76("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(
8765
+ return new Command80("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(
8036
8766
  "--regenerate",
8037
8767
  "Only regenerate slash commands and agent definitions (skip package updates)"
8038
8768
  ).action(async (opts, cmd) => {
@@ -8042,15 +8772,15 @@ function createUpdateCommand() {
8042
8772
  }
8043
8773
 
8044
8774
  // src/commands/usage.ts
8045
- import { Command as Command77 } from "commander";
8775
+ import { Command as Command81 } from "commander";
8046
8776
  async function loadAndPriceRecords(cwd, includeClaudeSessions = false) {
8047
- const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-7EBSGAHX.js");
8777
+ const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-WCSJHQPK.js");
8048
8778
  const records = readCostRecords(cwd);
8049
8779
  if (includeClaudeSessions) {
8050
8780
  const ccRecords = parseCCRecords();
8051
8781
  records.push(...ccRecords);
8052
8782
  }
8053
- if (records.length === 0) return records;
8783
+ if (records.length === 0) return { records, pricingData: null };
8054
8784
  const pricingData = await loadPricingData(cwd);
8055
8785
  for (const record of records) {
8056
8786
  if (record.model && record.costMicroUSD == null) {
@@ -8058,7 +8788,14 @@ async function loadAndPriceRecords(cwd, includeClaudeSessions = false) {
8058
8788
  if (cost != null) record.costMicroUSD = cost;
8059
8789
  }
8060
8790
  }
8061
- return records;
8791
+ return { records, pricingData };
8792
+ }
8793
+ function computeCacheSavings(cacheReadTokens, model, pricingData) {
8794
+ if (!cacheReadTokens || !model || !pricingData) return null;
8795
+ const pricing = pricingData.get(model);
8796
+ if (!pricing || pricing.cacheReadPer1M == null) return null;
8797
+ const savingsUSD = cacheReadTokens / 1e6 * (pricing.inputPer1M - pricing.cacheReadPer1M);
8798
+ return Math.round(savingsUSD * 1e6);
8062
8799
  }
8063
8800
  function formatMicroUSD(microUSD) {
8064
8801
  if (microUSD == null) return "N/A";
@@ -8086,7 +8823,10 @@ function registerDailyCommand(usage) {
8086
8823
  const globalOpts = cmd.optsWithGlobals();
8087
8824
  const days = Math.min(Math.max(parseInt(opts.days, 10) || 7, 1), 90);
8088
8825
  const cwd = process.cwd();
8089
- const records = await loadAndPriceRecords(cwd, globalOpts.includeClaudeSessions);
8826
+ const { records, pricingData } = await loadAndPriceRecords(
8827
+ cwd,
8828
+ globalOpts.includeClaudeSessions
8829
+ );
8090
8830
  if (records.length === 0) {
8091
8831
  if (globalOpts.json) {
8092
8832
  console.log(JSON.stringify([]));
@@ -8095,23 +8835,25 @@ function registerDailyCommand(usage) {
8095
8835
  }
8096
8836
  return;
8097
8837
  }
8098
- const { aggregateByDay } = await import("./dist-7EBSGAHX.js");
8838
+ const { aggregateByDay } = await import("./dist-WCSJHQPK.js");
8099
8839
  const dailyData = aggregateByDay(records);
8100
8840
  const limited = dailyData.slice(0, days);
8101
8841
  if (globalOpts.json) {
8102
8842
  const enriched = limited.map((day) => {
8103
8843
  const hitRate = computeCacheHitRate(day.cacheReadTokens, day.tokens.inputTokens);
8844
+ const savings = computeCacheSavings(day.cacheReadTokens, day.models[0], pricingData);
8104
8845
  return {
8105
8846
  ...day,
8106
- ...hitRate != null ? { cacheHitRate: Math.round(hitRate * 1e3) / 1e3 } : {}
8847
+ ...hitRate != null ? { cacheHitRate: Math.round(hitRate * 1e3) / 1e3 } : {},
8848
+ ...savings != null ? { cacheSavingsMicroUSD: savings } : {}
8107
8849
  };
8108
8850
  });
8109
8851
  console.log(JSON.stringify(enriched, null, 2));
8110
8852
  return;
8111
8853
  }
8112
8854
  const hasCacheData = limited.some((d) => d.cacheReadTokens != null && d.cacheReadTokens > 0);
8113
- const cacheHeader = hasCacheData ? " | Cache " : "";
8114
- const cacheDivider = hasCacheData ? " | ------" : "";
8855
+ const cacheHeader = hasCacheData ? " | Cache | Saved " : "";
8856
+ const cacheDivider = hasCacheData ? " | ------ | -------" : "";
8115
8857
  const header = "Date | Sessions | Input | Output | Model(s) | Cost " + cacheHeader;
8116
8858
  const divider = "-------------|----------|-----------|-----------|------------------------------|-------" + cacheDivider;
8117
8859
  logger.info(header);
@@ -8125,7 +8867,10 @@ function registerDailyCommand(usage) {
8125
8867
  const cost = formatMicroUSD(day.costMicroUSD);
8126
8868
  const cacheCol = hasCacheData ? (() => {
8127
8869
  const rate = computeCacheHitRate(day.cacheReadTokens, day.tokens.inputTokens);
8128
- return " | " + (rate != null ? formatPercent(rate).padStart(5) : " -");
8870
+ const savings = computeCacheSavings(day.cacheReadTokens, day.models[0], pricingData);
8871
+ const rateStr = rate != null ? formatPercent(rate).padStart(5) : " -";
8872
+ const savingsStr = savings != null ? formatMicroUSD(savings).padStart(7) : " N/A";
8873
+ return " | " + rateStr + " | " + savingsStr;
8129
8874
  })() : "";
8130
8875
  logger.info(
8131
8876
  `${date} | ${sessions} | ${input} | ${output} | ${models} | ${cost}${cacheCol}`
@@ -8138,7 +8883,7 @@ function registerSessionsCommand(usage) {
8138
8883
  const globalOpts = cmd.optsWithGlobals();
8139
8884
  const limit = Math.min(Math.max(parseInt(opts.limit, 10) || 10, 1), 100);
8140
8885
  const cwd = process.cwd();
8141
- const records = await loadAndPriceRecords(cwd, globalOpts.includeClaudeSessions);
8886
+ const { records } = await loadAndPriceRecords(cwd, globalOpts.includeClaudeSessions);
8142
8887
  if (records.length === 0) {
8143
8888
  if (globalOpts.json) {
8144
8889
  console.log(JSON.stringify([]));
@@ -8147,7 +8892,7 @@ function registerSessionsCommand(usage) {
8147
8892
  }
8148
8893
  return;
8149
8894
  }
8150
- const { aggregateBySession } = await import("./dist-7EBSGAHX.js");
8895
+ const { aggregateBySession } = await import("./dist-WCSJHQPK.js");
8151
8896
  const sessionData = aggregateBySession(records);
8152
8897
  const limited = sessionData.slice(0, limit);
8153
8898
  if (globalOpts.json) {
@@ -8189,7 +8934,7 @@ function reportSessionNotFound(id, sessionData, useJson) {
8189
8934
  }
8190
8935
  }
8191
8936
  }
8192
- function printSessionDetail(match) {
8937
+ function printSessionDetail(match, pricingData) {
8193
8938
  logger.info(`Session: ${match.sessionId}`);
8194
8939
  logger.info(`Started: ${match.firstTimestamp}`);
8195
8940
  logger.info(`Ended: ${match.lastTimestamp}`);
@@ -8208,9 +8953,13 @@ function printSessionDetail(match) {
8208
8953
  }
8209
8954
  const hitRate = computeCacheHitRate(match.cacheReadTokens, match.tokens.inputTokens);
8210
8955
  if (hitRate != null) {
8956
+ const savings = computeCacheSavings(match.cacheReadTokens, match.model, pricingData);
8211
8957
  logger.info("");
8212
8958
  logger.info("Cache Performance:");
8213
8959
  logger.info(` Cache hit rate: ${formatPercent(hitRate)}`);
8960
+ if (savings != null) {
8961
+ logger.info(` Estimated savings: ${formatMicroUSD(savings)}`);
8962
+ }
8214
8963
  }
8215
8964
  logger.info("");
8216
8965
  logger.info(`Cost: ${formatMicroUSD(match.costMicroUSD)}`);
@@ -8219,8 +8968,11 @@ function registerSessionCommand(usage) {
8219
8968
  usage.command("session <id>").description("Show detailed token breakdown for a specific session").action(async (id, _opts, cmd) => {
8220
8969
  const globalOpts = cmd.optsWithGlobals();
8221
8970
  const cwd = process.cwd();
8222
- const records = await loadAndPriceRecords(cwd, globalOpts.includeClaudeSessions);
8223
- const { aggregateBySession } = await import("./dist-7EBSGAHX.js");
8971
+ const { records, pricingData } = await loadAndPriceRecords(
8972
+ cwd,
8973
+ globalOpts.includeClaudeSessions
8974
+ );
8975
+ const { aggregateBySession } = await import("./dist-WCSJHQPK.js");
8224
8976
  const sessionData = aggregateBySession(records);
8225
8977
  const match = sessionData.find((s) => s.sessionId === id);
8226
8978
  if (!match) {
@@ -8230,21 +8982,23 @@ function registerSessionCommand(usage) {
8230
8982
  }
8231
8983
  if (globalOpts.json) {
8232
8984
  const hitRate = computeCacheHitRate(match.cacheReadTokens, match.tokens.inputTokens);
8985
+ const savings = computeCacheSavings(match.cacheReadTokens, match.model, pricingData);
8233
8986
  const enriched = {
8234
8987
  ...match,
8235
- ...hitRate != null ? { cacheHitRate: Math.round(hitRate * 1e3) / 1e3 } : {}
8988
+ ...hitRate != null ? { cacheHitRate: Math.round(hitRate * 1e3) / 1e3 } : {},
8989
+ ...savings != null ? { cacheSavingsMicroUSD: savings } : {}
8236
8990
  };
8237
8991
  console.log(JSON.stringify(enriched, null, 2));
8238
8992
  return;
8239
8993
  }
8240
- printSessionDetail(match);
8994
+ printSessionDetail(match, pricingData);
8241
8995
  });
8242
8996
  }
8243
8997
  function registerLatestCommand(usage) {
8244
8998
  usage.command("latest").description("Show the most recently completed session cost summary").action(async (_opts, cmd) => {
8245
8999
  const globalOpts = cmd.optsWithGlobals();
8246
9000
  const cwd = process.cwd();
8247
- const records = await loadAndPriceRecords(cwd, globalOpts.includeClaudeSessions);
9001
+ const { records } = await loadAndPriceRecords(cwd, globalOpts.includeClaudeSessions);
8248
9002
  if (records.length === 0) {
8249
9003
  if (globalOpts.json) {
8250
9004
  console.log(JSON.stringify({ error: "No usage data found" }));
@@ -8253,7 +9007,7 @@ function registerLatestCommand(usage) {
8253
9007
  }
8254
9008
  return;
8255
9009
  }
8256
- const { aggregateBySession } = await import("./dist-7EBSGAHX.js");
9010
+ const { aggregateBySession } = await import("./dist-WCSJHQPK.js");
8257
9011
  const sessionData = aggregateBySession(records);
8258
9012
  const latest = sessionData[0];
8259
9013
  if (!latest) {
@@ -8279,7 +9033,7 @@ function registerLatestCommand(usage) {
8279
9033
  });
8280
9034
  }
8281
9035
  function createUsageCommand() {
8282
- const usage = new Command77("usage").description("Token usage and cost tracking");
9036
+ const usage = new Command81("usage").description("Token usage and cost tracking");
8283
9037
  usage.option(
8284
9038
  "--include-claude-sessions",
8285
9039
  "Include Claude Code session data from ~/.claude/projects/"
@@ -8292,15 +9046,17 @@ function createUsageCommand() {
8292
9046
  }
8293
9047
 
8294
9048
  // src/commands/validate.ts
8295
- import { Command as Command78 } from "commander";
8296
- import * as path58 from "path";
9049
+ init_dist();
9050
+ init_dist();
9051
+ import { Command as Command82 } from "commander";
9052
+ import * as path62 from "path";
8297
9053
  async function runValidate(options) {
8298
9054
  const configResult = resolveConfig(options.configPath);
8299
9055
  if (!configResult.ok) {
8300
9056
  return configResult;
8301
9057
  }
8302
9058
  const config = configResult.value;
8303
- const cwd = options.cwd ?? (options.configPath ? path58.dirname(path58.resolve(options.configPath)) : process.cwd());
9059
+ const cwd = options.cwd ?? (options.configPath ? path62.dirname(path62.resolve(options.configPath)) : process.cwd());
8304
9060
  const result = {
8305
9061
  valid: true,
8306
9062
  checks: {
@@ -8310,7 +9066,7 @@ async function runValidate(options) {
8310
9066
  },
8311
9067
  issues: []
8312
9068
  };
8313
- const agentsMapPath = path58.resolve(cwd, config.agentsMapPath);
9069
+ const agentsMapPath = path62.resolve(cwd, config.agentsMapPath);
8314
9070
  const agentsResult = await validateAgentsMap(agentsMapPath);
8315
9071
  if (agentsResult.ok) {
8316
9072
  result.checks.agentsMap = true;
@@ -8346,6 +9102,26 @@ async function runValidate(options) {
8346
9102
  });
8347
9103
  }
8348
9104
  result.checks.fileStructure = true;
9105
+ if (options.agentConfigs) {
9106
+ const agentCfg = await validateAgentConfigs(cwd, {
9107
+ strict: options.strict === true,
9108
+ ...options.agnixBin !== void 0 && { agnixBin: options.agnixBin }
9109
+ });
9110
+ result.agentConfigs = agentCfg;
9111
+ result.checks.agentConfigs = agentCfg.valid;
9112
+ if (!agentCfg.valid) result.valid = false;
9113
+ for (const finding of agentCfg.issues) {
9114
+ result.issues.push({
9115
+ check: "agentConfigs",
9116
+ file: finding.file,
9117
+ ...finding.line !== void 0 && { line: finding.line },
9118
+ ruleId: finding.ruleId,
9119
+ severity: finding.severity,
9120
+ message: finding.message,
9121
+ ...finding.suggestion !== void 0 && { suggestion: finding.suggestion }
9122
+ });
9123
+ }
9124
+ }
8349
9125
  return Ok(result);
8350
9126
  }
8351
9127
  function resolveValidateMode(globalOpts) {
@@ -8355,11 +9131,11 @@ function resolveValidateMode(globalOpts) {
8355
9131
  return OutputMode.TEXT;
8356
9132
  }
8357
9133
  async function printCrossCheckWarnings(mode) {
8358
- const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-Y4PDR63C.js");
9134
+ const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-ZOWFA3DB.js");
8359
9135
  const cwd = process.cwd();
8360
9136
  const crossResult = await runCrossCheck2({
8361
- specsDir: path58.join(cwd, "docs", "specs"),
8362
- plansDir: path58.join(cwd, "docs", "plans"),
9137
+ specsDir: path62.join(cwd, "docs", "specs"),
9138
+ plansDir: path62.join(cwd, "docs", "plans"),
8363
9139
  projectPath: cwd
8364
9140
  });
8365
9141
  if (!crossResult.ok || crossResult.value.warnings === 0) return;
@@ -8371,37 +9147,59 @@ async function printCrossCheckWarnings(mode) {
8371
9147
  ${crossResult.value.warnings} warnings`);
8372
9148
  }
8373
9149
  function createValidateCommand3() {
8374
- const command = new Command78("validate").description("Run all validation checks").option("--cross-check", "Run cross-artifact consistency validation").action(async (opts, cmd) => {
8375
- const globalOpts = cmd.optsWithGlobals();
8376
- const mode = resolveValidateMode(globalOpts);
8377
- const formatter = new OutputFormatter(mode);
8378
- const result = await runValidate({
8379
- configPath: globalOpts.config,
8380
- json: globalOpts.json,
8381
- verbose: globalOpts.verbose,
8382
- quiet: globalOpts.quiet
8383
- });
8384
- if (!result.ok) {
8385
- if (mode === OutputMode.JSON) console.log(JSON.stringify({ error: result.error.message }));
8386
- else logger.error(result.error.message);
8387
- process.exit(result.error.exitCode);
8388
- }
8389
- if (opts.crossCheck) await printCrossCheckWarnings(mode);
8390
- const output = formatter.formatValidation({
8391
- valid: result.value.valid,
8392
- issues: result.value.issues
8393
- });
8394
- if (output) console.log(output);
8395
- process.exit(result.value.valid ? ExitCode.SUCCESS : ExitCode.VALIDATION_FAILED);
8396
- });
9150
+ const command = new Command82("validate").description("Run all validation checks").option("--cross-check", "Run cross-artifact consistency validation").option(
9151
+ "--agent-configs",
9152
+ "Validate agent configs (CLAUDE.md, hooks, skills) via agnix or built-in fallback rules"
9153
+ ).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()));
8397
9154
  return command;
8398
9155
  }
9156
+ async function runValidateAction(opts, globalOpts) {
9157
+ const mode = resolveValidateMode(globalOpts);
9158
+ const formatter = new OutputFormatter(mode);
9159
+ const result = await runValidate({
9160
+ ...typeof globalOpts.config === "string" && { configPath: globalOpts.config },
9161
+ json: globalOpts.json === true,
9162
+ verbose: globalOpts.verbose === true,
9163
+ quiet: globalOpts.quiet === true,
9164
+ agentConfigs: opts.agentConfigs === true,
9165
+ strict: opts.strict === true,
9166
+ ...typeof opts.agnixBin === "string" && { agnixBin: opts.agnixBin }
9167
+ });
9168
+ if (!result.ok) {
9169
+ if (mode === OutputMode.JSON) console.log(JSON.stringify({ error: result.error.message }));
9170
+ else logger.error(result.error.message);
9171
+ process.exit(result.error.exitCode);
9172
+ }
9173
+ if (opts.crossCheck) await printCrossCheckWarnings(mode);
9174
+ emitValidateOutput(result.value, mode, formatter);
9175
+ process.exit(result.value.valid ? ExitCode.SUCCESS : ExitCode.VALIDATION_FAILED);
9176
+ }
9177
+ function emitValidateOutput(value, mode, formatter) {
9178
+ if (mode === OutputMode.JSON) {
9179
+ console.log(JSON.stringify(value, null, 2));
9180
+ return;
9181
+ }
9182
+ const output = formatter.formatValidation({ valid: value.valid, issues: value.issues });
9183
+ if (output) console.log(output);
9184
+ if (value.agentConfigs) printAgentConfigSummary(value.agentConfigs, mode);
9185
+ }
9186
+ function printAgentConfigSummary(cfg, mode) {
9187
+ if (mode === OutputMode.QUIET) return;
9188
+ const engineLabel = cfg.engine === "agnix" ? "agnix" : "built-in fallback rules";
9189
+ const note = cfg.fellBackBecause ? ` (${cfg.fellBackBecause})` : "";
9190
+ console.log(`
9191
+ Agent configs checked via ${engineLabel}${note}`);
9192
+ if (cfg.engine === "fallback" && cfg.fellBackBecause === "binary-not-found") {
9193
+ console.log(" Install agnix for 385+ rule coverage: https://github.com/agent-sh/agnix");
9194
+ }
9195
+ }
8399
9196
 
8400
9197
  // src/commands/_registry.ts
8401
9198
  var commandCreators = [
8402
9199
  createAddCommand,
8403
9200
  createAdoptionCommand,
8404
9201
  createAgentCommand,
9202
+ createAuditProtectedCommand,
8405
9203
  createBlueprintCommand,
8406
9204
  createCheckArchCommand,
8407
9205
  createCheckDepsCommand,
@@ -8434,6 +9232,7 @@ var commandCreators = [
8434
9232
  createPerfCommand,
8435
9233
  createPersonaCommand,
8436
9234
  createPredictCommand,
9235
+ createPublishAnalysesCommand,
8437
9236
  createQueryCommand,
8438
9237
  createRecommendCommand,
8439
9238
  createScanCommand,
@@ -8444,6 +9243,7 @@ var commandCreators = [
8444
9243
  createSkillCommand,
8445
9244
  createSnapshotCommand,
8446
9245
  createStateCommand,
9246
+ createSyncAnalysesCommand,
8447
9247
  createTaintCommand,
8448
9248
  createTelemetryCommand,
8449
9249
  createTraceabilityCommand,
@@ -8456,7 +9256,7 @@ var commandCreators = [
8456
9256
 
8457
9257
  // src/index.ts
8458
9258
  function createProgram() {
8459
- const program = new Command79();
9259
+ const program = new Command83();
8460
9260
  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");
8461
9261
  for (const creator of commandCreators) {
8462
9262
  program.addCommand(creator());