@ipation/specbridge 2.3.0 → 2.4.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
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { SourceFile, Project } from 'ts-morph';
3
3
  import { Document } from 'yaml';
4
+ import { Logger as Logger$1 } from 'pino';
4
5
  import { Application } from 'express';
5
6
  export { minimatch } from 'minimatch';
6
7
 
@@ -1127,6 +1128,7 @@ declare class VerificationEngine {
1127
1128
  private astCache;
1128
1129
  private resultsCache;
1129
1130
  private pluginsLoaded;
1131
+ private logger;
1130
1132
  constructor(registry?: Registry);
1131
1133
  /**
1132
1134
  * Run verification
@@ -1556,6 +1558,7 @@ interface StoredReport {
1556
1558
  */
1557
1559
  declare class ReportStorage {
1558
1560
  private storageDir;
1561
+ private logger;
1559
1562
  constructor(basePath: string);
1560
1563
  /**
1561
1564
  * Save a compliance report to storage
@@ -1850,6 +1853,10 @@ declare function matchesAnyPattern(filePath: string, patterns: string[], options
1850
1853
  cwd?: string;
1851
1854
  }): boolean;
1852
1855
 
1856
+ type Logger = Pick<Logger$1, 'debug' | 'info' | 'warn' | 'error' | 'child'>;
1857
+ declare function getLogger(bindings?: Record<string, unknown>): Logger;
1858
+ declare const logger: Logger;
1859
+
1853
1860
  /**
1854
1861
  * Analytics engine - Provide insights into compliance trends and decision impact
1855
1862
  */
@@ -1935,6 +1942,7 @@ declare class DashboardServer {
1935
1942
  private cacheTimestamp;
1936
1943
  private readonly CACHE_TTL;
1937
1944
  private refreshInterval;
1945
+ private logger;
1938
1946
  constructor(options: DashboardOptions);
1939
1947
  /**
1940
1948
  * Start the server with background cache refresh
@@ -2006,4 +2014,4 @@ interface GitHubPrCommentOptions {
2006
2014
  declare function formatViolationsForGitHub(violations: Violation[], limit?: number): string;
2007
2015
  declare function postPrComment(violations: Violation[], options: GitHubPrCommentOptions): Promise<void>;
2008
2016
 
2009
- export { type AffectedFile, type AgentContext, AgentContextGenerator, AlreadyInitializedError, AnalyticsEngine, type AnalyticsSummary, type Analyzer, AnalyzerNotFoundError, ApiVerifier, type ApplicableConstraint, type ApplicableDecision, AstCache, AutofixEngine, type AutofixPatch, type AutofixResult, type BuildDependencyGraphOptions, CodeScanner, ComplexityVerifier, type ComplianceReport, ConfigError, type Constraint, type ConstraintCheck, ConstraintCheckSchema, type ConstraintCheckSchema_, type ConstraintException, ConstraintExceptionSchema, type ConstraintExceptionSchema_, ConstraintSchema, type ConstraintSchema_, type ConstraintType, ConstraintTypeSchema, type ConstraintTypeSchema_, type ContextOptions, DashboardServer, type Decision, type DecisionCompliance, type DecisionContent, DecisionContentSchema, type DecisionContentSchema_, type DecisionFilter, type DecisionMetadata, DecisionMetadataSchema, type DecisionMetadataSchema_, type DecisionMetrics, DecisionNotFoundError, DecisionSchema, type DecisionStatus, DecisionStatusSchema, type DecisionStatusSchema_, type DecisionTypeSchema, DecisionValidationError, type DependencyGraph, DependencyVerifier, type DriftAnalysis, ErrorsAnalyzer, ErrorsVerifier, FileSystemError, type GitHubPrCommentOptions, type GlobOptions, type GraphNode, HookError, type ImpactAnalysis, ImportsAnalyzer, ImportsVerifier, InferenceEngine, InferenceError, type InferenceOptions, type InferenceResult, type Insight, 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 ReporterResult, 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 VerificationIssue, type VerificationLevel, type VerificationOptions, type VerificationResult, type VerificationWarning, type Verifier, VerifierNotFoundError, type VerifierPlugin, type VerifierPluginMetadata, type Violation, type ViolationFix, analyzeTrend, buildDependencyGraph, builtinAnalyzers, builtinVerifiers, calculateConfidence, checkDegradation, clearVerifierPool, createDashboardServer, createInferenceEngine, createPattern, createPropagationEngine, createRegistry, createScannerFromConfig, createVerificationEngine, createViolation, defaultConfig, defineVerifierPlugin, detectDrift, ensureDir, extractSnippet, formatConsoleReport, formatContextAsJson, formatContextAsMarkdown, formatContextAsMcp, formatError, formatMarkdownReport, formatValidationErrors, formatViolationsForGitHub, 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, postPrComment, readFilesInDir, readTextFile, runInference, selectVerifierForConstraint, shouldApplyConstraintToFile, startLspServer, stringifyYaml, templates, updateYamlDocument, validateConfig, validateDecision, validateDecisionFile, writeTextFile };
2017
+ export { type AffectedFile, type AgentContext, AgentContextGenerator, AlreadyInitializedError, AnalyticsEngine, type AnalyticsSummary, type Analyzer, AnalyzerNotFoundError, ApiVerifier, type ApplicableConstraint, type ApplicableDecision, AstCache, AutofixEngine, type AutofixPatch, type AutofixResult, type BuildDependencyGraphOptions, CodeScanner, ComplexityVerifier, type ComplianceReport, ConfigError, type Constraint, type ConstraintCheck, ConstraintCheckSchema, type ConstraintCheckSchema_, type ConstraintException, ConstraintExceptionSchema, type ConstraintExceptionSchema_, ConstraintSchema, type ConstraintSchema_, type ConstraintType, ConstraintTypeSchema, type ConstraintTypeSchema_, type ContextOptions, DashboardServer, type Decision, type DecisionCompliance, type DecisionContent, DecisionContentSchema, type DecisionContentSchema_, type DecisionFilter, type DecisionMetadata, DecisionMetadataSchema, type DecisionMetadataSchema_, type DecisionMetrics, DecisionNotFoundError, DecisionSchema, type DecisionStatus, DecisionStatusSchema, type DecisionStatusSchema_, type DecisionTypeSchema, DecisionValidationError, type DependencyGraph, DependencyVerifier, type DriftAnalysis, ErrorsAnalyzer, ErrorsVerifier, FileSystemError, type GitHubPrCommentOptions, type GlobOptions, type GraphNode, HookError, type ImpactAnalysis, ImportsAnalyzer, ImportsVerifier, InferenceEngine, InferenceError, type InferenceOptions, type InferenceResult, type Insight, type LevelConfig, LinksSchema, type LoadError, type LoadResult, type LoadedDecision, type Logger, 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 ReporterResult, 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 VerificationIssue, type VerificationLevel, type VerificationOptions, type VerificationResult, type VerificationWarning, type Verifier, VerifierNotFoundError, type VerifierPlugin, type VerifierPluginMetadata, type Violation, type ViolationFix, analyzeTrend, buildDependencyGraph, builtinAnalyzers, builtinVerifiers, calculateConfidence, checkDegradation, clearVerifierPool, createDashboardServer, createInferenceEngine, createPattern, createPropagationEngine, createRegistry, createScannerFromConfig, createVerificationEngine, createViolation, defaultConfig, defineVerifierPlugin, detectDrift, ensureDir, extractSnippet, formatConsoleReport, formatContextAsJson, formatContextAsMarkdown, formatContextAsMcp, formatError, formatMarkdownReport, formatValidationErrors, formatViolationsForGitHub, generateContext, generateFormattedContext, generateReport, getAffectedFiles, getAffectingDecisions, getAnalyzer, getAnalyzerIds, getChangedFiles, getConfigPath, getDecisionsDir, getInferredDir, getLogger, getReportsDir, getSpecBridgeDir, getTransitiveDependencies, getVerifier, getVerifierIds, getVerifiersDir, glob, isConstraintExcepted, isDirectory, loadConfig, loadDecisionFile, loadDecisionsFromDir, logger, matchesAnyPattern, matchesPattern, mergeWithDefaults, normalizePath, parseYaml, parseYamlDocument, pathExists, postPrComment, readFilesInDir, readTextFile, runInference, selectVerifierForConstraint, shouldApplyConstraintToFile, startLspServer, stringifyYaml, templates, updateYamlDocument, validateConfig, validateDecision, validateDecisionFile, writeTextFile };
package/dist/index.js CHANGED
@@ -1582,7 +1582,6 @@ async function runInference(config, options) {
1582
1582
 
1583
1583
  // src/verification/engine.ts
1584
1584
  import { Project as Project2 } from "ts-morph";
1585
- import chalk from "chalk";
1586
1585
 
1587
1586
  // src/verification/verifiers/base.ts
1588
1587
  function defineVerifierPlugin(plugin) {
@@ -2150,7 +2149,8 @@ var DependencyVerifier = class {
2150
2149
  const sccs = tarjanScc(graph);
2151
2150
  const current = projectFilePath;
2152
2151
  for (const scc of sccs) {
2153
- const hasSelfLoop = scc.length === 1 && (graph.get(scc[0])?.has(scc[0]) ?? false);
2152
+ const first = scc[0];
2153
+ const hasSelfLoop = first !== void 0 && scc.length === 1 && (graph.get(first)?.has(first) ?? false);
2154
2154
  const isCycle = scc.length > 1 || hasSelfLoop;
2155
2155
  if (!isCycle) continue;
2156
2156
  if (!scc.includes(current)) continue;
@@ -2591,10 +2591,36 @@ import { existsSync } from "fs";
2591
2591
  import { join as join3 } from "path";
2592
2592
  import { pathToFileURL } from "url";
2593
2593
  import fg2 from "fast-glob";
2594
+
2595
+ // src/utils/logger.ts
2596
+ import pino from "pino";
2597
+ var defaultOptions = {
2598
+ level: process.env.SPECBRIDGE_LOG_LEVEL || "info",
2599
+ timestamp: pino.stdTimeFunctions.isoTime,
2600
+ base: {
2601
+ service: "specbridge"
2602
+ }
2603
+ };
2604
+ var destination = pino.destination({
2605
+ fd: 2,
2606
+ // stderr
2607
+ sync: false
2608
+ });
2609
+ var rootLogger = pino(defaultOptions, destination);
2610
+ function getLogger(bindings) {
2611
+ if (!bindings) {
2612
+ return rootLogger;
2613
+ }
2614
+ return rootLogger.child(bindings);
2615
+ }
2616
+ var logger = getLogger();
2617
+
2618
+ // src/verification/plugins/loader.ts
2594
2619
  var PluginLoader = class {
2595
2620
  plugins = /* @__PURE__ */ new Map();
2596
2621
  loaded = false;
2597
2622
  loadErrors = [];
2623
+ logger = getLogger({ module: "verification.plugins.loader" });
2598
2624
  /**
2599
2625
  * Load all plugins from the specified base path
2600
2626
  *
@@ -2617,15 +2643,15 @@ var PluginLoader = class {
2617
2643
  } catch (error) {
2618
2644
  const message = error instanceof Error ? error.message : String(error);
2619
2645
  this.loadErrors.push({ file, error: message });
2620
- console.warn(`Failed to load plugin from ${file}: ${message}`);
2646
+ this.logger.warn({ file, error: message }, "Failed to load plugin");
2621
2647
  }
2622
2648
  }
2623
2649
  this.loaded = true;
2624
2650
  if (this.plugins.size > 0) {
2625
- console.error(`Loaded ${this.plugins.size} custom verifier(s)`);
2651
+ this.logger.info({ count: this.plugins.size }, "Loaded custom verifier plugins");
2626
2652
  }
2627
2653
  if (this.loadErrors.length > 0) {
2628
- console.warn(`Failed to load ${this.loadErrors.length} plugin(s)`);
2654
+ this.logger.warn({ count: this.loadErrors.length }, "Plugin load failures");
2629
2655
  }
2630
2656
  }
2631
2657
  /**
@@ -2801,8 +2827,9 @@ var builtinVerifiers = {
2801
2827
  };
2802
2828
  var verifierInstances = /* @__PURE__ */ new Map();
2803
2829
  function getVerifier(id) {
2804
- if (verifierInstances.has(id)) {
2805
- return verifierInstances.get(id);
2830
+ const pooled = verifierInstances.get(id);
2831
+ if (pooled) {
2832
+ return pooled;
2806
2833
  }
2807
2834
  const pluginLoader2 = getPluginLoader();
2808
2835
  const customVerifier = pluginLoader2.getVerifier(id);
@@ -3007,6 +3034,7 @@ var VerificationEngine = class {
3007
3034
  astCache;
3008
3035
  resultsCache;
3009
3036
  pluginsLoaded = false;
3037
+ logger = getLogger({ module: "verification.engine" });
3010
3038
  constructor(registry) {
3011
3039
  this.registry = registry || createRegistry();
3012
3040
  this.project = new Project2({
@@ -3180,13 +3208,12 @@ var VerificationEngine = class {
3180
3208
  );
3181
3209
  if (!verifier) {
3182
3210
  const requestedVerifier = constraint.check?.verifier || constraint.verifier || "auto-detected";
3183
- console.warn(
3184
- chalk.yellow(
3185
- `Warning: No verifier found for ${decision.metadata.id}/${constraint.id}
3186
- Requested: ${requestedVerifier}
3187
- Available: ${getVerifierIds().join(", ")}`
3188
- )
3189
- );
3211
+ this.logger.warn({
3212
+ decisionId: decision.metadata.id,
3213
+ constraintId: constraint.id,
3214
+ requestedVerifier,
3215
+ availableVerifiers: getVerifierIds()
3216
+ }, "No verifier found for constraint");
3190
3217
  warnings.push({
3191
3218
  type: "missing_verifier",
3192
3219
  message: `No verifier found for constraint (requested: ${requestedVerifier})`,
@@ -3292,17 +3319,14 @@ var VerificationEngine = class {
3292
3319
  } catch (error) {
3293
3320
  const errorMessage = error instanceof Error ? error.message : String(error);
3294
3321
  const errorStack = error instanceof Error ? error.stack : void 0;
3295
- console.error(
3296
- chalk.red(
3297
- `Error: Verifier '${verifier.id}' failed
3298
- File: ${filePath}
3299
- Decision: ${decision.metadata.id}/${constraint.id}
3300
- Error: ${errorMessage}`
3301
- )
3302
- );
3303
- if (errorStack) {
3304
- console.error(chalk.dim(errorStack));
3305
- }
3322
+ this.logger.error({
3323
+ verifierId: verifier.id,
3324
+ filePath,
3325
+ decisionId: decision.metadata.id,
3326
+ constraintId: constraint.id,
3327
+ error: errorMessage,
3328
+ stack: errorStack
3329
+ }, "Verifier execution failed");
3306
3330
  errors.push({
3307
3331
  type: "verifier_exception",
3308
3332
  message: `Verifier '${verifier.id}' failed: ${errorMessage}`,
@@ -3438,6 +3462,10 @@ var AutofixEngine = class {
3438
3462
  const edits = [];
3439
3463
  for (const violation of fileViolations) {
3440
3464
  const fix = violation.autofix;
3465
+ if (!fix) {
3466
+ skippedViolations++;
3467
+ continue;
3468
+ }
3441
3469
  if (options.interactive) {
3442
3470
  const ok = await confirmFix(`Apply fix: ${fix.description} (${filePath}:${violation.line ?? 1})?`);
3443
3471
  if (!ok) {
@@ -3566,7 +3594,24 @@ var PropagationEngine = class {
3566
3594
  if (!this.graph) {
3567
3595
  await this.initialize(config, options);
3568
3596
  }
3569
- const affectedFilePaths = getAffectedFiles(this.graph, decisionId);
3597
+ const graph = this.graph;
3598
+ if (!graph) {
3599
+ return {
3600
+ decision: decisionId,
3601
+ change,
3602
+ affectedFiles: [],
3603
+ estimatedEffort: "low",
3604
+ migrationSteps: [
3605
+ {
3606
+ order: 1,
3607
+ description: "Run verification to confirm all violations resolved",
3608
+ files: [],
3609
+ automated: true
3610
+ }
3611
+ ]
3612
+ };
3613
+ }
3614
+ const affectedFilePaths = getAffectedFiles(graph, decisionId);
3570
3615
  const verificationEngine = createVerificationEngine(this.registry);
3571
3616
  const result = await verificationEngine.verify(config, {
3572
3617
  files: affectedFilePaths,
@@ -3944,54 +3989,54 @@ var Reporter = class {
3944
3989
  };
3945
3990
 
3946
3991
  // src/reporting/formats/console.ts
3947
- import chalk2 from "chalk";
3992
+ import chalk from "chalk";
3948
3993
  import { table } from "table";
3949
3994
  function formatConsoleReport(report) {
3950
3995
  const lines = [];
3951
3996
  lines.push("");
3952
- lines.push(chalk2.bold.blue("SpecBridge Compliance Report"));
3953
- lines.push(chalk2.dim(`Generated: ${new Date(report.timestamp).toLocaleString()}`));
3954
- lines.push(chalk2.dim(`Project: ${report.project}`));
3997
+ lines.push(chalk.bold.blue("SpecBridge Compliance Report"));
3998
+ lines.push(chalk.dim(`Generated: ${new Date(report.timestamp).toLocaleString()}`));
3999
+ lines.push(chalk.dim(`Project: ${report.project}`));
3955
4000
  lines.push("");
3956
4001
  const complianceColor = getComplianceColor(report.summary.compliance);
3957
- lines.push(chalk2.bold("Overall Compliance"));
4002
+ lines.push(chalk.bold("Overall Compliance"));
3958
4003
  lines.push(` ${complianceColor(formatComplianceBar(report.summary.compliance))} ${complianceColor(`${report.summary.compliance}%`)}`);
3959
4004
  lines.push("");
3960
- lines.push(chalk2.bold("Summary"));
4005
+ lines.push(chalk.bold("Summary"));
3961
4006
  lines.push(` Decisions: ${report.summary.activeDecisions} active / ${report.summary.totalDecisions} total`);
3962
4007
  lines.push(` Constraints: ${report.summary.totalConstraints}`);
3963
4008
  lines.push("");
3964
- lines.push(chalk2.bold("Violations"));
4009
+ lines.push(chalk.bold("Violations"));
3965
4010
  const { violations } = report.summary;
3966
4011
  const violationParts = [];
3967
4012
  if (violations.critical > 0) {
3968
- violationParts.push(chalk2.red(`${violations.critical} critical`));
4013
+ violationParts.push(chalk.red(`${violations.critical} critical`));
3969
4014
  }
3970
4015
  if (violations.high > 0) {
3971
- violationParts.push(chalk2.yellow(`${violations.high} high`));
4016
+ violationParts.push(chalk.yellow(`${violations.high} high`));
3972
4017
  }
3973
4018
  if (violations.medium > 0) {
3974
- violationParts.push(chalk2.cyan(`${violations.medium} medium`));
4019
+ violationParts.push(chalk.cyan(`${violations.medium} medium`));
3975
4020
  }
3976
4021
  if (violations.low > 0) {
3977
- violationParts.push(chalk2.dim(`${violations.low} low`));
4022
+ violationParts.push(chalk.dim(`${violations.low} low`));
3978
4023
  }
3979
4024
  if (violationParts.length > 0) {
3980
4025
  lines.push(` ${violationParts.join(" | ")}`);
3981
4026
  } else {
3982
- lines.push(chalk2.green(" No violations"));
4027
+ lines.push(chalk.green(" No violations"));
3983
4028
  }
3984
4029
  lines.push("");
3985
4030
  if (report.byDecision.length > 0) {
3986
- lines.push(chalk2.bold("By Decision"));
4031
+ lines.push(chalk.bold("By Decision"));
3987
4032
  lines.push("");
3988
4033
  const tableData = [
3989
4034
  [
3990
- chalk2.bold("Decision"),
3991
- chalk2.bold("Status"),
3992
- chalk2.bold("Constraints"),
3993
- chalk2.bold("Violations"),
3994
- chalk2.bold("Compliance")
4035
+ chalk.bold("Decision"),
4036
+ chalk.bold("Status"),
4037
+ chalk.bold("Constraints"),
4038
+ chalk.bold("Violations"),
4039
+ chalk.bold("Compliance")
3995
4040
  ]
3996
4041
  ];
3997
4042
  for (const dec of report.byDecision) {
@@ -4001,7 +4046,7 @@ function formatConsoleReport(report) {
4001
4046
  truncate(dec.title, 40),
4002
4047
  statusColor(dec.status),
4003
4048
  String(dec.constraints),
4004
- dec.violations > 0 ? chalk2.red(String(dec.violations)) : chalk2.green("0"),
4049
+ dec.violations > 0 ? chalk.red(String(dec.violations)) : chalk.green("0"),
4005
4050
  compColor(`${dec.compliance}%`)
4006
4051
  ]);
4007
4052
  }
@@ -4035,23 +4080,23 @@ function formatComplianceBar(compliance) {
4035
4080
  return "\u2588".repeat(filled) + "\u2591".repeat(empty);
4036
4081
  }
4037
4082
  function getComplianceColor(compliance) {
4038
- if (compliance >= 90) return chalk2.green;
4039
- if (compliance >= 70) return chalk2.yellow;
4040
- if (compliance >= 50) return chalk2.hex("#FFA500");
4041
- return chalk2.red;
4083
+ if (compliance >= 90) return chalk.green;
4084
+ if (compliance >= 70) return chalk.yellow;
4085
+ if (compliance >= 50) return chalk.hex("#FFA500");
4086
+ return chalk.red;
4042
4087
  }
4043
4088
  function getStatusColor(status) {
4044
4089
  switch (status) {
4045
4090
  case "active":
4046
- return chalk2.green;
4091
+ return chalk.green;
4047
4092
  case "draft":
4048
- return chalk2.yellow;
4093
+ return chalk.yellow;
4049
4094
  case "deprecated":
4050
- return chalk2.gray;
4095
+ return chalk.gray;
4051
4096
  case "superseded":
4052
- return chalk2.blue;
4097
+ return chalk.blue;
4053
4098
  default:
4054
- return chalk2.white;
4099
+ return chalk.white;
4055
4100
  }
4056
4101
  }
4057
4102
  function truncate(str, length) {
@@ -4125,6 +4170,7 @@ function formatProgressBar(percentage) {
4125
4170
  import { join as join4 } from "path";
4126
4171
  var ReportStorage = class {
4127
4172
  storageDir;
4173
+ logger = getLogger({ module: "reporting.storage" });
4128
4174
  constructor(basePath) {
4129
4175
  this.storageDir = join4(getSpecBridgeDir(basePath), "reports", "history");
4130
4176
  }
@@ -4183,7 +4229,7 @@ var ReportStorage = class {
4183
4229
  const timestamp = file.replace("report-", "").replace(".json", "");
4184
4230
  return { timestamp, report };
4185
4231
  } catch (error) {
4186
- console.warn(`Warning: Failed to load report ${file}:`, error);
4232
+ this.logger.warn({ file, error }, "Failed to load report file");
4187
4233
  return null;
4188
4234
  }
4189
4235
  });
@@ -4227,7 +4273,7 @@ var ReportStorage = class {
4227
4273
  const fs = await import("fs/promises");
4228
4274
  await fs.unlink(filepath);
4229
4275
  } catch (error) {
4230
- console.warn(`Warning: Failed to delete old report ${file}:`, error);
4276
+ this.logger.warn({ file, error }, "Failed to delete old report file");
4231
4277
  }
4232
4278
  }
4233
4279
  return filesToDelete.length;
@@ -5064,6 +5110,7 @@ var DashboardServer = class {
5064
5110
  CACHE_TTL = 6e4;
5065
5111
  // 1 minute
5066
5112
  refreshInterval = null;
5113
+ logger = getLogger({ module: "dashboard.server" });
5067
5114
  constructor(options) {
5068
5115
  this.cwd = options.cwd;
5069
5116
  this.config = options.config;
@@ -5080,7 +5127,11 @@ var DashboardServer = class {
5080
5127
  await this.registry.load();
5081
5128
  await this.refreshCache();
5082
5129
  this.refreshInterval = setInterval(
5083
- () => this.refreshCache().catch(console.error),
5130
+ () => {
5131
+ void this.refreshCache().catch((error) => {
5132
+ this.logger.error({ error }, "Background cache refresh failed");
5133
+ });
5134
+ },
5084
5135
  this.CACHE_TTL
5085
5136
  );
5086
5137
  }
@@ -5103,7 +5154,7 @@ var DashboardServer = class {
5103
5154
  this.cacheTimestamp = Date.now();
5104
5155
  await this.reportStorage.save(report);
5105
5156
  } catch (error) {
5106
- console.error("Cache refresh failed:", error);
5157
+ this.logger.error({ error }, "Cache refresh failed");
5107
5158
  if (!this.cachedReport) {
5108
5159
  try {
5109
5160
  const stored = await this.reportStorage.loadLatest();
@@ -5111,7 +5162,7 @@ var DashboardServer = class {
5111
5162
  this.cachedReport = stored.report;
5112
5163
  }
5113
5164
  } catch (fallbackError) {
5114
- console.error("Failed to load fallback report:", fallbackError);
5165
+ this.logger.error({ error: fallbackError }, "Failed to load fallback report");
5115
5166
  }
5116
5167
  }
5117
5168
  }
@@ -5374,7 +5425,7 @@ import { TextDocument } from "vscode-languageserver-textdocument";
5374
5425
  import { fileURLToPath as fileURLToPath2 } from "url";
5375
5426
  import path3 from "path";
5376
5427
  import { Project as Project3 } from "ts-morph";
5377
- import chalk3 from "chalk";
5428
+ import chalk2 from "chalk";
5378
5429
  function severityToDiagnostic(severity) {
5379
5430
  switch (severity) {
5380
5431
  case "critical":
@@ -5485,7 +5536,7 @@ var SpecBridgeLspServer = class {
5485
5536
  if (!await pathExists(getSpecBridgeDir(this.cwd))) {
5486
5537
  const err = new NotInitializedError();
5487
5538
  this.initError = err.message;
5488
- if (this.options.verbose) this.connection.console.error(chalk3.red(this.initError));
5539
+ if (this.options.verbose) this.connection.console.error(chalk2.red(this.initError));
5489
5540
  return;
5490
5541
  }
5491
5542
  try {
@@ -5494,7 +5545,7 @@ var SpecBridgeLspServer = class {
5494
5545
  await getPluginLoader().loadPlugins(this.cwd);
5495
5546
  } catch (error) {
5496
5547
  const msg = error instanceof Error ? error.message : String(error);
5497
- if (this.options.verbose) this.connection.console.error(chalk3.red(`Plugin load failed: ${msg}`));
5548
+ if (this.options.verbose) this.connection.console.error(chalk2.red(`Plugin load failed: ${msg}`));
5498
5549
  }
5499
5550
  this.registry = createRegistry({ basePath: this.cwd });
5500
5551
  await this.registry.load();
@@ -5507,11 +5558,11 @@ var SpecBridgeLspServer = class {
5507
5558
  }
5508
5559
  }
5509
5560
  if (this.options.verbose) {
5510
- this.connection.console.log(chalk3.dim(`Loaded ${this.decisions.length} active decision(s)`));
5561
+ this.connection.console.log(chalk2.dim(`Loaded ${this.decisions.length} active decision(s)`));
5511
5562
  }
5512
5563
  } catch (error) {
5513
5564
  this.initError = error instanceof Error ? error.message : String(error);
5514
- if (this.options.verbose) this.connection.console.error(chalk3.red(this.initError));
5565
+ if (this.options.verbose) this.connection.console.error(chalk2.red(this.initError));
5515
5566
  }
5516
5567
  }
5517
5568
  async verifyTextDocument(doc) {
@@ -5579,6 +5630,9 @@ async function startLspServer(options) {
5579
5630
  // src/integrations/github.ts
5580
5631
  function toMdTable(rows) {
5581
5632
  const header = rows[0];
5633
+ if (!header) {
5634
+ return "";
5635
+ }
5582
5636
  const body = rows.slice(1);
5583
5637
  const sep = header.map(() => "---");
5584
5638
  const lines = [
@@ -5722,6 +5776,7 @@ export {
5722
5776
  getConfigPath,
5723
5777
  getDecisionsDir,
5724
5778
  getInferredDir,
5779
+ getLogger,
5725
5780
  getReportsDir,
5726
5781
  getSpecBridgeDir,
5727
5782
  getTransitiveDependencies,
@@ -5734,6 +5789,7 @@ export {
5734
5789
  loadConfig,
5735
5790
  loadDecisionFile,
5736
5791
  loadDecisionsFromDir,
5792
+ logger,
5737
5793
  matchesAnyPattern,
5738
5794
  matchesPattern,
5739
5795
  mergeWithDefaults,