@harness-engineering/cli 1.6.0 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/agents/personas/code-reviewer.yaml +2 -0
  2. package/dist/agents/personas/codebase-health-analyst.yaml +5 -0
  3. package/dist/agents/personas/performance-guardian.yaml +26 -0
  4. package/dist/agents/personas/security-reviewer.yaml +35 -0
  5. package/dist/agents/skills/claude-code/harness-autopilot/SKILL.md +494 -0
  6. package/dist/agents/skills/claude-code/harness-autopilot/skill.yaml +52 -0
  7. package/dist/agents/skills/claude-code/harness-code-review/SKILL.md +15 -0
  8. package/dist/agents/skills/claude-code/harness-integrity/SKILL.md +20 -6
  9. package/dist/agents/skills/claude-code/harness-perf/SKILL.md +231 -0
  10. package/dist/agents/skills/claude-code/harness-perf/skill.yaml +47 -0
  11. package/dist/agents/skills/claude-code/harness-perf-tdd/SKILL.md +236 -0
  12. package/dist/agents/skills/claude-code/harness-perf-tdd/skill.yaml +47 -0
  13. package/dist/agents/skills/claude-code/harness-pre-commit-review/SKILL.md +27 -2
  14. package/dist/agents/skills/claude-code/harness-release-readiness/SKILL.md +657 -0
  15. package/dist/agents/skills/claude-code/harness-release-readiness/skill.yaml +57 -0
  16. package/dist/agents/skills/claude-code/harness-security-review/SKILL.md +206 -0
  17. package/dist/agents/skills/claude-code/harness-security-review/skill.yaml +50 -0
  18. package/dist/agents/skills/claude-code/harness-security-scan/SKILL.md +102 -0
  19. package/dist/agents/skills/claude-code/harness-security-scan/skill.yaml +41 -0
  20. package/dist/agents/skills/claude-code/harness-state-management/SKILL.md +22 -8
  21. package/dist/agents/skills/gemini-cli/harness-autopilot/SKILL.md +494 -0
  22. package/dist/agents/skills/gemini-cli/harness-autopilot/skill.yaml +52 -0
  23. package/dist/agents/skills/gemini-cli/harness-perf/SKILL.md +231 -0
  24. package/dist/agents/skills/gemini-cli/harness-perf/skill.yaml +47 -0
  25. package/dist/agents/skills/gemini-cli/harness-perf-tdd/SKILL.md +236 -0
  26. package/dist/agents/skills/gemini-cli/harness-perf-tdd/skill.yaml +47 -0
  27. package/dist/agents/skills/gemini-cli/harness-release-readiness/SKILL.md +657 -0
  28. package/dist/agents/skills/gemini-cli/harness-release-readiness/skill.yaml +57 -0
  29. package/dist/agents/skills/gemini-cli/harness-security-review/skill.yaml +50 -0
  30. package/dist/agents/skills/gemini-cli/harness-security-scan/SKILL.md +102 -0
  31. package/dist/agents/skills/gemini-cli/harness-security-scan/skill.yaml +41 -0
  32. package/dist/bin/harness.js +1 -1
  33. package/dist/{chunk-VS4OTOKZ.js → chunk-O6NEKDYP.js} +789 -299
  34. package/dist/index.js +1 -1
  35. package/package.json +2 -2
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-ACMDUQJG.js";
9
9
 
10
10
  // src/index.ts
11
- import { Command as Command39 } from "commander";
11
+ import { Command as Command43 } from "commander";
12
12
  import { VERSION } from "@harness-engineering/core";
13
13
 
14
14
  // src/commands/validate.ts
@@ -400,10 +400,404 @@ function createCheckDepsCommand() {
400
400
  return command;
401
401
  }
402
402
 
403
- // src/commands/check-docs.ts
403
+ // src/commands/check-perf.ts
404
404
  import { Command as Command3 } from "commander";
405
405
  import * as path4 from "path";
406
- import { Ok as Ok4, Err as Err2 } from "@harness-engineering/core";
406
+ import { Ok as Ok4, EntropyAnalyzer } from "@harness-engineering/core";
407
+ async function runCheckPerf(cwd, options) {
408
+ const runAll = !options.structural && !options.size && !options.coupling;
409
+ const analyzer = new EntropyAnalyzer({
410
+ rootDir: path4.resolve(cwd),
411
+ analyze: {
412
+ complexity: runAll || !!options.structural,
413
+ coupling: runAll || !!options.coupling,
414
+ sizeBudget: runAll || !!options.size
415
+ }
416
+ });
417
+ const analysisResult = await analyzer.analyze();
418
+ if (!analysisResult.ok) {
419
+ return Ok4({
420
+ valid: false,
421
+ violations: [
422
+ {
423
+ tier: 1,
424
+ severity: "error",
425
+ metric: "analysis-error",
426
+ file: "",
427
+ value: 0,
428
+ threshold: 0,
429
+ message: `Analysis failed: ${analysisResult.error.message}`
430
+ }
431
+ ],
432
+ stats: { filesAnalyzed: 0, violationCount: 1, errorCount: 1, warningCount: 0, infoCount: 0 }
433
+ });
434
+ }
435
+ const report = analysisResult.value;
436
+ const violations = [];
437
+ if (report.complexity) {
438
+ for (const v of report.complexity.violations) {
439
+ violations.push({
440
+ tier: v.tier,
441
+ severity: v.severity,
442
+ metric: v.metric,
443
+ file: v.file,
444
+ value: v.value,
445
+ threshold: v.threshold,
446
+ message: v.message || `[Tier ${v.tier}] ${v.metric}: ${v.function} (${v.value} > ${v.threshold})`
447
+ });
448
+ }
449
+ }
450
+ if (report.coupling) {
451
+ for (const v of report.coupling.violations) {
452
+ violations.push({
453
+ tier: v.tier,
454
+ severity: v.severity,
455
+ metric: v.metric,
456
+ file: v.file,
457
+ value: v.value,
458
+ threshold: v.threshold,
459
+ message: v.message || `[Tier ${v.tier}] ${v.metric}: ${v.file} (${v.value} > ${v.threshold})`
460
+ });
461
+ }
462
+ }
463
+ if (report.sizeBudget) {
464
+ for (const v of report.sizeBudget.violations) {
465
+ violations.push({
466
+ tier: v.tier,
467
+ severity: v.severity,
468
+ metric: "sizeBudget",
469
+ file: v.package,
470
+ value: v.currentSize,
471
+ threshold: v.budgetSize,
472
+ message: `[Tier ${v.tier}] Size: ${v.package} (${v.currentSize}B > ${v.budgetSize}B)`
473
+ });
474
+ }
475
+ }
476
+ const hasErrors = violations.some((v) => v.severity === "error");
477
+ const errorCount = violations.filter((v) => v.severity === "error").length;
478
+ const warningCount = violations.filter((v) => v.severity === "warning").length;
479
+ const infoCount = violations.filter((v) => v.severity === "info").length;
480
+ return Ok4({
481
+ valid: !hasErrors,
482
+ violations,
483
+ stats: {
484
+ filesAnalyzed: report.complexity?.stats.filesAnalyzed ?? 0,
485
+ violationCount: violations.length,
486
+ errorCount,
487
+ warningCount,
488
+ infoCount
489
+ }
490
+ });
491
+ }
492
+ function createCheckPerfCommand() {
493
+ const command = new Command3("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) => {
494
+ const globalOpts = cmd.optsWithGlobals();
495
+ const mode = globalOpts.json ? OutputMode.JSON : globalOpts.quiet ? OutputMode.QUIET : globalOpts.verbose ? OutputMode.VERBOSE : OutputMode.TEXT;
496
+ const formatter = new OutputFormatter(mode);
497
+ const result = await runCheckPerf(process.cwd(), {
498
+ structural: opts.structural,
499
+ coupling: opts.coupling,
500
+ size: opts.size
501
+ });
502
+ if (!result.ok) {
503
+ if (mode === OutputMode.JSON) {
504
+ console.log(JSON.stringify({ error: result.error.message }));
505
+ } else {
506
+ logger.error(result.error.message);
507
+ }
508
+ process.exit(ExitCode.ERROR);
509
+ }
510
+ const issues = result.value.violations.map((v) => ({
511
+ file: v.file,
512
+ message: v.message
513
+ }));
514
+ const output = formatter.formatValidation({
515
+ valid: result.value.valid,
516
+ issues
517
+ });
518
+ if (output) {
519
+ console.log(output);
520
+ }
521
+ process.exit(result.value.valid ? ExitCode.SUCCESS : ExitCode.VALIDATION_FAILED);
522
+ });
523
+ return command;
524
+ }
525
+
526
+ // src/commands/check-security.ts
527
+ import { Command as Command4 } from "commander";
528
+ import * as path5 from "path";
529
+ import { execSync } from "child_process";
530
+ import { Ok as Ok5, SecurityScanner, parseSecurityConfig } from "@harness-engineering/core";
531
+ var SEVERITY_RANK = {
532
+ error: 3,
533
+ warning: 2,
534
+ info: 1
535
+ };
536
+ function getChangedFiles(cwd) {
537
+ try {
538
+ const output = execSync("git diff --name-only HEAD~1", {
539
+ cwd,
540
+ encoding: "utf-8"
541
+ });
542
+ return output.trim().split("\n").filter((f) => f.length > 0).map((f) => path5.resolve(cwd, f));
543
+ } catch {
544
+ return [];
545
+ }
546
+ }
547
+ async function runCheckSecurity(cwd, options) {
548
+ const projectRoot = path5.resolve(cwd);
549
+ let configData = {};
550
+ try {
551
+ const fs22 = await import("fs");
552
+ const configPath = path5.join(projectRoot, "harness.config.json");
553
+ if (fs22.existsSync(configPath)) {
554
+ const raw = fs22.readFileSync(configPath, "utf-8");
555
+ const parsed = JSON.parse(raw);
556
+ configData = parsed.security ?? {};
557
+ }
558
+ } catch {
559
+ }
560
+ const securityConfig = parseSecurityConfig(configData);
561
+ const scanner = new SecurityScanner(securityConfig);
562
+ scanner.configureForProject(projectRoot);
563
+ let filesToScan;
564
+ if (options.changedOnly) {
565
+ filesToScan = getChangedFiles(projectRoot);
566
+ } else {
567
+ const { glob: glob2 } = await import("glob");
568
+ const pattern = "**/*.{ts,tsx,js,jsx,go,py,java,rb}";
569
+ const ignore = securityConfig.exclude ?? [
570
+ "**/node_modules/**",
571
+ "**/dist/**",
572
+ "**/*.test.ts",
573
+ "**/fixtures/**"
574
+ ];
575
+ filesToScan = await glob2(pattern, { cwd: projectRoot, absolute: true, ignore });
576
+ }
577
+ const result = await scanner.scanFiles(filesToScan);
578
+ const threshold = options.severity ?? "warning";
579
+ const thresholdRank = SEVERITY_RANK[threshold];
580
+ const filtered = result.findings.filter((f) => SEVERITY_RANK[f.severity] >= thresholdRank);
581
+ const hasErrors = filtered.some((f) => f.severity === "error");
582
+ return Ok5({
583
+ valid: !hasErrors,
584
+ findings: filtered,
585
+ stats: {
586
+ filesScanned: result.scannedFiles,
587
+ rulesApplied: result.rulesApplied,
588
+ errorCount: filtered.filter((f) => f.severity === "error").length,
589
+ warningCount: filtered.filter((f) => f.severity === "warning").length,
590
+ infoCount: filtered.filter((f) => f.severity === "info").length
591
+ }
592
+ });
593
+ }
594
+ function createCheckSecurityCommand() {
595
+ const command = new Command4("check-security").description("Run lightweight security scan: secrets, injection, XSS, weak crypto").option("--severity <level>", "Minimum severity threshold", "warning").hook("preAction", (thisCommand) => {
596
+ const severity = thisCommand.opts().severity;
597
+ if (!["error", "warning", "info"].includes(severity)) {
598
+ logger.error(`Invalid severity: "${severity}". Must be one of: error, warning, info`);
599
+ process.exit(ExitCode.ERROR);
600
+ }
601
+ }).option("--changed-only", "Only scan git-changed files").action(async (opts, cmd) => {
602
+ const globalOpts = cmd.optsWithGlobals();
603
+ const mode = globalOpts.json ? OutputMode.JSON : globalOpts.quiet ? OutputMode.QUIET : globalOpts.verbose ? OutputMode.VERBOSE : OutputMode.TEXT;
604
+ const formatter = new OutputFormatter(mode);
605
+ const result = await runCheckSecurity(process.cwd(), {
606
+ severity: opts.severity,
607
+ changedOnly: opts.changedOnly
608
+ });
609
+ if (!result.ok) {
610
+ if (mode === OutputMode.JSON) {
611
+ console.log(JSON.stringify({ error: result.error.message }));
612
+ } else {
613
+ logger.error(result.error.message);
614
+ }
615
+ process.exit(ExitCode.ERROR);
616
+ }
617
+ const issues = result.value.findings.map((f) => ({
618
+ file: `${f.file}:${f.line}`,
619
+ message: `[${f.ruleId}] ${f.severity.toUpperCase()} ${f.message}`
620
+ }));
621
+ const output = formatter.formatValidation({
622
+ valid: result.value.valid,
623
+ issues
624
+ });
625
+ if (output) {
626
+ console.log(output);
627
+ }
628
+ process.exit(result.value.valid ? ExitCode.SUCCESS : ExitCode.VALIDATION_FAILED);
629
+ });
630
+ return command;
631
+ }
632
+
633
+ // src/commands/perf.ts
634
+ import { Command as Command5 } from "commander";
635
+ import * as path6 from "path";
636
+ import { BaselineManager, CriticalPathResolver } from "@harness-engineering/core";
637
+ function createPerfCommand() {
638
+ const perf = new Command5("perf").description("Performance benchmark and baseline management");
639
+ perf.command("bench [glob]").description("Run benchmarks via vitest bench").action(async (glob2, _opts, cmd) => {
640
+ const globalOpts = cmd.optsWithGlobals();
641
+ const cwd = process.cwd();
642
+ const { BenchmarkRunner } = await import("@harness-engineering/core");
643
+ const runner = new BenchmarkRunner();
644
+ const benchFiles = runner.discover(cwd, glob2);
645
+ if (benchFiles.length === 0) {
646
+ if (globalOpts.json) {
647
+ console.log(JSON.stringify({ benchFiles: [], message: "No .bench.ts files found" }));
648
+ } else {
649
+ logger.info("No .bench.ts files found. Create *.bench.ts files to add benchmarks.");
650
+ }
651
+ return;
652
+ }
653
+ if (globalOpts.json) {
654
+ logger.info(`Found ${benchFiles.length} benchmark file(s). Running...`);
655
+ } else {
656
+ logger.info(`Found ${benchFiles.length} benchmark file(s):`);
657
+ for (const f of benchFiles) {
658
+ logger.info(` ${f}`);
659
+ }
660
+ logger.info("Running benchmarks...");
661
+ }
662
+ const result = await runner.run(glob2 ? { cwd, glob: glob2 } : { cwd });
663
+ if (globalOpts.json) {
664
+ console.log(JSON.stringify({ results: result.results, success: result.success }));
665
+ } else {
666
+ if (result.success && result.results.length > 0) {
667
+ logger.info(`
668
+ Results (${result.results.length} benchmarks):`);
669
+ for (const r of result.results) {
670
+ logger.info(
671
+ ` ${r.file}::${r.name}: ${r.opsPerSec} ops/s (mean: ${r.meanMs.toFixed(2)}ms)`
672
+ );
673
+ }
674
+ logger.info("\nTo save as baselines: harness perf baselines update");
675
+ } else {
676
+ logger.info("Benchmark run completed. Check output above for details.");
677
+ if (result.rawOutput) {
678
+ console.log(result.rawOutput);
679
+ }
680
+ }
681
+ }
682
+ });
683
+ const baselines = perf.command("baselines").description("Manage performance baselines");
684
+ baselines.command("show").description("Display current baselines").action(async (_opts, cmd) => {
685
+ const globalOpts = cmd.optsWithGlobals();
686
+ const cwd = process.cwd();
687
+ const manager = new BaselineManager(cwd);
688
+ const data = manager.load();
689
+ if (!data) {
690
+ if (globalOpts.json) {
691
+ console.log(JSON.stringify({ baselines: null, message: "No baselines file found" }));
692
+ } else {
693
+ logger.info("No baselines file found at .harness/perf/baselines.json");
694
+ }
695
+ return;
696
+ }
697
+ if (globalOpts.json) {
698
+ console.log(JSON.stringify(data, null, 2));
699
+ } else {
700
+ logger.info(`Baselines (updated: ${data.updatedAt}, from: ${data.updatedFrom})`);
701
+ for (const [name, baseline] of Object.entries(data.benchmarks)) {
702
+ logger.info(
703
+ ` ${name}: ${baseline.opsPerSec} ops/s (mean: ${baseline.meanMs}ms, p99: ${baseline.p99Ms}ms)`
704
+ );
705
+ }
706
+ }
707
+ });
708
+ baselines.command("update").description("Update baselines from latest benchmark run").action(async (_opts, cmd) => {
709
+ const globalOpts = cmd.optsWithGlobals();
710
+ const cwd = process.cwd();
711
+ const { BenchmarkRunner } = await import("@harness-engineering/core");
712
+ const runner = new BenchmarkRunner();
713
+ const manager = new BaselineManager(cwd);
714
+ logger.info("Running benchmarks to update baselines...");
715
+ const benchResult = await runner.run({ cwd });
716
+ if (!benchResult.success || benchResult.results.length === 0) {
717
+ logger.error(
718
+ "No benchmark results to save. Run `harness perf bench` first to verify benchmarks work."
719
+ );
720
+ return;
721
+ }
722
+ let commitHash = "unknown";
723
+ try {
724
+ const { execSync: execSync5 } = await import("child_process");
725
+ commitHash = execSync5("git rev-parse --short HEAD", { cwd, encoding: "utf-8" }).trim();
726
+ } catch {
727
+ }
728
+ manager.save(benchResult.results, commitHash);
729
+ if (globalOpts.json) {
730
+ console.log(JSON.stringify({ updated: benchResult.results.length, commitHash }));
731
+ } else {
732
+ logger.info(`Updated ${benchResult.results.length} baseline(s) from commit ${commitHash}`);
733
+ logger.info("Baselines saved to .harness/perf/baselines.json");
734
+ }
735
+ });
736
+ perf.command("report").description("Full performance report with metrics, trends, and hotspots").action(async (_opts, cmd) => {
737
+ const globalOpts = cmd.optsWithGlobals();
738
+ const cwd = process.cwd();
739
+ const { EntropyAnalyzer: EntropyAnalyzer3 } = await import("@harness-engineering/core");
740
+ const analyzer = new EntropyAnalyzer3({
741
+ rootDir: path6.resolve(cwd),
742
+ analyze: { complexity: true, coupling: true }
743
+ });
744
+ const result = await analyzer.analyze();
745
+ if (!result.ok) {
746
+ logger.error(result.error.message);
747
+ return;
748
+ }
749
+ const report = result.value;
750
+ if (globalOpts.json) {
751
+ console.log(
752
+ JSON.stringify(
753
+ {
754
+ complexity: report.complexity,
755
+ coupling: report.coupling,
756
+ sizeBudget: report.sizeBudget
757
+ },
758
+ null,
759
+ 2
760
+ )
761
+ );
762
+ } else {
763
+ logger.info("=== Performance Report ===");
764
+ if (report.complexity) {
765
+ logger.info(
766
+ `Complexity: ${report.complexity.stats.violationCount} violations (${report.complexity.stats.errorCount} errors, ${report.complexity.stats.warningCount} warnings)`
767
+ );
768
+ }
769
+ if (report.coupling) {
770
+ logger.info(
771
+ `Coupling: ${report.coupling.stats.violationCount} violations (${report.coupling.stats.warningCount} warnings)`
772
+ );
773
+ }
774
+ }
775
+ });
776
+ perf.command("critical-paths").description("Show resolved critical path set (annotations + graph inference)").action(async (_opts, cmd) => {
777
+ const globalOpts = cmd.optsWithGlobals();
778
+ const cwd = process.cwd();
779
+ const resolver = new CriticalPathResolver(cwd);
780
+ const result = await resolver.resolve();
781
+ if (globalOpts.json) {
782
+ console.log(JSON.stringify(result, null, 2));
783
+ } else {
784
+ logger.info(
785
+ `Critical paths: ${result.stats.total} (${result.stats.annotated} annotated, ${result.stats.graphInferred} graph-inferred)`
786
+ );
787
+ for (const entry of result.entries) {
788
+ logger.info(
789
+ ` ${entry.file}::${entry.function} [${entry.source}]${entry.fanIn ? ` (fan-in: ${entry.fanIn})` : ""}`
790
+ );
791
+ }
792
+ }
793
+ });
794
+ return perf;
795
+ }
796
+
797
+ // src/commands/check-docs.ts
798
+ import { Command as Command6 } from "commander";
799
+ import * as path7 from "path";
800
+ import { Ok as Ok6, Err as Err2 } from "@harness-engineering/core";
407
801
  import { checkDocCoverage, validateKnowledgeMap as validateKnowledgeMap2 } from "@harness-engineering/core";
