@ipation/specbridge 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1718,6 +1718,121 @@ declare function formatConsoleReport(report: ComplianceReport): string;
1718
1718
  */
1719
1719
  declare function formatMarkdownReport(report: ComplianceReport): string;
1720
1720
 
1721
+ interface StoredReport {
1722
+ timestamp: string;
1723
+ report: ComplianceReport;
1724
+ }
1725
+ /**
1726
+ * ReportStorage - Handles persistence and retrieval of historical reports
1727
+ */
1728
+ declare class ReportStorage {
1729
+ private storageDir;
1730
+ constructor(basePath: string);
1731
+ /**
1732
+ * Save a compliance report to storage
1733
+ */
1734
+ save(report: ComplianceReport): Promise<string>;
1735
+ /**
1736
+ * Load the most recent report
1737
+ */
1738
+ loadLatest(): Promise<StoredReport | null>;
1739
+ /**
1740
+ * Load historical reports for the specified number of days
1741
+ */
1742
+ loadHistory(days?: number): Promise<StoredReport[]>;
1743
+ /**
1744
+ * Load a specific report by date
1745
+ */
1746
+ loadByDate(date: string): Promise<ComplianceReport | null>;
1747
+ /**
1748
+ * Get all available report dates
1749
+ */
1750
+ getAvailableDates(): Promise<string[]>;
1751
+ /**
1752
+ * Clear old reports (keep only the most recent N days)
1753
+ */
1754
+ cleanup(keepDays?: number): Promise<number>;
1755
+ }
1756
+
1757
+ /**
1758
+ * Drift detection - Analyze compliance trends between reports
1759
+ */
1760
+
1761
+ type TrendDirection = 'improving' | 'stable' | 'degrading';
1762
+ interface DriftAnalysis {
1763
+ decisionId: string;
1764
+ title: string;
1765
+ trend: TrendDirection;
1766
+ complianceChange: number;
1767
+ newViolations: number;
1768
+ fixedViolations: number;
1769
+ currentCompliance: number;
1770
+ previousCompliance: number;
1771
+ }
1772
+ interface OverallDrift {
1773
+ trend: TrendDirection;
1774
+ complianceChange: number;
1775
+ summary: {
1776
+ newViolations: {
1777
+ critical: number;
1778
+ high: number;
1779
+ medium: number;
1780
+ low: number;
1781
+ total: number;
1782
+ };
1783
+ fixedViolations: {
1784
+ critical: number;
1785
+ high: number;
1786
+ medium: number;
1787
+ low: number;
1788
+ total: number;
1789
+ };
1790
+ };
1791
+ byDecision: DriftAnalysis[];
1792
+ mostImproved: DriftAnalysis[];
1793
+ mostDegraded: DriftAnalysis[];
1794
+ }
1795
+ /**
1796
+ * Detect drift between current and previous compliance reports
1797
+ */
1798
+ declare function detectDrift(current: ComplianceReport, previous: ComplianceReport): Promise<OverallDrift>;
1799
+ /**
1800
+ * Analyze compliance trend over multiple reports
1801
+ */
1802
+ interface TrendAnalysis {
1803
+ period: {
1804
+ start: string;
1805
+ end: string;
1806
+ days: number;
1807
+ };
1808
+ overall: {
1809
+ startCompliance: number;
1810
+ endCompliance: number;
1811
+ change: number;
1812
+ trend: TrendDirection;
1813
+ dataPoints: Array<{
1814
+ date: string;
1815
+ compliance: number;
1816
+ }>;
1817
+ };
1818
+ decisions: Array<{
1819
+ decisionId: string;
1820
+ title: string;
1821
+ startCompliance: number;
1822
+ endCompliance: number;
1823
+ change: number;
1824
+ trend: TrendDirection;
1825
+ dataPoints: Array<{
1826
+ date: string;
1827
+ compliance: number;
1828
+ }>;
1829
+ }>;
1830
+ }
1831
+ declare function analyzeTrend(reports: Array<{
1832
+ timestamp: string;
1833
+ report: ComplianceReport;
1834
+ }>): Promise<TrendAnalysis>;
1835
+
1721
1836
  /**
1722
1837
  * Agent context generator
1723
1838
  */
@@ -1905,4 +2020,4 @@ declare function matchesAnyPattern(filePath: string, patterns: string[], options
1905
2020
  cwd?: string;
1906
2021
  }): boolean;
