ctxo-mcp 0.6.3 → 0.6.5

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/README.md CHANGED
@@ -173,6 +173,8 @@ npx ctxo-mcp stats
173
173
 
174
174
  **Privacy Masking** AWS keys, GCP service accounts, Azure connection strings, JWTs, private IPs, env secrets automatically redacted. Extensible via `.ctxo/masking.json`.
175
175
 
176
+ **Interactive Graph** `ctxo visualize` generates a self-contained HTML with vis.js dependency graph. ForceAtlas2 layout, PageRank-based sizing, layer coloring, blast radius on click, git history per symbol, dead code and anti-pattern filters, dark/light theme.
177
+
176
178
  **Debug Mode** `DEBUG=ctxo:*` for all debug output, or `DEBUG=ctxo:git,ctxo:storage` for specific namespaces.
177
179
 
178
180
  **Per-tool savings vs manual approach:**
@@ -580,6 +580,92 @@ var SessionRecorderAdapter = class {
580
580
  }
581
581
  };
582
582
 
583
+ // src/core/graph/symbol-graph.ts
584
+ var SymbolGraph = class {
585
+ nodes = /* @__PURE__ */ new Map();
586
+ nodesByFileAndName = /* @__PURE__ */ new Map();
587
+ forwardEdges = /* @__PURE__ */ new Map();
588
+ reverseEdges = /* @__PURE__ */ new Map();
589
+ edgeSet = /* @__PURE__ */ new Set();
590
+ addNode(node) {
591
+ this.nodes.set(node.symbolId, node);
592
+ const [p0, p1] = node.symbolId.split("::");
593
+ if (p0 !== void 0 && p1 !== void 0) {
594
+ this.nodesByFileAndName.set(`${p0}::${p1}`, node);
595
+ }
596
+ }
597
+ addEdge(edge) {
598
+ const resolvedFrom = this.resolveNodeId(edge.from);
599
+ const resolvedTo = this.resolveNodeId(edge.to);
600
+ const resolvedEdge = { from: resolvedFrom, to: resolvedTo, kind: edge.kind };
601
+ const edgeKey = `${resolvedEdge.from}|${resolvedEdge.to}|${resolvedEdge.kind}`;
602
+ if (this.edgeSet.has(edgeKey)) return;
603
+ this.edgeSet.add(edgeKey);
604
+ const forward = this.forwardEdges.get(resolvedEdge.from) ?? [];
605
+ forward.push(resolvedEdge);
606
+ this.forwardEdges.set(resolvedEdge.from, forward);
607
+ const reverse = this.reverseEdges.get(resolvedEdge.to) ?? [];
608
+ reverse.push(resolvedEdge);
609
+ this.reverseEdges.set(resolvedEdge.to, reverse);
610
+ }
611
+ getNode(symbolId) {
612
+ return this.nodes.get(symbolId) ?? this.resolveNodeFuzzy(symbolId);
613
+ }
614
+ resolveNodeId(id) {
615
+ if (this.nodes.has(id)) return id;
616
+ const [p0, p1] = id.split("::");
617
+ if (p0 !== void 0 && p1 !== void 0) {
618
+ const fuzzyKey = `${p0}::${p1}`;
619
+ const match = this.nodesByFileAndName.get(fuzzyKey);
620
+ if (match) return match.symbolId;
621
+ const jsToTs = p0.replace(/\.js$/, ".ts");
622
+ if (jsToTs !== p0) {
623
+ const altKey = `${jsToTs}::${p1}`;
624
+ const altMatch = this.nodesByFileAndName.get(altKey);
625
+ if (altMatch) return altMatch.symbolId;
626
+ }
627
+ }
628
+ return id;
629
+ }
630
+ resolveNodeFuzzy(id) {
631
+ const [p0, p1] = id.split("::");
632
+ if (p0 !== void 0 && p1 !== void 0) {
633
+ const match = this.nodesByFileAndName.get(`${p0}::${p1}`);
634
+ if (match) return match;
635
+ const jsToTs = p0.replace(/\.js$/, ".ts");
636
+ if (jsToTs !== p0) {
637
+ return this.nodesByFileAndName.get(`${jsToTs}::${p1}`);
638
+ }
639
+ }
640
+ return void 0;
641
+ }
642
+ getForwardEdges(symbolId) {
643
+ return this.forwardEdges.get(symbolId) ?? [];
644
+ }
645
+ getReverseEdges(symbolId) {
646
+ return this.reverseEdges.get(symbolId) ?? [];
647
+ }
648
+ hasNode(symbolId) {
649
+ return this.nodes.has(symbolId) || this.resolveNodeFuzzy(symbolId) !== void 0;
650
+ }
651
+ get nodeCount() {
652
+ return this.nodes.size;
653
+ }
654
+ get edgeCount() {
655
+ return this.edgeSet.size;
656
+ }
657
+ allNodes() {
658
+ return [...this.nodes.values()];
659
+ }
660
+ allEdges() {
661
+ const edges = [];
662
+ for (const list of this.forwardEdges.values()) {
663
+ edges.push(...list);
664
+ }
665
+ return edges;
666
+ }
667
+ };
668
+
583
669
  // src/core/why-context/revert-detector.ts
584
670
  var REVERT_QUOTED_PATTERN = /^Revert "(.+)"$/;
585
671
  var REVERT_PREFIX_PATTERN = /^revert:\s*(.+)$/i;
@@ -619,6 +705,345 @@ var RevertDetector = class {
619
705
  }
620
706
  };
621
707
 
