@ipation/specbridge 2.3.0 → 2.4.2

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/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/cli/index.ts
4
4
  import { Command as Command20 } from "commander";
5
- import chalk20 from "chalk";
5
+ import chalk19 from "chalk";
6
6
  import { readFileSync as readFileSync2 } from "fs";
7
7
  import { fileURLToPath as fileURLToPath4 } from "url";
8
8
  import { dirname as dirname5, join as join14 } from "path";
@@ -343,12 +343,7 @@ import fg from "fast-glob";
343
343
  import { minimatch } from "minimatch";
344
344
  import { relative, isAbsolute } from "path";
345
345
  async function glob(patterns, options = {}) {
346
- const {
347
- cwd = process.cwd(),
348
- ignore = [],
349
- absolute = false,
350
- onlyFiles = true
351
- } = options;
346
+ const { cwd = process.cwd(), ignore = [], absolute = false, onlyFiles = true } = options;
352
347
  return fg(patterns, {
353
348
  cwd,
354
349
  ignore,
@@ -582,12 +577,24 @@ var FUNCTION_PATTERNS = [
582
577
  { convention: "snake_case", regex: /^[a-z][a-z0-9_]*$/, description: "Functions use snake_case" }
583
578
  ];
584
579
  var INTERFACE_PATTERNS = [
585
- { convention: "PascalCase", regex: /^[A-Z][a-zA-Z0-9]*$/, description: "Interfaces use PascalCase" },
586
- { convention: "IPrefixed", regex: /^I[A-Z][a-zA-Z0-9]*$/, description: "Interfaces are prefixed with I" }
580
+ {
581
+ convention: "PascalCase",
582
+ regex: /^[A-Z][a-zA-Z0-9]*$/,
583
+ description: "Interfaces use PascalCase"
584
+ },
585
+ {
586
+ convention: "IPrefixed",
587
+ regex: /^I[A-Z][a-zA-Z0-9]*$/,
588
+ description: "Interfaces are prefixed with I"
589
+ }
587
590
  ];
588
591
  var TYPE_PATTERNS = [
589
592
  { convention: "PascalCase", regex: /^[A-Z][a-zA-Z0-9]*$/, description: "Types use PascalCase" },
590
- { convention: "TSuffixed", regex: /^[A-Z][a-zA-Z0-9]*Type$/, description: "Types are suffixed with Type" }
593
+ {
594
+ convention: "TSuffixed",
595
+ regex: /^[A-Z][a-zA-Z0-9]*Type$/,
596
+ description: "Types are suffixed with Type"
597
+ }
591
598
  ];
592
599
  var NamingAnalyzer = class {
593
600
  id = "naming";
@@ -608,7 +615,10 @@ var NamingAnalyzer = class {
608
615
  analyzeClassNaming(scanner) {
609
616
  const classes = scanner.findClasses();
610
617
  if (classes.length < 3) return null;
611
- const matches = this.findBestMatch(classes.map((c) => c.name), CLASS_PATTERNS);
618
+ const matches = this.findBestMatch(
619
+ classes.map((c) => c.name),
620
+ CLASS_PATTERNS
621
+ );
612
622
  if (!matches) return null;
613
623
  return createPattern(this.id, {
614
624
  id: "naming-classes",
@@ -632,7 +642,10 @@ var NamingAnalyzer = class {
632
642
  analyzeFunctionNaming(scanner) {
633
643
  const functions = scanner.findFunctions();
634
644
  if (functions.length < 3) return null;
635
- const matches = this.findBestMatch(functions.map((f) => f.name), FUNCTION_PATTERNS);
645
+ const matches = this.findBestMatch(
646
+ functions.map((f) => f.name),
647
+ FUNCTION_PATTERNS
648
+ );
636
649
  if (!matches) return null;
637
650
  return createPattern(this.id, {
638
651
  id: "naming-functions",
@@ -656,7 +669,10 @@ var NamingAnalyzer = class {
656
669
  analyzeInterfaceNaming(scanner) {
657
670
  const interfaces = scanner.findInterfaces();
658
671
  if (interfaces.length < 3) return null;
659
- const matches = this.findBestMatch(interfaces.map((i) => i.name), INTERFACE_PATTERNS);
672
+ const matches = this.findBestMatch(
673
+ interfaces.map((i) => i.name),
674
+ INTERFACE_PATTERNS
675
+ );
660
676
  if (!matches) return null;
661
677
  return createPattern(this.id, {
662
678
  id: "naming-interfaces",
@@ -680,7 +696,10 @@ var NamingAnalyzer = class {
680
696
  analyzeTypeNaming(scanner) {
681
697
  const types = scanner.findTypeAliases();
682
698
  if (types.length < 3) return null;
683
- const matches = this.findBestMatch(types.map((t) => t.name), TYPE_PATTERNS);
699
+ const matches = this.findBestMatch(
700
+ types.map((t) => t.name),
701
+ TYPE_PATTERNS
702
+ );
684
703
  if (!matches) return null;
685
704
  return createPattern(this.id, {
686
705
  id: "naming-types",
@@ -769,8 +788,12 @@ var ImportsAnalyzer = class {
769
788
  analyzeRelativeImports(scanner) {
770
789
  const imports = scanner.findImports();
771
790
  const relativeImports = imports.filter((i) => i.module.startsWith("."));
772
- const absoluteImports = imports.filter((i) => !i.module.startsWith(".") && !i.module.startsWith("@"));
773
- const aliasImports = imports.filter((i) => i.module.startsWith("@/") || i.module.startsWith("~"));
791
+ const absoluteImports = imports.filter(
792
+ (i) => !i.module.startsWith(".") && !i.module.startsWith("@")
793
+ );
794
+ const aliasImports = imports.filter(
795
+ (i) => i.module.startsWith("@/") || i.module.startsWith("~")
796
+ );
774
797
  const total = relativeImports.length + absoluteImports.length + aliasImports.length;
775
798
  if (total < 10) return null;
776
799
  if (aliasImports.length > relativeImports.length && aliasImports.length >= 5) {
@@ -837,18 +860,20 @@ var ImportsAnalyzer = class {
837
860
  for (const [packageName, data] of moduleCounts) {
838
861
  if (data.count >= 5) {
839
862
  const confidence = Math.min(100, 50 + data.count * 2);
840
- patterns.push(createPattern(this.id, {
841
- id: `imports-module-${packageName.replace(/[/@]/g, "-")}`,
842
- name: `${packageName} Usage`,
843
- description: `${packageName} is used across ${data.count} files`,
844
- confidence,
845
- occurrences: data.count,
846
- examples: data.examples.slice(0, 3).map((i) => ({
847
- file: i.file,
848
- line: i.line,
849
- snippet: `import { ${i.named.slice(0, 2).join(", ") || "..."} } from '${i.module}'`
850
- }))
851
- }));
863
+ patterns.push(
864
+ createPattern(this.id, {
865
+ id: `imports-module-${packageName.replace(/[/@]/g, "-")}`,
866
+ name: `${packageName} Usage`,
867
+ description: `${packageName} is used across ${data.count} files`,
868
+ confidence,
869
+ occurrences: data.count,
870
+ examples: data.examples.slice(0, 3).map((i) => ({
871
+ file: i.file,
872
+ line: i.line,
873
+ snippet: `import { ${i.named.slice(0, 2).join(", ") || "..."} } from '${i.module}'`
874
+ }))
875
+ })
876
+ );
852
877
  }
853
878
  }
854
879
  return patterns;
@@ -893,24 +918,26 @@ var StructureAnalyzer = class {
893
918
  const count = dirCounts.get(name);
894
919
  if (count && count >= 3) {
895
920
  const exampleFiles = files.filter((f) => basename(dirname2(f.path)) === name).slice(0, 3);
896
- patterns.push(createPattern(this.id, {
897
- id: `structure-dir-${name}`,
898
- name: `${name}/ Directory Convention`,
899
- description,
900
- confidence: Math.min(100, 60 + count * 5),
901
- occurrences: count,
902
- examples: exampleFiles.map((f) => ({
903
- file: f.path,
904
- line: 1,
905
- snippet: basename(f.path)
906
- })),
907
- suggestedConstraint: {
908
- type: "convention",
909
- rule: `${name.charAt(0).toUpperCase() + name.slice(1)} should be placed in the ${name}/ directory`,
910
- severity: "low",
911
- scope: `src/**/${name}/**/*.ts`
912
- }
913
- }));
921
+ patterns.push(
922
+ createPattern(this.id, {
923
+ id: `structure-dir-${name}`,
924
+ name: `${name}/ Directory Convention`,
925
+ description,
926
+ confidence: Math.min(100, 60 + count * 5),
927
+ occurrences: count,
928
+ examples: exampleFiles.map((f) => ({
929
+ file: f.path,
930
+ line: 1,
931
+ snippet: basename(f.path)
932
+ })),
933
+ suggestedConstraint: {
934
+ type: "convention",
935
+ rule: `${name.charAt(0).toUpperCase() + name.slice(1)} should be placed in the ${name}/ directory`,
936
+ severity: "low",
937
+ scope: `src/**/${name}/**/*.ts`
938
+ }
939
+ })
940
+ );
914
941
  }
915
942
  }
916
943
  return patterns;
@@ -920,29 +947,55 @@ var StructureAnalyzer = class {
920
947
  const suffixPatterns = [
921
948
  { suffix: ".test.ts", pattern: /\.test\.ts$/, description: "Test files use .test.ts suffix" },
922
949
  { suffix: ".spec.ts", pattern: /\.spec\.ts$/, description: "Test files use .spec.ts suffix" },
923
- { suffix: ".types.ts", pattern: /\.types\.ts$/, description: "Type definition files use .types.ts suffix" },
924
- { suffix: ".utils.ts", pattern: /\.utils\.ts$/, description: "Utility files use .utils.ts suffix" },
925
- { suffix: ".service.ts", pattern: /\.service\.ts$/, description: "Service files use .service.ts suffix" },
926
- { suffix: ".controller.ts", pattern: /\.controller\.ts$/, description: "Controller files use .controller.ts suffix" },
927
- { suffix: ".model.ts", pattern: /\.model\.ts$/, description: "Model files use .model.ts suffix" },
928
- { suffix: ".schema.ts", pattern: /\.schema\.ts$/, description: "Schema files use .schema.ts suffix" }
950
+ {
951
+ suffix: ".types.ts",
952
+ pattern: /\.types\.ts$/,
953
+ description: "Type definition files use .types.ts suffix"
954
+ },
955
+ {
956
+ suffix: ".utils.ts",
957
+ pattern: /\.utils\.ts$/,
958
+ description: "Utility files use .utils.ts suffix"
959
+ },
960
+ {
961
+ suffix: ".service.ts",
962
+ pattern: /\.service\.ts$/,
963
+ description: "Service files use .service.ts suffix"
964
+ },
965
+ {
966
+ suffix: ".controller.ts",
967
+ pattern: /\.controller\.ts$/,
968
+ description: "Controller files use .controller.ts suffix"
969
+ },
970
+ {
971
+ suffix: ".model.ts",
972
+ pattern: /\.model\.ts$/,
973
+ description: "Model files use .model.ts suffix"
974
+ },
975
+ {
976
+ suffix: ".schema.ts",
977
+ pattern: /\.schema\.ts$/,
978
+ description: "Schema files use .schema.ts suffix"
979
+ }
929
980
  ];
930
981
  for (const { suffix, pattern, description } of suffixPatterns) {
931
982
  const matchingFiles = files.filter((f) => pattern.test(f.path));
932
983
  if (matchingFiles.length >= 3) {
933
984
  const confidence = Math.min(100, 60 + matchingFiles.length * 3);
934
- patterns.push(createPattern(this.id, {
935
- id: `structure-suffix-${suffix.replace(/\./g, "-")}`,
936
- name: `${suffix} File Naming`,
937
- description,
938
- confidence,
939
- occurrences: matchingFiles.length,
940
- examples: matchingFiles.slice(0, 3).map((f) => ({
941
- file: f.path,
942
- line: 1,
943
- snippet: basename(f.path)
944
- }))
945
- }));
985
+ patterns.push(
986
+ createPattern(this.id, {
987
+ id: `structure-suffix-${suffix.replace(/\./g, "-")}`,
988
+ name: `${suffix} File Naming`,
989
+ description,
990
+ confidence,
991
+ occurrences: matchingFiles.length,
992
+ examples: matchingFiles.slice(0, 3).map((f) => ({
993
+ file: f.path,
994
+ line: 1,
995
+ snippet: basename(f.path)
996
+ }))
997
+ })
998
+ );
946
999
  }
947
1000
  }
948
1001
  return patterns;
@@ -1308,7 +1361,9 @@ Results saved to: ${outputPath}`));
1308
1361
  console.log("");
1309
1362
  console.log(chalk2.cyan("Next steps:"));
1310
1363
  console.log(" Review detected patterns and create decisions for important ones.");
1311
- console.log(` Use ${chalk2.bold("specbridge decision create <id>")} to create a new decision.`);
1364
+ console.log(
1365
+ ` Use ${chalk2.bold("specbridge decision create <id>")} to create a new decision.`
1366
+ );
1312
1367
  }
1313
1368
  } catch (error) {
1314
1369
  spinner.fail("Inference failed");
@@ -1324,7 +1379,9 @@ Detected ${patterns.length} pattern(s):
1324
1379
  console.log(chalk2.bold(`${pattern.name}`));
1325
1380
  console.log(chalk2.dim(` ID: ${pattern.id}`));
1326
1381
  console.log(` ${pattern.description}`);
1327
- console.log(` Confidence: ${confidenceColor(`${pattern.confidence}%`)} (${pattern.occurrences} occurrences)`);
1382
+ console.log(
1383
+ ` Confidence: ${confidenceColor(`${pattern.confidence}%`)} (${pattern.occurrences} occurrences)`
1384
+ );
1328
1385
  console.log(chalk2.dim(` Analyzer: ${pattern.analyzer}`));
1329
1386
  if (pattern.examples.length > 0) {
1330
1387
  console.log(chalk2.dim(" Examples:"));
@@ -1345,12 +1402,11 @@ Detected ${patterns.length} pattern(s):
1345
1402
 
1346
1403
  // src/cli/commands/verify.ts
1347
1404
  import { Command as Command3 } from "commander";
1348
- import chalk5 from "chalk";
1405
+ import chalk4 from "chalk";
1349
1406
  import ora3 from "ora";
1350
1407
 
1351
1408
  // src/verification/engine.ts
1352
1409
  import { Project as Project2 } from "ts-morph";
1353
- import chalk3 from "chalk";
1354
1410
 
1355
1411
  // src/registry/loader.ts
1356
1412
  import { join as join4 } from "path";
@@ -1608,17 +1664,13 @@ var Registry = class {
1608
1664
  * Get decisions by tag
1609
1665
  */
1610
1666
  getByTag(tag) {
1611
- return this.getAll().filter(
1612
- (d) => d.metadata.tags?.includes(tag)
1613
- );
1667
+ return this.getAll().filter((d) => d.metadata.tags?.includes(tag));
1614
1668
  }
1615
1669
  /**
1616
1670
  * Get decisions by owner
1617
1671
  */
1618
1672
  getByOwner(owner) {
1619
- return this.getAll().filter(
1620
- (d) => d.metadata.owners.includes(owner)
1621
- );
1673
+ return this.getAll().filter((d) => d.metadata.owners.includes(owner));
1622
1674
  }
1623
1675
  /**
1624
1676
  * Apply filter to decisions
@@ -1629,21 +1681,15 @@ var Registry = class {
1629
1681
  return false;
1630
1682
  }
1631
1683
  if (filter.tags) {
1632
- const hasTags = filter.tags.some(
1633
- (tag) => decision.metadata.tags?.includes(tag)
1634
- );
1684
+ const hasTags = filter.tags.some((tag) => decision.metadata.tags?.includes(tag));
1635
1685
  if (!hasTags) return false;
1636
1686
  }
1637
1687
  if (filter.constraintType) {
1638
- const hasType = decision.constraints.some(
1639
- (c) => filter.constraintType?.includes(c.type)
1640
- );
1688
+ const hasType = decision.constraints.some((c) => filter.constraintType?.includes(c.type));
1641
1689
  if (!hasType) return false;
1642
1690
  }
1643
1691
  if (filter.severity) {
1644
- const hasSeverity = decision.constraints.some(
1645
- (c) => filter.severity?.includes(c.severity)
1646
- );
1692
+ const hasSeverity = decision.constraints.some((c) => filter.severity?.includes(c.severity));
1647
1693
  if (!hasSeverity) return false;
1648
1694
  }
1649
1695
  return true;
@@ -1738,17 +1784,19 @@ var NamingVerifier = class {
1738
1784
  for (const classDecl of sourceFile.getClasses()) {
1739
1785
  const name = classDecl.getName();
1740
1786
  if (name && !pattern.regex.test(name)) {
1741
- violations.push(createViolation({
1742
- decisionId,
1743
- constraintId: constraint.id,
1744
- type: constraint.type,
1745
- severity: constraint.severity,
1746
- message: `Class "${name}" does not follow ${pattern.description} naming convention`,
1747
- file: filePath,
1748
- line: classDecl.getStartLineNumber(),
1749
- column: classDecl.getStart() - classDecl.getStartLinePos(),
1750
- suggestion: `Rename to follow ${pattern.description}`
1751
- }));
1787
+ violations.push(
1788
+ createViolation({
1789
+ decisionId,
1790
+ constraintId: constraint.id,
1791
+ type: constraint.type,
1792
+ severity: constraint.severity,
1793
+ message: `Class "${name}" does not follow ${pattern.description} naming convention`,
1794
+ file: filePath,
1795
+ line: classDecl.getStartLineNumber(),
1796
+ column: classDecl.getStart() - classDecl.getStartLinePos(),
1797
+ suggestion: `Rename to follow ${pattern.description}`
1798
+ })
1799
+ );
1752
1800
  }
1753
1801
  }
1754
1802
  }
@@ -1756,16 +1804,18 @@ var NamingVerifier = class {
1756
1804
  for (const funcDecl of sourceFile.getFunctions()) {
1757
1805
  const name = funcDecl.getName();
1758
1806
  if (name && !pattern.regex.test(name)) {
1759
- violations.push(createViolation({
1760
- decisionId,
1761
- constraintId: constraint.id,
1762
- type: constraint.type,
1763
- severity: constraint.severity,
1764
- message: `Function "${name}" does not follow ${pattern.description} naming convention`,
1765
- file: filePath,
1766
- line: funcDecl.getStartLineNumber(),
1767
- suggestion: `Rename to follow ${pattern.description}`
1768
- }));
1807
+ violations.push(
1808
+ createViolation({
1809
+ decisionId,
1810
+ constraintId: constraint.id,
1811
+ type: constraint.type,
1812
+ severity: constraint.severity,
1813
+ message: `Function "${name}" does not follow ${pattern.description} naming convention`,
1814
+ file: filePath,
1815
+ line: funcDecl.getStartLineNumber(),
1816
+ suggestion: `Rename to follow ${pattern.description}`
1817
+ })
1818
+ );
1769
1819
  }
1770
1820
  }
1771
1821
  }
@@ -1773,16 +1823,18 @@ var NamingVerifier = class {
1773
1823
  for (const interfaceDecl of sourceFile.getInterfaces()) {
1774
1824
  const name = interfaceDecl.getName();
1775
1825
  if (!pattern.regex.test(name)) {
1776
- violations.push(createViolation({
1777
- decisionId,
1778
- constraintId: constraint.id,
1779
- type: constraint.type,
1780
- severity: constraint.severity,
1781
- message: `Interface "${name}" does not follow ${pattern.description} naming convention`,
1782
- file: filePath,
1783
- line: interfaceDecl.getStartLineNumber(),
1784
- suggestion: `Rename to follow ${pattern.description}`
1785
- }));
1826
+ violations.push(
1827
+ createViolation({
1828
+ decisionId,
1829
+ constraintId: constraint.id,
1830
+ type: constraint.type,
1831
+ severity: constraint.severity,
1832
+ message: `Interface "${name}" does not follow ${pattern.description} naming convention`,
1833
+ file: filePath,
1834
+ line: interfaceDecl.getStartLineNumber(),
1835
+ suggestion: `Rename to follow ${pattern.description}`
1836
+ })
1837
+ );
1786
1838
  }
1787
1839
  }
1788
1840
  }
@@ -1790,16 +1842,18 @@ var NamingVerifier = class {
1790
1842
  for (const typeAlias of sourceFile.getTypeAliases()) {
1791
1843
  const name = typeAlias.getName();
1792
1844
  if (!pattern.regex.test(name)) {
1793
- violations.push(createViolation({
1794
- decisionId,
1795
- constraintId: constraint.id,
1796
- type: constraint.type,
1797
- severity: constraint.severity,
1798
- message: `Type "${name}" does not follow ${pattern.description} naming convention`,
1799
- file: filePath,
1800
- line: typeAlias.getStartLineNumber(),
1801
- suggestion: `Rename to follow ${pattern.description}`
1802
- }));
1845
+ violations.push(
1846
+ createViolation({
1847
+ decisionId,
1848
+ constraintId: constraint.id,
1849
+ type: constraint.type,
1850
+ severity: constraint.severity,
1851
+ message: `Type "${name}" does not follow ${pattern.description} naming convention`,
1852
+ file: filePath,
1853
+ line: typeAlias.getStartLineNumber(),
1854
+ suggestion: `Rename to follow ${pattern.description}`
1855
+ })
1856
+ );
1803
1857
  }
1804
1858
  }
1805
1859
  }
@@ -1834,20 +1888,22 @@ var ImportsVerifier = class {
1834
1888
  const ms = importDecl.getModuleSpecifier();
1835
1889
  const start = ms.getStart() + 1;
1836
1890
  const end = ms.getEnd() - 1;
1837
- violations.push(createViolation({
1838
- decisionId,
1839
- constraintId: constraint.id,
1840
- type: constraint.type,
1841
- severity: constraint.severity,
1842
- message: `Relative import "${moduleSpec}" should include a .js extension`,
1843
- file: filePath,
1844
- line: importDecl.getStartLineNumber(),
1845
- suggestion: `Update to "${suggested}"`,
1846
- autofix: {
1847
- description: "Add/normalize .js extension in import specifier",
1848
- edits: [{ start, end, text: suggested }]
1849
- }
1850
- }));
1891
+ violations.push(
1892
+ createViolation({
1893
+ decisionId,
1894
+ constraintId: constraint.id,
1895
+ type: constraint.type,
1896
+ severity: constraint.severity,
1897
+ message: `Relative import "${moduleSpec}" should include a .js extension`,
1898
+ file: filePath,
1899
+ line: importDecl.getStartLineNumber(),
1900
+ suggestion: `Update to "${suggested}"`,
1901
+ autofix: {
1902
+ description: "Add/normalize .js extension in import specifier",
1903
+ edits: [{ start, end, text: suggested }]
1904
+ }
1905
+ })
1906
+ );
1851
1907
  }
1852
1908
  }
1853
1909
  if (rule.includes("barrel") || rule.includes("index")) {
@@ -1856,16 +1912,18 @@ var ImportsVerifier = class {
1856
1912
  if (!moduleSpec.startsWith(".")) continue;
1857
1913
  if (moduleSpec.match(/\.(ts|js|tsx|jsx)$/) || moduleSpec.match(/\/[^/]+$/)) {
1858
1914
  if (!moduleSpec.endsWith("/index") && !moduleSpec.endsWith("index")) {
1859
- violations.push(createViolation({
1860
- decisionId,
1861
- constraintId: constraint.id,
1862
- type: constraint.type,
1863
- severity: constraint.severity,
1864
- message: `Import from "${moduleSpec}" should use barrel (index) import`,
1865
- file: filePath,
1866
- line: importDecl.getStartLineNumber(),
1867
- suggestion: "Import from the parent directory index file instead"
1868
- }));
1915
+ violations.push(
1916
+ createViolation({
1917
+ decisionId,
1918
+ constraintId: constraint.id,
1919
+ type: constraint.type,
1920
+ severity: constraint.severity,
1921
+ message: `Import from "${moduleSpec}" should use barrel (index) import`,
1922
+ file: filePath,
1923
+ line: importDecl.getStartLineNumber(),
1924
+ suggestion: "Import from the parent directory index file instead"
1925
+ })
1926
+ );
1869
1927
  }
1870
1928
  }
1871
1929
  }
@@ -1874,16 +1932,18 @@ var ImportsVerifier = class {
1874
1932
  for (const importDecl of sourceFile.getImportDeclarations()) {
1875
1933
  const moduleSpec = importDecl.getModuleSpecifierValue();
1876
1934
  if (moduleSpec.match(/^\.\.\/\.\.\/\.\.\//)) {
1877
- violations.push(createViolation({
1878
- decisionId,
1879
- constraintId: constraint.id,
1880
- type: constraint.type,
1881
- severity: constraint.severity,
1882
- message: `Deep relative import "${moduleSpec}" should use path alias`,
1883
- file: filePath,
1884
- line: importDecl.getStartLineNumber(),
1885
- suggestion: "Use path alias (e.g., @/module) for deep imports"
1886
- }));
1935
+ violations.push(
1936
+ createViolation({
1937
+ decisionId,
1938
+ constraintId: constraint.id,
1939
+ type: constraint.type,
1940
+ severity: constraint.severity,
1941
+ message: `Deep relative import "${moduleSpec}" should use path alias`,
1942
+ file: filePath,
1943
+ line: importDecl.getStartLineNumber(),
1944
+ suggestion: "Use path alias (e.g., @/module) for deep imports"
1945
+ })
1946
+ );
1887
1947
  }
1888
1948
  }
1889
1949
  }
@@ -1892,16 +1952,18 @@ var ImportsVerifier = class {
1892
1952
  for (const importDecl of sourceFile.getImportDeclarations()) {
1893
1953
  const moduleSpec = importDecl.getModuleSpecifierValue();
1894
1954
  if (moduleSpec.includes(currentFilename.split("/").pop() || "")) {
1895
- violations.push(createViolation({
1896
- decisionId,
1897
- constraintId: constraint.id,
1898
- type: constraint.type,
1899
- severity: constraint.severity,
1900
- message: `Possible circular import detected: "${moduleSpec}"`,
1901
- file: filePath,
1902
- line: importDecl.getStartLineNumber(),
1903
- suggestion: "Review import structure for circular dependencies"
1904
- }));
1955
+ violations.push(
1956
+ createViolation({
1957
+ decisionId,
1958
+ constraintId: constraint.id,
1959
+ type: constraint.type,
1960
+ severity: constraint.severity,
1961
+ message: `Possible circular import detected: "${moduleSpec}"`,
1962
+ file: filePath,
1963
+ line: importDecl.getStartLineNumber(),
1964
+ suggestion: "Review import structure for circular dependencies"
1965
+ })
1966
+ );
1905
1967
  }
1906
1968
  }
1907
1969
  }
@@ -1909,16 +1971,18 @@ var ImportsVerifier = class {
1909
1971
  for (const importDecl of sourceFile.getImportDeclarations()) {
1910
1972
  const namespaceImport = importDecl.getNamespaceImport();
1911
1973
  if (namespaceImport) {
1912
- violations.push(createViolation({
1913
- decisionId,
1914
- constraintId: constraint.id,
1915
- type: constraint.type,
1916
- severity: constraint.severity,
1917
- message: `Namespace import "* as ${namespaceImport.getText()}" should use named imports`,
1918
- file: filePath,
1919
- line: importDecl.getStartLineNumber(),
1920
- suggestion: "Use specific named imports instead of namespace import"
1921
- }));
1974
+ violations.push(
1975
+ createViolation({
1976
+ decisionId,
1977
+ constraintId: constraint.id,
1978
+ type: constraint.type,
1979
+ severity: constraint.severity,
1980
+ message: `Namespace import "* as ${namespaceImport.getText()}" should use named imports`,
1981
+ file: filePath,
1982
+ line: importDecl.getStartLineNumber(),
1983
+ suggestion: "Use specific named imports instead of namespace import"
1984
+ })
1985
+ );
1922
1986
  }
1923
1987
  }
1924
1988
  }
@@ -1944,16 +2008,18 @@ var ErrorsVerifier = class {
1944
2008
  if (!className?.endsWith("Error") && !className?.endsWith("Exception")) continue;
1945
2009
  const extendsClause = classDecl.getExtends();
1946
2010
  if (!extendsClause) {
1947
- violations.push(createViolation({
1948
- decisionId,
1949
- constraintId: constraint.id,
1950
- type: constraint.type,
1951
- severity: constraint.severity,
1952
- message: `Error class "${className}" does not extend any base class`,
1953
- file: filePath,
1954
- line: classDecl.getStartLineNumber(),
1955
- suggestion: requiredBase ? `Extend ${requiredBase}` : "Extend a base error class for consistent error handling"
1956
- }));
2011
+ violations.push(
2012
+ createViolation({
2013
+ decisionId,
2014
+ constraintId: constraint.id,
2015
+ type: constraint.type,
2016
+ severity: constraint.severity,
2017
+ message: `Error class "${className}" does not extend any base class`,
2018
+ file: filePath,
2019
+ line: classDecl.getStartLineNumber(),
2020
+ suggestion: requiredBase ? `Extend ${requiredBase}` : "Extend a base error class for consistent error handling"
2021
+ })
2022
+ );
1957
2023
  } else if (requiredBase) {
1958
2024
  const baseName = extendsClause.getText();
1959
2025
  if (baseName !== requiredBase && baseName !== "Error") {
@@ -1968,16 +2034,18 @@ var ErrorsVerifier = class {
1968
2034
  if (expression) {
1969
2035
  const text = expression.getText();
1970
2036
  if (text.startsWith("new Error(")) {
1971
- violations.push(createViolation({
1972
- decisionId,
1973
- constraintId: constraint.id,
1974
- type: constraint.type,
1975
- severity: constraint.severity,
1976
- message: "Throwing generic Error instead of custom error class",
1977
- file: filePath,
1978
- line: node.getStartLineNumber(),
1979
- suggestion: "Use a custom error class for better error handling"
1980
- }));
2037
+ violations.push(
2038
+ createViolation({
2039
+ decisionId,
2040
+ constraintId: constraint.id,
2041
+ type: constraint.type,
2042
+ severity: constraint.severity,
2043
+ message: "Throwing generic Error instead of custom error class",
2044
+ file: filePath,
2045
+ line: node.getStartLineNumber(),
2046
+ suggestion: "Use a custom error class for better error handling"
2047
+ })
2048
+ );
1981
2049
  }
1982
2050
  }
1983
2051
  }
@@ -1991,16 +2059,18 @@ var ErrorsVerifier = class {
1991
2059
  const block = catchClause.getBlock();
1992
2060
  const statements = block.getStatements();
1993
2061
  if (statements.length === 0) {
1994
- violations.push(createViolation({
1995
- decisionId,
1996
- constraintId: constraint.id,
1997
- type: constraint.type,
1998
- severity: constraint.severity,
1999
- message: "Empty catch block swallows error without handling",
2000
- file: filePath,
2001
- line: catchClause.getStartLineNumber(),
2002
- suggestion: "Add error handling, logging, or rethrow the error"
2003
- }));
2062
+ violations.push(
2063
+ createViolation({
2064
+ decisionId,
2065
+ constraintId: constraint.id,
2066
+ type: constraint.type,
2067
+ severity: constraint.severity,
2068
+ message: "Empty catch block swallows error without handling",
2069
+ file: filePath,
2070
+ line: catchClause.getStartLineNumber(),
2071
+ suggestion: "Add error handling, logging, or rethrow the error"
2072
+ })
2073
+ );
2004
2074
  }
2005
2075
  }
2006
2076
  }
@@ -2012,16 +2082,18 @@ var ErrorsVerifier = class {
2012
2082
  const expression = node.getExpression();
2013
2083
  const text = expression.getText();
2014
2084
  if (text === "console.error" || text === "console.log") {
2015
- violations.push(createViolation({
2016
- decisionId,
2017
- constraintId: constraint.id,
2018
- type: constraint.type,
2019
- severity: constraint.severity,
2020
- message: `Using ${text} instead of proper logging`,
2021
- file: filePath,
2022
- line: node.getStartLineNumber(),
2023
- suggestion: "Use a proper logging library"
2024
- }));
2085
+ violations.push(
2086
+ createViolation({
2087
+ decisionId,
2088
+ constraintId: constraint.id,
2089
+ type: constraint.type,
2090
+ severity: constraint.severity,
2091
+ message: `Using ${text} instead of proper logging`,
2092
+ file: filePath,
2093
+ line: node.getStartLineNumber(),
2094
+ suggestion: "Use a proper logging library"
2095
+ })
2096
+ );
2025
2097
  }
2026
2098
  }
2027
2099
  });
@@ -2052,16 +2124,18 @@ var RegexVerifier = class {
2052
2124
  while ((match = regex.exec(fileText)) !== null) {
2053
2125
  const beforeMatch = fileText.substring(0, match.index);
2054
2126
  const lineNumber = beforeMatch.split("\n").length;
2055
- violations.push(createViolation({
2056
- decisionId,
2057
- constraintId: constraint.id,
2058
- type: constraint.type,
2059
- severity: constraint.severity,
2060
- message: `Found forbidden pattern: "${match[0]}"`,
2061
- file: filePath,
2062
- line: lineNumber,
2063
- suggestion: `Remove or replace the pattern matching /${patternToForbid}/`
2064
- }));
2127
+ violations.push(
2128
+ createViolation({
2129
+ decisionId,
2130
+ constraintId: constraint.id,
2131
+ type: constraint.type,
2132
+ severity: constraint.severity,
2133
+ message: `Found forbidden pattern: "${match[0]}"`,
2134
+ file: filePath,
2135
+ line: lineNumber,
2136
+ suggestion: `Remove or replace the pattern matching /${patternToForbid}/`
2137
+ })
2138
+ );
2065
2139
  }
2066
2140
  } catch {
2067
2141
  }
@@ -2071,15 +2145,17 @@ var RegexVerifier = class {
2071
2145
  try {
2072
2146
  const regex = new RegExp(patternToRequire);
2073
2147
  if (!regex.test(fileText)) {
2074
- violations.push(createViolation({
2075
- decisionId,
2076
- constraintId: constraint.id,
2077
- type: constraint.type,
2078
- severity: constraint.severity,
2079
- message: `File does not contain required pattern: /${patternToRequire}/`,
2080
- file: filePath,
2081
- suggestion: `Add code matching /${patternToRequire}/`
2082
- }));
2148
+ violations.push(
2149
+ createViolation({
2150
+ decisionId,
2151
+ constraintId: constraint.id,
2152
+ type: constraint.type,
2153
+ severity: constraint.severity,
2154
+ message: `File does not contain required pattern: /${patternToRequire}/`,
2155
+ file: filePath,
2156
+ suggestion: `Add code matching /${patternToRequire}/`
2157
+ })
2158
+ );
2083
2159
  }
2084
2160
  } catch {
2085
2161
  }
@@ -2218,7 +2294,9 @@ function parseBannedDependency(rule) {
2218
2294
  return value.length > 0 ? value : null;
2219
2295
  }
2220
2296
  function parseLayerRule(rule) {
2221
- const m = rule.match(/(\w+)\s{1,5}layer\s{1,5}cannot\s{1,5}depend\s{1,5}on\s{1,5}(\w+)\s{1,5}layer/i);
2297
+ const m = rule.match(
2298
+ /(\w+)\s{1,5}layer\s{1,5}cannot\s{1,5}depend\s{1,5}on\s{1,5}(\w+)\s{1,5}layer/i
2299
+ );
2222
2300
  const fromLayer = m?.[1]?.toLowerCase();
2223
2301
  const toLayer = m?.[2]?.toLowerCase();
2224
2302
  if (!fromLayer || !toLayer) return null;
@@ -2244,22 +2322,25 @@ var DependencyVerifier = class {
2244
2322
  const sccs = tarjanScc(graph);
2245
2323
  const current = projectFilePath;
2246
2324
  for (const scc of sccs) {
2247
- const hasSelfLoop = scc.length === 1 && (graph.get(scc[0])?.has(scc[0]) ?? false);
2325
+ const first = scc[0];
2326
+ const hasSelfLoop = first !== void 0 && scc.length === 1 && (graph.get(first)?.has(first) ?? false);
2248
2327
  const isCycle = scc.length > 1 || hasSelfLoop;
2249
2328
  if (!isCycle) continue;
2250
2329
  if (!scc.includes(current)) continue;
2251
2330
  const sorted = [...scc].sort();
2252
2331
  if (sorted[0] !== current) continue;
2253
- violations.push(createViolation({
2254
- decisionId,
2255
- constraintId: constraint.id,
2256
- type: constraint.type,
2257
- severity: constraint.severity,
2258
- message: `Circular dependency detected across: ${sorted.join(" -> ")}`,
2259
- file: filePath,
2260
- line: 1,
2261
- suggestion: "Break the cycle by extracting shared abstractions or reversing the dependency"
2262
- }));
2332
+ violations.push(
2333
+ createViolation({
2334
+ decisionId,
2335
+ constraintId: constraint.id,
2336
+ type: constraint.type,
2337
+ severity: constraint.severity,
2338
+ message: `Circular dependency detected across: ${sorted.join(" -> ")}`,
2339
+ file: filePath,
2340
+ line: 1,
2341
+ suggestion: "Break the cycle by extracting shared abstractions or reversing the dependency"
2342
+ })
2343
+ );
2263
2344
  }
2264
2345
  }
2265
2346
  const layerRule = parseLayerRule(rule);
@@ -2269,16 +2350,18 @@ var DependencyVerifier = class {
2269
2350
  const resolved = resolveToSourceFilePath(project, projectFilePath, moduleSpec);
2270
2351
  if (!resolved) continue;
2271
2352
  if (fileInLayer(resolved, layerRule.toLayer)) {
2272
- violations.push(createViolation({
2273
- decisionId,
2274
- constraintId: constraint.id,
2275
- type: constraint.type,
2276
- severity: constraint.severity,
2277
- message: `Layer violation: ${layerRule.fromLayer} depends on ${layerRule.toLayer} via import "${moduleSpec}"`,
2278
- file: filePath,
2279
- line: importDecl.getStartLineNumber(),
2280
- suggestion: `Refactor to remove dependency from ${layerRule.fromLayer} to ${layerRule.toLayer}`
2281
- }));
2353
+ violations.push(
2354
+ createViolation({
2355
+ decisionId,
2356
+ constraintId: constraint.id,
2357
+ type: constraint.type,
2358
+ severity: constraint.severity,
2359
+ message: `Layer violation: ${layerRule.fromLayer} depends on ${layerRule.toLayer} via import "${moduleSpec}"`,
2360
+ file: filePath,
2361
+ line: importDecl.getStartLineNumber(),
2362
+ suggestion: `Refactor to remove dependency from ${layerRule.fromLayer} to ${layerRule.toLayer}`
2363
+ })
2364
+ );
2282
2365
  }
2283
2366
  }
2284
2367
  }
@@ -2288,16 +2371,18 @@ var DependencyVerifier = class {
2288
2371
  for (const importDecl of sourceFile.getImportDeclarations()) {
2289
2372
  const moduleSpec = importDecl.getModuleSpecifierValue();
2290
2373
  if (moduleSpec.toLowerCase().includes(bannedLower)) {
2291
- violations.push(createViolation({
2292
- decisionId,
2293
- constraintId: constraint.id,
2294
- type: constraint.type,
2295
- severity: constraint.severity,
2296
- message: `Banned dependency import detected: "${moduleSpec}"`,
2297
- file: filePath,
2298
- line: importDecl.getStartLineNumber(),
2299
- suggestion: `Remove or replace dependency "${banned}"`
2300
- }));
2374
+ violations.push(
2375
+ createViolation({
2376
+ decisionId,
2377
+ constraintId: constraint.id,
2378
+ type: constraint.type,
2379
+ severity: constraint.severity,
2380
+ message: `Banned dependency import detected: "${moduleSpec}"`,
2381
+ file: filePath,
2382
+ line: importDecl.getStartLineNumber(),
2383
+ suggestion: `Remove or replace dependency "${banned}"`
2384
+ })
2385
+ );
2301
2386
  }
2302
2387
  }
2303
2388
  }
@@ -2308,16 +2393,18 @@ var DependencyVerifier = class {
2308
2393
  if (!moduleSpec.startsWith(".")) continue;
2309
2394
  const depth = (moduleSpec.match(/\.\.\//g) || []).length;
2310
2395
  if (depth > maxDepth) {
2311
- violations.push(createViolation({
2312
- decisionId,
2313
- constraintId: constraint.id,
2314
- type: constraint.type,
2315
- severity: constraint.severity,
2316
- message: `Import depth ${depth} exceeds maximum ${maxDepth}: "${moduleSpec}"`,
2317
- file: filePath,
2318
- line: importDecl.getStartLineNumber(),
2319
- suggestion: "Use a shallower module boundary (or introduce a public entrypoint for this dependency)"
2320
- }));
2396
+ violations.push(
2397
+ createViolation({
2398
+ decisionId,
2399
+ constraintId: constraint.id,
2400
+ type: constraint.type,
2401
+ severity: constraint.severity,
2402
+ message: `Import depth ${depth} exceeds maximum ${maxDepth}: "${moduleSpec}"`,
2403
+ file: filePath,
2404
+ line: importDecl.getStartLineNumber(),
2405
+ suggestion: "Use a shallower module boundary (or introduce a public entrypoint for this dependency)"
2406
+ })
2407
+ );
2321
2408
  }
2322
2409
  }
2323
2410
  }
@@ -2326,7 +2413,9 @@ var DependencyVerifier = class {
2326
2413
  };
2327
2414
 
2328
2415
  // src/verification/verifiers/complexity.ts
2329
- import { Node as Node4 } from "ts-morph";
2416
+ import {
2417
+ Node as Node4
2418
+ } from "ts-morph";
2330
2419
  import { SyntaxKind as SyntaxKind2 } from "ts-morph";
2331
2420
  function parseLimit(rule, pattern) {
2332
2421
  const m = rule.match(pattern);
@@ -2410,16 +2499,18 @@ var ComplexityVerifier = class {
2410
2499
  if (maxLines !== null) {
2411
2500
  const lineCount = getFileLineCount(sourceFile.getFullText());
2412
2501
  if (lineCount > maxLines) {
2413
- violations.push(createViolation({
2414
- decisionId,
2415
- constraintId: constraint.id,
2416
- type: constraint.type,
2417
- severity: constraint.severity,
2418
- message: `File has ${lineCount} lines which exceeds maximum ${maxLines}`,
2419
- file: filePath,
2420
- line: 1,
2421
- suggestion: "Split the file into smaller modules"
2422
- }));
2502
+ violations.push(
2503
+ createViolation({
2504
+ decisionId,
2505
+ constraintId: constraint.id,
2506
+ type: constraint.type,
2507
+ severity: constraint.severity,
2508
+ message: `File has ${lineCount} lines which exceeds maximum ${maxLines}`,
2509
+ file: filePath,
2510
+ line: 1,
2511
+ suggestion: "Split the file into smaller modules"
2512
+ })
2513
+ );
2423
2514
  }
2424
2515
  }
2425
2516
  const functionLikes = [
@@ -2433,46 +2524,52 @@ var ComplexityVerifier = class {
2433
2524
  if (maxComplexity !== null) {
2434
2525
  const complexity = calculateCyclomaticComplexity(fn);
2435
2526
  if (complexity > maxComplexity) {
2436
- violations.push(createViolation({
2437
- decisionId,
2438
- constraintId: constraint.id,
2439
- type: constraint.type,
2440
- severity: constraint.severity,
2441
- message: `Function ${fnName} has cyclomatic complexity ${complexity} which exceeds maximum ${maxComplexity}`,
2442
- file: filePath,
2443
- line: fn.getStartLineNumber(),
2444
- suggestion: "Refactor to reduce branching or extract smaller functions"
2445
- }));
2527
+ violations.push(
2528
+ createViolation({
2529
+ decisionId,
2530
+ constraintId: constraint.id,
2531
+ type: constraint.type,
2532
+ severity: constraint.severity,
2533
+ message: `Function ${fnName} has cyclomatic complexity ${complexity} which exceeds maximum ${maxComplexity}`,
2534
+ file: filePath,
2535
+ line: fn.getStartLineNumber(),
2536
+ suggestion: "Refactor to reduce branching or extract smaller functions"
2537
+ })
2538
+ );
2446
2539
  }
2447
2540
  }
2448
2541
  if (maxParams !== null) {
2449
2542
  const paramCount = fn.getParameters().length;
2450
2543
  if (paramCount > maxParams) {
2451
- violations.push(createViolation({
2452
- decisionId,
2453
- constraintId: constraint.id,
2454
- type: constraint.type,
2455
- severity: constraint.severity,
2456
- message: `Function ${fnName} has ${paramCount} parameters which exceeds maximum ${maxParams}`,
2457
- file: filePath,
2458
- line: fn.getStartLineNumber(),
2459
- suggestion: "Consider grouping parameters into an options object"
2460
- }));
2544
+ violations.push(
2545
+ createViolation({
2546
+ decisionId,
2547
+ constraintId: constraint.id,
2548
+ type: constraint.type,
2549
+ severity: constraint.severity,
2550
+ message: `Function ${fnName} has ${paramCount} parameters which exceeds maximum ${maxParams}`,
2551
+ file: filePath,
2552
+ line: fn.getStartLineNumber(),
2553
+ suggestion: "Consider grouping parameters into an options object"
2554
+ })
2555
+ );
2461
2556
  }
2462
2557
  }
2463
2558
  if (maxNesting !== null) {
2464
2559
  const depth = maxNestingDepth(fn);
2465
2560
  if (depth > maxNesting) {
2466
- violations.push(createViolation({
2467
- decisionId,
2468
- constraintId: constraint.id,
2469
- type: constraint.type,
2470
- severity: constraint.severity,
2471
- message: `Function ${fnName} has nesting depth ${depth} which exceeds maximum ${maxNesting}`,
2472
- file: filePath,
2473
- line: fn.getStartLineNumber(),
2474
- suggestion: "Reduce nesting by using early returns or extracting functions"
2475
- }));
2561
+ violations.push(
2562
+ createViolation({
2563
+ decisionId,
2564
+ constraintId: constraint.id,
2565
+ type: constraint.type,
2566
+ severity: constraint.severity,
2567
+ message: `Function ${fnName} has nesting depth ${depth} which exceeds maximum ${maxNesting}`,
2568
+ file: filePath,
2569
+ line: fn.getStartLineNumber(),
2570
+ suggestion: "Reduce nesting by using early returns or extracting functions"
2571
+ })
2572
+ );
2476
2573
  }
2477
2574
  }
2478
2575
  }
@@ -2508,48 +2605,54 @@ var SecurityVerifier = class {
2508
2605
  if (!init || !isStringLiteralLike(init)) continue;
2509
2606
  const value = init.getText().slice(1, -1);
2510
2607
  if (value.length === 0) continue;
2511
- violations.push(createViolation({
2512
- decisionId,
2513
- constraintId: constraint.id,
2514
- type: constraint.type,
2515
- severity: constraint.severity,
2516
- message: `Possible hardcoded secret in variable "${name}"`,
2517
- file: filePath,
2518
- line: vd.getStartLineNumber(),
2519
- suggestion: "Move secrets to environment variables or a secret manager"
2520
- }));
2608
+ violations.push(
2609
+ createViolation({
2610
+ decisionId,
2611
+ constraintId: constraint.id,
2612
+ type: constraint.type,
2613
+ severity: constraint.severity,
2614
+ message: `Possible hardcoded secret in variable "${name}"`,
2615
+ file: filePath,
2616
+ line: vd.getStartLineNumber(),
2617
+ suggestion: "Move secrets to environment variables or a secret manager"
2618
+ })
2619
+ );
2521
2620
  }
2522
2621
  for (const pa of sourceFile.getDescendantsOfKind(SyntaxKind3.PropertyAssignment)) {
2523
2622
  const propName = pa.getNameNode().getText();
2524
2623
  if (!SECRET_NAME_RE.test(propName)) continue;
2525
2624
  const init = pa.getInitializer();
2526
2625
  if (!init || !isStringLiteralLike(init)) continue;
2527
- violations.push(createViolation({
2528
- decisionId,
2529
- constraintId: constraint.id,
2530
- type: constraint.type,
2531
- severity: constraint.severity,
2532
- message: `Possible hardcoded secret in object property ${propName}`,
2533
- file: filePath,
2534
- line: pa.getStartLineNumber(),
2535
- suggestion: "Move secrets to environment variables or a secret manager"
2536
- }));
2626
+ violations.push(
2627
+ createViolation({
2628
+ decisionId,
2629
+ constraintId: constraint.id,
2630
+ type: constraint.type,
2631
+ severity: constraint.severity,
2632
+ message: `Possible hardcoded secret in object property ${propName}`,
2633
+ file: filePath,
2634
+ line: pa.getStartLineNumber(),
2635
+ suggestion: "Move secrets to environment variables or a secret manager"
2636
+ })
2637
+ );
2537
2638
  }
2538
2639
  }
2539
2640
  if (checkEval) {
2540
2641
  for (const call of sourceFile.getDescendantsOfKind(SyntaxKind3.CallExpression)) {
2541
2642
  const exprText = call.getExpression().getText();
2542
2643
  if (exprText === "eval" || exprText === "Function") {
2543
- violations.push(createViolation({
2544
- decisionId,
2545
- constraintId: constraint.id,
2546
- type: constraint.type,
2547
- severity: constraint.severity,
2548
- message: `Unsafe dynamic code execution via ${exprText}()`,
2549
- file: filePath,
2550
- line: call.getStartLineNumber(),
2551
- suggestion: "Avoid eval/Function; use safer alternatives"
2552
- }));
2644
+ violations.push(
2645
+ createViolation({
2646
+ decisionId,
2647
+ constraintId: constraint.id,
2648
+ type: constraint.type,
2649
+ severity: constraint.severity,
2650
+ message: `Unsafe dynamic code execution via ${exprText}()`,
2651
+ file: filePath,
2652
+ line: call.getStartLineNumber(),
2653
+ suggestion: "Avoid eval/Function; use safer alternatives"
2654
+ })
2655
+ );
2553
2656
  }
2554
2657
  }
2555
2658
  }
@@ -2559,29 +2662,33 @@ var SecurityVerifier = class {
2559
2662
  const propertyAccess = left.asKind(SyntaxKind3.PropertyAccessExpression);
2560
2663
  if (!propertyAccess) continue;
2561
2664
  if (propertyAccess.getName() === "innerHTML") {
2562
- violations.push(createViolation({
2665
+ violations.push(
2666
+ createViolation({
2667
+ decisionId,
2668
+ constraintId: constraint.id,
2669
+ type: constraint.type,
2670
+ severity: constraint.severity,
2671
+ message: "Potential XSS: assignment to innerHTML",
2672
+ file: filePath,
2673
+ line: bin.getStartLineNumber(),
2674
+ suggestion: "Prefer textContent or a safe templating/escaping strategy"
2675
+ })
2676
+ );
2677
+ }
2678
+ }
2679
+ if (sourceFile.getFullText().includes("dangerouslySetInnerHTML")) {
2680
+ violations.push(
2681
+ createViolation({
2563
2682
  decisionId,
2564
2683
  constraintId: constraint.id,
2565
2684
  type: constraint.type,
2566
2685
  severity: constraint.severity,
2567
- message: "Potential XSS: assignment to innerHTML",
2686
+ message: "Potential XSS: usage of dangerouslySetInnerHTML",
2568
2687
  file: filePath,
2569
- line: bin.getStartLineNumber(),
2570
- suggestion: "Prefer textContent or a safe templating/escaping strategy"
2571
- }));
2572
- }
2573
- }
2574
- if (sourceFile.getFullText().includes("dangerouslySetInnerHTML")) {
2575
- violations.push(createViolation({
2576
- decisionId,
2577
- constraintId: constraint.id,
2578
- type: constraint.type,
2579
- severity: constraint.severity,
2580
- message: "Potential XSS: usage of dangerouslySetInnerHTML",
2581
- file: filePath,
2582
- line: 1,
2583
- suggestion: "Avoid dangerouslySetInnerHTML or ensure content is sanitized"
2584
- }));
2688
+ line: 1,
2689
+ suggestion: "Avoid dangerouslySetInnerHTML or ensure content is sanitized"
2690
+ })
2691
+ );
2585
2692
  }
2586
2693
  }
2587
2694
  if (checkSql) {
@@ -2600,31 +2707,35 @@ var SecurityVerifier = class {
2600
2707
  if (!text.includes("select") && !text.includes("insert") && !text.includes("update") && !text.includes("delete")) {
2601
2708
  continue;
2602
2709
  }
2603
- violations.push(createViolation({
2604
- decisionId,
2605
- constraintId: constraint.id,
2606
- type: constraint.type,
2607
- severity: constraint.severity,
2608
- message: "Potential SQL injection: dynamically constructed SQL query",
2609
- file: filePath,
2610
- line: call.getStartLineNumber(),
2611
- suggestion: "Use parameterized queries / prepared statements"
2612
- }));
2710
+ violations.push(
2711
+ createViolation({
2712
+ decisionId,
2713
+ constraintId: constraint.id,
2714
+ type: constraint.type,
2715
+ severity: constraint.severity,
2716
+ message: "Potential SQL injection: dynamically constructed SQL query",
2717
+ file: filePath,
2718
+ line: call.getStartLineNumber(),
2719
+ suggestion: "Use parameterized queries / prepared statements"
2720
+ })
2721
+ );
2613
2722
  }
2614
2723
  }
2615
2724
  if (checkProto) {
2616
2725
  const text = sourceFile.getFullText();
2617
2726
  if (text.includes("__proto__") || text.includes("constructor.prototype")) {
2618
- violations.push(createViolation({
2619
- decisionId,
2620
- constraintId: constraint.id,
2621
- type: constraint.type,
2622
- severity: constraint.severity,
2623
- message: "Potential prototype pollution pattern detected",
2624
- file: filePath,
2625
- line: 1,
2626
- suggestion: "Avoid writing to __proto__/prototype; validate object keys"
2627
- }));
2727
+ violations.push(
2728
+ createViolation({
2729
+ decisionId,
2730
+ constraintId: constraint.id,
2731
+ type: constraint.type,
2732
+ severity: constraint.severity,
2733
+ message: "Potential prototype pollution pattern detected",
2734
+ file: filePath,
2735
+ line: 1,
2736
+ suggestion: "Avoid writing to __proto__/prototype; validate object keys"
2737
+ })
2738
+ );
2628
2739
  }
2629
2740
  }
2630
2741
  return violations;
@@ -2664,16 +2775,18 @@ var ApiVerifier = class {
2664
2775
  const pathValue = stringLiteral.getLiteralValue();
2665
2776
  if (typeof pathValue !== "string") continue;
2666
2777
  if (!isKebabPath(pathValue)) {
2667
- violations.push(createViolation({
2668
- decisionId,
2669
- constraintId: constraint.id,
2670
- type: constraint.type,
2671
- severity: constraint.severity,
2672
- message: `Endpoint path "${pathValue}" is not kebab-case`,
2673
- file: filePath,
2674
- line: call.getStartLineNumber(),
2675
- suggestion: "Use lowercase and hyphens in static path segments (e.g., /user-settings)"
2676
- }));
2778
+ violations.push(
2779
+ createViolation({
2780
+ decisionId,
2781
+ constraintId: constraint.id,
2782
+ type: constraint.type,
2783
+ severity: constraint.severity,
2784
+ message: `Endpoint path "${pathValue}" is not kebab-case`,
2785
+ file: filePath,
2786
+ line: call.getStartLineNumber(),
2787
+ suggestion: "Use lowercase and hyphens in static path segments (e.g., /user-settings)"
2788
+ })
2789
+ );
2677
2790
  }
2678
2791
  }
2679
2792
  return violations;
@@ -2685,10 +2798,36 @@ import { existsSync } from "fs";
2685
2798
  import { join as join5 } from "path";
2686
2799
  import { pathToFileURL } from "url";
2687
2800
  import fg2 from "fast-glob";
2801
+
2802
+ // src/utils/logger.ts
2803
+ import pino from "pino";
2804
+ var defaultOptions = {
2805
+ level: process.env.SPECBRIDGE_LOG_LEVEL || "info",
2806
+ timestamp: pino.stdTimeFunctions.isoTime,
2807
+ base: {
2808
+ service: "specbridge"
2809
+ }
2810
+ };
2811
+ var destination = pino.destination({
2812
+ fd: 2,
2813
+ // stderr
2814
+ sync: false
2815
+ });
2816
+ var rootLogger = pino(defaultOptions, destination);
2817
+ function getLogger(bindings) {
2818
+ if (!bindings) {
2819
+ return rootLogger;
2820
+ }
2821
+ return rootLogger.child(bindings);
2822
+ }
2823
+ var logger = getLogger();
2824
+
2825
+ // src/verification/plugins/loader.ts
2688
2826
  var PluginLoader = class {
2689
2827
  plugins = /* @__PURE__ */ new Map();
2690
2828
  loaded = false;
2691
2829
  loadErrors = [];
2830
+ logger = getLogger({ module: "verification.plugins.loader" });
2692
2831
  /**
2693
2832
  * Load all plugins from the specified base path
2694
2833
  *
@@ -2711,15 +2850,15 @@ var PluginLoader = class {
2711
2850
  } catch (error) {
2712
2851
  const message = error instanceof Error ? error.message : String(error);
2713
2852
  this.loadErrors.push({ file, error: message });
2714
- console.warn(`Failed to load plugin from ${file}: ${message}`);
2853
+ this.logger.warn({ file, error: message }, "Failed to load plugin");
2715
2854
  }
2716
2855
  }
2717
2856
  this.loaded = true;
2718
2857
  if (this.plugins.size > 0) {
2719
- console.error(`Loaded ${this.plugins.size} custom verifier(s)`);
2858
+ this.logger.info({ count: this.plugins.size }, "Loaded custom verifier plugins");
2720
2859
  }
2721
2860
  if (this.loadErrors.length > 0) {
2722
- console.warn(`Failed to load ${this.loadErrors.length} plugin(s)`);
2861
+ this.logger.warn({ count: this.loadErrors.length }, "Plugin load failures");
2723
2862
  }
2724
2863
  }
2725
2864
  /**
@@ -2828,7 +2967,10 @@ var PluginLoader = class {
2828
2967
  return { success: false, error: `Plugin ${id} requires params but none were provided` };
2829
2968
  }
2830
2969
  if (typeof plugin.paramsSchema !== "object" || !plugin.paramsSchema || !("parse" in plugin.paramsSchema)) {
2831
- return { success: false, error: `Plugin ${id} has invalid paramsSchema (must be a Zod schema)` };
2970
+ return {
2971
+ success: false,
2972
+ error: `Plugin ${id} has invalid paramsSchema (must be a Zod schema)`
2973
+ };
2832
2974
  }
2833
2975
  const schema = plugin.paramsSchema;
2834
2976
  if (schema.safeParse) {
@@ -2895,8 +3037,9 @@ var builtinVerifiers = {
2895
3037
  };
2896
3038
  var verifierInstances = /* @__PURE__ */ new Map();
2897
3039
  function getVerifier(id) {
2898
- if (verifierInstances.has(id)) {
2899
- return verifierInstances.get(id);
3040
+ const pooled = verifierInstances.get(id);
3041
+ if (pooled) {
3042
+ return pooled;
2900
3043
  }
2901
3044
  const pluginLoader2 = getPluginLoader();
2902
3045
  const customVerifier = pluginLoader2.getVerifier(id);
@@ -3098,6 +3241,7 @@ var VerificationEngine = class {
3098
3241
  astCache;
3099
3242
  resultsCache;
3100
3243
  pluginsLoaded = false;
3244
+ logger = getLogger({ module: "verification.engine" });
3101
3245
  constructor(registry) {
3102
3246
  this.registry = registry || createRegistry();
3103
3247
  this.project = new Project2({
@@ -3271,12 +3415,14 @@ var VerificationEngine = class {
3271
3415
  );
3272
3416
  if (!verifier) {
3273
3417
  const requestedVerifier = constraint.check?.verifier || constraint.verifier || "auto-detected";
3274
- console.warn(
3275
- chalk3.yellow(
3276
- `Warning: No verifier found for ${decision.metadata.id}/${constraint.id}
3277
- Requested: ${requestedVerifier}
3278
- Available: ${getVerifierIds().join(", ")}`
3279
- )
3418
+ this.logger.warn(
3419
+ {
3420
+ decisionId: decision.metadata.id,
3421
+ constraintId: constraint.id,
3422
+ requestedVerifier,
3423
+ availableVerifiers: getVerifierIds()
3424
+ },
3425
+ "No verifier found for constraint"
3280
3426
  );
3281
3427
  warnings.push({
3282
3428
  type: "missing_verifier",
@@ -3323,7 +3469,10 @@ var VerificationEngine = class {
3323
3469
  }
3324
3470
  if (constraint.check?.verifier && constraint.check?.params) {
3325
3471
  const pluginLoader2 = getPluginLoader();
3326
- const validationResult = pluginLoader2.validateParams(constraint.check.verifier, constraint.check.params);
3472
+ const validationResult = pluginLoader2.validateParams(
3473
+ constraint.check.verifier,
3474
+ constraint.check.params
3475
+ );
3327
3476
  if (!validationResult.success) {
3328
3477
  warnings.push({
3329
3478
  type: "invalid_params",
@@ -3383,17 +3532,17 @@ var VerificationEngine = class {
3383
3532
  } catch (error) {
3384
3533
  const errorMessage = error instanceof Error ? error.message : String(error);
3385
3534
  const errorStack = error instanceof Error ? error.stack : void 0;
3386
- console.error(
3387
- chalk3.red(
3388
- `Error: Verifier '${verifier.id}' failed
3389
- File: ${filePath}
3390
- Decision: ${decision.metadata.id}/${constraint.id}
3391
- Error: ${errorMessage}`
3392
- )
3535
+ this.logger.error(
3536
+ {
3537
+ verifierId: verifier.id,
3538
+ filePath,
3539
+ decisionId: decision.metadata.id,
3540
+ constraintId: constraint.id,
3541
+ error: errorMessage,
3542
+ stack: errorStack
3543
+ },
3544
+ "Verifier execution failed"
3393
3545
  );
3394
- if (errorStack) {
3395
- console.error(chalk3.dim(errorStack));
3396
- }
3397
3546
  errors.push({
3398
3547
  type: "verifier_exception",
3399
3548
  message: `Verifier '${verifier.id}' failed: ${errorMessage}`,
@@ -3509,8 +3658,14 @@ var AutofixEngine = class {
3509
3658
  const edits = [];
3510
3659
  for (const violation of fileViolations) {
3511
3660
  const fix = violation.autofix;
3661
+ if (!fix) {
3662
+ skippedViolations++;
3663
+ continue;
3664
+ }
3512
3665
  if (options.interactive) {
3513
- const ok = await confirmFix(`Apply fix: ${fix.description} (${filePath}:${violation.line ?? 1})?`);
3666
+ const ok = await confirmFix(
3667
+ `Apply fix: ${fix.description} (${filePath}:${violation.line ?? 1})?`
3668
+ );
3514
3669
  if (!ok) {
3515
3670
  skippedViolations++;
3516
3671
  continue;
@@ -3541,7 +3696,11 @@ import { resolve } from "path";
3541
3696
  var execFileAsync = promisify(execFile);
3542
3697
  async function getChangedFiles(cwd) {
3543
3698
  try {
3544
- const { stdout: stdout2 } = await execFileAsync("git", ["diff", "--name-only", "--diff-filter=AM", "HEAD"], { cwd });
3699
+ const { stdout: stdout2 } = await execFileAsync(
3700
+ "git",
3701
+ ["diff", "--name-only", "--diff-filter=AM", "HEAD"],
3702
+ { cwd }
3703
+ );
3545
3704
  const rel = stdout2.trim().split("\n").map((s) => s.trim()).filter(Boolean);
3546
3705
  const abs = [];
3547
3706
  for (const file of rel) {
@@ -3555,7 +3714,7 @@ async function getChangedFiles(cwd) {
3555
3714
  }
3556
3715
 
3557
3716
  // src/verification/explain.ts
3558
- import chalk4 from "chalk";
3717
+ import chalk3 from "chalk";
3559
3718
  var ExplainReporter = class {
3560
3719
  entries = [];
3561
3720
  /**
@@ -3569,10 +3728,10 @@ var ExplainReporter = class {
3569
3728
  */
3570
3729
  print() {
3571
3730
  if (this.entries.length === 0) {
3572
- console.log(chalk4.dim("No constraints were evaluated."));
3731
+ console.log(chalk3.dim("No constraints were evaluated."));
3573
3732
  return;
3574
3733
  }
3575
- console.log(chalk4.bold("\n=== Verification Explanation ===\n"));
3734
+ console.log(chalk3.bold("\n=== Verification Explanation ===\n"));
3576
3735
  const byFile = /* @__PURE__ */ new Map();
3577
3736
  for (const entry of this.entries) {
3578
3737
  const existing = byFile.get(entry.file) || [];
@@ -3580,22 +3739,22 @@ var ExplainReporter = class {
3580
3739
  byFile.set(entry.file, existing);
3581
3740
  }
3582
3741
  for (const [file, entries] of byFile) {
3583
- console.log(chalk4.underline(file));
3742
+ console.log(chalk3.underline(file));
3584
3743
  for (const entry of entries) {
3585
- const icon = entry.applied ? chalk4.green("\u2713") : chalk4.dim("\u2298");
3744
+ const icon = entry.applied ? chalk3.green("\u2713") : chalk3.dim("\u2298");
3586
3745
  const constraintId = `${entry.decision.metadata.id}/${entry.constraint.id}`;
3587
3746
  console.log(` ${icon} ${constraintId}`);
3588
- console.log(chalk4.dim(` ${entry.reason}`));
3747
+ console.log(chalk3.dim(` ${entry.reason}`));
3589
3748
  if (entry.applied && entry.selectedVerifier) {
3590
- console.log(chalk4.dim(` Verifier: ${entry.selectedVerifier}`));
3749
+ console.log(chalk3.dim(` Verifier: ${entry.selectedVerifier}`));
3591
3750
  if (entry.verifierOutput) {
3592
3751
  if (entry.verifierOutput.error) {
3593
- console.log(chalk4.red(` Error: ${entry.verifierOutput.error}`));
3752
+ console.log(chalk3.red(` Error: ${entry.verifierOutput.error}`));
3594
3753
  } else {
3595
3754
  const violationText = entry.verifierOutput.violations === 1 ? "1 violation" : `${entry.verifierOutput.violations} violations`;
3596
- const resultColor = entry.verifierOutput.violations > 0 ? chalk4.red : chalk4.green;
3755
+ const resultColor = entry.verifierOutput.violations > 0 ? chalk3.red : chalk3.green;
3597
3756
  console.log(
3598
- chalk4.dim(` Result: `) + resultColor(violationText) + chalk4.dim(` in ${entry.verifierOutput.duration}ms`)
3757
+ chalk3.dim(` Result: `) + resultColor(violationText) + chalk3.dim(` in ${entry.verifierOutput.duration}ms`)
3599
3758
  );
3600
3759
  }
3601
3760
  }
@@ -3605,9 +3764,9 @@ var ExplainReporter = class {
3605
3764
  }
3606
3765
  const applied = this.entries.filter((e) => e.applied).length;
3607
3766
  const skipped = this.entries.filter((e) => !e.applied).length;
3608
- console.log(chalk4.bold("Summary:"));
3609
- console.log(` Constraints Applied: ${chalk4.green(applied)}`);
3610
- console.log(` Constraints Skipped: ${chalk4.dim(skipped)}`);
3767
+ console.log(chalk3.bold("Summary:"));
3768
+ console.log(` Constraints Applied: ${chalk3.green(applied)}`);
3769
+ console.log(` Constraints Skipped: ${chalk3.dim(skipped)}`);
3611
3770
  }
3612
3771
  /**
3613
3772
  * Get all entries
@@ -3624,7 +3783,10 @@ var ExplainReporter = class {
3624
3783
  };
3625
3784
 
3626
3785
  // src/cli/commands/verify.ts
3627
- var verifyCommand = new Command3("verify").description("Verify code compliance against decisions").option("-l, --level <level>", "Verification level (commit, pr, full)", "full").option("-f, --files <patterns>", "Comma-separated file patterns to check").option("-d, --decisions <ids>", "Comma-separated decision IDs to check").option("-s, --severity <levels>", "Comma-separated severity levels (critical, high, medium, low)").option("--json", "Output as JSON").option("--incremental", "Only verify changed files (git diff --name-only --diff-filter=AM HEAD)").option("--explain", "Show detailed explanation of verification process").option("--fix", "Apply auto-fixes for supported violations").option("--dry-run", "Show what would be fixed without applying (requires --fix)").option("--interactive", "Confirm each fix interactively (requires --fix)").action(async (options) => {
3786
+ var verifyCommand = new Command3("verify").description("Verify code compliance against decisions").option("-l, --level <level>", "Verification level (commit, pr, full)", "full").option("-f, --files <patterns>", "Comma-separated file patterns to check").option("-d, --decisions <ids>", "Comma-separated decision IDs to check").option(
3787
+ "-s, --severity <levels>",
3788
+ "Comma-separated severity levels (critical, high, medium, low)"
3789
+ ).option("--json", "Output as JSON").option("--incremental", "Only verify changed files (git diff --name-only --diff-filter=AM HEAD)").option("--explain", "Show detailed explanation of verification process").option("--fix", "Apply auto-fixes for supported violations").option("--dry-run", "Show what would be fixed without applying (requires --fix)").option("--interactive", "Confirm each fix interactively (requires --fix)").action(async (options) => {
3628
3790
  const cwd = process.cwd();
3629
3791
  if (!await pathExists(getSpecBridgeDir(cwd))) {
3630
3792
  throw new NotInitializedError();
@@ -3657,7 +3819,7 @@ var verifyCommand = new Command3("verify").description("Verify code compliance a
3657
3819
  if (fixableCount === 0) {
3658
3820
  spinner.stop();
3659
3821
  if (!options.json) {
3660
- console.log(chalk5.yellow("No auto-fixable violations found"));
3822
+ console.log(chalk4.yellow("No auto-fixable violations found"));
3661
3823
  }
3662
3824
  } else {
3663
3825
  spinner.text = `Applying ${fixableCount} auto-fix(es)...`;
@@ -3682,34 +3844,34 @@ var verifyCommand = new Command3("verify").description("Verify code compliance a
3682
3844
  console.log(JSON.stringify({ ...result, autofix: fixResult }, null, 2));
3683
3845
  } else {
3684
3846
  if (result.warnings && result.warnings.length > 0) {
3685
- console.log(chalk5.yellow.bold("\nWarnings:"));
3847
+ console.log(chalk4.yellow.bold("\nWarnings:"));
3686
3848
  for (const warning of result.warnings) {
3687
- console.log(chalk5.yellow(` \u26A0 ${warning.message}`));
3688
- console.log(chalk5.dim(` ${warning.decisionId}/${warning.constraintId}`));
3849
+ console.log(chalk4.yellow(` \u26A0 ${warning.message}`));
3850
+ console.log(chalk4.dim(` ${warning.decisionId}/${warning.constraintId}`));
3689
3851
  if (warning.file) {
3690
- console.log(chalk5.dim(` File: ${warning.file}`));
3852
+ console.log(chalk4.dim(` File: ${warning.file}`));
3691
3853
  }
3692
3854
  }
3693
3855
  console.log("");
3694
3856
  }
3695
3857
  if (result.errors && result.errors.length > 0) {
3696
- console.log(chalk5.red.bold("\nErrors:"));
3858
+ console.log(chalk4.red.bold("\nErrors:"));
3697
3859
  for (const error of result.errors) {
3698
- console.log(chalk5.red(` \u2717 ${error.message}`));
3860
+ console.log(chalk4.red(` \u2717 ${error.message}`));
3699
3861
  if (error.decisionId && error.constraintId) {
3700
- console.log(chalk5.dim(` ${error.decisionId}/${error.constraintId}`));
3862
+ console.log(chalk4.dim(` ${error.decisionId}/${error.constraintId}`));
3701
3863
  }
3702
3864
  if (error.file) {
3703
- console.log(chalk5.dim(` File: ${error.file}`));
3865
+ console.log(chalk4.dim(` File: ${error.file}`));
3704
3866
  }
3705
3867
  }
3706
3868
  console.log("");
3707
3869
  }
3708
3870
  printResult(result, level);
3709
3871
  if (options.fix && fixResult) {
3710
- console.log(chalk5.green(`\u2713 Applied ${fixResult.applied.length} fix(es)`));
3872
+ console.log(chalk4.green(`\u2713 Applied ${fixResult.applied.length} fix(es)`));
3711
3873
  if (fixResult.skipped > 0) {
3712
- console.log(chalk5.yellow(`\u2298 Skipped ${fixResult.skipped} fix(es)`));
3874
+ console.log(chalk4.yellow(`\u2298 Skipped ${fixResult.skipped} fix(es)`));
3713
3875
  }
3714
3876
  console.log("");
3715
3877
  }
@@ -3728,8 +3890,8 @@ var verifyCommand = new Command3("verify").description("Verify code compliance a
3728
3890
  function printResult(result, level) {
3729
3891
  console.log("");
3730
3892
  if (result.violations.length === 0) {
3731
- console.log(chalk5.green("\u2713 All checks passed!"));
3732
- console.log(chalk5.dim(` ${result.checked} files checked in ${result.duration}ms`));
3893
+ console.log(chalk4.green("\u2713 All checks passed!"));
3894
+ console.log(chalk4.dim(` ${result.checked} files checked in ${result.duration}ms`));
3733
3895
  return;
3734
3896
  }
3735
3897
  const byFile = /* @__PURE__ */ new Map();
@@ -3739,17 +3901,15 @@ function printResult(result, level) {
3739
3901
  byFile.set(violation.file, existing);
3740
3902
  }
3741
3903
  for (const [file, violations] of byFile) {
3742
- console.log(chalk5.underline(file));
3904
+ console.log(chalk4.underline(file));
3743
3905
  for (const v of violations) {
3744
3906
  const typeIcon = getTypeIcon(v.type);
3745
3907
  const severityColor = getSeverityColor(v.severity);
3746
3908
  const location = v.line ? `:${v.line}${v.column ? `:${v.column}` : ""}` : "";
3747
- console.log(
3748
- ` ${typeIcon} ${severityColor(`[${v.severity}]`)} ${v.message}`
3749
- );
3750
- console.log(chalk5.dim(` ${v.decisionId}/${v.constraintId}${location}`));
3909
+ console.log(` ${typeIcon} ${severityColor(`[${v.severity}]`)} ${v.message}`);
3910
+ console.log(chalk4.dim(` ${v.decisionId}/${v.constraintId}${location}`));
3751
3911
  if (v.suggestion) {
3752
- console.log(chalk5.cyan(` Suggestion: ${v.suggestion}`));
3912
+ console.log(chalk4.cyan(` Suggestion: ${v.suggestion}`));
3753
3913
  }
3754
3914
  }
3755
3915
  console.log("");
@@ -3758,29 +3918,31 @@ function printResult(result, level) {
3758
3918
  const highCount = result.violations.filter((v) => v.severity === "high").length;
3759
3919
  const mediumCount = result.violations.filter((v) => v.severity === "medium").length;
3760
3920
  const lowCount = result.violations.filter((v) => v.severity === "low").length;
3761
- console.log(chalk5.bold("Summary:"));
3762
- console.log(` Files: ${result.checked} checked, ${result.passed} passed, ${result.failed} failed`);
3921
+ console.log(chalk4.bold("Summary:"));
3922
+ console.log(
3923
+ ` Files: ${result.checked} checked, ${result.passed} passed, ${result.failed} failed`
3924
+ );
3763
3925
  const violationParts = [];
3764
- if (criticalCount > 0) violationParts.push(chalk5.red(`${criticalCount} critical`));
3765
- if (highCount > 0) violationParts.push(chalk5.yellow(`${highCount} high`));
3766
- if (mediumCount > 0) violationParts.push(chalk5.cyan(`${mediumCount} medium`));
3767
- if (lowCount > 0) violationParts.push(chalk5.dim(`${lowCount} low`));
3926
+ if (criticalCount > 0) violationParts.push(chalk4.red(`${criticalCount} critical`));
3927
+ if (highCount > 0) violationParts.push(chalk4.yellow(`${highCount} high`));
3928
+ if (mediumCount > 0) violationParts.push(chalk4.cyan(`${mediumCount} medium`));
3929
+ if (lowCount > 0) violationParts.push(chalk4.dim(`${lowCount} low`));
3768
3930
  console.log(` Violations: ${violationParts.join(", ")}`);
3769
3931
  console.log(` Duration: ${result.duration}ms`);
3770
3932
  if (!result.success) {
3771
3933
  console.log("");
3772
3934
  const blockingTypes = level === "commit" ? "invariant or critical" : level === "pr" ? "invariant, critical, or high" : "invariant";
3773
- console.log(chalk5.red(`\u2717 Verification failed. ${blockingTypes} violations must be resolved.`));
3935
+ console.log(chalk4.red(`\u2717 Verification failed. ${blockingTypes} violations must be resolved.`));
3774
3936
  }
3775
3937
  }
3776
3938
  function getTypeIcon(type) {
3777
3939
  switch (type) {
3778
3940
  case "invariant":
3779
- return chalk5.red("\u25CF");
3941
+ return chalk4.red("\u25CF");
3780
3942
  case "convention":
3781
- return chalk5.yellow("\u25CF");
3943
+ return chalk4.yellow("\u25CF");
3782
3944
  case "guideline":
3783
- return chalk5.green("\u25CF");
3945
+ return chalk4.green("\u25CF");
3784
3946
  default:
3785
3947
  return "\u25CB";
3786
3948
  }
@@ -3788,15 +3950,15 @@ function getTypeIcon(type) {
3788
3950
  function getSeverityColor(severity) {
3789
3951
  switch (severity) {
3790
3952
  case "critical":
3791
- return chalk5.red;
3953
+ return chalk4.red;
3792
3954
  case "high":
3793
- return chalk5.yellow;
3955
+ return chalk4.yellow;
3794
3956
  case "medium":
3795
- return chalk5.cyan;
3957
+ return chalk4.cyan;
3796
3958
  case "low":
3797
- return chalk5.dim;
3959
+ return chalk4.dim;
3798
3960
  default:
3799
- return chalk5.white;
3961
+ return chalk4.white;
3800
3962
  }
3801
3963
  }
3802
3964
 
@@ -3805,15 +3967,15 @@ import { Command as Command8 } from "commander";
3805
3967
 
3806
3968
  // src/cli/commands/decision/list.ts
3807
3969
  import { Command as Command4 } from "commander";
3808
- import chalk6 from "chalk";
3970
+ import chalk5 from "chalk";
3809
3971
  import { table } from "table";
3810
3972
  var listDecisions = new Command4("list").description("List all architectural decisions").option("-s, --status <status>", "Filter by status (draft, active, deprecated, superseded)").option("-t, --tag <tag>", "Filter by tag").option("--json", "Output as JSON").action(async (options) => {
3811
3973
  const registry = createRegistry();
3812
3974
  const result = await registry.load();
3813
3975
  if (result.errors.length > 0) {
3814
- console.warn(chalk6.yellow("\nWarnings:"));
3976
+ console.warn(chalk5.yellow("\nWarnings:"));
3815
3977
  for (const err of result.errors) {
3816
- console.warn(chalk6.yellow(` - ${err.filePath}: ${err.error}`));
3978
+ console.warn(chalk5.yellow(` - ${err.filePath}: ${err.error}`));
3817
3979
  }
3818
3980
  console.log("");
3819
3981
  }
@@ -3826,7 +3988,7 @@ var listDecisions = new Command4("list").description("List all architectural dec
3826
3988
  }
3827
3989
  const decisions = registry.getAll(filter);
3828
3990
  if (decisions.length === 0) {
3829
- console.log(chalk6.yellow("No decisions found."));
3991
+ console.log(chalk5.yellow("No decisions found."));
3830
3992
  return;
3831
3993
  }
3832
3994
  if (options.json) {
@@ -3835,11 +3997,11 @@ var listDecisions = new Command4("list").description("List all architectural dec
3835
3997
  }
3836
3998
  const data = [
3837
3999
  [
3838
- chalk6.bold("ID"),
3839
- chalk6.bold("Title"),
3840
- chalk6.bold("Status"),
3841
- chalk6.bold("Constraints"),
3842
- chalk6.bold("Tags")
4000
+ chalk5.bold("ID"),
4001
+ chalk5.bold("Title"),
4002
+ chalk5.bold("Status"),
4003
+ chalk5.bold("Constraints"),
4004
+ chalk5.bold("Tags")
3843
4005
  ]
3844
4006
  ];
3845
4007
  for (const decision of decisions) {
@@ -3853,40 +4015,42 @@ var listDecisions = new Command4("list").description("List all architectural dec
3853
4015
  (decision.metadata.tags || []).join(", ") || "-"
3854
4016
  ]);
3855
4017
  }
3856
- console.log(table(data, {
3857
- border: {
3858
- topBody: "",
3859
- topJoin: "",
3860
- topLeft: "",
3861
- topRight: "",
3862
- bottomBody: "",
3863
- bottomJoin: "",
3864
- bottomLeft: "",
3865
- bottomRight: "",
3866
- bodyLeft: "",
3867
- bodyRight: "",
3868
- bodyJoin: " ",
3869
- joinBody: "",
3870
- joinLeft: "",
3871
- joinRight: "",
3872
- joinJoin: ""
3873
- },
3874
- drawHorizontalLine: (index) => index === 1
3875
- }));
3876
- console.log(chalk6.dim(`Total: ${decisions.length} decision(s)`));
4018
+ console.log(
4019
+ table(data, {
4020
+ border: {
4021
+ topBody: "",
4022
+ topJoin: "",
4023
+ topLeft: "",
4024
+ topRight: "",
4025
+ bottomBody: "",
4026
+ bottomJoin: "",
4027
+ bottomLeft: "",
4028
+ bottomRight: "",
4029
+ bodyLeft: "",
4030
+ bodyRight: "",
4031
+ bodyJoin: " ",
4032
+ joinBody: "",
4033
+ joinLeft: "",
4034
+ joinRight: "",
4035
+ joinJoin: ""
4036
+ },
4037
+ drawHorizontalLine: (index) => index === 1
4038
+ })
4039
+ );
4040
+ console.log(chalk5.dim(`Total: ${decisions.length} decision(s)`));
3877
4041
  });
3878
4042
  function getStatusColor(status) {
3879
4043
  switch (status) {
3880
4044
  case "active":
3881
- return chalk6.green;
4045
+ return chalk5.green;
3882
4046
  case "draft":
3883
- return chalk6.yellow;
4047
+ return chalk5.yellow;
3884
4048
  case "deprecated":
3885
- return chalk6.gray;
4049
+ return chalk5.gray;
3886
4050
  case "superseded":
3887
- return chalk6.blue;
4051
+ return chalk5.blue;
3888
4052
  default:
3889
- return chalk6.white;
4053
+ return chalk5.white;
3890
4054
  }
3891
4055
  }
3892
4056
  function getConstraintTypeSummary(types) {
@@ -3899,9 +4063,9 @@ function getConstraintTypeSummary(types) {
3899
4063
  counts[type]++;
3900
4064
  }
3901
4065
  const parts = [];
3902
- if (counts.invariant > 0) parts.push(chalk6.red(`${counts.invariant}I`));
3903
- if (counts.convention > 0) parts.push(chalk6.yellow(`${counts.convention}C`));
3904
- if (counts.guideline > 0) parts.push(chalk6.green(`${counts.guideline}G`));
4066
+ if (counts.invariant > 0) parts.push(chalk5.red(`${counts.invariant}I`));
4067
+ if (counts.convention > 0) parts.push(chalk5.yellow(`${counts.convention}C`));
4068
+ if (counts.guideline > 0) parts.push(chalk5.green(`${counts.guideline}G`));
3905
4069
  return parts.join(" ") || "-";
3906
4070
  }
3907
4071
  function truncate(str, length) {
@@ -3911,7 +4075,7 @@ function truncate(str, length) {
3911
4075
 
3912
4076
  // src/cli/commands/decision/show.ts
3913
4077
  import { Command as Command5 } from "commander";
3914
- import chalk7 from "chalk";
4078
+ import chalk6 from "chalk";
3915
4079
  var showDecision = new Command5("show").description("Show details of a specific decision").argument("<id>", "Decision ID").option("--json", "Output as JSON").action(async (id, options) => {
3916
4080
  const registry = createRegistry();
3917
4081
  await registry.load();
@@ -3924,74 +4088,74 @@ var showDecision = new Command5("show").description("Show details of a specific
3924
4088
  });
3925
4089
  function printDecision(decision) {
3926
4090
  const { metadata, decision: content, constraints } = decision;
3927
- console.log(chalk7.bold.blue(`
4091
+ console.log(chalk6.bold.blue(`
3928
4092
  ${metadata.title}`));
3929
- console.log(chalk7.dim(`ID: ${metadata.id}`));
4093
+ console.log(chalk6.dim(`ID: ${metadata.id}`));
3930
4094
  console.log("");
3931
- console.log(chalk7.bold("Status:"), getStatusBadge(metadata.status));
3932
- console.log(chalk7.bold("Owners:"), metadata.owners.join(", "));
4095
+ console.log(chalk6.bold("Status:"), getStatusBadge(metadata.status));
4096
+ console.log(chalk6.bold("Owners:"), metadata.owners.join(", "));
3933
4097
  if (metadata.tags && metadata.tags.length > 0) {
3934
- console.log(chalk7.bold("Tags:"), metadata.tags.map((t) => chalk7.cyan(t)).join(", "));
4098
+ console.log(chalk6.bold("Tags:"), metadata.tags.map((t) => chalk6.cyan(t)).join(", "));
3935
4099
  }
3936
4100
  if (metadata.createdAt) {
3937
- console.log(chalk7.bold("Created:"), metadata.createdAt);
4101
+ console.log(chalk6.bold("Created:"), metadata.createdAt);
3938
4102
  }
3939
4103
  if (metadata.supersededBy) {
3940
- console.log(chalk7.bold("Superseded by:"), chalk7.yellow(metadata.supersededBy));
4104
+ console.log(chalk6.bold("Superseded by:"), chalk6.yellow(metadata.supersededBy));
3941
4105
  }
3942
4106
  console.log("");
3943
- console.log(chalk7.bold.underline("Summary"));
4107
+ console.log(chalk6.bold.underline("Summary"));
3944
4108
  console.log(content.summary);
3945
4109
  console.log("");
3946
- console.log(chalk7.bold.underline("Rationale"));
4110
+ console.log(chalk6.bold.underline("Rationale"));
3947
4111
  console.log(content.rationale);
3948
4112
  console.log("");
3949
4113
  if (content.context) {
3950
- console.log(chalk7.bold.underline("Context"));
4114
+ console.log(chalk6.bold.underline("Context"));
3951
4115
  console.log(content.context);
3952
4116
  console.log("");
3953
4117
  }
3954
4118
  if (content.consequences && content.consequences.length > 0) {
3955
- console.log(chalk7.bold.underline("Consequences"));
4119
+ console.log(chalk6.bold.underline("Consequences"));
3956
4120
  for (const consequence of content.consequences) {
3957
4121
  console.log(` \u2022 ${consequence}`);
3958
4122
  }
3959
4123
  console.log("");
3960
4124
  }
3961
- console.log(chalk7.bold.underline(`Constraints (${constraints.length})`));
4125
+ console.log(chalk6.bold.underline(`Constraints (${constraints.length})`));
3962
4126
  for (const constraint of constraints) {
3963
4127
  const typeIcon = getTypeIcon2(constraint.type);
3964
4128
  const severityBadge = getSeverityBadge(constraint.severity);
3965
4129
  console.log(`
3966
- ${typeIcon} ${chalk7.bold(constraint.id)} ${severityBadge}`);
4130
+ ${typeIcon} ${chalk6.bold(constraint.id)} ${severityBadge}`);
3967
4131
  console.log(` ${constraint.rule}`);
3968
- console.log(chalk7.dim(` Scope: ${constraint.scope}`));
4132
+ console.log(chalk6.dim(` Scope: ${constraint.scope}`));
3969
4133
  if (constraint.verifier) {
3970
- console.log(chalk7.dim(` Verifier: ${constraint.verifier}`));
4134
+ console.log(chalk6.dim(` Verifier: ${constraint.verifier}`));
3971
4135
  }
3972
4136
  if (constraint.exceptions && constraint.exceptions.length > 0) {
3973
- console.log(chalk7.dim(` Exceptions: ${constraint.exceptions.length}`));
4137
+ console.log(chalk6.dim(` Exceptions: ${constraint.exceptions.length}`));
3974
4138
  }
3975
4139
  }
3976
4140
  console.log("");
3977
4141
  if (decision.verification?.automated && decision.verification.automated.length > 0) {
3978
- console.log(chalk7.bold.underline("Automated Verification"));
4142
+ console.log(chalk6.bold.underline("Automated Verification"));
3979
4143
  for (const check of decision.verification.automated) {
3980
4144
  console.log(` \u2022 ${check.check} (${check.frequency})`);
3981
- console.log(chalk7.dim(` Target: ${check.target}`));
4145
+ console.log(chalk6.dim(` Target: ${check.target}`));
3982
4146
  }
3983
4147
  console.log("");
3984
4148
  }
3985
4149
  if (decision.links) {
3986
4150
  const { related, supersedes, references } = decision.links;
3987
4151
  if (related && related.length > 0) {
3988
- console.log(chalk7.bold("Related:"), related.join(", "));
4152
+ console.log(chalk6.bold("Related:"), related.join(", "));
3989
4153
  }
3990
4154
  if (supersedes && supersedes.length > 0) {
3991
- console.log(chalk7.bold("Supersedes:"), supersedes.join(", "));
4155
+ console.log(chalk6.bold("Supersedes:"), supersedes.join(", "));
3992
4156
  }
3993
4157
  if (references && references.length > 0) {
3994
- console.log(chalk7.bold("References:"));
4158
+ console.log(chalk6.bold("References:"));
3995
4159
  for (const ref of references) {
3996
4160
  console.log(` \u2022 ${ref}`);
3997
4161
  }
@@ -4001,13 +4165,13 @@ ${metadata.title}`));
4001
4165
  function getStatusBadge(status) {
4002
4166
  switch (status) {
4003
4167
  case "active":
4004
- return chalk7.bgGreen.black(" ACTIVE ");
4168
+ return chalk6.bgGreen.black(" ACTIVE ");
4005
4169
  case "draft":
4006
- return chalk7.bgYellow.black(" DRAFT ");
4170
+ return chalk6.bgYellow.black(" DRAFT ");
4007
4171
  case "deprecated":
4008
- return chalk7.bgGray.white(" DEPRECATED ");
4172
+ return chalk6.bgGray.white(" DEPRECATED ");
4009
4173
  case "superseded":
4010
- return chalk7.bgBlue.white(" SUPERSEDED ");
4174
+ return chalk6.bgBlue.white(" SUPERSEDED ");
4011
4175
  default:
4012
4176
  return status;
4013
4177
  }
@@ -4015,11 +4179,11 @@ function getStatusBadge(status) {
4015
4179
  function getTypeIcon2(type) {
4016
4180
  switch (type) {
4017
4181
  case "invariant":
4018
- return chalk7.red("\u25CF");
4182
+ return chalk6.red("\u25CF");
4019
4183
  case "convention":
4020
- return chalk7.yellow("\u25CF");
4184
+ return chalk6.yellow("\u25CF");
4021
4185
  case "guideline":
4022
- return chalk7.green("\u25CF");
4186
+ return chalk6.green("\u25CF");
4023
4187
  default:
4024
4188
  return "\u25CB";
4025
4189
  }
@@ -4027,13 +4191,13 @@ function getTypeIcon2(type) {
4027
4191
  function getSeverityBadge(severity) {
4028
4192
  switch (severity) {
4029
4193
  case "critical":
4030
- return chalk7.bgRed.white(" CRITICAL ");
4194
+ return chalk6.bgRed.white(" CRITICAL ");
4031
4195
  case "high":
4032
- return chalk7.bgYellow.black(" HIGH ");
4196
+ return chalk6.bgYellow.black(" HIGH ");
4033
4197
  case "medium":
4034
- return chalk7.bgCyan.black(" MEDIUM ");
4198
+ return chalk6.bgCyan.black(" MEDIUM ");
4035
4199
  case "low":
4036
- return chalk7.bgGray.white(" LOW ");
4200
+ return chalk6.bgGray.white(" LOW ");
4037
4201
  default:
4038
4202
  return severity;
4039
4203
  }
@@ -4041,7 +4205,7 @@ function getSeverityBadge(severity) {
4041
4205
 
4042
4206
  // src/cli/commands/decision/validate.ts
4043
4207
  import { Command as Command6 } from "commander";
4044
- import chalk8 from "chalk";
4208
+ import chalk7 from "chalk";
4045
4209
  import ora4 from "ora";
4046
4210
  import { join as join6 } from "path";
4047
4211
  var validateDecisions = new Command6("validate").description("Validate decision files").option("-f, --file <path>", "Validate a specific file").action(async (options) => {
@@ -4080,14 +4244,14 @@ var validateDecisions = new Command6("validate").description("Validate decision
4080
4244
  }
4081
4245
  spinner.stop();
4082
4246
  if (invalid === 0) {
4083
- console.log(chalk8.green(`\u2713 All ${valid} decision file(s) are valid.`));
4247
+ console.log(chalk7.green(`\u2713 All ${valid} decision file(s) are valid.`));
4084
4248
  } else {
4085
- console.log(chalk8.red(`\u2717 ${invalid} of ${files.length} decision file(s) have errors.
4249
+ console.log(chalk7.red(`\u2717 ${invalid} of ${files.length} decision file(s) have errors.
4086
4250
  `));
4087
4251
  for (const { file, errors: fileErrors } of errors) {
4088
- console.log(chalk8.red(`File: ${file}`));
4252
+ console.log(chalk7.red(`File: ${file}`));
4089
4253
  for (const err of fileErrors) {
4090
- console.log(chalk8.dim(` - ${err}`));
4254
+ console.log(chalk7.dim(` - ${err}`));
4091
4255
  }
4092
4256
  console.log("");
4093
4257
  }
@@ -4101,21 +4265,31 @@ var validateDecisions = new Command6("validate").description("Validate decision
4101
4265
 
4102
4266
  // src/cli/commands/decision/create.ts
4103
4267
  import { Command as Command7 } from "commander";
4104
- import chalk9 from "chalk";
4268
+ import chalk8 from "chalk";
4105
4269
  import { join as join7 } from "path";
4106
- var createDecision = new Command7("create").description("Create a new decision file").argument("<id>", "Decision ID (e.g., auth-001)").requiredOption("-t, --title <title>", "Decision title").requiredOption("-s, --summary <summary>", "One-sentence summary").option("--type <type>", "Default constraint type (invariant, convention, guideline)", "convention").option("--severity <severity>", "Default constraint severity (critical, high, medium, low)", "medium").option("--scope <scope>", "Default constraint scope (glob pattern)", "src/**/*.ts").option("-o, --owner <owner>", "Owner name", "team").action(async (id, options) => {
4270
+ var createDecision = new Command7("create").description("Create a new decision file").argument("<id>", "Decision ID (e.g., auth-001)").requiredOption("-t, --title <title>", "Decision title").requiredOption("-s, --summary <summary>", "One-sentence summary").option(
4271
+ "--type <type>",
4272
+ "Default constraint type (invariant, convention, guideline)",
4273
+ "convention"
4274
+ ).option(
4275
+ "--severity <severity>",
4276
+ "Default constraint severity (critical, high, medium, low)",
4277
+ "medium"
4278
+ ).option("--scope <scope>", "Default constraint scope (glob pattern)", "src/**/*.ts").option("-o, --owner <owner>", "Owner name", "team").action(async (id, options) => {
4107
4279
  const cwd = process.cwd();
4108
4280
  if (!await pathExists(getSpecBridgeDir(cwd))) {
4109
4281
  throw new NotInitializedError();
4110
4282
  }
4111
4283
  if (!/^[a-z0-9-]+$/.test(id)) {
4112
- console.error(chalk9.red("Error: Decision ID must be lowercase alphanumeric with hyphens only."));
4284
+ console.error(
4285
+ chalk8.red("Error: Decision ID must be lowercase alphanumeric with hyphens only.")
4286
+ );
4113
4287
  process.exit(1);
4114
4288
  }
4115
4289
  const decisionsDir = getDecisionsDir(cwd);
4116
4290
  const filePath = join7(decisionsDir, `${id}.decision.yaml`);
4117
4291
  if (await pathExists(filePath)) {
4118
- console.error(chalk9.red(`Error: Decision file already exists: ${filePath}`));
4292
+ console.error(chalk8.red(`Error: Decision file already exists: ${filePath}`));
4119
4293
  process.exit(1);
4120
4294
  }
4121
4295
  const decision = {
@@ -4151,13 +4325,15 @@ var createDecision = new Command7("create").description("Create a new decision f
4151
4325
  }
4152
4326
  };
4153
4327
  await writeTextFile(filePath, stringifyYaml(decision));
4154
- console.log(chalk9.green(`\u2713 Created decision: ${filePath}`));
4328
+ console.log(chalk8.green(`\u2713 Created decision: ${filePath}`));
4155
4329
  console.log("");
4156
- console.log(chalk9.cyan("Next steps:"));
4330
+ console.log(chalk8.cyan("Next steps:"));
4157
4331
  console.log(` 1. Edit the file to add rationale, context, and consequences`);
4158
4332
  console.log(` 2. Define constraints with appropriate scopes`);
4159
- console.log(` 3. Run ${chalk9.bold("specbridge decision validate")} to check syntax`);
4160
- console.log(` 4. Change status from ${chalk9.yellow("draft")} to ${chalk9.green("active")} when ready`);
4333
+ console.log(` 3. Run ${chalk8.bold("specbridge decision validate")} to check syntax`);
4334
+ console.log(
4335
+ ` 4. Change status from ${chalk8.yellow("draft")} to ${chalk8.green("active")} when ready`
4336
+ );
4161
4337
  });
4162
4338
 
4163
4339
  // src/cli/commands/decision/index.ts
@@ -4165,7 +4341,7 @@ var decisionCommand = new Command8("decision").description("Manage architectural
4165
4341
 
4166
4342
  // src/cli/commands/hook.ts
4167
4343
  import { Command as Command9 } from "commander";
4168
- import chalk10 from "chalk";
4344
+ import chalk9 from "chalk";
4169
4345
  import ora5 from "ora";
4170
4346
  import { join as join8 } from "path";
4171
4347
  var HOOK_SCRIPT = `#!/bin/sh
@@ -4197,14 +4373,16 @@ function createHookCommand() {
4197
4373
  } else if (options.lefthook) {
4198
4374
  spinner.succeed("Lefthook detected");
4199
4375
  console.log("");
4200
- console.log(chalk10.cyan("Add this to your lefthook.yml:"));
4376
+ console.log(chalk9.cyan("Add this to your lefthook.yml:"));
4201
4377
  console.log("");
4202
- console.log(chalk10.dim(`pre-commit:
4378
+ console.log(
4379
+ chalk9.dim(`pre-commit:
4203
4380
  commands:
4204
4381
  specbridge:
4205
4382
  glob: "*.{ts,tsx}"
4206
4383
  run: npx specbridge hook run --level commit --files {staged_files}
4207
- `));
4384
+ `)
4385
+ );
4208
4386
  return;
4209
4387
  } else {
4210
4388
  if (await pathExists(join8(cwd, ".husky"))) {
@@ -4214,14 +4392,16 @@ function createHookCommand() {
4214
4392
  } else if (await pathExists(join8(cwd, "lefthook.yml"))) {
4215
4393
  spinner.succeed("Lefthook detected");
4216
4394
  console.log("");
4217
- console.log(chalk10.cyan("Add this to your lefthook.yml:"));
4395
+ console.log(chalk9.cyan("Add this to your lefthook.yml:"));
4218
4396
  console.log("");
4219
- console.log(chalk10.dim(`pre-commit:
4397
+ console.log(
4398
+ chalk9.dim(`pre-commit:
4220
4399
  commands:
4221
4400
  specbridge:
4222
4401
  glob: "*.{ts,tsx}"
4223
4402
  run: npx specbridge hook run --level commit --files {staged_files}
4224
- `));
4403
+ `)
4404
+ );
4225
4405
  return;
4226
4406
  } else {
4227
4407
  hookPath = join8(cwd, ".git", "hooks", "pre-commit");
@@ -4231,7 +4411,7 @@ function createHookCommand() {
4231
4411
  }
4232
4412
  if (await pathExists(hookPath) && !options.force) {
4233
4413
  spinner.fail("Hook already exists");
4234
- console.log(chalk10.yellow(`Use --force to overwrite: ${hookPath}`));
4414
+ console.log(chalk9.yellow(`Use --force to overwrite: ${hookPath}`));
4235
4415
  return;
4236
4416
  }
4237
4417
  await writeTextFile(hookPath, hookContent);
@@ -4241,9 +4421,9 @@ function createHookCommand() {
4241
4421
  } catch {
4242
4422
  }
4243
4423
  spinner.succeed("Pre-commit hook installed");
4244
- console.log(chalk10.dim(` Path: ${hookPath}`));
4424
+ console.log(chalk9.dim(` Path: ${hookPath}`));
4245
4425
  console.log("");
4246
- console.log(chalk10.cyan("The hook will run on each commit and verify staged files."));
4426
+ console.log(chalk9.cyan("The hook will run on each commit and verify staged files."));
4247
4427
  } catch (error) {
4248
4428
  spinner.fail("Failed to install hook");
4249
4429
  throw error;
@@ -4263,7 +4443,11 @@ function createHookCommand() {
4263
4443
  const { promisify: promisify2 } = await import("util");
4264
4444
  const execFileAsync2 = promisify2(execFile2);
4265
4445
  try {
4266
- const { stdout: stdout2 } = await execFileAsync2("git", ["diff", "--cached", "--name-only", "--diff-filter=AM"], { cwd });
4446
+ const { stdout: stdout2 } = await execFileAsync2(
4447
+ "git",
4448
+ ["diff", "--cached", "--name-only", "--diff-filter=AM"],
4449
+ { cwd }
4450
+ );
4267
4451
  files = stdout2.trim().split("\n").map((s) => s.trim()).filter(Boolean).filter((f) => /\.(ts|tsx|js|jsx)$/.test(f));
4268
4452
  } catch {
4269
4453
  files = [];
@@ -4279,21 +4463,21 @@ function createHookCommand() {
4279
4463
  cwd
4280
4464
  });
4281
4465
  if (result.violations.length === 0) {
4282
- console.log(chalk10.green("\u2713 SpecBridge: All checks passed"));
4466
+ console.log(chalk9.green("\u2713 SpecBridge: All checks passed"));
4283
4467
  process.exit(0);
4284
4468
  }
4285
- console.log(chalk10.red(`\u2717 SpecBridge: ${result.violations.length} violation(s) found`));
4469
+ console.log(chalk9.red(`\u2717 SpecBridge: ${result.violations.length} violation(s) found`));
4286
4470
  console.log("");
4287
4471
  for (const v of result.violations) {
4288
4472
  const location = v.line ? `:${v.line}` : "";
4289
4473
  console.log(` ${v.file}${location}: ${v.message}`);
4290
- console.log(chalk10.dim(` [${v.severity}] ${v.decisionId}/${v.constraintId}`));
4474
+ console.log(chalk9.dim(` [${v.severity}] ${v.decisionId}/${v.constraintId}`));
4291
4475
  }
4292
4476
  console.log("");
4293
- console.log(chalk10.yellow("Run `specbridge verify` for full details."));
4477
+ console.log(chalk9.yellow("Run `specbridge verify` for full details."));
4294
4478
  process.exit(result.success ? 0 : 1);
4295
4479
  } catch (error) {
4296
- console.error(chalk10.red("SpecBridge verification failed"));
4480
+ console.error(chalk9.red("SpecBridge verification failed"));
4297
4481
  console.error(error instanceof Error ? error.message : String(error));
4298
4482
  process.exit(1);
4299
4483
  }
@@ -4332,7 +4516,7 @@ var hookCommand = createHookCommand();
4332
4516
 
4333
4517
  // src/cli/commands/report.ts
4334
4518
  import { Command as Command10 } from "commander";
4335
- import chalk12 from "chalk";
4519
+ import chalk11 from "chalk";
4336
4520
  import ora6 from "ora";
4337
4521
  import { join as join10 } from "path";
4338
4522
 
@@ -4365,10 +4549,7 @@ async function generateReport(config, options = {}) {
4365
4549
  medium: decisionViolations.filter((v) => v.severity === "medium").length,
4366
4550
  low: decisionViolations.filter((v) => v.severity === "low").length
4367
4551
  };
4368
- weightedScore = decisionViolations.reduce(
4369
- (score, v) => score + weights[v.severity],
4370
- 0
4371
- );
4552
+ weightedScore = decisionViolations.reduce((score, v) => score + weights[v.severity], 0);
4372
4553
  compliance = Math.max(0, 100 - weightedScore);
4373
4554
  if (decisionViolations.length > 0 && constraintCount > 0) {
4374
4555
  const violationRate = decisionViolations.length / constraintCount;
@@ -4416,54 +4597,58 @@ async function generateReport(config, options = {}) {
4416
4597
  }
4417
4598
 
4418
4599
  // src/reporting/formats/console.ts
4419
- import chalk11 from "chalk";
4600
+ import chalk10 from "chalk";
4420
4601
  import { table as table2 } from "table";
4421
4602
  function formatConsoleReport(report) {
4422
4603
  const lines = [];
4423
4604
  lines.push("");
4424
- lines.push(chalk11.bold.blue("SpecBridge Compliance Report"));
4425
- lines.push(chalk11.dim(`Generated: ${new Date(report.timestamp).toLocaleString()}`));
4426
- lines.push(chalk11.dim(`Project: ${report.project}`));
4605
+ lines.push(chalk10.bold.blue("SpecBridge Compliance Report"));
4606
+ lines.push(chalk10.dim(`Generated: ${new Date(report.timestamp).toLocaleString()}`));
4607
+ lines.push(chalk10.dim(`Project: ${report.project}`));
4427
4608
  lines.push("");
4428
4609
  const complianceColor = getComplianceColor(report.summary.compliance);
4429
- lines.push(chalk11.bold("Overall Compliance"));
4430
- lines.push(` ${complianceColor(formatComplianceBar(report.summary.compliance))} ${complianceColor(`${report.summary.compliance}%`)}`);
4610
+ lines.push(chalk10.bold("Overall Compliance"));
4611
+ lines.push(
4612
+ ` ${complianceColor(formatComplianceBar(report.summary.compliance))} ${complianceColor(`${report.summary.compliance}%`)}`
4613
+ );
4431
4614
  lines.push("");
4432
- lines.push(chalk11.bold("Summary"));
4433
- lines.push(` Decisions: ${report.summary.activeDecisions} active / ${report.summary.totalDecisions} total`);
4615
+ lines.push(chalk10.bold("Summary"));
4616
+ lines.push(
4617
+ ` Decisions: ${report.summary.activeDecisions} active / ${report.summary.totalDecisions} total`
4618
+ );
4434
4619
  lines.push(` Constraints: ${report.summary.totalConstraints}`);
4435
4620
  lines.push("");
4436
- lines.push(chalk11.bold("Violations"));
4621
+ lines.push(chalk10.bold("Violations"));
4437
4622
  const { violations } = report.summary;
4438
4623
  const violationParts = [];
4439
4624
  if (violations.critical > 0) {
4440
- violationParts.push(chalk11.red(`${violations.critical} critical`));
4625
+ violationParts.push(chalk10.red(`${violations.critical} critical`));
4441
4626
  }
4442
4627
  if (violations.high > 0) {
4443
- violationParts.push(chalk11.yellow(`${violations.high} high`));
4628
+ violationParts.push(chalk10.yellow(`${violations.high} high`));
4444
4629
  }
4445
4630
  if (violations.medium > 0) {
4446
- violationParts.push(chalk11.cyan(`${violations.medium} medium`));
4631
+ violationParts.push(chalk10.cyan(`${violations.medium} medium`));
4447
4632
  }
4448
4633
  if (violations.low > 0) {
4449
- violationParts.push(chalk11.dim(`${violations.low} low`));
4634
+ violationParts.push(chalk10.dim(`${violations.low} low`));
4450
4635
  }
4451
4636
  if (violationParts.length > 0) {
4452
4637
  lines.push(` ${violationParts.join(" | ")}`);
4453
4638
  } else {
4454
- lines.push(chalk11.green(" No violations"));
4639
+ lines.push(chalk10.green(" No violations"));
4455
4640
  }
4456
4641
  lines.push("");
4457
4642
  if (report.byDecision.length > 0) {
4458
- lines.push(chalk11.bold("By Decision"));
4643
+ lines.push(chalk10.bold("By Decision"));
4459
4644
  lines.push("");
4460
4645
  const tableData = [
4461
4646
  [
4462
- chalk11.bold("Decision"),
4463
- chalk11.bold("Status"),
4464
- chalk11.bold("Constraints"),
4465
- chalk11.bold("Violations"),
4466
- chalk11.bold("Compliance")
4647
+ chalk10.bold("Decision"),
4648
+ chalk10.bold("Status"),
4649
+ chalk10.bold("Constraints"),
4650
+ chalk10.bold("Violations"),
4651
+ chalk10.bold("Compliance")
4467
4652
  ]
4468
4653
  ];
4469
4654
  for (const dec of report.byDecision) {
@@ -4473,7 +4658,7 @@ function formatConsoleReport(report) {
4473
4658
  truncate2(dec.title, 40),
4474
4659
  statusColor(dec.status),
4475
4660
  String(dec.constraints),
4476
- dec.violations > 0 ? chalk11.red(String(dec.violations)) : chalk11.green("0"),
4661
+ dec.violations > 0 ? chalk10.red(String(dec.violations)) : chalk10.green("0"),
4477
4662
  compColor(`${dec.compliance}%`)
4478
4663
  ]);
4479
4664
  }
@@ -4507,23 +4692,23 @@ function formatComplianceBar(compliance) {
4507
4692
  return "\u2588".repeat(filled) + "\u2591".repeat(empty);
4508
4693
  }
4509
4694
  function getComplianceColor(compliance) {
4510
- if (compliance >= 90) return chalk11.green;
4511
- if (compliance >= 70) return chalk11.yellow;
4512
- if (compliance >= 50) return chalk11.hex("#FFA500");
4513
- return chalk11.red;
4695
+ if (compliance >= 90) return chalk10.green;
4696
+ if (compliance >= 70) return chalk10.yellow;
4697
+ if (compliance >= 50) return chalk10.hex("#FFA500");
4698
+ return chalk10.red;
4514
4699
  }
4515
4700
  function getStatusColor2(status) {
4516
4701
  switch (status) {
4517
4702
  case "active":
4518
- return chalk11.green;
4703
+ return chalk10.green;
4519
4704
  case "draft":
4520
- return chalk11.yellow;
4705
+ return chalk10.yellow;
4521
4706
  case "deprecated":
4522
- return chalk11.gray;
4707
+ return chalk10.gray;
4523
4708
  case "superseded":
4524
- return chalk11.blue;
4709
+ return chalk10.blue;
4525
4710
  default:
4526
- return chalk11.white;
4711
+ return chalk10.white;
4527
4712
  }
4528
4713
  }
4529
4714
  function truncate2(str, length) {
@@ -4547,7 +4732,9 @@ function formatMarkdownReport(report) {
4547
4732
  lines.push("");
4548
4733
  lines.push("## Summary");
4549
4734
  lines.push("");
4550
- lines.push(`- **Active Decisions:** ${report.summary.activeDecisions} / ${report.summary.totalDecisions}`);
4735
+ lines.push(
4736
+ `- **Active Decisions:** ${report.summary.activeDecisions} / ${report.summary.totalDecisions}`
4737
+ );
4551
4738
  lines.push(`- **Total Constraints:** ${report.summary.totalConstraints}`);
4552
4739
  lines.push("");
4553
4740
  lines.push("### Violations");
@@ -4597,6 +4784,7 @@ function formatProgressBar(percentage) {
4597
4784
  import { join as join9 } from "path";
4598
4785
  var ReportStorage = class {
4599
4786
  storageDir;
4787
+ logger = getLogger({ module: "reporting.storage" });
4600
4788
  constructor(basePath) {
4601
4789
  this.storageDir = join9(getSpecBridgeDir(basePath), "reports", "history");
4602
4790
  }
@@ -4655,7 +4843,7 @@ var ReportStorage = class {
4655
4843
  const timestamp = file.replace("report-", "").replace(".json", "");
4656
4844
  return { timestamp, report };
4657
4845
  } catch (error) {
4658
- console.warn(`Warning: Failed to load report ${file}:`, error);
4846
+ this.logger.warn({ file, error }, "Failed to load report file");
4659
4847
  return null;
4660
4848
  }
4661
4849
  });
@@ -4699,7 +4887,7 @@ var ReportStorage = class {
4699
4887
  const fs = await import("fs/promises");
4700
4888
  await fs.unlink(filepath);
4701
4889
  } catch (error) {
4702
- console.warn(`Warning: Failed to delete old report ${file}:`, error);
4890
+ this.logger.warn({ file, error }, "Failed to delete old report file");
4703
4891
  }
4704
4892
  }
4705
4893
  return filesToDelete.length;
@@ -4710,9 +4898,7 @@ var ReportStorage = class {
4710
4898
  async function detectDrift(current, previous) {
4711
4899
  const byDecision = [];
4712
4900
  for (const currDecision of current.byDecision) {
4713
- const prevDecision = previous.byDecision.find(
4714
- (d) => d.decisionId === currDecision.decisionId
4715
- );
4901
+ const prevDecision = previous.byDecision.find((d) => d.decisionId === currDecision.decisionId);
4716
4902
  if (!prevDecision) {
4717
4903
  byDecision.push({
4718
4904
  decisionId: currDecision.decisionId,
@@ -4901,57 +5087,75 @@ var reportCommand = new Command10("report").description("Generate compliance rep
4901
5087
  const storage = new ReportStorage(cwd);
4902
5088
  await storage.save(report);
4903
5089
  if (options.trend) {
4904
- console.log("\n" + chalk12.blue.bold("=== Compliance Trend Analysis ===\n"));
5090
+ console.log("\n" + chalk11.blue.bold("=== Compliance Trend Analysis ===\n"));
4905
5091
  const days = parseInt(options.days || "30", 10);
4906
5092
  const history = await storage.loadHistory(days);
4907
5093
  if (history.length < 2) {
4908
- console.log(chalk12.yellow(`Not enough data for trend analysis. Found ${history.length} report(s), need at least 2.`));
5094
+ console.log(
5095
+ chalk11.yellow(
5096
+ `Not enough data for trend analysis. Found ${history.length} report(s), need at least 2.`
5097
+ )
5098
+ );
4909
5099
  } else {
4910
5100
  const trend = await analyzeTrend(history);
4911
- console.log(chalk12.bold(`Period: ${trend.period.start} to ${trend.period.end} (${trend.period.days} days)`));
4912
- console.log(`
4913
- Overall Compliance: ${trend.overall.startCompliance}% \u2192 ${trend.overall.endCompliance}% (${trend.overall.change > 0 ? "+" : ""}${trend.overall.change.toFixed(1)}%)`);
5101
+ console.log(
5102
+ chalk11.bold(
5103
+ `Period: ${trend.period.start} to ${trend.period.end} (${trend.period.days} days)`
5104
+ )
5105
+ );
5106
+ console.log(
5107
+ `
5108
+ Overall Compliance: ${trend.overall.startCompliance}% \u2192 ${trend.overall.endCompliance}% (${trend.overall.change > 0 ? "+" : ""}${trend.overall.change.toFixed(1)}%)`
5109
+ );
4914
5110
  const trendEmoji = trend.overall.trend === "improving" ? "\u{1F4C8}" : trend.overall.trend === "degrading" ? "\u{1F4C9}" : "\u27A1\uFE0F";
4915
- const trendColor = trend.overall.trend === "improving" ? chalk12.green : trend.overall.trend === "degrading" ? chalk12.red : chalk12.yellow;
5111
+ const trendColor = trend.overall.trend === "improving" ? chalk11.green : trend.overall.trend === "degrading" ? chalk11.red : chalk11.yellow;
4916
5112
  console.log(trendColor(`${trendEmoji} Trend: ${trend.overall.trend.toUpperCase()}`));
4917
5113
  const degrading = trend.decisions.filter((d) => d.trend === "degrading").slice(0, 3);
4918
5114
  if (degrading.length > 0) {
4919
- console.log(chalk12.red("\n\u26A0\uFE0F Most Degraded Decisions:"));
5115
+ console.log(chalk11.red("\n\u26A0\uFE0F Most Degraded Decisions:"));
4920
5116
  degrading.forEach((d) => {
4921
- console.log(` \u2022 ${d.title}: ${d.startCompliance}% \u2192 ${d.endCompliance}% (${d.change.toFixed(1)}%)`);
5117
+ console.log(
5118
+ ` \u2022 ${d.title}: ${d.startCompliance}% \u2192 ${d.endCompliance}% (${d.change.toFixed(1)}%)`
5119
+ );
4922
5120
  });
4923
5121
  }
4924
5122
  const improving = trend.decisions.filter((d) => d.trend === "improving").slice(0, 3);
4925
5123
  if (improving.length > 0) {
4926
- console.log(chalk12.green("\n\u2705 Most Improved Decisions:"));
5124
+ console.log(chalk11.green("\n\u2705 Most Improved Decisions:"));
4927
5125
  improving.forEach((d) => {
4928
- console.log(` \u2022 ${d.title}: ${d.startCompliance}% \u2192 ${d.endCompliance}% (+${d.change.toFixed(1)}%)`);
5126
+ console.log(
5127
+ ` \u2022 ${d.title}: ${d.startCompliance}% \u2192 ${d.endCompliance}% (+${d.change.toFixed(1)}%)`
5128
+ );
4929
5129
  });
4930
5130
  }
4931
5131
  }
4932
5132
  console.log("");
4933
5133
  }
4934
5134
  if (options.drift) {
4935
- console.log("\n" + chalk12.blue.bold("=== Drift Analysis ===\n"));
5135
+ console.log("\n" + chalk11.blue.bold("=== Drift Analysis ===\n"));
4936
5136
  const history = await storage.loadHistory(2);
4937
5137
  if (history.length < 2) {
4938
- console.log(chalk12.yellow("Not enough data for drift analysis. Need at least 2 reports."));
5138
+ console.log(chalk11.yellow("Not enough data for drift analysis. Need at least 2 reports."));
4939
5139
  } else {
4940
5140
  const currentEntry = history[0];
4941
5141
  const previousEntry = history[1];
4942
5142
  if (!currentEntry || !previousEntry) {
4943
- console.log(chalk12.yellow("Invalid history data."));
5143
+ console.log(chalk11.yellow("Invalid history data."));
4944
5144
  return;
4945
5145
  }
4946
5146
  const drift = await detectDrift(currentEntry.report, previousEntry.report);
4947
- console.log(chalk12.bold(`Comparing: ${previousEntry.timestamp} vs ${currentEntry.timestamp}`));
4948
- console.log(`
4949
- Compliance Change: ${drift.complianceChange > 0 ? "+" : ""}${drift.complianceChange.toFixed(1)}%`);
5147
+ console.log(
5148
+ chalk11.bold(`Comparing: ${previousEntry.timestamp} vs ${currentEntry.timestamp}`)
5149
+ );
5150
+ console.log(
5151
+ `
5152
+ Compliance Change: ${drift.complianceChange > 0 ? "+" : ""}${drift.complianceChange.toFixed(1)}%`
5153
+ );
4950
5154
  const driftEmoji = drift.trend === "improving" ? "\u{1F4C8}" : drift.trend === "degrading" ? "\u{1F4C9}" : "\u27A1\uFE0F";
4951
- const driftColor = drift.trend === "improving" ? chalk12.green : drift.trend === "degrading" ? chalk12.red : chalk12.yellow;
5155
+ const driftColor = drift.trend === "improving" ? chalk11.green : drift.trend === "degrading" ? chalk11.red : chalk11.yellow;
4952
5156
  console.log(driftColor(`${driftEmoji} Overall Trend: ${drift.trend.toUpperCase()}`));
4953
5157
  if (drift.summary.newViolations.total > 0) {
4954
- console.log(chalk12.red(`
5158
+ console.log(chalk11.red(`
4955
5159
  \u26A0\uFE0F New Violations: ${drift.summary.newViolations.total}`));
4956
5160
  if (drift.summary.newViolations.critical > 0) {
4957
5161
  console.log(` \u2022 Critical: ${drift.summary.newViolations.critical}`);
@@ -4967,8 +5171,10 @@ Compliance Change: ${drift.complianceChange > 0 ? "+" : ""}${drift.complianceCha
4967
5171
  }
4968
5172
  }
4969
5173
  if (drift.summary.fixedViolations.total > 0) {
4970
- console.log(chalk12.green(`
4971
- \u2705 Fixed Violations: ${drift.summary.fixedViolations.total}`));
5174
+ console.log(
5175
+ chalk11.green(`
5176
+ \u2705 Fixed Violations: ${drift.summary.fixedViolations.total}`)
5177
+ );
4972
5178
  if (drift.summary.fixedViolations.critical > 0) {
4973
5179
  console.log(` \u2022 Critical: ${drift.summary.fixedViolations.critical}`);
4974
5180
  }
@@ -4983,18 +5189,22 @@ Compliance Change: ${drift.complianceChange > 0 ? "+" : ""}${drift.complianceCha
4983
5189
  }
4984
5190
  }
4985
5191
  if (drift.mostDegraded.length > 0) {
4986
- console.log(chalk12.red("\n\u{1F4C9} Most Degraded:"));
5192
+ console.log(chalk11.red("\n\u{1F4C9} Most Degraded:"));
4987
5193
  drift.mostDegraded.forEach((d) => {
4988
- console.log(` \u2022 ${d.title}: ${d.previousCompliance}% \u2192 ${d.currentCompliance}% (${d.complianceChange.toFixed(1)}%)`);
5194
+ console.log(
5195
+ ` \u2022 ${d.title}: ${d.previousCompliance}% \u2192 ${d.currentCompliance}% (${d.complianceChange.toFixed(1)}%)`
5196
+ );
4989
5197
  if (d.newViolations > 0) {
4990
5198
  console.log(` +${d.newViolations} new violation(s)`);
4991
5199
  }
4992
5200
  });
4993
5201
  }
4994
5202
  if (drift.mostImproved.length > 0) {
4995
- console.log(chalk12.green("\n\u{1F4C8} Most Improved:"));
5203
+ console.log(chalk11.green("\n\u{1F4C8} Most Improved:"));
4996
5204
  drift.mostImproved.forEach((d) => {
4997
- console.log(` \u2022 ${d.title}: ${d.previousCompliance}% \u2192 ${d.currentCompliance}% (+${d.complianceChange.toFixed(1)}%)`);
5205
+ console.log(
5206
+ ` \u2022 ${d.title}: ${d.previousCompliance}% \u2192 ${d.currentCompliance}% (+${d.complianceChange.toFixed(1)}%)`
5207
+ );
4998
5208
  if (d.fixedViolations > 0) {
4999
5209
  console.log(` -${d.fixedViolations} fixed violation(s)`);
5000
5210
  }
@@ -5027,12 +5237,9 @@ Compliance Change: ${drift.complianceChange > 0 ? "+" : ""}${drift.complianceCha
5027
5237
  }
5028
5238
  }
5029
5239
  if (options.output || options.save) {
5030
- const outputPath = options.output || join10(
5031
- getReportsDir(cwd),
5032
- `health-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.${extension}`
5033
- );
5240
+ const outputPath = options.output || join10(getReportsDir(cwd), `health-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.${extension}`);
5034
5241
  await writeTextFile(outputPath, output);
5035
- console.log(chalk12.green(`
5242
+ console.log(chalk11.green(`
5036
5243
  Report saved to: ${outputPath}`));
5037
5244
  if (options.save && !options.output) {
5038
5245
  const latestPath = join10(getReportsDir(cwd), `health-latest.${extension}`);
@@ -5047,7 +5254,7 @@ Report saved to: ${outputPath}`));
5047
5254
 
5048
5255
  // src/cli/commands/context.ts
5049
5256
  import { Command as Command11 } from "commander";
5050
- import chalk13 from "chalk";
5257
+ import chalk12 from "chalk";
5051
5258
 
5052
5259
  // src/agent/context.generator.ts
5053
5260
  async function generateContext(filePath, config, options = {}) {
@@ -5167,12 +5374,12 @@ var contextCommand = new Command11("context").description("Generate architectura
5167
5374
  });
5168
5375
  if (options.output) {
5169
5376
  await writeTextFile(options.output, output);
5170
- console.log(chalk13.green(`Context saved to: ${options.output}`));
5377
+ console.log(chalk12.green(`Context saved to: ${options.output}`));
5171
5378
  } else {
5172
5379
  console.log(output);
5173
5380
  }
5174
5381
  } catch (error) {
5175
- console.error(chalk13.red("Failed to generate context"));
5382
+ console.error(chalk12.red("Failed to generate context"));
5176
5383
  throw error;
5177
5384
  }
5178
5385
  });
@@ -5181,12 +5388,19 @@ var contextCommand = new Command11("context").description("Generate architectura
5181
5388
  import { Command as Command12 } from "commander";
5182
5389
 
5183
5390
  // src/lsp/server.ts
5184
- import { createConnection, ProposedFeatures, TextDocuments, TextDocumentSyncKind, DiagnosticSeverity, CodeActionKind } from "vscode-languageserver/node.js";
5391
+ import {
5392
+ createConnection,
5393
+ ProposedFeatures,
5394
+ TextDocuments,
5395
+ TextDocumentSyncKind,
5396
+ DiagnosticSeverity,
5397
+ CodeActionKind
5398
+ } from "vscode-languageserver/node.js";
5185
5399
  import { TextDocument } from "vscode-languageserver-textdocument";
5186
5400
  import { fileURLToPath } from "url";
5187
5401
  import path3 from "path";
5188
5402
  import { Project as Project3 } from "ts-morph";
5189
- import chalk14 from "chalk";
5403
+ import chalk13 from "chalk";
5190
5404
  function severityToDiagnostic(severity) {
5191
5405
  switch (severity) {
5192
5406
  case "critical":
@@ -5297,7 +5511,7 @@ var SpecBridgeLspServer = class {
5297
5511
  if (!await pathExists(getSpecBridgeDir(this.cwd))) {
5298
5512
  const err = new NotInitializedError();
5299
5513
  this.initError = err.message;
5300
- if (this.options.verbose) this.connection.console.error(chalk14.red(this.initError));
5514
+ if (this.options.verbose) this.connection.console.error(chalk13.red(this.initError));
5301
5515
  return;
5302
5516
  }
5303
5517
  try {
@@ -5306,7 +5520,8 @@ var SpecBridgeLspServer = class {
5306
5520
  await getPluginLoader().loadPlugins(this.cwd);
5307
5521
  } catch (error) {
5308
5522
  const msg = error instanceof Error ? error.message : String(error);
5309
- if (this.options.verbose) this.connection.console.error(chalk14.red(`Plugin load failed: ${msg}`));
5523
+ if (this.options.verbose)
5524
+ this.connection.console.error(chalk13.red(`Plugin load failed: ${msg}`));
5310
5525
  }
5311
5526
  this.registry = createRegistry({ basePath: this.cwd });
5312
5527
  await this.registry.load();
@@ -5319,11 +5534,13 @@ var SpecBridgeLspServer = class {
5319
5534
  }
5320
5535
  }
5321
5536
  if (this.options.verbose) {
5322
- this.connection.console.log(chalk14.dim(`Loaded ${this.decisions.length} active decision(s)`));
5537
+ this.connection.console.log(
5538
+ chalk13.dim(`Loaded ${this.decisions.length} active decision(s)`)
5539
+ );
5323
5540
  }
5324
5541
  } catch (error) {
5325
5542
  this.initError = error instanceof Error ? error.message : String(error);
5326
- if (this.options.verbose) this.connection.console.error(chalk14.red(this.initError));
5543
+ if (this.options.verbose) this.connection.console.error(chalk13.red(this.initError));
5327
5544
  }
5328
5545
  }
5329
5546
  async verifyTextDocument(doc) {
@@ -5337,7 +5554,11 @@ var SpecBridgeLspServer = class {
5337
5554
  for (const decision of this.decisions) {
5338
5555
  for (const constraint of decision.constraints) {
5339
5556
  if (!shouldApplyConstraintToFile({ filePath, constraint, cwd: this.cwd })) continue;
5340
- const verifier = selectVerifierForConstraint(constraint.rule, constraint.verifier, constraint.check);
5557
+ const verifier = selectVerifierForConstraint(
5558
+ constraint.rule,
5559
+ constraint.verifier,
5560
+ constraint.check
5561
+ );
5341
5562
  if (!verifier) continue;
5342
5563
  const ctx = {
5343
5564
  filePath,
@@ -5395,7 +5616,7 @@ var lspCommand = new Command12("lsp").description("Start SpecBridge language ser
5395
5616
 
5396
5617
  // src/cli/commands/watch.ts
5397
5618
  import { Command as Command13 } from "commander";
5398
- import chalk15 from "chalk";
5619
+ import chalk14 from "chalk";
5399
5620
  import chokidar from "chokidar";
5400
5621
  import path4 from "path";
5401
5622
  var watchCommand = new Command13("watch").description("Watch for changes and verify files continuously").option("-l, --level <level>", "Verification level (commit, pr, full)", "full").option("--debounce <ms>", "Debounce verify on rapid changes", "150").action(async (options) => {
@@ -5416,15 +5637,15 @@ var watchCommand = new Command13("watch").description("Watch for changes and ver
5416
5637
  files: [absolutePath],
5417
5638
  cwd
5418
5639
  });
5419
- const prefix = result.success ? chalk15.green("\u2713") : chalk15.red("\u2717");
5640
+ const prefix = result.success ? chalk14.green("\u2713") : chalk14.red("\u2717");
5420
5641
  const summary = `${prefix} ${path4.relative(cwd, absolutePath)}: ${result.violations.length} violation(s)`;
5421
5642
  console.log(summary);
5422
5643
  for (const v of result.violations.slice(0, 20)) {
5423
5644
  const loc = v.line ? `:${v.line}${v.column ? `:${v.column}` : ""}` : "";
5424
- console.log(chalk15.dim(` - ${v.file}${loc}: ${v.message} [${v.severity}]`));
5645
+ console.log(chalk14.dim(` - ${v.file}${loc}: ${v.message} [${v.severity}]`));
5425
5646
  }
5426
5647
  if (result.violations.length > 20) {
5427
- console.log(chalk15.dim(` \u2026 ${result.violations.length - 20} more`));
5648
+ console.log(chalk14.dim(` \u2026 ${result.violations.length - 20} more`));
5428
5649
  }
5429
5650
  };
5430
5651
  const watcher = chokidar.watch(config.project.sourceRoots, {
@@ -5433,7 +5654,7 @@ var watchCommand = new Command13("watch").description("Watch for changes and ver
5433
5654
  ignoreInitial: true,
5434
5655
  persistent: true
5435
5656
  });
5436
- console.log(chalk15.blue("Watching for changes..."));
5657
+ console.log(chalk14.blue("Watching for changes..."));
5437
5658
  watcher.on("change", (changedPath) => {
5438
5659
  pendingPath = changedPath;
5439
5660
  if (timer) clearTimeout(timer);
@@ -5725,7 +5946,7 @@ var promptCommand = new Command15("prompt").description("Generate AI agent promp
5725
5946
 
5726
5947
  // src/cli/commands/analytics.ts
5727
5948
  import { Command as Command16 } from "commander";
5728
- import chalk16 from "chalk";
5949
+ import chalk15 from "chalk";
5729
5950
  import ora7 from "ora";
5730
5951
 
5731
5952
  // src/analytics/engine.ts
@@ -5915,9 +6136,7 @@ var AnalyticsEngine = class {
5915
6136
  overallTrend = "down";
5916
6137
  }
5917
6138
  }
5918
- const sortedByCompliance = [...latest.byDecision].sort(
5919
- (a, b) => b.compliance - a.compliance
5920
- );
6139
+ const sortedByCompliance = [...latest.byDecision].sort((a, b) => b.compliance - a.compliance);
5921
6140
  const topDecisions = sortedByCompliance.slice(0, 5).map((d) => ({
5922
6141
  decisionId: d.decisionId,
5923
6142
  title: d.title,
@@ -5954,7 +6173,7 @@ var analyticsCommand = new Command16("analytics").description("Analyze complianc
5954
6173
  const history = await storage.loadHistory(days);
5955
6174
  if (history.length === 0) {
5956
6175
  spinner.fail("No historical reports found");
5957
- console.log(chalk16.yellow("\nGenerate reports with: specbridge report"));
6176
+ console.log(chalk15.yellow("\nGenerate reports with: specbridge report"));
5958
6177
  return;
5959
6178
  }
5960
6179
  spinner.succeed(`Loaded ${history.length} historical report(s)`);
@@ -5971,17 +6190,19 @@ var analyticsCommand = new Command16("analytics").description("Analyze complianc
5971
6190
  }
5972
6191
  if (decisionId) {
5973
6192
  const metrics = await engine.analyzeDecision(decisionId, history);
5974
- console.log("\n" + chalk16.blue.bold(`=== Decision Analytics: ${metrics.title} ===
6193
+ console.log("\n" + chalk15.blue.bold(`=== Decision Analytics: ${metrics.title} ===
5975
6194
  `));
5976
- console.log(chalk16.bold("Overview:"));
6195
+ console.log(chalk15.bold("Overview:"));
5977
6196
  console.log(` ID: ${metrics.decisionId}`);
5978
6197
  console.log(` Current Violations: ${metrics.totalViolations}`);
5979
6198
  console.log(` Average Compliance: ${metrics.averageComplianceScore.toFixed(1)}%`);
5980
6199
  const trendEmoji = metrics.trendDirection === "up" ? "\u{1F4C8}" : metrics.trendDirection === "down" ? "\u{1F4C9}" : "\u27A1\uFE0F";
5981
- const trendColor = metrics.trendDirection === "up" ? chalk16.green : metrics.trendDirection === "down" ? chalk16.red : chalk16.yellow;
5982
- console.log(` ${trendColor(`${trendEmoji} Trend: ${metrics.trendDirection.toUpperCase()}`)}`);
6200
+ const trendColor = metrics.trendDirection === "up" ? chalk15.green : metrics.trendDirection === "down" ? chalk15.red : chalk15.yellow;
6201
+ console.log(
6202
+ ` ${trendColor(`${trendEmoji} Trend: ${metrics.trendDirection.toUpperCase()}`)}`
6203
+ );
5983
6204
  if (metrics.history.length > 0) {
5984
- console.log(chalk16.bold("\nCompliance History:"));
6205
+ console.log(chalk15.bold("\nCompliance History:"));
5985
6206
  const recentHistory = metrics.history.slice(-10);
5986
6207
  recentHistory.forEach((h) => {
5987
6208
  const icon = h.violations === 0 ? "\u2705" : "\u26A0\uFE0F";
@@ -5990,58 +6211,60 @@ var analyticsCommand = new Command16("analytics").description("Analyze complianc
5990
6211
  }
5991
6212
  } else {
5992
6213
  const summary = await engine.generateSummary(history);
5993
- console.log("\n" + chalk16.blue.bold("=== Overall Analytics ===\n"));
5994
- console.log(chalk16.bold("Summary:"));
6214
+ console.log("\n" + chalk15.blue.bold("=== Overall Analytics ===\n"));
6215
+ console.log(chalk15.bold("Summary:"));
5995
6216
  console.log(` Total Decisions: ${summary.totalDecisions}`);
5996
6217
  console.log(` Average Compliance: ${summary.averageCompliance}%`);
5997
6218
  console.log(` Critical Issues: ${summary.criticalIssues}`);
5998
6219
  const trendEmoji = summary.overallTrend === "up" ? "\u{1F4C8}" : summary.overallTrend === "down" ? "\u{1F4C9}" : "\u27A1\uFE0F";
5999
- const trendColor = summary.overallTrend === "up" ? chalk16.green : summary.overallTrend === "down" ? chalk16.red : chalk16.yellow;
6000
- console.log(` ${trendColor(`${trendEmoji} Overall Trend: ${summary.overallTrend.toUpperCase()}`)}`);
6220
+ const trendColor = summary.overallTrend === "up" ? chalk15.green : summary.overallTrend === "down" ? chalk15.red : chalk15.yellow;
6221
+ console.log(
6222
+ ` ${trendColor(`${trendEmoji} Overall Trend: ${summary.overallTrend.toUpperCase()}`)}`
6223
+ );
6001
6224
  if (summary.topDecisions.length > 0) {
6002
- console.log(chalk16.green("\n\u2705 Top Performing Decisions:"));
6225
+ console.log(chalk15.green("\n\u2705 Top Performing Decisions:"));
6003
6226
  summary.topDecisions.forEach((d, i) => {
6004
6227
  console.log(` ${i + 1}. ${d.title}: ${d.compliance}%`);
6005
6228
  });
6006
6229
  }
6007
6230
  if (summary.bottomDecisions.length > 0) {
6008
- console.log(chalk16.red("\n\u26A0\uFE0F Decisions Needing Attention:"));
6231
+ console.log(chalk15.red("\n\u26A0\uFE0F Decisions Needing Attention:"));
6009
6232
  summary.bottomDecisions.forEach((d, i) => {
6010
6233
  console.log(` ${i + 1}. ${d.title}: ${d.compliance}%`);
6011
6234
  });
6012
6235
  }
6013
6236
  if (options.insights || summary.criticalIssues > 0) {
6014
- console.log(chalk16.blue.bold("\n=== Insights ===\n"));
6237
+ console.log(chalk15.blue.bold("\n=== Insights ===\n"));
6015
6238
  const insights = summary.insights;
6016
6239
  const warnings = insights.filter((i) => i.type === "warning");
6017
6240
  const successes = insights.filter((i) => i.type === "success");
6018
6241
  const infos = insights.filter((i) => i.type === "info");
6019
6242
  if (warnings.length > 0) {
6020
- console.log(chalk16.red("\u26A0\uFE0F Warnings:"));
6243
+ console.log(chalk15.red("\u26A0\uFE0F Warnings:"));
6021
6244
  warnings.forEach((i) => {
6022
6245
  console.log(` \u2022 ${i.message}`);
6023
6246
  if (i.details) {
6024
- console.log(chalk16.gray(` ${i.details}`));
6247
+ console.log(chalk15.gray(` ${i.details}`));
6025
6248
  }
6026
6249
  });
6027
6250
  console.log("");
6028
6251
  }
6029
6252
  if (successes.length > 0) {
6030
- console.log(chalk16.green("\u2705 Positive Trends:"));
6253
+ console.log(chalk15.green("\u2705 Positive Trends:"));
6031
6254
  successes.forEach((i) => {
6032
6255
  console.log(` \u2022 ${i.message}`);
6033
6256
  if (i.details) {
6034
- console.log(chalk16.gray(` ${i.details}`));
6257
+ console.log(chalk15.gray(` ${i.details}`));
6035
6258
  }
6036
6259
  });
6037
6260
  console.log("");
6038
6261
  }
6039
6262
  if (infos.length > 0) {
6040
- console.log(chalk16.blue("\u{1F4A1} Suggestions:"));
6263
+ console.log(chalk15.blue("\u{1F4A1} Suggestions:"));
6041
6264
  infos.forEach((i) => {
6042
6265
  console.log(` \u2022 ${i.message}`);
6043
6266
  if (i.details) {
6044
- console.log(chalk16.gray(` ${i.details}`));
6267
+ console.log(chalk15.gray(` ${i.details}`));
6045
6268
  }
6046
6269
  });
6047
6270
  console.log("");
@@ -6051,10 +6274,12 @@ var analyticsCommand = new Command16("analytics").description("Analyze complianc
6051
6274
  const latestEntry = history[history.length - 1];
6052
6275
  const oldestEntry = history[0];
6053
6276
  if (latestEntry && oldestEntry) {
6054
- console.log(chalk16.gray(`
6055
- Data range: ${latestEntry.timestamp} to ${oldestEntry.timestamp}`));
6277
+ console.log(
6278
+ chalk15.gray(`
6279
+ Data range: ${latestEntry.timestamp} to ${oldestEntry.timestamp}`)
6280
+ );
6056
6281
  }
6057
- console.log(chalk16.gray(`Analyzing ${history.length} report(s) over ${days} days
6282
+ console.log(chalk15.gray(`Analyzing ${history.length} report(s) over ${days} days
6058
6283
  `));
6059
6284
  } catch (error) {
6060
6285
  spinner.fail("Analytics failed");
@@ -6064,7 +6289,7 @@ Data range: ${latestEntry.timestamp} to ${oldestEntry.timestamp}`));
6064
6289
 
6065
6290
  // src/cli/commands/dashboard.ts
6066
6291
  import { Command as Command17 } from "commander";
6067
- import chalk17 from "chalk";
6292
+ import chalk16 from "chalk";
6068
6293
 
6069
6294
  // src/dashboard/server.ts
6070
6295
  import express from "express";
@@ -6083,6 +6308,7 @@ var DashboardServer = class {
6083
6308
  CACHE_TTL = 6e4;
6084
6309
  // 1 minute
6085
6310
  refreshInterval = null;
6311
+ logger = getLogger({ module: "dashboard.server" });
6086
6312
  constructor(options) {
6087
6313
  this.cwd = options.cwd;
6088
6314
  this.config = options.config;
@@ -6098,10 +6324,11 @@ var DashboardServer = class {
6098
6324
  async start() {
6099
6325
  await this.registry.load();
6100
6326
  await this.refreshCache();
6101
- this.refreshInterval = setInterval(
6102
- () => this.refreshCache().catch(console.error),
6103
- this.CACHE_TTL
6104
- );
6327
+ this.refreshInterval = setInterval(() => {
6328
+ void this.refreshCache().catch((error) => {
6329
+ this.logger.error({ error }, "Background cache refresh failed");
6330
+ });
6331
+ }, this.CACHE_TTL);
6105
6332
  }
6106
6333
  /**
6107
6334
  * Stop the server and clear intervals
@@ -6122,7 +6349,7 @@ var DashboardServer = class {
6122
6349
  this.cacheTimestamp = Date.now();
6123
6350
  await this.reportStorage.save(report);
6124
6351
  } catch (error) {
6125
- console.error("Cache refresh failed:", error);
6352
+ this.logger.error({ error }, "Cache refresh failed");
6126
6353
  if (!this.cachedReport) {
6127
6354
  try {
6128
6355
  const stored = await this.reportStorage.loadLatest();
@@ -6130,7 +6357,7 @@ var DashboardServer = class {
6130
6357
  this.cachedReport = stored.report;
6131
6358
  }
6132
6359
  } catch (fallbackError) {
6133
- console.error("Failed to load fallback report:", fallbackError);
6360
+ this.logger.error({ error: fallbackError }, "Failed to load fallback report");
6134
6361
  }
6135
6362
  }
6136
6363
  }
@@ -6373,11 +6600,13 @@ var DashboardServer = class {
6373
6600
  */
6374
6601
  setupStaticFiles() {
6375
6602
  const publicDir = join12(__dirname, "public");
6376
- this.app.use(express.static(publicDir, {
6377
- maxAge: "1h",
6378
- // Cache static assets
6379
- etag: true
6380
- }));
6603
+ this.app.use(
6604
+ express.static(publicDir, {
6605
+ maxAge: "1h",
6606
+ // Cache static assets
6607
+ etag: true
6608
+ })
6609
+ );
6381
6610
  this.app.get("/{*path}", (_req, res) => {
6382
6611
  res.sendFile(join12(publicDir, "index.html"));
6383
6612
  });
@@ -6393,7 +6622,7 @@ var dashboardCommand = new Command17("dashboard").description("Start compliance
6393
6622
  if (!await pathExists(getSpecBridgeDir(cwd))) {
6394
6623
  throw new NotInitializedError();
6395
6624
  }
6396
- console.log(chalk17.blue("Starting SpecBridge dashboard..."));
6625
+ console.log(chalk16.blue("Starting SpecBridge dashboard..."));
6397
6626
  try {
6398
6627
  const config = await loadConfig(cwd);
6399
6628
  const server = createDashboardServer({ cwd, config });
@@ -6401,32 +6630,34 @@ var dashboardCommand = new Command17("dashboard").description("Start compliance
6401
6630
  const port = parseInt(options.port || "3000", 10);
6402
6631
  const host = options.host || "localhost";
6403
6632
  server.getApp().listen(port, host, () => {
6404
- console.log(chalk17.green(`
6633
+ console.log(chalk16.green(`
6405
6634
  \u2713 Dashboard running at http://${host}:${port}`));
6406
- console.log(chalk17.gray(" Press Ctrl+C to stop\n"));
6407
- console.log(chalk17.bold("API Endpoints:"));
6408
- console.log(` ${chalk17.cyan(`http://${host}:${port}/api/health`)} - Health check`);
6409
- console.log(` ${chalk17.cyan(`http://${host}:${port}/api/report/latest`)} - Latest report (cached)`);
6410
- console.log(` ${chalk17.cyan(`http://${host}:${port}/api/decisions`)} - All decisions`);
6411
- console.log(` ${chalk17.cyan(`http://${host}:${port}/api/analytics/summary`)} - Analytics`);
6635
+ console.log(chalk16.gray(" Press Ctrl+C to stop\n"));
6636
+ console.log(chalk16.bold("API Endpoints:"));
6637
+ console.log(` ${chalk16.cyan(`http://${host}:${port}/api/health`)} - Health check`);
6638
+ console.log(
6639
+ ` ${chalk16.cyan(`http://${host}:${port}/api/report/latest`)} - Latest report (cached)`
6640
+ );
6641
+ console.log(` ${chalk16.cyan(`http://${host}:${port}/api/decisions`)} - All decisions`);
6642
+ console.log(` ${chalk16.cyan(`http://${host}:${port}/api/analytics/summary`)} - Analytics`);
6412
6643
  console.log("");
6413
6644
  });
6414
6645
  const shutdown = () => {
6415
- console.log(chalk17.yellow("\n\nShutting down dashboard..."));
6646
+ console.log(chalk16.yellow("\n\nShutting down dashboard..."));
6416
6647
  server.stop();
6417
6648
  process.exit(0);
6418
6649
  };
6419
6650
  process.on("SIGINT", shutdown);
6420
6651
  process.on("SIGTERM", shutdown);
6421
6652
  } catch (error) {
6422
- console.error(chalk17.red("Failed to start dashboard:"), error);
6653
+ console.error(chalk16.red("Failed to start dashboard:"), error);
6423
6654
  throw error;
6424
6655
  }
6425
6656
  });
6426
6657
 
6427
6658
  // src/cli/commands/impact.ts
6428
6659
  import { Command as Command18 } from "commander";
6429
- import chalk18 from "chalk";
6660
+ import chalk17 from "chalk";
6430
6661
  import ora8 from "ora";
6431
6662
 
6432
6663
  // src/propagation/graph.ts
@@ -6513,7 +6744,24 @@ var PropagationEngine = class {
6513
6744
  if (!this.graph) {
6514
6745
  await this.initialize(config, options);
6515
6746
  }
6516
- const affectedFilePaths = getAffectedFiles(this.graph, decisionId);
6747
+ const graph = this.graph;
6748
+ if (!graph) {
6749
+ return {
6750
+ decision: decisionId,
6751
+ change,
6752
+ affectedFiles: [],
6753
+ estimatedEffort: "low",
6754
+ migrationSteps: [
6755
+ {
6756
+ order: 1,
6757
+ description: "Run verification to confirm all violations resolved",
6758
+ files: [],
6759
+ automated: true
6760
+ }
6761
+ ]
6762
+ };
6763
+ }
6764
+ const affectedFilePaths = getAffectedFiles(graph, decisionId);
6517
6765
  const verificationEngine = createVerificationEngine(this.registry);
6518
6766
  const result = await verificationEngine.verify(config, {
6519
6767
  files: affectedFilePaths,
@@ -6546,10 +6794,7 @@ var PropagationEngine = class {
6546
6794
  } else {
6547
6795
  estimatedEffort = "high";
6548
6796
  }
6549
- const migrationSteps = this.generateMigrationSteps(
6550
- affectedFiles,
6551
- totalAutoFixable > 0
6552
- );
6797
+ const migrationSteps = this.generateMigrationSteps(affectedFiles, totalAutoFixable > 0);
6553
6798
  return {
6554
6799
  decision: decisionId,
6555
6800
  change,
@@ -6572,9 +6817,7 @@ var PropagationEngine = class {
6572
6817
  automated: true
6573
6818
  });
6574
6819
  }
6575
- const filesWithManualFixes = affectedFiles.filter(
6576
- (f) => f.violations > f.autoFixable
6577
- );
6820
+ const filesWithManualFixes = affectedFiles.filter((f) => f.violations > f.autoFixable);
6578
6821
  if (filesWithManualFixes.length > 0) {
6579
6822
  const highPriority = filesWithManualFixes.filter((f) => f.violations > 5);
6580
6823
  const mediumPriority = filesWithManualFixes.filter(
@@ -6637,8 +6880,8 @@ var impactCommand = new Command18("impact").description("Analyze impact of decis
6637
6880
  const changeType = options.change || "modified";
6638
6881
  if (!["created", "modified", "deprecated"].includes(changeType)) {
6639
6882
  spinner.fail();
6640
- console.error(chalk18.red(`Invalid change type: ${changeType}`));
6641
- console.error(chalk18.dim("Valid types: created, modified, deprecated"));
6883
+ console.error(chalk17.red(`Invalid change type: ${changeType}`));
6884
+ console.error(chalk17.dim("Valid types: created, modified, deprecated"));
6642
6885
  process.exit(1);
6643
6886
  }
6644
6887
  spinner.text = `Analyzing impact of ${changeType} decision ${decisionId}...`;
@@ -6656,44 +6899,44 @@ var impactCommand = new Command18("impact").description("Analyze impact of decis
6656
6899
  }
6657
6900
  });
6658
6901
  function printImpactAnalysis(analysis, showSteps) {
6659
- console.log(chalk18.bold(`
6902
+ console.log(chalk17.bold(`
6660
6903
  === Impact Analysis: ${analysis.decision} ===
6661
6904
  `));
6662
- const changeLabel = chalk18.cyan(analysis.change);
6905
+ const changeLabel = chalk17.cyan(analysis.change);
6663
6906
  console.log(`Change Type: ${changeLabel}`);
6664
- const effortColor = analysis.estimatedEffort === "high" ? chalk18.red : analysis.estimatedEffort === "medium" ? chalk18.yellow : chalk18.green;
6907
+ const effortColor = analysis.estimatedEffort === "high" ? chalk17.red : analysis.estimatedEffort === "medium" ? chalk17.yellow : chalk17.green;
6665
6908
  console.log(`Estimated Effort: ${effortColor(analysis.estimatedEffort.toUpperCase())}
6666
6909
  `);
6667
- console.log(chalk18.bold(`Affected Files: ${analysis.affectedFiles.length}`));
6910
+ console.log(chalk17.bold(`Affected Files: ${analysis.affectedFiles.length}`));
6668
6911
  if (analysis.affectedFiles.length > 0) {
6669
6912
  const displayCount = Math.min(analysis.affectedFiles.length, 10);
6670
6913
  for (let i = 0; i < displayCount; i++) {
6671
6914
  const file = analysis.affectedFiles[i];
6672
6915
  if (!file) continue;
6673
6916
  const violationText = file.violations === 1 ? "1 violation" : `${file.violations} violations`;
6674
- const autoFixText = file.autoFixable > 0 ? chalk18.green(` (${file.autoFixable} auto-fixable)`) : "";
6675
- console.log(` ${chalk18.red("\u25CF")} ${file.path} - ${violationText}${autoFixText}`);
6917
+ const autoFixText = file.autoFixable > 0 ? chalk17.green(` (${file.autoFixable} auto-fixable)`) : "";
6918
+ console.log(` ${chalk17.red("\u25CF")} ${file.path} - ${violationText}${autoFixText}`);
6676
6919
  }
6677
6920
  if (analysis.affectedFiles.length > displayCount) {
6678
6921
  const remaining = analysis.affectedFiles.length - displayCount;
6679
- console.log(chalk18.dim(` ... and ${remaining} more file(s)`));
6922
+ console.log(chalk17.dim(` ... and ${remaining} more file(s)`));
6680
6923
  }
6681
6924
  } else {
6682
- console.log(chalk18.green(" No violations found"));
6925
+ console.log(chalk17.green(" No violations found"));
6683
6926
  }
6684
6927
  if (showSteps && analysis.migrationSteps && analysis.migrationSteps.length > 0) {
6685
- console.log(chalk18.bold("\nMigration Plan:"));
6928
+ console.log(chalk17.bold("\nMigration Plan:"));
6686
6929
  for (const step of analysis.migrationSteps) {
6687
6930
  const icon = step.automated ? "\u{1F916}" : "\u{1F464}";
6688
- const typeLabel = step.automated ? chalk18.green("[Automated]") : chalk18.yellow("[Manual]");
6931
+ const typeLabel = step.automated ? chalk17.green("[Automated]") : chalk17.yellow("[Manual]");
6689
6932
  console.log(` ${icon} Step ${step.order}: ${step.description} ${typeLabel}`);
6690
6933
  if (step.files.length > 0) {
6691
6934
  const displayFiles = Math.min(step.files.length, 3);
6692
6935
  for (let i = 0; i < displayFiles; i++) {
6693
- console.log(chalk18.dim(` - ${step.files[i]}`));
6936
+ console.log(chalk17.dim(` - ${step.files[i]}`));
6694
6937
  }
6695
6938
  if (step.files.length > displayFiles) {
6696
- console.log(chalk18.dim(` ... and ${step.files.length - displayFiles} more file(s)`));
6939
+ console.log(chalk17.dim(` ... and ${step.files.length - displayFiles} more file(s)`));
6697
6940
  }
6698
6941
  }
6699
6942
  console.log("");
@@ -6702,15 +6945,17 @@ function printImpactAnalysis(analysis, showSteps) {
6702
6945
  const totalViolations = analysis.affectedFiles.reduce((sum, f) => sum + f.violations, 0);
6703
6946
  const totalAutoFixable = analysis.affectedFiles.reduce((sum, f) => sum + f.autoFixable, 0);
6704
6947
  const manualFixes = totalViolations - totalAutoFixable;
6705
- console.log(chalk18.bold("Summary:"));
6948
+ console.log(chalk17.bold("Summary:"));
6706
6949
  console.log(` Total Violations: ${totalViolations}`);
6707
- console.log(` Auto-fixable: ${chalk18.green(totalAutoFixable)}`);
6708
- console.log(` Manual Fixes Required: ${manualFixes > 0 ? chalk18.yellow(manualFixes) : chalk18.green(0)}`);
6950
+ console.log(` Auto-fixable: ${chalk17.green(totalAutoFixable)}`);
6951
+ console.log(
6952
+ ` Manual Fixes Required: ${manualFixes > 0 ? chalk17.yellow(manualFixes) : chalk17.green(0)}`
6953
+ );
6709
6954
  }
6710
6955
 
6711
6956
  // src/cli/commands/migrate.ts
6712
6957
  import { Command as Command19 } from "commander";
6713
- import chalk19 from "chalk";
6958
+ import chalk18 from "chalk";
6714
6959
  import ora9 from "ora";
6715
6960
  import { join as join13 } from "path";
6716
6961
  import { readdir as readdir2, copyFile, mkdir as mkdir2, readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
@@ -6721,17 +6966,17 @@ var migrateCommand = new Command19("migrate").description("Migrate SpecBridge co
6721
6966
  }
6722
6967
  const from = options.from || "v1";
6723
6968
  const to = options.to || "v2";
6724
- console.log(chalk19.blue.bold(`
6969
+ console.log(chalk18.blue.bold(`
6725
6970
  === SpecBridge Migration: ${from} \u2192 ${to} ===
6726
6971
  `));
6727
6972
  if (from !== "v1" && from !== "v1.3") {
6728
- console.error(chalk19.red(`Unsupported source version: ${from}`));
6729
- console.log(chalk19.gray("Supported: v1, v1.3"));
6973
+ console.error(chalk18.red(`Unsupported source version: ${from}`));
6974
+ console.log(chalk18.gray("Supported: v1, v1.3"));
6730
6975
  process.exit(1);
6731
6976
  }
6732
6977
  if (to !== "v2" && to !== "v2.0") {
6733
- console.error(chalk19.red(`Unsupported target version: ${to}`));
6734
- console.log(chalk19.gray("Supported: v2, v2.0"));
6978
+ console.error(chalk18.red(`Unsupported target version: ${to}`));
6979
+ console.log(chalk18.gray("Supported: v2, v2.0"));
6735
6980
  process.exit(1);
6736
6981
  }
6737
6982
  const spinner = ora9("Analyzing current configuration...").start();
@@ -6751,7 +6996,7 @@ var migrateCommand = new Command19("migrate").description("Migrate SpecBridge co
6751
6996
  const config = await loadConfig(cwd);
6752
6997
  const v1Report = await generateReport(config, { cwd, legacyCompliance: true });
6753
6998
  v1Compliance = v1Report.summary.compliance;
6754
- } catch (error) {
6999
+ } catch {
6755
7000
  spinner.warn("Could not generate v1 baseline report");
6756
7001
  }
6757
7002
  spinner.text = "Migrating decision files...";
@@ -6773,7 +7018,7 @@ var migrateCommand = new Command19("migrate").description("Migrate SpecBridge co
6773
7018
  v2: v2Compliance,
6774
7019
  difference: v2Compliance - v1Compliance
6775
7020
  };
6776
- } catch (error) {
7021
+ } catch {
6777
7022
  spinner.warn("Could not generate v2 comparison report");
6778
7023
  }
6779
7024
  }
@@ -6782,41 +7027,49 @@ var migrateCommand = new Command19("migrate").description("Migrate SpecBridge co
6782
7027
  report.changes.push("All decisions validated successfully");
6783
7028
  }
6784
7029
  spinner.succeed(options.dryRun ? "Migration preview complete" : "Migration complete");
6785
- console.log(chalk19.green.bold("\n\u2713 Migration Summary:\n"));
6786
- console.log(chalk19.bold("Backup:"));
7030
+ console.log(chalk18.green.bold("\n\u2713 Migration Summary:\n"));
7031
+ console.log(chalk18.bold("Backup:"));
6787
7032
  console.log(` ${report.backupPath}`);
6788
7033
  console.log("");
6789
- console.log(chalk19.bold("Changes:"));
7034
+ console.log(chalk18.bold("Changes:"));
6790
7035
  for (const change of report.changes) {
6791
7036
  console.log(` \u2022 ${change}`);
6792
7037
  }
6793
7038
  console.log("");
6794
7039
  if (report.complianceComparison) {
6795
- console.log(chalk19.bold("Compliance Comparison:"));
7040
+ console.log(chalk18.bold("Compliance Comparison:"));
6796
7041
  console.log(` v1.3 formula: ${report.complianceComparison.v1}%`);
6797
7042
  console.log(` v2.0 formula: ${report.complianceComparison.v2}%`);
6798
7043
  const diff = report.complianceComparison.difference;
6799
- const diffColor = diff > 0 ? chalk19.green : diff < 0 ? chalk19.red : chalk19.yellow;
7044
+ const diffColor = diff > 0 ? chalk18.green : diff < 0 ? chalk18.red : chalk18.yellow;
6800
7045
  console.log(` Difference: ${diffColor(`${diff > 0 ? "+" : ""}${diff.toFixed(1)}%`)}`);
6801
7046
  console.log("");
6802
7047
  if (Math.abs(diff) > 10) {
6803
- console.log(chalk19.yellow("\u26A0\uFE0F Note: Compliance score changed significantly due to severity weighting."));
6804
- console.log(chalk19.gray(" Consider adjusting CI thresholds if needed.\n"));
7048
+ console.log(
7049
+ chalk18.yellow(
7050
+ "\u26A0\uFE0F Note: Compliance score changed significantly due to severity weighting."
7051
+ )
7052
+ );
7053
+ console.log(chalk18.gray(" Consider adjusting CI thresholds if needed.\n"));
6805
7054
  }
6806
7055
  }
6807
7056
  if (options.dryRun) {
6808
- console.log(chalk19.yellow("This was a dry run. No changes were applied."));
6809
- console.log(chalk19.gray("Run without --dry-run to apply changes.\n"));
7057
+ console.log(chalk18.yellow("This was a dry run. No changes were applied."));
7058
+ console.log(chalk18.gray("Run without --dry-run to apply changes.\n"));
6810
7059
  } else {
6811
- console.log(chalk19.green("\u2713 Migration successful!"));
6812
- console.log(chalk19.gray(`
7060
+ console.log(chalk18.green("\u2713 Migration successful!"));
7061
+ console.log(
7062
+ chalk18.gray(
7063
+ `
6813
7064
  Rollback: Copy files from ${report.backupPath} back to .specbridge/decisions/
6814
- `));
7065
+ `
7066
+ )
7067
+ );
6815
7068
  }
6816
7069
  } catch (error) {
6817
7070
  spinner.fail("Migration failed");
6818
- console.error(chalk19.red("\nError:"), error instanceof Error ? error.message : error);
6819
- console.log(chalk19.gray("\nNo changes were applied."));
7071
+ console.error(chalk18.red("\nError:"), error instanceof Error ? error.message : error);
7072
+ console.log(chalk18.gray("\nNo changes were applied."));
6820
7073
  throw error;
6821
7074
  }
6822
7075
  });
@@ -6832,10 +7085,7 @@ async function createBackup(cwd, dryRun) {
6832
7085
  const files = await readdir2(decisionsDir);
6833
7086
  for (const file of files) {
6834
7087
  if (file.endsWith(".decision.yaml") || file.endsWith(".decision.yml")) {
6835
- await copyFile(
6836
- join13(decisionsDir, file),
6837
- join13(backupDir, file)
6838
- );
7088
+ await copyFile(join13(decisionsDir, file), join13(backupDir, file));
6839
7089
  }
6840
7090
  }
6841
7091
  }
@@ -6863,7 +7113,7 @@ async function migrateDecisions(cwd, dryRun) {
6863
7113
  "$1check:\n$1 verifier: $2"
6864
7114
  );
6865
7115
  if (dryRun) {
6866
- console.log(chalk19.gray(` Would migrate: ${file}`));
7116
+ console.log(chalk18.gray(` Would migrate: ${file}`));
6867
7117
  updatedCount++;
6868
7118
  } else {
6869
7119
  await writeFile3(filePath, migratedContent, "utf-8");
@@ -6878,7 +7128,9 @@ var __dirname2 = dirname5(fileURLToPath4(import.meta.url));
6878
7128
  var packageJsonPath = join14(__dirname2, "../package.json");
6879
7129
  var packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf-8"));
6880
7130
  var program = new Command20();
6881
- program.name("specbridge").description("Architecture Decision Runtime - Transform architectural decisions into executable, verifiable constraints").version(packageJson.version);
7131
+ program.name("specbridge").description(
7132
+ "Architecture Decision Runtime - Transform architectural decisions into executable, verifiable constraints"
7133
+ ).version(packageJson.version);
6882
7134
  program.addCommand(initCommand);
6883
7135
  program.addCommand(inferCommand);
6884
7136
  program.addCommand(verifyCommand);
@@ -6901,11 +7153,11 @@ program.exitOverride((err) => {
6901
7153
  if (err.code === "commander.version") {
6902
7154
  process.exit(0);
6903
7155
  }
6904
- console.error(chalk20.red(formatError(err)));
7156
+ console.error(chalk19.red(formatError(err)));
6905
7157
  process.exit(1);
6906
7158
  });
6907
7159
  program.parseAsync(process.argv).catch((error) => {
6908
- console.error(chalk20.red(formatError(error)));
7160
+ console.error(chalk19.red(formatError(error)));
6909
7161
  process.exit(1);
6910
7162
  });
6911
7163
  //# sourceMappingURL=cli.js.map