1907
2022
 
1908
- export { type AffectedFile, type AgentContext, AgentContextGenerator, AlreadyInitializedError, type Analyzer, AnalyzerNotFoundError, ApiVerifier, type ApplicableConstraint, type ApplicableDecision, AstCache, AutofixEngine, type AutofixPatch, type AutofixResult, CodeScanner, ComplexityVerifier, type ComplianceReport, ConfigError, type Constraint, type ConstraintException, ConstraintExceptionSchema, type ConstraintExceptionSchema_, ConstraintSchema, type ConstraintSchema_, type ConstraintType, ConstraintTypeSchema, type ConstraintTypeSchema_, type ContextOptions, type Decision, type DecisionCompliance, type DecisionContent, DecisionContentSchema, type DecisionContentSchema_, type DecisionFilter, type DecisionMetadata, DecisionMetadataSchema, type DecisionMetadataSchema_, DecisionNotFoundError, DecisionSchema, type DecisionStatus, DecisionStatusSchema, type DecisionStatusSchema_, type DecisionTypeSchema, DecisionValidationError, type DependencyGraph, DependencyVerifier, ErrorsAnalyzer, ErrorsVerifier, FileSystemError, type GlobOptions, type GraphNode, HookError, type ImpactAnalysis, ImportsAnalyzer, ImportsVerifier, InferenceEngine, InferenceError, type InferenceOptions, type InferenceResult, type LevelConfig, LinksSchema, type LoadError, type LoadResult, type LoadedDecision, type McpServerOptions, type MigrationStep, NamingAnalyzer, NamingVerifier, NotInitializedError, type Pattern, type PatternExample, type PromptTemplate, PropagationEngine, type PropagationOptions, RegexVerifier, Registry, type RegistryConstraintMatch, RegistryError, type RegistryOptions, type ReportOptions, Reporter, type ScanOptions, type ScanResult, type ScannedFile, SecurityVerifier, type Severity, SeveritySchema, type SeveritySchema_, type SpecBridgeConfig, SpecBridgeConfigSchema, type SpecBridgeConfigType, SpecBridgeError, SpecBridgeMcpServer, StructureAnalyzer, type TextEdit, type TrendData, type VerificationConfig, VerificationConfigSchema, type VerificationConfigSchema_, type VerificationContext, VerificationEngine, VerificationError, type VerificationFrequency, VerificationFrequencySchema, type VerificationFrequencySchema_, type VerificationLevel, type VerificationOptions, type VerificationResult, type Verifier, VerifierNotFoundError, type Violation, type ViolationFix, buildDependencyGraph, builtinAnalyzers, builtinVerifiers, calculateConfidence, checkDegradation, createInferenceEngine, createPattern, createPropagationEngine, createRegistry, createScannerFromConfig, createVerificationEngine, createViolation, defaultConfig, ensureDir, extractSnippet, formatConsoleReport, formatContextAsJson, formatContextAsMarkdown, formatContextAsMcp, formatError, formatMarkdownReport, formatValidationErrors, generateContext, generateFormattedContext, generateReport, getAffectedFiles, getAffectingDecisions, getAnalyzer, getAnalyzerIds, getChangedFiles, getConfigPath, getDecisionsDir, getInferredDir, getReportsDir, getSpecBridgeDir, getTransitiveDependencies, getVerifier, getVerifierIds, getVerifiersDir, glob, isConstraintExcepted, isDirectory, loadConfig, loadDecisionFile, loadDecisionsFromDir, matchesAnyPattern, matchesPattern, mergeWithDefaults, normalizePath, parseYaml, parseYamlDocument, pathExists, readFilesInDir, readTextFile, runInference, selectVerifierForConstraint, shouldApplyConstraintToFile, stringifyYaml, templates, updateYamlDocument, validateConfig, validateDecision, validateDecisionFile, writeTextFile };
2023
+ export { type AffectedFile, type AgentContext, AgentContextGenerator, AlreadyInitializedError, type Analyzer, AnalyzerNotFoundError, ApiVerifier, type ApplicableConstraint, type ApplicableDecision, AstCache, AutofixEngine, type AutofixPatch, type AutofixResult, CodeScanner, ComplexityVerifier, type ComplianceReport, ConfigError, type Constraint, type ConstraintException, ConstraintExceptionSchema, type ConstraintExceptionSchema_, ConstraintSchema, type ConstraintSchema_, type ConstraintType, ConstraintTypeSchema, type ConstraintTypeSchema_, type ContextOptions, type Decision, type DecisionCompliance, type DecisionContent, DecisionContentSchema, type DecisionContentSchema_, type DecisionFilter, type DecisionMetadata, DecisionMetadataSchema, type DecisionMetadataSchema_, DecisionNotFoundError, DecisionSchema, type DecisionStatus, DecisionStatusSchema, type DecisionStatusSchema_, type DecisionTypeSchema, DecisionValidationError, type DependencyGraph, DependencyVerifier, type DriftAnalysis, ErrorsAnalyzer, ErrorsVerifier, FileSystemError, type GlobOptions, type GraphNode, HookError, type ImpactAnalysis, ImportsAnalyzer, ImportsVerifier, InferenceEngine, InferenceError, type InferenceOptions, type InferenceResult, type LevelConfig, LinksSchema, type LoadError, type LoadResult, type LoadedDecision, type McpServerOptions, type MigrationStep, NamingAnalyzer, NamingVerifier, NotInitializedError, type OverallDrift, type Pattern, type PatternExample, type PromptTemplate, PropagationEngine, type PropagationOptions, RegexVerifier, Registry, type RegistryConstraintMatch, RegistryError, type RegistryOptions, type ReportOptions, ReportStorage, Reporter, type ScanOptions, type ScanResult, type ScannedFile, SecurityVerifier, type Severity, SeveritySchema, type SeveritySchema_, type SpecBridgeConfig, SpecBridgeConfigSchema, type SpecBridgeConfigType, SpecBridgeError, SpecBridgeMcpServer, type StoredReport, StructureAnalyzer, type TextEdit, type TrendAnalysis, type TrendData, type TrendDirection, type VerificationConfig, VerificationConfigSchema, type VerificationConfigSchema_, type VerificationContext, VerificationEngine, VerificationError, type VerificationFrequency, VerificationFrequencySchema, type VerificationFrequencySchema_, type VerificationLevel, type VerificationOptions, type VerificationResult, type Verifier, VerifierNotFoundError, type Violation, type ViolationFix, analyzeTrend, buildDependencyGraph, builtinAnalyzers, builtinVerifiers, calculateConfidence, checkDegradation, createInferenceEngine, createPattern, createPropagationEngine, createRegistry, createScannerFromConfig, createVerificationEngine, createViolation, defaultConfig, detectDrift, ensureDir, extractSnippet, formatConsoleReport, formatContextAsJson, formatContextAsMarkdown, formatContextAsMcp, formatError, formatMarkdownReport, formatValidationErrors, generateContext, generateFormattedContext, generateReport, getAffectedFiles, getAffectingDecisions, getAnalyzer, getAnalyzerIds, getChangedFiles, getConfigPath, getDecisionsDir, getInferredDir, getReportsDir, getSpecBridgeDir, getTransitiveDependencies, getVerifier, getVerifierIds, getVerifiersDir, glob, isConstraintExcepted, isDirectory, loadConfig, loadDecisionFile, loadDecisionsFromDir, matchesAnyPattern, matchesPattern, mergeWithDefaults, normalizePath, parseYaml, parseYamlDocument, pathExists, readFilesInDir, readTextFile, runInference, selectVerifierForConstraint, shouldApplyConstraintToFile, stringifyYaml, templates, updateYamlDocument, validateConfig, validateDecision, validateDecisionFile, writeTextFile };
package/dist/index.js CHANGED
@@ -3552,6 +3552,291 @@ function formatProgressBar(percentage) {
3552
3552
  return `\`${filledChar.repeat(filled)}${emptyChar.repeat(empty)}\` ${percentage}%`;
3553
3553
  }
