@harness-engineering/cli 1.13.1 → 1.14.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 (139) hide show
  1. package/dist/agents/skills/claude-code/harness-brainstorming/SKILL.md +39 -0
  2. package/dist/agents/skills/claude-code/harness-code-review/SKILL.md +44 -0
  3. package/dist/agents/skills/claude-code/harness-execution/SKILL.md +44 -0
  4. package/dist/agents/skills/claude-code/harness-planning/SKILL.md +39 -0
  5. package/dist/agents/skills/claude-code/harness-release-readiness/SKILL.md +3 -3
  6. package/dist/agents/skills/claude-code/harness-verification/SKILL.md +35 -0
  7. package/dist/agents/skills/claude-code/initialize-harness-project/SKILL.md +11 -3
  8. package/dist/agents/skills/gemini-cli/harness-brainstorming/SKILL.md +39 -0
  9. package/dist/agents/skills/gemini-cli/harness-code-review/SKILL.md +44 -0
  10. package/dist/agents/skills/gemini-cli/harness-execution/SKILL.md +44 -0
  11. package/dist/agents/skills/gemini-cli/harness-planning/SKILL.md +39 -0
  12. package/dist/agents/skills/gemini-cli/harness-release-readiness/SKILL.md +3 -3
  13. package/dist/agents/skills/gemini-cli/harness-verification/SKILL.md +35 -0
  14. package/dist/agents/skills/gemini-cli/initialize-harness-project/SKILL.md +11 -3
  15. package/dist/agents-md-YTYQDA3P.js +8 -0
  16. package/dist/{architecture-2R5Z4ZAF.js → architecture-JQZYM4US.js} +4 -4
  17. package/dist/bin/harness-mcp.js +14 -14
  18. package/dist/bin/harness.js +24 -24
  19. package/dist/{check-phase-gate-2OFZ7OWW.js → check-phase-gate-L3RADYWO.js} +4 -4
  20. package/dist/{chunk-QY4T6YAZ.js → chunk-3C2MLBPJ.js} +4 -4
  21. package/dist/{chunk-UAX4I5ZE.js → chunk-6KTUUFRN.js} +2 -2
  22. package/dist/{chunk-ND6PNADU.js → chunk-7IP4JIFL.js} +9 -9
  23. package/dist/{chunk-C2ERUR3L.js → chunk-7MJAPE3Z.js} +165 -49
  24. package/dist/{chunk-PQ5YK4AY.js → chunk-ABQHQ6I5.js} +1583 -1169
  25. package/dist/{chunk-QPEH2QPG.js → chunk-DBSOCI3G.js} +53 -54
  26. package/dist/{chunk-MHBMTPW7.js → chunk-ERS5EVUZ.js} +9 -0
  27. package/dist/{chunk-JSTQ3AWB.js → chunk-FIAPHX37.js} +1 -1
  28. package/dist/{chunk-IMFVFNJE.js → chunk-FTMXDOR6.js} +1 -1
  29. package/dist/{chunk-72GHBOL2.js → chunk-GZKSBLQL.js} +1 -1
  30. package/dist/{chunk-K6XAPGML.js → chunk-H7Y5CKTM.js} +1 -1
  31. package/dist/{chunk-4ZMOCPYO.js → chunk-NLVUVUGD.js} +1 -1
  32. package/dist/{chunk-Z77YQRQT.js → chunk-O5OJVPL6.js} +16 -5
  33. package/dist/{chunk-NKDM3FMH.js → chunk-OD3S2NHN.js} +1 -1
  34. package/dist/{chunk-65FRIL4D.js → chunk-OSXBPAMK.js} +1 -1
  35. package/dist/{chunk-DZS7CJKL.js → chunk-OXLLOSSR.js} +45 -47
  36. package/dist/{chunk-TS3XWPW5.js → chunk-RCWZBSK5.js} +1 -1
  37. package/dist/{chunk-NOPU4RZ4.js → chunk-S2FXOWOR.js} +3 -3
  38. package/dist/{chunk-VUCPTQ6G.js → chunk-SD3SQOZ2.js} +1 -1
  39. package/dist/{chunk-IM32EEDM.js → chunk-TPOTOBR7.js} +9 -9
  40. package/dist/{chunk-SSKDAOX5.js → chunk-XKECDXJS.js} +436 -340
  41. package/dist/{chunk-TKJZKICB.js → chunk-YPYGXRDR.js} +7 -7
  42. package/dist/{chunk-Q6AB7W5Z.js → chunk-YQ6KC6TE.js} +1 -1
  43. package/dist/{chunk-NERR4TAO.js → chunk-YZD2MRNQ.js} +972 -747
  44. package/dist/ci-workflow-EQZFVX3P.js +8 -0
  45. package/dist/{dist-HXHWB7SV.js → dist-B26DFXMP.js} +571 -478
  46. package/dist/{dist-L7LAAQAS.js → dist-DZ63LLUD.js} +1 -1
  47. package/dist/{dist-2B363XUH.js → dist-HWXF2C3R.js} +18 -2
  48. package/dist/{dist-D4RYGUZE.js → dist-USY2C5JL.js} +3 -1
  49. package/dist/{docs-FZOPM4GK.js → docs-7ECGYMAV.js} +4 -4
  50. package/dist/engine-EG4EH4IX.js +8 -0
  51. package/dist/{entropy-LVHJMFGH.js → entropy-5USWKLVS.js} +3 -3
  52. package/dist/{feedback-IHLVLMRD.js → feedback-UTBXZZHF.js} +1 -1
  53. package/dist/{generate-agent-definitions-64S3CLEZ.js → generate-agent-definitions-3PM5EU7V.js} +4 -4
  54. package/dist/{graph-loader-GJZ4FN4Y.js → graph-loader-2M2HXDQI.js} +1 -1
  55. package/dist/index.d.ts +148 -9
  56. package/dist/index.js +24 -24
  57. package/dist/loader-ZPALXIVR.js +10 -0
  58. package/dist/{mcp-JQUI7BVZ.js → mcp-362EZHF4.js} +14 -14
  59. package/dist/{performance-ZTVSUANN.js → performance-OQAFMJUD.js} +3 -3
  60. package/dist/{review-pipeline-76JHKGSV.js → review-pipeline-C4GCFVGP.js} +1 -1
  61. package/dist/runtime-7YLVK453.js +9 -0
  62. package/dist/{security-FWQZF2IZ.js → security-PZOX7AQS.js} +1 -1
  63. package/dist/templates/axum/Cargo.toml.hbs +8 -0
  64. package/dist/templates/axum/src/main.rs +12 -0
  65. package/dist/templates/axum/template.json +16 -0
  66. package/dist/templates/django/manage.py.hbs +19 -0
  67. package/dist/templates/django/requirements.txt.hbs +1 -0
  68. package/dist/templates/django/src/settings.py.hbs +44 -0
  69. package/dist/templates/django/src/urls.py +6 -0
  70. package/dist/templates/django/src/wsgi.py.hbs +9 -0
  71. package/dist/templates/django/template.json +21 -0
  72. package/dist/templates/express/package.json.hbs +15 -0
  73. package/dist/templates/express/src/app.ts +12 -0
  74. package/dist/templates/express/src/lib/.gitkeep +0 -0
  75. package/dist/templates/express/template.json +16 -0
  76. package/dist/templates/fastapi/requirements.txt.hbs +2 -0
  77. package/dist/templates/fastapi/src/main.py +8 -0
  78. package/dist/templates/fastapi/template.json +20 -0
  79. package/dist/templates/gin/go.mod.hbs +5 -0
  80. package/dist/templates/gin/main.go +15 -0
  81. package/dist/templates/gin/template.json +19 -0
  82. package/dist/templates/go-base/.golangci.yml +16 -0
  83. package/dist/templates/go-base/AGENTS.md.hbs +35 -0
  84. package/dist/templates/go-base/go.mod.hbs +3 -0
  85. package/dist/templates/go-base/harness.config.json.hbs +17 -0
  86. package/dist/templates/go-base/main.go +7 -0
  87. package/dist/templates/go-base/template.json +14 -0
  88. package/dist/templates/java-base/AGENTS.md.hbs +35 -0
  89. package/dist/templates/java-base/checkstyle.xml +20 -0
  90. package/dist/templates/java-base/harness.config.json.hbs +16 -0
  91. package/dist/templates/java-base/pom.xml.hbs +39 -0
  92. package/dist/templates/java-base/src/main/java/App.java.hbs +5 -0
  93. package/dist/templates/java-base/template.json +13 -0
  94. package/dist/templates/nestjs/nest-cli.json +5 -0
  95. package/dist/templates/nestjs/package.json.hbs +18 -0
  96. package/dist/templates/nestjs/src/app.module.ts +8 -0
  97. package/dist/templates/nestjs/src/lib/.gitkeep +0 -0
  98. package/dist/templates/nestjs/src/main.ts +11 -0
  99. package/dist/templates/nestjs/template.json +16 -0
  100. package/dist/templates/nextjs/template.json +15 -1
  101. package/dist/templates/python-base/.python-version +1 -0
  102. package/dist/templates/python-base/AGENTS.md.hbs +32 -0
  103. package/dist/templates/python-base/harness.config.json.hbs +16 -0
  104. package/dist/templates/python-base/pyproject.toml.hbs +18 -0
  105. package/dist/templates/python-base/ruff.toml +5 -0
  106. package/dist/templates/python-base/src/__init__.py +0 -0
  107. package/dist/templates/python-base/template.json +13 -0
  108. package/dist/templates/react-vite/index.html +12 -0
  109. package/dist/templates/react-vite/package.json.hbs +18 -0
  110. package/dist/templates/react-vite/src/App.tsx +7 -0
  111. package/dist/templates/react-vite/src/lib/.gitkeep +0 -0
  112. package/dist/templates/react-vite/src/main.tsx +9 -0
  113. package/dist/templates/react-vite/template.json +19 -0
  114. package/dist/templates/react-vite/vite.config.ts +6 -0
  115. package/dist/templates/rust-base/AGENTS.md.hbs +35 -0
  116. package/dist/templates/rust-base/Cargo.toml.hbs +6 -0
  117. package/dist/templates/rust-base/clippy.toml +2 -0
  118. package/dist/templates/rust-base/harness.config.json.hbs +17 -0
  119. package/dist/templates/rust-base/src/main.rs +3 -0
  120. package/dist/templates/rust-base/template.json +14 -0
  121. package/dist/templates/spring-boot/pom.xml.hbs +50 -0
  122. package/dist/templates/spring-boot/src/main/java/Application.java.hbs +19 -0
  123. package/dist/templates/spring-boot/template.json +15 -0
  124. package/dist/templates/vue/index.html +12 -0
  125. package/dist/templates/vue/package.json.hbs +16 -0
  126. package/dist/templates/vue/src/App.vue +7 -0
  127. package/dist/templates/vue/src/lib/.gitkeep +0 -0
  128. package/dist/templates/vue/src/main.ts +4 -0
  129. package/dist/templates/vue/template.json +19 -0
  130. package/dist/templates/vue/vite.config.ts +6 -0
  131. package/dist/{validate-GCHZJIL7.js → validate-FD3Z6VJD.js} +4 -4
  132. package/dist/validate-cross-check-WNJM6H2D.js +8 -0
  133. package/package.json +5 -5
  134. package/dist/agents-md-XU3BHE22.js +0 -8
  135. package/dist/ci-workflow-EHV65NQB.js +0 -8
  136. package/dist/engine-OL4T6NZS.js +0 -8
  137. package/dist/loader-DPYFB6R6.js +0 -10
  138. package/dist/runtime-X7U6SC7K.js +0 -9
  139. package/dist/validate-cross-check-STFHYMAZ.js +0 -8