708
+ // src/core/overlay/architectural-overlay.ts
709
+ var DEFAULT_RULES = [
710
+ // Test layer (matched first — __tests__, .test.ts, tests/, fixtures)
711
+ { pattern: /__tests__/, layer: "Test" },
712
+ { pattern: /\.test\.ts$/, layer: "Test" },
713
+ { pattern: /\btests\b/, layer: "Test" },
714
+ { pattern: /\bfixtures?\b/, layer: "Test" },
715
+ // Composition root
716
+ { pattern: /src\/index\.ts$/, layer: "Composition" },
717
+ // Domain
718
+ { pattern: /\bcore\b/, layer: "Domain" },
719
+ { pattern: /\bports?\b/, layer: "Domain" },
720
+ // Adapter
721
+ { pattern: /\badapters?\b/, layer: "Adapter" },
722
+ { pattern: /\bcli\b/, layer: "Adapter" },
723
+ // Infrastructure
724
+ { pattern: /\binfra\b/, layer: "Infrastructure" },
725
+ { pattern: /\bdb\b/, layer: "Infrastructure" },
726
+ { pattern: /\bqueue\b/, layer: "Infrastructure" },
727
+ // Config files
728
+ { pattern: /\.(config|rc)\.(ts|js|json)$/, layer: "Configuration" }
729
+ ];
730
+ var ArchitecturalOverlay = class {
731
+ rules;
732
+ constructor(customRules) {
733
+ this.rules = (customRules ?? DEFAULT_RULES).map(({ pattern, layer }) => ({
734
+ pattern: new RegExp(pattern.source, pattern.flags),
735
+ layer
736
+ }));
737
+ }
738
+ classify(filePaths) {
739
+ const layers = {};
740
+ for (const filePath of filePaths) {
741
+ const layer = this.matchLayer(filePath);
742
+ const list = layers[layer] ?? [];
743
+ list.push(filePath);
744
+ layers[layer] = list;
745
+ }
746
+ return { layers };
747
+ }
748
+ matchLayer(filePath) {
749
+ for (const rule of this.rules) {
750
+ if (rule.pattern.test(filePath)) {
751
+ return rule.layer;
752
+ }
753
+ }
754
+ return "Unknown";
755
+ }
756
+ };
757
+
758
+ // src/core/dead-code/dead-code-detector.ts
759
+ var TEST_PATTERNS = [/__tests__/, /\.test\.ts$/, /\.spec\.ts$/, /\btests\//, /\bfixtures?\//];
760
+ var CONFIG_PATTERNS = [/\.(config|rc)\.(ts|js|json)$/, /tsconfig/, /eslint/, /\.d\.ts$/];
761
+ var FRAMEWORK_PATTERNS = [
762
+ // Vitest
763
+ /^(describe|it|expect|beforeAll|beforeEach|afterAll|afterEach|vi)$/,
764
+ // MCP SDK
765
+ /^(registerTool|registerPrompt|connect|close)$/,
766
+ // Zod schemas (conventionally exported for validation)
767
+ /Schema$/,
768
+ // Node.js lifecycle
769
+ /^main$/
770
+ ];
771
+ var SCAFFOLDING_PATTERNS = [
772
+ { regex: /\bTODO\b/i, pattern: "TODO" },
773
+ { regex: /\bFIXME\b/i, pattern: "FIXME" },
774
+ { regex: /\bHACK\b/i, pattern: "HACK" },
775
+ { regex: /\bPLACEHOLDER\b/i, pattern: "PLACEHOLDER" },
776
+ { regex: /\bXXX\b/, pattern: "XXX" },
777
+ { regex: /Phase\s+\d|Step\s+\d/i, pattern: "PHASE/STEP" },
778
+ { regex: /not\s+(?:yet\s+)?implement/i, pattern: "NOT_IMPLEMENTED" },
779
+ { regex: /temporary|temp\s+fix/i, pattern: "TEMPORARY" }
780
+ ];
781
+ var DeadCodeDetector = class {
782
+ detect(graph, options = {}) {
783
+ const allNodes = graph.allNodes();
784
+ const candidates = options.includeTests ? allNodes : allNodes.filter((n) => {
785
+ const file = n.symbolId.split("::")[0] ?? "";
786
+ return !this.matchesAny(file, TEST_PATTERNS) && !this.matchesAny(file, CONFIG_PATTERNS);
787
+ });
788
+ if (candidates.length === 0) {
789
+ return { totalSymbols: 0, reachableSymbols: 0, deadSymbols: [], unusedExports: [], deadFiles: [], scaffolding: this.detectScaffolding(options.sourceContents), deadCodePercentage: 0 };
790
+ }
791
+ const candidateIds = new Set(candidates.map((n) => n.symbolId));
792
+ const entryPoints = /* @__PURE__ */ new Set();
793
+ for (const node of candidates) {
794
+ if (this.isFrameworkSymbol(node.name)) {
795
+ entryPoints.add(node.symbolId);
796
+ continue;
797
+ }
798
+ const reverseEdges = graph.getReverseEdges(node.symbolId);
799
+ const forwardEdges = graph.getForwardEdges(node.symbolId);
800
+ const incomingFromCandidates = reverseEdges.filter((e) => candidateIds.has(e.from) && e.from !== node.symbolId);
801
+ if (incomingFromCandidates.length === 0) {
802
+ const hasOutgoing = forwardEdges.some((e) => candidateIds.has(e.to));
803
+ if (hasOutgoing || reverseEdges.length > 0) {
804
+ entryPoints.add(node.symbolId);
805
+ }
806
+ }
807
+ }
808
+ const reachable = /* @__PURE__ */ new Set();
809
+ const queue = [...entryPoints];
810
+ while (queue.length > 0) {
811
+ const current = queue.shift();
812
+ if (reachable.has(current)) continue;
813
+ reachable.add(current);
814
+ for (const edge of graph.getForwardEdges(current)) {
815
+ if (!reachable.has(edge.to) && candidateIds.has(edge.to)) {
816
+ queue.push(edge.to);
817
+ }
818
+ }
819
+ }
820
+ const deadIds = /* @__PURE__ */ new Set();
821
+ for (const node of candidates) {
822
+ if (!reachable.has(node.symbolId)) {
823
+ deadIds.add(node.symbolId);
824
+ }
825
+ }
826
+ const cascadeDepthMap = this.computeCascadeDepths(graph, deadIds, candidateIds);
827
+ const deadSymbols = [];
828
+ for (const node of candidates) {
829
+ if (!deadIds.has(node.symbolId)) continue;
830
+ const file = node.symbolId.split("::")[0] ?? "";
831
+ const confidence = this.computeConfidence(graph, node.symbolId, deadIds);
832
+ const cascadeDepth = cascadeDepthMap.get(node.symbolId);
833
+ deadSymbols.push({
834
+ symbolId: node.symbolId,
835
+ file,
836
+ name: node.name,
837
+ kind: node.kind,
838
+ confidence,
839
+ reason: this.describeReason(confidence),
840
+ ...cascadeDepth !== void 0 && cascadeDepth > 0 ? { cascadeDepth } : {}
841
+ });
842
+ }
843
+ const fileStats = /* @__PURE__ */ new Map();
844
+ for (const node of candidates) {
845
+ const file = node.symbolId.split("::")[0] ?? "";
846
+ const stats = fileStats.get(file) ?? { total: 0, dead: 0 };
847
+ stats.total++;
848
+ if (deadIds.has(node.symbolId)) stats.dead++;
849
+ fileStats.set(file, stats);
850
+ }
851
+ const deadFiles = [...fileStats.entries()].filter(([, s]) => s.total > 0 && s.dead === s.total).map(([file]) => file);
852
+ const unusedExports = [];
853
+ for (const node of candidates) {
854
+ const reverseEdges = graph.getReverseEdges(node.symbolId);
855
+ const externalImporters = reverseEdges.filter(
856
+ (e) => candidateIds.has(e.from) && e.from !== node.symbolId
857
+ );
858
+ if (externalImporters.length === 0 && !deadIds.has(node.symbolId)) {
859
+ unusedExports.push({
860
+ symbolId: node.symbolId,
861
+ file: node.symbolId.split("::")[0] ?? "",
862
+ name: node.name,
863
+ kind: node.kind
864
+ });
865
+ }
866
+ }
867
+ const scaffolding = this.detectScaffolding(options.sourceContents);
868
+ const deadCodePercentage = Math.round(deadSymbols.length / candidates.length * 100 * 10) / 10;
869
+ return {
870
+ totalSymbols: candidates.length,
871
+ reachableSymbols: reachable.size,
872
+ deadSymbols,
873
+ unusedExports,
874
+ deadFiles,
875
+ scaffolding,
876
+ deadCodePercentage
877
+ };
878
+ }
879
+ computeConfidence(graph, symbolId, deadIds) {
880
+ const reverseEdges = graph.getReverseEdges(symbolId);
881
+ if (reverseEdges.length === 0) {
882
+ return 1;
883
+ }
884
+ const allImportersExcluded = reverseEdges.every((e) => {
885
+ const importerFile = e.from.split("::")[0] ?? "";
886
+ return this.matchesAny(importerFile, TEST_PATTERNS) || this.matchesAny(importerFile, CONFIG_PATTERNS);
887
+ });
888
+ if (allImportersExcluded) {
889
+ return 0.9;
890
+ }
891
+ const allImportersDead = reverseEdges.every((e) => deadIds.has(e.from));
892
+ if (allImportersDead) {
893
+ return 0.7;
894
+ }
895
+ return 0.7;
896
+ }
897
+ describeReason(confidence) {
898
+ switch (confidence) {
899
+ case 1:
900
+ return "Zero importers \u2014 no code references this symbol";
901
+ case 0.9:
902
+ return "Only referenced from test/config files";
903
+ case 0.7:
904
+ return "All importers are themselves dead (cascading)";
905
+ }
906
+ }
907
+ isFrameworkSymbol(name) {
908
+ return FRAMEWORK_PATTERNS.some((p) => p.test(name));
909
+ }
910
+ computeCascadeDepths(graph, deadIds, candidateIds) {
911
+ const depths = /* @__PURE__ */ new Map();
912
+ const rootDead = /* @__PURE__ */ new Set();
913
+ for (const id of deadIds) {
914
+ const reverseEdges = graph.getReverseEdges(id);
915
+ const deadImporters = reverseEdges.filter((e) => deadIds.has(e.from) && e.from !== id);
916
+ if (deadImporters.length === 0) {
917
+ rootDead.add(id);
918
+ depths.set(id, 0);
919
+ }
920
+ }
921
+ const queue = [...rootDead].map((id) => ({ id, depth: 0 }));
922
+ const visited = new Set(rootDead);
923
+ while (queue.length > 0) {
924
+ const current = queue.shift();
925
+ for (const edge of graph.getForwardEdges(current.id)) {
926
+ if (deadIds.has(edge.to) && !visited.has(edge.to) && candidateIds.has(edge.to)) {
927
+ const newDepth = current.depth + 1;
928
+ depths.set(edge.to, newDepth);
929
+ visited.add(edge.to);
930
+ queue.push({ id: edge.to, depth: newDepth });
931
+ }
932
+ }
933
+ }
934
+ return depths;
935
+ }
936
+ detectScaffolding(sourceContents) {
937
+ if (!sourceContents || sourceContents.size === 0) return [];
938
+ const results = [];
939
+ for (const [file, content] of sourceContents) {
940
+ if (this.matchesAny(file, TEST_PATTERNS) || this.matchesAny(file, CONFIG_PATTERNS)) continue;
941
+ const lines = content.split("\n");
942
+ for (let i = 0; i < lines.length; i++) {
943
+ const line = lines[i];
944
+ for (const { regex, pattern } of SCAFFOLDING_PATTERNS) {
945
+ if (regex.test(line)) {
946
+ results.push({
947
+ file,
948
+ line: i + 1,
949
+ pattern,
950
+ text: line.trim().slice(0, 120)
951
+ });
952
+ break;
953
+ }
954
+ }
955
+ }
956
+ }
957
+ return results;
958
+ }
959
+ matchesAny(value, patterns) {
960
+ return patterns.some((p) => p.test(value));
961
+ }
962
+ };
963
+
964
+ // src/core/importance/pagerank-calculator.ts
965
+ var DEFAULT_DAMPING = 0.85;
966
+ var DEFAULT_MAX_ITERATIONS = 100;
967
+ var DEFAULT_TOLERANCE = 1e-6;
968
+ var DEFAULT_LIMIT = 25;
969
+ var PageRankCalculator = class {
970
+ calculate(graph, options = {}) {
971
+ const damping = options.damping ?? DEFAULT_DAMPING;
972
+ const maxIterations = options.maxIterations ?? DEFAULT_MAX_ITERATIONS;
973
+ const tolerance = options.tolerance ?? DEFAULT_TOLERANCE;
974
+ const limit = options.limit ?? DEFAULT_LIMIT;
975
+ const nodes = graph.allNodes();
976
+ const n = nodes.length;
977
+ if (n === 0) {
978
+ return { rankings: [], totalSymbols: 0, iterations: 0, converged: true };
979
+ }
980
+ const scores = /* @__PURE__ */ new Map();
981
+ const initialScore = 1 / n;
982
+ for (const node of nodes) {
983
+ scores.set(node.symbolId, initialScore);
984
+ }
985
+ const outDegree = /* @__PURE__ */ new Map();
986
+ for (const node of nodes) {
987
+ outDegree.set(node.symbolId, graph.getForwardEdges(node.symbolId).length);
988
+ }
989
+ let iterations = 0;
990
+ let converged = false;
991
+ while (iterations < maxIterations) {
992
+ const newScores = /* @__PURE__ */ new Map();
993
+ const base = (1 - damping) / n;
994
+ let danglingSum = 0;
995
+ for (const node of nodes) {
996
+ if ((outDegree.get(node.symbolId) ?? 0) === 0) {
997
+ danglingSum += scores.get(node.symbolId) ?? 0;
998
+ }
999
+ }
1000
+ const danglingContrib = damping * danglingSum / n;
1001
+ for (const node of nodes) {
1002
+ let incomingScore = 0;
1003
+ const reverseEdges = graph.getReverseEdges(node.symbolId);
1004
+ const contributors = /* @__PURE__ */ new Set();
1005
+ for (const edge of reverseEdges) {
1006
+ if (contributors.has(edge.from)) continue;
1007
+ contributors.add(edge.from);
1008
+ const fromScore = scores.get(edge.from) ?? 0;
1009
+ const fromOut = outDegree.get(edge.from) ?? 1;
1010
+ incomingScore += fromScore / fromOut;
1011
+ }
1012
+ newScores.set(node.symbolId, base + damping * incomingScore + danglingContrib);
1013
+ }
1014
+ let maxDelta = 0;
1015
+ for (const node of nodes) {
1016
+ const delta = Math.abs((newScores.get(node.symbolId) ?? 0) - (scores.get(node.symbolId) ?? 0));
1017
+ if (delta > maxDelta) maxDelta = delta;
1018
+ }
1019
+ for (const [id, score] of newScores) {
1020
+ scores.set(id, score);
1021
+ }
1022
+ iterations++;
1023
+ if (maxDelta < tolerance) {
1024
+ converged = true;
1025
+ break;
1026
+ }
1027
+ }
1028
+ const entries = nodes.map((node) => ({
1029
+ symbolId: node.symbolId,
1030
+ name: node.name,
1031
+ kind: node.kind,
1032
+ file: node.symbolId.split("::")[0] ?? "",
1033
+ score: Math.round((scores.get(node.symbolId) ?? 0) * 1e6) / 1e6,
1034
+ inDegree: graph.getReverseEdges(node.symbolId).length,
1035
+ outDegree: outDegree.get(node.symbolId) ?? 0
1036
+ }));
1037
+ entries.sort((a, b) => b.score - a.score);
1038
+ return {
1039
+ rankings: entries.slice(0, limit),
1040
+ totalSymbols: n,
1041
+ iterations,
1042
+ converged
1043
+ };
1044
+ }
1045
+ };
1046
+
622
1047
  // src/core/co-change/co-change-analyzer.ts
623
1048
  var MIN_SHARED_COMMITS = 2;
624
1049
  var MIN_FREQUENCY = 0.1;
@@ -693,10 +1118,14 @@ function loadCoChangeMap(matrix) {
693
1118
  export {
694
1119
  createLogger,
695
1120
  SqliteStorageAdapter,
1121
+ SymbolGraph,
696
1122
  RevertDetector,
697
1123
  SimpleGitAdapter,
1124
+ ArchitecturalOverlay,
1125
+ DeadCodeDetector,
1126
+ PageRankCalculator,
698
1127
  aggregateCoChanges,
699
1128
  loadCoChangeMap,
700
1129
  SessionRecorderAdapter
701
1130
  };
702
- //# sourceMappingURL=chunk-WZKXGKKI.js.map
1131
+ //# sourceMappingURL=chunk-AXSQRYM3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/logger.ts","../src/adapters/storage/sqlite-storage-adapter.ts","../src/adapters/git/simple-git-adapter.ts","../src/adapters/stats/session-recorder-adapter.ts","../src/core/graph/symbol-graph.ts","../src/core/why-context/revert-detector.ts","../src/core/overlay/architectural-overlay.ts","../src/core/dead-code/dead-code-detector.ts","../src/core/importance/pagerank-calculator.ts","../src/core/co-change/co-change-analyzer.ts"],"sourcesContent":["/**\n * Structured logger for Ctxo — all output goes to stderr (stdout reserved for MCP JSON-RPC).\n *\n * Supports debug mode via DEBUG environment variable:\n * DEBUG=ctxo:* — all debug output\n * DEBUG=ctxo:git — only git adapter debug\n * DEBUG=ctxo:index — only indexing debug\n * DEBUG=ctxo:mcp — only MCP handler debug\n * DEBUG=ctxo:storage — only storage adapter debug\n * DEBUG=ctxo:masking — only masking pipeline debug\n *\n * Always outputs: info, warn, error (regardless of DEBUG).\n * Only outputs debug when matching namespace is enabled.\n */\n\nfunction isDebugEnabled(namespace: string): boolean {\n const debugEnv = process.env['DEBUG'] ?? '';\n if (!debugEnv) return false;\n const patterns = debugEnv.split(',').map(p => p.trim());\n return patterns.some(p => {\n if (p === '*' || p === 'ctxo:*') return true;\n if (p === namespace) return true;\n if (p.endsWith(':*') && namespace.startsWith(p.slice(0, -1))) return true;\n return false;\n });\n}\n\nexport interface Logger {\n debug(message: string, ...args: unknown[]): void;\n info(message: string, ...args: unknown[]): void;\n warn(message: string, ...args: unknown[]): void;\n error(message: string, ...args: unknown[]): void;\n}\n\nexport function createLogger(namespace: string): Logger {\n const prefix = `[${namespace}]`;\n\n return {\n debug(message: string, ...args: unknown[]) {\n if (isDebugEnabled(namespace)) {\n console.error(`${prefix} DEBUG ${message}`, ...args);\n }\n },\n info(message: string, ...args: unknown[]) {\n console.error(`${prefix} ${message}`, ...args);\n },\n warn(message: string, ...args: unknown[]) {\n console.error(`${prefix} WARN ${message}`, ...args);\n },\n error(message: string, ...args: unknown[]) {\n console.error(`${prefix} ERROR ${message}`, ...args);\n },\n };\n}\n","import initSqlJs, { type Database } from 'sql.js';\nimport { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport type { FileIndex, GraphEdge, SymbolNode } from '../../core/types.js';\nimport type { IStoragePort } from '../../ports/i-storage-port.js';\nimport { JsonIndexReader } from './json-index-reader.js';\nimport { createLogger } from '../../core/logger.js';\n\nconst log = createLogger('ctxo:storage');\n\nexport class SqliteStorageAdapter implements IStoragePort {\n private db: Database | undefined;\n private readonly dbPath: string;\n private readonly ctxoRoot: string;\n\n constructor(ctxoRoot: string) {\n this.ctxoRoot = ctxoRoot;\n this.dbPath = join(ctxoRoot, '.cache', 'symbols.db');\n }\n\n async init(): Promise<void> {\n const SQL = await initSqlJs();\n\n if (existsSync(this.dbPath)) {\n try {\n const buffer = readFileSync(this.dbPath);\n this.db = new SQL.Database(buffer);\n this.verifyIntegrity();\n } catch {\n log.warn('Corrupt DB detected, rebuilding from JSON index');\n this.db = new SQL.Database();\n this.createTables();\n }\n } else {\n this.db = new SQL.Database();\n this.createTables();\n }\n\n // Always rebuild from JSON — it is the source of truth\n this.rebuildFromJson();\n }\n\n async initEmpty(): Promise<void> {\n const SQL = await initSqlJs();\n if (existsSync(this.dbPath)) {\n try {\n const buffer = readFileSync(this.dbPath);\n this.db = new SQL.Database(buffer);\n } catch {\n this.db = new SQL.Database();\n }\n } else {\n this.db = new SQL.Database();\n }\n this.createTables();\n this.clearIndexTables();\n }\n\n /** Returns the underlying sql.js Database instance. Throws if not initialized. */\n getDb(): Database {\n if (!this.db) {\n throw new Error('SqliteStorageAdapter not initialized. Call init() first.');\n }\n return this.db;\n }\n\n private database(): Database {\n return this.getDb();\n }\n\n private verifyIntegrity(): void {\n const db = this.database();\n const result = db.exec('PRAGMA integrity_check');\n const firstRow = result[0]?.values[0];\n if (!firstRow || firstRow[0] !== 'ok') {\n throw new Error('SQLite integrity check failed');\n }\n\n const tables = db.exec(\n \"SELECT name FROM sqlite_master WHERE type='table' AND name IN ('symbols', 'edges', 'files')\",\n );\n if (!tables[0] || tables[0].values.length < 3) {\n throw new Error('Missing required tables');\n }\n }\n\n private createTables(): void {\n const db = this.database();\n db.run(`\n CREATE TABLE IF NOT EXISTS files (\n file_path TEXT PRIMARY KEY,\n last_modified INTEGER NOT NULL\n )\n `);\n db.run(`\n CREATE TABLE IF NOT EXISTS symbols (\n symbol_id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n kind TEXT NOT NULL,\n file_path TEXT NOT NULL,\n start_line INTEGER NOT NULL,\n end_line INTEGER NOT NULL\n )\n `);\n db.run(`\n CREATE TABLE IF NOT EXISTS edges (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n from_symbol TEXT NOT NULL,\n to_symbol TEXT NOT NULL,\n kind TEXT NOT NULL\n )\n `);\n db.run('CREATE INDEX IF NOT EXISTS idx_symbols_file ON symbols(file_path)');\n db.run('CREATE INDEX IF NOT EXISTS idx_edges_from ON edges(from_symbol)');\n db.run('CREATE INDEX IF NOT EXISTS idx_edges_to ON edges(to_symbol)');\n }\n\n private clearIndexTables(): void {\n const db = this.database();\n db.run('DELETE FROM edges');\n db.run('DELETE FROM symbols');\n db.run('DELETE FROM files');\n }\n\n private rebuildFromJson(): void {\n const reader = new JsonIndexReader(this.ctxoRoot);\n const indices = reader.readAll();\n\n if (indices.length > 0) {\n this.bulkWrite(indices);\n }\n }\n\n writeSymbolFile(fileIndex: FileIndex): void {\n const db = this.database();\n\n db.run('BEGIN TRANSACTION');\n try {\n this.deleteFileData(db, fileIndex.file);\n\n db.run(\n 'INSERT INTO files (file_path, last_modified) VALUES (?, ?)',\n [fileIndex.file, fileIndex.lastModified],\n );\n\n for (const sym of fileIndex.symbols) {\n db.run(\n 'INSERT OR REPLACE INTO symbols (symbol_id, name, kind, file_path, start_line, end_line) VALUES (?, ?, ?, ?, ?, ?)',\n [sym.symbolId, sym.name, sym.kind, fileIndex.file, sym.startLine, sym.endLine],\n );\n }\n\n for (const edge of fileIndex.edges) {\n db.run(\n 'INSERT INTO edges (from_symbol, to_symbol, kind) VALUES (?, ?, ?)',\n [edge.from, edge.to, edge.kind],\n );\n }\n\n db.run('COMMIT');\n this.persistIfNeeded();\n } catch (err) {\n db.run('ROLLBACK');\n throw err;\n }\n }\n\n /**\n * Read symbol file from SQLite cache.\n * NOTE: SQLite only caches symbols + edges (graph topology).\n * intent, antiPatterns, and complexity live in committed JSON index only.\n * Use JsonIndexReader for full FileIndex data.\n */\n readSymbolFile(relativePath: string): FileIndex | undefined {\n const db = this.database();\n\n const fileResult = db.exec(\n 'SELECT file_path, last_modified FROM files WHERE file_path = ?',\n [relativePath],\n );\n if (!fileResult[0] || fileResult[0].values.length === 0) {\n return undefined;\n }\n\n const [filePath, lastModified] = fileResult[0].values[0] as [string, number];\n\n const symbols = this.getSymbolsForFile(db, filePath);\n const edges = this.getEdgesForFile(db, filePath);\n\n return {\n file: filePath,\n lastModified,\n symbols,\n edges,\n // Not stored in SQLite — use JsonIndexReader for these fields\n intent: [],\n antiPatterns: [],\n };\n }\n\n listIndexedFiles(): string[] {\n const db = this.database();\n const result = db.exec('SELECT file_path FROM files ORDER BY file_path');\n if (!result[0]) return [];\n return result[0].values.map((row) => row[0] as string);\n }\n\n deleteSymbolFile(relativePath: string): void {\n const db = this.database();\n this.deleteFileData(db, relativePath);\n }\n\n getSymbolById(symbolId: string): SymbolNode | undefined {\n const db = this.database();\n const result = db.exec(\n 'SELECT symbol_id, name, kind, start_line, end_line FROM symbols WHERE symbol_id = ?',\n [symbolId],\n );\n if (!result[0] || result[0].values.length === 0) {\n return undefined;\n }\n\n const [sid, name, kind, startLine, endLine] = result[0].values[0] as [string, string, string, number, number];\n return { symbolId: sid, name, kind: kind as SymbolNode['kind'], startLine, endLine };\n }\n\n getEdgesFrom(symbolId: string): GraphEdge[] {\n const db = this.database();\n const result = db.exec(\n 'SELECT from_symbol, to_symbol, kind FROM edges WHERE from_symbol = ?',\n [symbolId],\n );\n if (!result[0]) return [];\n return result[0].values.map((row) => ({\n from: row[0] as string,\n to: row[1] as string,\n kind: row[2] as GraphEdge['kind'],\n }));\n }\n\n getEdgesTo(symbolId: string): GraphEdge[] {\n const db = this.database();\n const result = db.exec(\n 'SELECT from_symbol, to_symbol, kind FROM edges WHERE to_symbol = ?',\n [symbolId],\n );\n if (!result[0]) return [];\n return result[0].values.map((row) => ({\n from: row[0] as string,\n to: row[1] as string,\n kind: row[2] as GraphEdge['kind'],\n }));\n }\n\n getAllSymbols(): SymbolNode[] {\n const db = this.database();\n const result = db.exec(\n 'SELECT symbol_id, name, kind, start_line, end_line FROM symbols',\n );\n if (!result[0]) return [];\n return result[0].values.map((row) => ({\n symbolId: row[0] as string,\n name: row[1] as string,\n kind: row[2] as SymbolNode['kind'],\n startLine: row[3] as number,\n endLine: row[4] as number,\n }));\n }\n\n getAllEdges(): GraphEdge[] {\n const db = this.database();\n const result = db.exec(\n 'SELECT from_symbol, to_symbol, kind FROM edges',\n );\n if (!result[0]) return [];\n return result[0].values.map((row) => ({\n from: row[0] as string,\n to: row[1] as string,\n kind: row[2] as GraphEdge['kind'],\n }));\n }\n\n bulkWrite(indices: FileIndex[]): void {\n const db = this.database();\n\n db.run('BEGIN TRANSACTION');\n try {\n for (const fileIndex of indices) {\n this.deleteFileData(db, fileIndex.file);\n\n db.run(\n 'INSERT INTO files (file_path, last_modified) VALUES (?, ?)',\n [fileIndex.file, fileIndex.lastModified],\n );\n\n for (const sym of fileIndex.symbols) {\n db.run(\n 'INSERT OR REPLACE INTO symbols (symbol_id, name, kind, file_path, start_line, end_line) VALUES (?, ?, ?, ?, ?, ?)',\n [sym.symbolId, sym.name, sym.kind, fileIndex.file, sym.startLine, sym.endLine],\n );\n }\n\n for (const edge of fileIndex.edges) {\n db.run(\n 'INSERT INTO edges (from_symbol, to_symbol, kind) VALUES (?, ?, ?)',\n [edge.from, edge.to, edge.kind],\n );\n }\n }\n\n db.run('COMMIT');\n this.persistIfNeeded();\n } catch (err) {\n db.run('ROLLBACK');\n throw err;\n }\n }\n\n persist(): void {\n const db = this.database();\n const data = db.export();\n const buffer = Buffer.from(data);\n mkdirSync(dirname(this.dbPath), { recursive: true });\n writeFileSync(this.dbPath, buffer);\n }\n\n close(): void {\n if (this.db) {\n this.persist();\n this.db.close();\n this.db = undefined;\n }\n }\n\n private persistIfNeeded(): void {\n try {\n this.persist();\n } catch (err) {\n log.error(`Failed to persist DB: ${(err as Error).message}`);\n }\n }\n\n private deleteFileData(db: Database, filePath: string): void {\n // Delete edges originating from this file's symbols\n const symResult = db.exec(\n 'SELECT symbol_id FROM symbols WHERE file_path = ?',\n [filePath],\n );\n if (symResult[0]) {\n for (const row of symResult[0].values) {\n db.run('DELETE FROM edges WHERE from_symbol = ?', [row[0]]);\n }\n }\n db.run('DELETE FROM symbols WHERE file_path = ?', [filePath]);\n db.run('DELETE FROM files WHERE file_path = ?', [filePath]);\n }\n\n private getSymbolsForFile(db: Database, filePath: string): SymbolNode[] {\n const result = db.exec(\n 'SELECT symbol_id, name, kind, start_line, end_line FROM symbols WHERE file_path = ? ORDER BY start_line',\n [filePath],\n );\n if (!result[0]) return [];\n return result[0].values.map((row) => ({\n symbolId: row[0] as string,\n name: row[1] as string,\n kind: row[2] as SymbolNode['kind'],\n startLine: row[3] as number,\n endLine: row[4] as number,\n }));\n }\n\n private getEdgesForFile(db: Database, filePath: string): GraphEdge[] {\n const result = db.exec(\n `SELECT e.from_symbol, e.to_symbol, e.kind FROM edges e\n INNER JOIN symbols s ON e.from_symbol = s.symbol_id\n WHERE s.file_path = ?`,\n [filePath],\n );\n if (!result[0]) return [];\n return result[0].values.map((row) => ({\n from: row[0] as string,\n to: row[1] as string,\n kind: row[2] as GraphEdge['kind'],\n }));\n }\n}\n","import { simpleGit, type SimpleGit } from 'simple-git';\nimport type { IGitPort } from '../../ports/i-git-port.js';\nimport type { CommitRecord, ChurnData } from '../../core/types.js';\nimport { createLogger } from '../../core/logger.js';\n\nconst log = createLogger('ctxo:git');\n\nexport class SimpleGitAdapter implements IGitPort {\n private readonly git: SimpleGit;\n\n constructor(projectRoot: string) {\n this.git = simpleGit(projectRoot);\n log.debug('Initialized with root: %s', projectRoot);\n }\n\n async isAvailable(): Promise<boolean> {\n try {\n await this.git.version();\n return true;\n } catch {\n return false;\n }\n }\n\n async getCommitHistory(filePath: string, maxCount?: number): Promise<CommitRecord[]> {\n try {\n const log = await this.git.log({\n file: filePath,\n '--follow': null,\n ...(maxCount ? { maxCount } : {}),\n });\n\n return log.all.map((entry) => ({\n hash: entry.hash,\n message: entry.message,\n date: entry.date,\n author: entry.author_name,\n }));\n } catch (err) {\n log.error(` Failed to get history for ${filePath}: ${(err as Error).message}`);\n return [];\n }\n }\n\n async getBatchHistory(maxCount = 20): Promise<Map<string, CommitRecord[]>> {\n try {\n const log = await this.git.log({ maxCount: maxCount * 50, '--name-only': null });\n const result = new Map<string, CommitRecord[]>();\n\n for (const entry of log.all) {\n const record: CommitRecord = {\n hash: entry.hash,\n message: entry.message,\n date: entry.date,\n author: entry.author_name,\n };\n\n const diff = entry.diff;\n const files: string[] = diff?.files?.map((f: { file: string }) => f.file.replace(/\\\\/g, '/')) ?? [];\n\n for (const file of files) {\n let list = result.get(file);\n if (!list) { list = []; result.set(file, list); }\n if (list.length < maxCount) {\n list.push(record);\n }\n }\n }\n\n return result;\n } catch (err) {\n log.error(` Batch history failed: ${(err as Error).message}`);\n return new Map();\n }\n }\n\n async getChangedFiles(since: string): Promise<string[]> {\n try {\n const diff = await this.git.diffSummary([since]);\n return diff.files.map((f) => f.file.replace(/\\\\/g, '/'));\n } catch (err) {\n log.error(` Failed to get changed files since ${since}: ${(err as Error).message}`);\n return [];\n }\n }\n\n async getFileChurn(filePath: string): Promise<ChurnData> {\n try {\n const log = await this.git.log({ file: filePath, '--follow': null });\n\n return {\n filePath,\n commitCount: log.total,\n };\n } catch (err) {\n log.error(` Failed to get churn for ${filePath}: ${(err as Error).message}`);\n return { filePath, commitCount: 0 };\n }\n }\n}\n","import type { Database } from 'sql.js';\nimport type {\n ISessionRecorderPort,\n SessionEvent,\n AggregatedStats,\n ToolStats,\n SymbolStats,\n DetailLevelStats,\n} from '../../ports/i-session-recorder-port.js';\nimport { createLogger } from '../../core/logger.js';\n\nconst log = createLogger('ctxo:stats');\n\nexport class SessionRecorderAdapter implements ISessionRecorderPort {\n private readonly db: Database;\n private readonly onWrite: (() => void) | null;\n private tableCreated = false;\n\n constructor(db: Database, onWrite?: () => void) {\n this.db = db;\n this.onWrite = onWrite ?? null;\n }\n\n private ensureTable(): void {\n if (this.tableCreated) return;\n this.db.run(`\n CREATE TABLE IF NOT EXISTS session_events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n timestamp TEXT DEFAULT (datetime('now')),\n tool TEXT NOT NULL,\n symbol_id TEXT,\n detail_level TEXT,\n response_tokens INTEGER,\n response_bytes INTEGER,\n latency_ms REAL,\n truncated BOOLEAN DEFAULT 0\n )\n `);\n this.db.run('CREATE INDEX IF NOT EXISTS idx_session_timestamp ON session_events(timestamp)');\n this.db.run('CREATE INDEX IF NOT EXISTS idx_session_tool ON session_events(tool)');\n this.tableCreated = true;\n }\n\n record(event: SessionEvent): void {\n try {\n this.ensureTable();\n this.db.run(\n `INSERT INTO session_events (tool, symbol_id, detail_level, response_tokens, response_bytes, latency_ms, truncated)\n VALUES (?, ?, ?, ?, ?, ?, ?)`,\n [\n event.tool,\n event.symbolId,\n event.detailLevel,\n event.responseTokens,\n event.responseBytes,\n event.latencyMs,\n event.truncated ? 1 : 0,\n ],\n );\n this.flushToDisk();\n } catch (err) {\n log.error(`Failed to record session event: ${(err as Error).message}`);\n }\n }\n\n queryStats(days?: number): AggregatedStats {\n this.ensureTable();\n\n const whereClause = days != null && days > 0\n ? `WHERE timestamp >= datetime('now', '-${Math.floor(days)} days')`\n : '';\n\n // Total calls and tokens\n const summaryResult = this.db.exec(\n `SELECT COUNT(*) as total_calls, COALESCE(SUM(response_tokens), 0) as total_tokens\n FROM session_events ${whereClause}`,\n );\n const summaryRow = summaryResult[0]?.values[0];\n const totalCalls = Number(summaryRow?.[0] ?? 0);\n const totalTokensServed = Number(summaryRow?.[1] ?? 0);\n\n // Top tools\n const toolsResult = this.db.exec(\n `SELECT tool, COUNT(*) as calls, CAST(AVG(response_tokens) AS INTEGER) as avg_tokens\n FROM session_events ${whereClause}\n GROUP BY tool ORDER BY calls DESC LIMIT 5`,\n );\n const topTools: ToolStats[] = (toolsResult[0]?.values ?? []).map((row) => ({\n tool: String(row[0]),\n calls: Number(row[1]),\n avgTokens: Number(row[2]),\n }));\n\n // Top symbols (exclude nulls)\n const symbolWherePrefix = whereClause ? whereClause + ' AND' : 'WHERE';\n const symbolsResult = this.db.exec(\n `SELECT symbol_id, COUNT(*) as queries\n FROM session_events ${symbolWherePrefix} symbol_id IS NOT NULL\n GROUP BY symbol_id ORDER BY queries DESC LIMIT 5`,\n );\n const topSymbols: SymbolStats[] = (symbolsResult[0]?.values ?? []).map((row) => {\n const symbolId = String(row[0]);\n const parts = symbolId.split('::');\n const name = parts.length >= 2 ? parts[1]! : symbolId;\n return {\n symbolId,\n name,\n queries: Number(row[1]),\n };\n });\n\n // Detail level distribution\n const levelResult = this.db.exec(\n `SELECT detail_level, COUNT(*) as count\n FROM session_events ${symbolWherePrefix} detail_level IS NOT NULL\n GROUP BY detail_level ORDER BY detail_level`,\n );\n const levelRows = levelResult[0]?.values ?? [];\n const levelTotal = levelRows.reduce((sum, row) => sum + Number(row[1]), 0);\n const detailLevelDistribution: DetailLevelStats[] = levelRows.map((row) => ({\n level: String(row[0]),\n count: Number(row[1]),\n percentage: levelTotal > 0 ? Math.round((Number(row[1]) / levelTotal) * 100) : 0,\n }));\n\n return {\n totalCalls,\n totalTokensServed,\n topTools,\n topSymbols,\n detailLevelDistribution,\n };\n }\n\n clear(): void {\n this.ensureTable();\n this.db.run('DELETE FROM session_events');\n this.flushToDisk();\n }\n\n private flushToDisk(): void {\n try {\n this.onWrite?.();\n } catch (err) {\n log.error(`Failed to flush stats to disk: ${(err as Error).message}`);\n }\n }\n}\n","import type { SymbolNode, GraphEdge } from '../types.js';\n\nexport class SymbolGraph {\n private readonly nodes = new Map<string, SymbolNode>();\n private readonly nodesByFileAndName = new Map<string, SymbolNode>();\n private readonly forwardEdges = new Map<string, GraphEdge[]>();\n private readonly reverseEdges = new Map<string, GraphEdge[]>();\n private edgeSet = new Set<string>();\n\n addNode(node: SymbolNode): void {\n this.nodes.set(node.symbolId, node);\n // Index by file::name (ignoring kind) for fuzzy edge resolution\n const [p0, p1] = node.symbolId.split('::');\n if (p0 !== undefined && p1 !== undefined) {\n this.nodesByFileAndName.set(`${p0}::${p1}`, node);\n }\n }\n\n addEdge(edge: GraphEdge): void {\n // Resolve edge endpoints: if exact ID not found, try fuzzy match by file::name\n const resolvedFrom = this.resolveNodeId(edge.from);\n const resolvedTo = this.resolveNodeId(edge.to);\n const resolvedEdge: GraphEdge = { from: resolvedFrom, to: resolvedTo, kind: edge.kind };\n\n const edgeKey = `${resolvedEdge.from}|${resolvedEdge.to}|${resolvedEdge.kind}`;\n if (this.edgeSet.has(edgeKey)) return;\n this.edgeSet.add(edgeKey);\n\n const forward = this.forwardEdges.get(resolvedEdge.from) ?? [];\n forward.push(resolvedEdge);\n this.forwardEdges.set(resolvedEdge.from, forward);\n\n const reverse = this.reverseEdges.get(resolvedEdge.to) ?? [];\n reverse.push(resolvedEdge);\n this.reverseEdges.set(resolvedEdge.to, reverse);\n }\n\n getNode(symbolId: string): SymbolNode | undefined {\n return this.nodes.get(symbolId) ?? this.resolveNodeFuzzy(symbolId);\n }\n\n private resolveNodeId(id: string): string {\n if (this.nodes.has(id)) return id;\n const [p0, p1] = id.split('::');\n if (p0 !== undefined && p1 !== undefined) {\n const fuzzyKey = `${p0}::${p1}`;\n const match = this.nodesByFileAndName.get(fuzzyKey);\n if (match) return match.symbolId;\n\n // Try .js → .ts extension swap (handles unresolved module specifiers)\n const jsToTs = p0.replace(/\\.js$/, '.ts');\n if (jsToTs !== p0) {\n const altKey = `${jsToTs}::${p1}`;\n const altMatch = this.nodesByFileAndName.get(altKey);\n if (altMatch) return altMatch.symbolId;\n }\n }\n return id;\n }\n\n private resolveNodeFuzzy(id: string): SymbolNode | undefined {\n const [p0, p1] = id.split('::');\n if (p0 !== undefined && p1 !== undefined) {\n const match = this.nodesByFileAndName.get(`${p0}::${p1}`);\n if (match) return match;\n\n // Try .js → .ts extension swap (consistent with resolveNodeId)\n const jsToTs = p0.replace(/\\.js$/, '.ts');\n if (jsToTs !== p0) {\n return this.nodesByFileAndName.get(`${jsToTs}::${p1}`);\n }\n }\n return undefined;\n }\n\n getForwardEdges(symbolId: string): GraphEdge[] {\n return this.forwardEdges.get(symbolId) ?? [];\n }\n\n getReverseEdges(symbolId: string): GraphEdge[] {\n return this.reverseEdges.get(symbolId) ?? [];\n }\n\n hasNode(symbolId: string): boolean {\n return this.nodes.has(symbolId) || this.resolveNodeFuzzy(symbolId) !== undefined;\n }\n\n get nodeCount(): number {\n return this.nodes.size;\n }\n\n get edgeCount(): number {\n return this.edgeSet.size;\n }\n\n allNodes(): SymbolNode[] {\n return [...this.nodes.values()];\n }\n\n allEdges(): GraphEdge[] {\n const edges: GraphEdge[] = [];\n for (const list of this.forwardEdges.values()) {\n edges.push(...list);\n }\n return edges;\n }\n}\n","import type { CommitRecord, AntiPattern } from '../types.js';\n\n// Explicit revert patterns\nconst REVERT_QUOTED_PATTERN = /^Revert \"(.+)\"$/;\nconst REVERT_PREFIX_PATTERN = /^revert:\\s*(.+)$/i;\n\n// Undo/rollback patterns\nconst UNDO_PATTERN = /^undo[:\\s]/i;\nconst ROLLBACK_PATTERN = /^rollback[:\\s]/i;\n\n// Indirect revert indicators (keywords in commit message body)\nconst INDIRECT_KEYWORDS = [\n /\\brevert(?:s|ed|ing)?\\b/i,\n /\\broll(?:s|ed|ing)?\\s*back\\b/i,\n /\\bundo(?:es|ne|ing)?\\b/i,\n /\\bbacked?\\s*out\\b/i,\n /\\bremov(?:e|es|ed|ing)\\s+(?:broken|buggy|faulty)\\b/i,\n];\n\nexport class RevertDetector {\n detect(commits: readonly CommitRecord[]): AntiPattern[] {\n const antiPatterns: AntiPattern[] = [];\n\n for (const commit of commits) {\n if (!commit.message) continue;\n\n if (this.isRevert(commit.message)) {\n antiPatterns.push({\n hash: commit.hash,\n message: commit.message,\n date: commit.date,\n });\n }\n }\n\n return antiPatterns;\n }\n\n private isRevert(message: string): boolean {\n // Explicit patterns (high confidence)\n if (REVERT_QUOTED_PATTERN.test(message)) return true;\n if (REVERT_PREFIX_PATTERN.test(message)) return true;\n if (UNDO_PATTERN.test(message)) return true;\n if (ROLLBACK_PATTERN.test(message)) return true;\n\n // Indirect indicators (keyword search in full message)\n for (const pattern of INDIRECT_KEYWORDS) {\n if (pattern.test(message)) return true;\n }\n\n return false;\n }\n}\n","export interface LayerRule {\n readonly pattern: RegExp;\n readonly layer: string;\n}\n\nexport interface OverlayResult {\n readonly layers: Record<string, string[]>;\n}\n\nconst DEFAULT_RULES: LayerRule[] = [\n // Test layer (matched first — __tests__, .test.ts, tests/, fixtures)\n { pattern: /__tests__/, layer: 'Test' },\n { pattern: /\\.test\\.ts$/, layer: 'Test' },\n { pattern: /\\btests\\b/, layer: 'Test' },\n { pattern: /\\bfixtures?\\b/, layer: 'Test' },\n // Composition root\n { pattern: /src\\/index\\.ts$/, layer: 'Composition' },\n // Domain\n { pattern: /\\bcore\\b/, layer: 'Domain' },\n { pattern: /\\bports?\\b/, layer: 'Domain' },\n // Adapter\n { pattern: /\\badapters?\\b/, layer: 'Adapter' },\n { pattern: /\\bcli\\b/, layer: 'Adapter' },\n // Infrastructure\n { pattern: /\\binfra\\b/, layer: 'Infrastructure' },\n { pattern: /\\bdb\\b/, layer: 'Infrastructure' },\n { pattern: /\\bqueue\\b/, layer: 'Infrastructure' },\n // Config files\n { pattern: /\\.(config|rc)\\.(ts|js|json)$/, layer: 'Configuration' },\n];\n\nexport class ArchitecturalOverlay {\n private readonly rules: LayerRule[];\n\n constructor(customRules?: LayerRule[]) {\n this.rules = (customRules ?? DEFAULT_RULES).map(({ pattern, layer }) => ({\n pattern: new RegExp(pattern.source, pattern.flags),\n layer,\n }));\n }\n\n classify(filePaths: readonly string[]): OverlayResult {\n const layers: Record<string, string[]> = {};\n\n for (const filePath of filePaths) {\n const layer = this.matchLayer(filePath);\n const list = layers[layer] ?? [];\n list.push(filePath);\n layers[layer] = list;\n }\n\n return { layers };\n }\n\n private matchLayer(filePath: string): string {\n for (const rule of this.rules) {\n if (rule.pattern.test(filePath)) {\n return rule.layer;\n }\n }\n return 'Unknown';\n }\n}\n","import type { SymbolGraph } from '../graph/symbol-graph.js';\nimport type { SymbolKind } from '../types.js';\n\nexport type DeadCodeConfidence = 1.0 | 0.9 | 0.7;\n\nexport interface DeadSymbolEntry {\n readonly symbolId: string;\n readonly file: string;\n readonly name: string;\n readonly kind: SymbolKind;\n readonly confidence: DeadCodeConfidence;\n readonly reason: string;\n readonly cascadeDepth?: number;\n}\n\nexport interface UnusedExportEntry {\n readonly symbolId: string;\n readonly file: string;\n readonly name: string;\n readonly kind: SymbolKind;\n}\n\nexport interface ScaffoldingEntry {\n readonly file: string;\n readonly line: number;\n readonly pattern: string;\n readonly text: string;\n}\n\nexport interface DeadCodeResult {\n readonly totalSymbols: number;\n readonly reachableSymbols: number;\n readonly deadSymbols: DeadSymbolEntry[];\n readonly unusedExports: UnusedExportEntry[];\n readonly deadFiles: string[];\n readonly scaffolding: ScaffoldingEntry[];\n readonly deadCodePercentage: number;\n}\n\n// Patterns for files to exclude from dead code analysis\nconst TEST_PATTERNS = [/__tests__/, /\\.test\\.ts$/, /\\.spec\\.ts$/, /\\btests\\//, /\\bfixtures?\\//];\nconst CONFIG_PATTERNS = [/\\.(config|rc)\\.(ts|js|json)$/, /tsconfig/, /eslint/, /\\.d\\.ts$/];\n\n// Framework lifecycle symbols — should never be flagged as dead\nconst FRAMEWORK_PATTERNS = [\n // Vitest\n /^(describe|it|expect|beforeAll|beforeEach|afterAll|afterEach|vi)$/,\n // MCP SDK\n /^(registerTool|registerPrompt|connect|close)$/,\n // Zod schemas (conventionally exported for validation)\n /Schema$/,\n // Node.js lifecycle\n /^main$/,\n];\n\n// Scaffolding / AI artifact patterns\nconst SCAFFOLDING_PATTERNS = [\n { regex: /\\bTODO\\b/i, pattern: 'TODO' },\n { regex: /\\bFIXME\\b/i, pattern: 'FIXME' },\n { regex: /\\bHACK\\b/i, pattern: 'HACK' },\n { regex: /\\bPLACEHOLDER\\b/i, pattern: 'PLACEHOLDER' },\n { regex: /\\bXXX\\b/, pattern: 'XXX' },\n { regex: /Phase\\s+\\d|Step\\s+\\d/i, pattern: 'PHASE/STEP' },\n { regex: /not\\s+(?:yet\\s+)?implement/i, pattern: 'NOT_IMPLEMENTED' },\n { regex: /temporary|temp\\s+fix/i, pattern: 'TEMPORARY' },\n];\n\nexport class DeadCodeDetector {\n detect(\n graph: SymbolGraph,\n options: { includeTests?: boolean; sourceContents?: Map<string, string> } = {},\n ): DeadCodeResult {\n const allNodes = graph.allNodes();\n\n // Filter candidates — exclude tests/config by default\n const candidates = options.includeTests\n ? allNodes\n : allNodes.filter((n) => {\n const file = n.symbolId.split('::')[0] ?? '';\n return !this.matchesAny(file, TEST_PATTERNS) && !this.matchesAny(file, CONFIG_PATTERNS);\n });\n\n if (candidates.length === 0) {\n return { totalSymbols: 0, reachableSymbols: 0, deadSymbols: [], unusedExports: [], deadFiles: [], scaffolding: this.detectScaffolding(options.sourceContents), deadCodePercentage: 0 };\n }\n\n // Dynamic entry point detection: symbols with zero reverse edges (no one imports them)\n const candidateIds = new Set(candidates.map((n) => n.symbolId));\n const entryPoints = new Set<string>();\n\n for (const node of candidates) {\n // Framework lifecycle symbols are always entry points (never dead)\n if (this.isFrameworkSymbol(node.name)) {\n entryPoints.add(node.symbolId);\n continue;\n }\n\n const reverseEdges = graph.getReverseEdges(node.symbolId);\n const forwardEdges = graph.getForwardEdges(node.symbolId);\n\n // Filter: only count reverse edges from OTHER candidates\n const incomingFromCandidates = reverseEdges.filter((e) => candidateIds.has(e.from) && e.from !== node.symbolId);\n\n if (incomingFromCandidates.length === 0) {\n const hasOutgoing = forwardEdges.some((e) => candidateIds.has(e.to));\n if (hasOutgoing || reverseEdges.length > 0) {\n entryPoints.add(node.symbolId);\n }\n }\n }\n\n // Forward BFS from all entry points — mark reachable symbols\n const reachable = new Set<string>();\n const queue = [...entryPoints];\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n if (reachable.has(current)) continue;\n reachable.add(current);\n\n for (const edge of graph.getForwardEdges(current)) {\n if (!reachable.has(edge.to) && candidateIds.has(edge.to)) {\n queue.push(edge.to);\n }\n }\n }\n\n // Dead = candidates not reachable from any entry point\n // These are \"island\" symbols — circular deps where the whole cluster is unreachable\n const deadIds = new Set<string>();\n for (const node of candidates) {\n if (!reachable.has(node.symbolId)) {\n deadIds.add(node.symbolId);\n }\n }\n\n // Compute cascade depth: how deep in the dead chain is this symbol?\n const cascadeDepthMap = this.computeCascadeDepths(graph, deadIds, candidateIds);\n\n // Compute confidence per dead symbol\n const deadSymbols: DeadSymbolEntry[] = [];\n for (const node of candidates) {\n if (!deadIds.has(node.symbolId)) continue;\n\n const file = node.symbolId.split('::')[0] ?? '';\n const confidence = this.computeConfidence(graph, node.symbolId, deadIds);\n const cascadeDepth = cascadeDepthMap.get(node.symbolId);\n\n deadSymbols.push({\n symbolId: node.symbolId,\n file,\n name: node.name,\n kind: node.kind,\n confidence,\n reason: this.describeReason(confidence),\n ...(cascadeDepth !== undefined && cascadeDepth > 0 ? { cascadeDepth } : {}),\n });\n }\n\n // Dead files = files where ALL candidate symbols are dead\n const fileStats = new Map<string, { total: number; dead: number }>();\n for (const node of candidates) {\n const file = node.symbolId.split('::')[0] ?? '';\n const stats = fileStats.get(file) ?? { total: 0, dead: 0 };\n stats.total++;\n if (deadIds.has(node.symbolId)) stats.dead++;\n fileStats.set(file, stats);\n }\n\n const deadFiles = [...fileStats.entries()]\n .filter(([, s]) => s.total > 0 && s.dead === s.total)\n .map(([file]) => file);\n\n // Unused exports: symbols with zero reverse edges from other candidates\n // Different from dead code: an unused export may be an entry point (reachable)\n // but still never imported by anyone\n const unusedExports: UnusedExportEntry[] = [];\n for (const node of candidates) {\n const reverseEdges = graph.getReverseEdges(node.symbolId);\n const externalImporters = reverseEdges.filter((e) =>\n candidateIds.has(e.from) && e.from !== node.symbolId,\n );\n\n if (externalImporters.length === 0 && !deadIds.has(node.symbolId)) {\n // Exported, reachable (entry point), but nobody imports it\n unusedExports.push({\n symbolId: node.symbolId,\n file: node.symbolId.split('::')[0] ?? '',\n name: node.name,\n kind: node.kind,\n });\n }\n }\n\n // Scaffolding detection: scan source contents for TODO/FIXME/HACK patterns\n const scaffolding = this.detectScaffolding(options.sourceContents);\n\n const deadCodePercentage = Math.round((deadSymbols.length / candidates.length) * 100 * 10) / 10;\n\n return {\n totalSymbols: candidates.length,\n reachableSymbols: reachable.size,\n deadSymbols,\n unusedExports,\n deadFiles,\n scaffolding,\n deadCodePercentage,\n };\n }\n\n private computeConfidence(\n graph: SymbolGraph,\n symbolId: string,\n deadIds: Set<string>,\n ): DeadCodeConfidence {\n const reverseEdges = graph.getReverseEdges(symbolId);\n\n // 1.0: Zero importers at all — definitely dead\n if (reverseEdges.length === 0) {\n return 1.0;\n }\n\n // 0.9: Has importers but they're all from test/config files\n const allImportersExcluded = reverseEdges.every((e) => {\n const importerFile = e.from.split('::')[0] ?? '';\n return this.matchesAny(importerFile, TEST_PATTERNS) || this.matchesAny(importerFile, CONFIG_PATTERNS);\n });\n if (allImportersExcluded) {\n return 0.9;\n }\n\n // 0.7: All importers are themselves dead (cascading dead code)\n const allImportersDead = reverseEdges.every((e) => deadIds.has(e.from));\n if (allImportersDead) {\n return 0.7;\n }\n\n // Shouldn't reach here if symbol is truly dead, but default to 0.7\n return 0.7;\n }\n\n private describeReason(confidence: DeadCodeConfidence): string {\n switch (confidence) {\n case 1.0:\n return 'Zero importers — no code references this symbol';\n case 0.9:\n return 'Only referenced from test/config files';\n case 0.7:\n return 'All importers are themselves dead (cascading)';\n }\n }\n\n private isFrameworkSymbol(name: string): boolean {\n return FRAMEWORK_PATTERNS.some((p) => p.test(name));\n }\n\n private computeCascadeDepths(\n graph: SymbolGraph,\n deadIds: Set<string>,\n candidateIds: Set<string>,\n ): Map<string, number> {\n const depths = new Map<string, number>();\n\n // Find root dead symbols (zero reverse edges from other dead symbols)\n const rootDead = new Set<string>();\n for (const id of deadIds) {\n const reverseEdges = graph.getReverseEdges(id);\n const deadImporters = reverseEdges.filter((e) => deadIds.has(e.from) && e.from !== id);\n if (deadImporters.length === 0) {\n rootDead.add(id);\n depths.set(id, 0);\n }\n }\n\n // BFS from root dead symbols to compute cascade depth\n const queue = [...rootDead].map((id) => ({ id, depth: 0 }));\n const visited = new Set(rootDead);\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n for (const edge of graph.getForwardEdges(current.id)) {\n if (deadIds.has(edge.to) && !visited.has(edge.to) && candidateIds.has(edge.to)) {\n const newDepth = current.depth + 1;\n depths.set(edge.to, newDepth);\n visited.add(edge.to);\n queue.push({ id: edge.to, depth: newDepth });\n }\n }\n }\n\n return depths;\n }\n\n private detectScaffolding(sourceContents?: Map<string, string>): ScaffoldingEntry[] {\n if (!sourceContents || sourceContents.size === 0) return [];\n\n const results: ScaffoldingEntry[] = [];\n\n for (const [file, content] of sourceContents) {\n // Skip test/config files\n if (this.matchesAny(file, TEST_PATTERNS) || this.matchesAny(file, CONFIG_PATTERNS)) continue;\n\n const lines = content.split('\\n');\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!;\n for (const { regex, pattern } of SCAFFOLDING_PATTERNS) {\n if (regex.test(line)) {\n results.push({\n file,\n line: i + 1,\n pattern,\n text: line.trim().slice(0, 120),\n });\n break; // One match per line\n }\n }\n }\n }\n\n return results;\n }\n\n private matchesAny(value: string, patterns: RegExp[]): boolean {\n return patterns.some((p) => p.test(value));\n }\n}\n","import type { SymbolGraph } from '../graph/symbol-graph.js';\n\nexport interface PageRankEntry {\n readonly symbolId: string;\n readonly name: string;\n readonly kind: string;\n readonly file: string;\n readonly score: number;\n readonly inDegree: number;\n readonly outDegree: number;\n}\n\nexport interface PageRankResult {\n readonly rankings: PageRankEntry[];\n readonly totalSymbols: number;\n readonly iterations: number;\n readonly converged: boolean;\n}\n\nexport interface PageRankOptions {\n readonly damping?: number;\n readonly maxIterations?: number;\n readonly tolerance?: number;\n readonly limit?: number;\n}\n\nconst DEFAULT_DAMPING = 0.85;\nconst DEFAULT_MAX_ITERATIONS = 100;\nconst DEFAULT_TOLERANCE = 1e-6;\nconst DEFAULT_LIMIT = 25;\n\nexport class PageRankCalculator {\n calculate(graph: SymbolGraph, options: PageRankOptions = {}): PageRankResult {\n const damping = options.damping ?? DEFAULT_DAMPING;\n const maxIterations = options.maxIterations ?? DEFAULT_MAX_ITERATIONS;\n const tolerance = options.tolerance ?? DEFAULT_TOLERANCE;\n const limit = options.limit ?? DEFAULT_LIMIT;\n\n const nodes = graph.allNodes();\n const n = nodes.length;\n\n if (n === 0) {\n return { rankings: [], totalSymbols: 0, iterations: 0, converged: true };\n }\n\n // Initialize scores uniformly\n const scores = new Map<string, number>();\n const initialScore = 1 / n;\n for (const node of nodes) {\n scores.set(node.symbolId, initialScore);\n }\n\n // Precompute out-degree for each node\n const outDegree = new Map<string, number>();\n for (const node of nodes) {\n outDegree.set(node.symbolId, graph.getForwardEdges(node.symbolId).length);\n }\n\n // Iterative PageRank\n // Edge direction: A->B means \"A depends on B\" (A imports/calls/uses B)\n // PageRank: B is important if many important nodes link to B\n // Score distribution: each node distributes its score via forward edges to dependencies\n // So we accumulate via reverse edges: for each node, sum score from nodes that point to it\n let iterations = 0;\n let converged = false;\n\n while (iterations < maxIterations) {\n const newScores = new Map<string, number>();\n const base = (1 - damping) / n;\n\n // Handle dangling nodes (no outgoing edges) — distribute their score evenly\n let danglingSum = 0;\n for (const node of nodes) {\n if ((outDegree.get(node.symbolId) ?? 0) === 0) {\n danglingSum += scores.get(node.symbolId) ?? 0;\n }\n }\n const danglingContrib = damping * danglingSum / n;\n\n for (const node of nodes) {\n let incomingScore = 0;\n\n // Sum contributions from all nodes that have a forward edge TO this node\n // (i.e., reverse edges of this node)\n const reverseEdges = graph.getReverseEdges(node.symbolId);\n const contributors = new Set<string>();\n for (const edge of reverseEdges) {\n if (contributors.has(edge.from)) continue; // deduplicate multi-edges\n contributors.add(edge.from);\n const fromScore = scores.get(edge.from) ?? 0;\n const fromOut = outDegree.get(edge.from) ?? 1;\n incomingScore += fromScore / fromOut;\n }\n\n newScores.set(node.symbolId, base + damping * incomingScore + danglingContrib);\n }\n\n // Check convergence\n let maxDelta = 0;\n for (const node of nodes) {\n const delta = Math.abs((newScores.get(node.symbolId) ?? 0) - (scores.get(node.symbolId) ?? 0));\n if (delta > maxDelta) maxDelta = delta;\n }\n\n // Update scores\n for (const [id, score] of newScores) {\n scores.set(id, score);\n }\n\n iterations++;\n\n if (maxDelta < tolerance) {\n converged = true;\n break;\n }\n }\n\n // Build ranked results\n const entries: PageRankEntry[] = nodes.map((node) => ({\n symbolId: node.symbolId,\n name: node.name,\n kind: node.kind,\n file: node.symbolId.split('::')[0] ?? '',\n score: Math.round((scores.get(node.symbolId) ?? 0) * 1_000_000) / 1_000_000,\n inDegree: graph.getReverseEdges(node.symbolId).length,\n outDegree: outDegree.get(node.symbolId) ?? 0,\n }));\n\n // Sort by score descending\n entries.sort((a, b) => b.score - a.score);\n\n return {\n rankings: entries.slice(0, limit),\n totalSymbols: n,\n iterations,\n converged,\n };\n }\n}\n","import type { CoChangeEntry, CoChangeMatrix, FileIndex } from '../types.js';\n\nconst MIN_SHARED_COMMITS = 2;\nconst MIN_FREQUENCY = 0.1;\n\nexport function aggregateCoChanges(indices: readonly FileIndex[]): CoChangeMatrix {\n // Step 1: Build commit → files map\n const commitToFiles = new Map<string, Set<string>>();\n const fileCommitCounts = new Map<string, number>();\n\n for (const idx of indices) {\n const file = idx.file;\n let count = 0;\n for (const intent of idx.intent) {\n count++;\n let files = commitToFiles.get(intent.hash);\n if (!files) {\n files = new Set();\n commitToFiles.set(intent.hash, files);\n }\n files.add(file);\n }\n if (count > 0) {\n fileCommitCounts.set(file, count);\n }\n }\n\n // Step 2: Count shared commits per file pair\n const pairCounts = new Map<string, number>();\n for (const files of commitToFiles.values()) {\n if (files.size < 2) continue;\n const sorted = [...files].sort();\n for (let i = 0; i < sorted.length; i++) {\n for (let j = i + 1; j < sorted.length; j++) {\n const key = `${sorted[i]}|${sorted[j]}`;\n pairCounts.set(key, (pairCounts.get(key) ?? 0) + 1);\n }\n }\n }\n\n // Step 3: Calculate frequency and filter\n const entries: CoChangeEntry[] = [];\n for (const [key, sharedCommits] of pairCounts) {\n if (sharedCommits < MIN_SHARED_COMMITS) continue;\n\n const [file1, file2] = key.split('|') as [string, string];\n const count1 = fileCommitCounts.get(file1) ?? 0;\n const count2 = fileCommitCounts.get(file2) ?? 0;\n const minCount = Math.min(count1, count2);\n if (minCount === 0) continue;\n\n const frequency = Math.round((sharedCommits / minCount) * 1000) / 1000;\n if (frequency < MIN_FREQUENCY) continue;\n\n entries.push({ file1, file2, sharedCommits, frequency });\n }\n\n entries.sort((a, b) => b.frequency - a.frequency || b.sharedCommits - a.sharedCommits);\n\n return {\n version: 1,\n timestamp: Math.floor(Date.now() / 1000),\n entries,\n };\n}\n\nexport function loadCoChangeMap(matrix: CoChangeMatrix): Map<string, CoChangeEntry[]> {\n const map = new Map<string, CoChangeEntry[]>();\n for (const entry of matrix.entries) {\n let list1 = map.get(entry.file1);\n if (!list1) { list1 = []; map.set(entry.file1, list1); }\n list1.push(entry);\n\n let list2 = map.get(entry.file2);\n if (!list2) { list2 = []; map.set(entry.file2, list2); }\n list2.push(entry);\n }\n return map;\n}\n"],"mappings":"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","names":["log","log"]}