3554
3554
 
3555
+ // src/reporting/storage.ts
3556
+ import { join as join3 } from "path";
3557
+ var ReportStorage = class {
3558
+ storageDir;
3559
+ constructor(basePath) {
3560
+ this.storageDir = join3(getSpecBridgeDir(basePath), "reports", "history");
3561
+ }
3562
+ /**
3563
+ * Save a compliance report to storage
3564
+ */
3565
+ async save(report) {
3566
+ await ensureDir(this.storageDir);
3567
+ const date = new Date(report.timestamp).toISOString().split("T")[0];
3568
+ const filename = `report-${date}.json`;
3569
+ const filepath = join3(this.storageDir, filename);
3570
+ await writeTextFile(filepath, JSON.stringify(report, null, 2));
3571
+ return filepath;
3572
+ }
3573
+ /**
3574
+ * Load the most recent report
3575
+ */
3576
+ async loadLatest() {
3577
+ if (!await pathExists(this.storageDir)) {
3578
+ return null;
3579
+ }
3580
+ const files = await readFilesInDir(this.storageDir);
3581
+ if (files.length === 0) {
3582
+ return null;
3583
+ }
3584
+ const sortedFiles = files.filter((f) => f.startsWith("report-") && f.endsWith(".json")).sort().reverse();
3585
+ if (sortedFiles.length === 0) {
3586
+ return null;
3587
+ }
3588
+ const latestFile = sortedFiles[0];
3589
+ if (!latestFile) {
3590
+ return null;
3591
+ }
3592
+ const content = await readTextFile(join3(this.storageDir, latestFile));
3593
+ const report = JSON.parse(content);
3594
+ return {
3595
+ timestamp: latestFile.replace("report-", "").replace(".json", ""),
3596
+ report
3597
+ };
3598
+ }
3599
+ /**
3600
+ * Load historical reports for the specified number of days
3601
+ */
3602
+ async loadHistory(days = 30) {
3603
+ if (!await pathExists(this.storageDir)) {
3604
+ return [];
3605
+ }
3606
+ const files = await readFilesInDir(this.storageDir);
3607
+ const reportFiles = files.filter((f) => f.startsWith("report-") && f.endsWith(".json")).sort().reverse();
3608
+ const recentFiles = reportFiles.slice(0, days);
3609
+ const reports = [];
3610
+ for (const file of recentFiles) {
3611
+ try {
3612
+ const content = await readTextFile(join3(this.storageDir, file));
3613
+ const report = JSON.parse(content);
3614
+ const timestamp = file.replace("report-", "").replace(".json", "");
3615
+ reports.push({ timestamp, report });
3616
+ } catch (error) {
3617
+ console.warn(`Warning: Failed to load report ${file}:`, error);
3618
+ }
3619
+ }
3620
+ return reports;
3621
+ }
3622
+ /**
3623
+ * Load a specific report by date
3624
+ */
3625
+ async loadByDate(date) {
3626
+ const filepath = join3(this.storageDir, `report-${date}.json`);
3627
+ if (!await pathExists(filepath)) {
3628
+ return null;
3629
+ }
3630
+ const content = await readTextFile(filepath);
3631
+ return JSON.parse(content);
3632
+ }
3633
+ /**
3634
+ * Get all available report dates
3635
+ */
3636
+ async getAvailableDates() {
3637
+ if (!await pathExists(this.storageDir)) {
3638
+ return [];
3639
+ }
3640
+ const files = await readFilesInDir(this.storageDir);
3641
+ return files.filter((f) => f.startsWith("report-") && f.endsWith(".json")).map((f) => f.replace("report-", "").replace(".json", "")).sort().reverse();
3642
+ }
3643
+ /**
3644
+ * Clear old reports (keep only the most recent N days)
3645
+ */
3646
+ async cleanup(keepDays = 90) {
3647
+ if (!await pathExists(this.storageDir)) {
3648
+ return 0;
3649
+ }
3650
+ const files = await readFilesInDir(this.storageDir);
3651
+ const reportFiles = files.filter((f) => f.startsWith("report-") && f.endsWith(".json")).sort().reverse();
3652
+ const filesToDelete = reportFiles.slice(keepDays);
3653
+ for (const file of filesToDelete) {
3654
+ try {
3655
+ const filepath = join3(this.storageDir, file);
3656
+ const fs = await import("fs/promises");
3657
+ await fs.unlink(filepath);
3658
+ } catch (error) {
3659
+ console.warn(`Warning: Failed to delete old report ${file}:`, error);
3660
+ }
3661
+ }
3662
+ return filesToDelete.length;
3663
+ }
3664
+ };
3665
+
3666
+ // src/reporting/drift.ts
3667
+ async function detectDrift(current, previous) {
3668
+ const byDecision = [];
3669
+ for (const currDecision of current.byDecision) {
3670
+ const prevDecision = previous.byDecision.find(
3671
+ (d) => d.decisionId === currDecision.decisionId
3672
+ );
3673
+ if (!prevDecision) {
3674
+ byDecision.push({
3675
+ decisionId: currDecision.decisionId,
3676
+ title: currDecision.title,
3677
+ trend: "stable",
3678
+ complianceChange: 0,
3679
+ newViolations: currDecision.violations,
3680
+ fixedViolations: 0,
3681
+ currentCompliance: currDecision.compliance,
3682
+ previousCompliance: currDecision.compliance
3683
+ });
3684
+ continue;
3685
+ }
3686
+ const complianceChange = currDecision.compliance - prevDecision.compliance;
3687
+ const violationDiff = currDecision.violations - prevDecision.violations;
3688
+ let trend;
3689
+ if (complianceChange > 5) {
3690
+ trend = "improving";
3691
+ } else if (complianceChange < -5) {
3692
+ trend = "degrading";
3693
+ } else {
3694
+ trend = "stable";
3695
+ }
3696
+ byDecision.push({
3697
+ decisionId: currDecision.decisionId,
3698
+ title: currDecision.title,
3699
+ trend,
3700
+ complianceChange,
3701
+ newViolations: Math.max(0, violationDiff),
3702
+ fixedViolations: Math.max(0, -violationDiff),
3703
+ currentCompliance: currDecision.compliance,
3704
+ previousCompliance: prevDecision.compliance
3705
+ });
3706
+ }
3707
+ const overallComplianceChange = current.summary.compliance - previous.summary.compliance;
3708
+ let overallTrend;
3709
+ if (overallComplianceChange > 5) {
3710
+ overallTrend = "improving";
3711
+ } else if (overallComplianceChange < -5) {
3712
+ overallTrend = "degrading";
3713
+ } else {
3714
+ overallTrend = "stable";
3715
+ }
3716
+ const newViolations = {
3717
+ critical: Math.max(
3718
+ 0,
3719
+ current.summary.violations.critical - previous.summary.violations.critical
3720
+ ),
3721
+ high: Math.max(0, current.summary.violations.high - previous.summary.violations.high),
3722
+ medium: Math.max(0, current.summary.violations.medium - previous.summary.violations.medium),
3723
+ low: Math.max(0, current.summary.violations.low - previous.summary.violations.low),
3724
+ total: 0
3725
+ };
3726
+ newViolations.total = newViolations.critical + newViolations.high + newViolations.medium + newViolations.low;
3727
+ const fixedViolations = {
3728
+ critical: Math.max(
3729
+ 0,
3730
+ previous.summary.violations.critical - current.summary.violations.critical
3731
+ ),
3732
+ high: Math.max(0, previous.summary.violations.high - current.summary.violations.high),
3733
+ medium: Math.max(0, previous.summary.violations.medium - current.summary.violations.medium),
3734
+ low: Math.max(0, previous.summary.violations.low - current.summary.violations.low),
3735
+ total: 0
3736
+ };
3737
+ fixedViolations.total = fixedViolations.critical + fixedViolations.high + fixedViolations.medium + fixedViolations.low;
3738
+ const improving = byDecision.filter((d) => d.trend === "improving");
3739
+ const degrading = byDecision.filter((d) => d.trend === "degrading");
3740
+ const mostImproved = improving.sort((a, b) => b.complianceChange - a.complianceChange).slice(0, 5);
3741
+ const mostDegraded = degrading.sort((a, b) => a.complianceChange - b.complianceChange).slice(0, 5);
3742
+ return {
3743
+ trend: overallTrend,
3744
+ complianceChange: overallComplianceChange,
3745
+ summary: {
3746
+ newViolations,
3747
+ fixedViolations
3748
+ },
3749
+ byDecision,
3750
+ mostImproved,
3751
+ mostDegraded
3752
+ };
3753
+ }
3754
+ async function analyzeTrend(reports) {
3755
+ if (reports.length === 0) {
3756
+ throw new Error("No reports provided for trend analysis");
3757
+ }
3758
+ const sortedReports = reports.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
3759
+ const firstReport = sortedReports[0]?.report;
3760
+ const lastReport = sortedReports[sortedReports.length - 1]?.report;
3761
+ if (!firstReport || !lastReport) {
3762
+ throw new Error("Invalid reports data");
3763
+ }
3764
+ const overallChange = lastReport.summary.compliance - firstReport.summary.compliance;
3765
+ let overallTrend;
3766
+ if (overallChange > 5) {
3767
+ overallTrend = "improving";
3768
+ } else if (overallChange < -5) {
3769
+ overallTrend = "degrading";
3770
+ } else {
3771
+ overallTrend = "stable";
3772
+ }
3773
+ const overallDataPoints = sortedReports.map((r) => ({
3774
+ date: r.timestamp,
3775
+ compliance: r.report.summary.compliance
3776
+ }));
3777
+ const decisionMap = /* @__PURE__ */ new Map();
3778
+ for (const { report } of sortedReports) {
3779
+ for (const decision of report.byDecision) {
3780
+ if (!decisionMap.has(decision.decisionId)) {
3781
+ decisionMap.set(decision.decisionId, []);
3782
+ }
3783
+ decisionMap.get(decision.decisionId).push(decision);
3784
+ }
3785
+ }
3786
+ const decisions = Array.from(decisionMap.entries()).map(([decisionId, data]) => {
3787
+ const first = data[0];
3788
+ const last = data[data.length - 1];
3789
+ if (!first || !last) {
3790
+ throw new Error(`Invalid decision data for ${decisionId}`);
3791
+ }
3792
+ const change = last.compliance - first.compliance;
3793
+ let trend;
3794
+ if (change > 5) {
3795
+ trend = "improving";
3796
+ } else if (change < -5) {
3797
+ trend = "degrading";
3798
+ } else {
3799
+ trend = "stable";
3800
+ }
3801
+ const dataPoints = sortedReports.map((r) => {
3802
+ const decision = r.report.byDecision.find((d) => d.decisionId === decisionId);
3803
+ return {
3804
+ date: r.timestamp,
3805
+ compliance: decision?.compliance ?? 0
3806
+ };
3807
+ });
3808
+ return {
3809
+ decisionId,
3810
+ title: last.title,
3811
+ startCompliance: first.compliance,
3812
+ endCompliance: last.compliance,
3813
+ change,
3814
+ trend,
3815
+ dataPoints
3816
+ };
3817
+ });
3818
+ const firstTimestamp = sortedReports[0]?.timestamp;
3819
+ const lastTimestamp = sortedReports[sortedReports.length - 1]?.timestamp;
3820
+ if (!firstTimestamp || !lastTimestamp) {
3821
+ throw new Error("Invalid report timestamps");
3822
+ }
3823
+ return {
3824
+ period: {
3825
+ start: firstTimestamp,
3826
+ end: lastTimestamp,
3827
+ days: sortedReports.length
3828
+ },
3829
+ overall: {
3830
+ startCompliance: firstReport.summary.compliance,
3831
+ endCompliance: lastReport.summary.compliance,
3832
+ change: overallChange,
3833
+ trend: overallTrend,
3834
+ dataPoints: overallDataPoints
3835
+ },
3836
+ decisions
3837
+ };
3838
+ }
3839
+
3555
3840
  // src/agent/context.generator.ts
3556
3841
  async function generateContext(filePath, config, options = {}) {
3557
3842
  const { includeRationale = config.agent?.includeRationale ?? true, cwd = process.cwd() } = options;
@@ -4010,6 +4295,7 @@ export {
4010
4295
  RegexVerifier,
4011
4296
  Registry,
4012
4297
  RegistryError,
4298
+ ReportStorage,
4013
4299
  Reporter,
4014
4300
  SecurityVerifier,
4015
4301
  SeveritySchema,
@@ -4022,6 +4308,7 @@ export {
4022
4308
  VerificationError,
4023
4309
  VerificationFrequencySchema,
4024
4310
  VerifierNotFoundError,
4311
+ analyzeTrend,
4025
4312
  buildDependencyGraph2 as buildDependencyGraph,
4026
4313
  builtinAnalyzers,
4027
4314
  builtinVerifiers,
@@ -4035,6 +4322,7 @@ export {
4035
4322
  createVerificationEngine,
4036
4323
  createViolation,
4037
4324
  defaultConfig,
4325
+ detectDrift,
4038
4326
  ensureDir,
4039
4327
  extractSnippet,
4040
4328
  formatConsoleReport,