@harness-engineering/cli 1.24.1 → 1.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) 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-HNIO6AUX.js +15 -0
  27. package/dist/{assess-project-7JZCVM7D.js → assess-project-6MV5TNY3.js} +2 -1
  28. package/dist/bin/harness-mcp.js +19 -18
  29. package/dist/bin/harness.js +48 -31
  30. package/dist/{check-phase-gate-YQHAH4LL.js → check-phase-gate-VCBQHQAC.js} +6 -5
  31. package/dist/{chunk-Q3XYV5UC.js → chunk-47N6R2F4.js} +5 -1
  32. package/dist/chunk-4NK7Z3BP.js +43 -0
  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-ZKVLBOYA.js → chunk-AZT567I6.js} +1495 -662
  37. package/dist/{chunk-TDLOFNNQ.js → chunk-BOQRTARD.js} +1 -1
  38. package/dist/{chunk-NU3BPEDR.js → chunk-CXJTVICF.js} +5 -5
  39. package/dist/{chunk-LOUH2LIC.js → chunk-EHRZMOQ2.js} +5 -1
  40. package/dist/{chunk-IDZNPTYD.js → chunk-EPUKTTJZ.js} +6 -1
  41. package/dist/{chunk-C5FTJ3EU.js → chunk-F23H3U5U.js} +62 -1
  42. package/dist/{chunk-BWWNQPPO.js → chunk-FES2YEQU.js} +10 -10
  43. package/dist/{chunk-W2OZK3KC.js → chunk-FIQL2HND.js} +6 -2
  44. package/dist/{chunk-FP53DDB5.js → chunk-FSNPBT74.js} +5 -1
  45. package/dist/{chunk-TUJAHI22.js → chunk-GEEYCQDS.js} +14 -10
  46. package/dist/{chunk-ZOVATVQC.js → chunk-HIK6OEUF.js} +6 -1
  47. package/dist/chunk-K2SON7TI.js +5382 -0
  48. package/dist/chunk-KFQGP6VL.js +33 -0
  49. package/dist/{chunk-JGMCHJ6B.js → chunk-M55DGGF3.js} +10 -6
  50. package/dist/{chunk-PCWYYFBP.js → chunk-MI6MA6OP.js} +16906 -13135
  51. package/dist/{chunk-YDRB55Q4.js → chunk-P7PANON5.js} +5 -1
  52. package/dist/chunk-RL4VMEXL.js +53 -0
  53. package/dist/{chunk-XNEMDKV5.js → chunk-RRHUCDRD.js} +1 -1
  54. package/dist/{chunk-LY3YVKXL.js → chunk-RX7TUMBR.js} +606 -175
  55. package/dist/{chunk-MS6KDQW7.js → chunk-TYV4EUAD.js} +13 -9
  56. package/dist/{chunk-2DAMN7ED.js → chunk-UQZBZINS.js} +6 -6
  57. package/dist/{chunk-HVFCCARH.js → chunk-UV3BZMGT.js} +64 -3
  58. package/dist/{chunk-AVVJ5EUU.js → chunk-WIQA4BSH.js} +12 -5
  59. package/dist/{chunk-UQEUYRBP.js → chunk-WWACIIBA.js} +1 -1
  60. package/dist/{chunk-LVYPPKZI.js → chunk-XTITAVUR.js} +5 -1
  61. package/dist/{chunk-JT732X6S.js → chunk-YTP2UDPV.js} +3 -3
  62. package/dist/ci-workflow-RTM7VVTD.js +10 -0
  63. package/dist/{constants-5JGUXPEK.js → constants-P4M3C2T3.js} +1 -0
  64. package/dist/{create-skill-QCXINA5Q.js → create-skill-6QWJHQYS.js} +3 -2
  65. package/dist/{dist-666AAZQ6.js → dist-3EWNRFFQ.js} +6 -1
  66. package/dist/{dist-RRMRZRV7.js → dist-ABTKRYZH.js} +1 -0
  67. package/dist/{dist-QW5G3GX3.js → dist-RADHOOXG.js} +4 -1
  68. package/dist/{dist-TZQUURSP.js → dist-WCSJHQPK.js} +108 -3
  69. package/dist/docs-UBOGGHTY.js +16 -0
  70. package/dist/engine-MJJAP5CH.js +10 -0
  71. package/dist/entropy-EMSXF2PX.js +14 -0
  72. package/dist/{feedback-RSPUJCJJ.js → feedback-ZLUX72HD.js} +3 -2
  73. package/dist/{generate-agent-definitions-OLYWXTYH.js → generate-agent-definitions-AWLPJ27C.js} +6 -5
  74. package/dist/{glob-helper-SRXMZPKM.js → glob-helper-VCQXK5XY.js} +2 -0
  75. package/dist/{graph-loader-M6FXJAKK.js → graph-loader-JHQVQRUS.js} +2 -1
  76. package/dist/index.d.ts +21 -15
  77. package/dist/index.js +36 -33
  78. package/dist/loader-JVSJZSWZ.js +12 -0
  79. package/dist/mcp-2553PNUC.js +38 -0
  80. package/dist/{performance-VVWOIJTE.js → performance-7AGWJUY4.js} +7 -6
  81. package/dist/review-pipeline-ZWVQJTJX.js +13 -0
  82. package/dist/{runner-TY7DJGQV.js → runner-AAEF2TXY.js} +1 -0
  83. package/dist/runtime-D6YUQPP2.js +11 -0
  84. package/dist/scan-MPJ6JHUY.js +9 -0
  85. package/dist/security-JLZUAQYT.js +14 -0
  86. package/dist/skill-executor-MOCUIAYS.js +9 -0
  87. package/dist/templates/base/.agnix.toml +16 -0
  88. package/dist/templates/orchestrator/WORKFLOW.md +55 -7
  89. package/dist/templates/orchestrator/template.json +1 -0
  90. package/dist/tool-tiers-7QGZ3FKY.js +98 -0
  91. package/dist/validate-TIIHRPMA.js +14 -0
  92. package/dist/validate-cross-check-ZOWFA3DB.js +10 -0
  93. package/dist/{version-KFFPOQAX.js → version-CUI434OP.js} +1 -0
  94. package/package.json +5 -5
  95. package/dist/agents-md-MMVRF77L.js +0 -9
  96. package/dist/architecture-JAEDPYQZ.js +0 -14
  97. package/dist/chunk-5LMZA5LZ.js +0 -38
  98. package/dist/chunk-OZIHPMA7.js +0 -5171
  99. package/dist/ci-workflow-HDH4LCOF.js +0 -9
  100. package/dist/docs-6SPJYTRR.js +0 -15
  101. package/dist/engine-CSITRE3J.js +0 -9
  102. package/dist/entropy-FZP643BK.js +0 -13
  103. package/dist/loader-P2P7CV2A.js +0 -11
  104. package/dist/mcp-JZG22CYT.js +0 -37
  105. package/dist/review-pipeline-XSRO7HYZ.js +0 -9
  106. package/dist/runtime-VMOIIP6B.js +0 -10
  107. package/dist/security-4AQ46P3H.js +0 -9
  108. package/dist/skill-executor-GA7BDX3F.js +0 -8
  109. package/dist/validate-T7UTXLEQ.js +0 -13
  110. package/dist/validate-cross-check-M4NP2UF5.js +0 -9
@@ -1,22 +1,25 @@
1
+ import {
2
+ generateAgentsMd
3
+ } from "./chunk-5BQ5BOJL.js";
1
4
  import {
2
5
  generateCIWorkflow
3
- } from "./chunk-LOUH2LIC.js";
6
+ } from "./chunk-EHRZMOQ2.js";
7
+ import {
8
+ TemplateEngine
9
+ } from "./chunk-FSNPBT74.js";
4
10
  import {
5
11
  generate,
6
12
  validate
7
13
  } from "./chunk-CQ553GZO.js";
8
14
  import {
9
15
  generateRuntime
10
- } from "./chunk-LM5Z2WCA.js";
11
- import {
12
- generateAgentsMd
13
- } from "./chunk-I4QR3HNH.js";
16
+ } from "./chunk-6VZQJ5CX.js";
14
17
  import {
15
18
  runPersona
16
19
  } from "./chunk-GISMXMVL.js";
17
20
  import {
18
21
  executeSkill
19
- } from "./chunk-UQEUYRBP.js";
22
+ } from "./chunk-WWACIIBA.js";
20
23
  import {
21
24
  ALLOWED_PERSONA_COMMANDS
22
25
  } from "./chunk-TEFCFC4H.js";
@@ -25,21 +28,21 @@ import {
25
28
  OutputMode,
26
29
  createCheckPhaseGateCommand,
27
30
  findFiles
28
- } from "./chunk-W2OZK3KC.js";
31
+ } from "./chunk-FIQL2HND.js";
29
32
  import {
30
33
  createGenerateAgentDefinitionsCommand,
31
34
  generateAgentDefinitions
32
- } from "./chunk-NV4FPO7J.js";
35
+ } from "./chunk-AT74HEQM.js";
33
36
  import {
34
37
  toKebabCase
35
38
  } from "./chunk-KET4QQZB.js";
36
39
  import {
37
40
  listPersonas,
38
41
  loadPersona
39
- } from "./chunk-YDRB55Q4.js";
42
+ } from "./chunk-P7PANON5.js";
40
43
  import {
41
- TemplateEngine
42
- } from "./chunk-FP53DDB5.js";
44
+ createScanCommand
45
+ } from "./chunk-4NK7Z3BP.js";
43
46
  import {
44
47
  appendFrameworkAgents,
45
48
  captureHealthSnapshot,
@@ -53,11 +56,11 @@ import {
53
56
  loadOrRebuildIndex,
54
57
  persistToolingConfig,
55
58
  recommend
56
- } from "./chunk-LY3YVKXL.js";
59
+ } from "./chunk-RX7TUMBR.js";
57
60
  import {
58
61
  findConfigFile,
59
62
  resolveConfig
60
- } from "./chunk-AVVJ5EUU.js";
63
+ } from "./chunk-WIQA4BSH.js";
61
64
  import {
62
65
  VALID_PLATFORMS
63
66
  } from "./chunk-3ISINLYT.js";
@@ -71,7 +74,7 @@ import {
71
74
  } from "./chunk-6B6UN6SG.js";
72
75
  import {
73
76
  loadGraphStore
74
- } from "./chunk-TDLOFNNQ.js";
77
+ } from "./chunk-BOQRTARD.js";
75
78
  import {
76
79
  CLI_VERSION
77
80
  } from "./chunk-BM3PWGXQ.js";
@@ -81,11 +84,14 @@ import {
81
84
  BaselineManager,
82
85
  BlueprintGenerator,
83
86
  BundleSchema,
87
+ CINotifier,
84
88
  CriticalPathResolver,
85
89
  EntropyAnalyzer,
90
+ GitHubIssuesSyncAdapter,
86
91
  PredictionEngine,
87
92
  ProjectScanner,
88
93
  SecurityScanner,
94
+ SecurityTimelineManager,
89
95
  SpecImpactEstimator,
90
96
  TimelineManager,
91
97
  TypeScriptParser,
@@ -107,16 +113,20 @@ import {
107
113
  detectDeadCode,
108
114
  detectDocDrift,
109
115
  diff,
116
+ dist_exports,
110
117
  extractBundle,
111
118
  generateSuggestions,
112
119
  getOrCreateInstallId,
120
+ init_dist,
113
121
  listStreams,
114
122
  listTaintedSessions,
115
123
  loadState,
116
124
  loadStreamIndex,
125
+ loadTrackerSyncConfig,
117
126
  mapInjectionFindings,
118
127
  mapSecurityFindings,
119
128
  parseDiff,
129
+ parseFileRegions,
120
130
  parseManifest,
121
131
  parseSecurityConfig,
122
132
  pruneLearnings,
@@ -132,35 +142,41 @@ import {
132
142
  runReviewPipeline,
133
143
  scanForInjection,
134
144
  setActiveStream,
145
+ validateAgentConfigs,
135
146
  validateAgentsMap,
136
147
  validateDependencies,
137
148
  validateKnowledgeMap,
138
149
  writeConfig,
139
150
  writeLockfile
140
- } from "./chunk-PCWYYFBP.js";
151
+ } from "./chunk-MI6MA6OP.js";
141
152
  import {
142
153
  Err,
143
154
  Ok
144
- } from "./chunk-5LMZA5LZ.js";
155
+ } from "./chunk-RL4VMEXL.js";
145
156
  import {
146
157
  createCreateSkillCommand
147
- } from "./chunk-XNEMDKV5.js";
158
+ } from "./chunk-RRHUCDRD.js";
148
159
  import {
149
160
  logger
150
161
  } from "./chunk-EBJQ6N4M.js";
151
162
  import {
152
163
  SkillMetadataSchema
153
- } from "./chunk-ZOVATVQC.js";
164
+ } from "./chunk-HIK6OEUF.js";
154
165
  import {
155
166
  CLIError,
156
167
  ExitCode,
157
168
  handleError
158
169
  } from "./chunk-3WGJMBKH.js";
170
+ import {
171
+ __require,
172
+ __toCommonJS
173
+ } from "./chunk-KFQGP6VL.js";
159
174
 
160
175
  // src/index.ts
161
- import { Command as Command80 } from "commander";
176
+ import { Command as Command83 } from "commander";
162
177
 
163
178
  // src/commands/add.ts
179
+ init_dist();
164
180
  import { Command } from "commander";
165
181
  import * as fs from "fs";
166
182
  import * as path from "path";
@@ -248,7 +264,7 @@ function addDoc(name, cwd, configResult, created) {
248
264
  return Ok(void 0);
249
265
  }
