@harness-engineering/core 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -18,17 +18,17 @@ import { promisify } from "util";
18
18
  import { glob } from "glob";
19
19
  var accessAsync = promisify(access);
20
20
  var readFileAsync = promisify(readFile);
21
- async function fileExists(path2) {
21
+ async function fileExists(path3) {
22
22
  try {
23
- await accessAsync(path2, constants.F_OK);
23
+ await accessAsync(path3, constants.F_OK);
24
24
  return true;
25
25
  } catch {
26
26
  return false;
27
27
  }
28
28
  }
29
- async function readFileContent(path2) {
29
+ async function readFileContent(path3) {
30
30
  try {
31
- const content = await readFileAsync(path2, "utf-8");
31
+ const content = await readFileAsync(path3, "utf-8");
32
32
  return Ok(content);
33
33
  } catch (error) {
34
34
  return Err(error);
@@ -76,15 +76,15 @@ function validateConfig(data, schema) {
76
76
  let message = "Configuration validation failed";
77
77
  const suggestions = [];
78
78
  if (firstError) {
79
- const path2 = firstError.path.join(".");
80
- const pathDisplay = path2 ? ` at "${path2}"` : "";
79
+ const path3 = firstError.path.join(".");
80
+ const pathDisplay = path3 ? ` at "${path3}"` : "";
81
81
  if (firstError.code === "invalid_type") {
82
82
  const received = firstError.received;
83
83
  const expected = firstError.expected;
84
84
  if (received === "undefined") {
85
85
  code = "MISSING_FIELD";
86
86
  message = `Missing required field${pathDisplay}: ${firstError.message}`;
87
- suggestions.push(`Field "${path2}" is required and must be of type "${expected}"`);
87
+ suggestions.push(`Field "${path3}" is required and must be of type "${expected}"`);
88
88
  } else {
89
89
  code = "INVALID_TYPE";
90
90
  message = `Invalid type${pathDisplay}: ${firstError.message}`;
@@ -297,27 +297,30 @@ function extractSections(content) {
297
297
  return result;
298
298
  });
299
299
  }
300
- function isExternalLink(path2) {
301
- return path2.startsWith("http://") || path2.startsWith("https://") || path2.startsWith("#") || path2.startsWith("mailto:");
300
+ function isExternalLink(path3) {
301
+ return path3.startsWith("http://") || path3.startsWith("https://") || path3.startsWith("#") || path3.startsWith("mailto:");
302
302
  }
303
303
  function resolveLinkPath(linkPath, baseDir) {
304
304
  return linkPath.startsWith(".") ? join(baseDir, linkPath) : linkPath;
305
305
  }
306
- async function validateAgentsMap(path2 = "./AGENTS.md") {
307
- const contentResult = await readFileContent(path2);
306
+ async function validateAgentsMap(path3 = "./AGENTS.md") {
307
+ console.warn(
308
+ "[harness] validateAgentsMap() is deprecated. Use graph-based validation via Assembler.checkCoverage() from @harness-engineering/graph"
309
+ );
310
+ const contentResult = await readFileContent(path3);
308
311
  if (!contentResult.ok) {
309
312
  return Err(
310
313
  createError(
311
314
  "PARSE_ERROR",
312
315
  `Failed to read AGENTS.md: ${contentResult.error.message}`,
313
- { path: path2 },
316
+ { path: path3 },
314
317
  ["Ensure the file exists", "Check file permissions"]
315
318
  )
316
319
  );
317
320
  }
318
321
  const content = contentResult.value;
319
322
  const sections = extractSections(content);
320
- const baseDir = dirname(path2);
323
+ const baseDir = dirname(path3);
321
324
  const sectionTitles = sections.map((s) => s.title);
322
325
  const missingSections = REQUIRED_SECTIONS.filter(
323
326
  (required) => !sectionTitles.some((title) => title.toLowerCase().includes(required.toLowerCase()))
@@ -379,7 +382,21 @@ function suggestSection(filePath, domain) {
379
382
  return `${domain} Reference`;
380
383
  }
381
384
  async function checkDocCoverage(domain, options = {}) {
382
- const { docsDir = "./docs", sourceDir = "./src", excludePatterns = [] } = options;
385
+ const { docsDir = "./docs", sourceDir = "./src", excludePatterns = [], graphCoverage } = options;
386
+ if (graphCoverage) {
387
+ const gaps = graphCoverage.undocumented.map((file) => ({
388
+ file,
389
+ suggestedSection: suggestSection(file, domain),
390
+ importance: determineImportance(file)
391
+ }));
392
+ return Ok({
393
+ domain,
394
+ documented: graphCoverage.documented,
395
+ undocumented: graphCoverage.undocumented,
396
+ coveragePercentage: graphCoverage.coveragePercentage,
397
+ gaps
398
+ });
399
+ }
383
400
  try {
384
401
  const sourceFiles = await findFiles("**/*.{ts,js,tsx,jsx}", sourceDir);
385
402
  const filteredSourceFiles = sourceFiles.filter((file) => {
@@ -445,8 +462,8 @@ async function checkDocCoverage(domain, options = {}) {
445
462
 
446
463
  // src/context/knowledge-map.ts
447
464
  import { join as join2, basename as basename2, relative as relative2 } from "path";
448
- function suggestFix(path2, existingFiles) {
449
- const targetName = basename2(path2).toLowerCase();
465
+ function suggestFix(path3, existingFiles) {
466
+ const targetName = basename2(path3).toLowerCase();
450
467
  const similar = existingFiles.find((file) => {
451
468
  const fileName = basename2(file).toLowerCase();
452
469
  return fileName.includes(targetName) || targetName.includes(fileName);
@@ -454,9 +471,12 @@ function suggestFix(path2, existingFiles) {
454
471
  if (similar) {
455
472
  return `Did you mean "${similar}"?`;
456
473
  }
457
- return `Create the file "${path2}" or remove the link`;
474
+ return `Create the file "${path3}" or remove the link`;
458
475
  }
459
476
  async function validateKnowledgeMap(rootDir = process.cwd()) {
477
+ console.warn(
478
+ "[harness] validateKnowledgeMap() is deprecated. Use graph-based validation via Assembler.checkCoverage() from @harness-engineering/graph"
479
+ );
460
480
  const agentsPath = join2(rootDir, "AGENTS.md");
461
481
  const agentsResult = await validateAgentsMap(agentsPath);
462
482
  if (!agentsResult.ok) {
@@ -530,18 +550,9 @@ function matchesExcludePattern(relativePath, excludePatterns) {
530
550
  return regex.test(relativePath);
531
551
  });
532
552
  }
533
- async function generateAgentsMap(config) {
553
+ async function generateAgentsMap(config, graphSections) {
534
554
  const { rootDir, includePaths, excludePaths, sections = DEFAULT_SECTIONS } = config;
535
555
  try {
536
- const allFiles = [];
537
- for (const pattern of includePaths) {
538
- const files = await findFiles(pattern, rootDir);
539
- allFiles.push(...files);
540
- }
541
- const filteredFiles = allFiles.filter((file) => {
542
- const relativePath = relative3(rootDir, file);
543
- return !matchesExcludePattern(relativePath, excludePaths);
544
- });
545
556
  const lines = [];
546
557
  lines.push("# AI Agent Knowledge Map");
547
558
  lines.push("");
@@ -551,41 +562,68 @@ async function generateAgentsMap(config) {
551
562
  lines.push("");
552
563
  lines.push("> Add a brief description of this project, its purpose, and key technologies.");
553
564
  lines.push("");
554
- lines.push("## Repository Structure");
555
- lines.push("");
556
- const grouped = groupByDirectory(filteredFiles, rootDir);
557
- for (const [dir, files] of grouped) {
558
- if (dir !== ".") {
559
- lines.push(`### ${dir}/`);
565
+ if (graphSections) {
566
+ for (const section of graphSections) {
567
+ lines.push(`## ${section.name}`);
560
568
  lines.push("");
561
- }
562
- for (const file of files.slice(0, 10)) {
563
- lines.push(formatFileLink(file));
564
- }
565
- if (files.length > 10) {
566
- lines.push(`- _... and ${files.length - 10} more files_`);
567
- }
568
- lines.push("");
569
- }
570
- for (const section of sections) {
571
- lines.push(`## ${section.name}`);
572
- lines.push("");
573
- if (section.description) {
574
- lines.push(section.description);
569
+ if (section.description) {
570
+ lines.push(section.description);
571
+ lines.push("");
572
+ }
573
+ for (const file of section.files.slice(0, 20)) {
574
+ lines.push(formatFileLink(file));
575
+ }
576
+ if (section.files.length > 20) {
577
+ lines.push(`- _... and ${section.files.length - 20} more files_`);
578
+ }
575
579
  lines.push("");
576
580
  }
577
- const sectionFiles = await findFiles(section.pattern, rootDir);
578
- const filteredSectionFiles = sectionFiles.filter((file) => {
581
+ } else {
582
+ const allFiles = [];
583
+ for (const pattern of includePaths) {
584
+ const files = await findFiles(pattern, rootDir);
585
+ allFiles.push(...files);
586
+ }
587
+ const filteredFiles = allFiles.filter((file) => {
579
588
  const relativePath = relative3(rootDir, file);
580
589
  return !matchesExcludePattern(relativePath, excludePaths);
581
590
  });
582
- for (const file of filteredSectionFiles.slice(0, 20)) {
583
- lines.push(formatFileLink(relative3(rootDir, file)));
591
+ lines.push("## Repository Structure");
592
+ lines.push("");
593
+ const grouped = groupByDirectory(filteredFiles, rootDir);
594
+ for (const [dir, files] of grouped) {
595
+ if (dir !== ".") {
596
+ lines.push(`### ${dir}/`);
597
+ lines.push("");
598
+ }
599
+ for (const file of files.slice(0, 10)) {
600
+ lines.push(formatFileLink(file));
601
+ }
602
+ if (files.length > 10) {
603
+ lines.push(`- _... and ${files.length - 10} more files_`);
604
+ }
605
+ lines.push("");
584
606
  }
585
- if (filteredSectionFiles.length > 20) {
586
- lines.push(`- _... and ${filteredSectionFiles.length - 20} more files_`);
607
+ for (const section of sections) {
608
+ lines.push(`## ${section.name}`);
609
+ lines.push("");
610
+ if (section.description) {
611
+ lines.push(section.description);
612
+ lines.push("");
613
+ }
614
+ const sectionFiles = await findFiles(section.pattern, rootDir);
615
+ const filteredSectionFiles = sectionFiles.filter((file) => {
616
+ const relativePath = relative3(rootDir, file);
617
+ return !matchesExcludePattern(relativePath, excludePaths);
618
+ });
619
+ for (const file of filteredSectionFiles.slice(0, 20)) {
620
+ lines.push(formatFileLink(relative3(rootDir, file)));
621
+ }
622
+ if (filteredSectionFiles.length > 20) {
623
+ lines.push(`- _... and ${filteredSectionFiles.length - 20} more files_`);
624
+ }
625
+ lines.push("");
587
626
  }
588
- lines.push("");
589
627
  }
590
628
  lines.push("## Development Workflow");
591
629
  lines.push("");
@@ -615,7 +653,21 @@ var DEFAULT_RATIOS = {
615
653
  interfaces: 0.1,
616
654
  reserve: 0.1
617
655
  };
618
- function contextBudget(totalTokens, overrides) {
656
+ var NODE_TYPE_TO_CATEGORY = {
657
+ file: "activeCode",
658
+ function: "activeCode",
659
+ class: "activeCode",
660
+ method: "activeCode",
661
+ interface: "interfaces",
662
+ variable: "interfaces",
663
+ adr: "projectManifest",
664
+ document: "projectManifest",
665
+ spec: "taskSpec",
666
+ task: "taskSpec",
667
+ prompt: "systemPrompt",
668
+ system: "systemPrompt"
669
+ };
670
+ function contextBudget(totalTokens, overrides, graphDensity) {
619
671
  const ratios = {
620
672
  systemPrompt: DEFAULT_RATIOS.systemPrompt,
621
673
  projectManifest: DEFAULT_RATIOS.projectManifest,
@@ -624,6 +676,52 @@ function contextBudget(totalTokens, overrides) {
624
676
  interfaces: DEFAULT_RATIOS.interfaces,
625
677
  reserve: DEFAULT_RATIOS.reserve
626
678
  };
679
+ if (graphDensity) {
680
+ const categoryWeights = {
681
+ systemPrompt: 0,
682
+ projectManifest: 0,
683
+ taskSpec: 0,
684
+ activeCode: 0,
685
+ interfaces: 0,
686
+ reserve: 0
687
+ };
688
+ for (const [nodeType, count] of Object.entries(graphDensity)) {
689
+ const category = NODE_TYPE_TO_CATEGORY[nodeType];
690
+ if (category) {
691
+ categoryWeights[category] += count;
692
+ }
693
+ }
694
+ const totalWeight = Object.values(categoryWeights).reduce((sum, w) => sum + w, 0);
695
+ if (totalWeight > 0) {
696
+ const MIN_ALLOCATION = 0.01;
697
+ for (const key of Object.keys(ratios)) {
698
+ if (categoryWeights[key] > 0) {
699
+ ratios[key] = categoryWeights[key] / totalWeight;
700
+ } else {
701
+ ratios[key] = MIN_ALLOCATION;
702
+ }
703
+ }
704
+ if (ratios.reserve < DEFAULT_RATIOS.reserve) {
705
+ ratios.reserve = DEFAULT_RATIOS.reserve;
706
+ }
707
+ if (ratios.systemPrompt < DEFAULT_RATIOS.systemPrompt) {
708
+ ratios.systemPrompt = DEFAULT_RATIOS.systemPrompt;
709
+ }
710
+ const ratioSum = Object.values(ratios).reduce((sum, r) => sum + r, 0);
711
+ for (const key of Object.keys(ratios)) {
712
+ ratios[key] = ratios[key] / ratioSum;
713
+ }
714
+ for (const key of Object.keys(ratios)) {
715
+ if (ratios[key] < MIN_ALLOCATION) {
716
+ ratios[key] = MIN_ALLOCATION;
717
+ }
718
+ }
719
+ const finalSum = Object.values(ratios).reduce((sum, r) => sum + r, 0);
720
+ for (const key of Object.keys(ratios)) {
721
+ ratios[key] = ratios[key] / finalSum;
722
+ }
723
+ }
724
+ }
627
725
  if (overrides) {
628
726
  let overrideSum = 0;
629
727
  const overrideKeys = [];
@@ -637,12 +735,12 @@ function contextBudget(totalTokens, overrides) {
637
735
  }
638
736
  if (overrideKeys.length > 0 && overrideKeys.length < 6) {
639
737
  const remaining = 1 - overrideSum;
640
- const nonOverridden = Object.keys(DEFAULT_RATIOS).filter(
738
+ const nonOverridden = Object.keys(ratios).filter(
641
739
  (k) => !overrideKeys.includes(k)
642
740
  );
643
- const originalSum = nonOverridden.reduce((sum, k) => sum + DEFAULT_RATIOS[k], 0);
741
+ const originalSum = nonOverridden.reduce((sum, k) => sum + ratios[k], 0);
644
742
  for (const k of nonOverridden) {
645
- ratios[k] = remaining * (DEFAULT_RATIOS[k] / originalSum);
743
+ ratios[k] = remaining * (ratios[k] / originalSum);
646
744
  }
647
745
  }
648
746
  }
@@ -693,7 +791,7 @@ var PHASE_PRIORITIES = {
693
791
  { category: "config", patterns: ["harness.config.json", "package.json"], priority: 5 }
694
792
  ]
695
793
  };
696
- function contextFilter(phase, maxCategories) {
794
+ function contextFilter(phase, maxCategories, graphFilePaths) {
697
795
  const categories = PHASE_PRIORITIES[phase];
698
796
  const limit = maxCategories ?? categories.length;
699
797
  const included = categories.slice(0, limit);
@@ -702,7 +800,7 @@ function contextFilter(phase, maxCategories) {
702
800
  phase,
703
801
  includedCategories: included.map((c) => c.category),
704
802
  excludedCategories: excluded.map((c) => c.category),
705
- filePatterns: included.flatMap((c) => c.patterns)
803
+ filePatterns: graphFilePaths ?? included.flatMap((c) => c.patterns)
706
804
  };
707
805
  }
708
806
  function getPhaseCategories(phase) {
@@ -746,7 +844,13 @@ function getImportType(imp) {
746
844
  if (imp.kind === "type") return "type-only";
747
845
  return "static";
748
846
  }
749
- async function buildDependencyGraph(files, parser) {
847
+ async function buildDependencyGraph(files, parser, graphDependencyData) {
848
+ if (graphDependencyData) {
849
+ return Ok({
850
+ nodes: graphDependencyData.nodes,
851
+ edges: graphDependencyData.edges
852
+ });
853
+ }
750
854
  const nodes = [...files];
751
855
  const edges = [];
752
856
  for (const file of files) {
@@ -796,7 +900,19 @@ function checkLayerViolations(graph, layers, rootDir) {
796
900
  return violations;
797
901
  }
798
902
  async function validateDependencies(config) {
799
- const { layers, rootDir, parser, fallbackBehavior = "error" } = config;
903
+ const { layers, rootDir, parser, fallbackBehavior = "error", graphDependencyData } = config;
904
+ if (graphDependencyData) {
905
+ const graphResult2 = await buildDependencyGraph([], parser, graphDependencyData);
906
+ if (!graphResult2.ok) {
907
+ return Err(graphResult2.error);
908
+ }
909
+ const violations2 = checkLayerViolations(graphResult2.value, layers, rootDir);
910
+ return Ok({
911
+ valid: violations2.length === 0,
912
+ violations: violations2,
913
+ graph: graphResult2.value
914
+ });
915
+ }
800
916
  const healthResult = await parser.health();
801
917
  if (!healthResult.ok || !healthResult.value.available) {
802
918
  if (fallbackBehavior === "skip") {
@@ -930,8 +1046,8 @@ function detectCircularDeps(graph) {
930
1046
  largestCycle
931
1047
  });
932
1048
  }
933
- async function detectCircularDepsInFiles(files, parser) {
934
- const graphResult = await buildDependencyGraph(files, parser);
1049
+ async function detectCircularDepsInFiles(files, parser, graphDependencyData) {
1050
+ const graphResult = await buildDependencyGraph(files, parser, graphDependencyData);
935
1051
  if (!graphResult.ok) {
936
1052
  return graphResult;
937
1053
  }
@@ -949,8 +1065,8 @@ function createBoundaryValidator(schema, name) {
949
1065
  return Ok(result.data);
950
1066
  }
951
1067
  const suggestions = result.error.issues.map((issue) => {
952
- const path2 = issue.path.join(".");
953
- return path2 ? `${path2}: ${issue.message}` : issue.message;
1068
+ const path3 = issue.path.join(".");
1069
+ return path3 ? `${path3}: ${issue.message}` : issue.message;
954
1070
  });
955
1071
  return Err(
956
1072
  createError(
@@ -1019,11 +1135,11 @@ function walk(node, visitor) {
1019
1135
  var TypeScriptParser = class {
1020
1136
  name = "typescript";
1021
1137
  extensions = [".ts", ".tsx", ".mts", ".cts"];
1022
- async parseFile(path2) {
1023
- const contentResult = await readFileContent(path2);
1138
+ async parseFile(path3) {
1139
+ const contentResult = await readFileContent(path3);
1024
1140
  if (!contentResult.ok) {
1025
1141
  return Err(
1026
- createParseError("NOT_FOUND", `File not found: ${path2}`, { path: path2 }, [
1142
+ createParseError("NOT_FOUND", `File not found: ${path3}`, { path: path3 }, [
1027
1143
  "Check that the file exists",
1028
1144
  "Verify the path is correct"
1029
1145
  ])
@@ -1033,7 +1149,7 @@ var TypeScriptParser = class {
1033
1149
  const ast = parse(contentResult.value, {
1034
1150
  loc: true,
1035
1151
  range: true,
1036
- jsx: path2.endsWith(".tsx"),
1152
+ jsx: path3.endsWith(".tsx"),
1037
1153
  errorOnUnknownASTType: false
1038
1154
  });
1039
1155
  return Ok({
@@ -1044,7 +1160,7 @@ var TypeScriptParser = class {
1044
1160
  } catch (e) {
1045
1161
  const error = e;
1046
1162
  return Err(
1047
- createParseError("SYNTAX_ERROR", `Failed to parse ${path2}: ${error.message}`, { path: path2 }, [
1163
+ createParseError("SYNTAX_ERROR", `Failed to parse ${path3}: ${error.message}`, { path: path3 }, [
1048
1164
  "Check for syntax errors in the file",
1049
1165
  "Ensure valid TypeScript syntax"
1050
1166
  ])
@@ -1328,22 +1444,22 @@ function extractInlineRefs(content) {
1328
1444
  }
1329
1445
  return refs;
1330
1446
  }
1331
- async function parseDocumentationFile(path2) {
1332
- const contentResult = await readFileContent(path2);
1447
+ async function parseDocumentationFile(path3) {
1448
+ const contentResult = await readFileContent(path3);
1333
1449
  if (!contentResult.ok) {
1334
1450
  return Err(
1335
1451
  createEntropyError(
1336
1452
  "PARSE_ERROR",
1337
- `Failed to read documentation file: ${path2}`,
1338
- { file: path2 },
1453
+ `Failed to read documentation file: ${path3}`,
1454
+ { file: path3 },
1339
1455
  ["Check that the file exists"]
1340
1456
  )
1341
1457
  );
1342
1458
  }
1343
1459
  const content = contentResult.value;
1344
- const type = path2.endsWith(".md") ? "markdown" : "text";
1460
+ const type = path3.endsWith(".md") ? "markdown" : "text";
1345
1461
  return Ok({
1346
- path: path2,
1462
+ path: path3,
1347
1463
  type,
1348
1464
  content,
1349
1465
  codeBlocks: extractCodeBlocks(content),
@@ -1651,7 +1767,45 @@ async function checkStructureDrift(snapshot, _config) {
1651
1767
  }
1652
1768
  return drifts;
1653
1769
  }
1654
- async function detectDocDrift(snapshot, config) {
1770
+ async function detectDocDrift(snapshot, config, graphDriftData) {
1771
+ if (graphDriftData) {
1772
+ const drifts2 = [];
1773
+ for (const target of graphDriftData.missingTargets) {
1774
+ drifts2.push({
1775
+ type: "api-signature",
1776
+ docFile: target,
1777
+ line: 0,
1778
+ reference: target,
1779
+ context: "graph-missing-target",
1780
+ issue: "NOT_FOUND",
1781
+ details: `Graph node "${target}" has no matching code target`,
1782
+ confidence: "high"
1783
+ });
1784
+ }
1785
+ for (const edge of graphDriftData.staleEdges) {
1786
+ drifts2.push({
1787
+ type: "api-signature",
1788
+ docFile: edge.docNodeId,
1789
+ line: 0,
1790
+ reference: edge.codeNodeId,
1791
+ context: `graph-stale-edge:${edge.edgeType}`,
1792
+ issue: "NOT_FOUND",
1793
+ details: `Stale edge from doc "${edge.docNodeId}" to code "${edge.codeNodeId}" (${edge.edgeType})`,
1794
+ confidence: "medium"
1795
+ });
1796
+ }
1797
+ const severity2 = drifts2.length === 0 ? "none" : drifts2.length <= 3 ? "low" : drifts2.length <= 10 ? "medium" : "high";
1798
+ return Ok({
1799
+ drifts: drifts2,
1800
+ stats: {
1801
+ docsScanned: graphDriftData.staleEdges.length,
1802
+ referencesChecked: graphDriftData.staleEdges.length + graphDriftData.missingTargets.length,
1803
+ driftsFound: drifts2.length,
1804
+ byType: { api: drifts2.length, example: 0, structure: 0 }
1805
+ },
1806
+ severity: severity2
1807
+ });
1808
+ }
1655
1809
  const fullConfig = { ...DEFAULT_DRIFT_CONFIG, ...config };
1656
1810
  const drifts = [];
1657
1811
  if (fullConfig.checkApiSignatures) {
@@ -1889,7 +2043,54 @@ function findDeadInternals(snapshot, _reachability) {
1889
2043
  }
1890
2044
  return deadInternals;
1891
2045
  }
1892
- async function detectDeadCode(snapshot) {
2046
+ async function detectDeadCode(snapshot, graphDeadCodeData) {
2047
+ if (graphDeadCodeData) {
2048
+ const deadFiles2 = [];
2049
+ const deadExports2 = [];
2050
+ const fileTypes = /* @__PURE__ */ new Set(["file", "module"]);
2051
+ const exportTypes = /* @__PURE__ */ new Set(["function", "class", "method", "interface", "variable"]);
2052
+ for (const node of graphDeadCodeData.unreachableNodes) {
2053
+ if (fileTypes.has(node.type)) {
2054
+ deadFiles2.push({
2055
+ path: node.path || node.id,
2056
+ reason: "NO_IMPORTERS",
2057
+ exportCount: 0,
2058
+ lineCount: 0
2059
+ });
2060
+ } else if (exportTypes.has(node.type)) {
2061
+ const exportType = node.type === "method" ? "function" : node.type;
2062
+ deadExports2.push({
2063
+ file: node.path || node.id,
2064
+ name: node.name,
2065
+ line: 0,
2066
+ type: exportType,
2067
+ isDefault: false,
2068
+ reason: "NO_IMPORTERS"
2069
+ });
2070
+ }
2071
+ }
2072
+ const reachableCount = graphDeadCodeData.reachableNodeIds instanceof Set ? graphDeadCodeData.reachableNodeIds.size : graphDeadCodeData.reachableNodeIds.length;
2073
+ const fileNodes = graphDeadCodeData.unreachableNodes.filter((n) => fileTypes.has(n.type));
2074
+ const exportNodes = graphDeadCodeData.unreachableNodes.filter((n) => exportTypes.has(n.type));
2075
+ const totalFiles = reachableCount + fileNodes.length;
2076
+ const totalExports2 = exportNodes.length + (reachableCount > 0 ? reachableCount : 0);
2077
+ const report2 = {
2078
+ deadExports: deadExports2,
2079
+ deadFiles: deadFiles2,
2080
+ deadInternals: [],
2081
+ unusedImports: [],
2082
+ stats: {
2083
+ filesAnalyzed: totalFiles,
2084
+ entryPointsUsed: [],
2085
+ totalExports: totalExports2,
2086
+ deadExportCount: deadExports2.length,
2087
+ totalFiles,
2088
+ deadFileCount: deadFiles2.length,
2089
+ estimatedDeadLines: 0
2090
+ }
2091
+ };
2092
+ return Ok(report2);
2093
+ }
1893
2094
  const reachability = buildReachabilityMap(snapshot);
1894
2095
  const usageMap = buildExportUsageMap(snapshot);
1895
2096
  const deadExports = findDeadExports(snapshot, usageMap, reachability);
@@ -2215,22 +2416,39 @@ var EntropyAnalyzer = class {
2215
2416
  };
2216
2417
  }
2217
2418
  /**
2218
- * Run full entropy analysis
2419
+ * Run full entropy analysis.
2420
+ * When graphOptions is provided, passes graph data to drift and dead code detectors
2421
+ * for graph-enhanced analysis instead of snapshot-based analysis.
2219
2422
  */
2220
- async analyze() {
2423
+ async analyze(graphOptions) {
2221
2424
  const startTime = Date.now();
2222
- const snapshotResult = await buildSnapshot(this.config);
2223
- if (!snapshotResult.ok) {
2224
- return Err(snapshotResult.error);
2425
+ const needsSnapshot = !graphOptions || !graphOptions.graphDriftData || !graphOptions.graphDeadCodeData;
2426
+ if (needsSnapshot) {
2427
+ const snapshotResult = await buildSnapshot(this.config);
2428
+ if (!snapshotResult.ok) {
2429
+ return Err(snapshotResult.error);
2430
+ }
2431
+ this.snapshot = snapshotResult.value;
2432
+ } else {
2433
+ this.snapshot = {
2434
+ files: [],
2435
+ dependencyGraph: { nodes: [], edges: [] },
2436
+ exportMap: { byFile: /* @__PURE__ */ new Map(), byName: /* @__PURE__ */ new Map() },
2437
+ docs: [],
2438
+ codeReferences: [],
2439
+ entryPoints: [],
2440
+ rootDir: this.config.rootDir,
2441
+ config: this.config,
2442
+ buildTime: 0
2443
+ };
2225
2444
  }
2226
- this.snapshot = snapshotResult.value;
2227
2445
  let driftReport;
2228
2446
  let deadCodeReport;
2229
2447
  let patternReport;
2230
2448
  const analysisErrors = [];
2231
2449
  if (this.config.analyze.drift) {
2232
2450
  const driftConfig = typeof this.config.analyze.drift === "object" ? this.config.analyze.drift : {};
2233
- const result = await detectDocDrift(this.snapshot, driftConfig);
2451
+ const result = await detectDocDrift(this.snapshot, driftConfig, graphOptions?.graphDriftData);
2234
2452
  if (result.ok) {
2235
2453
  driftReport = result.value;
2236
2454
  } else {
@@ -2238,7 +2456,7 @@ var EntropyAnalyzer = class {
2238
2456
  }
2239
2457
  }
2240
2458
  if (this.config.analyze.deadCode) {
2241
- const result = await detectDeadCode(this.snapshot);
2459
+ const result = await detectDeadCode(this.snapshot, graphOptions?.graphDeadCodeData);
2242
2460
  if (result.ok) {
2243
2461
  deadCodeReport = result.value;
2244
2462
  } else {
@@ -2335,22 +2553,22 @@ var EntropyAnalyzer = class {
2335
2553
  /**
2336
2554
  * Run drift detection only (snapshot must be built first)
2337
2555
  */
2338
- async detectDrift(config) {
2556
+ async detectDrift(config, graphDriftData) {
2339
2557
  const snapshotResult = await this.ensureSnapshot();
2340
2558
  if (!snapshotResult.ok) {
2341
2559
  return Err(snapshotResult.error);
2342
2560
  }
2343
- return detectDocDrift(snapshotResult.value, config || {});
2561
+ return detectDocDrift(snapshotResult.value, config || {}, graphDriftData);
2344
2562
  }
2345
2563
  /**
2346
2564
  * Run dead code detection only (snapshot must be built first)
2347
2565
  */
2348
- async detectDeadCode() {
2566
+ async detectDeadCode(graphDeadCodeData) {
2349
2567
  const snapshotResult = await this.ensureSnapshot();
2350
2568
  if (!snapshotResult.ok) {
2351
2569
  return Err(snapshotResult.error);
2352
2570
  }
2353
- return detectDeadCode(snapshotResult.value);
2571
+ return detectDeadCode(snapshotResult.value, graphDeadCodeData);
2354
2572
  }
2355
2573
  /**
2356
2574
  * Run pattern detection only (snapshot must be built first)
@@ -2818,7 +3036,7 @@ function parseDiff(diff) {
2818
3036
  });
2819
3037
  }
2820
3038
  }
2821
- async function analyzeDiff(changes, options) {
3039
+ async function analyzeDiff(changes, options, graphImpactData) {
2822
3040
  if (!options?.enabled) {
2823
3041
  return Ok([]);
2824
3042
  }
@@ -2869,26 +3087,75 @@ async function analyzeDiff(changes, options) {
2869
3087
  }
2870
3088
  }
2871
3089
  if (options.checkTestCoverage) {
2872
- const addedSourceFiles = changes.files.filter(
2873
- (f) => f.status === "added" && f.path.endsWith(".ts") && !f.path.includes(".test.")
2874
- );
2875
- const testFiles = changes.files.filter((f) => f.path.includes(".test."));
2876
- for (const sourceFile of addedSourceFiles) {
2877
- const expectedTestPath = sourceFile.path.replace(".ts", ".test.ts");
2878
- const hasTest = testFiles.some(
2879
- (t) => t.path.includes(expectedTestPath) || t.path.includes(sourceFile.path.replace(".ts", ""))
3090
+ if (graphImpactData) {
3091
+ for (const file of changes.files) {
3092
+ if (file.status === "added" && file.path.endsWith(".ts") && !file.path.includes(".test.")) {
3093
+ const hasGraphTest = graphImpactData.affectedTests.some(
3094
+ (t) => t.coversFile === file.path
3095
+ );
3096
+ if (!hasGraphTest) {
3097
+ items.push({
3098
+ id: `test-coverage-${file.path}`,
3099
+ category: "diff",
3100
+ check: "Test coverage (graph)",
3101
+ passed: false,
3102
+ severity: "warning",
3103
+ details: `New file ${file.path} has no test file linked in the graph`,
3104
+ file: file.path
3105
+ });
3106
+ }
3107
+ }
3108
+ }
3109
+ } else {
3110
+ const addedSourceFiles = changes.files.filter(
3111
+ (f) => f.status === "added" && f.path.endsWith(".ts") && !f.path.includes(".test.")
2880
3112
  );
2881
- if (!hasTest) {
2882
- items.push({
2883
- id: `diff-${++itemId}`,
2884
- category: "diff",
2885
- check: `Test coverage: ${sourceFile.path}`,
2886
- passed: false,
2887
- severity: "warning",
2888
- details: "New source file added without corresponding test file",
2889
- file: sourceFile.path,
2890
- suggestion: `Add tests in ${expectedTestPath}`
2891
- });
3113
+ const testFiles = changes.files.filter((f) => f.path.includes(".test."));
3114
+ for (const sourceFile of addedSourceFiles) {
3115
+ const expectedTestPath = sourceFile.path.replace(".ts", ".test.ts");
3116
+ const hasTest = testFiles.some(
3117
+ (t) => t.path.includes(expectedTestPath) || t.path.includes(sourceFile.path.replace(".ts", ""))
3118
+ );
3119
+ if (!hasTest) {
3120
+ items.push({
3121
+ id: `diff-${++itemId}`,
3122
+ category: "diff",
3123
+ check: `Test coverage: ${sourceFile.path}`,
3124
+ passed: false,
3125
+ severity: "warning",
3126
+ details: "New source file added without corresponding test file",
3127
+ file: sourceFile.path,
3128
+ suggestion: `Add tests in ${expectedTestPath}`
3129
+ });
3130
+ }
3131
+ }
3132
+ }
3133
+ }
3134
+ if (graphImpactData && graphImpactData.impactScope > 20) {
3135
+ items.push({
3136
+ id: "impact-scope",
3137
+ category: "diff",
3138
+ check: "Impact scope",
3139
+ passed: false,
3140
+ severity: "warning",
3141
+ details: `Changes affect ${graphImpactData.impactScope} downstream dependents \u2014 consider a thorough review`
3142
+ });
3143
+ }
3144
+ if (graphImpactData) {
3145
+ for (const file of changes.files) {
3146
+ if (file.status === "modified" && file.path.endsWith(".ts") && !file.path.includes(".test.")) {
3147
+ const hasDoc = graphImpactData.affectedDocs.some((d) => d.documentsFile === file.path);
3148
+ if (!hasDoc) {
3149
+ items.push({
3150
+ id: `doc-coverage-${file.path}`,
3151
+ category: "diff",
3152
+ check: "Documentation coverage (graph)",
3153
+ passed: true,
3154
+ severity: "info",
3155
+ details: `Modified file ${file.path} has no documentation linked in the graph`,
3156
+ file: file.path
3157
+ });
3158
+ }
2892
3159
  }
2893
3160
  }
2894
3161
  }
@@ -2899,13 +3166,16 @@ async function analyzeDiff(changes, options) {
2899
3166
  var ChecklistBuilder = class {
2900
3167
  rootDir;
2901
3168
  harnessOptions;
3169
+ graphHarnessData;
2902
3170
  customRules = [];
2903
3171
  diffOptions;
3172
+ graphImpactData;
2904
3173
  constructor(rootDir) {
2905
3174
  this.rootDir = rootDir;
2906
3175
  }
2907
- withHarnessChecks(options) {
3176
+ withHarnessChecks(options, graphData) {
2908
3177
  this.harnessOptions = options ?? { context: true, constraints: true, entropy: true };
3178
+ this.graphHarnessData = graphData;
2909
3179
  return this;
2910
3180
  }
2911
3181
  addRule(rule) {
@@ -2916,46 +3186,79 @@ var ChecklistBuilder = class {
2916
3186
  this.customRules.push(...rules);
2917
3187
  return this;
2918
3188
  }
2919
- withDiffAnalysis(options) {
3189
+ withDiffAnalysis(options, graphImpactData) {
2920
3190
  this.diffOptions = options;
3191
+ this.graphImpactData = graphImpactData;
2921
3192
  return this;
2922
3193
  }
2923
3194
  async run(changes) {
2924
3195
  const startTime = Date.now();
2925
3196
  const items = [];
2926
3197
  if (this.harnessOptions) {
2927
- if (this.harnessOptions.context) {
2928
- items.push({
2929
- id: "harness-context",
2930
- category: "harness",
2931
- check: "Context Engineering (AGENTS.md, doc coverage)",
2932
- passed: true,
2933
- severity: "info",
2934
- details: "Harness context validation not yet integrated. See Module 2 (context/).",
2935
- suggestion: "Integrate with validateAgentsMap(), checkDocCoverage() from context module"
2936
- });
3198
+ if (this.harnessOptions.context !== false) {
3199
+ if (this.graphHarnessData) {
3200
+ items.push({
3201
+ id: "harness-context",
3202
+ category: "harness",
3203
+ check: "Context validation",
3204
+ passed: this.graphHarnessData.graphExists && this.graphHarnessData.nodeCount > 0,
3205
+ severity: "info",
3206
+ details: this.graphHarnessData.graphExists ? `Graph loaded: ${this.graphHarnessData.nodeCount} nodes, ${this.graphHarnessData.edgeCount} edges` : "No graph available \u2014 run harness scan to build the knowledge graph"
3207
+ });
3208
+ } else {
3209
+ items.push({
3210
+ id: "harness-context",
3211
+ category: "harness",
3212
+ check: "Context validation",
3213
+ passed: true,
3214
+ severity: "info",
3215
+ details: "Harness context validation not yet integrated (run with graph for real checks)"
3216
+ });
3217
+ }
2937
3218
  }
2938
- if (this.harnessOptions.constraints) {
2939
- items.push({
2940
- id: "harness-constraints",
2941
- category: "harness",
2942
- check: "Architectural Constraints (dependencies, boundaries)",
2943
- passed: true,
2944
- severity: "info",
2945
- details: "Harness constraints validation not yet integrated. See Module 3 (constraints/).",
2946
- suggestion: "Integrate with validateDependencies(), detectCircularDeps() from constraints module"
2947
- });
3219
+ if (this.harnessOptions.constraints !== false) {
3220
+ if (this.graphHarnessData) {
3221
+ const violations = this.graphHarnessData.constraintViolations;
3222
+ items.push({
3223
+ id: "harness-constraints",
3224
+ category: "harness",
3225
+ check: "Constraint validation",
3226
+ passed: violations === 0,
3227
+ severity: violations > 0 ? "error" : "info",
3228
+ details: violations === 0 ? "No constraint violations detected" : `${violations} constraint violation(s) detected`
3229
+ });
3230
+ } else {
3231
+ items.push({
3232
+ id: "harness-constraints",
3233
+ category: "harness",
3234
+ check: "Constraint validation",
3235
+ passed: true,
3236
+ severity: "info",
3237
+ details: "Harness constraint validation not yet integrated (run with graph for real checks)"
3238
+ });
3239
+ }
2948
3240
  }
2949
- if (this.harnessOptions.entropy) {
2950
- items.push({
2951
- id: "harness-entropy",
2952
- category: "harness",
2953
- check: "Entropy Management (drift, dead code)",
2954
- passed: true,
2955
- severity: "info",
2956
- details: "Harness entropy validation not yet integrated. See Module 4 (entropy/).",
2957
- suggestion: "Integrate with EntropyAnalyzer from entropy module"
2958
- });
3241
+ if (this.harnessOptions.entropy !== false) {
3242
+ if (this.graphHarnessData) {
3243
+ const issues = this.graphHarnessData.unreachableNodes + this.graphHarnessData.undocumentedFiles;
3244
+ items.push({
3245
+ id: "harness-entropy",
3246
+ category: "harness",
3247
+ check: "Entropy detection",
3248
+ passed: issues === 0,
3249
+ severity: issues > 0 ? "warning" : "info",
3250
+ details: issues === 0 ? "No entropy issues detected" : `${this.graphHarnessData.unreachableNodes} unreachable node(s), ${this.graphHarnessData.undocumentedFiles} undocumented file(s)`
3251
+ });
3252
+ } else {
3253
+ items.push({
3254
+ id: "harness-entropy",
3255
+ category: "harness",
3256
+ check: "Entropy detection",
3257
+ passed: true,
3258
+ severity: "info",
3259
+ details: "Harness entropy detection not yet integrated (run with graph for real checks)"
3260
+ });
3261
+ }
2959
3262
  }
2960
3263
  }
2961
3264
  for (const rule of this.customRules) {
@@ -2991,7 +3294,7 @@ var ChecklistBuilder = class {
2991
3294
  }
2992
3295
  }
2993
3296
  if (this.diffOptions) {
2994
- const diffResult = await analyzeDiff(changes, this.diffOptions);
3297
+ const diffResult = await analyzeDiff(changes, this.diffOptions, this.graphImpactData);
2995
3298
  if (diffResult.ok) {
2996
3299
  items.push(...diffResult.value);
2997
3300
  }
@@ -3018,16 +3321,16 @@ var ChecklistBuilder = class {
3018
3321
  };
3019
3322
 
3020
3323
  // src/feedback/review/self-review.ts
3021
- async function createSelfReview(changes, config) {
3324
+ async function createSelfReview(changes, config, graphData) {
3022
3325
  const builder = new ChecklistBuilder(config.rootDir);
3023
3326
  if (config.harness) {
3024
- builder.withHarnessChecks(config.harness);
3327
+ builder.withHarnessChecks(config.harness, graphData?.harness);
3025
3328
  }
3026
3329
  if (config.customRules) {
3027
3330
  builder.addRules(config.customRules);
3028
3331
  }
3029
3332
  if (config.diffAnalysis) {
3030
- builder.withDiffAnalysis(config.diffAnalysis);
3333
+ builder.withDiffAnalysis(config.diffAnalysis, graphData?.impact);
3031
3334
  }
3032
3335
  return builder.run(changes);
3033
3336
  }
@@ -3854,6 +4157,188 @@ async function runMultiTurnPipeline(initialContext, turnExecutor, options) {
3854
4157
  };
3855
4158
  }
3856
4159
 
4160
+ // src/ci/check-orchestrator.ts
4161
+ import * as path2 from "path";
4162
+ var ALL_CHECKS = ["validate", "deps", "docs", "entropy", "phase-gate"];
4163
+ async function runSingleCheck(name, projectRoot, config) {
4164
+ const start = Date.now();
4165
+ const issues = [];
4166
+ try {
4167
+ switch (name) {
4168
+ case "validate": {
4169
+ const agentsPath = path2.join(projectRoot, config.agentsMapPath ?? "AGENTS.md");
4170
+ const result = await validateAgentsMap(agentsPath);
4171
+ if (!result.ok) {
4172
+ issues.push({ severity: "error", message: result.error.message });
4173
+ } else if (!result.value.valid) {
4174
+ if (result.value.errors) {
4175
+ for (const err of result.value.errors) {
4176
+ issues.push({ severity: "error", message: err.message });
4177
+ }
4178
+ }
4179
+ for (const section of result.value.missingSections) {
4180
+ issues.push({ severity: "warning", message: `Missing section: ${section}` });
4181
+ }
4182
+ for (const link of result.value.brokenLinks) {
4183
+ issues.push({
4184
+ severity: "warning",
4185
+ message: `Broken link: ${link.text} \u2192 ${link.path}`,
4186
+ file: link.path
4187
+ });
4188
+ }
4189
+ }
4190
+ break;
4191
+ }
4192
+ case "deps": {
4193
+ const rawLayers = config.layers;
4194
+ if (rawLayers && rawLayers.length > 0) {
4195
+ const parser = new TypeScriptParser();
4196
+ const layers = rawLayers.map(
4197
+ (l) => defineLayer(
4198
+ l.name,
4199
+ Array.isArray(l.patterns) ? l.patterns : [l.pattern],
4200
+ l.allowedDependencies
4201
+ )
4202
+ );
4203
+ const result = await validateDependencies({
4204
+ layers,
4205
+ rootDir: projectRoot,
4206
+ parser
4207
+ });
4208
+ if (!result.ok) {
4209
+ issues.push({ severity: "error", message: result.error.message });
4210
+ } else if (result.value.violations.length > 0) {
4211
+ for (const v of result.value.violations) {
4212
+ issues.push({
4213
+ severity: "error",
4214
+ message: `${v.reason}: ${v.file} imports ${v.imports} (${v.fromLayer} \u2192 ${v.toLayer})`,
4215
+ file: v.file,
4216
+ line: v.line
4217
+ });
4218
+ }
4219
+ }
4220
+ }
4221
+ break;
4222
+ }
4223
+ case "docs": {
4224
+ const docsDir = path2.join(projectRoot, config.docsDir ?? "docs");
4225
+ const result = await checkDocCoverage("project", { docsDir });
4226
+ if (!result.ok) {
4227
+ issues.push({ severity: "warning", message: result.error.message });
4228
+ } else if (result.value.gaps.length > 0) {
4229
+ for (const gap of result.value.gaps) {
4230
+ issues.push({
4231
+ severity: "warning",
4232
+ message: `Undocumented: ${gap.file} (suggested: ${gap.suggestedSection})`,
4233
+ file: gap.file
4234
+ });
4235
+ }
4236
+ }
4237
+ break;
4238
+ }
4239
+ case "entropy": {
4240
+ const analyzer = new EntropyAnalyzer({
4241
+ rootDir: projectRoot,
4242
+ analyze: { drift: true, deadCode: true, patterns: false }
4243
+ });
4244
+ const result = await analyzer.analyze();
4245
+ if (!result.ok) {
4246
+ issues.push({ severity: "warning", message: result.error.message });
4247
+ } else {
4248
+ const report = result.value;
4249
+ if (report.drift) {
4250
+ for (const drift of report.drift.drifts) {
4251
+ issues.push({
4252
+ severity: "warning",
4253
+ message: `Doc drift (${drift.type}): ${drift.details}`,
4254
+ file: drift.docFile,
4255
+ line: drift.line
4256
+ });
4257
+ }
4258
+ }
4259
+ if (report.deadCode) {
4260
+ for (const dead of report.deadCode.deadExports) {
4261
+ issues.push({
4262
+ severity: "warning",
4263
+ message: `Dead export: ${dead.name}`,
4264
+ file: dead.file,
4265
+ line: dead.line
4266
+ });
4267
+ }
4268
+ }
4269
+ }
4270
+ break;
4271
+ }
4272
+ case "phase-gate": {
4273
+ const phaseGates = config.phaseGates;
4274
+ if (!phaseGates?.enabled) {
4275
+ break;
4276
+ }
4277
+ issues.push({
4278
+ severity: "warning",
4279
+ message: "Phase gate is enabled but requires CLI context. Run `harness check-phase-gate` separately for full validation."
4280
+ });
4281
+ break;
4282
+ }
4283
+ }
4284
+ } catch (error) {
4285
+ issues.push({
4286
+ severity: "error",
4287
+ message: `Check '${name}' threw: ${error instanceof Error ? error.message : String(error)}`
4288
+ });
4289
+ }
4290
+ const hasErrors = issues.some((i) => i.severity === "error");
4291
+ const hasWarnings = issues.some((i) => i.severity === "warning");
4292
+ const status = hasErrors ? "fail" : hasWarnings ? "warn" : "pass";
4293
+ return {
4294
+ name,
4295
+ status,
4296
+ issues,
4297
+ durationMs: Date.now() - start
4298
+ };
4299
+ }
4300
+ function buildSummary(checks) {
4301
+ return {
4302
+ total: checks.length,
4303
+ passed: checks.filter((c) => c.status === "pass").length,
4304
+ failed: checks.filter((c) => c.status === "fail").length,
4305
+ warnings: checks.filter((c) => c.status === "warn").length,
4306
+ skipped: checks.filter((c) => c.status === "skip").length
4307
+ };
4308
+ }
4309
+ function determineExitCode(summary, failOn = "error") {
4310
+ if (summary.failed > 0) return 1;
4311
+ if (failOn === "warning" && summary.warnings > 0) return 1;
4312
+ return 0;
4313
+ }
4314
+ async function runCIChecks(input) {
4315
+ const { projectRoot, config, skip = [], failOn = "error" } = input;
4316
+ try {
4317
+ const checks = [];
4318
+ for (const name of ALL_CHECKS) {
4319
+ if (skip.includes(name)) {
4320
+ checks.push({ name, status: "skip", issues: [], durationMs: 0 });
4321
+ } else {
4322
+ const result = await runSingleCheck(name, projectRoot, config);
4323
+ checks.push(result);
4324
+ }
4325
+ }
4326
+ const summary = buildSummary(checks);
4327
+ const exitCode = determineExitCode(summary, failOn);
4328
+ const report = {
4329
+ version: 1,
4330
+ project: config.name ?? "unknown",
4331
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4332
+ checks,
4333
+ summary,
4334
+ exitCode
4335
+ };
4336
+ return Ok(report);
4337
+ } catch (error) {
4338
+ return Err(error instanceof Error ? error : new Error(String(error)));
4339
+ }
4340
+ }
4341
+
3857
4342
  // src/index.ts
3858
4343
  var VERSION = "0.6.0";
3859
4344
  export {
@@ -3917,6 +4402,7 @@ export {
3917
4402
  requestPeerReview,
3918
4403
  resetFeedbackConfig,
3919
4404
  resolveFileToLayer,
4405
+ runCIChecks,
3920
4406
  runMechanicalGate,
3921
4407
  runMultiTurnPipeline,
3922
4408
  runPipeline,