408
802
  async function runCheckDocs(options) {
409
803
  const cwd = options.cwd ?? process.cwd();
@@ -413,8 +807,8 @@ async function runCheckDocs(options) {
413
807
  return configResult;
414
808
  }
415
809
  const config = configResult.value;
416
- const docsDir = path4.resolve(cwd, config.docsDir);
417
- const sourceDir = path4.resolve(cwd, config.rootDir);
810
+ const docsDir = path7.resolve(cwd, config.docsDir);
811
+ const sourceDir = path7.resolve(cwd, config.rootDir);
418
812
  const coverageResult = await checkDocCoverage("project", {
419
813
  docsDir,
420
814
  sourceDir,
@@ -443,10 +837,10 @@ async function runCheckDocs(options) {
443
837
  undocumented: coverageResult.value.undocumented,
444
838
  brokenLinks
445
839
  };
446
- return Ok4(result);
840
+ return Ok6(result);
447
841
  }
448
842
  function createCheckDocsCommand() {
449
- const command = new Command3("check-docs").description("Check documentation coverage").option("--min-coverage <percent>", "Minimum coverage percentage", "80").action(async (opts, cmd) => {
843
+ const command = new Command6("check-docs").description("Check documentation coverage").option("--min-coverage <percent>", "Minimum coverage percentage", "80").action(async (opts, cmd) => {
450
844
  const globalOpts = cmd.optsWithGlobals();
451
845
  const mode = globalOpts.json ? OutputMode.JSON : globalOpts.quiet ? OutputMode.QUIET : globalOpts.verbose ? OutputMode.VERBOSE : OutputMode.TEXT;
452
846
  const formatter = new OutputFormatter(mode);
@@ -498,17 +892,17 @@ function createCheckDocsCommand() {
498
892
  }
499
893
 
500
894
  // src/commands/init.ts
501
- import { Command as Command5 } from "commander";
895
+ import { Command as Command8 } from "commander";
502
896
  import chalk3 from "chalk";
503
897
  import * as fs5 from "fs";
504
- import * as path8 from "path";
505
- import { Ok as Ok6, Err as Err4 } from "@harness-engineering/core";
898
+ import * as path11 from "path";
899
+ import { Ok as Ok8, Err as Err4 } from "@harness-engineering/core";
506
900
 
507
901
  // src/templates/engine.ts
508
902
  import * as fs2 from "fs";
509
- import * as path5 from "path";
903
+ import * as path8 from "path";
510
904
  import Handlebars from "handlebars";
511
- import { Ok as Ok5, Err as Err3 } from "@harness-engineering/core";
905
+ import { Ok as Ok7, Err as Err3 } from "@harness-engineering/core";
512
906
 
513
907
  // src/templates/schema.ts
514
908
  import { z as z2 } from "zod";
@@ -576,13 +970,13 @@ var TemplateEngine = class {
576
970
  const templates = [];
577
971
  for (const entry of entries) {
578
972
  if (!entry.isDirectory()) continue;
579
- const metaPath = path5.join(this.templatesDir, entry.name, "template.json");
973
+ const metaPath = path8.join(this.templatesDir, entry.name, "template.json");
580
974
  if (!fs2.existsSync(metaPath)) continue;
581
975
  const raw = JSON.parse(fs2.readFileSync(metaPath, "utf-8"));
582
976
  const parsed = TemplateMetadataSchema.safeParse(raw);
583
977
  if (parsed.success) templates.push(parsed.data);
584
978
  }
585
- return Ok5(templates);
979
+ return Ok7(templates);
586
980
  } catch (error) {
587
981
  return Err3(
588
982
  new Error(
@@ -594,7 +988,7 @@ var TemplateEngine = class {
594
988
  resolveTemplate(level, framework) {
595
989
  const levelDir = this.findTemplateDir(level, "level");
596
990
  if (!levelDir) return Err3(new Error(`Template not found for level: ${level}`));
597
- const metaPath = path5.join(levelDir, "template.json");
991
+ const metaPath = path8.join(levelDir, "template.json");
598
992
  const metaRaw = JSON.parse(fs2.readFileSync(metaPath, "utf-8"));
599
993
  const metaResult = TemplateMetadataSchema.safeParse(metaRaw);
600
994
  if (!metaResult.success)
@@ -602,7 +996,7 @@ var TemplateEngine = class {
602
996
  const metadata = metaResult.data;
603
997
  let files = [];
604
998
  if (metadata.extends) {
605
- const baseDir = path5.join(this.templatesDir, metadata.extends);
999
+ const baseDir = path8.join(this.templatesDir, metadata.extends);
606
1000
  if (fs2.existsSync(baseDir)) files = this.collectFiles(baseDir, metadata.extends);
607
1001
  }
608
1002
  const levelFiles = this.collectFiles(levelDir, level);
@@ -611,7 +1005,7 @@ var TemplateEngine = class {
611
1005
  if (framework) {
612
1006
  const frameworkDir = this.findTemplateDir(framework, "framework");
613
1007
  if (!frameworkDir) return Err3(new Error(`Framework template not found: ${framework}`));
614
- const fMetaPath = path5.join(frameworkDir, "template.json");
1008
+ const fMetaPath = path8.join(frameworkDir, "template.json");
615
1009
  const fMetaRaw = JSON.parse(fs2.readFileSync(fMetaPath, "utf-8"));
616
1010
  const fMetaResult = TemplateMetadataSchema.safeParse(fMetaRaw);
617
1011
  if (fMetaResult.success) overlayMetadata = fMetaResult.data;
@@ -621,7 +1015,7 @@ var TemplateEngine = class {
621
1015
  files = files.filter((f) => f.relativePath !== "template.json");
622
1016
  const resolved = { metadata, files };
623
1017
  if (overlayMetadata !== void 0) resolved.overlayMetadata = overlayMetadata;
624
- return Ok5(resolved);
1018
+ return Ok7(resolved);
625
1019
  }
626
1020
  render(template, context) {
627
1021
  const rendered = [];
@@ -673,20 +1067,20 @@ var TemplateEngine = class {
673
1067
  const msg = error instanceof Error ? error.message : String(error);
674
1068
  return Err3(new Error(`JSON merge failed: ${msg}`));
675
1069
  }
676
- return Ok5({ files: rendered });
1070
+ return Ok7({ files: rendered });
677
1071
  }
678
1072
  write(files, targetDir, options) {
679
1073
  try {
680
1074
  const written = [];
681
1075
  for (const file of files.files) {
682
- const targetPath = path5.join(targetDir, file.relativePath);
683
- const dir = path5.dirname(targetPath);
1076
+ const targetPath = path8.join(targetDir, file.relativePath);
1077
+ const dir = path8.dirname(targetPath);
684
1078
  if (!options.overwrite && fs2.existsSync(targetPath)) continue;
685
1079
  fs2.mkdirSync(dir, { recursive: true });
686
1080
  fs2.writeFileSync(targetPath, file.content);
687
1081
  written.push(file.relativePath);
688
1082
  }
689
- return Ok5(written);
1083
+ return Ok7(written);
690
1084
  } catch (error) {
691
1085
  return Err3(
692
1086
  new Error(
@@ -699,16 +1093,16 @@ var TemplateEngine = class {
699
1093
  const entries = fs2.readdirSync(this.templatesDir, { withFileTypes: true });
700
1094
  for (const entry of entries) {
701
1095
  if (!entry.isDirectory()) continue;
702
- const metaPath = path5.join(this.templatesDir, entry.name, "template.json");
1096
+ const metaPath = path8.join(this.templatesDir, entry.name, "template.json");
703
1097
  if (!fs2.existsSync(metaPath)) continue;
704
1098
  const raw = JSON.parse(fs2.readFileSync(metaPath, "utf-8"));
705
1099
  const parsed = TemplateMetadataSchema.safeParse(raw);
706
1100
  if (!parsed.success) continue;
707
1101
  if (type === "level" && parsed.data.level === name)
708
- return path5.join(this.templatesDir, entry.name);
1102
+ return path8.join(this.templatesDir, entry.name);
709
1103
  if (type === "framework" && parsed.data.framework === name)
710
- return path5.join(this.templatesDir, entry.name);
711
- if (parsed.data.name === name) return path5.join(this.templatesDir, entry.name);
1104
+ return path8.join(this.templatesDir, entry.name);
1105
+ if (parsed.data.name === name) return path8.join(this.templatesDir, entry.name);
712
1106
  }
713
1107
  return null;
714
1108
  }
@@ -717,12 +1111,12 @@ var TemplateEngine = class {
717
1111
  const walk = (currentDir) => {
718
1112
  const entries = fs2.readdirSync(currentDir, { withFileTypes: true });
719
1113
  for (const entry of entries) {
720
- const fullPath = path5.join(currentDir, entry.name);
1114
+ const fullPath = path8.join(currentDir, entry.name);
721
1115
  if (entry.isDirectory()) {
722
1116
  walk(fullPath);
723
1117
  } else {
724
1118
  files.push({
725
- relativePath: path5.relative(dir, fullPath),
1119
+ relativePath: path8.relative(dir, fullPath),
726
1120
  absolutePath: fullPath,
727
1121
  isHandlebars: entry.name.endsWith(".hbs"),
728
1122
  sourceTemplate: sourceName
@@ -754,51 +1148,51 @@ var TemplateEngine = class {
754
1148
 
755
1149
  // src/utils/paths.ts
756
1150
  import * as fs3 from "fs";
757
- import * as path6 from "path";
1151
+ import * as path9 from "path";
758
1152
  import { fileURLToPath } from "url";
759
1153
  var __filename = fileURLToPath(import.meta.url);
760
- var __dirname = path6.dirname(__filename);
1154
+ var __dirname = path9.dirname(__filename);
761
1155
  function findUpDir(targetName, marker, maxLevels = 8) {
762
1156
  let dir = __dirname;
763
1157
  for (let i = 0; i < maxLevels; i++) {
764
- const candidate = path6.join(dir, targetName);
1158
+ const candidate = path9.join(dir, targetName);
765
1159
  if (fs3.existsSync(candidate) && fs3.statSync(candidate).isDirectory()) {
766
- if (fs3.existsSync(path6.join(candidate, marker))) {
1160
+ if (fs3.existsSync(path9.join(candidate, marker))) {
767
1161
  return candidate;
768
1162
  }
769
1163
  }
770
- dir = path6.dirname(dir);
1164
+ dir = path9.dirname(dir);
771
1165
  }
772
1166
  return null;
773
1167
  }
774
1168
  function resolveTemplatesDir() {
775
- return findUpDir("templates", "base") ?? path6.join(__dirname, "templates");
1169
+ return findUpDir("templates", "base") ?? path9.join(__dirname, "templates");
776
1170
  }
777
1171
  function resolvePersonasDir() {
778
1172
  const agentsDir = findUpDir("agents", "personas");
779
1173
  if (agentsDir) {
780
- return path6.join(agentsDir, "personas");
1174
+ return path9.join(agentsDir, "personas");
781
1175
  }
782
- return path6.join(__dirname, "agents", "personas");
1176
+ return path9.join(__dirname, "agents", "personas");
783
1177
  }
784
1178
  function resolveSkillsDir() {
785
1179
  const agentsDir = findUpDir("agents", "skills");
786
1180
  if (agentsDir) {
787
- return path6.join(agentsDir, "skills", "claude-code");
1181
+ return path9.join(agentsDir, "skills", "claude-code");
788
1182
  }
789
- return path6.join(__dirname, "agents", "skills", "claude-code");
1183
+ return path9.join(__dirname, "agents", "skills", "claude-code");
790
1184
  }
791
1185
  function resolveProjectSkillsDir(cwd) {
792
1186
  let dir = cwd ?? process.cwd();
793
1187
  for (let i = 0; i < 8; i++) {
794
- const candidate = path6.join(dir, "agents", "skills", "claude-code");
1188
+ const candidate = path9.join(dir, "agents", "skills", "claude-code");
795
1189
  if (fs3.existsSync(candidate) && fs3.statSync(candidate).isDirectory()) {
796
- const agentsDir = path6.join(dir, "agents");
797
- if (fs3.existsSync(path6.join(agentsDir, "skills"))) {
1190
+ const agentsDir = path9.join(dir, "agents");
1191
+ if (fs3.existsSync(path9.join(agentsDir, "skills"))) {
798
1192
  return candidate;
799
1193
  }
800
1194
  }
801
- const parent = path6.dirname(dir);
1195
+ const parent = path9.dirname(dir);
802
1196
  if (parent === dir) break;
803
1197
  dir = parent;
804
1198
  }
@@ -807,15 +1201,15 @@ function resolveProjectSkillsDir(cwd) {
807
1201
  function resolveGlobalSkillsDir() {
808
1202
  const agentsDir = findUpDir("agents", "skills");
809
1203
  if (agentsDir) {
810
- return path6.join(agentsDir, "skills", "claude-code");
1204
+ return path9.join(agentsDir, "skills", "claude-code");
811
1205
  }
812
- return path6.join(__dirname, "agents", "skills", "claude-code");
1206
+ return path9.join(__dirname, "agents", "skills", "claude-code");
813
1207
  }
814
1208
 
815
1209
  // src/commands/setup-mcp.ts
816
- import { Command as Command4 } from "commander";
1210
+ import { Command as Command7 } from "commander";
817
1211
  import * as fs4 from "fs";
818
- import * as path7 from "path";
1212
+ import * as path10 from "path";
819
1213
  import * as os from "os";
820
1214
  import chalk2 from "chalk";
821
1215
  var HARNESS_MCP_ENTRY = {
@@ -832,7 +1226,7 @@ function readJsonFile(filePath) {
832
1226
  }
833
1227
  }
834
1228
  function writeJsonFile(filePath, data) {
835
- const dir = path7.dirname(filePath);
1229
+ const dir = path10.dirname(filePath);
836
1230
  if (!fs4.existsSync(dir)) {
837
1231
  fs4.mkdirSync(dir, { recursive: true });
838
1232
  }
@@ -851,7 +1245,7 @@ function configureMcpServer(configPath) {
851
1245
  return true;
852
1246
  }
853
1247
  function addGeminiTrustedFolder(cwd) {
854
- const trustedPath = path7.join(os.homedir(), ".gemini", "trustedFolders.json");
1248
+ const trustedPath = path10.join(os.homedir(), ".gemini", "trustedFolders.json");
855
1249
  const folders = readJsonFile(trustedPath) ?? {};
856
1250
  if (folders[cwd] === "TRUST_FOLDER") {
857
1251
  return false;
@@ -865,7 +1259,7 @@ function setupMcp(cwd, client) {
865
1259
  const skipped = [];
866
1260
  let trustedFolder = false;
867
1261
  if (client === "all" || client === "claude") {
868
- const configPath = path7.join(cwd, ".mcp.json");
1262
+ const configPath = path10.join(cwd, ".mcp.json");
869
1263
  if (configureMcpServer(configPath)) {
870
1264
  configured.push("Claude Code");
871
1265
  } else {
@@ -873,7 +1267,7 @@ function setupMcp(cwd, client) {
873
1267
  }
874
1268
  }
875
1269
  if (client === "all" || client === "gemini") {
876
- const configPath = path7.join(cwd, ".gemini", "settings.json");
1270
+ const configPath = path10.join(cwd, ".gemini", "settings.json");
877
1271
  if (configureMcpServer(configPath)) {
878
1272
  configured.push("Gemini CLI");
879
1273
  } else {
@@ -884,7 +1278,7 @@ function setupMcp(cwd, client) {
884
1278
  return { configured, skipped, trustedFolder };
885
1279
  }
886
1280
  function createSetupMcpCommand() {
887
- return new Command4("setup-mcp").description("Configure MCP server for AI agent integration").option("--client <client>", "Client to configure (claude, gemini, all)", "all").action(async (opts, cmd) => {
1281
+ return new Command7("setup-mcp").description("Configure MCP server for AI agent integration").option("--client <client>", "Client to configure (claude, gemini, all)", "all").action(async (opts, cmd) => {
888
1282
  const globalOpts = cmd.optsWithGlobals();
889
1283
  const cwd = process.cwd();
890
1284
  const { configured, skipped, trustedFolder } = setupMcp(cwd, opts.client);
@@ -910,8 +1304,12 @@ function createSetupMcpCommand() {
910
1304
  }
911
1305
  console.log("");
912
1306
  console.log(chalk2.bold("The harness MCP server provides:"));
913
- console.log(" - 15 tools for validation, entropy detection, and skill execution");
914
- console.log(" - 4 resources for project context, skills, rules, and learnings");
1307
+ console.log(
1308
+ " - 31 tools for validation, entropy detection, skill execution, graph querying, and more"
1309
+ );
1310
+ console.log(
1311
+ " - 8 resources for project context, skills, rules, learnings, state, and graph data"
1312
+ );
915
1313
  console.log("");
916
1314
  console.log(`Run ${chalk2.cyan("harness skill list")} to see available skills.`);
917
1315
  console.log("");
@@ -923,10 +1321,10 @@ function createSetupMcpCommand() {
923
1321
  // src/commands/init.ts
924
1322
  async function runInit(options) {
925
1323
  const cwd = options.cwd ?? process.cwd();
926
- const name = options.name ?? path8.basename(cwd);
1324
+ const name = options.name ?? path11.basename(cwd);
927
1325
  const level = options.level ?? "basic";
928
1326
  const force = options.force ?? false;
929
- const configPath = path8.join(cwd, "harness.config.json");
1327
+ const configPath = path11.join(cwd, "harness.config.json");
930
1328
  if (!force && fs5.existsSync(configPath)) {
931
1329
  return Err4(
932
1330
  new CLIError("Project already initialized. Use --force to overwrite.", ExitCode.ERROR)
@@ -950,10 +1348,10 @@ async function runInit(options) {
950
1348
  if (!writeResult.ok) {
951
1349
  return Err4(new CLIError(writeResult.error.message, ExitCode.ERROR));
952
1350
  }
953
- return Ok6({ filesCreated: writeResult.value });
1351
+ return Ok8({ filesCreated: writeResult.value });
954
1352
  }
955
1353
  function createInitCommand() {
956
- const command = new Command5("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) => {
1354
+ 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) => {
957
1355
  const globalOpts = cmd.optsWithGlobals();
958
1356
  const result = await runInit({
959
1357
  name: opts.name,
@@ -995,9 +1393,9 @@ function createInitCommand() {
995
1393
  }
996
1394
 
997
1395
  // src/commands/cleanup.ts
998
- import { Command as Command6 } from "commander";
999
- import * as path9 from "path";
1000
- import { Ok as Ok7, Err as Err5, EntropyAnalyzer } from "@harness-engineering/core";
1396
+ import { Command as Command9 } from "commander";
1397
+ import * as path12 from "path";
1398
+ import { Ok as Ok9, Err as Err5, EntropyAnalyzer as EntropyAnalyzer2 } from "@harness-engineering/core";
1001
1399
  async function runCleanup(options) {
1002
1400
  const cwd = options.cwd ?? process.cwd();
1003
1401
  const type = options.type ?? "all";
@@ -1012,11 +1410,11 @@ async function runCleanup(options) {
1012
1410
  patternViolations: [],
1013
1411
  totalIssues: 0
1014
1412
  };
1015
- const rootDir = path9.resolve(cwd, config.rootDir);
1016
- const docsDir = path9.resolve(cwd, config.docsDir);
1413
+ const rootDir = path12.resolve(cwd, config.rootDir);
1414
+ const docsDir = path12.resolve(cwd, config.docsDir);
1017
1415
  const entropyConfig = {
1018
1416
  rootDir,
1019
- entryPoints: [path9.join(rootDir, "src/index.ts")],
1417
+ entryPoints: [path12.join(rootDir, "src/index.ts")],
1020
1418
  docPaths: [docsDir],
1021
1419
  analyze: {
1022
1420
  drift: type === "all" || type === "drift",
@@ -1025,7 +1423,7 @@ async function runCleanup(options) {
1025
1423
  },
1026
1424
  exclude: config.entropy?.excludePatterns ?? ["**/node_modules/**", "**/*.test.ts"]
1027
1425
  };
1028
- const analyzer = new EntropyAnalyzer(entropyConfig);
1426
+ const analyzer = new EntropyAnalyzer2(entropyConfig);
1029
1427
  const analysisResult = await analyzer.analyze();
1030
1428
  if (!analysisResult.ok) {
1031
1429
  return Err5(
@@ -1053,10 +1451,10 @@ async function runCleanup(options) {
1053
1451
  }));
1054
1452
  }
1055
1453
  result.totalIssues = result.driftIssues.length + result.deadCode.length + result.patternViolations.length;
1056
- return Ok7(result);
1454
+ return Ok9(result);
1057
1455
  }
1058
1456
  function createCleanupCommand() {
1059
- const command = new Command6("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) => {
1457
+ 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) => {
1060
1458
  const globalOpts = cmd.optsWithGlobals();
1061
1459
  const mode = globalOpts.json ? OutputMode.JSON : globalOpts.quiet ? OutputMode.QUIET : globalOpts.verbose ? OutputMode.VERBOSE : OutputMode.TEXT;
1062
1460
  const formatter = new OutputFormatter(mode);
@@ -1116,10 +1514,10 @@ function createCleanupCommand() {
1116
1514
  }
1117
1515
 
1118
1516
  // src/commands/fix-drift.ts
1119
- import { Command as Command7 } from "commander";
1120
- import * as path10 from "path";
1517
+ import { Command as Command10 } from "commander";
1518
+ import * as path13 from "path";
1121
1519
  import {
1122
- Ok as Ok8,
1520
+ Ok as Ok10,
1123
1521
  Err as Err6,
1124
1522
  buildSnapshot,
1125
1523
  detectDocDrift,
@@ -1136,11 +1534,11 @@ async function runFixDrift(options) {
1136
1534
  return Err6(configResult.error);
1137
1535
  }
1138
1536
  const config = configResult.value;
1139
- const rootDir = path10.resolve(cwd, config.rootDir);
1140
- const docsDir = path10.resolve(cwd, config.docsDir);
1537
+ const rootDir = path13.resolve(cwd, config.rootDir);
1538
+ const docsDir = path13.resolve(cwd, config.docsDir);
1141
1539
  const entropyConfig = {
1142
1540
  rootDir,
1143
- entryPoints: [path10.join(rootDir, "src/index.ts")],
1541
+ entryPoints: [path13.join(rootDir, "src/index.ts")],
1144
1542
  docPaths: [docsDir],
1145
1543
  analyze: {
1146
1544
  drift: true,
@@ -1224,10 +1622,10 @@ async function runFixDrift(options) {
1224
1622
  fixes: appliedFixes,
1225
1623
  suggestions
1226
1624
  };
1227
- return Ok8(result);
1625
+ return Ok10(result);
1228
1626
  }
1229
1627
  function createFixDriftCommand() {
1230
- const command = new Command7("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) => {
1628
+ 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) => {
1231
1629
  const globalOpts = cmd.optsWithGlobals();
1232
1630
  const mode = globalOpts.json ? OutputMode.JSON : globalOpts.quiet ? OutputMode.QUIET : globalOpts.verbose ? OutputMode.VERBOSE : OutputMode.TEXT;
1233
1631
  const formatter = new OutputFormatter(mode);
@@ -1287,20 +1685,20 @@ function createFixDriftCommand() {
1287
1685
  }
1288
1686
 
1289
1687
  // src/commands/agent/index.ts
1290
- import { Command as Command10 } from "commander";
1688
+ import { Command as Command13 } from "commander";
1291
1689
 
1292
1690
  // src/commands/agent/run.ts
1293
- import { Command as Command8 } from "commander";
1294
- import * as path14 from "path";
1691
+ import { Command as Command11 } from "commander";
1692
+ import * as path17 from "path";
1295
1693
  import * as childProcess from "child_process";
1296
- import { Ok as Ok10, Err as Err8 } from "@harness-engineering/core";
1694
+ import { Ok as Ok12, Err as Err8 } from "@harness-engineering/core";
1297
1695
  import { requestPeerReview } from "@harness-engineering/core";
1298
1696
 
1299
1697
  // src/persona/loader.ts
1300
1698
  import * as fs6 from "fs";
1301
- import * as path11 from "path";
1699
+ import * as path14 from "path";
1302
1700
  import YAML from "yaml";
1303
- import { Ok as Ok9, Err as Err7 } from "@harness-engineering/core";
1701
+ import { Ok as Ok11, Err as Err7 } from "@harness-engineering/core";
1304
1702
 
1305
1703
  // src/persona/schema.ts
1306
1704
  import { z as z3 } from "zod";
@@ -1394,7 +1792,7 @@ function loadPersona(filePath) {
1394
1792
  if (!result.success) {
1395
1793
  return Err7(new Error(`Invalid persona ${filePath}: ${result.error.message}`));
1396
1794
  }
1397
- return Ok9(normalizePersona(result.data));
1795
+ return Ok11(normalizePersona(result.data));
1398
1796
  } catch (error) {
1399
1797
  return Err7(
1400
1798
  new Error(`Failed to load persona: ${error instanceof Error ? error.message : String(error)}`)
@@ -1403,17 +1801,17 @@ function loadPersona(filePath) {
1403
1801
  }
1404
1802
  function listPersonas(dir) {
1405
1803
  try {
1406
- if (!fs6.existsSync(dir)) return Ok9([]);
1804
+ if (!fs6.existsSync(dir)) return Ok11([]);
1407
1805
  const entries = fs6.readdirSync(dir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
1408
1806
  const personas = [];
1409
1807
  for (const entry of entries) {
1410
- const filePath = path11.join(dir, entry);
1808
+ const filePath = path14.join(dir, entry);
1411
1809
  const result = loadPersona(filePath);
1412
1810
  if (result.ok) {
1413
1811
  personas.push({ name: result.value.name, description: result.value.description, filePath });
1414
1812
  }
1415
1813
  }
1416
- return Ok9(personas);
1814
+ return Ok11(personas);
1417
1815
  } catch (error) {
1418
1816
  return Err7(
1419
1817
  new Error(
@@ -1425,9 +1823,9 @@ function listPersonas(dir) {
1425
1823
 
1426
1824
  // src/persona/trigger-detector.ts
1427
1825
  import * as fs7 from "fs";
1428
- import * as path12 from "path";
1826
+ import * as path15 from "path";
1429
1827
  function detectTrigger(projectPath) {
1430
- const handoffPath = path12.join(projectPath, ".harness", "handoff.json");
1828
+ const handoffPath = path15.join(projectPath, ".harness", "handoff.json");
1431
1829
  if (!fs7.existsSync(handoffPath)) {
1432
1830
  return { trigger: "manual" };
1433
1831
  }
@@ -1506,8 +1904,8 @@ async function runPersona(persona, context) {
1506
1904
  const result = await Promise.race([
1507
1905
  context.commandExecutor(step.command),
1508
1906
  new Promise(
1509
- (resolve18) => setTimeout(
1510
- () => resolve18({
1907
+ (resolve22) => setTimeout(
1908
+ () => resolve22({
1511
1909
  ok: false,
1512
1910
  error: new Error(TIMEOUT_ERROR_MESSAGE)
1513
1911
  }),
@@ -1562,7 +1960,7 @@ async function runPersona(persona, context) {
1562
1960
  const result = await Promise.race([
1563
1961
  context.skillExecutor(step.skill, skillContext),
1564
1962
  new Promise(
1565
- (resolve18) => setTimeout(() => resolve18(SKILL_TIMEOUT_RESULT), remainingTime)
1963
+ (resolve22) => setTimeout(() => resolve22(SKILL_TIMEOUT_RESULT), remainingTime)
1566
1964
  )
1567
1965
  ]);
1568
1966
  const durationMs = Date.now() - stepStart;
@@ -1606,7 +2004,7 @@ async function runPersona(persona, context) {
1606
2004
 
1607
2005
  // src/persona/skill-executor.ts
1608
2006
  import * as fs8 from "fs";
1609
- import * as path13 from "path";
2007
+ import * as path16 from "path";
1610
2008
  import { parse as parse2 } from "yaml";
1611
2009
  function resolveOutputMode(mode, trigger) {
1612
2010
  if (mode !== "auto") return mode;
@@ -1615,7 +2013,7 @@ function resolveOutputMode(mode, trigger) {
1615
2013
  function buildArtifactPath(projectPath, headSha) {
1616
2014
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
1617
2015
  const sha = headSha?.slice(0, 7) ?? "unknown";
1618
- return path13.join(projectPath, ".harness", "reviews", `${date}-${sha}.md`);
2016
+ return path16.join(projectPath, ".harness", "reviews", `${date}-${sha}.md`);
1619
2017
  }
1620
2018
  function buildArtifactContent(skillName, trigger, headSha) {
1621
2019
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -1661,7 +2059,7 @@ function buildArtifactContent(skillName, trigger, headSha) {
1661
2059
  async function executeSkill(skillName, context) {
1662
2060
  const startTime = Date.now();
1663
2061
  const skillsDir = resolveSkillsDir();
1664
- const skillDir = path13.join(skillsDir, skillName);
2062
+ const skillDir = path16.join(skillsDir, skillName);
1665
2063
  if (!fs8.existsSync(skillDir)) {
1666
2064
  return {
1667
2065
  status: "fail",
@@ -1669,7 +2067,7 @@ async function executeSkill(skillName, context) {
1669
2067
  durationMs: Date.now() - startTime
1670
2068
  };
1671
2069
  }
1672
- const yamlPath = path13.join(skillDir, "skill.yaml");
2070
+ const yamlPath = path16.join(skillDir, "skill.yaml");
1673
2071
  if (!fs8.existsSync(yamlPath)) {
1674
2072
  return {
1675
2073
  status: "fail",
@@ -1687,7 +2085,7 @@ async function executeSkill(skillName, context) {
1687
2085
  durationMs: Date.now() - startTime
1688
2086
  };
1689
2087
  }
1690
- const skillMdPath = path13.join(skillDir, "SKILL.md");
2088
+ const skillMdPath = path16.join(skillDir, "SKILL.md");
1691
2089
  if (!fs8.existsSync(skillMdPath)) {
1692
2090
  return {
1693
2091
  status: "fail",
@@ -1707,7 +2105,7 @@ Trigger: ${context.trigger}
1707
2105
  if (resolvedMode === "artifact") {
1708
2106
  artifactPath = buildArtifactPath(context.projectPath, context.headSha);
1709
2107
  const artifactContent = buildArtifactContent(skillName, context.trigger, context.headSha);
1710
- const dir = path13.dirname(artifactPath);
2108
+ const dir = path16.dirname(artifactPath);
1711
2109
  fs8.mkdirSync(dir, { recursive: true });
1712
2110
  fs8.writeFileSync(artifactPath, artifactContent, "utf-8");
1713
2111
  }
@@ -1724,6 +2122,8 @@ var ALLOWED_PERSONA_COMMANDS = /* @__PURE__ */ new Set([
1724
2122
  "validate",
1725
2123
  "check-deps",
1726
2124
  "check-docs",
2125
+ "check-perf",
2126
+ "check-security",
1727
2127
  "cleanup",
1728
2128
  "fix-drift",
1729
2129
  "add"
@@ -1765,7 +2165,7 @@ async function runAgentTask(task, options) {
1765
2165
  return Err8(new CLIError(`Agent task failed: ${reviewResult.error.message}`, ExitCode.ERROR));
1766
2166
  }
1767
2167
  const review = reviewResult.value;
1768
- return Ok10({
2168
+ return Ok12({
1769
2169
  success: review.approved,
1770
2170
  output: review.approved ? `Agent task '${task}' completed successfully` : `Agent task '${task}' found issues:
1771
2171
  ${review.comments.map((c) => ` - ${c.message}`).join("\n")}`
@@ -1782,11 +2182,11 @@ var VALID_TRIGGERS = /* @__PURE__ */ new Set([
1782
2182
  "auto"
1783
2183
  ]);
1784
2184
  function createRunCommand() {
1785
- return new Command8("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) => {
2185
+ 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) => {
1786
2186
  const globalOpts = cmd.optsWithGlobals();
1787
2187
  if (opts.persona) {
1788
2188
  const personasDir = resolvePersonasDir();
1789
- const filePath = path14.join(personasDir, `${opts.persona}.yaml`);
2189
+ const filePath = path17.join(personasDir, `${opts.persona}.yaml`);
1790
2190
  const personaResult = loadPersona(filePath);
1791
2191
  if (!personaResult.ok) {
1792
2192
  logger.error(personaResult.error.message);
@@ -1801,7 +2201,7 @@ function createRunCommand() {
1801
2201
  }
1802
2202
  try {
1803
2203
  childProcess.execFileSync("npx", ["harness", command], { stdio: "inherit" });
1804
- return Ok10(null);
2204
+ return Ok12(null);
1805
2205
  } catch (error) {
1806
2206
  return Err8(new Error(error instanceof Error ? error.message : String(error)));
1807
2207
  }
@@ -1843,9 +2243,9 @@ function createRunCommand() {
1843
2243
  }
1844
2244
 
1845
2245
  // src/commands/agent/review.ts
1846
- import { Command as Command9 } from "commander";
1847
- import { execSync } from "child_process";
1848
- import { Ok as Ok11, Err as Err9 } from "@harness-engineering/core";
2246
+ import { Command as Command12 } from "commander";
2247
+ import { execSync as execSync2 } from "child_process";
2248
+ import { Ok as Ok13, Err as Err9 } from "@harness-engineering/core";
1849
2249
  import { createSelfReview, parseDiff } from "@harness-engineering/core";
1850
2250
  async function runAgentReview(options) {
1851
2251
  const configResult = resolveConfig(options.configPath);
@@ -1855,15 +2255,15 @@ async function runAgentReview(options) {
1855
2255
  const config = configResult.value;
1856
2256
  let diff;
1857
2257
  try {
1858
- diff = execSync("git diff --cached", { encoding: "utf-8" });
2258
+ diff = execSync2("git diff --cached", { encoding: "utf-8" });
1859
2259
  if (!diff) {
1860
- diff = execSync("git diff", { encoding: "utf-8" });
2260
+ diff = execSync2("git diff", { encoding: "utf-8" });
1861
2261
  }
1862
2262
  } catch {
1863
2263
  return Err9(new CLIError("Failed to get git diff", ExitCode.ERROR));
1864
2264
  }
1865
2265
  if (!diff) {
1866
- return Ok11({
2266
+ return Ok13({
1867
2267
  passed: true,
1868
2268
  checklist: [{ check: "No changes to review", passed: true }]
1869
2269
  });
@@ -1883,7 +2283,7 @@ async function runAgentReview(options) {
1883
2283
  if (!review.ok) {
1884
2284
  return Err9(new CLIError(review.error.message, ExitCode.ERROR));
1885
2285
  }
1886
- return Ok11({
2286
+ return Ok13({
1887
2287
  passed: review.value.passed,
1888
2288
  checklist: review.value.items.map((item) => ({
1889
2289
  check: item.check,
@@ -1893,7 +2293,7 @@ async function runAgentReview(options) {
1893
2293
  });
1894
2294
  }
1895
2295
  function createReviewCommand() {
1896
- return new Command9("review").description("Run self-review on current changes").action(async (_opts, cmd) => {
2296
+ return new Command12("review").description("Run self-review on current changes").action(async (_opts, cmd) => {
1897
2297
  const globalOpts = cmd.optsWithGlobals();
1898
2298
  const mode = globalOpts.json ? OutputMode.JSON : globalOpts.quiet ? OutputMode.QUIET : OutputMode.TEXT;
1899
2299
  const result = await runAgentReview({
@@ -1929,17 +2329,17 @@ function createReviewCommand() {
1929
2329
 
1930
2330
  // src/commands/agent/index.ts
1931
2331
  function createAgentCommand() {
1932
- const command = new Command10("agent").description("Agent orchestration commands");
2332
+ const command = new Command13("agent").description("Agent orchestration commands");
1933
2333
  command.addCommand(createRunCommand());
1934
2334
  command.addCommand(createReviewCommand());
1935
2335
  return command;
1936
2336
  }
1937
2337
 
1938
2338
  // src/commands/add.ts
1939
- import { Command as Command11 } from "commander";
2339
+ import { Command as Command14 } from "commander";
1940
2340
  import * as fs9 from "fs";
1941
- import * as path15 from "path";
1942
- import { Ok as Ok12, Err as Err10 } from "@harness-engineering/core";
2341
+ import * as path18 from "path";
2342
+ import { Ok as Ok14, Err as Err10 } from "@harness-engineering/core";
1943
2343
  var LAYER_INDEX_TEMPLATE = (name) => `// ${name} layer
1944
2344
  // Add your ${name} exports here
1945
2345
 
@@ -1983,12 +2383,12 @@ async function runAdd(componentType, name, options) {
1983
2383
  try {
1984
2384
  switch (componentType) {
1985
2385
  case "layer": {
1986
- const layerDir = path15.join(cwd, "src", name);
2386
+ const layerDir = path18.join(cwd, "src", name);
1987
2387
  if (!fs9.existsSync(layerDir)) {
1988
2388
  fs9.mkdirSync(layerDir, { recursive: true });
1989
2389
  created.push(`src/${name}/`);
1990
2390
  }
1991
- const indexPath = path15.join(layerDir, "index.ts");
2391
+ const indexPath = path18.join(layerDir, "index.ts");
1992
2392
  if (!fs9.existsSync(indexPath)) {
1993
2393
  fs9.writeFileSync(indexPath, LAYER_INDEX_TEMPLATE(name));
1994
2394
  created.push(`src/${name}/index.ts`);
@@ -1996,7 +2396,7 @@ async function runAdd(componentType, name, options) {
1996
2396
  break;
1997
2397
  }
1998
2398
  case "module": {
1999
- const modulePath = path15.join(cwd, "src", `${name}.ts`);
2399
+ const modulePath = path18.join(cwd, "src", `${name}.ts`);
2000
2400
  if (fs9.existsSync(modulePath)) {
2001
2401
  return Err10(new CLIError(`Module ${name} already exists`, ExitCode.ERROR));
2002
2402
  }
@@ -2005,11 +2405,11 @@ async function runAdd(componentType, name, options) {
2005
2405
  break;
2006
2406
  }
2007
2407
  case "doc": {
2008
- const docsDir = path15.join(cwd, "docs");
2408
+ const docsDir = path18.join(cwd, "docs");
2009
2409
  if (!fs9.existsSync(docsDir)) {
2010
2410
  fs9.mkdirSync(docsDir, { recursive: true });
2011
2411
  }
2012
- const docPath = path15.join(docsDir, `${name}.md`);
2412
+ const docPath = path18.join(docsDir, `${name}.md`);
2013
2413
  if (fs9.existsSync(docPath)) {
2014
2414
  return Err10(new CLIError(`Doc ${name} already exists`, ExitCode.ERROR));
2015
2415
  }
@@ -2022,18 +2422,18 @@ async function runAdd(componentType, name, options) {
2022
2422
  generateSkillFiles2({
2023
2423
  name,
2024
2424
  description: `${name} skill`,
2025
- outputDir: path15.join(cwd, "agents", "skills", "claude-code")
2425
+ outputDir: path18.join(cwd, "agents", "skills", "claude-code")
2026
2426
  });
2027
2427
  created.push(`agents/skills/claude-code/${name}/skill.yaml`);
2028
2428
  created.push(`agents/skills/claude-code/${name}/SKILL.md`);
2029
2429
  break;
2030
2430
  }
2031
2431
  case "persona": {
2032
- const personasDir = path15.join(cwd, "agents", "personas");
2432
+ const personasDir = path18.join(cwd, "agents", "personas");
2033
2433
  if (!fs9.existsSync(personasDir)) {
2034
2434
  fs9.mkdirSync(personasDir, { recursive: true });
2035
2435
  }
2036
- const personaPath = path15.join(personasDir, `${name}.yaml`);
2436
+ const personaPath = path18.join(personasDir, `${name}.yaml`);
2037
2437
  if (fs9.existsSync(personaPath)) {
2038
2438
  return Err10(new CLIError(`Persona ${name} already exists`, ExitCode.ERROR));
2039
2439
  }
@@ -2059,7 +2459,7 @@ focus_areas: []
2059
2459
  );
2060
2460
  }
2061
2461
  }
2062
- return Ok12({ created });
2462
+ return Ok14({ created });
2063
2463
  } catch (error) {
2064
2464
  return Err10(
2065
2465
  new CLIError(
@@ -2070,7 +2470,7 @@ focus_areas: []
2070
2470
  }
2071
2471
  }
2072
2472
  function createAddCommand() {
2073
- const command = new Command11("add").description("Add a component to the project").argument("<type>", "Component type (layer, module, doc, skill, persona)").argument("<name>", "Component name").action(async (type, name, _opts, cmd) => {
2473
+ const command = new Command14("add").description("Add a component to the project").argument("<type>", "Component type (layer, module, doc, skill, persona)").argument("<name>", "Component name").action(async (type, name, _opts, cmd) => {
2074
2474
  const globalOpts = cmd.optsWithGlobals();
2075
2475
  const result = await runAdd(type, name, {
2076
2476
  ...globalOpts.config !== void 0 && { configPath: globalOpts.config }
@@ -2091,13 +2491,13 @@ function createAddCommand() {
2091
2491
  }
2092
2492
 
2093
2493
  // src/commands/linter/index.ts
2094
- import { Command as Command14 } from "commander";
2494
+ import { Command as Command17 } from "commander";
2095
2495
 
2096
2496
  // src/commands/linter/generate.ts
2097
- import { Command as Command12 } from "commander";
2497
+ import { Command as Command15 } from "commander";
2098
2498
  import { generate } from "@harness-engineering/linter-gen";
2099
2499
  function createGenerateCommand() {
2100
- return new Command12("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) => {
2500
+ return new Command15("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) => {
2101
2501
  try {
2102
2502
  if (options.verbose) {
2103
2503
  logger.info(`Parsing config: ${options.config}`);
@@ -2159,10 +2559,10 @@ Generated ${result.rulesGenerated.length} rules to ${result.outputDir}`);
2159
2559
  }
2160
2560
 
2161
2561
  // src/commands/linter/validate.ts
2162
- import { Command as Command13 } from "commander";
2562
+ import { Command as Command16 } from "commander";
2163
2563
  import { validate } from "@harness-engineering/linter-gen";
2164
2564
  function createValidateCommand2() {
2165
- return new Command13("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) => {
2565
+ return new Command16("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) => {
2166
2566
  try {
2167
2567
  const result = await validate({ configPath: options.config });
2168
2568
  if (options.json) {
@@ -2181,7 +2581,7 @@ function createValidateCommand2() {
2181
2581
 
2182
2582
  // src/commands/linter/index.ts
2183
2583
  function createLinterCommand() {
2184
- const linter = new Command14("linter").description(
2584
+ const linter = new Command17("linter").description(
2185
2585
  "Generate and validate ESLint rules from YAML config"
2186
2586
  );
2187
2587
  linter.addCommand(createGenerateCommand());
@@ -2190,12 +2590,12 @@ function createLinterCommand() {
2190
2590
  }
2191
2591
 
2192
2592
  // src/commands/persona/index.ts
2193
- import { Command as Command17 } from "commander";
2593
+ import { Command as Command20 } from "commander";
2194
2594
 
2195
2595
  // src/commands/persona/list.ts
2196
- import { Command as Command15 } from "commander";
2596
+ import { Command as Command18 } from "commander";
2197
2597
  function createListCommand() {
2198
- return new Command15("list").description("List available agent personas").action(async (_opts, cmd) => {
2598
+ return new Command18("list").description("List available agent personas").action(async (_opts, cmd) => {
2199
2599
  const globalOpts = cmd.optsWithGlobals();
2200
2600
  const personasDir = resolvePersonasDir();
2201
2601
  const result = listPersonas(personasDir);
@@ -2224,12 +2624,12 @@ function createListCommand() {
2224
2624
  }
2225
2625
 
2226
2626
  // src/commands/persona/generate.ts
2227
- import { Command as Command16 } from "commander";
2627
+ import { Command as Command19 } from "commander";
2228
2628
  import * as fs10 from "fs";
2229
- import * as path16 from "path";
2629
+ import * as path19 from "path";
2230
2630
 
2231
2631
  // src/persona/generators/runtime.ts
2232
- import { Ok as Ok13, Err as Err11 } from "@harness-engineering/core";
2632
+ import { Ok as Ok15, Err as Err11 } from "@harness-engineering/core";
2233
2633
 
2234
2634
  // src/utils/string.ts
2235
2635
  function toKebabCase(name) {
@@ -2246,7 +2646,7 @@ function generateRuntime(persona) {
2246
2646
  timeout: persona.config.timeout,
2247
2647
  severity: persona.config.severity
2248
2648
  };
2249
- return Ok13(JSON.stringify(config, null, 2));
2649
+ return Ok15(JSON.stringify(config, null, 2));
2250
2650
  } catch (error) {
2251
2651
  return Err11(
2252
2652
  new Error(
@@ -2257,7 +2657,7 @@ function generateRuntime(persona) {
2257
2657
  }
2258
2658
 
2259
2659
  // src/persona/generators/agents-md.ts
2260
- import { Ok as Ok14, Err as Err12 } from "@harness-engineering/core";
2660
+ import { Ok as Ok16, Err as Err12 } from "@harness-engineering/core";
2261
2661
  function formatTrigger(trigger) {
2262
2662
  switch (trigger.event) {
2263
2663
  case "on_pr": {
@@ -2291,7 +2691,7 @@ function generateAgentsMd(persona) {
2291
2691
 
2292
2692
  **When this agent flags an issue:** Fix violations before merging. Run ${allCommands} locally to validate.
2293
2693
  `;
2294
- return Ok14(fragment);
2694
+ return Ok16(fragment);
2295
2695
  } catch (error) {
2296
2696
  return Err12(
2297
2697
  new Error(
@@ -2303,7 +2703,7 @@ function generateAgentsMd(persona) {
2303
2703
 
2304
2704
  // src/persona/generators/ci-workflow.ts
2305
2705
  import YAML2 from "yaml";
2306
- import { Ok as Ok15, Err as Err13 } from "@harness-engineering/core";
2706
+ import { Ok as Ok17, Err as Err13 } from "@harness-engineering/core";
2307
2707
  function buildGitHubTriggers(triggers) {
2308
2708
  const on = {};
2309
2709
  for (const trigger of triggers) {
@@ -2351,7 +2751,7 @@ function generateCIWorkflow(persona, platform) {
2351
2751
  }
2352
2752
  }
2353
2753
  };
2354
- return Ok15(YAML2.stringify(workflow, { lineWidth: 0 }));
2754
+ return Ok17(YAML2.stringify(workflow, { lineWidth: 0 }));
2355
2755
  } catch (error) {
2356
2756
  return Err13(
2357
2757
  new Error(
@@ -2363,25 +2763,25 @@ function generateCIWorkflow(persona, platform) {
2363
2763
 
2364
2764
  // src/commands/persona/generate.ts
2365
2765
  function createGenerateCommand2() {
2366
- return new Command16("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) => {
2766
+ return new Command19("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) => {
2367
2767
  const globalOpts = cmd.optsWithGlobals();
2368
2768
  const personasDir = resolvePersonasDir();
2369
- const filePath = path16.join(personasDir, `${name}.yaml`);
2769
+ const filePath = path19.join(personasDir, `${name}.yaml`);
2370
2770
  const personaResult = loadPersona(filePath);
2371
2771
  if (!personaResult.ok) {
2372
2772
  logger.error(personaResult.error.message);
2373
2773
  process.exit(ExitCode.ERROR);
2374
2774
  }
2375
2775
  const persona = personaResult.value;
2376
- const outputDir = path16.resolve(opts.outputDir);
2776
+ const outputDir = path19.resolve(opts.outputDir);
2377
2777
  const slug = toKebabCase(persona.name);
2378
2778
  const only = opts.only;
2379
2779
  const generated = [];
2380
2780
  if (!only || only === "runtime") {
2381
2781
  const result = generateRuntime(persona);
2382
2782
  if (result.ok) {
2383
- const outPath = path16.join(outputDir, `${slug}.runtime.json`);
2384
- fs10.mkdirSync(path16.dirname(outPath), { recursive: true });
2783
+ const outPath = path19.join(outputDir, `${slug}.runtime.json`);
2784
+ fs10.mkdirSync(path19.dirname(outPath), { recursive: true });
2385
2785
  fs10.writeFileSync(outPath, result.value);
2386
2786
  generated.push(outPath);
2387
2787
  }
@@ -2389,7 +2789,7 @@ function createGenerateCommand2() {
2389
2789
  if (!only || only === "agents-md") {
2390
2790
  const result = generateAgentsMd(persona);
2391
2791
  if (result.ok) {
2392
- const outPath = path16.join(outputDir, `${slug}.agents.md`);
2792
+ const outPath = path19.join(outputDir, `${slug}.agents.md`);
2393
2793
  fs10.writeFileSync(outPath, result.value);
2394
2794
  generated.push(outPath);
2395
2795
  }
@@ -2397,8 +2797,8 @@ function createGenerateCommand2() {
2397
2797
  if (!only || only === "ci") {
2398
2798
  const result = generateCIWorkflow(persona, "github");
2399
2799
  if (result.ok) {
2400
- const outPath = path16.join(outputDir, ".github", "workflows", `${slug}.yml`);
2401
- fs10.mkdirSync(path16.dirname(outPath), { recursive: true });
2800
+ const outPath = path19.join(outputDir, ".github", "workflows", `${slug}.yml`);
2801
+ fs10.mkdirSync(path19.dirname(outPath), { recursive: true });
2402
2802
  fs10.writeFileSync(outPath, result.value);
2403
2803
  generated.push(outPath);
2404
2804
  }
@@ -2413,22 +2813,22 @@ function createGenerateCommand2() {
2413
2813
 
2414
2814
  // src/commands/persona/index.ts
2415
2815
  function createPersonaCommand() {
2416
- const command = new Command17("persona").description("Agent persona management commands");
2816
+ const command = new Command20("persona").description("Agent persona management commands");
2417
2817
  command.addCommand(createListCommand());
2418
2818
  command.addCommand(createGenerateCommand2());
2419
2819
  return command;
2420
2820
  }
2421
2821
 
2422
2822
  // src/commands/skill/index.ts
2423
- import { Command as Command22 } from "commander";
2823
+ import { Command as Command25 } from "commander";
2424
2824
 
2425
2825
  // src/commands/skill/list.ts
2426
- import { Command as Command18 } from "commander";
2826
+ import { Command as Command21 } from "commander";
2427
2827
  import * as fs11 from "fs";
2428
- import * as path17 from "path";
2828
+ import * as path20 from "path";
2429
2829
  import { parse as parse3 } from "yaml";
2430
2830
  function createListCommand2() {
2431
- return new Command18("list").description("List available skills").action(async (_opts, cmd) => {
2831
+ return new Command21("list").description("List available skills").action(async (_opts, cmd) => {
2432
2832
  const globalOpts = cmd.optsWithGlobals();
2433
2833
  const skillsDir = resolveSkillsDir();
2434
2834
  if (!fs11.existsSync(skillsDir)) {
@@ -2439,7 +2839,7 @@ function createListCommand2() {
2439
2839
  const entries = fs11.readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
2440
2840
  const skills = [];
2441
2841
  for (const name of entries) {
2442
- const yamlPath = path17.join(skillsDir, name, "skill.yaml");
2842
+ const yamlPath = path20.join(skillsDir, name, "skill.yaml");
2443
2843
  if (!fs11.existsSync(yamlPath)) continue;
2444
2844
  try {
2445
2845
  const raw = fs11.readFileSync(yamlPath, "utf-8");
@@ -2472,13 +2872,13 @@ function createListCommand2() {
2472
2872
  }
2473
2873
 
2474
2874
  // src/commands/skill/run.ts
2475
- import { Command as Command19 } from "commander";
2875
+ import { Command as Command22 } from "commander";
2476
2876
  import * as fs12 from "fs";
2477
- import * as path18 from "path";
2877
+ import * as path21 from "path";
2478
2878
  import { parse as parse4 } from "yaml";
2479
2879
 
2480
2880
  // src/skill/complexity.ts
2481
- import { execSync as execSync2 } from "child_process";
2881
+ import { execSync as execSync3 } from "child_process";
2482
2882
  function evaluateSignals(signals) {
2483
2883
  if (signals.fileCount >= 3) return "full";
2484
2884
  if (signals.newDir) return "full";
@@ -2490,17 +2890,17 @@ function evaluateSignals(signals) {
2490
2890
  }
2491
2891
  function detectComplexity(projectPath) {
2492
2892
  try {
2493
- const base = execSync2("git merge-base HEAD main", {
2893
+ const base = execSync3("git merge-base HEAD main", {
2494
2894
  cwd: projectPath,
2495
2895
  encoding: "utf-8",
2496
2896
  stdio: ["pipe", "pipe", "pipe"]
2497
2897
  }).trim();
2498
- const diffFiles = execSync2(`git diff --name-only ${base}`, {
2898
+ const diffFiles = execSync3(`git diff --name-only ${base}`, {
2499
2899
  cwd: projectPath,
2500
2900
  encoding: "utf-8",
2501
2901
  stdio: ["pipe", "pipe", "pipe"]
2502
2902
  }).trim().split("\n").filter(Boolean);
2503
- const diffStat = execSync2(`git diff --stat ${base}`, {
2903
+ const diffStat = execSync3(`git diff --stat ${base}`, {
2504
2904
  cwd: projectPath,
2505
2905
  encoding: "utf-8",
2506
2906
  stdio: ["pipe", "pipe", "pipe"]
@@ -2562,15 +2962,15 @@ ${options.priorState}`);
2562
2962
 
2563
2963
  // src/commands/skill/run.ts
2564
2964
  function createRunCommand2() {
2565
- return new Command19("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) => {
2965
+ 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) => {
2566
2966
  const skillsDir = resolveSkillsDir();
2567
- const skillDir = path18.join(skillsDir, name);
2967
+ const skillDir = path21.join(skillsDir, name);
2568
2968
  if (!fs12.existsSync(skillDir)) {
2569
2969
  logger.error(`Skill not found: ${name}`);
2570
2970
  process.exit(ExitCode.ERROR);
2571
2971
  return;
2572
2972
  }
2573
- const yamlPath = path18.join(skillDir, "skill.yaml");
2973
+ const yamlPath = path21.join(skillDir, "skill.yaml");
2574
2974
  let metadata = null;
2575
2975
  if (fs12.existsSync(yamlPath)) {
2576
2976
  try {
@@ -2585,15 +2985,15 @@ function createRunCommand2() {
2585
2985
  if (metadata?.phases && metadata.phases.length > 0) {
2586
2986
  const requested = opts.complexity ?? "auto";
2587
2987
  if (requested === "auto") {
2588
- const projectPath2 = opts.path ? path18.resolve(opts.path) : process.cwd();
2988
+ const projectPath2 = opts.path ? path21.resolve(opts.path) : process.cwd();
2589
2989
  complexity = detectComplexity(projectPath2);
2590
2990
  } else {
2591
2991
  complexity = requested;
2592
2992
  }
2593
2993
  }
2594
2994
  let principles;
2595
- const projectPath = opts.path ? path18.resolve(opts.path) : process.cwd();
2596
- const principlesPath = path18.join(projectPath, "docs", "principles.md");
2995
+ const projectPath = opts.path ? path21.resolve(opts.path) : process.cwd();
2996
+ const principlesPath = path21.join(projectPath, "docs", "principles.md");
2597
2997
  if (fs12.existsSync(principlesPath)) {
2598
2998
  principles = fs12.readFileSync(principlesPath, "utf-8");
2599
2999
  }
@@ -2610,13 +3010,13 @@ function createRunCommand2() {
2610
3010
  }
2611
3011
  if (metadata?.state.persistent && metadata.state.files.length > 0) {
2612
3012
  for (const stateFilePath of metadata.state.files) {
2613
- const fullPath = path18.join(projectPath, stateFilePath);
3013
+ const fullPath = path21.join(projectPath, stateFilePath);
2614
3014
  if (fs12.existsSync(fullPath)) {
2615
3015
  const stat = fs12.statSync(fullPath);
2616
3016
  if (stat.isDirectory()) {
2617
- const files = fs12.readdirSync(fullPath).map((f) => ({ name: f, mtime: fs12.statSync(path18.join(fullPath, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
3017
+ const files = fs12.readdirSync(fullPath).map((f) => ({ name: f, mtime: fs12.statSync(path21.join(fullPath, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
2618
3018
  if (files.length > 0) {
2619
- priorState = fs12.readFileSync(path18.join(fullPath, files[0].name), "utf-8");
3019
+ priorState = fs12.readFileSync(path21.join(fullPath, files[0].name), "utf-8");
2620
3020
  }
2621
3021
  } else {
2622
3022
  priorState = fs12.readFileSync(fullPath, "utf-8");
@@ -2638,7 +3038,7 @@ function createRunCommand2() {
2638
3038
  ...stateWarning !== void 0 && { stateWarning },
2639
3039
  party: opts.party
2640
3040
  });
2641
- const skillMdPath = path18.join(skillDir, "SKILL.md");
3041
+ const skillMdPath = path21.join(skillDir, "SKILL.md");
2642
3042
  if (!fs12.existsSync(skillMdPath)) {
2643
3043
  logger.error(`SKILL.md not found for skill: ${name}`);
2644
3044
  process.exit(ExitCode.ERROR);
@@ -2646,7 +3046,7 @@ function createRunCommand2() {
2646
3046
  }
2647
3047
  let content = fs12.readFileSync(skillMdPath, "utf-8");
2648
3048
  if (metadata?.state.persistent && opts.path) {
2649
- const stateFile = path18.join(projectPath, ".harness", "state.json");
3049
+ const stateFile = path21.join(projectPath, ".harness", "state.json");
2650
3050
  if (fs12.existsSync(stateFile)) {
2651
3051
  const stateContent = fs12.readFileSync(stateFile, "utf-8");
2652
3052
  content += `
@@ -2665,9 +3065,9 @@ ${stateContent}
2665
3065
  }
2666
3066
 
2667
3067
  // src/commands/skill/validate.ts
2668
- import { Command as Command20 } from "commander";
3068
+ import { Command as Command23 } from "commander";
2669
3069
  import * as fs13 from "fs";
2670
- import * as path19 from "path";
3070
+ import * as path22 from "path";
2671
3071
  import { parse as parse5 } from "yaml";
2672
3072
  var REQUIRED_SECTIONS = [
2673
3073
  "## When to Use",
@@ -2677,7 +3077,7 @@ var REQUIRED_SECTIONS = [
2677
3077
  "## Examples"
2678
3078
  ];
2679
3079
  function createValidateCommand3() {
2680
- return new Command20("validate").description("Validate all skill.yaml files and SKILL.md structure").action(async (_opts, cmd) => {
3080
+ return new Command23("validate").description("Validate all skill.yaml files and SKILL.md structure").action(async (_opts, cmd) => {
2681
3081
  const globalOpts = cmd.optsWithGlobals();
2682
3082
  const skillsDir = resolveSkillsDir();
2683
3083
  if (!fs13.existsSync(skillsDir)) {
@@ -2689,9 +3089,9 @@ function createValidateCommand3() {
2689
3089
  const errors = [];
2690
3090
  let validated = 0;
2691
3091
  for (const name of entries) {
2692
- const skillDir = path19.join(skillsDir, name);
2693
- const yamlPath = path19.join(skillDir, "skill.yaml");
2694
- const skillMdPath = path19.join(skillDir, "SKILL.md");
3092
+ const skillDir = path22.join(skillsDir, name);
3093
+ const yamlPath = path22.join(skillDir, "skill.yaml");
3094
+ const skillMdPath = path22.join(skillDir, "SKILL.md");
2695
3095
  if (!fs13.existsSync(yamlPath)) {
2696
3096
  errors.push(`${name}: missing skill.yaml`);
2697
3097
  continue;
@@ -2746,21 +3146,21 @@ function createValidateCommand3() {
2746
3146
  }
2747
3147
 
2748
3148
  // src/commands/skill/info.ts
2749
- import { Command as Command21 } from "commander";
3149
+ import { Command as Command24 } from "commander";
2750
3150
  import * as fs14 from "fs";
2751
- import * as path20 from "path";
3151
+ import * as path23 from "path";
2752
3152
  import { parse as parse6 } from "yaml";
2753
3153
  function createInfoCommand() {
2754
- return new Command21("info").description("Show metadata for a skill").argument("<name>", "Skill name (e.g., harness-tdd)").action(async (name, _opts, cmd) => {
3154
+ return new Command24("info").description("Show metadata for a skill").argument("<name>", "Skill name (e.g., harness-tdd)").action(async (name, _opts, cmd) => {
2755
3155
  const globalOpts = cmd.optsWithGlobals();
2756
3156
  const skillsDir = resolveSkillsDir();
2757
- const skillDir = path20.join(skillsDir, name);
3157
+ const skillDir = path23.join(skillsDir, name);
2758
3158
  if (!fs14.existsSync(skillDir)) {
2759
3159
  logger.error(`Skill not found: ${name}`);
2760
3160
  process.exit(ExitCode.ERROR);
2761
3161
  return;
2762
3162
  }
2763
- const yamlPath = path20.join(skillDir, "skill.yaml");
3163
+ const yamlPath = path23.join(skillDir, "skill.yaml");
2764
3164
  if (!fs14.existsSync(yamlPath)) {
2765
3165
  logger.error(`skill.yaml not found for skill: ${name}`);
2766
3166
  process.exit(ExitCode.ERROR);
@@ -2808,7 +3208,7 @@ function createInfoCommand() {
2808
3208
 
2809
3209
  // src/commands/skill/index.ts
2810
3210
  function createSkillCommand() {
2811
- const command = new Command22("skill").description("Skill management commands");
3211
+ const command = new Command25("skill").description("Skill management commands");
2812
3212
  command.addCommand(createListCommand2());
2813
3213
  command.addCommand(createRunCommand2());
2814
3214
  command.addCommand(createValidateCommand3());
@@ -2817,17 +3217,17 @@ function createSkillCommand() {
2817
3217
  }
2818
3218
 
2819
3219
  // src/commands/state/index.ts
2820
- import { Command as Command26 } from "commander";
3220
+ import { Command as Command30 } from "commander";
2821
3221
 
2822
3222
  // src/commands/state/show.ts
2823
- import { Command as Command23 } from "commander";
2824
- import * as path21 from "path";
3223
+ import { Command as Command26 } from "commander";
3224
+ import * as path24 from "path";
2825
3225
  import { loadState } from "@harness-engineering/core";
2826
3226
  function createShowCommand() {
2827
- return new Command23("show").description("Show current project state").option("--path <path>", "Project root path", ".").action(async (opts, cmd) => {
3227
+ return new Command26("show").description("Show current project state").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(async (opts, cmd) => {
2828
3228
  const globalOpts = cmd.optsWithGlobals();
2829
- const projectPath = path21.resolve(opts.path);
2830
- const result = await loadState(projectPath);
3229
+ const projectPath = path24.resolve(opts.path);
3230
+ const result = await loadState(projectPath, opts.stream);
2831
3231
  if (!result.ok) {
2832
3232
  logger.error(result.error.message);
2833
3233
  process.exit(ExitCode.ERROR);
@@ -2839,6 +3239,7 @@ function createShowCommand() {
2839
3239
  } else if (globalOpts.quiet) {
2840
3240
  console.log(JSON.stringify(state));
2841
3241
  } else {
3242
+ if (opts.stream) console.log(`Stream: ${opts.stream}`);
2842
3243
  console.log(`Schema Version: ${state.schemaVersion}`);
2843
3244
  if (state.position.phase) console.log(`Phase: ${state.position.phase}`);
2844
3245
  if (state.position.task) console.log(`Task: ${state.position.task}`);
@@ -2865,14 +3266,26 @@ Decisions: ${state.decisions.length}`);
2865
3266
  }
2866
3267
 
2867
3268
  // src/commands/state/reset.ts
2868
- import { Command as Command24 } from "commander";
3269
+ import { Command as Command27 } from "commander";
2869
3270
  import * as fs15 from "fs";
2870
- import * as path22 from "path";
3271
+ import * as path25 from "path";
2871
3272
  import * as readline from "readline";
3273
+ import { resolveStreamPath } from "@harness-engineering/core";
2872
3274
  function createResetCommand() {
2873
- return new Command24("reset").description("Reset project state (deletes .harness/state.json)").option("--path <path>", "Project root path", ".").option("--yes", "Skip confirmation prompt").action(async (opts, _cmd) => {
2874
- const projectPath = path22.resolve(opts.path);
2875
- const statePath = path22.join(projectPath, ".harness", "state.json");
3275
+ return new Command27("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, _cmd) => {
3276
+ const projectPath = path25.resolve(opts.path);
3277
+ let statePath;
3278
+ if (opts.stream) {
3279
+ const streamResult = await resolveStreamPath(projectPath, { stream: opts.stream });
3280
+ if (!streamResult.ok) {
3281
+ logger.error(streamResult.error.message);
3282
+ process.exit(ExitCode.ERROR);
3283
+ return;
3284
+ }
3285
+ statePath = path25.join(streamResult.value, "state.json");
3286
+ } else {
3287
+ statePath = path25.join(projectPath, ".harness", "state.json");
3288
+ }
2876
3289
  if (!fs15.existsSync(statePath)) {
2877
3290
  logger.info("No state file found. Nothing to reset.");
2878
3291
  process.exit(ExitCode.SUCCESS);
@@ -2880,8 +3293,8 @@ function createResetCommand() {
2880
3293
  }
2881
3294
  if (!opts.yes) {
2882
3295
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
2883
- const answer = await new Promise((resolve18) => {
2884
- rl.question("Reset project state? This cannot be undone. [y/N] ", resolve18);
3296
+ const answer = await new Promise((resolve22) => {
3297
+ rl.question("Reset project state? This cannot be undone. [y/N] ", resolve22);
2885
3298
  });
2886
3299
  rl.close();
2887
3300
  if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "yes") {
@@ -2903,13 +3316,13 @@ function createResetCommand() {
2903
3316
  }
2904
3317
 
2905
3318
  // src/commands/state/learn.ts
2906
- import { Command as Command25 } from "commander";
2907
- import * as path23 from "path";
3319
+ import { Command as Command28 } from "commander";
3320
+ import * as path26 from "path";
2908
3321
  import { appendLearning } from "@harness-engineering/core";
2909
3322
  function createLearnCommand() {
2910
- return new Command25("learn").description("Append a learning to .harness/learnings.md").argument("<message>", "The learning to record").option("--path <path>", "Project root path", ".").action(async (message, opts, _cmd) => {
2911
- const projectPath = path23.resolve(opts.path);
2912
- const result = await appendLearning(projectPath, message);
3323
+ return new Command28("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) => {
3324
+ const projectPath = path26.resolve(opts.path);
3325
+ const result = await appendLearning(projectPath, message, void 0, void 0, opts.stream);
2913
3326
  if (!result.ok) {
2914
3327
  logger.error(result.error.message);
2915
3328
  process.exit(ExitCode.ERROR);
@@ -2920,29 +3333,103 @@ function createLearnCommand() {
2920
3333
  });
2921
3334
  }
2922
3335
 
3336
+ // src/commands/state/streams.ts
3337
+ import { Command as Command29 } from "commander";
3338
+ import * as path27 from "path";
3339
+ import {
3340
+ createStream,
3341
+ listStreams,
3342
+ archiveStream,
3343
+ setActiveStream,
3344
+ loadStreamIndex
3345
+ } from "@harness-engineering/core";
3346
+ function createStreamsCommand() {
3347
+ const command = new Command29("streams").description("Manage state streams");
3348
+ command.command("list").description("List all known streams").option("--path <path>", "Project root path", ".").action(async (opts, cmd) => {
3349
+ const globalOpts = cmd.optsWithGlobals();
3350
+ const projectPath = path27.resolve(opts.path);
3351
+ const indexResult = await loadStreamIndex(projectPath);
3352
+ const result = await listStreams(projectPath);
3353
+ if (!result.ok) {
3354
+ logger.error(result.error.message);
3355
+ process.exit(ExitCode.ERROR);
3356
+ return;
3357
+ }
3358
+ const active = indexResult.ok ? indexResult.value.activeStream : null;
3359
+ if (globalOpts.json) {
3360
+ logger.raw({ activeStream: active, streams: result.value });
3361
+ } else {
3362
+ if (result.value.length === 0) {
3363
+ console.log("No streams found.");
3364
+ }
3365
+ for (const s of result.value) {
3366
+ const marker = s.name === active ? " (active)" : "";
3367
+ const branch = s.branch ? ` [${s.branch}]` : "";
3368
+ console.log(` ${s.name}${marker}${branch} \u2014 last active: ${s.lastActiveAt}`);
3369
+ }
3370
+ }
3371
+ process.exit(ExitCode.SUCCESS);
3372
+ });
3373
+ 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) => {
3374
+ const projectPath = path27.resolve(opts.path);
3375
+ const result = await createStream(projectPath, name, opts.branch);
3376
+ if (!result.ok) {
3377
+ logger.error(result.error.message);
3378
+ process.exit(ExitCode.ERROR);
3379
+ return;
3380
+ }
3381
+ logger.success(`Stream '${name}' created.`);
3382
+ process.exit(ExitCode.SUCCESS);
3383
+ });
3384
+ command.command("archive <name>").description("Archive a stream").option("--path <path>", "Project root path", ".").action(async (name, opts) => {
3385
+ const projectPath = path27.resolve(opts.path);
3386
+ const result = await archiveStream(projectPath, name);
3387
+ if (!result.ok) {
3388
+ logger.error(result.error.message);
3389
+ process.exit(ExitCode.ERROR);
3390
+ return;
3391
+ }
3392
+ logger.success(`Stream '${name}' archived.`);
3393
+ process.exit(ExitCode.SUCCESS);
3394
+ });
3395
+ command.command("activate <name>").description("Set the active stream").option("--path <path>", "Project root path", ".").action(async (name, opts) => {
3396
+ const projectPath = path27.resolve(opts.path);
3397
+ const result = await setActiveStream(projectPath, name);
3398
+ if (!result.ok) {
3399
+ logger.error(result.error.message);
3400
+ process.exit(ExitCode.ERROR);
3401
+ return;
3402
+ }
3403
+ logger.success(`Active stream set to '${name}'.`);
3404
+ process.exit(ExitCode.SUCCESS);
3405
+ });
3406
+ return command;
3407
+ }
3408
+
2923
3409
  // src/commands/state/index.ts
2924
3410
  function createStateCommand() {
2925
- const command = new Command26("state").description("Project state management commands");
3411
+ const command = new Command30("state").description("Project state management commands");
2926
3412
  command.addCommand(createShowCommand());
2927
3413
  command.addCommand(createResetCommand());
2928
3414
  command.addCommand(createLearnCommand());
3415
+ command.addCommand(createStreamsCommand());
2929
3416
  return command;
2930
3417
  }
2931
3418
 
2932
3419
  // src/commands/check-phase-gate.ts
2933
- import { Command as Command27 } from "commander";
2934
- import * as path24 from "path";
3420
+ import { Command as Command31 } from "commander";
3421
+ import * as path28 from "path";
2935
3422
  import * as fs16 from "fs";
2936
- import { Ok as Ok16 } from "@harness-engineering/core";
3423
+ import { Ok as Ok18 } from "@harness-engineering/core";
2937
3424
  function resolveSpecPath(implFile, implPattern, specPattern, cwd) {
2938
- const relImpl = path24.relative(cwd, implFile);
3425
+ const relImpl = path28.relative(cwd, implFile);
2939
3426
  const implBase = (implPattern.split("*")[0] ?? "").replace(/\/+$/, "");
2940
3427
  const afterBase = relImpl.startsWith(implBase + "/") ? relImpl.slice(implBase.length + 1) : relImpl;
2941
3428
  const segments = afterBase.split("/");
2942
3429
  const firstSegment = segments[0] ?? "";
2943
- const feature = segments.length > 1 ? firstSegment : path24.basename(firstSegment, path24.extname(firstSegment));
3430
+ const feature = segments.length > 1 ? firstSegment : path28.basename(firstSegment, path28.extname(firstSegment));
2944
3431
  const specRelative = specPattern.replace("{feature}", feature);
2945
- return path24.resolve(cwd, specRelative);
3432
+ return path28.resolve(cwd, specRelative);
2946
3433
  }
2947
3434
  async function runCheckPhaseGate(options) {
2948
3435
  const configResult = resolveConfig(options.configPath);
@@ -2950,9 +3437,9 @@ async function runCheckPhaseGate(options) {
2950
3437
  return configResult;
2951
3438
  }
2952
3439
  const config = configResult.value;
2953
- const cwd = options.cwd ?? (options.configPath ? path24.dirname(path24.resolve(options.configPath)) : process.cwd());
3440
+ const cwd = options.cwd ?? (options.configPath ? path28.dirname(path28.resolve(options.configPath)) : process.cwd());
2954
3441
  if (!config.phaseGates?.enabled) {
2955
- return Ok16({
3442
+ return Ok18({
2956
3443
  pass: true,
2957
3444
  skipped: true,
2958
3445
  missingSpecs: [],
@@ -2969,14 +3456,14 @@ async function runCheckPhaseGate(options) {
2969
3456
  const expectedSpec = resolveSpecPath(implFile, mapping.implPattern, mapping.specPattern, cwd);
2970
3457
  if (!fs16.existsSync(expectedSpec)) {
2971
3458
  missingSpecs.push({
2972
- implFile: path24.relative(cwd, implFile),
2973
- expectedSpec: path24.relative(cwd, expectedSpec)
3459
+ implFile: path28.relative(cwd, implFile),
3460
+ expectedSpec: path28.relative(cwd, expectedSpec)
2974
3461
  });
2975
3462
  }
2976
3463
  }
2977
3464
  }
2978
3465
  const pass = missingSpecs.length === 0;
2979
- return Ok16({
3466
+ return Ok18({
2980
3467
  pass,
2981
3468
  skipped: false,
2982
3469
  severity: phaseGates.severity,
@@ -2985,7 +3472,7 @@ async function runCheckPhaseGate(options) {
2985
3472
  });
2986
3473
  }
2987
3474
  function createCheckPhaseGateCommand() {
2988
- const command = new Command27("check-phase-gate").description("Verify that implementation files have matching spec documents").action(async (_opts, cmd) => {
3475
+ const command = new Command31("check-phase-gate").description("Verify that implementation files have matching spec documents").action(async (_opts, cmd) => {
2989
3476
  const globalOpts = cmd.optsWithGlobals();
2990
3477
  const mode = globalOpts.json ? OutputMode.JSON : globalOpts.quiet ? OutputMode.QUIET : globalOpts.verbose ? OutputMode.VERBOSE : OutputMode.TEXT;
2991
3478
  const formatter = new OutputFormatter(mode);
@@ -3039,15 +3526,15 @@ function createCheckPhaseGateCommand() {
3039
3526
  }
3040
3527
 
3041
3528
  // src/commands/generate-slash-commands.ts
3042
- import { Command as Command28 } from "commander";
3529
+ import { Command as Command32 } from "commander";
3043
3530
  import fs19 from "fs";
3044
- import path27 from "path";
3531
+ import path31 from "path";
3045
3532
  import os2 from "os";
3046
3533
  import readline2 from "readline";
3047
3534
 
3048
3535
  // src/slash-commands/normalize.ts
3049
3536
  import fs17 from "fs";
3050
- import path25 from "path";
3537
+ import path29 from "path";
3051
3538
  import { parse as parse7 } from "yaml";
3052
3539
 
3053
3540
  // src/slash-commands/normalize-name.ts
@@ -3071,7 +3558,7 @@ function normalizeSkills(skillSources, platforms) {
3071
3558
  if (!fs17.existsSync(skillsDir)) continue;
3072
3559
  const entries = fs17.readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory());
3073
3560
  for (const entry of entries) {
3074
- const yamlPath = path25.join(skillsDir, entry.name, "skill.yaml");
3561
+ const yamlPath = path29.join(skillsDir, entry.name, "skill.yaml");
3075
3562
  if (!fs17.existsSync(yamlPath)) continue;
3076
3563
  let raw;
3077
3564
  try {
@@ -3099,15 +3586,15 @@ function normalizeSkills(skillSources, platforms) {
3099
3586
  continue;
3100
3587
  }
3101
3588
  nameMap.set(normalized, { skillName: meta.name, source });
3102
- const skillMdPath = path25.join(skillsDir, entry.name, "SKILL.md");
3589
+ const skillMdPath = path29.join(skillsDir, entry.name, "SKILL.md");
3103
3590
  const skillMdContent = fs17.existsSync(skillMdPath) ? fs17.readFileSync(skillMdPath, "utf-8") : "";
3104
- const skillMdRelative = path25.relative(
3591
+ const skillMdRelative = path29.relative(
3105
3592
  process.cwd(),
3106
- path25.join(skillsDir, entry.name, "SKILL.md")
3593
+ path29.join(skillsDir, entry.name, "SKILL.md")
3107
3594
  );
3108
- const skillYamlRelative = path25.relative(
3595
+ const skillYamlRelative = path29.relative(
3109
3596
  process.cwd(),
3110
- path25.join(skillsDir, entry.name, "skill.yaml")
3597
+ path29.join(skillsDir, entry.name, "skill.yaml")
3111
3598
  );
3112
3599
  const args = (meta.cli?.args ?? []).map((a) => ({
3113
3600
  name: a.name,
@@ -3277,7 +3764,7 @@ function renderGemini(spec, skillMdContent, skillYamlContent) {
3277
3764
 
3278
3765
  // src/slash-commands/sync.ts
3279
3766
  import fs18 from "fs";
3280
- import path26 from "path";
3767
+ import path30 from "path";
3281
3768
 
3282
3769
  // src/agent-definitions/constants.ts
3283
3770
  var GENERATED_HEADER_AGENT = "<!-- Generated by harness generate-agent-definitions. Do not edit. -->";
@@ -3289,7 +3776,7 @@ function computeSyncPlan(outputDir, rendered) {
3289
3776
  const removed = [];
3290
3777
  const unchanged = [];
3291
3778
  for (const [filename, content] of rendered) {
3292
- const filePath = path26.join(outputDir, filename);
3779
+ const filePath = path30.join(outputDir, filename);
3293
3780
  if (!fs18.existsSync(filePath)) {
3294
3781
  added.push(filename);
3295
3782
  } else {
@@ -3303,12 +3790,12 @@ function computeSyncPlan(outputDir, rendered) {
3303
3790
  }
3304
3791
  if (fs18.existsSync(outputDir)) {
3305
3792
  const existing = fs18.readdirSync(outputDir).filter((f) => {
3306
- const stat = fs18.statSync(path26.join(outputDir, f));
3793
+ const stat = fs18.statSync(path30.join(outputDir, f));
3307
3794
  return stat.isFile();
3308
3795
  });
3309
3796
  for (const filename of existing) {
3310
3797
  if (rendered.has(filename)) continue;
3311
- const content = fs18.readFileSync(path26.join(outputDir, filename), "utf-8");
3798
+ const content = fs18.readFileSync(path30.join(outputDir, filename), "utf-8");
3312
3799
  if (content.includes(GENERATED_HEADER_CLAUDE) || content.includes(GENERATED_HEADER_GEMINI) || content.includes(GENERATED_HEADER_AGENT)) {
3313
3800
  removed.push(filename);
3314
3801
  }
@@ -3321,12 +3808,12 @@ function applySyncPlan(outputDir, rendered, plan, deleteOrphans) {
3321
3808
  for (const filename of [...plan.added, ...plan.updated]) {
3322
3809
  const content = rendered.get(filename);
3323
3810
  if (content !== void 0) {
3324
- fs18.writeFileSync(path26.join(outputDir, filename), content);
3811
+ fs18.writeFileSync(path30.join(outputDir, filename), content);
3325
3812
  }
3326
3813
  }
3327
3814
  if (deleteOrphans) {
3328
3815
  for (const filename of plan.removed) {
3329
- const filePath = path26.join(outputDir, filename);
3816
+ const filePath = path30.join(outputDir, filename);
3330
3817
  if (fs18.existsSync(filePath)) {
3331
3818
  fs18.unlinkSync(filePath);
3332
3819
  }
@@ -3337,24 +3824,24 @@ function applySyncPlan(outputDir, rendered, plan, deleteOrphans) {
3337
3824
  // src/commands/generate-slash-commands.ts
3338
3825
  function resolveOutputDir(platform, opts) {
3339
3826
  if (opts.output) {
3340
- return path27.join(opts.output, "harness");
3827
+ return path31.join(opts.output, "harness");
3341
3828
  }
3342
3829
  if (opts.global) {
3343
3830
  const home = os2.homedir();
3344
- return platform === "claude-code" ? path27.join(home, ".claude", "commands", "harness") : path27.join(home, ".gemini", "commands", "harness");
3831
+ return platform === "claude-code" ? path31.join(home, ".claude", "commands", "harness") : path31.join(home, ".gemini", "commands", "harness");
3345
3832
  }
3346
- return platform === "claude-code" ? path27.join("agents", "commands", "claude-code", "harness") : path27.join("agents", "commands", "gemini-cli", "harness");
3833
+ return platform === "claude-code" ? path31.join("agents", "commands", "claude-code", "harness") : path31.join("agents", "commands", "gemini-cli", "harness");
3347
3834
  }
3348
3835
  function fileExtension(platform) {
3349
3836
  return platform === "claude-code" ? ".md" : ".toml";
3350
3837
  }
3351
3838
  async function confirmDeletion(files) {
3352
3839
  const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
3353
- return new Promise((resolve18) => {
3840
+ return new Promise((resolve22) => {
3354
3841
  rl.question(`
3355
3842
  Remove ${files.length} orphaned command(s)? (y/N) `, (answer) => {
3356
3843
  rl.close();
3357
- resolve18(answer.toLowerCase() === "y");
3844
+ resolve22(answer.toLowerCase() === "y");
3358
3845
  });
3359
3846
  });
3360
3847
  }
@@ -3369,7 +3856,7 @@ function generateSlashCommands(opts) {
3369
3856
  }
3370
3857
  if (opts.includeGlobal || skillSources.length === 0) {
3371
3858
  const globalDir = resolveGlobalSkillsDir();
3372
- if (!projectDir || path27.resolve(globalDir) !== path27.resolve(projectDir)) {
3859
+ if (!projectDir || path31.resolve(globalDir) !== path31.resolve(projectDir)) {
3373
3860
  skillSources.push({ dir: globalDir, source: "global" });
3374
3861
  }
3375
3862
  }
@@ -3391,7 +3878,7 @@ function generateSlashCommands(opts) {
3391
3878
  executionContext: spec.prompt.executionContext.split("\n").map((line) => {
3392
3879
  if (line.startsWith("@")) {
3393
3880
  const relPath = line.slice(1);
3394
- return `@${path27.resolve(relPath)}`;
3881
+ return `@${path31.resolve(relPath)}`;
3395
3882
  }
3396
3883
  return line;
3397
3884
  }).join("\n")
@@ -3399,8 +3886,8 @@ function generateSlashCommands(opts) {
3399
3886
  } : spec;
3400
3887
  rendered.set(filename, renderClaudeCode(renderSpec));
3401
3888
  } else {
3402
- const mdPath = path27.join(spec.skillsBaseDir, spec.sourceDir, "SKILL.md");
3403
- const yamlPath = path27.join(spec.skillsBaseDir, spec.sourceDir, "skill.yaml");
3889
+ const mdPath = path31.join(spec.skillsBaseDir, spec.sourceDir, "SKILL.md");
3890
+ const yamlPath = path31.join(spec.skillsBaseDir, spec.sourceDir, "skill.yaml");
3404
3891
  const mdContent = fs19.existsSync(mdPath) ? fs19.readFileSync(mdPath, "utf-8") : "";
3405
3892
  const yamlContent = fs19.existsSync(yamlPath) ? fs19.readFileSync(yamlPath, "utf-8") : "";
3406
3893
  rendered.set(filename, renderGemini(spec, mdContent, yamlContent));
@@ -3428,7 +3915,7 @@ async function handleOrphanDeletion(results, opts) {
3428
3915
  const shouldDelete = opts.yes || await confirmDeletion(result.removed);
3429
3916
  if (shouldDelete) {
3430
3917
  for (const filename of result.removed) {
3431
- const filePath = path27.join(result.outputDir, filename);
3918
+ const filePath = path31.join(result.outputDir, filename);
3432
3919
  if (fs19.existsSync(filePath)) {
3433
3920
  fs19.unlinkSync(filePath);
3434
3921
  }
@@ -3437,7 +3924,7 @@ async function handleOrphanDeletion(results, opts) {
3437
3924
  }
3438
3925
  }
3439
3926
  function createGenerateSlashCommandsCommand() {
3440
- return new Command28("generate-slash-commands").description(
3927
+ return new Command32("generate-slash-commands").description(
3441
3928
  "Generate native slash commands for Claude Code and Gemini CLI from skill metadata"
3442
3929
  ).option("--platforms <list>", "Target platforms (comma-separated)", "claude-code,gemini-cli").option("--global", "Write to global config directories", false).option("--include-global", "Include built-in global skills alongside project skills", false).option("--output <dir>", "Custom output directory").option("--skills-dir <path>", "Skills directory to scan").option("--dry-run", "Show what would change without writing", false).option("--yes", "Skip deletion confirmation prompts", false).action(async (opts, cmd) => {
3443
3930
  const globalOpts = cmd.optsWithGlobals();
@@ -3502,10 +3989,10 @@ ${result.platform} \u2192 ${result.outputDir}`);
3502
3989
  }
3503
3990
 
3504
3991
  // src/commands/ci/index.ts
3505
- import { Command as Command31 } from "commander";
3992
+ import { Command as Command35 } from "commander";
3506
3993
 
3507
3994
  // src/commands/ci/check.ts
3508
- import { Command as Command29 } from "commander";
3995
+ import { Command as Command33 } from "commander";
3509
3996
  import { runCIChecks } from "@harness-engineering/core";
3510
3997
  var VALID_CHECKS = ["validate", "deps", "docs", "entropy", "phase-gate"];
3511
3998
  async function runCICheck(options) {
@@ -3537,7 +4024,7 @@ function parseFailOn(failOn) {
3537
4024
  return "error";
3538
4025
  }
3539
4026
  function createCheckCommand() {
3540
- return new Command29("check").description("Run all harness checks for CI (validate, deps, docs, entropy, phase-gate)").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) => {
4027
+ return new Command33("check").description("Run all harness checks for CI (validate, deps, docs, entropy, phase-gate)").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) => {
3541
4028
  const globalOpts = cmd.optsWithGlobals();
3542
4029
  const mode = globalOpts.json ? OutputMode.JSON : globalOpts.quiet ? OutputMode.QUIET : globalOpts.verbose ? OutputMode.VERBOSE : OutputMode.TEXT;
3543
4030
  const skip = parseSkip(opts.skip);
@@ -3581,10 +4068,10 @@ function createCheckCommand() {
3581
4068
  }
3582
4069
 
3583
4070
  // src/commands/ci/init.ts
3584
- import { Command as Command30 } from "commander";
4071
+ import { Command as Command34 } from "commander";
3585
4072
  import * as fs20 from "fs";
3586
- import * as path28 from "path";
3587
- import { Ok as Ok17, Err as Err14 } from "@harness-engineering/core";
4073
+ import * as path32 from "path";
4074
+ import { Ok as Ok19, Err as Err14 } from "@harness-engineering/core";
3588
4075
  var ALL_CHECKS = ["validate", "deps", "docs", "entropy", "phase-gate"];
3589
4076
  function buildSkipFlag(checks) {
3590
4077
  if (!checks) return "";
@@ -3670,7 +4157,7 @@ function generateCIConfig(options) {
3670
4157
  if (!entry) {
3671
4158
  return Err14(new CLIError(`Unknown platform: ${platform}`, ExitCode.ERROR));
3672
4159
  }
3673
- return Ok17({
4160
+ return Ok19({
3674
4161
  filename: entry.filename,
3675
4162
  content: entry.generate(skipFlag)
3676
4163
  });
@@ -3681,7 +4168,7 @@ function detectPlatform() {
3681
4168
  return null;
3682
4169
  }
3683
4170
  function createInitCommand2() {
3684
- return new Command30("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) => {
4171
+ return new Command34("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) => {
3685
4172
  const globalOpts = cmd.optsWithGlobals();
3686
4173
  const platform = opts.platform ?? detectPlatform() ?? "generic";
3687
4174
  const checks = opts.checks ? opts.checks.split(",").map((s) => s.trim()) : void 0;
@@ -3693,8 +4180,8 @@ function createInitCommand2() {
3693
4180
  process.exit(result.error.exitCode);
3694
4181
  }
3695
4182
  const { filename, content } = result.value;
3696
- const targetPath = path28.resolve(filename);
3697
- const dir = path28.dirname(targetPath);
4183
+ const targetPath = path32.resolve(filename);
4184
+ const dir = path32.dirname(targetPath);
3698
4185
  fs20.mkdirSync(dir, { recursive: true });
3699
4186
  fs20.writeFileSync(targetPath, content);
3700
4187
  if (platform === "generic") {
@@ -3711,15 +4198,15 @@ function createInitCommand2() {
3711
4198
 
3712
4199
  // src/commands/ci/index.ts
3713
4200
  function createCICommand() {
3714
- const command = new Command31("ci").description("CI/CD integration commands");
4201
+ const command = new Command35("ci").description("CI/CD integration commands");
3715
4202
  command.addCommand(createCheckCommand());
3716
4203
  command.addCommand(createInitCommand2());
3717
4204
  return command;
3718
4205
  }
3719
4206
 
3720
4207
  // src/commands/update.ts
3721
- import { Command as Command32 } from "commander";
3722
- import { execSync as execSync3 } from "child_process";
4208
+ import { Command as Command36 } from "commander";
4209
+ import { execSync as execSync4 } from "child_process";
3723
4210
  import { realpathSync } from "fs";
3724
4211
  import readline3 from "readline";
3725
4212
  import chalk4 from "chalk";
@@ -3739,7 +4226,7 @@ function detectPackageManager() {
3739
4226
  return "npm";
3740
4227
  }
3741
4228
  function getLatestVersion(pkg = "@harness-engineering/cli") {
3742
- const output = execSync3(`npm view ${pkg} dist-tags.latest`, {
4229
+ const output = execSync4(`npm view ${pkg} dist-tags.latest`, {
3743
4230
  encoding: "utf-8",
3744
4231
  timeout: 15e3
3745
4232
  });
@@ -3747,7 +4234,7 @@ function getLatestVersion(pkg = "@harness-engineering/cli") {
3747
4234
  }
3748
4235
  function getInstalledVersion(pm) {
3749
4236
  try {
3750
- const output = execSync3(`${pm} list -g @harness-engineering/cli --json`, {
4237
+ const output = execSync4(`${pm} list -g @harness-engineering/cli --json`, {
3751
4238
  encoding: "utf-8",
3752
4239
  timeout: 15e3
3753
4240
  });
@@ -3760,7 +4247,7 @@ function getInstalledVersion(pm) {
3760
4247
  }
3761
4248
  function getInstalledPackages(pm) {
3762
4249
  try {
3763
- const output = execSync3(`${pm} list -g --json`, {
4250
+ const output = execSync4(`${pm} list -g --json`, {
3764
4251
  encoding: "utf-8",
3765
4252
  timeout: 15e3
3766
4253
  });
@@ -3776,15 +4263,15 @@ function prompt(question) {
3776
4263
  input: process.stdin,
3777
4264
  output: process.stdout
3778
4265
  });
3779
- return new Promise((resolve18) => {
4266
+ return new Promise((resolve22) => {
3780
4267
  rl.question(question, (answer) => {
3781
4268
  rl.close();
3782
- resolve18(answer.trim().toLowerCase());
4269
+ resolve22(answer.trim().toLowerCase());
3783
4270
  });
3784
4271
  });
3785
4272
  }
3786
4273
  function createUpdateCommand() {
3787
- return new Command32("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) => {
4274
+ return new Command36("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) => {
3788
4275
  const globalOpts = cmd.optsWithGlobals();
3789
4276
  const pm = detectPackageManager();
3790
4277
  if (globalOpts.verbose) {
@@ -3827,7 +4314,7 @@ function createUpdateCommand() {
3827
4314
  }
3828
4315
  try {
3829
4316
  logger.info("Updating packages...");
3830
- execSync3(installCmd, { stdio: "inherit", timeout: 12e4 });
4317
+ execSync4(installCmd, { stdio: "inherit", timeout: 12e4 });
3831
4318
  console.log("");
3832
4319
  logger.success("Update complete");
3833
4320
  } catch {
@@ -3837,15 +4324,15 @@ function createUpdateCommand() {
3837
4324
  process.exit(ExitCode.ERROR);
3838
4325
  }
3839
4326
  console.log("");
3840
- const regenAnswer = await prompt("Regenerate slash commands? (y/N) ");
4327
+ const regenAnswer = await prompt("Regenerate slash commands and agent definitions? (y/N) ");
3841
4328
  if (regenAnswer === "y" || regenAnswer === "yes") {
3842
4329
  const scopeAnswer = await prompt("Generate for (g)lobal or (l)ocal project? (g/l) ");
3843
4330
  const globalFlag = scopeAnswer === "g" || scopeAnswer === "global" ? " --global" : "";
3844
4331
  try {
3845
- execSync3(`harness generate-slash-commands${globalFlag}`, { stdio: "inherit" });
4332
+ execSync4(`harness generate${globalFlag}`, { stdio: "inherit" });
3846
4333
  } catch {
3847
- logger.warn("Slash command generation failed. Run manually:");
3848
- console.log(` ${chalk4.cyan(`harness generate-slash-commands${globalFlag}`)}`);
4334
+ logger.warn("Generation failed. Run manually:");
4335
+ console.log(` ${chalk4.cyan(`harness generate${globalFlag}`)}`);
3849
4336
  }
3850
4337
  }
3851
4338
  process.exit(ExitCode.SUCCESS);
@@ -3853,9 +4340,9 @@ function createUpdateCommand() {
3853
4340
  }
3854
4341
 
3855
4342
  // src/commands/generate-agent-definitions.ts
3856
- import { Command as Command33 } from "commander";
4343
+ import { Command as Command37 } from "commander";
3857
4344
  import * as fs21 from "fs";
3858
- import * as path29 from "path";
4345
+ import * as path33 from "path";
3859
4346
  import * as os3 from "os";
3860
4347
 
3861
4348
  // src/agent-definitions/generator.ts
@@ -3991,17 +4478,17 @@ function renderGeminiAgent(def) {
3991
4478
  // src/commands/generate-agent-definitions.ts
3992
4479
  function resolveOutputDir2(platform, opts) {
3993
4480
  if (opts.output) {
3994
- return platform === "claude-code" ? path29.join(opts.output, "claude-code") : path29.join(opts.output, "gemini-cli");
4481
+ return platform === "claude-code" ? path33.join(opts.output, "claude-code") : path33.join(opts.output, "gemini-cli");
3995
4482
  }
3996
4483
  if (opts.global) {
3997
4484
  const home = os3.homedir();
3998
- return platform === "claude-code" ? path29.join(home, ".claude", "agents") : path29.join(home, ".gemini", "agents");
4485
+ return platform === "claude-code" ? path33.join(home, ".claude", "agents") : path33.join(home, ".gemini", "agents");
3999
4486
  }
4000
- return platform === "claude-code" ? path29.join("agents", "agents", "claude-code") : path29.join("agents", "agents", "gemini-cli");
4487
+ return platform === "claude-code" ? path33.join("agents", "agents", "claude-code") : path33.join("agents", "agents", "gemini-cli");
4001
4488
  }
4002
4489
  function loadSkillContent(skillName) {
4003
4490
  const skillsDir = resolveSkillsDir();
4004
- const skillMdPath = path29.join(skillsDir, skillName, "SKILL.md");
4491
+ const skillMdPath = path33.join(skillsDir, skillName, "SKILL.md");
4005
4492
  if (!fs21.existsSync(skillMdPath)) return null;
4006
4493
  return fs21.readFileSync(skillMdPath, "utf-8");
4007
4494
  }
@@ -4047,7 +4534,7 @@ function generateAgentDefinitions(opts) {
4047
4534
  return results;
4048
4535
  }
4049
4536
  function createGenerateAgentDefinitionsCommand() {
4050
- return new Command33("generate-agent-definitions").description("Generate agent definition files from personas for Claude Code and Gemini CLI").option("--platforms <list>", "Target platforms (comma-separated)", "claude-code,gemini-cli").option("--global", "Write to global agent directories", false).option("--output <dir>", "Custom output directory").option("--dry-run", "Show what would change without writing", false).action(async (opts, cmd) => {
4537
+ return new Command37("generate-agent-definitions").description("Generate agent definition files from personas for Claude Code and Gemini CLI").option("--platforms <list>", "Target platforms (comma-separated)", "claude-code,gemini-cli").option("--global", "Write to global agent directories", false).option("--output <dir>", "Custom output directory").option("--dry-run", "Show what would change without writing", false).action(async (opts, cmd) => {
4051
4538
  const globalOpts = cmd.optsWithGlobals();
4052
4539
  const platforms = opts.platforms.split(",").map((p) => p.trim());
4053
4540
  for (const p of platforms) {
@@ -4095,9 +4582,9 @@ ${result.platform} \u2192 ${result.outputDir}`);
4095
4582
  }
4096
4583
 
4097
4584
  // src/commands/generate.ts
4098
- import { Command as Command34 } from "commander";
4585
+ import { Command as Command38 } from "commander";
4099
4586
  function createGenerateCommand3() {
4100
- return new Command34("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) => {
4587
+ return new Command38("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) => {
4101
4588
  const globalOpts = cmd.optsWithGlobals();
4102
4589
  const platforms = opts.platforms.split(",").map((p) => p.trim());
4103
4590
  for (const p of platforms) {
@@ -4156,8 +4643,8 @@ function createGenerateCommand3() {
4156
4643
  }
4157
4644
 
4158
4645
  // src/commands/graph/scan.ts
4159
- import { Command as Command35 } from "commander";
4160
- import * as path30 from "path";
4646
+ import { Command as Command39 } from "commander";
4647
+ import * as path34 from "path";
4161
4648
  async function runScan(projectPath) {
4162
4649
  const { GraphStore, CodeIngestor, TopologicalLinker, KnowledgeIngestor, GitIngestor } = await import("@harness-engineering/graph");
4163
4650
  const store = new GraphStore();
@@ -4170,13 +4657,13 @@ async function runScan(projectPath) {
4170
4657
  await new GitIngestor(store).ingest(projectPath);
4171
4658
  } catch {
4172
4659
  }
4173
- const graphDir = path30.join(projectPath, ".harness", "graph");
4660
+ const graphDir = path34.join(projectPath, ".harness", "graph");
4174
4661
  await store.save(graphDir);
4175
4662
  return { nodeCount: store.nodeCount, edgeCount: store.edgeCount, durationMs: Date.now() - start };
4176
4663
  }
4177
4664
  function createScanCommand() {
4178
- return new Command35("scan").description("Scan project and build knowledge graph").argument("[path]", "Project root path", ".").action(async (inputPath, _opts, cmd) => {
4179
- const projectPath = path30.resolve(inputPath);
4665
+ return new Command39("scan").description("Scan project and build knowledge graph").argument("[path]", "Project root path", ".").action(async (inputPath, _opts, cmd) => {
4666
+ const projectPath = path34.resolve(inputPath);
4180
4667
  const globalOpts = cmd.optsWithGlobals();
4181
4668
  try {
4182
4669
  const result = await runScan(projectPath);
@@ -4195,12 +4682,12 @@ function createScanCommand() {
4195
4682
  }
4196
4683
 
4197
4684
  // src/commands/graph/ingest.ts
4198
- import { Command as Command36 } from "commander";
4199
- import * as path31 from "path";
4685
+ import { Command as Command40 } from "commander";
4686
+ import * as path35 from "path";
4200
4687
  async function loadConnectorConfig(projectPath, source) {
4201
4688
  try {
4202
4689
  const fs22 = await import("fs/promises");
4203
- const configPath = path31.join(projectPath, "harness.config.json");
4690
+ const configPath = path35.join(projectPath, "harness.config.json");
4204
4691
  const config = JSON.parse(await fs22.readFile(configPath, "utf-8"));
4205
4692
  const connector = config.graph?.connectors?.find(
4206
4693
  (c) => c.source === source
@@ -4241,7 +4728,7 @@ async function runIngest(projectPath, source, opts) {
4241
4728
  JiraConnector,
4242
4729
  SlackConnector
4243
4730
  } = await import("@harness-engineering/graph");
4244
- const graphDir = path31.join(projectPath, ".harness", "graph");
4731
+ const graphDir = path35.join(projectPath, ".harness", "graph");
4245
4732
  const store = new GraphStore();
4246
4733
  await store.load(graphDir);
4247
4734
  if (opts?.all) {
@@ -4302,13 +4789,13 @@ async function runIngest(projectPath, source, opts) {
4302
4789
  return result;
4303
4790
  }
4304
4791
  function createIngestCommand() {
4305
- return new Command36("ingest").description("Ingest data into the knowledge graph").option("--source <name>", "Source to ingest (code, knowledge, git, jira, slack)").option("--all", "Run all sources (code, knowledge, git, and configured connectors)").option("--full", "Force full re-ingestion").action(async (opts, cmd) => {
4792
+ return new Command40("ingest").description("Ingest data into the knowledge graph").option("--source <name>", "Source to ingest (code, knowledge, git, jira, slack)").option("--all", "Run all sources (code, knowledge, git, and configured connectors)").option("--full", "Force full re-ingestion").action(async (opts, cmd) => {
4306
4793
  if (!opts.source && !opts.all) {
4307
4794
  console.error("Error: --source or --all is required");
4308
4795
  process.exit(1);
4309
4796
  }
4310
4797
  const globalOpts = cmd.optsWithGlobals();
4311
- const projectPath = path31.resolve(globalOpts.config ? path31.dirname(globalOpts.config) : ".");
4798
+ const projectPath = path35.resolve(globalOpts.config ? path35.dirname(globalOpts.config) : ".");
4312
4799
  try {
4313
4800
  const result = await runIngest(projectPath, opts.source ?? "", {
4314
4801
  full: opts.full,
@@ -4330,12 +4817,12 @@ function createIngestCommand() {
4330
4817
  }
4331
4818
 
4332
4819
  // src/commands/graph/query.ts
4333
- import { Command as Command37 } from "commander";
4334
- import * as path32 from "path";
4820
+ import { Command as Command41 } from "commander";
4821
+ import * as path36 from "path";
4335
4822
  async function runQuery(projectPath, rootNodeId, opts) {
4336
4823
  const { GraphStore, ContextQL } = await import("@harness-engineering/graph");
4337
4824
  const store = new GraphStore();
4338
- const graphDir = path32.join(projectPath, ".harness", "graph");
4825
+ const graphDir = path36.join(projectPath, ".harness", "graph");
4339
4826
  const loaded = await store.load(graphDir);
4340
4827
  if (!loaded) throw new Error("No graph found. Run `harness scan` first.");
4341
4828
  const params = {
@@ -4349,9 +4836,9 @@ async function runQuery(projectPath, rootNodeId, opts) {
4349
4836
  return cql.execute(params);
4350
4837
  }
4351
4838
  function createQueryCommand() {
4352
- return new Command37("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) => {
4839
+ return new Command41("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) => {
4353
4840
  const globalOpts = cmd.optsWithGlobals();
4354
- const projectPath = path32.resolve(globalOpts.config ? path32.dirname(globalOpts.config) : ".");
4841
+ const projectPath = path36.resolve(globalOpts.config ? path36.dirname(globalOpts.config) : ".");
4355
4842
  try {
4356
4843
  const result = await runQuery(projectPath, rootNodeId, {
4357
4844
  depth: parseInt(opts.depth),
@@ -4377,18 +4864,18 @@ function createQueryCommand() {
4377
4864
  }
4378
4865
 
4379
4866
  // src/commands/graph/index.ts
4380
- import { Command as Command38 } from "commander";
4867
+ import { Command as Command42 } from "commander";
4381
4868
 
4382
4869
  // src/commands/graph/status.ts
4383
- import * as path33 from "path";
4870
+ import * as path37 from "path";
4384
4871
  async function runGraphStatus(projectPath) {
4385
4872
  const { GraphStore } = await import("@harness-engineering/graph");
4386
- const graphDir = path33.join(projectPath, ".harness", "graph");
4873
+ const graphDir = path37.join(projectPath, ".harness", "graph");
4387
4874
  const store = new GraphStore();
4388
4875
  const loaded = await store.load(graphDir);
4389
4876
  if (!loaded) return { status: "no_graph", message: "No graph found. Run `harness scan` first." };
4390
4877
  const fs22 = await import("fs/promises");
4391
- const metaPath = path33.join(graphDir, "metadata.json");
4878
+ const metaPath = path37.join(graphDir, "metadata.json");
4392
4879
  let lastScan = "unknown";
4393
4880
  try {
4394
4881
  const meta = JSON.parse(await fs22.readFile(metaPath, "utf-8"));
@@ -4402,7 +4889,7 @@ async function runGraphStatus(projectPath) {
4402
4889
  }
4403
4890
  let connectorSyncStatus = {};
4404
4891
  try {
4405
- const syncMetaPath = path33.join(graphDir, "sync-metadata.json");
4892
+ const syncMetaPath = path37.join(graphDir, "sync-metadata.json");
4406
4893
  const syncMeta = JSON.parse(await fs22.readFile(syncMetaPath, "utf-8"));
4407
4894
  for (const [name, data] of Object.entries(syncMeta.connectors ?? {})) {
4408
4895
  connectorSyncStatus[name] = data.lastSyncTimestamp;
@@ -4420,10 +4907,10 @@ async function runGraphStatus(projectPath) {
4420
4907
  }
4421
4908
 
4422
4909
  // src/commands/graph/export.ts
4423
- import * as path34 from "path";
4910
+ import * as path38 from "path";
4424
4911
  async function runGraphExport(projectPath, format) {
4425
4912
  const { GraphStore } = await import("@harness-engineering/graph");
4426
- const graphDir = path34.join(projectPath, ".harness", "graph");
4913
+ const graphDir = path38.join(projectPath, ".harness", "graph");
4427
4914
  const store = new GraphStore();
4428
4915
  const loaded = await store.load(graphDir);
4429
4916
  if (!loaded) throw new Error("No graph found. Run `harness scan` first.");
@@ -4452,13 +4939,13 @@ async function runGraphExport(projectPath, format) {
4452
4939
  }
4453
4940
 
4454
4941
  // src/commands/graph/index.ts
4455
- import * as path35 from "path";
4942
+ import * as path39 from "path";
4456
4943
  function createGraphCommand() {
4457
- const graph = new Command38("graph").description("Knowledge graph management");
4944
+ const graph = new Command42("graph").description("Knowledge graph management");
4458
4945
  graph.command("status").description("Show graph statistics").action(async (_opts, cmd) => {
4459
4946
  try {
4460
4947
  const globalOpts = cmd.optsWithGlobals();
4461
- const projectPath = path35.resolve(globalOpts.config ? path35.dirname(globalOpts.config) : ".");
4948
+ const projectPath = path39.resolve(globalOpts.config ? path39.dirname(globalOpts.config) : ".");
4462
4949
  const result = await runGraphStatus(projectPath);
4463
4950
  if (globalOpts.json) {
4464
4951
  console.log(JSON.stringify(result, null, 2));
@@ -4485,7 +4972,7 @@ function createGraphCommand() {
4485
4972
  });
4486
4973
  graph.command("export").description("Export graph").requiredOption("--format <format>", "Output format (json, mermaid)").action(async (opts, cmd) => {
4487
4974
  const globalOpts = cmd.optsWithGlobals();
4488
- const projectPath = path35.resolve(globalOpts.config ? path35.dirname(globalOpts.config) : ".");
4975
+ const projectPath = path39.resolve(globalOpts.config ? path39.dirname(globalOpts.config) : ".");
4489
4976
  try {
4490
4977
  const output = await runGraphExport(projectPath, opts.format);
4491
4978
  console.log(output);
@@ -4499,11 +4986,14 @@ function createGraphCommand() {
4499
4986
 
4500
4987
  // src/index.ts
4501
4988
  function createProgram() {
4502
- const program = new Command39();
4989
+ const program = new Command43();
4503
4990
  program.name("harness").description("CLI for Harness Engineering toolkit").version(VERSION).option("-c, --config <path>", "Path to config file").option("--json", "Output as JSON").option("--verbose", "Verbose output").option("--quiet", "Minimal output");
4504
4991
  program.addCommand(createValidateCommand());
4505
4992
  program.addCommand(createCheckDepsCommand());
4506
4993
  program.addCommand(createCheckDocsCommand());
4994
+ program.addCommand(createCheckPerfCommand());
4995
+ program.addCommand(createCheckSecurityCommand());
4996
+ program.addCommand(createPerfCommand());
4507
4997
  program.addCommand(createInitCommand());
4508
4998
  program.addCommand(createCleanupCommand());
4509
4999
  program.addCommand(createFixDriftCommand());