250
266
  async function addSkill(name, cwd, created) {
251
- const { generateSkillFiles: generateSkillFiles2 } = await import("./create-skill-QCXINA5Q.js");
267
+ const { generateSkillFiles: generateSkillFiles2 } = await import("./create-skill-6QWJHQYS.js");
252
268
  generateSkillFiles2({
253
269
  name,
254
270
  description: `${name} skill`,
@@ -346,7 +362,7 @@ function registerSkillsCommand(adoption) {
346
362
  const globalOpts = cmd.optsWithGlobals();
347
363
  const limit = Math.max(parseInt(opts.limit, 10) || 20, 1);
348
364
  const cwd = process.cwd();
349
- const { readAdoptionRecords, aggregateBySkill } = await import("./dist-TZQUURSP.js");
365
+ const { readAdoptionRecords, aggregateBySkill } = await import("./dist-WCSJHQPK.js");
350
366
  const records = readAdoptionRecords(cwd);
351
367
  if (records.length === 0) {
352
368
  if (globalOpts.json) {
@@ -382,7 +398,7 @@ function registerRecentCommand(adoption) {
382
398
  const globalOpts = cmd.optsWithGlobals();
383
399
  const limit = Math.max(parseInt(opts.limit, 10) || 20, 1);
384
400
  const cwd = process.cwd();
385
- const { readAdoptionRecords } = await import("./dist-TZQUURSP.js");
401
+ const { readAdoptionRecords } = await import("./dist-WCSJHQPK.js");
386
402
  const records = readAdoptionRecords(cwd);
387
403
  if (records.length === 0) {
388
404
  if (globalOpts.json) {
@@ -419,7 +435,7 @@ function registerSkillCommand(adoption) {
419
435
  adoption.command("skill <name>").description("Show detail for a specific skill").action(async (name, _opts, cmd) => {
420
436
  const globalOpts = cmd.optsWithGlobals();
421
437
  const cwd = process.cwd();
422
- const { readAdoptionRecords, aggregateBySkill } = await import("./dist-TZQUURSP.js");
438
+ const { readAdoptionRecords, aggregateBySkill } = await import("./dist-WCSJHQPK.js");
423
439
  const records = readAdoptionRecords(cwd);
424
440
  const skillRecords = records.filter((r) => r.skill === name);
425
441
  if (skillRecords.length === 0) {
@@ -485,6 +501,8 @@ function createAdoptionCommand() {
485
501
  import { Command as Command5 } from "commander";
486
502
 
487
503
  // src/commands/agent/run.ts
504
+ init_dist();
505
+ init_dist();
488
506
  import { Command as Command3 } from "commander";
489
507
  import * as path2 from "path";
490
508
  import * as childProcess from "child_process";
@@ -606,6 +624,7 @@ function createRunCommand() {
606
624
  }
607
625
 
608
626
  // src/commands/agent/review.ts
627
+ init_dist();
609
628
  import { Command as Command4 } from "commander";
610
629
  import { execSync } from "child_process";
611
630
  async function runAgentReview(options) {
@@ -654,7 +673,9 @@ async function runAgentReview(options) {
654
673
  comment: options.comment ?? false,
655
674
  ci: options.ci ?? false,
656
675
  deep: options.deep ?? false,
657
- noMechanical: options.noMechanical ?? false
676
+ noMechanical: options.noMechanical ?? false,
677
+ ...options.thorough ? { thorough: true } : {},
678
+ ...options.isolated ? { isolated: true } : {}
658
679
  },
659
680
  config
660
681
  });
@@ -700,7 +721,10 @@ function printReviewResult(result, mode) {
700
721
  }
701
722
  }
702
723
  function createReviewCommand() {
703
- 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) => {
704
728
  const globalOpts = cmd.optsWithGlobals();
705
729
  const mode = resolveReviewMode(globalOpts);
706
730
  const result = await runAgentReview({
@@ -711,7 +735,9 @@ function createReviewCommand() {
711
735
  comment: opts.comment,
712
736
  ci: opts.ci,
713
737
  deep: opts.deep,
714
- noMechanical: opts.mechanical === false
738
+ noMechanical: opts.mechanical === false,
739
+ thorough: opts.thorough,
740
+ isolated: opts.isolated
715
741
  });
716
742
  if (!result.ok) {
717
743
  if (mode === OutputMode.JSON) console.log(JSON.stringify({ error: result.error.message }));
@@ -734,21 +760,130 @@ function createAgentCommand() {
734
760
  return command;
735
761
  }
736
762
 
737
- // src/commands/blueprint.ts
763
+ // src/commands/audit-protected.ts
764
+ init_dist();
738
765
  import { Command as Command6 } from "commander";
739
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";
740
875
  function createBlueprintCommand() {
741
- 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) => {
742
877
  try {
743
- const rootDir = path3.resolve(projectPath);
744
- const outputDir = path3.resolve(options.output);
878
+ const rootDir = path4.resolve(projectPath);
879
+ const outputDir = path4.resolve(options.output);
745
880
  logger.info(`Scanning project at ${rootDir}...`);
746
881
  const scanner = new ProjectScanner(rootDir);
747
882
  const data = await scanner.scan();
748
883
  logger.info(`Generating blueprint to ${outputDir}...`);
749
884
  const generator = new BlueprintGenerator();
750
885
  await generator.generate(data, { outputDir });
751
- logger.success(`Blueprint generated successfully at ${path3.join(outputDir, "index.html")}`);
886
+ logger.success(`Blueprint generated successfully at ${path4.join(outputDir, "index.html")}`);
752
887
  } catch (error) {
753
888
  logger.error(
754
889
  `Failed to generate blueprint: ${error instanceof Error ? error.message : String(error)}`
@@ -759,7 +894,8 @@ function createBlueprintCommand() {
759
894
  }
760
895
 
761
896
  // src/commands/check-arch.ts
762
- import { Command as Command7 } from "commander";
897
+ init_dist();
898
+ import { Command as Command8 } from "commander";
763
899
  import { execSync as execSync2 } from "child_process";
764
900
  function getCommitHash(cwd) {
765
901
  try {
@@ -853,7 +989,7 @@ async function runCheckArch(options) {
853
989
  thresholdViolations
854
990
  });
855
991
  }
856
- function resolveOutputMode(globalOpts) {
992
+ function resolveOutputMode2(globalOpts) {
857
993
  if (globalOpts.json) return OutputMode.JSON;
858
994
  if (globalOpts.quiet) return OutputMode.QUIET;
859
995
  if (globalOpts.verbose) return OutputMode.VERBOSE;
@@ -889,9 +1025,9 @@ function printArchResult(value, mode, formatter) {
889
1025
  if (output) console.log(output);
890
1026
  }
891
1027
  function createCheckArchCommand() {
892
- 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) => {
893
1029
  const globalOpts = cmd.optsWithGlobals();
894
- const mode = resolveOutputMode(globalOpts);
1030
+ const mode = resolveOutputMode2(globalOpts);
895
1031
  const formatter = new OutputFormatter(mode);
896
1032
  const result = await runCheckArch({
897
1033
  configPath: globalOpts.config,
@@ -919,8 +1055,10 @@ function createCheckArchCommand() {
919
1055
  }
920
1056
 
921
1057
  // src/commands/check-deps.ts
922
- import { Command as Command8 } from "commander";
923
- import * as path4 from "path";
1058
+ init_dist();
1059
+ init_dist();
1060
+ import { Command as Command9 } from "commander";
1061
+ import * as path5 from "path";
924
1062
  async function runCheckDeps(options) {
925
1063
  const cwd = options.cwd ?? process.cwd();
926
1064
  const configResult = resolveConfig(options.configPath);
@@ -936,7 +1074,7 @@ async function runCheckDeps(options) {
936
1074
  if (!config.layers || config.layers.length === 0) {
937
1075
  return Ok(result);
938
1076
  }
939
- const rootDir = path4.resolve(cwd, config.rootDir);
1077
+ const rootDir = path5.resolve(cwd, config.rootDir);
940
1078
  const parser = new TypeScriptParser();
941
1079
  const layers = config.layers.map((l) => defineLayer(l.name, [l.pattern], l.allowedDependencies));
942
1080
  const layerConfig = {
@@ -1011,25 +1149,17 @@ async function runCheckDepsAction(globalOpts) {
1011
1149
  process.exit(result.value.valid ? ExitCode.SUCCESS : ExitCode.VALIDATION_FAILED);
1012
1150
  }
1013
1151
  function createCheckDepsCommand() {
1014
- 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) => {
1015
1153
  await runCheckDepsAction(cmd.optsWithGlobals());
1016
1154
  });
1017
1155
  return command;
1018
1156
  }
1019
1157
 
1020
1158
  // src/commands/check-docs.ts
1021
- import { Command as Command9 } from "commander";
1022
- import * as path5 from "path";
1023
-
1024
- // src/utils/output.ts
1025
- function resolveOutputMode2(globalOpts) {
1026
- if (globalOpts.json) return OutputMode.JSON;
1027
- if (globalOpts.quiet) return OutputMode.QUIET;
1028
- if (globalOpts.verbose) return OutputMode.VERBOSE;
1029
- return OutputMode.TEXT;
1030
- }
1031
-
1032
- // src/commands/check-docs.ts
1159
+ init_dist();
1160
+ init_dist();
1161
+ import { Command as Command10 } from "commander";
1162
+ import * as path6 from "path";
1033
1163
  async function runCheckDocs(options) {
1034
1164
  const cwd = options.cwd ?? process.cwd();
1035
1165
  const minCoverage = options.minCoverage ?? 80;
@@ -1038,8 +1168,8 @@ async function runCheckDocs(options) {
1038
1168
  return configResult;
1039
1169
  }
1040
1170
  const config = configResult.value;
1041
- const docsDir = path5.resolve(cwd, config.docsDir);
1042
- const sourceDir = path5.resolve(cwd, config.rootDir);
1171
+ const docsDir = path6.resolve(cwd, config.docsDir);
1172
+ const sourceDir = path6.resolve(cwd, config.rootDir);
1043
1173
  const coverageResult = await checkDocCoverage("project", {
1044
1174
  docsDir,
1045
1175
  sourceDir,
@@ -1106,9 +1236,9 @@ function printCheckDocsResult(value, mode, formatter) {
1106
1236
  }
1107
1237
  }
1108
1238
  function createCheckDocsCommand() {
1109
- 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) => {
1110
1240
  const globalOpts = cmd.optsWithGlobals();
1111
- const mode = resolveOutputMode2(globalOpts);
1241
+ const mode = resolveOutputMode(globalOpts);
1112
1242
  const formatter = new OutputFormatter(mode);
1113
1243
  const result = await runCheckDocs({
1114
1244
  configPath: globalOpts.config,
@@ -1136,12 +1266,13 @@ function createCheckDocsCommand() {
1136
1266
  }
1137
1267
 
1138
1268
  // src/commands/check-perf.ts
1139
- import { Command as Command10 } from "commander";
1140
- import * as path6 from "path";
1269
+ init_dist();
1270
+ import { Command as Command11 } from "commander";
1271
+ import * as path7 from "path";
1141
1272
  async function runCheckPerf(cwd, options) {
1142
1273
  const runAll2 = !options.structural && !options.size && !options.coupling;
1143
1274
  const analyzer = new EntropyAnalyzer({
1144
- rootDir: path6.resolve(cwd),
1275
+ rootDir: path7.resolve(cwd),
1145
1276
  analyze: {
1146
1277
  complexity: runAll2 || !!options.structural,
1147
1278
  coupling: runAll2 || !!options.coupling,
@@ -1253,15 +1384,16 @@ async function runCheckPerfAction(opts, globalOpts) {
1253
1384
  process.exit(result.value.valid ? ExitCode.SUCCESS : ExitCode.VALIDATION_FAILED);
1254
1385
  }
1255
1386
  function createCheckPerfCommand() {
1256
- 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) => {
1257
1388
  await runCheckPerfAction(opts, cmd.optsWithGlobals());
1258
1389
  });
1259
1390
  return command;
1260
1391
  }
1261
1392
 
1262
1393
  // src/commands/check-security.ts
1263
- import { Command as Command11 } from "commander";
1264
- import * as path7 from "path";
1394
+ init_dist();
1395
+ import { Command as Command12 } from "commander";
1396
+ import * as path8 from "path";
1265
1397
  import { execSync as execSync3 } from "child_process";
1266
1398
  var SEVERITY_RANK = {
1267
1399
  error: 3,
@@ -1274,19 +1406,19 @@ function getChangedFiles(cwd) {
1274
1406
  cwd,
1275
1407
  encoding: "utf-8"
1276
1408
  });
1277
- 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));
1278
1410
  } catch {
1279
1411
  return [];
1280
1412
  }
1281
1413
  }
1282
1414
  async function runCheckSecurity(cwd, options) {
1283
- const projectRoot = path7.resolve(cwd);
1415
+ const projectRoot = path8.resolve(cwd);
1284
1416
  let configData = {};
1285
1417
  try {
1286
- const fs36 = await import("fs");
1287
- const configPath = path7.join(projectRoot, "harness.config.json");
1288
- if (fs36.existsSync(configPath)) {
1289
- 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");
1290
1422
  const parsed = JSON.parse(raw);
1291
1423
  configData = parsed.security ?? {};
1292
1424
  }
@@ -1299,7 +1431,7 @@ async function runCheckSecurity(cwd, options) {
1299
1431
  if (options.changedOnly) {
1300
1432
  filesToScan = getChangedFiles(projectRoot);
1301
1433
  } else {
1302
- const { glob } = await import("glob");
1434
+ const { glob: glob2 } = await import("glob");
1303
1435
  const pattern = "**/*.{ts,tsx,js,jsx,go,py,java,rb}";
1304
1436
  const ignore = securityConfig.exclude ?? [
1305
1437
  "**/node_modules/**",
@@ -1307,9 +1439,19 @@ async function runCheckSecurity(cwd, options) {
1307
1439
  "**/*.test.ts",
1308
1440
  "**/fixtures/**"
1309
1441
  ];
1310
- filesToScan = await glob(pattern, { cwd: projectRoot, absolute: true, ignore });
1442
+ filesToScan = await glob2(pattern, { cwd: projectRoot, absolute: true, ignore });
1311
1443
  }
1312
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
+ }
1313
1455
  const threshold = options.severity ?? "warning";
1314
1456
  const thresholdRank = SEVERITY_RANK[threshold];
1315
1457
  const filtered = result.findings.filter((f) => SEVERITY_RANK[f.severity] >= thresholdRank);
@@ -1355,7 +1497,7 @@ async function runCheckSecurityAction(opts, globalOpts) {
1355
1497
  process.exit(result.value.valid ? ExitCode.SUCCESS : ExitCode.VALIDATION_FAILED);
1356
1498
  }
1357
1499
  function createCheckSecurityCommand() {
1358
- 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) => {
1359
1501
  const severity = thisCommand.opts().severity;
1360
1502
  if (!["error", "warning", "info"].includes(severity)) {
1361
1503
  logger.error(`Invalid severity: "${severity}". Must be one of: error, warning, info`);
@@ -1368,10 +1510,11 @@ function createCheckSecurityCommand() {
1368
1510
  }
1369
1511
 
1370
1512
  // src/commands/ci/index.ts
1371
- import { Command as Command14 } from "commander";
1513
+ import { Command as Command16 } from "commander";
1372
1514
 
1373
1515
  // src/commands/ci/check.ts
1374
- import { Command as Command12 } from "commander";
1516
+ init_dist();
1517
+ import { Command as Command13 } from "commander";
1375
1518
  var VALID_CHECKS = [
1376
1519
  "validate",
1377
1520
  "deps",
@@ -1435,7 +1578,7 @@ function printCheckReport(report) {
1435
1578
  }
1436
1579
  }
1437
1580
  async function runCheckAction(opts, globalOpts) {
1438
- const mode = resolveOutputMode2(globalOpts);
1581
+ const mode = resolveOutputMode(globalOpts);
1439
1582
  const skip = parseSkip(opts.skip);
1440
1583
  const failOn = parseFailOn(opts.failOn);
1441
1584
  const opts2 = {
@@ -1461,15 +1604,16 @@ async function runCheckAction(opts, globalOpts) {
1461
1604
  process.exit(report.exitCode);
1462
1605
  }
1463
1606
  function createCheckCommand() {
1464
- 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) => {
1465
1608
  await runCheckAction(opts, cmd.optsWithGlobals());
1466
1609
  });
1467
1610
  }
1468
1611
 
1469
1612
  // src/commands/ci/init.ts
1470
- import { Command as Command13 } from "commander";
1471
- import * as fs2 from "fs";
1472
- 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";
1473
1617
  var ALL_CHECKS = [
1474
1618
  "validate",
1475
1619
  "deps",
@@ -1571,17 +1715,17 @@ function generateCIConfig(options) {
1571
1715
  });
1572
1716
  }
1573
1717
  function detectPlatform() {
1574
- if (fs2.existsSync(".github")) return "github";
1575
- if (fs2.existsSync(".gitlab-ci.yml")) return "gitlab";
1718
+ if (fs3.existsSync(".github")) return "github";
1719
+ if (fs3.existsSync(".gitlab-ci.yml")) return "gitlab";
1576
1720
  return null;
1577
1721
  }
1578
1722
  function writeGeneratedConfig(filename, content, platform) {
1579
- const targetPath = path8.resolve(filename);
1580
- const dir = path8.dirname(targetPath);
1581
- fs2.mkdirSync(dir, { recursive: true });
1582
- 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);
1583
1727
  if (platform === "generic" && process.platform !== "win32") {
1584
- fs2.chmodSync(targetPath, "755");
1728
+ fs3.chmodSync(targetPath, "755");
1585
1729
  }
1586
1730
  }
1587
1731
  async function runInitAction(opts, globalOpts) {
@@ -1604,22 +1748,128 @@ async function runInitAction(opts, globalOpts) {
1604
1748
  }
1605
1749
  }
1606
1750
  function createInitCommand() {
1607
- 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) => {
1608
1752
  await runInitAction(opts, cmd.optsWithGlobals());
1609
1753
  });
1610
1754
  }
1611
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
+
1612
1860
  // src/commands/ci/index.ts
1613
1861
  function createCICommand() {
1614
- const command = new Command14("ci").description("CI/CD integration commands");
1862
+ const command = new Command16("ci").description("CI/CD integration commands");
1615
1863
  command.addCommand(createCheckCommand());
1616
1864
  command.addCommand(createInitCommand());
1865
+ command.addCommand(createNotifyCommand());
1617
1866
  return command;
1618
1867
  }
1619
1868
 
1620
1869
  // src/commands/cleanup.ts
1621
- import { Command as Command15 } from "commander";
1622
- import * as path9 from "path";
1870
+ init_dist();
1871
+ import { Command as Command17 } from "commander";
1872
+ import * as path10 from "path";
1623
1873
  async function runCleanup(options) {
1624
1874
  const cwd = options.cwd ?? process.cwd();
1625
1875
  const type = options.type ?? "all";
@@ -1634,11 +1884,11 @@ async function runCleanup(options) {
1634
1884
  patternViolations: [],
1635
1885
  totalIssues: 0
1636
1886
  };
1637
- const rootDir = path9.resolve(cwd, config.rootDir);
1638
- const docsDir = path9.resolve(cwd, config.docsDir);
1887
+ const rootDir = path10.resolve(cwd, config.rootDir);
1888
+ const docsDir = path10.resolve(cwd, config.docsDir);
1639
1889
  const entropyConfig = {
1640
1890
  rootDir,
1641
- entryPoints: [path9.join(rootDir, "src/index.ts")],
1891
+ ...config.entropy?.entryPoints && { entryPoints: config.entropy.entryPoints },
1642
1892
  docPaths: [docsDir],
1643
1893
  analyze: {
1644
1894
  drift: type === "all" || type === "drift",
@@ -1707,9 +1957,9 @@ function printCleanupResult(value, formatter) {
1707
1957
  }
1708
1958
  }
1709
1959
  function createCleanupCommand() {
1710
- 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) => {
1711
1961
  const globalOpts = cmd.optsWithGlobals();
1712
- const mode = resolveOutputMode2(globalOpts);
1962
+ const mode = resolveOutputMode(globalOpts);
1713
1963
  const formatter = new OutputFormatter(mode);
1714
1964
  const result = await runCleanup({
1715
1965
  configPath: globalOpts.config,
@@ -1737,20 +1987,21 @@ function createCleanupCommand() {
1737
1987
  }
1738
1988
 
1739
1989
  // src/commands/cleanup-sessions.ts
1740
- import { Command as Command16 } from "commander";
1741
- import * as fs3 from "fs";
1742
- 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";
1743
1994
  var STALE_TTL_MS = 24 * 60 * 60 * 1e3;
1744
1995
  function getMostRecentMtime(dirPath) {
1745
1996
  let latest = 0;
1746
1997
  try {
1747
- const entries = fs3.readdirSync(dirPath, { withFileTypes: true });
1998
+ const entries = fs5.readdirSync(dirPath, { withFileTypes: true });
1748
1999
  for (const entry of entries) {
1749
- const fullPath = path10.join(dirPath, entry.name);
1750
- const stat = fs3.statSync(fullPath);
2000
+ const fullPath = path11.join(dirPath, entry.name);
2001
+ const stat = fs5.statSync(fullPath);
1751
2002
  if (stat.mtimeMs > latest) latest = stat.mtimeMs;
1752
2003
  }
1753
- const dirStat = fs3.statSync(dirPath);
2004
+ const dirStat = fs5.statSync(dirPath);
1754
2005
  if (dirStat.mtimeMs > latest) latest = dirStat.mtimeMs;
1755
2006
  } catch {
1756
2007
  }
@@ -1759,14 +2010,14 @@ function getMostRecentMtime(dirPath) {
1759
2010
  async function runCleanupSessions(options) {
1760
2011
  const cwd = options.cwd ?? process.cwd();
1761
2012
  const dryRun = options.dryRun ?? false;
1762
- const sessionsDir = path10.join(cwd, ".harness", "sessions");
2013
+ const sessionsDir = path11.join(cwd, ".harness", "sessions");
1763
2014
  const result = { removed: [], kept: [] };
1764
- if (!fs3.existsSync(sessionsDir)) {
2015
+ if (!fs5.existsSync(sessionsDir)) {
1765
2016
  return Ok(result);
1766
2017
  }
1767
2018
  let entries;
1768
2019
  try {
1769
- entries = fs3.readdirSync(sessionsDir, { withFileTypes: true });
2020
+ entries = fs5.readdirSync(sessionsDir, { withFileTypes: true });
1770
2021
  } catch (err) {
1771
2022
  return Err(
1772
2023
  new CLIError(
@@ -1778,14 +2029,14 @@ async function runCleanupSessions(options) {
1778
2029
  const now = Date.now();
1779
2030
  for (const entry of entries) {
1780
2031
  if (!entry.isDirectory()) continue;
1781
- const sessionPath = path10.join(sessionsDir, entry.name);
2032
+ const sessionPath = path11.join(sessionsDir, entry.name);
1782
2033
  const mostRecent = getMostRecentMtime(sessionPath);
1783
2034
  const ageMs = now - mostRecent;
1784
2035
  if (ageMs > STALE_TTL_MS) {
1785
2036
  result.removed.push(entry.name);
1786
2037
  if (!dryRun) {
1787
2038
  try {
1788
- fs3.rmSync(sessionPath, { recursive: true, force: true });
2039
+ fs5.rmSync(sessionPath, { recursive: true, force: true });
1789
2040
  } catch (err) {
1790
2041
  return Err(
1791
2042
  new CLIError(
@@ -1828,9 +2079,9 @@ Cleaned up ${removed.length} stale session(s).`);
1828
2079
  }
1829
2080
  }
1830
2081
  function createCleanupSessionsCommand() {
1831
- 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) => {
1832
2083
  const globalOpts = cmd.optsWithGlobals();
1833
- const cwd = path10.resolve(opts.path);
2084
+ const cwd = path11.resolve(opts.path);
1834
2085
  const result = await runCleanupSessions({ cwd, dryRun: opts.dryRun });
1835
2086
  if (!result.ok) {
1836
2087
  logger.error(result.error.message);
@@ -1844,11 +2095,11 @@ function createCleanupSessionsCommand() {
1844
2095
  }
1845
2096
 
1846
2097
  // src/commands/dashboard.ts
1847
- import { Command as Command17 } from "commander";
2098
+ import { Command as Command19 } from "commander";
1848
2099
  import { spawn } from "child_process";
1849
2100
  import { existsSync as existsSync4 } from "fs";
1850
2101
  import { createRequire } from "module";
1851
- 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";
1852
2103
  import { setTimeout } from "timers";
1853
2104
  import { fileURLToPath } from "url";
1854
2105
  var __filename = fileURLToPath(import.meta.url);
@@ -1864,20 +2115,20 @@ function resolveServerScript() {
1864
2115
  try {
1865
2116
  const pkgPath = require2.resolve("@harness-engineering/dashboard/package.json");
1866
2117
  const pkgDir = dirname2(pkgPath);
1867
- const built2 = join7(pkgDir, "dist", "server", "serve.js");
2118
+ const built2 = join6(pkgDir, "dist", "server", "serve.js");
1868
2119
  if (existsSync4(built2)) return { script: built2, dev: false };
1869
- const dev2 = join7(pkgDir, "src", "server", "serve.ts");
2120
+ const dev2 = join6(pkgDir, "src", "server", "serve.ts");
1870
2121
  if (existsSync4(dev2)) return { script: dev2, dev: true };
1871
2122
  } catch {
1872
2123
  }
1873
2124
  const built = [
1874
- join7(__dirname, "..", "..", "..", "dashboard", "dist", "server", "serve.js"),
1875
- 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")
1876
2127
  ].find((p) => existsSync4(p));
1877
2128
  if (built) return { script: built, dev: false };
1878
2129
  const dev = [
1879
- join7(__dirname, "..", "..", "..", "dashboard", "src", "server", "serve.ts"),
1880
- 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")
1881
2132
  ].find((p) => existsSync4(p));
1882
2133
  return dev ? { script: dev, dev: true } : null;
1883
2134
  }
@@ -1898,8 +2149,8 @@ function spawnDashboardServer(server, env) {
1898
2149
  function runDashboard(opts) {
1899
2150
  const clientPort = Number(opts.port ?? DEFAULT_CLIENT_PORT);
1900
2151
  const apiPort = Number(opts.apiPort ?? DEFAULT_API_PORT);
1901
- const projectPath = resolve10(opts.cwd ?? process.cwd());
1902
- const url = `http://localhost:${clientPort}`;
2152
+ const projectPath = resolve11(opts.cwd ?? process.cwd());
2153
+ const url = `http://localhost:${apiPort}`;
1903
2154
  const server = resolveServerScript();
1904
2155
  if (!server) {
1905
2156
  console.error("Could not locate the dashboard server. Run `pnpm build` in packages/dashboard.");
@@ -1919,14 +2170,14 @@ function runDashboard(opts) {
1919
2170
  }
1920
2171
  }
1921
2172
  function createDashboardCommand() {
1922
- 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));
1923
2174
  }
1924
2175
 
1925
2176
  // src/commands/doctor.ts
1926
- import { Command as Command18 } from "commander";
1927
- import * as fs5 from "fs";
2177
+ import { Command as Command20 } from "commander";
2178
+ import * as fs7 from "fs";
1928
2179
  import * as os from "os";
1929
- import * as path12 from "path";
2180
+ import * as path13 from "path";
1930
2181
  import chalk from "chalk";
1931
2182
 
1932
2183
  // src/utils/node-version.ts
@@ -2009,24 +2260,24 @@ var INTEGRATION_REGISTRY = [
2009
2260
  ];
2010
2261
 
2011
2262
  // src/integrations/config.ts
2012
- import * as fs4 from "fs";
2013
- import * as path11 from "path";
2263
+ import * as fs6 from "fs";
2264
+ import * as path12 from "path";
2014
2265
  function readJsonSafe(filePath) {
2015
- if (!fs4.existsSync(filePath)) return null;
2266
+ if (!fs6.existsSync(filePath)) return null;
2016
2267
  try {
2017
- return JSON.parse(fs4.readFileSync(filePath, "utf-8"));
2268
+ return JSON.parse(fs6.readFileSync(filePath, "utf-8"));
2018
2269
  } catch {
2019
2270
  return null;
2020
2271
  }
2021
2272
  }
2022
2273
  function writeJson(filePath, data) {
2023
- const dir = path11.dirname(filePath);
2024
- if (!fs4.existsSync(dir)) {
2025
- fs4.mkdirSync(dir, { recursive: true });
2274
+ const dir = path12.dirname(filePath);
2275
+ if (!fs6.existsSync(dir)) {
2276
+ fs6.mkdirSync(dir, { recursive: true });
2026
2277
  }
2027
2278
  const tmp = filePath + ".tmp";
2028
- fs4.writeFileSync(tmp, JSON.stringify(data, null, 2) + "\n");
2029
- fs4.renameSync(tmp, filePath);
2279
+ fs6.writeFileSync(tmp, JSON.stringify(data, null, 2) + "\n");
2280
+ fs6.renameSync(tmp, filePath);
2030
2281
  }
2031
2282
  function readMcpConfig(filePath) {
2032
2283
  const config = readJsonSafe(filePath);
@@ -2040,7 +2291,7 @@ function writeMcpEntry(filePath, name, entry) {
2040
2291
  writeJson(filePath, config);
2041
2292
  }
2042
2293
  function removeMcpEntry(filePath, name) {
2043
- if (!fs4.existsSync(filePath)) return;
2294
+ if (!fs6.existsSync(filePath)) return;
2044
2295
  const config = readMcpConfig(filePath);
2045
2296
  delete config.mcpServers[name];
2046
2297
  writeJson(filePath, config);
@@ -2079,7 +2330,7 @@ function checkNodeVersion2() {
2079
2330
  }
2080
2331
  function countCommandFiles(dir, ext) {
2081
2332
  try {
2082
- return fs5.readdirSync(dir).filter((f) => f.endsWith(ext)).length;
2333
+ return fs7.readdirSync(dir).filter((f) => f.endsWith(ext)).length;
2083
2334
  } catch {
2084
2335
  return 0;
2085
2336
  }
@@ -2088,13 +2339,13 @@ function checkSlashCommands() {
2088
2339
  const platforms = [
2089
2340
  {
2090
2341
  name: "Claude Code",
2091
- dir: path12.join(os.homedir(), ".claude", "commands", "harness"),
2342
+ dir: path13.join(os.homedir(), ".claude", "commands", "harness"),
2092
2343
  ext: ".md",
2093
2344
  client: "claude-code"
2094
2345
  },
2095
2346
  {
2096
2347
  name: "Gemini CLI",
2097
- dir: path12.join(os.homedir(), ".gemini", "commands", "harness"),
2348
+ dir: path13.join(os.homedir(), ".gemini", "commands", "harness"),
2098
2349
  ext: ".toml",
2099
2350
  client: "gemini-cli"
2100
2351
  }
@@ -2118,7 +2369,7 @@ function checkSlashCommands() {
2118
2369
  }
2119
2370
  function checkMcpConfig(cwd) {
2120
2371
  const results = [];
2121
- const claudeConfig = readMcpConfig(path12.join(cwd, ".mcp.json"));
2372
+ const claudeConfig = readMcpConfig(path13.join(cwd, ".mcp.json"));
2122
2373
  if (claudeConfig.mcpServers?.["harness"]) {
2123
2374
  results.push({
2124
2375
  name: "mcp-claude",
@@ -2133,9 +2384,9 @@ function checkMcpConfig(cwd) {
2133
2384
  fix: "Run: harness setup-mcp --client claude"
2134
2385
  });
2135
2386
  }
2136
- const geminiDir = path12.join(cwd, ".gemini");
2137
- if (fs5.existsSync(geminiDir)) {
2138
- 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"));
2139
2390
  if (geminiConfig.mcpServers?.["harness"]) {
2140
2391
  results.push({
2141
2392
  name: "mcp-gemini",
@@ -2154,12 +2405,12 @@ function checkMcpConfig(cwd) {
2154
2405
  return results;
2155
2406
  }
2156
2407
  function loadMcpPresence(cwd) {
2157
- const mcpPath = path12.join(cwd, ".mcp.json");
2158
- const geminiDir = path12.join(cwd, ".gemini");
2159
- 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);
2160
2411
  return {
2161
2412
  mcpConfig: readMcpConfig(mcpPath),
2162
- geminiConfig: hasGemini ? readMcpConfig(path12.join(geminiDir, "settings.json")) : null,
2413
+ geminiConfig: hasGemini ? readMcpConfig(path13.join(geminiDir, "settings.json")) : null,
2163
2414
  hasGemini
2164
2415
  };
2165
2416
  }
@@ -2191,7 +2442,7 @@ function checkTier0Presence(def, presence) {
2191
2442
  function checkIntegrations(cwd) {
2192
2443
  const results = [];
2193
2444
  const presence = loadMcpPresence(cwd);
2194
- const configPath = path12.join(cwd, "harness.config.json");
2445
+ const configPath = path13.join(cwd, "harness.config.json");
2195
2446
  const integrationsConfig = readIntegrationsConfig(configPath);
2196
2447
  for (const def of INTEGRATION_REGISTRY.filter((d) => d.tier === 0)) {
2197
2448
  results.push(checkTier0Presence(def, presence));
@@ -2241,7 +2492,7 @@ function formatCheck(check) {
2241
2492
  return line;
2242
2493
  }
2243
2494
  function createDoctorCommand() {
2244
- 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) => {
2245
2496
  const globalOpts = cmd.optsWithGlobals();
2246
2497
  const cwd = process.cwd();
2247
2498
  const useJson = globalOpts.json;
@@ -2266,8 +2517,9 @@ function createDoctorCommand() {
2266
2517
  }
2267
2518
 
2268
2519
  // src/commands/fix-drift.ts
2269
- import { Command as Command19 } from "commander";
2270
- import * as path13 from "path";
2520
+ init_dist();
2521
+ import { Command as Command21 } from "commander";
2522
+ import * as path14 from "path";
2271
2523
  async function runFixDrift(options) {
2272
2524
  const cwd = options.cwd ?? process.cwd();
2273
2525
  const dryRun = options.dryRun !== false;
@@ -2276,11 +2528,11 @@ async function runFixDrift(options) {
2276
2528
  return Err(configResult.error);
2277
2529
  }
2278
2530
  const config = configResult.value;
2279
- const rootDir = path13.resolve(cwd, config.rootDir);
2280
- const docsDir = path13.resolve(cwd, config.docsDir);
2531
+ const rootDir = path14.resolve(cwd, config.rootDir);
2532
+ const docsDir = path14.resolve(cwd, config.docsDir);
2281
2533
  const entropyConfig = {
2282
2534
  rootDir,
2283
- entryPoints: [path13.join(rootDir, "src/index.ts")],
2535
+ ...config.entropy?.entryPoints && { entryPoints: config.entropy.entryPoints },
2284
2536
  docPaths: [docsDir],
2285
2537
  analyze: {
2286
2538
  drift: true,
@@ -2399,9 +2651,9 @@ function printFixDriftResult(value, mode, formatter) {
2399
2651
  }
2400
2652
  }
2401
2653
  function createFixDriftCommand() {
2402
- 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) => {
2403
2655
  const globalOpts = cmd.optsWithGlobals();
2404
- const mode = resolveOutputMode2(globalOpts);
2656
+ const mode = resolveOutputMode(globalOpts);
2405
2657
  const formatter = new OutputFormatter(mode);
2406
2658
  const result = await runFixDrift({
2407
2659
  configPath: globalOpts.config,
@@ -2429,7 +2681,7 @@ function createFixDriftCommand() {
2429
2681
  }
2430
2682
 
2431
2683
  // src/commands/generate.ts
2432
- import { Command as Command20 } from "commander";
2684
+ import { Command as Command22 } from "commander";
2433
2685
  function validatePlatforms(platforms) {
2434
2686
  for (const p of platforms) {
2435
2687
  if (!VALID_PLATFORMS.includes(p)) {
@@ -2489,27 +2741,27 @@ async function runGenerateAction(opts, globalOpts) {
2489
2741
  }
2490
2742
  }
2491
2743
  function createGenerateCommand() {
2492
- 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) => {
2493
2745
  await runGenerateAction(opts, cmd.optsWithGlobals());
2494
2746
  });
2495
2747
  }
2496
2748
 
2497
2749
  // src/commands/graph/index.ts
2498
- import { Command as Command24 } from "commander";
2750
+ import { Command as Command25 } from "commander";
2499
2751
 
2500
2752
  // src/commands/graph/status.ts
2501
- import * as path14 from "path";
2753
+ import * as path15 from "path";
2502
2754
  async function runGraphStatus(projectPath) {
2503
- const { GraphStore } = await import("./dist-QW5G3GX3.js");
2504
- const graphDir = path14.join(projectPath, ".harness", "graph");
2755
+ const { GraphStore } = await import("./dist-RADHOOXG.js");
2756
+ const graphDir = path15.join(projectPath, ".harness", "graph");
2505
2757
  const store = new GraphStore();
2506
2758
  const loaded = await store.load(graphDir);
2507
2759
  if (!loaded) return { status: "no_graph", message: "No graph found. Run `harness scan` first." };
2508
- const fs36 = await import("fs/promises");
2509
- const metaPath = path14.join(graphDir, "metadata.json");
2760
+ const fs41 = await import("fs/promises");
2761
+ const metaPath = path15.join(graphDir, "metadata.json");
2510
2762
  let lastScan = "unknown";
2511
2763
  try {
2512
- const meta = JSON.parse(await fs36.readFile(metaPath, "utf-8"));
2764
+ const meta = JSON.parse(await fs41.readFile(metaPath, "utf-8"));
2513
2765
  lastScan = meta.lastScanTimestamp;
2514
2766
  } catch {
2515
2767
  }
@@ -2520,8 +2772,8 @@ async function runGraphStatus(projectPath) {
2520
2772
  }
2521
2773
  let connectorSyncStatus = {};
2522
2774
  try {
2523
- const syncMetaPath = path14.join(graphDir, "sync-metadata.json");
2524
- 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"));
2525
2777
  for (const [name, data] of Object.entries(syncMeta.connectors ?? {})) {
2526
2778
  connectorSyncStatus[name] = data.lastSyncTimestamp;
2527
2779
  }
@@ -2538,10 +2790,10 @@ async function runGraphStatus(projectPath) {
2538
2790
  }
2539
2791
 
2540
2792
  // src/commands/graph/export.ts
2541
- import * as path15 from "path";
2793
+ import * as path16 from "path";
2542
2794
  async function runGraphExport(projectPath, format) {
2543
- const { GraphStore } = await import("./dist-QW5G3GX3.js");
2544
- const graphDir = path15.join(projectPath, ".harness", "graph");
2795
+ const { GraphStore } = await import("./dist-RADHOOXG.js");
2796
+ const graphDir = path16.join(projectPath, ".harness", "graph");
2545
2797
  const store = new GraphStore();
2546
2798
  const loaded = await store.load(graphDir);
2547
2799
  if (!loaded) throw new Error("No graph found. Run `harness scan` first.");
@@ -2572,50 +2824,11 @@ async function runGraphExport(projectPath, format) {
2572
2824
  // src/commands/graph/index.ts
2573
2825
  import * as path19 from "path";
2574
2826
 
2575
- // src/commands/graph/scan.ts
2576
- import { Command as Command21 } from "commander";
2577
- import * as path16 from "path";
2578
- async function runScan(projectPath) {
2579
- const { GraphStore, CodeIngestor, TopologicalLinker, KnowledgeIngestor, GitIngestor } = await import("./dist-QW5G3GX3.js");
2580
- const store = new GraphStore();
2581
- const start = Date.now();
2582
- await new CodeIngestor(store).ingest(projectPath);
2583
- new TopologicalLinker(store).link();
2584
- const knowledgeIngestor = new KnowledgeIngestor(store);
2585
- await knowledgeIngestor.ingestAll(projectPath);
2586
- try {
2587
- await new GitIngestor(store).ingest(projectPath);
2588
- } catch {
2589
- }
2590
- const graphDir = path16.join(projectPath, ".harness", "graph");
2591
- await store.save(graphDir);
2592
- return { nodeCount: store.nodeCount, edgeCount: store.edgeCount, durationMs: Date.now() - start };
2593
- }
2594
- function createScanCommand() {
2595
- return new Command21("scan").description("Scan project and build knowledge graph").argument("[path]", "Project root path", ".").action(async (inputPath, _opts, cmd) => {
2596
- const projectPath = path16.resolve(inputPath);
2597
- const globalOpts = cmd.optsWithGlobals();
2598
- try {
2599
- const result = await runScan(projectPath);
2600
- if (globalOpts.json) {
2601
- console.log(JSON.stringify(result));
2602
- } else {
2603
- console.log(
2604
- `Graph built: ${result.nodeCount} nodes, ${result.edgeCount} edges (${result.durationMs}ms)`
2605
- );
2606
- }
2607
- } catch (err) {
2608
- console.error("Scan failed:", err instanceof Error ? err.message : err);
2609
- process.exit(2);
2610
- }
2611
- });
2612
- }
2613
-
2614
2827
  // src/commands/graph/query.ts
2615
- import { Command as Command22 } from "commander";
2828
+ import { Command as Command23 } from "commander";
2616
2829
  import * as path17 from "path";
2617
2830
  async function runQuery(projectPath, rootNodeId, opts) {
2618
- const { GraphStore, ContextQL } = await import("./dist-QW5G3GX3.js");
2831
+ const { GraphStore, ContextQL } = await import("./dist-RADHOOXG.js");
2619
2832
  const store = new GraphStore();
2620
2833
  const graphDir = path17.join(projectPath, ".harness", "graph");
2621
2834
  const loaded = await store.load(graphDir);
@@ -2658,19 +2871,19 @@ async function runQueryAction(rootNodeId, opts, globalOpts) {
2658
2871
  }
2659
2872
  }
2660
2873
  function createQueryCommand() {
2661
- return new Command22("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) => {
2662
2875
  await runQueryAction(rootNodeId, opts, cmd.optsWithGlobals());
2663
2876
  });
2664
2877
  }
2665
2878
 
2666
2879
  // src/commands/graph/ingest.ts
2667
- import { Command as Command23 } from "commander";
2880
+ import { Command as Command24 } from "commander";
2668
2881
  import * as path18 from "path";
2669
2882
  async function loadConnectorConfig(projectPath, source) {
2670
2883
  try {
2671
- const fs36 = await import("fs/promises");
2884
+ const fs41 = await import("fs/promises");
2672
2885
  const configPath = path18.join(projectPath, "harness.config.json");
2673
- const config = JSON.parse(await fs36.readFile(configPath, "utf-8"));
2886
+ const config = JSON.parse(await fs41.readFile(configPath, "utf-8"));
2674
2887
  const connector = config.graph?.connectors?.[source];
2675
2888
  return connector ?? {};
2676
2889
  } catch {
@@ -2709,7 +2922,7 @@ async function runIngest(projectPath, source, opts) {
2709
2922
  SlackConnector,
2710
2923
  CIConnector,
2711
2924
  ConfluenceConnector
2712
- } = await import("./dist-QW5G3GX3.js");
2925
+ } = await import("./dist-RADHOOXG.js");
2713
2926
  const graphDir = path18.join(projectPath, ".harness", "graph");
2714
2927
  const store = new GraphStore();
2715
2928
  await store.load(graphDir);
@@ -2777,7 +2990,7 @@ async function runIngest(projectPath, source, opts) {
2777
2990
  return result;
2778
2991
  }
2779
2992
  function createIngestCommand() {
2780
- return new Command23("ingest").description("Ingest data into the knowledge graph").option(
2993
+ return new Command24("ingest").description("Ingest data into the knowledge graph").option(
2781
2994
  "--source <name>",
2782
2995
  "Source to ingest (code, knowledge, git, jira, slack, ci, confluence)"
2783
2996
  ).option("--all", "Run all sources (code, knowledge, git, and configured connectors)").option("--full", "Force full re-ingestion").action(async (opts, cmd) => {
@@ -2853,18 +3066,18 @@ async function runExportAction(opts, cmd) {
2853
3066
  }
2854
3067
  }
2855
3068
  function createGraphCommand() {
2856
- const graph = new Command24("graph").description("Knowledge graph management");
3069
+ const graph = new Command25("graph").description("Knowledge graph management");
2857
3070
  graph.command("status").description("Show graph statistics").action(runStatusAction);
2858
3071
  graph.command("export").description("Export graph").requiredOption("--format <format>", "Output format (json, mermaid)").action(runExportAction);
2859
3072
  return graph;
2860
3073
  }
2861
3074
 
2862
3075
  // src/commands/hooks/index.ts
2863
- import { Command as Command29 } from "commander";
3076
+ import { Command as Command30 } from "commander";
2864
3077
 
2865
3078
  // src/commands/hooks/init.ts
2866
- import { Command as Command25 } from "commander";
2867
- import * as fs6 from "fs";
3079
+ import { Command as Command26 } from "commander";
3080
+ import * as fs8 from "fs";
2868
3081
  import * as path20 from "path";
2869
3082
  import { fileURLToPath as fileURLToPath2 } from "url";
2870
3083
 
@@ -2899,7 +3112,7 @@ var __dirname2 = path20.dirname(__filename2);
2899
3112
  var VALID_PROFILES = ["minimal", "standard", "strict"];
2900
3113
  function resolveHookSourceDir() {
2901
3114
  const candidate = path20.resolve(__dirname2, "..", "..", "hooks");
2902
- if (fs6.existsSync(candidate)) {
3115
+ if (fs8.existsSync(candidate)) {
2903
3116
  return candidate;
2904
3117
  }
2905
3118
  throw new Error(`Cannot locate hook scripts directory. Expected at: ${candidate}`);
@@ -2928,11 +3141,11 @@ function mergeSettings(existing, hooksConfig) {
2928
3141
  function initHooks(options) {
2929
3142
  const { profile, projectDir } = options;
2930
3143
  const hooksDestDir = path20.join(projectDir, ".harness", "hooks");
2931
- fs6.mkdirSync(hooksDestDir, { recursive: true });
2932
- if (fs6.existsSync(hooksDestDir)) {
2933
- for (const entry of fs6.readdirSync(hooksDestDir)) {
3144
+ fs8.mkdirSync(hooksDestDir, { recursive: true });
3145
+ if (fs8.existsSync(hooksDestDir)) {
3146
+ for (const entry of fs8.readdirSync(hooksDestDir)) {
2934
3147
  if (entry.endsWith(".js")) {
2935
- fs6.unlinkSync(path20.join(hooksDestDir, entry));
3148
+ fs8.unlinkSync(path20.join(hooksDestDir, entry));
2936
3149
  }
2937
3150
  }
2938
3151
  }
@@ -2943,20 +3156,20 @@ function initHooks(options) {
2943
3156
  for (const script of activeScripts) {
2944
3157
  const srcFile = path20.join(sourceDir, `${script.name}.js`);
2945
3158
  const destFile = path20.join(hooksDestDir, `${script.name}.js`);
2946
- if (fs6.existsSync(srcFile)) {
2947
- fs6.copyFileSync(srcFile, destFile);
3159
+ if (fs8.existsSync(srcFile)) {
3160
+ fs8.copyFileSync(srcFile, destFile);
2948
3161
  copiedScripts.push(script.name);
2949
3162
  }
2950
3163
  }
2951
3164
  const profilePath = path20.join(hooksDestDir, "profile.json");
2952
- fs6.writeFileSync(profilePath, JSON.stringify({ profile }, null, 2) + "\n");
3165
+ fs8.writeFileSync(profilePath, JSON.stringify({ profile }, null, 2) + "\n");
2953
3166
  const claudeDir = path20.join(projectDir, ".claude");
2954
- fs6.mkdirSync(claudeDir, { recursive: true });
3167
+ fs8.mkdirSync(claudeDir, { recursive: true });
2955
3168
  const settingsPath = path20.join(claudeDir, "settings.json");
2956
3169
  let existing = {};
2957
- if (fs6.existsSync(settingsPath)) {
3170
+ if (fs8.existsSync(settingsPath)) {
2958
3171
  try {
2959
- existing = JSON.parse(fs6.readFileSync(settingsPath, "utf-8"));
3172
+ existing = JSON.parse(fs8.readFileSync(settingsPath, "utf-8"));
2960
3173
  } catch (e) {
2961
3174
  throw new Error(
2962
3175
  `Malformed .claude/settings.json \u2014 fix the JSON syntax before running hooks init. Parse error: ${e instanceof Error ? e.message : String(e)}`,
@@ -2966,11 +3179,11 @@ function initHooks(options) {
2966
3179
  }
2967
3180
  const hooksConfig = buildSettingsHooks(profile);
2968
3181
  const merged = mergeSettings(existing, hooksConfig);
2969
- fs6.writeFileSync(settingsPath, JSON.stringify(merged, null, 2) + "\n");
3182
+ fs8.writeFileSync(settingsPath, JSON.stringify(merged, null, 2) + "\n");
2970
3183
  return { copiedScripts, settingsPath, profilePath };
2971
3184
  }
2972
3185
  function createInitCommand2() {
2973
- return new Command25("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) => {
3186
+ 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) => {
2974
3187
  const globalOpts = cmd.optsWithGlobals();
2975
3188
  const profile = opts.profile;
2976
3189
  if (!VALID_PROFILES.includes(profile)) {
@@ -2994,7 +3207,9 @@ function createInitCommand2() {
2994
3207
  `Installed ${result.copiedScripts.length} hook scripts to .harness/hooks/`
2995
3208
  );
2996
3209
  logger.info(`Profile: ${profile}`);
2997
- logger.info(`Settings: ${path20.relative(projectDir, result.settingsPath)}`);
3210
+ logger.info(
3211
+ `Settings: ${path20.relative(projectDir, result.settingsPath).replaceAll("\\", "/")}`
3212
+ );
2998
3213
  logger.dim("Run 'harness hooks list' to see installed hooks");
2999
3214
  }
3000
3215
  } catch (err) {
@@ -3006,19 +3221,19 @@ function createInitCommand2() {
3006
3221
  }
3007
3222
 
3008
3223
  // src/commands/hooks/list.ts
3009
- import { Command as Command26 } from "commander";
3010
- import * as fs7 from "fs";
3224
+ import { Command as Command27 } from "commander";
3225
+ import * as fs9 from "fs";
3011
3226
  import * as path21 from "path";
3012
3227
  function listHooks(projectDir) {
3013
3228
  const hooksDir = path21.join(projectDir, ".harness", "hooks");
3014
3229
  const profilePath = path21.join(hooksDir, "profile.json");
3015
- if (!fs7.existsSync(profilePath)) {
3230
+ if (!fs9.existsSync(profilePath)) {
3016
3231
  return { installed: false, profile: null, hooks: [] };
3017
3232
  }
3018
3233
  let profile = "standard";
3019
3234
  let warning;
3020
3235
  try {
3021
- const data = JSON.parse(fs7.readFileSync(profilePath, "utf-8"));
3236
+ const data = JSON.parse(fs9.readFileSync(profilePath, "utf-8"));
3022
3237
  if (data.profile && ["minimal", "standard", "strict"].includes(data.profile)) {
3023
3238
  profile = data.profile;
3024
3239
  }
@@ -3039,7 +3254,7 @@ function listHooks(projectDir) {
3039
3254
  return result;
3040
3255
  }
3041
3256
  function createListCommand() {
3042
- return new Command26("list").description("Show installed hooks and active profile").action(async (_opts, cmd) => {
3257
+ return new Command27("list").description("Show installed hooks and active profile").action(async (_opts, cmd) => {
3043
3258
  const globalOpts = cmd.optsWithGlobals();
3044
3259
  const projectDir = process.cwd();
3045
3260
  const result = listHooks(projectDir);
@@ -3060,28 +3275,28 @@ function createListCommand() {
3060
3275
  }
3061
3276
 
3062
3277
  // src/commands/hooks/remove.ts
3063
- import { Command as Command27 } from "commander";
3064
- import * as fs8 from "fs";
3278
+ import { Command as Command28 } from "commander";
3279
+ import * as fs10 from "fs";
3065
3280
  import * as path22 from "path";
3066
3281
  function removeHooks(projectDir) {
3067
3282
  const hooksDir = path22.join(projectDir, ".harness", "hooks");
3068
3283
  const settingsPath = path22.join(projectDir, ".claude", "settings.json");
3069
3284
  let removed = false;
3070
3285
  let settingsCleaned = false;
3071
- if (fs8.existsSync(hooksDir)) {
3072
- fs8.rmSync(hooksDir, { recursive: true, force: true });
3286
+ if (fs10.existsSync(hooksDir)) {
3287
+ fs10.rmSync(hooksDir, { recursive: true, force: true });
3073
3288
  removed = true;
3074
3289
  }
3075
- if (fs8.existsSync(settingsPath)) {
3290
+ if (fs10.existsSync(settingsPath)) {
3076
3291
  try {
3077
- const settings = JSON.parse(fs8.readFileSync(settingsPath, "utf-8"));
3292
+ const settings = JSON.parse(fs10.readFileSync(settingsPath, "utf-8"));
3078
3293
  if (settings.hooks !== void 0) {
3079
3294
  delete settings.hooks;
3080
3295
  settingsCleaned = true;
3081
3296
  if (Object.keys(settings).length === 0) {
3082
- fs8.unlinkSync(settingsPath);
3297
+ fs10.unlinkSync(settingsPath);
3083
3298
  } else {
3084
- fs8.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
3299
+ fs10.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
3085
3300
  }
3086
3301
  }
3087
3302
  } catch {
@@ -3090,7 +3305,7 @@ function removeHooks(projectDir) {
3090
3305
  return { removed, hooksDir, settingsCleaned };
3091
3306
  }
3092
3307
  function createRemoveCommand() {
3093
- return new Command27("remove").description("Remove harness-managed hooks from the current project").action(async (_opts, cmd) => {
3308
+ return new Command28("remove").description("Remove harness-managed hooks from the current project").action(async (_opts, cmd) => {
3094
3309
  const globalOpts = cmd.optsWithGlobals();
3095
3310
  const projectDir = process.cwd();
3096
3311
  const result = removeHooks(projectDir);
@@ -3112,8 +3327,8 @@ function createRemoveCommand() {
3112
3327
  }
3113
3328
 
3114
3329
  // src/commands/hooks/add.ts
3115
- import { Command as Command28 } from "commander";
3116
- import * as fs9 from "fs";
3330
+ import { Command as Command29 } from "commander";
3331
+ import * as fs11 from "fs";
3117
3332
  import * as path23 from "path";
3118
3333
  import { fileURLToPath as fileURLToPath3 } from "url";
3119
3334
  var __dirname3 = path23.dirname(fileURLToPath3(import.meta.url));
@@ -3122,11 +3337,11 @@ var ALIASES = {
3122
3337
  };
3123
3338
  function hookSourceDir() {
3124
3339
  const d = path23.resolve(__dirname3, "..", "..", "hooks");
3125
- if (fs9.existsSync(d)) return d;
3340
+ if (fs11.existsSync(d)) return d;
3126
3341
  throw new Error(`Hook scripts not found: ${d}`);
3127
3342
  }
3128
3343
  function readJson(p) {
3129
- return fs9.existsSync(p) ? JSON.parse(fs9.readFileSync(p, "utf-8")) : {};
3344
+ return fs11.existsSync(p) ? JSON.parse(fs11.readFileSync(p, "utf-8")) : {};
3130
3345
  }
3131
3346
  function registerHook(s, ev, matcher, name) {
3132
3347
  if (!s.hooks[ev]) s.hooks[ev] = [];
@@ -3140,9 +3355,9 @@ function addHooks(hookName, projectDir) {
3140
3355
  const result = { added: [], alreadyInstalled: [], notFound: [] };
3141
3356
  const srcDir = hookSourceDir();
3142
3357
  const destDir = path23.join(projectDir, ".harness", "hooks");
3143
- fs9.mkdirSync(destDir, { recursive: true });
3358
+ fs11.mkdirSync(destDir, { recursive: true });
3144
3359
  const claudeDir = path23.join(projectDir, ".claude");
3145
- fs9.mkdirSync(claudeDir, { recursive: true });
3360
+ fs11.mkdirSync(claudeDir, { recursive: true });
3146
3361
  const settingsPath = path23.join(claudeDir, "settings.json");
3147
3362
  const settings = readJson(settingsPath);
3148
3363
  if (!settings.hooks) settings.hooks = {};
@@ -3154,22 +3369,22 @@ function addHooks(hookName, projectDir) {
3154
3369
  }
3155
3370
  const src = path23.join(srcDir, `${name}.js`);
3156
3371
  const dest = path23.join(destDir, `${name}.js`);
3157
- if (fs9.existsSync(dest)) {
3372
+ if (fs11.existsSync(dest)) {
3158
3373
  result.alreadyInstalled.push(name);
3159
- } else if (!fs9.existsSync(src)) {
3374
+ } else if (!fs11.existsSync(src)) {
3160
3375
  result.notFound.push(name);
3161
3376
  continue;
3162
3377
  } else {
3163
- fs9.copyFileSync(src, dest);
3378
+ fs11.copyFileSync(src, dest);
3164
3379
  result.added.push(name);
3165
3380
  }
3166
3381
  registerHook(settings, def.event, def.matcher, name);
3167
3382
  }
3168
- fs9.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
3383
+ fs11.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
3169
3384
  return result;
3170
3385
  }
3171
3386
  function createAddCommand2() {
3172
- return new Command28("add").argument("<hook-name>", "Hook name or alias (e.g., sentinel)").description("Add a hook without changing the profile").action(async (hookName, _opts, cmd) => {
3387
+ 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) => {
3173
3388
  const projectDir = process.cwd();
3174
3389
  try {
3175
3390
  const res = addHooks(hookName, projectDir);
@@ -3194,7 +3409,7 @@ function createAddCommand2() {
3194
3409
 
3195
3410
  // src/commands/hooks/index.ts
3196
3411
  function createHooksCommand() {
3197
- const command = new Command29("hooks").description("Manage Claude Code hook configurations");
3412
+ const command = new Command30("hooks").description("Manage Claude Code hook configurations");
3198
3413
  command.addCommand(createInitCommand2());
3199
3414
  command.addCommand(createListCommand());
3200
3415
  command.addCommand(createRemoveCommand());
@@ -3203,10 +3418,10 @@ function createHooksCommand() {
3203
3418
  }
3204
3419
 
3205
3420
  // src/commands/impact-preview.ts
3206
- import { Command as Command30 } from "commander";
3421
+ import { Command as Command31 } from "commander";
3207
3422
  import { execSync as execSync4 } from "child_process";
3208
3423
  import * as path24 from "path";
3209
- import * as fs10 from "fs";
3424
+ import * as fs12 from "fs";
3210
3425
  function getStagedFiles(cwd) {
3211
3426
  try {
3212
3427
  const output = execSync4("git diff --cached --name-only", {
@@ -3220,7 +3435,7 @@ function getStagedFiles(cwd) {
3220
3435
  }
3221
3436
  function graphExists(projectPath) {
3222
3437
  try {
3223
- return fs10.existsSync(path24.join(projectPath, ".harness", "graph", "graph.json"));
3438
+ return fs12.existsSync(path24.join(projectPath, ".harness", "graph", "graph.json"));
3224
3439
  } catch {
3225
3440
  return false;
3226
3441
  }
@@ -3402,7 +3617,7 @@ async function runImpactPreview(options) {
3402
3617
  return formatImpactOutput(stagedFiles, acc, options);
3403
3618
  }
3404
3619
  function createImpactPreviewCommand() {
3405
- const command = new Command30("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) => {
3620
+ 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) => {
3406
3621
  const output = await runImpactPreview({
3407
3622
  detailed: opts.detailed,
3408
3623
  perFile: opts.perFile,
@@ -3415,28 +3630,29 @@ function createImpactPreviewCommand() {
3415
3630
  }
3416
3631
 
3417
3632
  // src/commands/init.ts
3418
- import { Command as Command32 } from "commander";
3633
+ init_dist();
3634
+ import { Command as Command33 } from "commander";
3419
3635
  import chalk3 from "chalk";
3420
- import * as fs13 from "fs";
3636
+ import * as fs15 from "fs";
3421
3637
  import * as path27 from "path";
3422
3638
 
3423
3639
  // src/commands/setup-mcp.ts
3424
- import { Command as Command31 } from "commander";
3425
- import * as fs12 from "fs";
3640
+ import { Command as Command32 } from "commander";
3641
+ import * as fs14 from "fs";
3426
3642
  import * as path26 from "path";
3427
3643
  import * as os2 from "os";
3428
3644
  import chalk2 from "chalk";
3429
3645
  import * as clack from "@clack/prompts";
3430
3646
 
3431
3647
  // src/integrations/toml.ts
3432
- import * as fs11 from "fs";
3648
+ import * as fs13 from "fs";
3433
3649
  import * as path25 from "path";
3434
3650
  function writeTomlMcpEntry(filePath, name, entry) {
3435
3651
  const dir = path25.dirname(filePath);
3436
- if (!fs11.existsSync(dir)) {
3437
- fs11.mkdirSync(dir, { recursive: true });
3652
+ if (!fs13.existsSync(dir)) {
3653
+ fs13.mkdirSync(dir, { recursive: true });
3438
3654
  }
3439
- const existing = fs11.existsSync(filePath) ? fs11.readFileSync(filePath, "utf-8") : "";
3655
+ const existing = fs13.existsSync(filePath) ? fs13.readFileSync(filePath, "utf-8") : "";
3440
3656
  const blockHeader = `[mcp_servers.${name}]`;
3441
3657
  const newBlock = serializeTomlMcpBlock(name, entry);
3442
3658
  let updated;
@@ -3447,8 +3663,8 @@ function writeTomlMcpEntry(filePath, name, entry) {
3447
3663
  updated = existing + separator + newBlock;
3448
3664
  }
3449
3665
  const tmp = filePath + ".tmp";
3450
- fs11.writeFileSync(tmp, updated);
3451
- fs11.renameSync(tmp, filePath);
3666
+ fs13.writeFileSync(tmp, updated);
3667
+ fs13.renameSync(tmp, filePath);
3452
3668
  }
3453
3669
  function serializeTomlMcpBlock(name, entry) {
3454
3670
  const lines = [`[mcp_servers.${name}]`];
@@ -3506,6 +3722,7 @@ var CURSOR_CURATED_TOOLS = [
3506
3722
  "get_impact",
3507
3723
  "detect_entropy",
3508
3724
  "run_security_scan",
3725
+ "get_security_trends",
3509
3726
  "assess_project",
3510
3727
  "manage_state",
3511
3728
  "create_self_review",
@@ -3550,6 +3767,7 @@ var ALL_MCP_TOOLS = [
3550
3767
  "ingest_source",
3551
3768
  "generate_agent_definitions",
3552
3769
  "run_security_scan",
3770
+ "get_security_trends",
3553
3771
  "check_performance",
3554
3772
  "get_perf_baselines",
3555
3773
  "update_perf_baselines",
@@ -3576,7 +3794,8 @@ var ALL_MCP_TOOLS = [
3576
3794
  "recommend_skills",
3577
3795
  "compute_blast_radius",
3578
3796
  "dispatch_skills",
3579
- "compact"
3797
+ "compact",
3798
+ "detect_constraint_emergence"
3580
3799
  ];
3581
3800
  async function runCursorToolPicker() {
3582
3801
  try {
@@ -3604,20 +3823,20 @@ function writeCursorMcpEntryWithTools(configPath, tools) {
3604
3823
  });
3605
3824
  }
3606
3825
  function readJsonFile(filePath) {
3607
- if (!fs12.existsSync(filePath)) return null;
3826
+ if (!fs14.existsSync(filePath)) return null;
3608
3827
  try {
3609
- return JSON.parse(fs12.readFileSync(filePath, "utf-8"));
3828
+ return JSON.parse(fs14.readFileSync(filePath, "utf-8"));
3610
3829
  } catch {
3611
- fs12.copyFileSync(filePath, filePath + ".bak");
3830
+ fs14.copyFileSync(filePath, filePath + ".bak");
3612
3831
  return null;
3613
3832
  }
3614
3833
  }
3615
3834
  function writeJsonFile(filePath, data) {
3616
3835
  const dir = path26.dirname(filePath);
3617
- if (!fs12.existsSync(dir)) {
3618
- fs12.mkdirSync(dir, { recursive: true });
3836
+ if (!fs14.existsSync(dir)) {
3837
+ fs14.mkdirSync(dir, { recursive: true });
3619
3838
  }
3620
- fs12.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
3839
+ fs14.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
3621
3840
  }
3622
3841
  function configureMcpServer(configPath) {
3623
3842
  const config = readJsonFile(configPath) ?? {};
@@ -3665,8 +3884,8 @@ function setupMcp(cwd, client) {
3665
3884
  if (client === "all" || client === "codex") {
3666
3885
  const configPath = path26.join(cwd, ".codex", "config.toml");
3667
3886
  const alreadyConfigured = (() => {
3668
- if (!fs12.existsSync(configPath)) return false;
3669
- const content = fs12.readFileSync(configPath, "utf-8");
3887
+ if (!fs14.existsSync(configPath)) return false;
3888
+ const content = fs14.readFileSync(configPath, "utf-8");
3670
3889
  return content.includes("[mcp_servers.harness]");
3671
3890
  })();
3672
3891
  if (alreadyConfigured) {
@@ -3739,7 +3958,7 @@ function printMcpResult(configured, skipped, trustedFolder) {
3739
3958
  console.log("");
3740
3959
  }
3741
3960
  function createSetupMcpCommand() {
3742
- return new Command31("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) => {
3961
+ 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) => {
3743
3962
  const globalOpts = cmd.optsWithGlobals();
3744
3963
  const cwd = process.cwd();
3745
3964
  let configured;
@@ -3771,7 +3990,7 @@ function resolveInitDefaults(options) {
3771
3990
  async function runInit(options) {
3772
3991
  const { cwd, name, force } = resolveInitDefaults(options);
3773
3992
  const configPath = path27.join(cwd, "harness.config.json");
3774
- if (!force && fs13.existsSync(configPath)) {
3993
+ if (!force && fs15.existsSync(configPath)) {
3775
3994
  return Err(
3776
3995
  new CLIError("Project already initialized. Use --force to overwrite.", ExitCode.ERROR)
3777
3996
  );
@@ -3879,11 +4098,11 @@ async function runInitAction2(opts) {
3879
4098
  process.exit(ExitCode.SUCCESS);
3880
4099
  }
3881
4100
  function createInitCommand3() {
3882
- const command = new Command32("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) => {
4101
+ 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) => {
3883
4102
  const globalOpts = cmd.optsWithGlobals();
3884
4103
  await runInitAction2({
3885
4104
  name: opts.name,
3886
- level: opts.level,
4105
+ level: opts.template ?? opts.level,
3887
4106
  framework: opts.framework,
3888
4107
  language: opts.language,
3889
4108
  force: opts.force,
@@ -3894,15 +4113,15 @@ function createInitCommand3() {
3894
4113
  }
3895
4114
 
3896
4115
  // src/commands/install.ts
3897
- import * as fs18 from "fs";
4116
+ import * as fs20 from "fs";
3898
4117
  import * as os5 from "os";
3899
4118
  import * as path32 from "path";
3900
4119
  import { execFileSync as execFileSync3 } from "child_process";
3901
- import { Command as Command33 } from "commander";
4120
+ import { Command as Command34 } from "commander";
3902
4121
  import { parse as yamlParse } from "yaml";
3903
4122
 
3904
4123
  // src/registry/npm-client.ts
3905
- import * as fs14 from "fs";
4124
+ import * as fs16 from "fs";
3906
4125
  import * as path28 from "path";
3907
4126
  import * as os3 from "os";
3908
4127
  var NPM_REGISTRY = "https://registry.npmjs.org";
@@ -3929,7 +4148,7 @@ function readNpmrcToken(registryUrl) {
3929
4148
  const candidates = [path28.join(process.cwd(), ".npmrc"), path28.join(os3.homedir(), ".npmrc")];
3930
4149
  for (const npmrcPath of candidates) {
3931
4150
  try {
3932
- const content = fs14.readFileSync(npmrcPath, "utf-8");
4151
+ const content = fs16.readFileSync(npmrcPath, "utf-8");
3933
4152
  for (const line of content.split("\n")) {
3934
4153
  const trimmed = line.trim();
3935
4154
  if (trimmed.startsWith(registryPath)) {
@@ -4024,19 +4243,19 @@ async function searchNpmRegistry(query, registryUrl) {
4024
4243
  }
4025
4244
 
4026
4245
  // src/registry/tarball.ts
4027
- import * as fs15 from "fs";
4246
+ import * as fs17 from "fs";
4028
4247
  import * as path29 from "path";
4029
4248
  import * as os4 from "os";
4030
4249
  import { execFileSync as execFileSync2 } from "child_process";
4031
4250
  function extractTarball(tarballBuffer) {
4032
- const tmpDir = fs15.mkdtempSync(path29.join(os4.tmpdir(), "harness-skill-install-"));
4251
+ const tmpDir = fs17.mkdtempSync(path29.join(os4.tmpdir(), "harness-skill-install-"));
4033
4252
  const tarballPath = path29.join(tmpDir, "package.tgz");
4034
4253
  try {
4035
- fs15.writeFileSync(tarballPath, tarballBuffer);
4254
+ fs17.writeFileSync(tarballPath, tarballBuffer);
4036
4255
  execFileSync2("tar", ["-xzf", tarballPath, "-C", tmpDir], {
4037
4256
  timeout: 3e4
4038
4257
  });
4039
- fs15.unlinkSync(tarballPath);
4258
+ fs17.unlinkSync(tarballPath);
4040
4259
  } catch (err) {
4041
4260
  cleanupTempDir(tmpDir);
4042
4261
  throw new Error(
@@ -4047,22 +4266,22 @@ function extractTarball(tarballBuffer) {
4047
4266
  return tmpDir;
4048
4267
  }
4049
4268
  function placeSkillContent(extractedPkgDir, communityBaseDir, skillName, platforms) {
4050
- const files = fs15.readdirSync(extractedPkgDir);
4269
+ const files = fs17.readdirSync(extractedPkgDir);
4051
4270
  for (const platform of platforms) {
4052
4271
  const targetDir = path29.join(communityBaseDir, platform, skillName);
4053
- if (fs15.existsSync(targetDir)) {
4054
- fs15.rmSync(targetDir, { recursive: true, force: true });
4272
+ if (fs17.existsSync(targetDir)) {
4273
+ fs17.rmSync(targetDir, { recursive: true, force: true });
4055
4274
  }
4056
- fs15.mkdirSync(targetDir, { recursive: true });
4275
+ fs17.mkdirSync(targetDir, { recursive: true });
4057
4276
  for (const file of files) {
4058
4277
  if (file === "package.json" || file === "node_modules") continue;
4059
4278
  const srcPath = path29.join(extractedPkgDir, file);
4060
4279
  const destPath = path29.join(targetDir, file);
4061
- const stat = fs15.statSync(srcPath);
4280
+ const stat = fs17.statSync(srcPath);
4062
4281
  if (stat.isDirectory()) {
4063
- fs15.cpSync(srcPath, destPath, { recursive: true });
4282
+ fs17.cpSync(srcPath, destPath, { recursive: true });
4064
4283
  } else {
4065
- fs15.copyFileSync(srcPath, destPath);
4284
+ fs17.copyFileSync(srcPath, destPath);
4066
4285
  }
4067
4286
  }
4068
4287
  }
@@ -4070,14 +4289,14 @@ function placeSkillContent(extractedPkgDir, communityBaseDir, skillName, platfor
4070
4289
  function removeSkillContent(communityBaseDir, skillName, platforms) {
4071
4290
  for (const platform of platforms) {
4072
4291
  const targetDir = path29.join(communityBaseDir, platform, skillName);
4073
- if (fs15.existsSync(targetDir)) {
4074
- fs15.rmSync(targetDir, { recursive: true, force: true });
4292
+ if (fs17.existsSync(targetDir)) {
4293
+ fs17.rmSync(targetDir, { recursive: true, force: true });
4075
4294
  }
4076
4295
  }
4077
4296
  }
4078
4297
  function cleanupTempDir(dirPath) {
4079
4298
  try {
4080
- fs15.rmSync(dirPath, { recursive: true, force: true });
4299
+ fs17.rmSync(dirPath, { recursive: true, force: true });
4081
4300
  } catch {
4082
4301
  }
4083
4302
  }
@@ -4119,7 +4338,7 @@ function findDependentsOf(lockfile, targetPackageName) {
4119
4338
  }
4120
4339
 
4121
4340
  // src/registry/lockfile.ts
4122
- import * as fs16 from "fs";
4341
+ import * as fs18 from "fs";
4123
4342
  import * as path30 from "path";
4124
4343
  function createEmptyLockfile() {
4125
4344
  return { version: 1, skills: {} };
@@ -4140,10 +4359,10 @@ function sortedStringify(obj) {
4140
4359
  );
4141
4360
  }
4142
4361
  function readLockfile2(filePath) {
4143
- if (!fs16.existsSync(filePath)) {
4362
+ if (!fs18.existsSync(filePath)) {
4144
4363
  return createEmptyLockfile();
4145
4364
  }
4146
- const raw = fs16.readFileSync(filePath, "utf-8");
4365
+ const raw = fs18.readFileSync(filePath, "utf-8");
4147
4366
  let parsed;
4148
4367
  try {
4149
4368
  parsed = JSON.parse(raw);
@@ -4161,8 +4380,8 @@ function readLockfile2(filePath) {
4161
4380
  }
4162
4381
  function writeLockfile2(filePath, lockfile) {
4163
4382
  const dir = path30.dirname(filePath);
4164
- fs16.mkdirSync(dir, { recursive: true });
4165
- fs16.writeFileSync(filePath, sortedStringify(lockfile) + "\n", "utf-8");
4383
+ fs18.mkdirSync(dir, { recursive: true });
4384
+ fs18.writeFileSync(filePath, sortedStringify(lockfile) + "\n", "utf-8");
4166
4385
  }
4167
4386
  function updateLockfileEntry(lockfile, name, entry) {
4168
4387
  return {
@@ -4185,17 +4404,17 @@ function removeLockfileEntry(lockfile, name) {
4185
4404
  }
4186
4405
 
4187
4406
  // src/registry/bundled-skills.ts
4188
- import * as fs17 from "fs";
4407
+ import * as fs19 from "fs";
4189
4408
  import * as path31 from "path";
4190
4409
  function getBundledSkillNames(bundledSkillsDir) {
4191
- if (!fs17.existsSync(bundledSkillsDir)) {
4410
+ if (!fs19.existsSync(bundledSkillsDir)) {
4192
4411
  return /* @__PURE__ */ new Set();
4193
4412
  }
4194
- const entries = fs17.readdirSync(bundledSkillsDir);
4413
+ const entries = fs19.readdirSync(bundledSkillsDir);
4195
4414
  const names = /* @__PURE__ */ new Set();
4196
4415
  for (const entry of entries) {
4197
4416
  try {
4198
- const stat = fs17.statSync(path31.join(bundledSkillsDir, String(entry)));
4417
+ const stat = fs19.statSync(path31.join(bundledSkillsDir, String(entry)));
4199
4418
  if (stat.isDirectory()) {
4200
4419
  names.add(String(entry));
4201
4420
  }
@@ -4243,7 +4462,7 @@ function parseGitHubRef(from) {
4243
4462
  return null;
4244
4463
  }
4245
4464
  function cloneGitHubRepo(owner, repo, ref) {
4246
- const tmpDir = fs18.mkdtempSync(path32.join(os5.tmpdir(), "harness-gh-install-"));
4465
+ const tmpDir = fs20.mkdtempSync(path32.join(os5.tmpdir(), "harness-gh-install-"));
4247
4466
  const url = `https://github.com/${owner}/${repo}.git`;
4248
4467
  try {
4249
4468
  const cloneArgs = ["clone", "--depth", "1"];
@@ -4264,12 +4483,12 @@ function discoverSkillDirs(rootDir) {
4264
4483
  const skillDirs = [];
4265
4484
  function scan(dir, depth) {
4266
4485
  if (depth > 3) return;
4267
- if (!fs18.existsSync(dir)) return;
4268
- if (fs18.existsSync(path32.join(dir, "skill.yaml"))) {
4486
+ if (!fs20.existsSync(dir)) return;
4487
+ if (fs20.existsSync(path32.join(dir, "skill.yaml"))) {
4269
4488
  skillDirs.push(dir);
4270
4489
  return;
4271
4490
  }
4272
- const entries = fs18.readdirSync(dir, { withFileTypes: true });
4491
+ const entries = fs20.readdirSync(dir, { withFileTypes: true });
4273
4492
  for (const entry of entries) {
4274
4493
  if (!entry.isDirectory()) continue;
4275
4494
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
@@ -4281,15 +4500,15 @@ function discoverSkillDirs(rootDir) {
4281
4500
  }
4282
4501
  function resolveLocalPkgDir(fromPath) {
4283
4502
  const resolvedPath = path32.resolve(fromPath);
4284
- if (!fs18.existsSync(resolvedPath)) {
4503
+ if (!fs20.existsSync(resolvedPath)) {
4285
4504
  throw new Error(`--from path does not exist: ${resolvedPath}`);
4286
4505
  }
4287
- const stat = fs18.statSync(resolvedPath);
4506
+ const stat = fs20.statSync(resolvedPath);
4288
4507
  if (stat.isDirectory()) {
4289
4508
  return { pkgDir: resolvedPath, extractDir: null };
4290
4509
  }
4291
4510
  if (resolvedPath.endsWith(".tgz") || resolvedPath.endsWith(".tar.gz")) {
4292
- const tarballBuffer = fs18.readFileSync(resolvedPath);
4511
+ const tarballBuffer = fs20.readFileSync(resolvedPath);
4293
4512
  const extractDir = extractTarball(tarballBuffer);
4294
4513
  return { pkgDir: path32.join(extractDir, "package"), extractDir };
4295
4514
  }
@@ -4297,10 +4516,10 @@ function resolveLocalPkgDir(fromPath) {
4297
4516
  }
4298
4517
  function installSkillDir(pkgDir, resolvedPath, options) {
4299
4518
  const skillYamlPath = path32.join(pkgDir, "skill.yaml");
4300
- if (!fs18.existsSync(skillYamlPath)) {
4519
+ if (!fs20.existsSync(skillYamlPath)) {
4301
4520
  throw new Error(`No skill.yaml found at ${skillYamlPath}`);
4302
4521
  }
4303
- const skillYaml = validateSkillYaml(yamlParse(fs18.readFileSync(skillYamlPath, "utf-8")));
4522
+ const skillYaml = validateSkillYaml(yamlParse(fs20.readFileSync(skillYamlPath, "utf-8")));
4304
4523
  const shortName = skillYaml.name;
4305
4524
  const { communityBase, lockfilePath } = resolveCommunityBase(options.global ?? false);
4306
4525
  if (!options.global) {
@@ -4372,7 +4591,7 @@ async function runInstall(skillName, options) {
4372
4591
  };
4373
4592
  }
4374
4593
  const resolvedFrom = path32.resolve(options.from);
4375
- if (fs18.existsSync(resolvedFrom) && fs18.statSync(resolvedFrom).isDirectory() && !fs18.existsSync(path32.join(resolvedFrom, "skill.yaml"))) {
4594
+ if (fs20.existsSync(resolvedFrom) && fs20.statSync(resolvedFrom).isDirectory() && !fs20.existsSync(path32.join(resolvedFrom, "skill.yaml"))) {
4376
4595
  const results = await runBulkInstall(resolvedFrom, options);
4377
4596
  const installed = results.filter((r) => r.installed);
4378
4597
  return {
@@ -4415,10 +4634,10 @@ async function runInstall(skillName, options) {
4415
4634
  try {
4416
4635
  const extractedPkgDir = path32.join(extractDir, "package");
4417
4636
  const skillYamlPath = path32.join(extractedPkgDir, "skill.yaml");
4418
- if (!fs18.existsSync(skillYamlPath)) {
4637
+ if (!fs20.existsSync(skillYamlPath)) {
4419
4638
  throw new Error(`contains invalid skill.yaml: file not found in package`);
4420
4639
  }
4421
- const rawYaml = fs18.readFileSync(skillYamlPath, "utf-8");
4640
+ const rawYaml = fs20.readFileSync(skillYamlPath, "utf-8");
4422
4641
  const parsed = yamlParse(rawYaml);
4423
4642
  skillYaml = validateSkillYaml(parsed);
4424
4643
  placeSkillContent(extractedPkgDir, communityBase, shortName, skillYaml.platforms);
@@ -4458,7 +4677,7 @@ async function runInstall(skillName, options) {
4458
4677
  return result;
4459
4678
  }
4460
4679
  function createInstallCommand() {
4461
- const cmd = new Command33("install");
4680
+ const cmd = new Command34("install");
4462
4681
  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(
4463
4682
  "--from <source>",
4464
4683
  "Install from local path, directory, or GitHub (github:owner/repo, https://github.com/owner/repo)"
@@ -4491,15 +4710,16 @@ function createInstallCommand() {
4491
4710
  }
4492
4711
 
4493
4712
  // src/commands/install-constraints.ts
4494
- import * as fs19 from "fs/promises";
4713
+ init_dist();
4714
+ import * as fs21 from "fs/promises";
4495
4715
  import * as path33 from "path";
4496
- import { Command as Command34 } from "commander";
4716
+ import { Command as Command35 } from "commander";
4497
4717
  import semver3 from "semver";
4498
4718
  async function runInstallConstraints(options) {
4499
4719
  const { source, configPath, lockfilePath } = options;
4500
4720
  let rawBundle;
4501
4721
  try {
4502
- rawBundle = await fs19.readFile(source, "utf-8");
4722
+ rawBundle = await fs21.readFile(source, "utf-8");
4503
4723
  } catch (err) {
4504
4724
  if (isNodeError(err) && err.code === "ENOENT") {
4505
4725
  return { ok: false, error: `Bundle file not found: ${source}` };
@@ -4542,7 +4762,7 @@ async function runInstallConstraints(options) {
4542
4762
  }
4543
4763
  let localConfig;
4544
4764
  try {
4545
- const raw = await fs19.readFile(configPath, "utf-8");
4765
+ const raw = await fs21.readFile(configPath, "utf-8");
4546
4766
  localConfig = JSON.parse(raw);
4547
4767
  } catch (err) {
4548
4768
  return {
@@ -4747,17 +4967,18 @@ async function handleInstallConstraints(source, opts) {
4747
4967
  logInstallResult(result.value, opts);
4748
4968
  }
4749
4969
  function createInstallConstraintsCommand() {
4750
- const cmd = new Command34("install-constraints");
4970
+ const cmd = new Command35("install-constraints");
4751
4971
  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);
4752
4972
  return cmd;
4753
4973
  }
4754
4974
 
4755
4975
  // src/commands/integrations/index.ts
4756
- import { Command as Command39 } from "commander";
4976
+ import { Command as Command40 } from "commander";
4757
4977
 
4758
4978
  // src/commands/integrations/add.ts
4759
- import { Command as Command35 } from "commander";
4760
- import * as fs20 from "fs";
4979
+ init_dist();
4980
+ import { Command as Command36 } from "commander";
4981
+ import * as fs22 from "fs";
4761
4982
  import * as path34 from "path";
4762
4983
  import chalk4 from "chalk";
4763
4984
  function buildMcpEntry(def) {
@@ -4769,7 +4990,7 @@ function buildMcpEntry(def) {
4769
4990
  function writeMcpEntries(cwd, defName, mcpEntry) {
4770
4991
  writeMcpEntry(path34.join(cwd, ".mcp.json"), defName, mcpEntry);
4771
4992
  const geminiDir = path34.join(cwd, ".gemini");
4772
- if (fs20.existsSync(geminiDir)) {
4993
+ if (fs22.existsSync(geminiDir)) {
4773
4994
  writeMcpEntry(path34.join(geminiDir, "settings.json"), defName, mcpEntry);
4774
4995
  }
4775
4996
  }
@@ -4820,7 +5041,7 @@ function printAddSuccess(value) {
4820
5041
  }
4821
5042
  }
4822
5043
  function createAddIntegrationCommand() {
4823
- return new Command35("add").description("Enable an MCP integration").argument("<name>", "Integration name (e.g. perplexity, augment-code)").action(async (name, _opts, cmd) => {
5044
+ return new Command36("add").description("Enable an MCP integration").argument("<name>", "Integration name (e.g. perplexity, augment-code)").action(async (name, _opts, cmd) => {
4824
5045
  const globalOpts = cmd.optsWithGlobals();
4825
5046
  const result = addIntegration(process.cwd(), name);
4826
5047
  if (!result.ok) {
@@ -4834,7 +5055,7 @@ function createAddIntegrationCommand() {
4834
5055
  }
4835
5056
 
4836
5057
  // src/commands/integrations/list.ts
4837
- import { Command as Command36 } from "commander";
5058
+ import { Command as Command37 } from "commander";
4838
5059
  import * as path35 from "path";
4839
5060
  import chalk5 from "chalk";
4840
5061
  function printTier0Integrations(tier0, mcpServers) {
@@ -4891,12 +5112,13 @@ async function runListIntegrations(globalOpts) {
4891
5112
  process.exit(ExitCode.SUCCESS);
4892
5113
  }
4893
5114
  function createListIntegrationsCommand() {
4894
- return new Command36("list").description("Show all MCP integrations with status").action(async (_opts, cmd) => runListIntegrations(cmd.optsWithGlobals()));
5115
+ return new Command37("list").description("Show all MCP integrations with status").action(async (_opts, cmd) => runListIntegrations(cmd.optsWithGlobals()));
4895
5116
  }
4896
5117
 
4897
5118
  // src/commands/integrations/remove.ts
4898
- import { Command as Command37 } from "commander";
4899
- import * as fs21 from "fs";
5119
+ init_dist();
5120
+ import { Command as Command38 } from "commander";
5121
+ import * as fs23 from "fs";
4900
5122
  import * as path36 from "path";
4901
5123
  function removeIntegration(cwd, name) {
4902
5124
  const def = INTEGRATION_REGISTRY.find((i) => i.name === name);
@@ -4911,7 +5133,7 @@ function removeIntegration(cwd, name) {
4911
5133
  const mcpPath = path36.join(cwd, ".mcp.json");
4912
5134
  removeMcpEntry(mcpPath, def.name);
4913
5135
  const geminiDir = path36.join(cwd, ".gemini");
4914
- if (fs21.existsSync(geminiDir)) {
5136
+ if (fs23.existsSync(geminiDir)) {
4915
5137
  const geminiPath = path36.join(geminiDir, "settings.json");
4916
5138
  removeMcpEntry(geminiPath, def.name);
4917
5139
  }
@@ -4922,7 +5144,7 @@ function removeIntegration(cwd, name) {
4922
5144
  return Ok(def.displayName);
4923
5145
  }
4924
5146
  function createRemoveIntegrationCommand() {
4925
- return new Command37("remove").description("Remove an MCP integration").argument("<name>", "Integration name (e.g. perplexity, augment-code)").action(async (name, _opts, cmd) => {
5147
+ return new Command38("remove").description("Remove an MCP integration").argument("<name>", "Integration name (e.g. perplexity, augment-code)").action(async (name, _opts, cmd) => {
4926
5148
  const globalOpts = cmd.optsWithGlobals();
4927
5149
  const cwd = process.cwd();
4928
5150
  const result = removeIntegration(cwd, name);
@@ -4943,7 +5165,8 @@ function createRemoveIntegrationCommand() {
4943
5165
  }
4944
5166
 
4945
5167
  // src/commands/integrations/dismiss.ts
4946
- import { Command as Command38 } from "commander";
5168
+ init_dist();
5169
+ import { Command as Command39 } from "commander";
4947
5170
  import * as path37 from "path";
4948
5171
  function dismissIntegration(cwd, name) {
4949
5172
  const def = INTEGRATION_REGISTRY.find((i) => i.name === name);
@@ -4965,7 +5188,7 @@ function dismissIntegration(cwd, name) {
4965
5188
  return Ok(def.displayName);
4966
5189
  }
4967
5190
  function createDismissIntegrationCommand() {
4968
- return new Command38("dismiss").description("Suppress doctor recommendations for an integration").argument("<name>", "Integration name (e.g. perplexity, augment-code)").action(async (name, _opts, cmd) => {
5191
+ 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) => {
4969
5192
  const globalOpts = cmd.optsWithGlobals();
4970
5193
  const cwd = process.cwd();
4971
5194
  const result = dismissIntegration(cwd, name);
@@ -4989,7 +5212,7 @@ function createDismissIntegrationCommand() {
4989
5212
 
4990
5213
  // src/commands/integrations/index.ts
4991
5214
  function createIntegrationsCommand() {
4992
- const command = new Command39("integrations").description(
5215
+ const command = new Command40("integrations").description(
4993
5216
  "Manage MCP peer integrations (add, list, remove, dismiss)"
4994
5217
  );
4995
5218
  command.addCommand(createListIntegrationsCommand());
@@ -5000,10 +5223,11 @@ function createIntegrationsCommand() {
5000
5223
  }
5001
5224
 
5002
5225
  // src/commands/learnings/index.ts
5003
- import { Command as Command41 } from "commander";
5226
+ import { Command as Command42 } from "commander";
5004
5227
 
5005
5228
  // src/commands/learnings/prune.ts
5006
- import { Command as Command40 } from "commander";
5229
+ init_dist();
5230
+ import { Command as Command41 } from "commander";
5007
5231
  import * as path38 from "path";
5008
5232
  async function handlePrune(opts) {
5009
5233
  const projectPath = path38.resolve(opts.path);
@@ -5045,23 +5269,23 @@ function printPatternProposals(patterns) {
5045
5269
  );
5046
5270
  }
5047
5271
  function createPruneCommand() {
5048
- return new Command40("prune").description(
5272
+ return new Command41("prune").description(
5049
5273
  "Analyze global learnings for patterns, present improvement proposals, and archive old entries"
5050
5274
  ).option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(handlePrune);
5051
5275
  }
5052
5276
 
5053
5277
  // src/commands/learnings/index.ts
5054
5278
  function createLearningsCommand() {
5055
- const command = new Command41("learnings").description("Learnings management commands");
5279
+ const command = new Command42("learnings").description("Learnings management commands");
5056
5280
  command.addCommand(createPruneCommand());
5057
5281
  return command;
5058
5282
  }
5059
5283
 
5060
5284
  // src/commands/linter/index.ts
5061
- import { Command as Command44 } from "commander";
5285
+ import { Command as Command45 } from "commander";
5062
5286
 
5063
5287
  // src/commands/linter/generate.ts
5064
- import { Command as Command42 } from "commander";
5288
+ import { Command as Command43 } from "commander";
5065
5289
  function formatGenerateError(e) {
5066
5290
  switch (e.type) {
5067
5291
  case "parse":
@@ -5105,7 +5329,7 @@ function handleSuccess(result, useJson) {
5105
5329
  Generated ${result.rulesGenerated.length} rules to ${result.outputDir}`);
5106
5330
  }
5107
5331
  function createGenerateCommand2() {
5108
- return new Command42("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) => {
5332
+ 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) => {
5109
5333
  try {
5110
5334
  if (options.verbose) logger.info(`Parsing config: ${options.config}`);
5111
5335
  const result = await generate({
@@ -5127,9 +5351,9 @@ function createGenerateCommand2() {
5127
5351
  }
5128
5352
 
5129
5353
  // src/commands/linter/validate.ts
5130
- import { Command as Command43 } from "commander";
5354
+ import { Command as Command44 } from "commander";
5131
5355
  function createValidateCommand() {
5132
- return new Command43("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) => {
5356
+ 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) => {
5133
5357
  try {
5134
5358
  const result = await validate({ configPath: options.config });
5135
5359
  if (options.json) {
@@ -5148,7 +5372,7 @@ function createValidateCommand() {
5148
5372
 
5149
5373
  // src/commands/linter/index.ts
5150
5374
  function createLinterCommand() {
5151
- const linter = new Command44("linter").description(
5375
+ const linter = new Command45("linter").description(
5152
5376
  "Generate and validate ESLint rules from YAML config"
5153
5377
  );
5154
5378
  linter.addCommand(createGenerateCommand2());
@@ -5157,21 +5381,60 @@ function createLinterCommand() {
5157
5381
  }
5158
5382
 
5159
5383
  // src/commands/mcp.ts
5160
- import { Command as Command45 } from "commander";
5384
+ import { Command as Command46 } from "commander";
5385
+ var VALID_TIERS = /* @__PURE__ */ new Set(["core", "standard", "full"]);
5386
+ function parseTier(value) {
5387
+ const lower = value.toLowerCase();
5388
+ if (!VALID_TIERS.has(lower)) {
5389
+ throw new Error(`Invalid tier "${value}". Expected one of: core, standard, full.`);
5390
+ }
5391
+ return lower;
5392
+ }
5393
+ function parseBudget(value) {
5394
+ const n = Number.parseInt(value, 10);
5395
+ if (!Number.isFinite(n) || n < 0) {
5396
+ throw new Error(`Invalid --budget-tokens "${value}". Expected a non-negative integer.`);
5397
+ }
5398
+ return n;
5399
+ }
5161
5400
  function createMcpCommand() {
5162
- return new Command45("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) => {
5163
- const { startServer: startServer2 } = await import("./mcp-JZG22CYT.js");
5164
- await startServer2(opts.tools);
5401
+ 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(
5402
+ "--tier <core|standard|full>",
5403
+ "Load a preset tool tier instead of all tools",
5404
+ parseTier
5405
+ ).option(
5406
+ "--budget-tokens <n>",
5407
+ "Auto-select tier to fit this baseline token budget",
5408
+ parseBudget
5409
+ ).action(async (opts) => {
5410
+ const [{ startServer: startServer2, getToolDefinitions: getToolDefinitions2 }, { selectTier }] = await Promise.all([
5411
+ import("./mcp-2553PNUC.js"),
5412
+ import("./tool-tiers-7QGZ3FKY.js")
5413
+ ]);
5414
+ if (opts.tools && opts.tools.length > 0) {
5415
+ await startServer2(opts.tools);
5416
+ return;
5417
+ }
5418
+ if (opts.tier || opts.budgetTokens !== void 0) {
5419
+ const definitions = getToolDefinitions2();
5420
+ const selection = selectTier(definitions, {
5421
+ ...opts.tier ? { overrideTier: opts.tier } : {},
5422
+ ...opts.budgetTokens !== void 0 ? { tokenBudget: opts.budgetTokens } : {}
5423
+ });
5424
+ await startServer2(selection.filter);
5425
+ return;
5426
+ }
5427
+ await startServer2();
5165
5428
  });
5166
5429
  }
5167
5430
 
5168
5431
  // src/commands/orchestrator.ts
5169
- import { Command as Command46 } from "commander";
5432
+ import { Command as Command47 } from "commander";
5170
5433
  import * as path39 from "path";
5171
5434
  import { Orchestrator, WorkflowLoader, launchTUI } from "@harness-engineering/orchestrator";
5172
5435
  function createOrchestratorCommand() {
5173
- const orchestrator = new Command46("orchestrator");
5174
- orchestrator.command("run").description("Run the orchestrator daemon").option("-w, --workflow <path>", "Path to WORKFLOW.md", "WORKFLOW.md").action(async (opts) => {
5436
+ const orchestrator = new Command47("orchestrator");
5437
+ 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) => {
5175
5438
  const workflowPath = path39.resolve(process.cwd(), opts.workflow);
5176
5439
  const loader = new WorkflowLoader();
5177
5440
  const result = await loader.loadWorkflow(workflowPath);
@@ -5188,23 +5451,32 @@ function createOrchestratorCommand() {
5188
5451
  process.on("SIGINT", shutdown);
5189
5452
  process.on("SIGTERM", shutdown);
5190
5453
  daemon.start();
5191
- const { waitUntilExit } = launchTUI(daemon);
5192
- await waitUntilExit();
5454
+ if (opts.headless) {
5455
+ logger.info(
5456
+ "Orchestrator running in headless mode (no TUI). Use the web dashboard or Ctrl+C to stop."
5457
+ );
5458
+ await new Promise(() => {
5459
+ });
5460
+ } else {
5461
+ const { waitUntilExit } = launchTUI(daemon);
5462
+ await waitUntilExit();
5463
+ }
5193
5464
  process.exit(ExitCode.SUCCESS);
5194
5465
  });
5195
5466
  return orchestrator;
5196
5467
  }
5197
5468
 
5198
5469
  // src/commands/perf.ts
5199
- import { Command as Command47 } from "commander";
5470
+ init_dist();
5471
+ import { Command as Command48 } from "commander";
5200
5472
  import * as path40 from "path";
5201
5473
  function registerBenchCommand(perf) {
5202
- perf.command("bench [glob]").description("Run benchmarks via vitest bench").action(async (glob, _opts, cmd) => {
5474
+ perf.command("bench [glob]").description("Run benchmarks via vitest bench").action(async (glob2, _opts, cmd) => {
5203
5475
  const globalOpts = cmd.optsWithGlobals();
5204
5476
  const cwd = process.cwd();
5205
- const { BenchmarkRunner } = await import("./dist-TZQUURSP.js");
5477
+ const { BenchmarkRunner } = await import("./dist-WCSJHQPK.js");
5206
5478
  const runner = new BenchmarkRunner();
5207
- const benchFiles = runner.discover(cwd, glob);
5479
+ const benchFiles = runner.discover(cwd, glob2);
5208
5480
  if (benchFiles.length === 0) {
5209
5481
  if (globalOpts.json) {
5210
5482
  console.log(JSON.stringify({ benchFiles: [], message: "No .bench.ts files found" }));
@@ -5214,7 +5486,7 @@ function registerBenchCommand(perf) {
5214
5486
  return;
5215
5487
  }
5216
5488
  logBenchDiscovery(globalOpts.json, benchFiles);
5217
- const result = await runner.run(glob ? { cwd, glob } : { cwd });
5489
+ const result = await runner.run(glob2 ? { cwd, glob: glob2 } : { cwd });
5218
5490
  outputBenchResults(globalOpts.json, result);
5219
5491
  });
5220
5492
  }
@@ -5266,7 +5538,7 @@ async function runBaselinesShow(globalOpts) {
5266
5538
  }
5267
5539
  async function runBaselinesUpdate(globalOpts) {
5268
5540
  const cwd = process.cwd();
5269
- const { BenchmarkRunner } = await import("./dist-TZQUURSP.js");
5541
+ const { BenchmarkRunner } = await import("./dist-WCSJHQPK.js");
5270
5542
  const runner = new BenchmarkRunner();
5271
5543
  const manager = new BaselineManager(cwd);
5272
5544
  logger.info("Running benchmarks to update baselines...");
@@ -5307,7 +5579,7 @@ function registerReportCommand(perf) {
5307
5579
  perf.command("report").description("Full performance report with metrics, trends, and hotspots").action(async (_opts, cmd) => {
5308
5580
  const globalOpts = cmd.optsWithGlobals();
5309
5581
  const cwd = process.cwd();
5310
- const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-TZQUURSP.js");
5582
+ const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-WCSJHQPK.js");
5311
5583
  const analyzer = new EntropyAnalyzer2({
5312
5584
  rootDir: path40.resolve(cwd),
5313
5585
  analyze: { complexity: true, coupling: true }
@@ -5368,7 +5640,7 @@ function registerCriticalPathsCommand(perf) {
5368
5640
  });
5369
5641
  }
5370
5642
  function createPerfCommand() {
5371
- const perf = new Command47("perf").description("Performance benchmark and baseline management");
5643
+ const perf = new Command48("perf").description("Performance benchmark and baseline management");
5372
5644
  registerBenchCommand(perf);
5373
5645
  registerBaselinesCommands(perf);
5374
5646
  registerReportCommand(perf);
@@ -5377,10 +5649,10 @@ function createPerfCommand() {
5377
5649
  }
5378
5650
 
5379
5651
  // src/commands/persona/index.ts
5380
- import { Command as Command50 } from "commander";
5652
+ import { Command as Command51 } from "commander";
5381
5653
 
5382
5654
  // src/commands/persona/list.ts
5383
- import { Command as Command48 } from "commander";
5655
+ import { Command as Command49 } from "commander";
5384
5656
  function printPersonaList(personas) {
5385
5657
  if (personas.length === 0) {
5386
5658
  logger.info("No personas found.");
@@ -5394,7 +5666,7 @@ function printPersonaList(personas) {
5394
5666
  }
5395
5667
  }
5396
5668
  function createListCommand2() {
5397
- return new Command48("list").description("List available agent personas").action(async (_opts, cmd) => {
5669
+ return new Command49("list").description("List available agent personas").action(async (_opts, cmd) => {
5398
5670
  const globalOpts = cmd.optsWithGlobals();
5399
5671
  const personasDir = resolvePersonasDir();
5400
5672
  const result = listPersonas(personasDir);
@@ -5414,8 +5686,8 @@ function createListCommand2() {
5414
5686
  }
5415
5687
 
5416
5688
  // src/commands/persona/generate.ts
5417
- import { Command as Command49 } from "commander";
5418
- import * as fs22 from "fs";
5689
+ import { Command as Command50 } from "commander";
5690
+ import * as fs24 from "fs";
5419
5691
  import * as path41 from "path";
5420
5692
  function generatePersonaArtifacts(persona, outputDir, only) {
5421
5693
  const slug = toKebabCase(persona.name);
@@ -5424,8 +5696,8 @@ function generatePersonaArtifacts(persona, outputDir, only) {
5424
5696
  const result = generateRuntime(persona);
5425
5697
  if (result.ok) {
5426
5698
  const outPath = path41.join(outputDir, `${slug}.runtime.json`);
5427
- fs22.mkdirSync(path41.dirname(outPath), { recursive: true });
5428
- fs22.writeFileSync(outPath, result.value);
5699
+ fs24.mkdirSync(path41.dirname(outPath), { recursive: true });
5700
+ fs24.writeFileSync(outPath, result.value);
5429
5701
  generated.push(outPath);
5430
5702
  }
5431
5703
  }
@@ -5433,7 +5705,7 @@ function generatePersonaArtifacts(persona, outputDir, only) {
5433
5705
  const result = generateAgentsMd(persona);
5434
5706
  if (result.ok) {
5435
5707
  const outPath = path41.join(outputDir, `${slug}.agents.md`);
5436
- fs22.writeFileSync(outPath, result.value);
5708
+ fs24.writeFileSync(outPath, result.value);
5437
5709
  generated.push(outPath);
5438
5710
  }
5439
5711
  }
@@ -5441,15 +5713,15 @@ function generatePersonaArtifacts(persona, outputDir, only) {
5441
5713
  const result = generateCIWorkflow(persona, "github");
5442
5714
  if (result.ok) {
5443
5715
  const outPath = path41.join(outputDir, ".github", "workflows", `${slug}.yml`);
5444
- fs22.mkdirSync(path41.dirname(outPath), { recursive: true });
5445
- fs22.writeFileSync(outPath, result.value);
5716
+ fs24.mkdirSync(path41.dirname(outPath), { recursive: true });
5717
+ fs24.writeFileSync(outPath, result.value);
5446
5718
  generated.push(outPath);
5447
5719
  }
5448
5720
  }
5449
5721
  return generated;
5450
5722
  }
5451
5723
  function createGenerateCommand3() {
5452
- return new Command49("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) => {
5724
+ 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) => {
5453
5725
  const globalOpts = cmd.optsWithGlobals();
5454
5726
  const personaResult = loadPersona(path41.join(resolvePersonasDir(), `${name}.yaml`));
5455
5727
  if (!personaResult.ok) {
@@ -5471,14 +5743,15 @@ function createGenerateCommand3() {
5471
5743
 
5472
5744
  // src/commands/persona/index.ts
5473
5745
  function createPersonaCommand() {
5474
- const command = new Command50("persona").description("Agent persona management commands");
5746
+ const command = new Command51("persona").description("Agent persona management commands");
5475
5747
  command.addCommand(createListCommand2());
5476
5748
  command.addCommand(createGenerateCommand3());
5477
5749
  return command;
5478
5750
  }
5479
5751
 
5480
5752
  // src/commands/predict.ts
5481
- import { Command as Command51 } from "commander";
5753
+ init_dist();
5754
+ import { Command as Command52 } from "commander";
5482
5755
  import chalk6 from "chalk";
5483
5756
  var CATEGORY_ORDER = [
5484
5757
  "circular-deps",
@@ -5584,7 +5857,7 @@ function handlePredictError(err, mode) {
5584
5857
  process.exit(ExitCode.ERROR);
5585
5858
  }
5586
5859
  function createPredictCommand() {
5587
- const command = new Command51("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) => {
5860
+ 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) => {
5588
5861
  const globalOpts = cmd.optsWithGlobals();
5589
5862
  const mode = globalOpts.json ? OutputMode.JSON : OutputMode.TEXT;
5590
5863
  try {
@@ -5606,8 +5879,137 @@ function createPredictCommand() {
5606
5879
  return command;
5607
5880
  }
5608
5881
 
5882
+ // src/commands/publish-analyses.ts
5883
+ import * as fs25 from "fs";
5884
+ import * as path42 from "path";
5885
+ import { Command as Command53 } from "commander";
5886
+ init_dist();
5887
+ import {
5888
+ renderAnalysisComment,
5889
+ loadPublishedIndex,
5890
+ savePublishedIndex
5891
+ } from "@harness-engineering/orchestrator";
5892
+ function bootstrapTrackerCommand(opts, verb) {
5893
+ const projectPath = path42.resolve(opts.dir);
5894
+ const trackerConfig = loadTrackerSyncConfig(projectPath);
5895
+ if (!trackerConfig) {
5896
+ logger.error(`No tracker config found in harness.config.json. Cannot ${verb}.`);
5897
+ return null;
5898
+ }
5899
+ if (trackerConfig.kind !== "github") {
5900
+ logger.error(
5901
+ `${verb} currently only supports 'github' tracker kind. Found: ${String(trackerConfig.kind)}`
5902
+ );
5903
+ return null;
5904
+ }
5905
+ const projectEnvPath = path42.join(projectPath, ".env");
5906
+ if (fs25.existsSync(projectEnvPath) && !process.env.GITHUB_TOKEN) {
5907
+ __require("dotenv").config({ path: projectEnvPath });
5908
+ }
5909
+ const token = process.env.GITHUB_TOKEN;
5910
+ if (!token) {
5911
+ logger.error("No GITHUB_TOKEN environment variable found.");
5912
+ return null;
5913
+ }
5914
+ return { token, projectPath, trackerConfig };
5915
+ }
5916
+ function buildNameToExternalIdMap(projectPath) {
5917
+ const roadmapFile = path42.join(projectPath, "docs", "roadmap.md");
5918
+ if (!fs25.existsSync(roadmapFile)) {
5919
+ logger.error("No docs/roadmap.md found. Cannot map issue hashes to external IDs.");
5920
+ return null;
5921
+ }
5922
+ const { parseRoadmap } = (init_dist(), __toCommonJS(dist_exports));
5923
+ const roadmapParsed = parseRoadmap(fs25.readFileSync(roadmapFile, "utf-8"));
5924
+ if (!roadmapParsed.ok) {
5925
+ logger.error("Failed to parse docs/roadmap.md");
5926
+ return null;
5927
+ }
5928
+ const map = /* @__PURE__ */ new Map();
5929
+ for (const milestone of roadmapParsed.value.milestones) {
5930
+ for (const feature of milestone.features) {
5931
+ if (feature.externalId) {
5932
+ map.set(feature.name.toLowerCase(), feature.externalId);
5933
+ }
5934
+ }
5935
+ }
5936
+ return map;
5937
+ }
5938
+ function resolveExternalId(record, nameToExternalId) {
5939
+ if (record.externalId) return record.externalId;
5940
+ for (const [featName, extId] of nameToExternalId) {
5941
+ const prefix = featName.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 20);
5942
+ if (record.identifier.startsWith(prefix)) return extId;
5943
+ }
5944
+ return null;
5945
+ }
5946
+ async function publishUnpublishedAnalyses(analyses, adapter, nameToExternalId, publishedIndex) {
5947
+ let publishedCount = 0;
5948
+ for (const record of analyses) {
5949
+ if (publishedIndex[record.issueId]) continue;
5950
+ const externalId = resolveExternalId(record, nameToExternalId);
5951
+ if (!externalId) {
5952
+ logger.dim(`Skipping ${record.identifier}: could not resolve externalId from roadmap`);
5953
+ continue;
5954
+ }
5955
+ logger.info(`Publishing analysis for ${record.identifier} to ${externalId}...`);
5956
+ const result = await adapter.addComment(externalId, renderAnalysisComment(record));
5957
+ if (result.ok) {
5958
+ publishedIndex[record.issueId] = (/* @__PURE__ */ new Date()).toISOString();
5959
+ publishedCount++;
5960
+ } else {
5961
+ logger.error(`Failed to publish ${record.identifier}: ${result.error.message}`);
5962
+ }
5963
+ }
5964
+ return publishedCount;
5965
+ }
5966
+ async function runPublishAnalyses(opts) {
5967
+ const bootstrap = bootstrapTrackerCommand(opts, "publish");
5968
+ if (!bootstrap) return process.exit(1);
5969
+ const { token, projectPath, trackerConfig } = bootstrap;
5970
+ const nameToExternalId = buildNameToExternalIdMap(projectPath);
5971
+ if (!nameToExternalId) return process.exit(1);
5972
+ const { AnalysisArchive } = await import("@harness-engineering/orchestrator");
5973
+ const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-WCSJHQPK.js");
5974
+ const archive = new AnalysisArchive(path42.join(projectPath, ".harness", "analyses"));
5975
+ const analyses = await archive.list();
5976
+ if (analyses.length === 0) {
5977
+ logger.info("No analyses found to publish.");
5978
+ return;
5979
+ }
5980
+ const publishedIndex = loadPublishedIndex(projectPath);
5981
+ const adapter = new GitHubIssuesSyncAdapter2({ token, config: trackerConfig });
5982
+ const publishedCount = await publishUnpublishedAnalyses(
5983
+ analyses,
5984
+ adapter,
5985
+ nameToExternalId,
5986
+ publishedIndex
5987
+ );
5988
+ if (publishedCount > 0) {
5989
+ savePublishedIndex(projectPath, publishedIndex);
5990
+ logger.success(`Successfully published ${publishedCount} analyses.`);
5991
+ } else {
5992
+ logger.info("All analyses are already up to date on external tracker.");
5993
+ }
5994
+ }
5995
+ function createPublishAnalysesCommand() {
5996
+ const command = new Command53("publish-analyses").description(
5997
+ "Publishes locally generated intelligence analyses to the external issue tracker (e.g., GitHub)"
5998
+ ).option("-d, --dir <path>", "Workspace directory", process.cwd()).action(async (opts) => {
5999
+ try {
6000
+ await runPublishAnalyses(opts);
6001
+ } catch (err) {
6002
+ logger.error(
6003
+ `Error publishing analyses: ${err instanceof Error ? err.message : String(err)}`
6004
+ );
6005
+ process.exit(1);
6006
+ }
6007
+ });
6008
+ return command;
6009
+ }
6010
+
5609
6011
  // src/commands/recommend.ts
5610
- import { Command as Command52 } from "commander";
6012
+ import { Command as Command54 } from "commander";
5611
6013
  import chalk7 from "chalk";
5612
6014
  async function resolveSnapshot(cwd, noCache) {
5613
6015
  if (!noCache) {
@@ -5671,7 +6073,7 @@ function printRecommendations(result) {
5671
6073
  console.log("");
5672
6074
  }
5673
6075
  function createRecommendCommand() {
5674
- const command = new Command52("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) => {
6076
+ 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) => {
5675
6077
  const globalOpts = cmd.optsWithGlobals();
5676
6078
  const mode = globalOpts.json ? OutputMode.JSON : OutputMode.TEXT;
5677
6079
  try {
@@ -5707,9 +6109,10 @@ function createRecommendCommand() {
5707
6109
  }
5708
6110
 
5709
6111
  // src/commands/scan-config.ts
5710
- import { Command as Command53 } from "commander";
5711
- import { existsSync as existsSync21, readFileSync as readFileSync11, writeFileSync as writeFileSync12 } from "fs";
5712
- import { join as join32, relative as relative2 } from "path";
6112
+ init_dist();
6113
+ import { Command as Command55 } from "commander";
6114
+ import { existsSync as existsSync22, readFileSync as readFileSync14, writeFileSync as writeFileSync12 } from "fs";
6115
+ import { join as join30, relative as relative2 } from "path";
5713
6116
  var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".gemini/settings.json", "skill.yaml"];
5714
6117
  function stripHighSeverityPatterns(content, injectionFindings) {
5715
6118
  const highLines = /* @__PURE__ */ new Set();
@@ -5742,10 +6145,10 @@ function applyFix(filePath, targetDir, content, injectionFindings) {
5742
6145
  }
5743
6146
  }
5744
6147
  function scanSingleFile(filePath, targetDir, scanner, options) {
5745
- if (!existsSync21(filePath)) return null;
6148
+ if (!existsSync22(filePath)) return null;
5746
6149
  let content;
5747
6150
  try {
5748
- content = readFileSync11(filePath, "utf8");
6151
+ content = readFileSync14(filePath, "utf8");
5749
6152
  } catch {
5750
6153
  return null;
5751
6154
  }
@@ -5766,7 +6169,7 @@ async function runScanConfig(targetDir, options) {
5766
6169
  const scanner = new SecurityScanner(parseSecurityConfig({}));
5767
6170
  const results = [];
5768
6171
  for (const configFile of CONFIG_FILES) {
5769
- const result = scanSingleFile(join32(targetDir, configFile), targetDir, scanner, options);
6172
+ const result = scanSingleFile(join30(targetDir, configFile), targetDir, scanner, options);
5770
6173
  if (result) results.push(result);
5771
6174
  }
5772
6175
  return { exitCode: computeScanExitCode(results), results };
@@ -5796,7 +6199,7 @@ function formatTextOutput(result) {
5796
6199
  }
5797
6200
  }
5798
6201
  function createScanConfigCommand() {
5799
- const command = new Command53("scan-config").description(
6202
+ const command = new Command55("scan-config").description(
5800
6203
  "Scan CLAUDE.md, AGENTS.md, .gemini/settings.json, and skill.yaml for prompt injection patterns"
5801
6204
  ).option("--path <dir>", "Target directory to scan (default: cwd)").option("--fix", "Strip high-severity patterns from files in-place").action(async (opts, cmd) => {
5802
6205
  const globalOpts = cmd.optsWithGlobals();
@@ -5813,24 +6216,24 @@ function createScanConfigCommand() {
5813
6216
  }
5814
6217
 
5815
6218
  // src/commands/setup.ts
5816
- import { Command as Command54 } from "commander";
5817
- import * as fs24 from "fs";
6219
+ import { Command as Command56 } from "commander";
6220
+ import * as fs28 from "fs";
5818
6221
  import * as os7 from "os";
5819
- import * as path43 from "path";
5820
- import chalk8 from "chalk";
6222
+ import * as path45 from "path";
6223
+ import chalk9 from "chalk";
5821
6224
 
5822
6225
  // src/utils/first-run.ts
5823
- import * as fs23 from "fs";
6226
+ import * as fs26 from "fs";
5824
6227
  import * as os6 from "os";
5825
- import * as path42 from "path";
5826
- var HARNESS_DIR = path42.join(os6.homedir(), ".harness");
5827
- var MARKER_FILE = path42.join(HARNESS_DIR, ".setup-complete");
6228
+ import * as path43 from "path";
6229
+ var HARNESS_DIR = path43.join(os6.homedir(), ".harness");
6230
+ var MARKER_FILE = path43.join(HARNESS_DIR, ".setup-complete");
5828
6231
  function isFirstRun() {
5829
- return !fs23.existsSync(MARKER_FILE);
6232
+ return !fs26.existsSync(MARKER_FILE);
5830
6233
  }
5831
6234
  function markSetupComplete() {
5832
- fs23.mkdirSync(HARNESS_DIR, { recursive: true });
5833
- fs23.writeFileSync(MARKER_FILE, "", "utf-8");
6235
+ fs26.mkdirSync(HARNESS_DIR, { recursive: true });
6236
+ fs26.writeFileSync(MARKER_FILE, "", "utf-8");
5834
6237
  }
5835
6238
  function printFirstRunWelcome() {
5836
6239
  try {
@@ -5842,6 +6245,119 @@ function printFirstRunWelcome() {
5842
6245
  }
5843
6246
  }
5844
6247
 
6248
+ // src/commands/telemetry-wizard.ts
6249
+ import * as fs27 from "fs";
6250
+ import * as path44 from "path";
6251
+ import readline from "readline";
6252
+ import chalk8 from "chalk";
6253
+ function prompt(question) {
6254
+ const rl = readline.createInterface({
6255
+ input: process.stdin,
6256
+ output: process.stdout
6257
+ });
6258
+ return new Promise((resolve35) => {
6259
+ rl.question(question, (answer) => {
6260
+ rl.close();
6261
+ resolve35(answer.trim().toLowerCase());
6262
+ });
6263
+ });
6264
+ }
6265
+ function isNo(answer) {
6266
+ return answer === "n" || answer === "no";
6267
+ }
6268
+ function isTelemetryConfigured(cwd) {
6269
+ const configPath = path44.join(cwd, "harness.config.json");
6270
+ try {
6271
+ const raw = JSON.parse(fs27.readFileSync(configPath, "utf-8"));
6272
+ return raw.telemetry !== void 0 && raw.adoption !== void 0;
6273
+ } catch {
6274
+ return false;
6275
+ }
6276
+ }
6277
+ function writeTelemetryConfig(cwd, result) {
6278
+ const configPath = path44.join(cwd, "harness.config.json");
6279
+ try {
6280
+ const raw = JSON.parse(fs27.readFileSync(configPath, "utf-8"));
6281
+ raw.telemetry = { enabled: result.telemetryEnabled };
6282
+ raw.adoption = { enabled: result.adoptionEnabled };
6283
+ fs27.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
6284
+ } catch (err) {
6285
+ console.warn(
6286
+ `\u26A0 Could not update ${configPath}: ${err instanceof Error ? err.message : String(err)}`
6287
+ );
6288
+ }
6289
+ const hasIdentity = result.identity.project || result.identity.team || result.identity.alias;
6290
+ if (hasIdentity) {
6291
+ const harnessDir = path44.join(cwd, ".harness");
6292
+ fs27.mkdirSync(harnessDir, { recursive: true });
6293
+ const telemetryFile = path44.join(harnessDir, "telemetry.json");
6294
+ fs27.writeFileSync(telemetryFile, JSON.stringify({ identity: result.identity }, null, 2) + "\n");
6295
+ }
6296
+ }
6297
+ async function promptIdentity() {
6298
+ console.log(
6299
+ ` ${chalk8.cyan("Identity")} (optional) \u2014 tag telemetry with project/team/alias for filtering.`
6300
+ );
6301
+ console.log(" Leave blank to keep anonymous.");
6302
+ const answer = await prompt(" Set identity fields? (y/N) ");
6303
+ const identity = {};
6304
+ if (answer === "y" || answer === "yes") {
6305
+ const projectName = await prompt(" Project name: ");
6306
+ if (projectName) identity.project = projectName;
6307
+ const teamName = await prompt(" Team name: ");
6308
+ if (teamName) identity.team = teamName;
6309
+ const alias = await prompt(" Alias: ");
6310
+ if (alias) identity.alias = alias;
6311
+ }
6312
+ return identity;
6313
+ }
6314
+ async function runTelemetryWizard() {
6315
+ if (!process.stdin.isTTY) return null;
6316
+ console.log("");
6317
+ console.log(` ${chalk8.bold("Telemetry Configuration")}`);
6318
+ console.log("");
6319
+ console.log(" Harness can collect anonymous usage data to help improve the tool.");
6320
+ console.log(" You can change these settings anytime in harness.config.json.");
6321
+ console.log("");
6322
+ console.log(
6323
+ ` ${chalk8.cyan("Anonymous telemetry")} \u2014 error rates, command usage, performance metrics.`
6324
+ );
6325
+ console.log(" No code, file contents, or personally identifiable info is sent.");
6326
+ const telemetryAnswer = await prompt(" Enable anonymous telemetry? (Y/n) ");
6327
+ const telemetryEnabled = !isNo(telemetryAnswer);
6328
+ console.log("");
6329
+ console.log(
6330
+ ` ${chalk8.cyan("Adoption tracking")} \u2014 which skills are used, how often, and success rates.`
6331
+ );
6332
+ console.log(" Stored locally in .harness/adoption/. Never sent externally.");
6333
+ const adoptionAnswer = await prompt(" Enable local adoption tracking? (Y/n) ");
6334
+ const adoptionEnabled = !isNo(adoptionAnswer);
6335
+ console.log("");
6336
+ const identity = await promptIdentity();
6337
+ return { telemetryEnabled, adoptionEnabled, identity };
6338
+ }
6339
+ async function ensureTelemetryConfigured(cwd) {
6340
+ const configPath = path44.join(cwd, "harness.config.json");
6341
+ if (!fs27.existsSync(configPath)) {
6342
+ return { status: "warn", message: "Not a harness project \u2014 skipped telemetry configuration" };
6343
+ }
6344
+ if (isTelemetryConfigured(cwd)) {
6345
+ return { status: "pass", message: "Telemetry already configured" };
6346
+ }
6347
+ const result = await runTelemetryWizard();
6348
+ if (!result) {
6349
+ return { status: "warn", message: "Non-interactive mode \u2014 skipped telemetry configuration" };
6350
+ }
6351
+ writeTelemetryConfig(cwd, result);
6352
+ const parts = [];
6353
+ parts.push(`telemetry ${result.telemetryEnabled ? "enabled" : "disabled"}`);
6354
+ parts.push(`adoption ${result.adoptionEnabled ? "enabled" : "disabled"}`);
6355
+ if (result.identity.project || result.identity.team || result.identity.alias) {
6356
+ parts.push("identity set");
6357
+ }
6358
+ return { status: "pass", message: `Telemetry configured: ${parts.join(", ")}` };
6359
+ }
6360
+
5845
6361
  // src/commands/setup.ts
5846
6362
  function checkNodeVersion3() {
5847
6363
  const result = checkNodeVersion();
@@ -5868,7 +6384,7 @@ function runSlashCommandGeneration() {
5868
6384
  }
5869
6385
  }
5870
6386
  function detectClient(dirName) {
5871
- return fs24.existsSync(path43.join(os7.homedir(), dirName));
6387
+ return fs28.existsSync(path45.join(os7.homedir(), dirName));
5872
6388
  }
5873
6389
  async function runMcpSetup(cwd) {
5874
6390
  const results = [];
@@ -5905,12 +6421,12 @@ async function runMcpSetup(cwd) {
5905
6421
  return results;
5906
6422
  }
5907
6423
  function formatStep(result) {
5908
- const icon = result.status === "pass" ? chalk8.green("\u2713") : result.status === "warn" ? chalk8.yellow("\u26A0") : chalk8.red("\u2717");
6424
+ const icon = result.status === "pass" ? chalk9.green("\u2713") : result.status === "warn" ? chalk9.yellow("\u26A0") : chalk9.red("\u2717");
5909
6425
  return ` ${icon} ${result.message}`;
5910
6426
  }
5911
6427
  function configureTier0Integrations(cwd) {
5912
6428
  try {
5913
- const mcpPath = path43.join(cwd, ".mcp.json");
6429
+ const mcpPath = path45.join(cwd, ".mcp.json");
5914
6430
  const config = readMcpConfig(mcpPath);
5915
6431
  const tier0 = INTEGRATION_REGISTRY.filter((i) => i.tier === 0);
5916
6432
  const added = [];
@@ -5919,9 +6435,9 @@ function configureTier0Integrations(cwd) {
5919
6435
  writeMcpEntry(mcpPath, integration.name, integration.mcpConfig);
5920
6436
  added.push(integration.displayName);
5921
6437
  }
5922
- const geminiDir = path43.join(cwd, ".gemini");
5923
- if (fs24.existsSync(geminiDir)) {
5924
- const geminiPath = path43.join(geminiDir, "settings.json");
6438
+ const geminiDir = path45.join(cwd, ".gemini");
6439
+ if (fs28.existsSync(geminiDir)) {
6440
+ const geminiPath = path45.join(geminiDir, "settings.json");
5925
6441
  const geminiConfig = readMcpConfig(geminiPath);
5926
6442
  for (const integration of tier0) {
5927
6443
  if (!geminiConfig.mcpServers[integration.name]) {
@@ -5941,6 +6457,44 @@ function configureTier0Integrations(cwd) {
5941
6457
  return { status: "fail", message: `Tier 0 integration configuration failed \u2014 ${msg}` };
5942
6458
  }
5943
6459
  }
6460
+ function ensureHooks(cwd) {
6461
+ const configPath = path45.join(cwd, "harness.config.json");
6462
+ if (!fs28.existsSync(configPath)) {
6463
+ return { status: "warn", message: "Not a harness project \u2014 skipped hook installation" };
6464
+ }
6465
+ let profile = "standard";
6466
+ const profilePath = path45.join(cwd, ".harness", "hooks", "profile.json");
6467
+ try {
6468
+ const data = JSON.parse(fs28.readFileSync(profilePath, "utf-8"));
6469
+ if (data.profile && ["minimal", "standard", "strict"].includes(data.profile)) {
6470
+ profile = data.profile;
6471
+ }
6472
+ } catch {
6473
+ }
6474
+ try {
6475
+ const result = initHooks({ profile, projectDir: cwd });
6476
+ return {
6477
+ status: "pass",
6478
+ message: `Installed ${result.copiedScripts.length} hooks (${profile} profile)`
6479
+ };
6480
+ } catch (error) {
6481
+ const msg = error instanceof Error ? error.message : String(error);
6482
+ return { status: "warn", message: `Hook installation skipped \u2014 ${msg}` };
6483
+ }
6484
+ }
6485
+ async function runInitialGraphScan(cwd) {
6486
+ try {
6487
+ const { runScan: runScan2 } = await import("./scan-MPJ6JHUY.js");
6488
+ const result = await runScan2(cwd);
6489
+ return {
6490
+ status: "pass",
6491
+ message: `Built knowledge graph: ${result.nodeCount} nodes, ${result.edgeCount} edges`
6492
+ };
6493
+ } catch (error) {
6494
+ const msg = error instanceof Error ? error.message : String(error);
6495
+ return { status: "warn", message: `Knowledge graph creation skipped \u2014 ${msg}` };
6496
+ }
6497
+ }
5944
6498
  async function runSetup(cwd) {
5945
6499
  const steps = [];
5946
6500
  const nodeResult = checkNodeVersion3();
@@ -5954,6 +6508,12 @@ async function runSetup(cwd) {
5954
6508
  steps.push(...mcpResults);
5955
6509
  const tier0Result = configureTier0Integrations(cwd);
5956
6510
  steps.push(tier0Result);
6511
+ const hooksResult = ensureHooks(cwd);
6512
+ steps.push(hooksResult);
6513
+ const telemetryResult = await ensureTelemetryConfigured(cwd);
6514
+ steps.push(telemetryResult);
6515
+ const graphResult = await runInitialGraphScan(cwd);
6516
+ steps.push(graphResult);
5957
6517
  const success = steps.every((s) => s.status !== "fail");
5958
6518
  if (success) {
5959
6519
  markSetupComplete();
@@ -5961,10 +6521,10 @@ async function runSetup(cwd) {
5961
6521
  return { steps, success };
5962
6522
  }
5963
6523
  function createSetupCommand() {
5964
- return new Command54("setup").description("Configure harness environment: slash commands, MCP, and more").action(async () => {
6524
+ return new Command56("setup").description("Configure harness environment: slash commands, MCP, and more").action(async () => {
5965
6525
  const cwd = process.cwd();
5966
6526
  console.log("");
5967
- console.log(` ${chalk8.bold("harness setup")}`);
6527
+ console.log(` ${chalk9.bold("harness setup")}`);
5968
6528
  console.log("");
5969
6529
  const { steps, success } = await runSetup(cwd);
5970
6530
  for (const step of steps) {
@@ -5983,15 +6543,16 @@ function createSetupCommand() {
5983
6543
  }
5984
6544
 
5985
6545
  // src/commands/share.ts
5986
- import { Command as Command55 } from "commander";
5987
- import * as fs25 from "fs";
5988
- import * as path44 from "path";
6546
+ init_dist();
6547
+ import { Command as Command57 } from "commander";
6548
+ import * as fs29 from "fs";
6549
+ import * as path46 from "path";
5989
6550
  import { parse as parseYaml } from "yaml";
5990
6551
  var MANIFEST_FILENAME = "constraints.yaml";
5991
6552
  async function runShareAction(projectPath, options) {
5992
- const rootDir = path44.resolve(projectPath);
5993
- const manifestPath = path44.join(rootDir, MANIFEST_FILENAME);
5994
- if (!fs25.existsSync(manifestPath)) {
6553
+ const rootDir = path46.resolve(projectPath);
6554
+ const manifestPath = path46.join(rootDir, MANIFEST_FILENAME);
6555
+ if (!fs29.existsSync(manifestPath)) {
5995
6556
  logger.error(
5996
6557
  `No ${MANIFEST_FILENAME} found at ${manifestPath}.
5997
6558
  Create a constraints.yaml in your project root to define what to share.`
@@ -6000,7 +6561,7 @@ Create a constraints.yaml in your project root to define what to share.`
6000
6561
  }
6001
6562
  let parsed;
6002
6563
  try {
6003
- const raw = fs25.readFileSync(manifestPath, "utf-8");
6564
+ const raw = fs29.readFileSync(manifestPath, "utf-8");
6004
6565
  parsed = parseYaml(raw);
6005
6566
  } catch (err) {
6006
6567
  logger.error(
@@ -6014,7 +6575,7 @@ Create a constraints.yaml in your project root to define what to share.`
6014
6575
  process.exit(1);
6015
6576
  }
6016
6577
  const manifest = manifestResult.value;
6017
- const configResult = resolveConfig(path44.join(rootDir, "harness.config.json"));
6578
+ const configResult = resolveConfig(path46.join(rootDir, "harness.config.json"));
6018
6579
  if (!configResult.ok) {
6019
6580
  logger.error(configResult.error.message);
6020
6581
  process.exit(1);
@@ -6032,8 +6593,8 @@ Create a constraints.yaml in your project root to define what to share.`
6032
6593
  );
6033
6594
  process.exit(1);
6034
6595
  }
6035
- const outputDir = path44.resolve(options.output);
6036
- const outputPath = path44.join(outputDir, `${manifest.name}.harness-constraints.json`);
6596
+ const outputDir = path46.resolve(options.output);
6597
+ const outputPath = path46.join(outputDir, `${manifest.name}.harness-constraints.json`);
6037
6598
  const writeResult = await writeConfig(outputPath, bundle);
6038
6599
  if (!writeResult.ok) {
6039
6600
  logger.error(`Failed to write bundle: ${writeResult.error.message}`);
@@ -6042,28 +6603,28 @@ Create a constraints.yaml in your project root to define what to share.`
6042
6603
  logger.success(`Bundle written to ${outputPath}`);
6043
6604
  }
6044
6605
  function createShareCommand() {
6045
- return new Command55("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) => {
6606
+ 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) => {
6046
6607
  await runShareAction(projectPath, options);
6047
6608
  });
6048
6609
  }
6049
6610
 
6050
6611
  // src/commands/skill/index.ts
6051
- import { Command as Command63 } from "commander";
6612
+ import { Command as Command65 } from "commander";
6052
6613
 
6053
6614
  // src/commands/skill/list.ts
6054
- import { Command as Command56 } from "commander";
6055
- import * as fs26 from "fs";
6056
- import * as path45 from "path";
6615
+ import { Command as Command58 } from "commander";
6616
+ import * as fs30 from "fs";
6617
+ import * as path47 from "path";
6057
6618
  import { parse } from "yaml";
6058
6619
  function scanDirectory(dirPath, source) {
6059
- if (!fs26.existsSync(dirPath)) return [];
6060
- const entries = fs26.readdirSync(dirPath, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
6620
+ if (!fs30.existsSync(dirPath)) return [];
6621
+ const entries = fs30.readdirSync(dirPath, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
6061
6622
  const skills = [];
6062
6623
  for (const name of entries) {
6063
- const yamlPath = path45.join(dirPath, name, "skill.yaml");
6064
- if (!fs26.existsSync(yamlPath)) continue;
6624
+ const yamlPath = path47.join(dirPath, name, "skill.yaml");
6625
+ if (!fs30.existsSync(yamlPath)) continue;
6065
6626
  try {
6066
- const raw = fs26.readFileSync(yamlPath, "utf-8");
6627
+ const raw = fs30.readFileSync(yamlPath, "utf-8");
6067
6628
  const parsed = parse(raw);
6068
6629
  const result = SkillMetadataSchema.safeParse(parsed);
6069
6630
  if (result.success) {
@@ -6081,10 +6642,10 @@ function scanDirectory(dirPath, source) {
6081
6642
  }
6082
6643
  function collectCommunitySkills(seen, allSkills) {
6083
6644
  const globalDir = resolveGlobalSkillsDir();
6084
- const skillsDir = path45.dirname(globalDir);
6085
- const communityBase = path45.join(skillsDir, "community");
6086
- const communityPlatformDir = path45.join(communityBase, "claude-code");
6087
- const lockfilePath = path45.join(communityBase, "skills-lock.json");
6645
+ const skillsDir = path47.dirname(globalDir);
6646
+ const communityBase = path47.join(skillsDir, "community");
6647
+ const communityPlatformDir = path47.join(communityBase, "claude-code");
6648
+ const lockfilePath = path47.join(communityBase, "skills-lock.json");
6088
6649
  const lockfile = readLockfile2(lockfilePath);
6089
6650
  const communitySkills = scanDirectory(communityPlatformDir, "community");
6090
6651
  for (const skill of communitySkills) {
@@ -6154,7 +6715,7 @@ function printSkillsVerbose(skills) {
6154
6715
  for (const s of skills) printSkillEntry(s);
6155
6716
  }
6156
6717
  function createListCommand3() {
6157
- return new Command56("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) => {
6718
+ 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) => {
6158
6719
  const globalOpts = cmd.optsWithGlobals();
6159
6720
  const skills = collectSkills({ filter: resolveFilter(opts) });
6160
6721
  if (globalOpts.json) {
@@ -6169,9 +6730,9 @@ function createListCommand3() {
6169
6730
  }
6170
6731
 
6171
6732
  // src/commands/skill/run.ts
6172
- import { Command as Command57 } from "commander";
6173
- import * as fs27 from "fs";
6174
- import * as path46 from "path";
6733
+ import { Command as Command59 } from "commander";
6734
+ import * as fs31 from "fs";
6735
+ import * as path48 from "path";
6175
6736
  import { parse as parse2 } from "yaml";
6176
6737
 
6177
6738
  // src/skill/complexity.ts
@@ -6267,10 +6828,10 @@ ${options.principles}`);
6267
6828
 
6268
6829
  // src/commands/skill/run.ts
6269
6830
  function loadSkillMetadata(skillDir) {
6270
- const yamlPath = path46.join(skillDir, "skill.yaml");
6271
- if (!fs27.existsSync(yamlPath)) return null;
6831
+ const yamlPath = path48.join(skillDir, "skill.yaml");
6832
+ if (!fs31.existsSync(yamlPath)) return null;
6272
6833
  try {
6273
- const result = SkillMetadataSchema.safeParse(parse2(fs27.readFileSync(yamlPath, "utf-8")));
6834
+ const result = SkillMetadataSchema.safeParse(parse2(fs31.readFileSync(yamlPath, "utf-8")));
6274
6835
  return result.success ? result.data : null;
6275
6836
  } catch {
6276
6837
  return null;
@@ -6282,22 +6843,22 @@ function resolveComplexity(metadata, requested, projectPath) {
6282
6843
  return requested;
6283
6844
  }
6284
6845
  function loadPrinciples(projectPath) {
6285
- const principlesPath = path46.join(projectPath, "docs", "principles.md");
6286
- return fs27.existsSync(principlesPath) ? fs27.readFileSync(principlesPath, "utf-8") : void 0;
6846
+ const principlesPath = path48.join(projectPath, "docs", "principles.md");
6847
+ return fs31.existsSync(principlesPath) ? fs31.readFileSync(principlesPath, "utf-8") : void 0;
6287
6848
  }
6288
6849
  function readMostRecentFileInDir(dirPath) {
6289
- const files = fs27.readdirSync(dirPath).map((f) => ({ name: f, mtime: fs27.statSync(path46.join(dirPath, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
6290
- if (files.length > 0) return fs27.readFileSync(path46.join(dirPath, files[0].name), "utf-8");
6850
+ const files = fs31.readdirSync(dirPath).map((f) => ({ name: f, mtime: fs31.statSync(path48.join(dirPath, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
6851
+ if (files.length > 0) return fs31.readFileSync(path48.join(dirPath, files[0].name), "utf-8");
6291
6852
  return void 0;
6292
6853
  }
6293
6854
  function loadPriorState(metadata, projectPath) {
6294
6855
  if (!metadata?.state.persistent || metadata.state.files.length === 0) return void 0;
6295
6856
  for (const stateFilePath of metadata.state.files) {
6296
- const fullPath = path46.join(projectPath, stateFilePath);
6297
- if (!fs27.existsSync(fullPath)) continue;
6298
- const stat = fs27.statSync(fullPath);
6857
+ const fullPath = path48.join(projectPath, stateFilePath);
6858
+ if (!fs31.existsSync(fullPath)) continue;
6859
+ const stat = fs31.statSync(fullPath);
6299
6860
  if (stat.isDirectory()) return readMostRecentFileInDir(fullPath);
6300
- return fs27.readFileSync(fullPath, "utf-8");
6861
+ return fs31.readFileSync(fullPath, "utf-8");
6301
6862
  }
6302
6863
  return void 0;
6303
6864
  }
@@ -6317,9 +6878,9 @@ function resolvePhaseState(metadata, projectPath, phase) {
6317
6878
  }
6318
6879
  function appendProjectState(content, metadata, projectPath, hasPathOpt) {
6319
6880
  if (!metadata?.state.persistent || !hasPathOpt) return content;
6320
- const stateFile = path46.join(projectPath, ".harness", "state.json");
6321
- if (!fs27.existsSync(stateFile)) return content;
6322
- const stateContent = fs27.readFileSync(stateFile, "utf-8");
6881
+ const stateFile = path48.join(projectPath, ".harness", "state.json");
6882
+ if (!fs31.existsSync(stateFile)) return content;
6883
+ const stateContent = fs31.readFileSync(stateFile, "utf-8");
6323
6884
  return content + `
6324
6885
 
6325
6886
  ---
@@ -6331,14 +6892,14 @@ ${stateContent}
6331
6892
  }
6332
6893
  async function runSkill(name, opts) {
6333
6894
  const skillsDir = resolveSkillsDir();
6334
- const skillDir = path46.join(skillsDir, name);
6335
- if (!fs27.existsSync(skillDir)) {
6895
+ const skillDir = path48.join(skillsDir, name);
6896
+ if (!fs31.existsSync(skillDir)) {
6336
6897
  logger.error(`Skill not found: ${name}`);
6337
6898
  process.exit(ExitCode.ERROR);
6338
6899
  return;
6339
6900
  }
6340
6901
  const metadata = loadSkillMetadata(skillDir);
6341
- const projectPath = opts.path ? path46.resolve(opts.path) : process.cwd();
6902
+ const projectPath = opts.path ? path48.resolve(opts.path) : process.cwd();
6342
6903
  const complexity = resolveComplexity(
6343
6904
  metadata,
6344
6905
  opts.complexity ?? "standard",
@@ -6365,14 +6926,14 @@ async function runSkill(name, opts) {
6365
6926
  ...stateWarning !== void 0 && { stateWarning },
6366
6927
  ...opts.party !== void 0 && { party: opts.party }
6367
6928
  });
6368
- const skillMdPath = path46.join(skillDir, "SKILL.md");
6369
- if (!fs27.existsSync(skillMdPath)) {
6929
+ const skillMdPath = path48.join(skillDir, "SKILL.md");
6930
+ if (!fs31.existsSync(skillMdPath)) {
6370
6931
  logger.error(`SKILL.md not found for skill: ${name}`);
6371
6932
  process.exit(ExitCode.ERROR);
6372
6933
  return;
6373
6934
  }
6374
6935
  const content = appendProjectState(
6375
- fs27.readFileSync(skillMdPath, "utf-8"),
6936
+ fs31.readFileSync(skillMdPath, "utf-8"),
6376
6937
  metadata,
6377
6938
  projectPath,
6378
6939
  !!opts.path
@@ -6381,13 +6942,13 @@ async function runSkill(name, opts) {
6381
6942
  process.exit(ExitCode.SUCCESS);
6382
6943
  }
6383
6944
  function createRunCommand2() {
6384
- return new Command57("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));
6945
+ 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));
6385
6946
  }
6386
6947
 
6387
6948
  // src/commands/skill/validate.ts
6388
- import { Command as Command58 } from "commander";
6389
- import * as fs28 from "fs";
6390
- import * as path47 from "path";
6949
+ import { Command as Command60 } from "commander";
6950
+ import * as fs32 from "fs";
6951
+ import * as path49 from "path";
6391
6952
  import { parse as parse3 } from "yaml";
6392
6953
  var BEHAVIORAL_REQUIRED_SECTIONS = [
6393
6954
  "## When to Use",
@@ -6399,11 +6960,11 @@ var BEHAVIORAL_REQUIRED_SECTIONS = [
6399
6960
  ];
6400
6961
  var KNOWLEDGE_REQUIRED_SECTIONS = ["## Instructions"];
6401
6962
  function validateSkillMd(name, skillMdPath, skillType, errors) {
6402
- if (!fs28.existsSync(skillMdPath)) {
6963
+ if (!fs32.existsSync(skillMdPath)) {
6403
6964
  errors.push(`${name}: missing SKILL.md`);
6404
6965
  return;
6405
6966
  }
6406
- const mdContent = fs28.readFileSync(skillMdPath, "utf-8");
6967
+ const mdContent = fs32.readFileSync(skillMdPath, "utf-8");
6407
6968
  if (!mdContent.trim().startsWith("# ")) {
6408
6969
  errors.push(`${name}/SKILL.md: must start with an h1 heading`);
6409
6970
  }
@@ -6428,20 +6989,20 @@ function validateSkillMd(name, skillMdPath, skillType, errors) {
6428
6989
  }
6429
6990
  }
6430
6991
  function validateSkillEntry(name, skillsDir, errors) {
6431
- const skillDir = path47.join(skillsDir, name);
6432
- const yamlPath = path47.join(skillDir, "skill.yaml");
6433
- if (!fs28.existsSync(yamlPath)) {
6992
+ const skillDir = path49.join(skillsDir, name);
6993
+ const yamlPath = path49.join(skillDir, "skill.yaml");
6994
+ if (!fs32.existsSync(yamlPath)) {
6434
6995
  errors.push(`${name}: missing skill.yaml`);
6435
6996
  return false;
6436
6997
  }
6437
6998
  try {
6438
- const raw = fs28.readFileSync(yamlPath, "utf-8");
6999
+ const raw = fs32.readFileSync(yamlPath, "utf-8");
6439
7000
  const result = SkillMetadataSchema.safeParse(parse3(raw));
6440
7001
  if (!result.success) {
6441
7002
  errors.push(`${name}/skill.yaml: ${result.error.message}`);
6442
7003
  return false;
6443
7004
  }
6444
- validateSkillMd(name, path47.join(skillDir, "SKILL.md"), result.data.type, errors);
7005
+ validateSkillMd(name, path49.join(skillDir, "SKILL.md"), result.data.type, errors);
6445
7006
  return true;
6446
7007
  } catch (e) {
6447
7008
  errors.push(`${name}: parse error \u2014 ${e instanceof Error ? e.message : String(e)}`);
@@ -6449,15 +7010,15 @@ function validateSkillEntry(name, skillsDir, errors) {
6449
7010
  }
6450
7011
  }
6451
7012
  function createValidateCommand2() {
6452
- return new Command58("validate").description("Validate all skill.yaml files and SKILL.md structure").action(async (_opts, cmd) => {
7013
+ return new Command60("validate").description("Validate all skill.yaml files and SKILL.md structure").action(async (_opts, cmd) => {
6453
7014
  const globalOpts = cmd.optsWithGlobals();
6454
7015
  const skillsDir = resolveSkillsDir();
6455
- if (!fs28.existsSync(skillsDir)) {
7016
+ if (!fs32.existsSync(skillsDir)) {
6456
7017
  logger.info("No skills directory found.");
6457
7018
  process.exit(ExitCode.SUCCESS);
6458
7019
  return;
6459
7020
  }
6460
- const entries = fs28.readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
7021
+ const entries = fs32.readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
6461
7022
  const errors = [];
6462
7023
  let validated = 0;
6463
7024
  for (const name of entries) {
@@ -6479,24 +7040,24 @@ function createValidateCommand2() {
6479
7040
  }
6480
7041
 
6481
7042
  // src/commands/skill/info.ts
6482
- import { Command as Command59 } from "commander";
6483
- import * as fs29 from "fs";
6484
- import * as path48 from "path";
7043
+ import { Command as Command61 } from "commander";
7044
+ import * as fs33 from "fs";
7045
+ import * as path50 from "path";
6485
7046
  import { parse as parse4 } from "yaml";
6486
7047
  function loadSkillMetadata2(name) {
6487
7048
  const skillsDir = resolveSkillsDir();
6488
- const skillDir = path48.join(skillsDir, name);
6489
- if (!fs29.existsSync(skillDir)) {
7049
+ const skillDir = path50.join(skillsDir, name);
7050
+ if (!fs33.existsSync(skillDir)) {
6490
7051
  logger.error(`Skill not found: ${name}`);
6491
7052
  return { ok: false, exitCode: ExitCode.ERROR };
6492
7053
  }
6493
- const yamlPath = path48.join(skillDir, "skill.yaml");
6494
- if (!fs29.existsSync(yamlPath)) {
7054
+ const yamlPath = path50.join(skillDir, "skill.yaml");
7055
+ if (!fs33.existsSync(yamlPath)) {
6495
7056
  logger.error(`skill.yaml not found for skill: ${name}`);
6496
7057
  return { ok: false, exitCode: ExitCode.ERROR };
6497
7058
  }
6498
7059
  try {
6499
- const raw = fs29.readFileSync(yamlPath, "utf-8");
7060
+ const raw = fs33.readFileSync(yamlPath, "utf-8");
6500
7061
  const parsed = parse4(raw);
6501
7062
  const result = SkillMetadataSchema.safeParse(parsed);
6502
7063
  if (!result.success) {
@@ -6525,7 +7086,7 @@ function printSkillInfo(skill) {
6525
7086
  console.log(`Persistent: ${skill.state.persistent}`);
6526
7087
  }
6527
7088
  function createInfoCommand() {
6528
- return new Command59("info").description("Show metadata for a skill").argument("<name>", "Skill name (e.g., harness-tdd)").action(async (name, _opts, cmd) => {
7089
+ return new Command61("info").description("Show metadata for a skill").argument("<name>", "Skill name (e.g., harness-tdd)").action(async (name, _opts, cmd) => {
6529
7090
  const globalOpts = cmd.optsWithGlobals();
6530
7091
  const loaded = loadSkillMetadata2(name);
6531
7092
  if (!loaded.ok) {
@@ -6542,7 +7103,7 @@ function createInfoCommand() {
6542
7103
  }
6543
7104
 
6544
7105
  // src/commands/skill/search.ts
6545
- import { Command as Command60 } from "commander";
7106
+ import { Command as Command62 } from "commander";
6546
7107
  async function runSearch(query, opts) {
6547
7108
  const results = await searchNpmRegistry(query, opts.registry);
6548
7109
  return results.filter((r) => {
@@ -6587,15 +7148,15 @@ async function runSearchAction(query, opts, globalOpts) {
6587
7148
  }
6588
7149
  }
6589
7150
  function createSearchCommand() {
6590
- return new Command60("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) => {
7151
+ 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) => {
6591
7152
  await runSearchAction(query, opts, cmd.optsWithGlobals());
6592
7153
  });
6593
7154
  }
6594
7155
 
6595
7156
  // src/commands/skill/create.ts
6596
- import { Command as Command61 } from "commander";
6597
- import * as path49 from "path";
6598
- import * as fs30 from "fs";
7157
+ import { Command as Command63 } from "commander";
7158
+ import * as path51 from "path";
7159
+ import * as fs34 from "fs";
6599
7160
  import YAML from "yaml";
6600
7161
  var KEBAB_CASE_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
6601
7162
  function buildReadme(name, description) {
@@ -6679,22 +7240,22 @@ function runCreate(name, opts) {
6679
7240
  if (!KEBAB_CASE_RE.test(name)) {
6680
7241
  throw new Error(`Invalid skill name "${name}". Must be kebab-case (e.g., my-skill).`);
6681
7242
  }
6682
- const baseDir = opts.outputDir ?? path49.join(process.cwd(), "agents", "skills", "claude-code");
6683
- const skillDir = path49.join(baseDir, name);
6684
- if (fs30.existsSync(skillDir)) {
7243
+ const baseDir = opts.outputDir ?? path51.join(process.cwd(), "agents", "skills", "claude-code");
7244
+ const skillDir = path51.join(baseDir, name);
7245
+ if (fs34.existsSync(skillDir)) {
6685
7246
  throw new Error(`Skill directory already exists: ${skillDir}`);
6686
7247
  }
6687
- fs30.mkdirSync(skillDir, { recursive: true });
7248
+ fs34.mkdirSync(skillDir, { recursive: true });
6688
7249
  const description = opts.description || `A community skill: ${name}`;
6689
7250
  const skillYaml = buildSkillYaml(name, opts);
6690
- const skillYamlPath = path49.join(skillDir, "skill.yaml");
6691
- fs30.writeFileSync(skillYamlPath, YAML.stringify(skillYaml));
7251
+ const skillYamlPath = path51.join(skillDir, "skill.yaml");
7252
+ fs34.writeFileSync(skillYamlPath, YAML.stringify(skillYaml));
6692
7253
  const skillMd = buildSkillMd(name, description);
6693
- const skillMdPath = path49.join(skillDir, "SKILL.md");
6694
- fs30.writeFileSync(skillMdPath, skillMd);
7254
+ const skillMdPath = path51.join(skillDir, "SKILL.md");
7255
+ fs34.writeFileSync(skillMdPath, skillMd);
6695
7256
  const readme = buildReadme(name, description);
6696
- const readmePath = path49.join(skillDir, "README.md");
6697
- fs30.writeFileSync(readmePath, readme);
7257
+ const readmePath = path51.join(skillDir, "README.md");
7258
+ fs34.writeFileSync(readmePath, readme);
6698
7259
  return {
6699
7260
  name,
6700
7261
  directory: skillDir,
@@ -6708,12 +7269,12 @@ function printCreateResult(name, result) {
6708
7269
  }
6709
7270
  logger.info(`
6710
7271
  Next steps:`);
6711
- logger.info(` 1. Edit ${path49.join(result.directory, "SKILL.md")} with your skill content`);
7272
+ logger.info(` 1. Edit ${path51.join(result.directory, "SKILL.md")} with your skill content`);
6712
7273
  logger.info(` 2. Run: harness skill validate ${name}`);
6713
7274
  logger.info(` 3. Run: harness skills publish`);
6714
7275
  }
6715
7276
  function createCreateCommand() {
6716
- return new Command61("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) => {
7277
+ 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) => {
6717
7278
  const globalOpts = cmd.optsWithGlobals();
6718
7279
  try {
6719
7280
  const result = runCreate(name, {
@@ -6736,26 +7297,26 @@ function createCreateCommand() {
6736
7297
  }
6737
7298
 
6738
7299
  // src/commands/skill/publish.ts
6739
- import { Command as Command62 } from "commander";
6740
- import * as fs32 from "fs";
6741
- import * as path51 from "path";
7300
+ import { Command as Command64 } from "commander";
7301
+ import * as fs36 from "fs";
7302
+ import * as path53 from "path";
6742
7303
  import { execFileSync as execFileSync5 } from "child_process";
6743
7304
 
6744
7305
  // src/registry/validator.ts
6745
- import * as fs31 from "fs";
6746
- import * as path50 from "path";
7306
+ import * as fs35 from "fs";
7307
+ import * as path52 from "path";
6747
7308
  import { parse as parse5 } from "yaml";
6748
7309
  import semver4 from "semver";
6749
7310
  async function validateForPublish(skillDir, registryUrl) {
6750
7311
  const errors = [];
6751
- const skillYamlPath = path50.join(skillDir, "skill.yaml");
6752
- if (!fs31.existsSync(skillYamlPath)) {
7312
+ const skillYamlPath = path52.join(skillDir, "skill.yaml");
7313
+ if (!fs35.existsSync(skillYamlPath)) {
6753
7314
  errors.push("skill.yaml not found. Create one with: harness skill create <name>");
6754
7315
  return { valid: false, errors };
6755
7316
  }
6756
7317
  let skillMeta;
6757
7318
  try {
6758
- const raw = fs31.readFileSync(skillYamlPath, "utf-8");
7319
+ const raw = fs35.readFileSync(skillYamlPath, "utf-8");
6759
7320
  const parsed = parse5(raw);
6760
7321
  const result = SkillMetadataSchema.safeParse(parsed);
6761
7322
  if (!result.success) {
@@ -6777,11 +7338,11 @@ async function validateForPublish(skillDir, registryUrl) {
6777
7338
  if (!skillMeta.triggers || skillMeta.triggers.length === 0) {
6778
7339
  errors.push("At least one trigger is required. Add triggers to skill.yaml.");
6779
7340
  }
6780
- const skillMdPath = path50.join(skillDir, "SKILL.md");
6781
- if (!fs31.existsSync(skillMdPath)) {
7341
+ const skillMdPath = path52.join(skillDir, "SKILL.md");
7342
+ if (!fs35.existsSync(skillMdPath)) {
6782
7343
  errors.push("SKILL.md not found. Create it with content describing your skill.");
6783
7344
  } else {
6784
- const content = fs31.readFileSync(skillMdPath, "utf-8");
7345
+ const content = fs35.readFileSync(skillMdPath, "utf-8");
6785
7346
  if (!content.includes("## When to Use")) {
6786
7347
  errors.push('SKILL.md must contain a "## When to Use" section.');
6787
7348
  }
@@ -6861,11 +7422,11 @@ ${errorList}`);
6861
7422
  }
6862
7423
  const meta = validation.skillMeta;
6863
7424
  const pkg = derivePackageJson(meta);
6864
- const pkgPath = path51.join(skillDir, "package.json");
6865
- fs32.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
6866
- const readmePath = path51.join(skillDir, "README.md");
6867
- if (!fs32.existsSync(readmePath)) {
6868
- const skillMdContent = fs32.readFileSync(path51.join(skillDir, "SKILL.md"), "utf-8");
7425
+ const pkgPath = path53.join(skillDir, "package.json");
7426
+ fs36.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
7427
+ const readmePath = path53.join(skillDir, "README.md");
7428
+ if (!fs36.existsSync(readmePath)) {
7429
+ const skillMdContent = fs36.readFileSync(path53.join(skillDir, "SKILL.md"), "utf-8");
6869
7430
  const readme = `# ${pkg.name}
6870
7431
 
6871
7432
  ${meta.description}
@@ -6879,7 +7440,7 @@ harness install ${meta.name}
6879
7440
  ---
6880
7441
 
6881
7442
  ${skillMdContent}`;
6882
- fs32.writeFileSync(readmePath, readme);
7443
+ fs36.writeFileSync(readmePath, readme);
6883
7444
  }
6884
7445
  if (opts.dryRun) {
6885
7446
  return {
@@ -6905,7 +7466,7 @@ ${skillMdContent}`;
6905
7466
  };
6906
7467
  }
6907
7468
  function createPublishCommand() {
6908
- return new Command62("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) => {
7469
+ 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) => {
6909
7470
  const globalOpts = cmd.optsWithGlobals();
6910
7471
  const skillDir = opts.dir || process.cwd();
6911
7472
  try {
@@ -6930,7 +7491,7 @@ function createPublishCommand() {
6930
7491
 
6931
7492
  // src/commands/skill/index.ts
6932
7493
  function createSkillCommand() {
6933
- const command = new Command63("skill").description("Skill management commands");
7494
+ const command = new Command65("skill").description("Skill management commands");
6934
7495
  command.addCommand(createListCommand3());
6935
7496
  command.addCommand(createRunCommand2());
6936
7497
  command.addCommand(createValidateCommand2());
@@ -6942,9 +7503,10 @@ function createSkillCommand() {
6942
7503
  }
6943
7504
 
6944
7505
  // src/commands/snapshot.ts
6945
- import { Command as Command64 } from "commander";
7506
+ init_dist();
7507
+ import { Command as Command66 } from "commander";
6946
7508
  import { execSync as execSync5 } from "child_process";
6947
- import chalk9 from "chalk";
7509
+ import chalk10 from "chalk";
6948
7510
  function getCommitHash3(cwd) {
6949
7511
  try {
6950
7512
  return execSync5("git rev-parse --short HEAD", { cwd, encoding: "utf-8" }).toString().trim();
@@ -6969,9 +7531,9 @@ function formatDelta(delta) {
6969
7531
  function directionSymbol(direction) {
6970
7532
  switch (direction) {
6971
7533
  case "improving":
6972
- return chalk9.green("improving");
7534
+ return chalk10.green("improving");
6973
7535
  case "declining":
6974
- return chalk9.red("declining");
7536
+ return chalk10.red("declining");
6975
7537
  case "stable":
6976
7538
  return "=";
6977
7539
  }
@@ -7124,7 +7686,7 @@ function registerListCommand(parent) {
7124
7686
  });
7125
7687
  }
7126
7688
  function createSnapshotCommand() {
7127
- const command = new Command64("snapshot").description("Architecture timeline snapshot commands");
7689
+ const command = new Command66("snapshot").description("Architecture timeline snapshot commands");
7128
7690
  registerCaptureCommand(command);
7129
7691
  registerTrendsCommand(command);
7130
7692
  registerListCommand(command);
@@ -7132,11 +7694,12 @@ function createSnapshotCommand() {
7132
7694
  }
7133
7695
 
7134
7696
  // src/commands/state/index.ts
7135
- import { Command as Command69 } from "commander";
7697
+ import { Command as Command71 } from "commander";
7136
7698
 
7137
7699
  // src/commands/state/show.ts
7138
- import { Command as Command65 } from "commander";
7139
- import * as path52 from "path";
7700
+ init_dist();
7701
+ import { Command as Command67 } from "commander";
7702
+ import * as path54 from "path";
7140
7703
  function printStateText(state, stream) {
7141
7704
  if (stream) console.log(`Stream: ${stream}`);
7142
7705
  console.log(`Schema Version: ${state.schemaVersion}`);
@@ -7160,9 +7723,9 @@ function printStateProgress(progress) {
7160
7723
  }
7161
7724
  }
7162
7725
  function createShowCommand() {
7163
- return new Command65("show").description("Show current project state").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(async (opts, cmd) => {
7726
+ 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) => {
7164
7727
  const globalOpts = cmd.optsWithGlobals();
7165
- const result = await loadState(path52.resolve(opts.path), opts.stream);
7728
+ const result = await loadState(path54.resolve(opts.path), opts.stream);
7166
7729
  if (!result.ok) {
7167
7730
  logger.error(result.error.message);
7168
7731
  process.exit(ExitCode.ERROR);
@@ -7177,10 +7740,11 @@ function createShowCommand() {
7177
7740
  }
7178
7741
 
7179
7742
  // src/commands/state/reset.ts
7180
- import { Command as Command66 } from "commander";
7181
- import * as fs33 from "fs";
7182
- import * as path53 from "path";
7183
- import * as readline from "readline";
7743
+ init_dist();
7744
+ import { Command as Command68 } from "commander";
7745
+ import * as fs37 from "fs";
7746
+ import * as path55 from "path";
7747
+ import * as readline2 from "readline";
7184
7748
  async function resolveStatePath(projectPath, stream) {
7185
7749
  if (stream) {
7186
7750
  const streamResult = await resolveStreamPath(projectPath, { stream });
@@ -7188,22 +7752,22 @@ async function resolveStatePath(projectPath, stream) {
7188
7752
  logger.error(streamResult.error.message);
7189
7753
  process.exit(ExitCode.ERROR);
7190
7754
  }
7191
- return path53.join(streamResult.value, "state.json");
7755
+ return path55.join(streamResult.value, "state.json");
7192
7756
  }
7193
- return path53.join(projectPath, ".harness", "state.json");
7757
+ return path55.join(projectPath, ".harness", "state.json");
7194
7758
  }
7195
7759
  async function confirmReset() {
7196
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
7197
- const answer = await new Promise((resolve33) => {
7198
- rl.question("Reset project state? This cannot be undone. [y/N] ", resolve33);
7760
+ const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
7761
+ const answer = await new Promise((resolve35) => {
7762
+ rl.question("Reset project state? This cannot be undone. [y/N] ", resolve35);
7199
7763
  });
7200
7764
  rl.close();
7201
7765
  return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
7202
7766
  }
7203
7767
  async function runReset(opts) {
7204
- const projectPath = path53.resolve(opts.path);
7768
+ const projectPath = path55.resolve(opts.path);
7205
7769
  const statePath = await resolveStatePath(projectPath, opts.stream);
7206
- if (!fs33.existsSync(statePath)) {
7770
+ if (!fs37.existsSync(statePath)) {
7207
7771
  logger.info("No state file found. Nothing to reset.");
7208
7772
  process.exit(ExitCode.SUCCESS);
7209
7773
  }
@@ -7212,7 +7776,7 @@ async function runReset(opts) {
7212
7776
  process.exit(ExitCode.SUCCESS);
7213
7777
  }
7214
7778
  try {
7215
- fs33.unlinkSync(statePath);
7779
+ fs37.unlinkSync(statePath);
7216
7780
  logger.success("Project state reset.");
7217
7781
  } catch (e) {
7218
7782
  logger.error(`Failed to reset state: ${e instanceof Error ? e.message : String(e)}`);
@@ -7221,15 +7785,16 @@ async function runReset(opts) {
7221
7785
  process.exit(ExitCode.SUCCESS);
7222
7786
  }
7223
7787
  function createResetCommand() {
7224
- return new Command66("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));
7788
+ 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));
7225
7789
  }
7226
7790
 
7227
7791
  // src/commands/state/learn.ts
7228
- import { Command as Command67 } from "commander";
7229
- import * as path54 from "path";
7792
+ init_dist();
7793
+ import { Command as Command69 } from "commander";
7794
+ import * as path56 from "path";
7230
7795
  function createLearnCommand() {
7231
- return new Command67("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) => {
7232
- const projectPath = path54.resolve(opts.path);
7796
+ 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) => {
7797
+ const projectPath = path56.resolve(opts.path);
7233
7798
  const result = await appendLearning(projectPath, message, void 0, void 0, opts.stream);
7234
7799
  if (!result.ok) {
7235
7800
  logger.error(result.error.message);
@@ -7242,10 +7807,11 @@ function createLearnCommand() {
7242
7807
  }
7243
7808
 
7244
7809
  // src/commands/state/streams.ts
7245
- import { Command as Command68 } from "commander";
7246
- import * as path55 from "path";
7810
+ init_dist();
7811
+ import { Command as Command70 } from "commander";
7812
+ import * as path57 from "path";
7247
7813
  async function runListStreams(opts, globalOpts) {
7248
- const projectPath = path55.resolve(opts.path);
7814
+ const projectPath = path57.resolve(opts.path);
7249
7815
  const indexResult = await loadStreamIndex(projectPath);
7250
7816
  const result = await listStreams(projectPath);
7251
7817
  if (!result.ok) {
@@ -7266,7 +7832,7 @@ async function runListStreams(opts, globalOpts) {
7266
7832
  process.exit(ExitCode.SUCCESS);
7267
7833
  }
7268
7834
  async function runCreateStream(name, opts) {
7269
- const result = await createStream(path55.resolve(opts.path), name, opts.branch);
7835
+ const result = await createStream(path57.resolve(opts.path), name, opts.branch);
7270
7836
  if (!result.ok) {
7271
7837
  logger.error(result.error.message);
7272
7838
  process.exit(ExitCode.ERROR);
@@ -7275,7 +7841,7 @@ async function runCreateStream(name, opts) {
7275
7841
  process.exit(ExitCode.SUCCESS);
7276
7842
  }
7277
7843
  async function runArchiveStream(name, opts) {
7278
- const result = await archiveStream(path55.resolve(opts.path), name);
7844
+ const result = await archiveStream(path57.resolve(opts.path), name);
7279
7845
  if (!result.ok) {
7280
7846
  logger.error(result.error.message);
7281
7847
  process.exit(ExitCode.ERROR);
@@ -7284,7 +7850,7 @@ async function runArchiveStream(name, opts) {
7284
7850
  process.exit(ExitCode.SUCCESS);
7285
7851
  }
7286
7852
  async function runActivateStream(name, opts) {
7287
- const result = await setActiveStream(path55.resolve(opts.path), name);
7853
+ const result = await setActiveStream(path57.resolve(opts.path), name);
7288
7854
  if (!result.ok) {
7289
7855
  logger.error(result.error.message);
7290
7856
  process.exit(ExitCode.ERROR);
@@ -7293,7 +7859,7 @@ async function runActivateStream(name, opts) {
7293
7859
  process.exit(ExitCode.SUCCESS);
7294
7860
  }
7295
7861
  function createStreamsCommand() {
7296
- const command = new Command68("streams").description("Manage state streams");
7862
+ const command = new Command70("streams").description("Manage state streams");
7297
7863
  command.command("list").description("List all known streams").option("--path <path>", "Project root path", ".").action(async (opts, cmd) => runListStreams(opts, cmd.optsWithGlobals()));
7298
7864
  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));
7299
7865
  command.command("archive <name>").description("Archive a stream").option("--path <path>", "Project root path", ".").action(async (name, opts) => runArchiveStream(name, opts));
@@ -7303,7 +7869,7 @@ function createStreamsCommand() {
7303
7869
 
7304
7870
  // src/commands/state/index.ts
7305
7871
  function createStateCommand() {
7306
- const command = new Command69("state").description("Project state management commands");
7872
+ const command = new Command71("state").description("Project state management commands");
7307
7873
  command.addCommand(createShowCommand());
7308
7874
  command.addCommand(createResetCommand());
7309
7875
  command.addCommand(createLearnCommand());
@@ -7311,8 +7877,154 @@ function createStateCommand() {
7311
7877
  return command;
7312
7878
  }
7313
7879
 
7880
+ // src/commands/sync-analyses.ts
7881
+ import * as fs38 from "fs";
7882
+ import * as path58 from "path";
7883
+ import { Command as Command72 } from "commander";
7884
+ init_dist();
7885
+ function isValidRecord(record) {
7886
+ return typeof record.issueId === "string" && !!record.issueId && typeof record.identifier === "string" && !!record.identifier && typeof record.analyzedAt === "string" && !/[/\\]/.test(record.issueId);
7887
+ }
7888
+ function extractAnalysisFromComments(comments) {
7889
+ const sorted = [...comments].sort(
7890
+ (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
7891
+ );
7892
+ for (const comment of sorted) {
7893
+ const fenceRegex = /```json\s*\r?\n([\s\S]*?)\r?\n```/g;
7894
+ let match;
7895
+ while ((match = fenceRegex.exec(comment.body)) !== null) {
7896
+ try {
7897
+ const parsed = JSON.parse(match[1]);
7898
+ if (parsed._harness_analysis !== true) continue;
7899
+ const { _harness_analysis, _version, ...record } = parsed;
7900
+ if (isValidRecord(record)) return record;
7901
+ } catch {
7902
+ continue;
7903
+ }
7904
+ }
7905
+ }
7906
+ return null;
7907
+ }
7908
+ function bootstrapTrackerCommand2(opts, verb) {
7909
+ const projectPath = path58.resolve(opts.dir);
7910
+ const trackerConfig = loadTrackerSyncConfig(projectPath);
7911
+ if (!trackerConfig) {
7912
+ logger.error(`No tracker config found in harness.config.json. Cannot ${verb}.`);
7913
+ return null;
7914
+ }
7915
+ if (trackerConfig.kind !== "github") {
7916
+ logger.error(
7917
+ `${verb} currently only supports 'github' tracker kind. Found: ${String(trackerConfig.kind)}`
7918
+ );
7919
+ return null;
7920
+ }
7921
+ const projectEnvPath = path58.join(projectPath, ".env");
7922
+ if (fs38.existsSync(projectEnvPath) && !process.env.GITHUB_TOKEN) {
7923
+ __require("dotenv").config({ path: projectEnvPath });
7924
+ }
7925
+ const token = process.env.GITHUB_TOKEN;
7926
+ if (!token) {
7927
+ logger.error("No GITHUB_TOKEN environment variable found.");
7928
+ return null;
7929
+ }
7930
+ return { token, projectPath, trackerConfig };
7931
+ }
7932
+ function loadRoadmapFeatures(projectPath) {
7933
+ const roadmapFile = path58.join(projectPath, "docs", "roadmap.md");
7934
+ if (!fs38.existsSync(roadmapFile)) {
7935
+ logger.error("No docs/roadmap.md found. Cannot discover features with externalIds.");
7936
+ return null;
7937
+ }
7938
+ const { parseRoadmap } = (init_dist(), __toCommonJS(dist_exports));
7939
+ const roadmapParsed = parseRoadmap(fs38.readFileSync(roadmapFile, "utf-8"));
7940
+ if (!roadmapParsed.ok) {
7941
+ logger.error("Failed to parse docs/roadmap.md");
7942
+ return null;
7943
+ }
7944
+ const features = [];
7945
+ for (const milestone of roadmapParsed.value.milestones) {
7946
+ for (const feature of milestone.features) {
7947
+ if (feature.externalId) {
7948
+ features.push({ name: feature.name, externalId: feature.externalId });
7949
+ }
7950
+ }
7951
+ }
7952
+ return features;
7953
+ }
7954
+ async function syncFeatureAnalyses(features, adapter, archive, publishedIndex) {
7955
+ let syncedCount = 0;
7956
+ let skippedCount = 0;
7957
+ let warnCount = 0;
7958
+ for (const { name, externalId } of features) {
7959
+ const commentsResult = await adapter.fetchComments(externalId);
7960
+ if (!commentsResult.ok) {
7961
+ logger.warn(
7962
+ `Failed to fetch comments for "${name}" (${externalId}): ${commentsResult.error.message}`
7963
+ );
7964
+ warnCount++;
7965
+ continue;
7966
+ }
7967
+ const record = extractAnalysisFromComments(commentsResult.value);
7968
+ if (!record) {
7969
+ skippedCount++;
7970
+ continue;
7971
+ }
7972
+ await archive.save(record);
7973
+ publishedIndex[record.issueId] = record.analyzedAt;
7974
+ syncedCount++;
7975
+ logger.info(`Synced analysis for "${name}" (${record.identifier})`);
7976
+ }
7977
+ return { syncedCount, skippedCount, warnCount };
7978
+ }
7979
+ async function runSyncAnalyses(opts) {
7980
+ const bootstrap = bootstrapTrackerCommand2(opts, "sync");
7981
+ if (!bootstrap) return process.exit(1);
7982
+ const { token, projectPath, trackerConfig } = bootstrap;
7983
+ const features = loadRoadmapFeatures(projectPath);
7984
+ if (!features) return process.exit(1);
7985
+ if (features.length === 0) {
7986
+ logger.info("No roadmap features have externalIds. Nothing to sync.");
7987
+ return;
7988
+ }
7989
+ const { AnalysisArchive, loadPublishedIndex: loadPublishedIndex2, savePublishedIndex: savePublishedIndex2 } = await import("@harness-engineering/orchestrator");
7990
+ const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-WCSJHQPK.js");
7991
+ const adapter = new GitHubIssuesSyncAdapter2({ token, config: trackerConfig });
7992
+ const archive = new AnalysisArchive(path58.join(projectPath, ".harness", "analyses"));
7993
+ const publishedIndex = loadPublishedIndex2(projectPath);
7994
+ const { syncedCount, skippedCount, warnCount } = await syncFeatureAnalyses(
7995
+ features,
7996
+ adapter,
7997
+ archive,
7998
+ publishedIndex
7999
+ );
8000
+ if (syncedCount > 0) {
8001
+ savePublishedIndex2(projectPath, publishedIndex);
8002
+ logger.success(
8003
+ `Synced ${syncedCount} analysis record(s). Skipped ${skippedCount} (no analysis). Warnings: ${warnCount}.`
8004
+ );
8005
+ } else {
8006
+ logger.info(
8007
+ `No new analyses found on tracker. Skipped ${skippedCount} feature(s) with no analysis comments.`
8008
+ );
8009
+ }
8010
+ }
8011
+ function createSyncAnalysesCommand() {
8012
+ const command = new Command72("sync-analyses").description(
8013
+ "Pull published intelligence analyses from the external issue tracker into the local .harness/analyses/ directory"
8014
+ ).option("-d, --dir <path>", "Workspace directory", process.cwd()).action(async (opts) => {
8015
+ try {
8016
+ await runSyncAnalyses(opts);
8017
+ } catch (err) {
8018
+ logger.error(`Error syncing analyses: ${err instanceof Error ? err.message : String(err)}`);
8019
+ process.exit(1);
8020
+ }
8021
+ });
8022
+ return command;
8023
+ }
8024
+
7314
8025
  // src/commands/taint.ts
7315
- import { Command as Command70 } from "commander";
8026
+ init_dist();
8027
+ import { Command as Command73 } from "commander";
7316
8028
  function getProjectRoot() {
7317
8029
  return process.cwd();
7318
8030
  }
@@ -7384,27 +8096,28 @@ function registerStatusCommand(taint) {
7384
8096
  });
7385
8097
  }
7386
8098
  function createTaintCommand() {
7387
- const taint = new Command70("taint").description("Manage sentinel session taint state");
8099
+ const taint = new Command73("taint").description("Manage sentinel session taint state");
7388
8100
  registerClearCommand(taint);
7389
8101
  registerStatusCommand(taint);
7390
8102
  return taint;
7391
8103
  }
7392
8104
 
7393
8105
  // src/commands/telemetry/index.ts
7394
- import { Command as Command73 } from "commander";
8106
+ import { Command as Command76 } from "commander";
7395
8107
 
7396
8108
  // src/commands/telemetry/identify.ts
7397
- import { Command as Command71 } from "commander";
7398
- import * as fs34 from "fs";
7399
- import * as path56 from "path";
8109
+ init_dist();
8110
+ import { Command as Command74 } from "commander";
8111
+ import * as fs39 from "fs";
8112
+ import * as path59 from "path";
7400
8113
  function telemetryFilePath(cwd) {
7401
- return path56.join(cwd, ".harness", "telemetry.json");
8114
+ return path59.join(cwd, ".harness", "telemetry.json");
7402
8115
  }
7403
8116
  function writeTelemetryFile(cwd, data) {
7404
8117
  const filePath = telemetryFilePath(cwd);
7405
- const dir = path56.dirname(filePath);
7406
- fs34.mkdirSync(dir, { recursive: true });
7407
- fs34.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
8118
+ const dir = path59.dirname(filePath);
8119
+ fs39.mkdirSync(dir, { recursive: true });
8120
+ fs39.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
7408
8121
  }
7409
8122
  var IDENTITY_FIELDS = ["project", "team", "alias"];
7410
8123
  function applyIdentityFields(opts, identity) {
@@ -7418,7 +8131,7 @@ function printIdentity(identity) {
7418
8131
  }
7419
8132
  }
7420
8133
  function createIdentifyCommand() {
7421
- const cmd = new Command71("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) => {
8134
+ 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) => {
7422
8135
  const cwd = process.cwd();
7423
8136
  if (opts.clear) {
7424
8137
  writeTelemetryFile(cwd, { identity: {} });
@@ -7442,7 +8155,8 @@ function createIdentifyCommand() {
7442
8155
  }
7443
8156
 
7444
8157
  // src/commands/telemetry/status.ts
7445
- import { Command as Command72 } from "commander";
8158
+ init_dist();
8159
+ import { Command as Command75 } from "commander";
7446
8160
  function gatherEnvOverrides() {
7447
8161
  const overrides = {};
7448
8162
  if (process.env.DO_NOT_TRACK) overrides.DO_NOT_TRACK = process.env.DO_NOT_TRACK;
@@ -7480,7 +8194,7 @@ function printHumanStatus(result) {
7480
8194
  }
7481
8195
  }
7482
8196
  function createStatusCommand() {
7483
- const cmd = new Command72("status").description("Show current telemetry consent state, install ID, and identity").option("--json", "Output as JSON").action((opts) => {
8197
+ const cmd = new Command75("status").description("Show current telemetry consent state, install ID, and identity").option("--json", "Output as JSON").action((opts) => {
7484
8198
  const cwd = process.cwd();
7485
8199
  const envOverrides = gatherEnvOverrides();
7486
8200
  const consent = resolveConsent(cwd, void 0);
@@ -7513,15 +8227,15 @@ function createStatusCommand() {
7513
8227
 
7514
8228
  // src/commands/telemetry/index.ts
7515
8229
  function createTelemetryCommand() {
7516
- const command = new Command73("telemetry").description("Telemetry identity and status management");
8230
+ const command = new Command76("telemetry").description("Telemetry identity and status management");
7517
8231
  command.addCommand(createIdentifyCommand());
7518
8232
  command.addCommand(createStatusCommand());
7519
8233
  return command;
7520
8234
  }
7521
8235
 
7522
8236
  // src/commands/traceability.ts
7523
- import { Command as Command74 } from "commander";
7524
- import chalk10 from "chalk";
8237
+ import { Command as Command77 } from "commander";
8238
+ import chalk11 from "chalk";
7525
8239
  function confidenceLabel(maxConfidence) {
7526
8240
  if (maxConfidence >= 0.8) return "explicit";
7527
8241
  if (maxConfidence > 0) return "inferred";
@@ -7530,13 +8244,13 @@ function confidenceLabel(maxConfidence) {
7530
8244
  function statusIcon(status) {
7531
8245
  switch (status) {
7532
8246
  case "full":
7533
- return chalk10.green("\u2713 full");
8247
+ return chalk11.green("\u2713 full");
7534
8248
  case "code-only":
7535
- return chalk10.yellow("\u25D0 code-only");
8249
+ return chalk11.yellow("\u25D0 code-only");
7536
8250
  case "test-only":
7537
- return chalk10.yellow("\u25D0 test-only");
8251
+ return chalk11.yellow("\u25D0 test-only");
7538
8252
  case "none":
7539
- return chalk10.red("\u2717 none");
8253
+ return chalk11.red("\u2717 none");
7540
8254
  default:
7541
8255
  return status;
7542
8256
  }
@@ -7578,14 +8292,14 @@ function handleEmptyResults(mode) {
7578
8292
  }
7579
8293
  function printResultTable(result, mode) {
7580
8294
  const specLabel = result.specPath || result.featureName || "Unknown";
7581
- console.log(` ${chalk10.cyan(specLabel)} ${chalk10.dim(`(${result.summary.total} requirements)`)}`);
8295
+ console.log(` ${chalk11.cyan(specLabel)} ${chalk11.dim(`(${result.summary.total} requirements)`)}`);
7582
8296
  console.log("");
7583
8297
  const numWidth = 4;
7584
8298
  const nameWidth = mode === OutputMode.VERBOSE ? 44 : 36;
7585
8299
  const codeWidth = 6;
7586
8300
  const testWidth = 7;
7587
8301
  const confWidth = 12;
7588
- const header = chalk10.dim(
8302
+ const header = chalk11.dim(
7589
8303
  ` ${pad("#", numWidth)}${pad("Requirement", nameWidth)}${pad("Code", codeWidth)}${pad("Tests", testWidth)}${pad("Confidence", confWidth)}Status`
7590
8304
  );
7591
8305
  console.log(header);
@@ -7614,10 +8328,10 @@ function printRequirementRows(requirements, mode, cols) {
7614
8328
  );
7615
8329
  if (mode === OutputMode.VERBOSE) {
7616
8330
  for (const f of req.codeFiles) {
7617
- console.log(chalk10.dim(` code: ${f.path} (${f.method})`));
8331
+ console.log(chalk11.dim(` code: ${f.path} (${f.method})`));
7618
8332
  }
7619
8333
  for (const f of req.testFiles) {
7620
- console.log(chalk10.dim(` test: ${f.path} (${f.method})`));
8334
+ console.log(chalk11.dim(` test: ${f.path} (${f.method})`));
7621
8335
  }
7622
8336
  }
7623
8337
  }
@@ -7628,7 +8342,7 @@ function printCoverageSummary(summary) {
7628
8342
  const codePct = s.total > 0 ? Math.round(s.withCode / s.total * 100) : 0;
7629
8343
  const testPct = s.total > 0 ? Math.round(s.withTests / s.total * 100) : 0;
7630
8344
  console.log(
7631
- ` ${chalk10.bold("Coverage:")} ${fullyPct}% fully traced (${s.fullyTraced}/${s.total}), ${codePct}% with code (${s.withCode}/${s.total}), ${testPct}% with tests (${s.withTests}/${s.total})`
8345
+ ` ${chalk11.bold("Coverage:")} ${fullyPct}% fully traced (${s.fullyTraced}/${s.total}), ${codePct}% with code (${s.withCode}/${s.total}), ${testPct}% with tests (${s.withTests}/${s.total})`
7632
8346
  );
7633
8347
  }
7634
8348
  async function runTraceability(options) {
@@ -7636,7 +8350,7 @@ async function runTraceability(options) {
7636
8350
  const projectPath = process.cwd();
7637
8351
  const store = await loadGraphStore(projectPath);
7638
8352
  if (!store) handleNoStore(mode);
7639
- const graphModule = await import("./dist-QW5G3GX3.js");
8353
+ const graphModule = await import("./dist-RADHOOXG.js");
7640
8354
  const filterOptions = buildFilterOptions(options);
7641
8355
  const results = graphModule.queryTraceability(store, filterOptions);
7642
8356
  if (results.length === 0) handleEmptyResults(mode);
@@ -7644,14 +8358,14 @@ async function runTraceability(options) {
7644
8358
  console.log(JSON.stringify(results, null, 2));
7645
8359
  } else {
7646
8360
  console.log("");
7647
- console.log(chalk10.bold("Spec-to-Implementation Traceability"));
8361
+ console.log(chalk11.bold("Spec-to-Implementation Traceability"));
7648
8362
  console.log("");
7649
8363
  for (const result of results) printResultTable(result, mode);
7650
8364
  }
7651
8365
  process.exit(ExitCode.SUCCESS);
7652
8366
  }
7653
8367
  function createTraceabilityCommand() {
7654
- return new Command74("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) => {
8368
+ 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) => {
7655
8369
  const globalOpts = cmd.optsWithGlobals();
7656
8370
  await runTraceability({
7657
8371
  spec: opts.spec,
@@ -7664,15 +8378,15 @@ function createTraceabilityCommand() {
7664
8378
  }
7665
8379
 
7666
8380
  // src/commands/uninstall.ts
7667
- import * as path57 from "path";
7668
- import { Command as Command75 } from "commander";
8381
+ import * as path60 from "path";
8382
+ import { Command as Command78 } from "commander";
7669
8383
  async function runUninstall(skillName, options) {
7670
8384
  const packageName = resolvePackageName(skillName);
7671
8385
  const shortName = extractSkillName(packageName);
7672
8386
  const globalDir = resolveGlobalSkillsDir();
7673
- const skillsDir = path57.dirname(globalDir);
7674
- const communityBase = path57.join(skillsDir, "community");
7675
- const lockfilePath = path57.join(communityBase, "skills-lock.json");
8387
+ const skillsDir = path60.dirname(globalDir);
8388
+ const communityBase = path60.join(skillsDir, "community");
8389
+ const lockfilePath = path60.join(communityBase, "skills-lock.json");
7676
8390
  const lockfile = readLockfile2(lockfilePath);
7677
8391
  const entry = lockfile.skills[packageName];
7678
8392
  if (!entry) {
@@ -7702,7 +8416,7 @@ async function runUninstall(skillName, options) {
7702
8416
  return result;
7703
8417
  }
7704
8418
  function createUninstallCommand() {
7705
- const cmd = new Command75("uninstall");
8419
+ const cmd = new Command78("uninstall");
7706
8420
  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) => {
7707
8421
  try {
7708
8422
  const result = await runUninstall(skill, opts);
@@ -7721,9 +8435,10 @@ function createUninstallCommand() {
7721
8435
  }
7722
8436
 
7723
8437
  // src/commands/uninstall-constraints.ts
7724
- import * as fs35 from "fs/promises";
7725
- import * as path58 from "path";
7726
- import { Command as Command76 } from "commander";
8438
+ init_dist();
8439
+ import * as fs40 from "fs/promises";
8440
+ import * as path61 from "path";
8441
+ import { Command as Command79 } from "commander";
7727
8442
  async function runUninstallConstraints(options) {
7728
8443
  const { packageName, configPath, lockfilePath } = options;
7729
8444
  const lockfileResult = await readLockfile(lockfilePath);
@@ -7743,7 +8458,7 @@ async function runUninstallConstraints(options) {
7743
8458
  }
7744
8459
  let localConfig;
7745
8460
  try {
7746
- const raw = await fs35.readFile(configPath, "utf-8");
8461
+ const raw = await fs40.readFile(configPath, "utf-8");
7747
8462
  localConfig = JSON.parse(raw);
7748
8463
  } catch (err) {
7749
8464
  return {
@@ -7780,11 +8495,11 @@ async function runUninstallConstraints(options) {
7780
8495
  };
7781
8496
  }
7782
8497
  function createUninstallConstraintsCommand() {
7783
- const cmd = new Command76("uninstall-constraints");
8498
+ const cmd = new Command79("uninstall-constraints");
7784
8499
  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) => {
7785
8500
  let configPath;
7786
8501
  if (opts.config) {
7787
- configPath = path58.resolve(opts.config);
8502
+ configPath = path61.resolve(opts.config);
7788
8503
  } else {
7789
8504
  const found = findConfigFile();
7790
8505
  if (!found.ok) {
@@ -7793,8 +8508,8 @@ function createUninstallConstraintsCommand() {
7793
8508
  }
7794
8509
  configPath = found.value;
7795
8510
  }
7796
- const projectRoot = path58.dirname(configPath);
7797
- const lockfilePath = path58.join(projectRoot, ".harness", "constraints.lock.json");
8511
+ const projectRoot = path61.dirname(configPath);
8512
+ const lockfilePath = path61.join(projectRoot, ".harness", "constraints.lock.json");
7798
8513
  const result = await runUninstallConstraints({
7799
8514
  packageName: name,
7800
8515
  configPath,
@@ -7819,12 +8534,13 @@ function createUninstallConstraintsCommand() {
7819
8534
  }
7820
8535
 
7821
8536
  // src/commands/update.ts
7822
- import { Command as Command77 } from "commander";
8537
+ import { Command as Command80 } from "commander";
7823
8538
  import { execFile, execFileSync as execFileSync6 } from "child_process";
7824
- import { realpathSync } from "fs";
8539
+ import { realpathSync, existsSync as existsSync36, readFileSync as readFileSync25 } from "fs";
8540
+ import { join as join47 } from "path";
7825
8541
  import { promisify } from "util";
7826
- import readline2 from "readline";
7827
- import chalk11 from "chalk";
8542
+ import readline3 from "readline";
8543
+ import chalk12 from "chalk";
7828
8544
  function detectPackageManager() {
7829
8545
  try {
7830
8546
  const argv1 = process.argv[1];
@@ -7882,23 +8598,64 @@ function getInstalledPackages(pm) {
7882
8598
  return ["@harness-engineering/cli", "@harness-engineering/core"];
7883
8599
  }
7884
8600
  }
7885
- function prompt(question) {
7886
- const rl = readline2.createInterface({
8601
+ function prompt2(question) {
8602
+ const rl = readline3.createInterface({
7887
8603
  input: process.stdin,
7888
8604
  output: process.stdout
7889
8605
  });
7890
- return new Promise((resolve33) => {
8606
+ return new Promise((resolve35) => {
7891
8607
  rl.question(question, (answer) => {
7892
8608
  rl.close();
7893
- resolve33(answer.trim().toLowerCase());
8609
+ resolve35(answer.trim().toLowerCase());
7894
8610
  });
7895
8611
  });
7896
8612
  }
8613
+ async function ensureTelemetryIfNeeded() {
8614
+ const cwd = process.cwd();
8615
+ const result = await ensureTelemetryConfigured(cwd);
8616
+ if (result.status === "pass") {
8617
+ logger.success(result.message);
8618
+ } else if (result.status === "warn") {
8619
+ logger.warn(result.message);
8620
+ }
8621
+ }
8622
+ function refreshHooks() {
8623
+ const cwd = process.cwd();
8624
+ const configPath = join47(cwd, "harness.config.json");
8625
+ if (!existsSync36(configPath)) return;
8626
+ let profile = "standard";
8627
+ const profilePath = join47(cwd, ".harness", "hooks", "profile.json");
8628
+ try {
8629
+ const data = JSON.parse(readFileSync25(profilePath, "utf-8"));
8630
+ if (data.profile && ["minimal", "standard", "strict"].includes(data.profile)) {
8631
+ profile = data.profile;
8632
+ }
8633
+ } catch {
8634
+ }
8635
+ try {
8636
+ const result = initHooks({ profile, projectDir: cwd });
8637
+ logger.success(`Refreshed ${result.copiedScripts.length} hooks (${profile} profile)`);
8638
+ } catch (err) {
8639
+ const msg = err instanceof Error ? err.message : String(err);
8640
+ logger.warn(`Hook refresh failed: ${msg}`);
8641
+ }
8642
+ }
8643
+ function runLocalGraphScan() {
8644
+ try {
8645
+ logger.info("Scanning codebase to rebuild knowledge graph...");
8646
+ execFileSync6("harness", ["graph", "scan", "."], { stdio: "inherit" });
8647
+ } catch {
8648
+ logger.warn("Graph scan failed. Run manually:");
8649
+ console.log(` ${chalk12.cyan("harness graph scan .")}`);
8650
+ }
8651
+ }
7897
8652
  async function offerRegeneration() {
7898
8653
  console.log("");
7899
- const regenAnswer = await prompt("Regenerate slash commands and agent definitions? (Y/n) ");
8654
+ const regenAnswer = await prompt2(
8655
+ "Regenerate slash commands, agent definitions, and knowledge graph? (Y/n) "
8656
+ );
7900
8657
  if (regenAnswer === "n" || regenAnswer === "no") return;
7901
- const scopeAnswer = await prompt("Generate for (G)lobal or (l)ocal project? (G/l) ");
8658
+ const scopeAnswer = await prompt2("Generate for (G)lobal or (l)ocal project? (G/l) ");
7902
8659
  const isGlobal = scopeAnswer !== "l" && scopeAnswer !== "local";
7903
8660
  try {
7904
8661
  execFileSync6("harness", ["generate", ...isGlobal ? ["--global"] : []], {
@@ -7906,7 +8663,10 @@ async function offerRegeneration() {
7906
8663
  });
7907
8664
  } catch {
7908
8665
  logger.warn("Generation failed. Run manually:");
7909
- console.log(` ${chalk11.cyan(`harness generate${isGlobal ? " --global" : ""}`)}`);
8666
+ console.log(` ${chalk12.cyan(`harness generate${isGlobal ? " --global" : ""}`)}`);
8667
+ }
8668
+ if (!isGlobal) {
8669
+ runLocalGraphScan();
7910
8670
  }
7911
8671
  }
7912
8672
  async function checkAllPackages(packages, installedVersions) {
@@ -7958,14 +8718,16 @@ async function runUpdateAction(opts, globalOpts) {
7958
8718
  const { hasUpdates, outdated } = await checkAllPackages(packages, installedVersions);
7959
8719
  if (!hasUpdates) {
7960
8720
  logger.success("All packages are up to date");
8721
+ refreshHooks();
8722
+ await ensureTelemetryIfNeeded();
7961
8723
  await offerRegeneration();
7962
8724
  process.exit(ExitCode.SUCCESS);
7963
8725
  }
7964
8726
  console.log("");
7965
8727
  for (const { pkg, current, latest } of outdated) {
7966
8728
  const shortName = pkg.replace("@harness-engineering/", "");
7967
- const currentStr = current ? chalk11.dim(`v${current}`) : chalk11.dim("not installed");
7968
- logger.info(`${shortName}: ${currentStr} \u2192 ${chalk11.green(`v${latest}`)}`);
8729
+ const currentStr = current ? chalk12.dim(`v${current}`) : chalk12.dim("not installed");
8730
+ logger.info(`${shortName}: ${currentStr} \u2192 ${chalk12.green(`v${latest}`)}`);
7969
8731
  }
7970
8732
  console.log("");
7971
8733
  }
@@ -7981,14 +8743,16 @@ async function runUpdateAction(opts, globalOpts) {
7981
8743
  } catch {
7982
8744
  console.log("");
7983
8745
  logger.error("Update failed. You can try manually:");
7984
- console.log(` ${chalk11.cyan(installCmd)}`);
8746
+ console.log(` ${chalk12.cyan(installCmd)}`);
7985
8747
  process.exit(ExitCode.ERROR);
7986
8748
  }
8749
+ refreshHooks();
8750
+ await ensureTelemetryIfNeeded();
7987
8751
  await offerRegeneration();
7988
8752
  process.exit(ExitCode.SUCCESS);
7989
8753
  }
7990
8754
  function createUpdateCommand() {
7991
- return new Command77("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(
8755
+ 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(
7992
8756
  "--regenerate",
7993
8757
  "Only regenerate slash commands and agent definitions (skip package updates)"
7994
8758
  ).action(async (opts, cmd) => {
@@ -7998,15 +8762,15 @@ function createUpdateCommand() {
7998
8762
  }
7999
8763
 
8000
8764
  // src/commands/usage.ts
8001
- import { Command as Command78 } from "commander";
8765
+ import { Command as Command81 } from "commander";
8002
8766
  async function loadAndPriceRecords(cwd, includeClaudeSessions = false) {
8003
- const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-TZQUURSP.js");
8767
+ const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-WCSJHQPK.js");
8004
8768
  const records = readCostRecords(cwd);
8005
8769
  if (includeClaudeSessions) {
8006
8770
  const ccRecords = parseCCRecords();
8007
8771
  records.push(...ccRecords);
8008
8772
  }
8009
- if (records.length === 0) return records;
8773
+ if (records.length === 0) return { records, pricingData: null };
8010
8774
  const pricingData = await loadPricingData(cwd);
8011
8775
  for (const record of records) {
8012
8776
  if (record.model && record.costMicroUSD == null) {
@@ -8014,7 +8778,14 @@ async function loadAndPriceRecords(cwd, includeClaudeSessions = false) {
8014
8778
  if (cost != null) record.costMicroUSD = cost;
8015
8779
  }
8016
8780
  }
8017
- return records;
8781
+ return { records, pricingData };
8782
+ }
8783
+ function computeCacheSavings(cacheReadTokens, model, pricingData) {
8784
+ if (!cacheReadTokens || !model || !pricingData) return null;
8785
+ const pricing = pricingData.get(model);
8786
+ if (!pricing || pricing.cacheReadPer1M == null) return null;
8787
+ const savingsUSD = cacheReadTokens / 1e6 * (pricing.inputPer1M - pricing.cacheReadPer1M);
8788
+ return Math.round(savingsUSD * 1e6);
8018
8789
  }
8019
8790
  function formatMicroUSD(microUSD) {
8020
8791
  if (microUSD == null) return "N/A";
@@ -8042,7 +8813,10 @@ function registerDailyCommand(usage) {
8042
8813
  const globalOpts = cmd.optsWithGlobals();
8043
8814
  const days = Math.min(Math.max(parseInt(opts.days, 10) || 7, 1), 90);
8044
8815
  const cwd = process.cwd();
8045
- const records = await loadAndPriceRecords(cwd, globalOpts.includeClaudeSessions);
8816
+ const { records, pricingData } = await loadAndPriceRecords(
8817
+ cwd,
8818
+ globalOpts.includeClaudeSessions
8819
+ );
8046
8820
  if (records.length === 0) {
8047
8821
  if (globalOpts.json) {
8048
8822
  console.log(JSON.stringify([]));
@@ -8051,23 +8825,25 @@ function registerDailyCommand(usage) {
8051
8825
  }
8052
8826
  return;
8053
8827
  }
8054
- const { aggregateByDay } = await import("./dist-TZQUURSP.js");
8828
+ const { aggregateByDay } = await import("./dist-WCSJHQPK.js");
8055
8829
  const dailyData = aggregateByDay(records);
8056
8830
  const limited = dailyData.slice(0, days);
8057
8831
  if (globalOpts.json) {
8058
8832
  const enriched = limited.map((day) => {
8059
8833
  const hitRate = computeCacheHitRate(day.cacheReadTokens, day.tokens.inputTokens);
8834
+ const savings = computeCacheSavings(day.cacheReadTokens, day.models[0], pricingData);
8060
8835
  return {
8061
8836
  ...day,
8062
- ...hitRate != null ? { cacheHitRate: Math.round(hitRate * 1e3) / 1e3 } : {}
8837
+ ...hitRate != null ? { cacheHitRate: Math.round(hitRate * 1e3) / 1e3 } : {},
8838
+ ...savings != null ? { cacheSavingsMicroUSD: savings } : {}
8063
8839
  };
8064
8840
  });
8065
8841
  console.log(JSON.stringify(enriched, null, 2));
8066
8842
  return;
8067
8843
  }
8068
8844
  const hasCacheData = limited.some((d) => d.cacheReadTokens != null && d.cacheReadTokens > 0);
8069
- const cacheHeader = hasCacheData ? " | Cache " : "";
8070
- const cacheDivider = hasCacheData ? " | ------" : "";
8845
+ const cacheHeader = hasCacheData ? " | Cache | Saved " : "";
8846
+ const cacheDivider = hasCacheData ? " | ------ | -------" : "";
8071
8847
  const header = "Date | Sessions | Input | Output | Model(s) | Cost " + cacheHeader;
8072
8848
  const divider = "-------------|----------|-----------|-----------|------------------------------|-------" + cacheDivider;
8073
8849
  logger.info(header);
@@ -8081,7 +8857,10 @@ function registerDailyCommand(usage) {
8081
8857
  const cost = formatMicroUSD(day.costMicroUSD);
8082
8858
  const cacheCol = hasCacheData ? (() => {
8083
8859
  const rate = computeCacheHitRate(day.cacheReadTokens, day.tokens.inputTokens);
8084
- return " | " + (rate != null ? formatPercent(rate).padStart(5) : " -");
8860
+ const savings = computeCacheSavings(day.cacheReadTokens, day.models[0], pricingData);
8861
+ const rateStr = rate != null ? formatPercent(rate).padStart(5) : " -";
8862
+ const savingsStr = savings != null ? formatMicroUSD(savings).padStart(7) : " N/A";
8863
+ return " | " + rateStr + " | " + savingsStr;
8085
8864
  })() : "";
8086
8865
  logger.info(
8087
8866
  `${date} | ${sessions} | ${input} | ${output} | ${models} | ${cost}${cacheCol}`
@@ -8094,7 +8873,7 @@ function registerSessionsCommand(usage) {
8094
8873
  const globalOpts = cmd.optsWithGlobals();
8095
8874
  const limit = Math.min(Math.max(parseInt(opts.limit, 10) || 10, 1), 100);
8096
8875
  const cwd = process.cwd();
8097
- const records = await loadAndPriceRecords(cwd, globalOpts.includeClaudeSessions);
8876
+ const { records } = await loadAndPriceRecords(cwd, globalOpts.includeClaudeSessions);
8098
8877
  if (records.length === 0) {
8099
8878
  if (globalOpts.json) {
8100
8879
  console.log(JSON.stringify([]));
@@ -8103,7 +8882,7 @@ function registerSessionsCommand(usage) {
8103
8882
  }
8104
8883
  return;
8105
8884
  }
8106
- const { aggregateBySession } = await import("./dist-TZQUURSP.js");
8885
+ const { aggregateBySession } = await import("./dist-WCSJHQPK.js");
8107
8886
  const sessionData = aggregateBySession(records);
8108
8887
  const limited = sessionData.slice(0, limit);
8109
8888
  if (globalOpts.json) {
@@ -8145,7 +8924,7 @@ function reportSessionNotFound(id, sessionData, useJson) {
8145
8924
  }
8146
8925
  }
8147
8926
  }
8148
- function printSessionDetail(match) {
8927
+ function printSessionDetail(match, pricingData) {
8149
8928
  logger.info(`Session: ${match.sessionId}`);
8150
8929
  logger.info(`Started: ${match.firstTimestamp}`);
8151
8930
  logger.info(`Ended: ${match.lastTimestamp}`);
@@ -8164,9 +8943,13 @@ function printSessionDetail(match) {
8164
8943
  }
8165
8944
  const hitRate = computeCacheHitRate(match.cacheReadTokens, match.tokens.inputTokens);
8166
8945
  if (hitRate != null) {
8946
+ const savings = computeCacheSavings(match.cacheReadTokens, match.model, pricingData);
8167
8947
  logger.info("");
8168
8948
  logger.info("Cache Performance:");
8169
8949
  logger.info(` Cache hit rate: ${formatPercent(hitRate)}`);
8950
+ if (savings != null) {
8951
+ logger.info(` Estimated savings: ${formatMicroUSD(savings)}`);
8952
+ }
8170
8953
  }
8171
8954
  logger.info("");
8172
8955
  logger.info(`Cost: ${formatMicroUSD(match.costMicroUSD)}`);
@@ -8175,8 +8958,11 @@ function registerSessionCommand(usage) {
8175
8958
  usage.command("session <id>").description("Show detailed token breakdown for a specific session").action(async (id, _opts, cmd) => {
8176
8959
  const globalOpts = cmd.optsWithGlobals();
8177
8960
  const cwd = process.cwd();
8178
- const records = await loadAndPriceRecords(cwd, globalOpts.includeClaudeSessions);
8179
- const { aggregateBySession } = await import("./dist-TZQUURSP.js");
8961
+ const { records, pricingData } = await loadAndPriceRecords(
8962
+ cwd,
8963
+ globalOpts.includeClaudeSessions
8964
+ );
8965
+ const { aggregateBySession } = await import("./dist-WCSJHQPK.js");
8180
8966
  const sessionData = aggregateBySession(records);
8181
8967
  const match = sessionData.find((s) => s.sessionId === id);
8182
8968
  if (!match) {
@@ -8186,21 +8972,23 @@ function registerSessionCommand(usage) {
8186
8972
  }
8187
8973
  if (globalOpts.json) {
8188
8974
  const hitRate = computeCacheHitRate(match.cacheReadTokens, match.tokens.inputTokens);
8975
+ const savings = computeCacheSavings(match.cacheReadTokens, match.model, pricingData);
8189
8976
  const enriched = {
8190
8977
  ...match,
8191
- ...hitRate != null ? { cacheHitRate: Math.round(hitRate * 1e3) / 1e3 } : {}
8978
+ ...hitRate != null ? { cacheHitRate: Math.round(hitRate * 1e3) / 1e3 } : {},
8979
+ ...savings != null ? { cacheSavingsMicroUSD: savings } : {}
8192
8980
  };
8193
8981
  console.log(JSON.stringify(enriched, null, 2));
8194
8982
  return;
8195
8983
  }
8196
- printSessionDetail(match);
8984
+ printSessionDetail(match, pricingData);
8197
8985
  });
8198
8986
  }
8199
8987
  function registerLatestCommand(usage) {
8200
8988
  usage.command("latest").description("Show the most recently completed session cost summary").action(async (_opts, cmd) => {
8201
8989
  const globalOpts = cmd.optsWithGlobals();
8202
8990
  const cwd = process.cwd();
8203
- const records = await loadAndPriceRecords(cwd, globalOpts.includeClaudeSessions);
8991
+ const { records } = await loadAndPriceRecords(cwd, globalOpts.includeClaudeSessions);
8204
8992
  if (records.length === 0) {
8205
8993
  if (globalOpts.json) {
8206
8994
  console.log(JSON.stringify({ error: "No usage data found" }));
@@ -8209,7 +8997,7 @@ function registerLatestCommand(usage) {
8209
8997
  }
8210
8998
  return;
8211
8999
  }
8212
- const { aggregateBySession } = await import("./dist-TZQUURSP.js");
9000
+ const { aggregateBySession } = await import("./dist-WCSJHQPK.js");
8213
9001
  const sessionData = aggregateBySession(records);
8214
9002
  const latest = sessionData[0];
8215
9003
  if (!latest) {
@@ -8235,7 +9023,7 @@ function registerLatestCommand(usage) {
8235
9023
  });
8236
9024
  }
8237
9025
  function createUsageCommand() {
8238
- const usage = new Command78("usage").description("Token usage and cost tracking");
9026
+ const usage = new Command81("usage").description("Token usage and cost tracking");
8239
9027
  usage.option(
8240
9028
  "--include-claude-sessions",
8241
9029
  "Include Claude Code session data from ~/.claude/projects/"
@@ -8248,15 +9036,17 @@ function createUsageCommand() {
8248
9036
  }
8249
9037
 
8250
9038
  // src/commands/validate.ts
8251
- import { Command as Command79 } from "commander";
8252
- import * as path59 from "path";
9039
+ init_dist();
9040
+ init_dist();
9041
+ import { Command as Command82 } from "commander";
9042
+ import * as path62 from "path";
8253
9043
  async function runValidate(options) {
8254
9044
  const configResult = resolveConfig(options.configPath);
8255
9045
  if (!configResult.ok) {
8256
9046
  return configResult;
8257
9047
  }
8258
9048
  const config = configResult.value;
8259
- const cwd = options.cwd ?? (options.configPath ? path59.dirname(path59.resolve(options.configPath)) : process.cwd());
9049
+ const cwd = options.cwd ?? (options.configPath ? path62.dirname(path62.resolve(options.configPath)) : process.cwd());
8260
9050
  const result = {
8261
9051
  valid: true,
8262
9052
  checks: {
@@ -8266,7 +9056,7 @@ async function runValidate(options) {
8266
9056
  },
8267
9057
  issues: []
8268
9058
  };
8269
- const agentsMapPath = path59.resolve(cwd, config.agentsMapPath);
9059
+ const agentsMapPath = path62.resolve(cwd, config.agentsMapPath);
8270
9060
  const agentsResult = await validateAgentsMap(agentsMapPath);
8271
9061
  if (agentsResult.ok) {
8272
9062
  result.checks.agentsMap = true;
@@ -8302,6 +9092,26 @@ async function runValidate(options) {
8302
9092
  });
8303
9093
  }
8304
9094
  result.checks.fileStructure = true;
9095
+ if (options.agentConfigs) {
9096
+ const agentCfg = await validateAgentConfigs(cwd, {
9097
+ strict: options.strict === true,
9098
+ ...options.agnixBin !== void 0 && { agnixBin: options.agnixBin }
9099
+ });
9100
+ result.agentConfigs = agentCfg;
9101
+ result.checks.agentConfigs = agentCfg.valid;
9102
+ if (!agentCfg.valid) result.valid = false;
9103
+ for (const finding of agentCfg.issues) {
9104
+ result.issues.push({
9105
+ check: "agentConfigs",
9106
+ file: finding.file,
9107
+ ...finding.line !== void 0 && { line: finding.line },
9108
+ ruleId: finding.ruleId,
9109
+ severity: finding.severity,
9110
+ message: finding.message,
9111
+ ...finding.suggestion !== void 0 && { suggestion: finding.suggestion }
9112
+ });
9113
+ }
9114
+ }
8305
9115
  return Ok(result);
8306
9116
  }
8307
9117
  function resolveValidateMode(globalOpts) {
@@ -8311,11 +9121,11 @@ function resolveValidateMode(globalOpts) {
8311
9121
  return OutputMode.TEXT;
8312
9122
  }
8313
9123
  async function printCrossCheckWarnings(mode) {
8314
- const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-M4NP2UF5.js");
9124
+ const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-ZOWFA3DB.js");
8315
9125
  const cwd = process.cwd();
8316
9126
  const crossResult = await runCrossCheck2({
8317
- specsDir: path59.join(cwd, "docs", "specs"),
8318
- plansDir: path59.join(cwd, "docs", "plans"),
9127
+ specsDir: path62.join(cwd, "docs", "specs"),
9128
+ plansDir: path62.join(cwd, "docs", "plans"),
8319
9129
  projectPath: cwd
8320
9130
  });
8321
9131
  if (!crossResult.ok || crossResult.value.warnings === 0) return;
@@ -8327,37 +9137,59 @@ async function printCrossCheckWarnings(mode) {
8327
9137
  ${crossResult.value.warnings} warnings`);
8328
9138
  }
8329
9139
  function createValidateCommand3() {
8330
- const command = new Command79("validate").description("Run all validation checks").option("--cross-check", "Run cross-artifact consistency validation").action(async (opts, cmd) => {
8331
- const globalOpts = cmd.optsWithGlobals();
8332
- const mode = resolveValidateMode(globalOpts);
8333
- const formatter = new OutputFormatter(mode);
8334
- const result = await runValidate({
8335
- configPath: globalOpts.config,
8336
- json: globalOpts.json,
8337
- verbose: globalOpts.verbose,
8338
- quiet: globalOpts.quiet
8339
- });
8340
- if (!result.ok) {
8341
- if (mode === OutputMode.JSON) console.log(JSON.stringify({ error: result.error.message }));
8342
- else logger.error(result.error.message);
8343
- process.exit(result.error.exitCode);
8344
- }
8345
- if (opts.crossCheck) await printCrossCheckWarnings(mode);
8346
- const output = formatter.formatValidation({
8347
- valid: result.value.valid,
8348
- issues: result.value.issues
8349
- });
8350
- if (output) console.log(output);
8351
- process.exit(result.value.valid ? ExitCode.SUCCESS : ExitCode.VALIDATION_FAILED);
8352
- });
9140
+ const command = new Command82("validate").description("Run all validation checks").option("--cross-check", "Run cross-artifact consistency validation").option(
9141
+ "--agent-configs",
9142
+ "Validate agent configs (CLAUDE.md, hooks, skills) via agnix or built-in fallback rules"
9143
+ ).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()));
8353
9144
  return command;
8354
9145
  }
9146
+ async function runValidateAction(opts, globalOpts) {
9147
+ const mode = resolveValidateMode(globalOpts);
9148
+ const formatter = new OutputFormatter(mode);
9149
+ const result = await runValidate({
9150
+ ...typeof globalOpts.config === "string" && { configPath: globalOpts.config },
9151
+ json: globalOpts.json === true,
9152
+ verbose: globalOpts.verbose === true,
9153
+ quiet: globalOpts.quiet === true,
9154
+ agentConfigs: opts.agentConfigs === true,
9155
+ strict: opts.strict === true,
9156
+ ...typeof opts.agnixBin === "string" && { agnixBin: opts.agnixBin }
9157
+ });
9158
+ if (!result.ok) {
9159
+ if (mode === OutputMode.JSON) console.log(JSON.stringify({ error: result.error.message }));
9160
+ else logger.error(result.error.message);
9161
+ process.exit(result.error.exitCode);
9162
+ }
9163
+ if (opts.crossCheck) await printCrossCheckWarnings(mode);
9164
+ emitValidateOutput(result.value, mode, formatter);
9165
+ process.exit(result.value.valid ? ExitCode.SUCCESS : ExitCode.VALIDATION_FAILED);
9166
+ }
9167
+ function emitValidateOutput(value, mode, formatter) {
9168
+ if (mode === OutputMode.JSON) {
9169
+ console.log(JSON.stringify(value, null, 2));
9170
+ return;
9171
+ }
9172
+ const output = formatter.formatValidation({ valid: value.valid, issues: value.issues });
9173
+ if (output) console.log(output);
9174
+ if (value.agentConfigs) printAgentConfigSummary(value.agentConfigs, mode);
9175
+ }
9176
+ function printAgentConfigSummary(cfg, mode) {
9177
+ if (mode === OutputMode.QUIET) return;
9178
+ const engineLabel = cfg.engine === "agnix" ? "agnix" : "built-in fallback rules";
9179
+ const note = cfg.fellBackBecause ? ` (${cfg.fellBackBecause})` : "";
9180
+ console.log(`
9181
+ Agent configs checked via ${engineLabel}${note}`);
9182
+ if (cfg.engine === "fallback" && cfg.fellBackBecause === "binary-not-found") {
9183
+ console.log(" Install agnix for 385+ rule coverage: https://github.com/agent-sh/agnix");
9184
+ }
9185
+ }
8355
9186
 
8356
9187
  // src/commands/_registry.ts
8357
9188
  var commandCreators = [
8358
9189
  createAddCommand,
8359
9190
  createAdoptionCommand,
8360
9191
  createAgentCommand,
9192
+ createAuditProtectedCommand,
8361
9193
  createBlueprintCommand,
8362
9194
  createCheckArchCommand,
8363
9195
  createCheckDepsCommand,
@@ -8390,6 +9222,7 @@ var commandCreators = [
8390
9222
  createPerfCommand,
8391
9223
  createPersonaCommand,
8392
9224
  createPredictCommand,
9225
+ createPublishAnalysesCommand,
8393
9226
  createQueryCommand,
8394
9227
  createRecommendCommand,
8395
9228
  createScanCommand,
@@ -8400,6 +9233,7 @@ var commandCreators = [
8400
9233
  createSkillCommand,
8401
9234
  createSnapshotCommand,
8402
9235
  createStateCommand,
9236
+ createSyncAnalysesCommand,
8403
9237
  createTaintCommand,
8404
9238
  createTelemetryCommand,
8405
9239
  createTraceabilityCommand,
@@ -8412,7 +9246,7 @@ var commandCreators = [
8412
9246
 
8413
9247
  // src/index.ts
8414
9248
  function createProgram() {
8415
- const program = new Command80();
9249
+ const program = new Command83();
8416
9250
  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");
8417
9251
  for (const creator of commandCreators) {
8418
9252
  program.addCommand(creator());
@@ -8424,7 +9258,6 @@ export {
8424
9258
  runCheckArch,
8425
9259
  runGraphStatus,
8426
9260
  runGraphExport,
8427
- runScan,
8428
9261
  runQuery,
8429
9262
  runIngest,
8430
9263
  runImpactPreview,