@@ -1,20 +1,20 @@
1
1
  import {
2
2
  generateCIWorkflow
3
- } from "./chunk-VUCPTQ6G.js";
3
+ } from "./chunk-SD3SQOZ2.js";
4
4
  import {
5
5
  OutputFormatter,
6
6
  OutputMode,
7
7
  createCheckPhaseGateCommand,
8
8
  findFiles
9
- } from "./chunk-UAX4I5ZE.js";
9
+ } from "./chunk-6KTUUFRN.js";
10
10
  import {
11
11
  createGenerateAgentDefinitionsCommand,
12
12
  generateAgentDefinitions
13
- } from "./chunk-TS3XWPW5.js";
13
+ } from "./chunk-RCWZBSK5.js";
14
14
  import {
15
15
  listPersonas,
16
16
  loadPersona
17
- } from "./chunk-Q6AB7W5Z.js";
17
+ } from "./chunk-YQ6KC6TE.js";
18
18
  import {
19
19
  runPersona
20
20
  } from "./chunk-TRAPF4IX.js";
@@ -33,29 +33,31 @@ import {
33
33
  import {
34
34
  generate,
35
35
  validate
36
- } from "./chunk-QPEH2QPG.js";
36
+ } from "./chunk-DBSOCI3G.js";
37
37
  import {
38
38
  generateRuntime
39
- } from "./chunk-JSTQ3AWB.js";
39
+ } from "./chunk-FIAPHX37.js";
40
40
  import {
41
41
  toKebabCase
42
42
  } from "./chunk-KET4QQZB.js";
43
43
  import {
44
44
  generateAgentsMd
45
- } from "./chunk-NKDM3FMH.js";
45
+ } from "./chunk-OD3S2NHN.js";
46
46
  import {
47
+ appendFrameworkAgents,
47
48
  createGenerateSlashCommandsCommand,
48
49
  generateSlashCommands,
49
50
  handleGetImpact,
50
- handleOrphanDeletion
51
- } from "./chunk-NERR4TAO.js";
51
+ handleOrphanDeletion,
52
+ persistToolingConfig
53
+ } from "./chunk-YZD2MRNQ.js";
52
54
  import {
53
55
  VALID_PLATFORMS
54
56
  } from "./chunk-ZOAWBDWU.js";
55
57
  import {
56
58
  findConfigFile,
57
59
  resolveConfig
58
- } from "./chunk-Z77YQRQT.js";
60
+ } from "./chunk-O5OJVPL6.js";
59
61
  import {
60
62
  resolveGlobalSkillsDir,
61
63
  resolvePersonasDir,
@@ -76,7 +78,7 @@ import {
76
78
  } from "./chunk-BM3PWGXQ.js";
77
79
  import {
78
80
  TemplateEngine
79
- } from "./chunk-C2ERUR3L.js";
81
+ } from "./chunk-7MJAPE3Z.js";
80
82
  import {
81
83
  ArchBaselineManager,
82
84
  ArchConfigSchema,
@@ -125,11 +127,11 @@ import {
125
127
  validateKnowledgeMap,
126
128
  writeConfig,
127
129
  writeLockfile
128
- } from "./chunk-PQ5YK4AY.js";
130
+ } from "./chunk-ABQHQ6I5.js";
129
131
  import {
130
132
  Err,
131
133
  Ok
132
- } from "./chunk-MHBMTPW7.js";
134
+ } from "./chunk-ERS5EVUZ.js";
133
135
 
134
136
  // src/index.ts
135
137
  import { Command as Command55 } from "commander";
@@ -211,7 +213,7 @@ function createValidateCommand() {
211
213
  process.exit(result.error.exitCode);
212
214
  }
213
215
  if (opts.crossCheck) {
214
- const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-STFHYMAZ.js");
216
+ const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-WNJM6H2D.js");
215
217
  const cwd = process.cwd();
216
218
  const specsDir = path.join(cwd, "docs", "specs");
217
219
  const plansDir = path.join(cwd, "docs", "plans");
@@ -564,12 +566,11 @@ function createCheckSecurityCommand() {
564
566
  // src/commands/perf.ts
565
567
  import { Command as Command5 } from "commander";
566
568
  import * as path5 from "path";
567
- function createPerfCommand() {
568
- const perf = new Command5("perf").description("Performance benchmark and baseline management");
569
+ function registerBenchCommand(perf) {
569
570
  perf.command("bench [glob]").description("Run benchmarks via vitest bench").action(async (glob, _opts, cmd) => {
570
571
  const globalOpts = cmd.optsWithGlobals();
571
572
  const cwd = process.cwd();
572
- const { BenchmarkRunner } = await import("./dist-2B363XUH.js");
573
+ const { BenchmarkRunner } = await import("./dist-HWXF2C3R.js");
573
574
  const runner = new BenchmarkRunner();
574
575
  const benchFiles = runner.discover(cwd, glob);
575
576
  if (benchFiles.length === 0) {
@@ -580,48 +581,47 @@ function createPerfCommand() {
580
581
  }
581
582
  return;
582
583
  }
583
- if (globalOpts.json) {
584
- logger.info(`Found ${benchFiles.length} benchmark file(s). Running...`);
585
- } else {
586
- logger.info(`Found ${benchFiles.length} benchmark file(s):`);
587
- for (const f of benchFiles) {
588
- logger.info(` ${f}`);
589
- }
590
- logger.info("Running benchmarks...");
591
- }
584
+ logBenchDiscovery(globalOpts.json, benchFiles);
592
585
  const result = await runner.run(glob ? { cwd, glob } : { cwd });
593
- if (globalOpts.json) {
594
- console.log(JSON.stringify({ results: result.results, success: result.success }));
595
- } else {
596
- if (result.success && result.results.length > 0) {
597
- logger.info(`
586
+ outputBenchResults(globalOpts.json, result);
587
+ });
588
+ }
589
+ function logBenchDiscovery(json, benchFiles) {
590
+ if (json) {
591
+ logger.info(`Found ${benchFiles.length} benchmark file(s). Running...`);
592
+ } else {
593
+ logger.info(`Found ${benchFiles.length} benchmark file(s):`);
594
+ for (const f of benchFiles) logger.info(` ${f}`);
595
+ logger.info("Running benchmarks...");
596
+ }
597
+ }
598
+ function outputBenchResults(json, result) {
599
+ if (json) {
600
+ console.log(JSON.stringify({ results: result.results, success: result.success }));
601
+ return;
602
+ }
603
+ if (result.success && result.results.length > 0) {
604
+ logger.info(`
598
605
  Results (${result.results.length} benchmarks):`);
599
- for (const r of result.results) {
600
- logger.info(
601
- ` ${r.file}::${r.name}: ${r.opsPerSec} ops/s (mean: ${r.meanMs.toFixed(2)}ms)`
602
- );
603
- }
604
- logger.info("\nTo save as baselines: harness perf baselines update");
605
- } else {
606
- logger.info("Benchmark run completed. Check output above for details.");
607
- if (result.rawOutput) {
608
- console.log(result.rawOutput);
609
- }
610
- }
606
+ for (const r of result.results) {
607
+ logger.info(` ${r.file}::${r.name}: ${r.opsPerSec} ops/s (mean: ${r.meanMs.toFixed(2)}ms)`);
611
608
  }
612
- });
609
+ logger.info("\nTo save as baselines: harness perf baselines update");
610
+ } else {
611
+ logger.info("Benchmark run completed. Check output above for details.");
612
+ if (result.rawOutput) console.log(result.rawOutput);
613
+ }
614
+ }
615
+ function registerBaselinesCommands(perf) {
613
616
  const baselines = perf.command("baselines").description("Manage performance baselines");
614
617
  baselines.command("show").description("Display current baselines").action(async (_opts, cmd) => {
615
618
  const globalOpts = cmd.optsWithGlobals();
616
- const cwd = process.cwd();
617
- const manager = new BaselineManager(cwd);
619
+ const manager = new BaselineManager(process.cwd());
618
620
  const data = manager.load();
619
621
  if (!data) {
620
- if (globalOpts.json) {
621
- console.log(JSON.stringify({ baselines: null, message: "No baselines file found" }));
622
- } else {
623
- logger.info("No baselines file found at .harness/perf/baselines.json");
624
- }
622
+ console.log(
623
+ globalOpts.json ? JSON.stringify({ baselines: null, message: "No baselines file found" }) : "No baselines file found at .harness/perf/baselines.json"
624
+ );
625
625
  return;
626
626
  }
627
627
  if (globalOpts.json) {
@@ -638,7 +638,7 @@ Results (${result.results.length} benchmarks):`);
638
638
  baselines.command("update").description("Update baselines from latest benchmark run").action(async (_opts, cmd) => {
639
639
  const globalOpts = cmd.optsWithGlobals();
640
640
  const cwd = process.cwd();
641
- const { BenchmarkRunner } = await import("./dist-2B363XUH.js");
641
+ const { BenchmarkRunner } = await import("./dist-HWXF2C3R.js");
642
642
  const runner = new BenchmarkRunner();
643
643
  const manager = new BaselineManager(cwd);
644
644
  logger.info("Running benchmarks to update baselines...");
@@ -649,12 +649,7 @@ Results (${result.results.length} benchmarks):`);
649
649
  );
650
650
  return;
651
651
  }
652
- let commitHash = "unknown";
653
- try {
654
- const { execSync: execSync5 } = await import("child_process");
655
- commitHash = execSync5("git rev-parse --short HEAD", { cwd, encoding: "utf-8" }).trim();
656
- } catch {
657
- }
652
+ const commitHash = await getCommitHash(cwd);
658
653
  manager.save(benchResult.results, commitHash);
659
654
  if (globalOpts.json) {
660
655
  console.log(JSON.stringify({ updated: benchResult.results.length, commitHash }));
@@ -663,10 +658,20 @@ Results (${result.results.length} benchmarks):`);
663
658
  logger.info("Baselines saved to .harness/perf/baselines.json");
664
659
  }
665
660
  });
661
+ }
662
+ async function getCommitHash(cwd) {
663
+ try {
664
+ const { execSync: execSync5 } = await import("child_process");
665
+ return execSync5("git rev-parse --short HEAD", { cwd, encoding: "utf-8" }).trim();
666
+ } catch {
667
+ return "unknown";
668
+ }
669
+ }
670
+ function registerReportCommand(perf) {
666
671
  perf.command("report").description("Full performance report with metrics, trends, and hotspots").action(async (_opts, cmd) => {
667
672
  const globalOpts = cmd.optsWithGlobals();
668
673
  const cwd = process.cwd();
669
- const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-2B363XUH.js");
674
+ const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-HWXF2C3R.js");
670
675
  const analyzer = new EntropyAnalyzer2({
671
676
  rootDir: path5.resolve(cwd),
672
677
  analyze: { complexity: true, coupling: true }
@@ -703,10 +708,11 @@ Results (${result.results.length} benchmarks):`);
703
708
  }
704
709
  }
705
710
  });
711
+ }
712
+ function registerCriticalPathsCommand(perf) {
706
713
  perf.command("critical-paths").description("Show resolved critical path set (annotations + graph inference)").action(async (_opts, cmd) => {
707
714
  const globalOpts = cmd.optsWithGlobals();
708
- const cwd = process.cwd();
709
- const resolver = new CriticalPathResolver(cwd);
715
+ const resolver = new CriticalPathResolver(process.cwd());
710
716
  const result = await resolver.resolve();
711
717
  if (globalOpts.json) {
712
718
  console.log(JSON.stringify(result, null, 2));
@@ -721,12 +727,29 @@ Results (${result.results.length} benchmarks):`);
721
727
  }
722
728
  }
723
729
  });
730
+ }
731
+ function createPerfCommand() {
732
+ const perf = new Command5("perf").description("Performance benchmark and baseline management");
733
+ registerBenchCommand(perf);
734
+ registerBaselinesCommands(perf);
735
+ registerReportCommand(perf);
736
+ registerCriticalPathsCommand(perf);
724
737
  return perf;
725
738
  }
726
739
 
727
740
  // src/commands/check-docs.ts
728
741
  import { Command as Command6 } from "commander";
729
742
  import * as path6 from "path";
743
+
744
+ // src/utils/output.ts
745
+ function resolveOutputMode(globalOpts) {
746
+ if (globalOpts.json) return OutputMode.JSON;
747
+ if (globalOpts.quiet) return OutputMode.QUIET;
748
+ if (globalOpts.verbose) return OutputMode.VERBOSE;
749
+ return OutputMode.TEXT;
750
+ }
751
+
752
+ // src/commands/check-docs.ts
730
753
  async function runCheckDocs(options) {
731
754
  const cwd = options.cwd ?? process.cwd();
732
755
  const minCoverage = options.minCoverage ?? 80;
@@ -777,7 +800,7 @@ async function runCheckDocs(options) {
777
800
  function createCheckDocsCommand() {
778
801
  const command = new Command6("check-docs").description("Check documentation coverage").option("--min-coverage <percent>", "Minimum coverage percentage", "80").action(async (opts, cmd) => {
779
802
  const globalOpts = cmd.optsWithGlobals();
780
- const mode = globalOpts.json ? OutputMode.JSON : globalOpts.quiet ? OutputMode.QUIET : globalOpts.verbose ? OutputMode.VERBOSE : OutputMode.TEXT;
803
+ const mode = resolveOutputMode(globalOpts);
781
804
  const formatter = new OutputFormatter(mode);
782
805
  const result = await runCheckDocs({
783
806
  configPath: globalOpts.config,
@@ -947,7 +970,6 @@ function createSetupMcpCommand() {
947
970
  async function runInit(options) {
948
971
  const cwd = options.cwd ?? process.cwd();
949
972
  const name = options.name ?? path8.basename(cwd);
950
- const level = options.level ?? "basic";
951
973
  const force = options.force ?? false;
952
974
  const configPath = path8.join(cwd, "harness.config.json");
953
975
  if (!force && fs2.existsSync(configPath)) {
@@ -955,33 +977,82 @@ async function runInit(options) {
955
977
  new CLIError("Project already initialized. Use --force to overwrite.", ExitCode.ERROR)
956
978
  );
957
979
  }
958
- const templatesDir = resolveTemplatesDir();
959
- const engine = new TemplateEngine(templatesDir);
960
- const resolveResult = engine.resolveTemplate(level, options.framework);
961
- if (!resolveResult.ok) {
962
- return Err(new CLIError(resolveResult.error.message, ExitCode.ERROR));
980
+ const engine = new TemplateEngine(resolveTemplatesDir());
981
+ const templates = engine.listTemplates();
982
+ const templateList = templates.ok ? templates.value : [];
983
+ const validationError = validateFrameworkLanguage(options, templateList);
984
+ if (validationError) return Err(validationError);
985
+ const detected = tryAutoDetect(engine, cwd, options);
986
+ if (detected) return Ok(detected);
987
+ const language = resolveLanguage(options, templateList);
988
+ return scaffoldProject(engine, { cwd, name, force, language, options });
989
+ }
990
+ function validateFrameworkLanguage(options, templateList) {
991
+ if (!options.framework || !options.language) return null;
992
+ const fwTemplate = templateList.find((t) => t.framework === options.framework);
993
+ if (fwTemplate?.language && fwTemplate.language !== options.language) {
994
+ return new CLIError(
995
+ `Framework "${options.framework}" is a ${fwTemplate.language} framework, but --language ${options.language} was specified. Remove --language or use --language ${fwTemplate.language}.`,
996
+ ExitCode.ERROR
997
+ );
998
+ }
999
+ return null;
1000
+ }
1001
+ function tryAutoDetect(engine, cwd, options) {
1002
+ if (options.framework || options.language) return null;
1003
+ const detectResult = engine.detectFramework(cwd);
1004
+ if (detectResult.ok && detectResult.value.length > 0) {
1005
+ return { filesCreated: [], skippedConfigs: [], detectedFrameworks: detectResult.value };
963
1006
  }
1007
+ return null;
1008
+ }
1009
+ function resolveLanguage(options, templateList) {
1010
+ if (options.language) return options.language;
1011
+ if (options.framework) {
1012
+ const fwTemplate = templateList.find((t) => t.framework === options.framework);
1013
+ if (fwTemplate?.language) return fwTemplate.language;
1014
+ }
1015
+ return void 0;
1016
+ }
1017
+ function scaffoldProject(engine, ctx) {
1018
+ const { cwd, name, force, language, options } = ctx;
1019
+ const isNonJs = language && language !== "typescript";
1020
+ const level = isNonJs ? void 0 : options.level ?? "basic";
1021
+ const resolveResult = engine.resolveTemplate(level, options.framework, language);
1022
+ if (!resolveResult.ok) return Err(new CLIError(resolveResult.error.message, ExitCode.ERROR));
964
1023
  const renderResult = engine.render(resolveResult.value, {
965
1024
  projectName: name,
966
- level,
967
- ...options.framework !== void 0 && { framework: options.framework }
1025
+ level: level ?? "",
1026
+ ...options.framework !== void 0 && { framework: options.framework },
1027
+ ...language !== void 0 && { language }
968
1028
  });
969
- if (!renderResult.ok) {
970
- return Err(new CLIError(renderResult.error.message, ExitCode.ERROR));
971
- }
972
- const writeResult = engine.write(renderResult.value, cwd, { overwrite: force });
973
- if (!writeResult.ok) {
974
- return Err(new CLIError(writeResult.error.message, ExitCode.ERROR));
1029
+ if (!renderResult.ok) return Err(new CLIError(renderResult.error.message, ExitCode.ERROR));
1030
+ const writeResult = engine.write(renderResult.value, cwd, {
1031
+ overwrite: force,
1032
+ ...language !== void 0 && { language }
1033
+ });
1034
+ if (!writeResult.ok) return Err(new CLIError(writeResult.error.message, ExitCode.ERROR));
1035
+ if (writeResult.value.skippedConfigs.length > 0) {
1036
+ logger.warn("Skipped existing package config files:");
1037
+ for (const file of writeResult.value.skippedConfigs) {
1038
+ logger.info(` - ${file} (add harness dependencies manually)`);
1039
+ }
975
1040
  }
976
- return Ok({ filesCreated: writeResult.value });
1041
+ persistToolingConfig(cwd, resolveResult.value, options.framework);
1042
+ appendFrameworkAgents(cwd, options.framework, language);
1043
+ return Ok({
1044
+ filesCreated: writeResult.value.written,
1045
+ skippedConfigs: writeResult.value.skippedConfigs
1046
+ });
977
1047
  }
978
1048
  function createInitCommand() {
979
- const command = new Command8("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("-f, --force", "Overwrite existing files").option("-y, --yes", "Use defaults without prompting").action(async (opts, cmd) => {
1049
+ const command = new Command8("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) => {
980
1050
  const globalOpts = cmd.optsWithGlobals();
981
1051
  const result = await runInit({
982
1052
  name: opts.name,
983
1053
  level: opts.level,
984
1054
  framework: opts.framework,
1055
+ language: opts.language,
985
1056
  force: opts.force
986
1057
  });
987
1058
  if (!result.ok) {
@@ -1077,10 +1148,39 @@ async function runCleanup(options) {
1077
1148
  result.totalIssues = result.driftIssues.length + result.deadCode.length + result.patternViolations.length;
1078
1149
  return Ok(result);
1079
1150
  }
1151
+ function printCleanupResult(value, formatter) {
1152
+ console.log(
1153
+ formatter.formatSummary("Entropy issues", value.totalIssues.toString(), value.totalIssues === 0)
1154
+ );
1155
+ if (value.driftIssues.length > 0) {
1156
+ console.log("\nDocumentation drift:");
1157
+ for (const issue of value.driftIssues) {
1158
+ console.log(` - ${issue.file}: ${issue.issue}`);
1159
+ }
1160
+ }
1161
+ if (value.deadCode.length > 0) {
1162
+ console.log("\nDead code:");
1163
+ for (const item of value.deadCode.slice(0, 10)) {
1164
+ console.log(` - ${item.file}${item.symbol ? `: ${item.symbol}` : ""}`);
1165
+ }
1166
+ if (value.deadCode.length > 10) {
1167
+ console.log(` ... and ${value.deadCode.length - 10} more`);
1168
+ }
1169
+ }
1170
+ if (value.patternViolations.length > 0) {
1171
+ console.log("\nPattern violations:");
1172
+ for (const violation of value.patternViolations.slice(0, 10)) {
1173
+ console.log(` - ${violation.file} [${violation.pattern}]: ${violation.message}`);
1174
+ }
1175
+ if (value.patternViolations.length > 10) {
1176
+ console.log(` ... and ${value.patternViolations.length - 10} more`);
1177
+ }
1178
+ }
1179
+ }
1080
1180
  function createCleanupCommand() {
1081
1181
  const command = new Command9("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) => {
1082
1182
  const globalOpts = cmd.optsWithGlobals();
1083
- const mode = globalOpts.json ? OutputMode.JSON : globalOpts.quiet ? OutputMode.QUIET : globalOpts.verbose ? OutputMode.VERBOSE : OutputMode.TEXT;
1183
+ const mode = resolveOutputMode(globalOpts);
1084
1184
  const formatter = new OutputFormatter(mode);
1085
1185
  const result = await runCleanup({
1086
1186
  configPath: globalOpts.config,
@@ -1100,37 +1200,7 @@ function createCleanupCommand() {
1100
1200
  if (mode === OutputMode.JSON) {
1101
1201
  console.log(JSON.stringify(result.value, null, 2));
1102
1202
  } else if (mode !== OutputMode.QUIET || result.value.totalIssues > 0) {
1103
- console.log(
1104
- formatter.formatSummary(
1105
- "Entropy issues",
1106
- result.value.totalIssues.toString(),
1107
- result.value.totalIssues === 0
1108
- )
1109
- );
1110
- if (result.value.driftIssues.length > 0) {
1111
- console.log("\nDocumentation drift:");
1112
- for (const issue of result.value.driftIssues) {
1113
- console.log(` - ${issue.file}: ${issue.issue}`);
1114
- }
1115
- }
1116
- if (result.value.deadCode.length > 0) {
1117
- console.log("\nDead code:");
1118
- for (const item of result.value.deadCode.slice(0, 10)) {
1119
- console.log(` - ${item.file}${item.symbol ? `: ${item.symbol}` : ""}`);
1120
- }
1121
- if (result.value.deadCode.length > 10) {
1122
- console.log(` ... and ${result.value.deadCode.length - 10} more`);
1123
- }
1124
- }
1125
- if (result.value.patternViolations.length > 0) {
1126
- console.log("\nPattern violations:");
1127
- for (const violation of result.value.patternViolations.slice(0, 10)) {
1128
- console.log(` - ${violation.file} [${violation.pattern}]: ${violation.message}`);
1129
- }
1130
- if (result.value.patternViolations.length > 10) {
1131
- console.log(` ... and ${result.value.patternViolations.length - 10} more`);
1132
- }
1133
- }
1203
+ printCleanupResult(result.value, formatter);
1134
1204
  }
1135
1205
  process.exit(result.value.totalIssues === 0 ? ExitCode.SUCCESS : ExitCode.VALIDATION_FAILED);
1136
1206
  });
@@ -1238,10 +1308,42 @@ async function runFixDrift(options) {
1238
1308
  };
1239
1309
  return Ok(result);
1240
1310
  }
1311
+ function printFixDriftResult(value, mode, formatter) {
1312
+ const statusMessage = value.dryRun ? "(dry-run)" : "";
1313
+ console.log(
1314
+ formatter.formatSummary(
1315
+ `Fix drift ${statusMessage}`,
1316
+ `${value.fixes.length} fixes, ${value.suggestions.length} suggestions`,
1317
+ value.fixes.length === 0 && value.suggestions.length === 0
1318
+ )
1319
+ );
1320
+ if (value.fixes.length > 0) {
1321
+ console.log("\nFixes:");
1322
+ for (const fix of value.fixes.slice(0, 10)) {
1323
+ const status = fix.applied ? "[applied]" : "[pending]";
1324
+ console.log(` ${status} ${fix.action}: ${fix.file}`);
1325
+ }
1326
+ if (value.fixes.length > 10) {
1327
+ console.log(` ... and ${value.fixes.length - 10} more`);
1328
+ }
1329
+ }
1330
+ if (value.suggestions.length > 0 && (mode === OutputMode.VERBOSE || value.fixes.length === 0)) {
1331
+ console.log("\nSuggestions:");
1332
+ for (const suggestion of value.suggestions.slice(0, 10)) {
1333
+ console.log(` - ${suggestion.file}: ${suggestion.suggestion}`);
1334
+ }
1335
+ if (value.suggestions.length > 10) {
1336
+ console.log(` ... and ${value.suggestions.length - 10} more`);
1337
+ }
1338
+ }
1339
+ if (value.dryRun && value.fixes.length > 0) {
1340
+ console.log("\nRun with --no-dry-run to apply fixes.");
1341
+ }
1342
+ }
1241
1343
  function createFixDriftCommand() {
1242
1344
  const command = new Command10("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) => {
1243
1345
  const globalOpts = cmd.optsWithGlobals();
1244
- const mode = globalOpts.json ? OutputMode.JSON : globalOpts.quiet ? OutputMode.QUIET : globalOpts.verbose ? OutputMode.VERBOSE : OutputMode.TEXT;
1346
+ const mode = resolveOutputMode(globalOpts);
1245
1347
  const formatter = new OutputFormatter(mode);
1246
1348
  const result = await runFixDrift({
1247
1349
  configPath: globalOpts.config,
@@ -1261,37 +1363,7 @@ function createFixDriftCommand() {
1261
1363
  if (mode === OutputMode.JSON) {
1262
1364
  console.log(JSON.stringify(result.value, null, 2));
1263
1365
  } else if (mode !== OutputMode.QUIET || result.value.fixes.length > 0 || result.value.suggestions.length > 0) {
1264
- const { value } = result;
1265
- const statusMessage = value.dryRun ? "(dry-run)" : "";
1266
- console.log(
1267
- formatter.formatSummary(
1268
- `Fix drift ${statusMessage}`,
1269
- `${value.fixes.length} fixes, ${value.suggestions.length} suggestions`,
1270
- value.fixes.length === 0 && value.suggestions.length === 0
1271
- )
1272
- );
1273
- if (value.fixes.length > 0) {
1274
- console.log("\nFixes:");
1275
- for (const fix of value.fixes.slice(0, 10)) {
1276
- const status = fix.applied ? "[applied]" : "[pending]";
1277
- console.log(` ${status} ${fix.action}: ${fix.file}`);
1278
- }
1279
- if (value.fixes.length > 10) {
1280
- console.log(` ... and ${value.fixes.length - 10} more`);
1281
- }
1282
- }
1283
- if (value.suggestions.length > 0 && (mode === OutputMode.VERBOSE || value.fixes.length === 0)) {
1284
- console.log("\nSuggestions:");
1285
- for (const suggestion of value.suggestions.slice(0, 10)) {
1286
- console.log(` - ${suggestion.file}: ${suggestion.suggestion}`);
1287
- }
1288
- if (value.suggestions.length > 10) {
1289
- console.log(` ... and ${value.suggestions.length - 10} more`);
1290
- }
1291
- }
1292
- if (value.dryRun && value.fixes.length > 0) {
1293
- console.log("\nRun with --no-dry-run to apply fixes.");
1294
- }
1366
+ printFixDriftResult(result.value, mode, formatter);
1295
1367
  }
1296
1368
  process.exit(ExitCode.SUCCESS);
1297
1369
  });
@@ -1356,49 +1428,54 @@ var VALID_TRIGGERS = /* @__PURE__ */ new Set([
1356
1428
  "on_plan_approved",
1357
1429
  "auto"
1358
1430
  ]);
1431
+ function resolveTrigger(triggerOpt) {
1432
+ if (triggerOpt === "auto") return "auto";
1433
+ return VALID_TRIGGERS.has(triggerOpt) ? triggerOpt : "manual";
1434
+ }
1435
+ function createCommandExecutor() {
1436
+ return async (command) => {
1437
+ if (!ALLOWED_PERSONA_COMMANDS.has(command)) {
1438
+ return Err(new Error(`Unknown harness command: ${command}`));
1439
+ }
1440
+ try {
1441
+ childProcess.execFileSync("npx", ["harness", command], { stdio: "inherit" });
1442
+ return Ok(null);
1443
+ } catch (error) {
1444
+ return Err(new Error(error instanceof Error ? error.message : String(error)));
1445
+ }
1446
+ };
1447
+ }
1448
+ async function runPersonaMode(opts, quiet) {
1449
+ const personasDir = resolvePersonasDir();
1450
+ const filePath = path11.join(personasDir, `${opts.persona}.yaml`);
1451
+ const personaResult = loadPersona(filePath);
1452
+ if (!personaResult.ok) {
1453
+ logger.error(personaResult.error.message);
1454
+ process.exit(ExitCode.ERROR);
1455
+ }
1456
+ const report = await runPersona(personaResult.value, {
1457
+ trigger: resolveTrigger(opts.trigger),
1458
+ commandExecutor: createCommandExecutor(),
1459
+ skillExecutor: executeSkill,
1460
+ projectPath: process.cwd()
1461
+ });
1462
+ if (!quiet) {
1463
+ logger.info(`Persona '${report.persona}' status: ${report.status}`);
1464
+ for (const s of report.steps) {
1465
+ const icon = s.status === "pass" ? "v" : s.status === "fail" ? "x" : "-";
1466
+ const typeTag = s.type === "skill" ? " [skill]" : "";
1467
+ console.log(` [${icon}] ${s.name}${typeTag} (${s.durationMs}ms)`);
1468
+ if (s.artifactPath) console.log(` artifact: ${s.artifactPath}`);
1469
+ }
1470
+ }
1471
+ process.exit(report.status === "fail" ? ExitCode.ERROR : ExitCode.SUCCESS);
1472
+ }
1359
1473
  function createRunCommand() {
1360
1474
  return new Command11("run").description("Run an agent task").argument("[task]", "Task to run (review, doc-review, test-review)").option("--timeout <ms>", "Timeout in milliseconds", "300000").option("--persona <name>", "Run a persona by name").option("--trigger <context>", "Trigger context (auto, on_pr, on_commit, manual)", "auto").action(async (task, opts, cmd) => {
1361
1475
  const globalOpts = cmd.optsWithGlobals();
1362
1476
  if (opts.persona) {
1363
- const personasDir = resolvePersonasDir();
1364
- const filePath = path11.join(personasDir, `${opts.persona}.yaml`);
1365
- const personaResult = loadPersona(filePath);
1366
- if (!personaResult.ok) {
1367
- logger.error(personaResult.error.message);
1368
- process.exit(ExitCode.ERROR);
1369
- }
1370
- const persona = personaResult.value;
1371
- const projectPath = process.cwd();
1372
- const trigger = opts.trigger === "auto" ? "auto" : VALID_TRIGGERS.has(opts.trigger) ? opts.trigger : "manual";
1373
- const commandExecutor = async (command) => {
1374
- if (!ALLOWED_PERSONA_COMMANDS.has(command)) {
1375
- return Err(new Error(`Unknown harness command: ${command}`));
1376
- }
1377
- try {
1378
- childProcess.execFileSync("npx", ["harness", command], { stdio: "inherit" });
1379
- return Ok(null);
1380
- } catch (error) {
1381
- return Err(new Error(error instanceof Error ? error.message : String(error)));
1382
- }
1383
- };
1384
- const report = await runPersona(persona, {
1385
- trigger,
1386
- commandExecutor,
1387
- skillExecutor: executeSkill,
1388
- projectPath
1389
- });
1390
- if (!globalOpts.quiet) {
1391
- logger.info(`Persona '${report.persona}' status: ${report.status}`);
1392
- for (const s of report.steps) {
1393
- const icon = s.status === "pass" ? "v" : s.status === "fail" ? "x" : "-";
1394
- const typeTag = s.type === "skill" ? " [skill]" : "";
1395
- console.log(` [${icon}] ${s.name}${typeTag} (${s.durationMs}ms)`);
1396
- if (s.artifactPath) {
1397
- console.log(` artifact: ${s.artifactPath}`);
1398
- }
1399
- }
1400
- }
1401
- process.exit(report.status === "fail" ? ExitCode.ERROR : ExitCode.SUCCESS);
1477
+ await runPersonaMode(opts, globalOpts.quiet);
1478
+ return;
1402
1479
  }
1403
1480
  if (!task) {
1404
1481
  logger.error("Either a task argument or --persona flag is required.");
@@ -1987,6 +2064,33 @@ function scanDirectory(dirPath, source) {
1987
2064
  }
1988
2065
  return skills;
1989
2066
  }
2067
+ function collectCommunitySkills(seen, allSkills) {
2068
+ const globalDir = resolveGlobalSkillsDir();
2069
+ const skillsDir = path15.dirname(globalDir);
2070
+ const communityBase = path15.join(skillsDir, "community");
2071
+ const communityPlatformDir = path15.join(communityBase, "claude-code");
2072
+ const lockfilePath = path15.join(communityBase, "skills-lock.json");
2073
+ const lockfile = readLockfile2(lockfilePath);
2074
+ const communitySkills = scanDirectory(communityPlatformDir, "community");
2075
+ for (const skill of communitySkills) {
2076
+ const lockEntry = lockfile.skills[`@harness-skills/${skill.name}`];
2077
+ if (lockEntry) skill.version = lockEntry.version;
2078
+ }
2079
+ for (const [pkgName, entry] of Object.entries(lockfile.skills)) {
2080
+ const shortName = pkgName.replace("@harness-skills/", "");
2081
+ if (!seen.has(shortName)) {
2082
+ seen.add(shortName);
2083
+ allSkills.push({
2084
+ name: shortName,
2085
+ description: "",
2086
+ type: "",
2087
+ source: "community",
2088
+ version: entry.version
2089
+ });
2090
+ }
2091
+ }
2092
+ return communitySkills;
2093
+ }
1990
2094
  function collectSkills(opts) {
1991
2095
  const seen = /* @__PURE__ */ new Set();
1992
2096
  const allSkills = [];
@@ -2005,34 +2109,7 @@ function collectSkills(opts) {
2005
2109
  }
2006
2110
  }
2007
2111
  if (opts.filter === "all" || opts.filter === "installed") {
2008
- const globalDir = resolveGlobalSkillsDir();
2009
- const skillsDir = path15.dirname(globalDir);
2010
- const communityBase = path15.join(skillsDir, "community");
2011
- const communityPlatformDir = path15.join(communityBase, "claude-code");
2012
- const lockfilePath = path15.join(communityBase, "skills-lock.json");
2013
- const lockfile = readLockfile2(lockfilePath);
2014
- const communitySkills = scanDirectory(communityPlatformDir, "community");
2015
- for (const skill of communitySkills) {
2016
- const pkgName = `@harness-skills/${skill.name}`;
2017
- const lockEntry = lockfile.skills[pkgName];
2018
- if (lockEntry) {
2019
- skill.version = lockEntry.version;
2020
- }
2021
- }
2022
- addUnique(communitySkills);
2023
- for (const [pkgName, entry] of Object.entries(lockfile.skills)) {
2024
- const shortName = pkgName.replace("@harness-skills/", "");
2025
- if (!seen.has(shortName)) {
2026
- seen.add(shortName);
2027
- allSkills.push({
2028
- name: shortName,
2029
- description: "",
2030
- type: "",
2031
- source: "community",
2032
- version: entry.version
2033
- });
2034
- }
2035
- }
2112
+ addUnique(collectCommunitySkills(seen, allSkills));
2036
2113
  }
2037
2114
  if (opts.filter === "all") {
2038
2115
  const globalDir = resolveGlobalSkillsDir();
@@ -2166,6 +2243,69 @@ ${options.priorState}`);
2166
2243
  }
2167
2244
 
2168
2245
  // src/commands/skill/run.ts
2246
+ function loadSkillMetadata(skillDir) {
2247
+ const yamlPath = path16.join(skillDir, "skill.yaml");
2248
+ if (!fs7.existsSync(yamlPath)) return null;
2249
+ try {
2250
+ const result = SkillMetadataSchema.safeParse(parse2(fs7.readFileSync(yamlPath, "utf-8")));
2251
+ return result.success ? result.data : null;
2252
+ } catch {
2253
+ return null;
2254
+ }
2255
+ }
2256
+ function resolveComplexity(metadata, requested, projectPath) {
2257
+ if (!metadata?.phases || metadata.phases.length === 0) return void 0;
2258
+ if (requested === "auto") return detectComplexity(projectPath);
2259
+ return requested;
2260
+ }
2261
+ function loadPrinciples(projectPath) {
2262
+ const principlesPath = path16.join(projectPath, "docs", "principles.md");
2263
+ return fs7.existsSync(principlesPath) ? fs7.readFileSync(principlesPath, "utf-8") : void 0;
2264
+ }
2265
+ function readMostRecentFileInDir(dirPath) {
2266
+ const files = fs7.readdirSync(dirPath).map((f) => ({ name: f, mtime: fs7.statSync(path16.join(dirPath, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
2267
+ if (files.length > 0) return fs7.readFileSync(path16.join(dirPath, files[0].name), "utf-8");
2268
+ return void 0;
2269
+ }
2270
+ function loadPriorState(metadata, projectPath) {
2271
+ if (!metadata?.state.persistent || metadata.state.files.length === 0) return void 0;
2272
+ for (const stateFilePath of metadata.state.files) {
2273
+ const fullPath = path16.join(projectPath, stateFilePath);
2274
+ if (!fs7.existsSync(fullPath)) continue;
2275
+ const stat = fs7.statSync(fullPath);
2276
+ if (stat.isDirectory()) return readMostRecentFileInDir(fullPath);
2277
+ return fs7.readFileSync(fullPath, "utf-8");
2278
+ }
2279
+ return void 0;
2280
+ }
2281
+ function validatePhaseName(metadata, phase) {
2282
+ if (!metadata?.phases) return true;
2283
+ return metadata.phases.map((p) => p.name).includes(phase);
2284
+ }
2285
+ function resolvePhaseState(metadata, projectPath, phase) {
2286
+ if (!validatePhaseName(metadata, phase)) {
2287
+ const validPhases = metadata.phases.map((p) => p.name);
2288
+ logger.error(`Unknown phase: ${phase}. Valid phases: ${validPhases.join(", ")}`);
2289
+ return null;
2290
+ }
2291
+ const priorState = loadPriorState(metadata, projectPath);
2292
+ const stateWarning = !priorState && metadata?.state.persistent ? "No prior phase data found. Earlier phases have not been completed. Proceed with caution." : void 0;
2293
+ return { priorState, stateWarning };
2294
+ }
2295
+ function appendProjectState(content, metadata, projectPath, hasPathOpt) {
2296
+ if (!metadata?.state.persistent || !hasPathOpt) return content;
2297
+ const stateFile = path16.join(projectPath, ".harness", "state.json");
2298
+ if (!fs7.existsSync(stateFile)) return content;
2299
+ const stateContent = fs7.readFileSync(stateFile, "utf-8");
2300
+ return content + `
2301
+
2302
+ ---
2303
+ ## Project State
2304
+ \`\`\`json
2305
+ ${stateContent}
2306
+ \`\`\`
2307
+ `;
2308
+ }
2169
2309
  function createRunCommand2() {
2170
2310
  return new Command22("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>", "Complexity: auto, light, full", "auto").option("--phase <name>", "Start at a specific phase (for re-entry)").option("--party", "Enable multi-perspective evaluation").action(async (name, opts, _cmd) => {
2171
2311
  const skillsDir = resolveSkillsDir();
@@ -2175,64 +2315,24 @@ function createRunCommand2() {
2175
2315
  process.exit(ExitCode.ERROR);
2176
2316
  return;
2177
2317
  }
2178
- const yamlPath = path16.join(skillDir, "skill.yaml");
2179
- let metadata = null;
2180
- if (fs7.existsSync(yamlPath)) {
2181
- try {
2182
- const raw = fs7.readFileSync(yamlPath, "utf-8");
2183
- const parsed = parse2(raw);
2184
- const result = SkillMetadataSchema.safeParse(parsed);
2185
- if (result.success) metadata = result.data;
2186
- } catch {
2187
- }
2188
- }
2189
- let complexity;
2190
- if (metadata?.phases && metadata.phases.length > 0) {
2191
- const requested = opts.complexity ?? "auto";
2192
- if (requested === "auto") {
2193
- const projectPath2 = opts.path ? path16.resolve(opts.path) : process.cwd();
2194
- complexity = detectComplexity(projectPath2);
2195
- } else {
2196
- complexity = requested;
2197
- }
2198
- }
2199
- let principles;
2318
+ const metadata = loadSkillMetadata(skillDir);
2200
2319
  const projectPath = opts.path ? path16.resolve(opts.path) : process.cwd();
2201
- const principlesPath = path16.join(projectPath, "docs", "principles.md");
2202
- if (fs7.existsSync(principlesPath)) {
2203
- principles = fs7.readFileSync(principlesPath, "utf-8");
2204
- }
2320
+ const complexity = resolveComplexity(
2321
+ metadata,
2322
+ opts.complexity ?? "auto",
2323
+ projectPath
2324
+ );
2325
+ const principles = loadPrinciples(projectPath);
2205
2326
  let priorState;
2206
2327
  let stateWarning;
2207
2328
  if (opts.phase) {
2208
- if (metadata?.phases) {
2209
- const validPhases = metadata.phases.map((p) => p.name);
2210
- if (!validPhases.includes(opts.phase)) {
2211
- logger.error(`Unknown phase: ${opts.phase}. Valid phases: ${validPhases.join(", ")}`);
2212
- process.exit(ExitCode.ERROR);
2213
- return;
2214
- }
2215
- }
2216
- if (metadata?.state.persistent && metadata.state.files.length > 0) {
2217
- for (const stateFilePath of metadata.state.files) {
2218
- const fullPath = path16.join(projectPath, stateFilePath);
2219
- if (fs7.existsSync(fullPath)) {
2220
- const stat = fs7.statSync(fullPath);
2221
- if (stat.isDirectory()) {
2222
- const files = fs7.readdirSync(fullPath).map((f) => ({ name: f, mtime: fs7.statSync(path16.join(fullPath, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
2223
- if (files.length > 0) {
2224
- priorState = fs7.readFileSync(path16.join(fullPath, files[0].name), "utf-8");
2225
- }
2226
- } else {
2227
- priorState = fs7.readFileSync(fullPath, "utf-8");
2228
- }
2229
- break;
2230
- }
2231
- }
2232
- if (!priorState) {
2233
- stateWarning = "No prior phase data found. Earlier phases have not been completed. Proceed with caution.";
2234
- }
2329
+ const phaseResult = resolvePhaseState(metadata, projectPath, opts.phase);
2330
+ if (!phaseResult) {
2331
+ process.exit(ExitCode.ERROR);
2332
+ return;
2235
2333
  }
2334
+ priorState = phaseResult.priorState;
2335
+ stateWarning = phaseResult.stateWarning;
2236
2336
  }
2237
2337
  const preamble = buildPreamble({
2238
2338
  ...complexity !== void 0 && { complexity },
@@ -2249,21 +2349,12 @@ function createRunCommand2() {
2249
2349
  process.exit(ExitCode.ERROR);
2250
2350
  return;
2251
2351
  }
2252
- let content = fs7.readFileSync(skillMdPath, "utf-8");
2253
- if (metadata?.state.persistent && opts.path) {
2254
- const stateFile = path16.join(projectPath, ".harness", "state.json");
2255
- if (fs7.existsSync(stateFile)) {
2256
- const stateContent = fs7.readFileSync(stateFile, "utf-8");
2257
- content += `
2258
-
2259
- ---
2260
- ## Project State
2261
- \`\`\`json
2262
- ${stateContent}
2263
- \`\`\`
2264
- `;
2265
- }
2266
- }
2352
+ const content = appendProjectState(
2353
+ fs7.readFileSync(skillMdPath, "utf-8"),
2354
+ metadata,
2355
+ projectPath,
2356
+ !!opts.path
2357
+ );
2267
2358
  process.stdout.write(preamble + content);
2268
2359
  process.exit(ExitCode.SUCCESS);
2269
2360
  });
@@ -2281,6 +2372,48 @@ var REQUIRED_SECTIONS = [
2281
2372
  "## Success Criteria",
2282
2373
  "## Examples"
2283
2374
  ];
2375
+ function validateSkillMd(name, skillMdPath, skillType, errors) {
2376
+ if (!fs8.existsSync(skillMdPath)) {
2377
+ errors.push(`${name}: missing SKILL.md`);
2378
+ return;
2379
+ }
2380
+ const mdContent = fs8.readFileSync(skillMdPath, "utf-8");
2381
+ for (const section of REQUIRED_SECTIONS) {
2382
+ if (!mdContent.includes(section)) {
2383
+ errors.push(`${name}/SKILL.md: missing section "${section}"`);
2384
+ }
2385
+ }
2386
+ if (!mdContent.trim().startsWith("# ")) {
2387
+ errors.push(`${name}/SKILL.md: must start with an h1 heading`);
2388
+ }
2389
+ if (skillType === "rigid") {
2390
+ if (!mdContent.includes("## Gates"))
2391
+ errors.push(`${name}/SKILL.md: rigid skill missing "## Gates" section`);
2392
+ if (!mdContent.includes("## Escalation"))
2393
+ errors.push(`${name}/SKILL.md: rigid skill missing "## Escalation" section`);
2394
+ }
2395
+ }
2396
+ function validateSkillEntry(name, skillsDir, errors) {
2397
+ const skillDir = path17.join(skillsDir, name);
2398
+ const yamlPath = path17.join(skillDir, "skill.yaml");
2399
+ if (!fs8.existsSync(yamlPath)) {
2400
+ errors.push(`${name}: missing skill.yaml`);
2401
+ return false;
2402
+ }
2403
+ try {
2404
+ const raw = fs8.readFileSync(yamlPath, "utf-8");
2405
+ const result = SkillMetadataSchema.safeParse(parse3(raw));
2406
+ if (!result.success) {
2407
+ errors.push(`${name}/skill.yaml: ${result.error.message}`);
2408
+ return false;
2409
+ }
2410
+ validateSkillMd(name, path17.join(skillDir, "SKILL.md"), result.data.type, errors);
2411
+ return true;
2412
+ } catch (e) {
2413
+ errors.push(`${name}: parse error \u2014 ${e instanceof Error ? e.message : String(e)}`);
2414
+ return false;
2415
+ }
2416
+ }
2284
2417
  function createValidateCommand3() {
2285
2418
  return new Command23("validate").description("Validate all skill.yaml files and SKILL.md structure").action(async (_opts, cmd) => {
2286
2419
  const globalOpts = cmd.optsWithGlobals();
@@ -2294,46 +2427,7 @@ function createValidateCommand3() {
2294
2427
  const errors = [];
2295
2428
  let validated = 0;
2296
2429
  for (const name of entries) {
2297
- const skillDir = path17.join(skillsDir, name);
2298
- const yamlPath = path17.join(skillDir, "skill.yaml");
2299
- const skillMdPath = path17.join(skillDir, "SKILL.md");
2300
- if (!fs8.existsSync(yamlPath)) {
2301
- errors.push(`${name}: missing skill.yaml`);
2302
- continue;
2303
- }
2304
- try {
2305
- const raw = fs8.readFileSync(yamlPath, "utf-8");
2306
- const parsed = parse3(raw);
2307
- const result = SkillMetadataSchema.safeParse(parsed);
2308
- if (!result.success) {
2309
- errors.push(`${name}/skill.yaml: ${result.error.message}`);
2310
- continue;
2311
- }
2312
- if (fs8.existsSync(skillMdPath)) {
2313
- const mdContent = fs8.readFileSync(skillMdPath, "utf-8");
2314
- for (const section of REQUIRED_SECTIONS) {
2315
- if (!mdContent.includes(section)) {
2316
- errors.push(`${name}/SKILL.md: missing section "${section}"`);
2317
- }
2318
- }
2319
- if (!mdContent.trim().startsWith("# ")) {
2320
- errors.push(`${name}/SKILL.md: must start with an h1 heading`);
2321
- }
2322
- if (result.data.type === "rigid") {
2323
- if (!mdContent.includes("## Gates")) {
2324
- errors.push(`${name}/SKILL.md: rigid skill missing "## Gates" section`);
2325
- }
2326
- if (!mdContent.includes("## Escalation")) {
2327
- errors.push(`${name}/SKILL.md: rigid skill missing "## Escalation" section`);
2328
- }
2329
- }
2330
- } else {
2331
- errors.push(`${name}: missing SKILL.md`);
2332
- }
2333
- validated++;
2334
- } catch (e) {
2335
- errors.push(`${name}: parse error \u2014 ${e instanceof Error ? e.message : String(e)}`);
2336
- }
2430
+ if (validateSkillEntry(name, skillsDir, errors)) validated++;
2337
2431
  }
2338
2432
  if (globalOpts.json) {
2339
2433
  logger.raw({ validated, errors });
@@ -3192,7 +3286,7 @@ function parseFailOn(failOn) {
3192
3286
  function createCheckCommand() {
3193
3287
  return new Command34("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) => {
3194
3288
  const globalOpts = cmd.optsWithGlobals();
3195
- const mode = globalOpts.json ? OutputMode.JSON : globalOpts.quiet ? OutputMode.QUIET : globalOpts.verbose ? OutputMode.VERBOSE : OutputMode.TEXT;
3289
+ const mode = resolveOutputMode(globalOpts);
3196
3290
  const skip = parseSkip(opts.skip);
3197
3291
  const failOn = parseFailOn(opts.failOn);
3198
3292
  const result = await runCICheck({
@@ -3446,6 +3540,21 @@ function prompt(question) {
3446
3540
  });
3447
3541
  });
3448
3542
  }
3543
+ async function offerRegeneration() {
3544
+ console.log("");
3545
+ const regenAnswer = await prompt("Regenerate slash commands and agent definitions? (Y/n) ");
3546
+ if (regenAnswer === "n" || regenAnswer === "no") return;
3547
+ const scopeAnswer = await prompt("Generate for (G)lobal or (l)ocal project? (G/l) ");
3548
+ const isGlobal = scopeAnswer !== "l" && scopeAnswer !== "local";
3549
+ try {
3550
+ execFileSync4("harness", ["generate", ...isGlobal ? ["--global"] : []], {
3551
+ stdio: "inherit"
3552
+ });
3553
+ } catch {
3554
+ logger.warn("Generation failed. Run manually:");
3555
+ console.log(` ${chalk3.cyan(`harness generate${isGlobal ? " --global" : ""}`)}`);
3556
+ }
3557
+ }
3449
3558
  function createUpdateCommand() {
3450
3559
  return new Command37("update").description("Update all @harness-engineering packages to the latest version").option("--version <semver>", "Pin @harness-engineering/cli to a specific version").action(async (opts, cmd) => {
3451
3560
  const globalOpts = cmd.optsWithGlobals();
@@ -3499,20 +3608,7 @@ function createUpdateCommand() {
3499
3608
  console.log(` ${chalk3.cyan(installCmd)}`);
3500
3609
  process.exit(ExitCode.ERROR);
3501
3610
  }
3502
- console.log("");
3503
- const regenAnswer = await prompt("Regenerate slash commands and agent definitions? (Y/n) ");
3504
- if (regenAnswer !== "n" && regenAnswer !== "no") {
3505
- const scopeAnswer = await prompt("Generate for (G)lobal or (l)ocal project? (G/l) ");
3506
- const isGlobal = scopeAnswer !== "l" && scopeAnswer !== "local";
3507
- try {
3508
- execFileSync4("harness", ["generate", ...isGlobal ? ["--global"] : []], {
3509
- stdio: "inherit"
3510
- });
3511
- } catch {
3512
- logger.warn("Generation failed. Run manually:");
3513
- console.log(` ${chalk3.cyan(`harness generate${isGlobal ? " --global" : ""}`)}`);
3514
- }
3515
- }
3611
+ await offerRegeneration();
3516
3612
  process.exit(ExitCode.SUCCESS);
3517
3613
  });
3518
3614
  }
@@ -3582,7 +3678,7 @@ function createGenerateCommand3() {
3582
3678
  import { Command as Command39 } from "commander";
3583
3679
  import * as path29 from "path";
3584
3680
  async function runScan(projectPath) {
3585
- const { GraphStore, CodeIngestor, TopologicalLinker, KnowledgeIngestor, GitIngestor } = await import("./dist-HXHWB7SV.js");
3681
+ const { GraphStore, CodeIngestor, TopologicalLinker, KnowledgeIngestor, GitIngestor } = await import("./dist-B26DFXMP.js");
3586
3682
  const store = new GraphStore();
3587
3683
  const start = Date.now();
3588
3684
  await new CodeIngestor(store).ingest(projectPath);
@@ -3663,7 +3759,7 @@ async function runIngest(projectPath, source, opts) {
3663
3759
  SyncManager,
3664
3760
  JiraConnector,
3665
3761
  SlackConnector
3666
- } = await import("./dist-HXHWB7SV.js");
3762
+ } = await import("./dist-B26DFXMP.js");
3667
3763
  const graphDir = path30.join(projectPath, ".harness", "graph");
3668
3764
  const store = new GraphStore();
3669
3765
  await store.load(graphDir);
@@ -3756,7 +3852,7 @@ function createIngestCommand() {
3756
3852
  import { Command as Command41 } from "commander";
3757
3853
  import * as path31 from "path";
3758
3854
  async function runQuery(projectPath, rootNodeId, opts) {
3759
- const { GraphStore, ContextQL } = await import("./dist-HXHWB7SV.js");
3855
+ const { GraphStore, ContextQL } = await import("./dist-B26DFXMP.js");
3760
3856
  const store = new GraphStore();
3761
3857
  const graphDir = path31.join(projectPath, ".harness", "graph");
3762
3858
  const loaded = await store.load(graphDir);
@@ -3805,7 +3901,7 @@ import { Command as Command42 } from "commander";
3805
3901
  // src/commands/graph/status.ts
3806
3902
  import * as path32 from "path";
3807
3903
  async function runGraphStatus(projectPath) {
3808
- const { GraphStore } = await import("./dist-HXHWB7SV.js");
3904
+ const { GraphStore } = await import("./dist-B26DFXMP.js");
3809
3905
  const graphDir = path32.join(projectPath, ".harness", "graph");
3810
3906
  const store = new GraphStore();
3811
3907
  const loaded = await store.load(graphDir);
@@ -3845,7 +3941,7 @@ async function runGraphStatus(projectPath) {
3845
3941
  // src/commands/graph/export.ts
3846
3942
  import * as path33 from "path";
3847
3943
  async function runGraphExport(projectPath, format) {
3848
- const { GraphStore } = await import("./dist-HXHWB7SV.js");
3944
+ const { GraphStore } = await import("./dist-B26DFXMP.js");
3849
3945
  const graphDir = path33.join(projectPath, ".harness", "graph");
3850
3946
  const store = new GraphStore();
3851
3947
  const loaded = await store.load(graphDir);
@@ -3924,7 +4020,7 @@ function createGraphCommand() {
3924
4020
  import { Command as Command43 } from "commander";
3925
4021
  function createMcpCommand() {
3926
4022
  return new Command43("mcp").description("Start the MCP (Model Context Protocol) server on stdio").action(async () => {
3927
- const { startServer: startServer2 } = await import("./mcp-JQUI7BVZ.js");
4023
+ const { startServer: startServer2 } = await import("./mcp-362EZHF4.js");
3928
4024
  await startServer2();
3929
4025
  });
3930
4026
  }
@@ -4141,7 +4237,7 @@ function createImpactPreviewCommand() {
4141
4237
  // src/commands/check-arch.ts
4142
4238
  import { Command as Command45 } from "commander";
4143
4239
  import { execSync as execSync4 } from "child_process";
4144
- function getCommitHash(cwd) {
4240
+ function getCommitHash2(cwd) {
4145
4241
  try {
4146
4242
  return execSync4("git rev-parse --short HEAD", { cwd, encoding: "utf-8" }).toString().trim();
4147
4243
  } catch {
@@ -4189,7 +4285,7 @@ async function runCheckArch(options) {
4189
4285
  }
4190
4286
  const manager = new ArchBaselineManager(cwd, archConfig.baselinePath);
4191
4287
  if (options.updateBaseline) {
4192
- const commitHash = getCommitHash(cwd);
4288
+ const commitHash = getCommitHash2(cwd);
4193
4289
  const baseline2 = manager.capture(results, commitHash);
4194
4290
  manager.save(baseline2);
4195
4291
  return Ok({