@kodus/kodus-graph 0.2.8 → 0.2.10

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.
Files changed (171) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +252 -0
  3. package/dist/analysis/blast-radius.d.ts +2 -0
  4. package/dist/analysis/blast-radius.js +55 -0
  5. package/dist/analysis/communities.d.ts +28 -0
  6. package/dist/analysis/communities.js +100 -0
  7. package/dist/analysis/context-builder.d.ts +34 -0
  8. package/dist/analysis/context-builder.js +92 -0
  9. package/dist/analysis/diff.d.ts +41 -0
  10. package/dist/analysis/diff.js +155 -0
  11. package/dist/analysis/enrich.d.ts +5 -0
  12. package/dist/analysis/enrich.js +126 -0
  13. package/dist/analysis/flows.d.ts +27 -0
  14. package/dist/analysis/flows.js +86 -0
  15. package/dist/analysis/inheritance.d.ts +3 -0
  16. package/dist/analysis/inheritance.js +31 -0
  17. package/dist/analysis/prompt-formatter.d.ts +2 -0
  18. package/dist/analysis/prompt-formatter.js +173 -0
  19. package/dist/analysis/risk-score.d.ts +4 -0
  20. package/dist/analysis/risk-score.js +51 -0
  21. package/dist/analysis/search.d.ts +11 -0
  22. package/dist/analysis/search.js +64 -0
  23. package/dist/analysis/test-gaps.d.ts +2 -0
  24. package/dist/analysis/test-gaps.js +14 -0
  25. package/dist/cli.d.ts +2 -0
  26. package/dist/cli.js +210 -0
  27. package/dist/commands/analyze.d.ts +9 -0
  28. package/dist/commands/analyze.js +116 -0
  29. package/dist/commands/communities.d.ts +8 -0
  30. package/dist/commands/communities.js +9 -0
  31. package/dist/commands/context.d.ts +12 -0
  32. package/dist/commands/context.js +130 -0
  33. package/dist/commands/diff.d.ts +9 -0
  34. package/dist/commands/diff.js +89 -0
  35. package/dist/commands/flows.d.ts +8 -0
  36. package/dist/commands/flows.js +9 -0
  37. package/dist/commands/parse.d.ts +11 -0
  38. package/dist/commands/parse.js +101 -0
  39. package/dist/commands/search.d.ts +12 -0
  40. package/dist/commands/search.js +27 -0
  41. package/dist/commands/update.d.ts +7 -0
  42. package/dist/commands/update.js +154 -0
  43. package/dist/graph/builder.d.ts +6 -0
  44. package/dist/graph/builder.js +248 -0
  45. package/dist/graph/edges.d.ts +23 -0
  46. package/dist/graph/edges.js +159 -0
  47. package/dist/graph/json-writer.d.ts +9 -0
  48. package/dist/graph/json-writer.js +38 -0
  49. package/dist/graph/loader.d.ts +13 -0
  50. package/dist/graph/loader.js +101 -0
  51. package/dist/graph/merger.d.ts +7 -0
  52. package/dist/graph/merger.js +18 -0
  53. package/dist/graph/types.d.ts +252 -0
  54. package/dist/graph/types.js +1 -0
  55. package/dist/parser/batch.d.ts +5 -0
  56. package/dist/parser/batch.js +93 -0
  57. package/dist/parser/discovery.d.ts +7 -0
  58. package/dist/parser/discovery.js +61 -0
  59. package/dist/parser/extractor.d.ts +4 -0
  60. package/dist/parser/extractor.js +33 -0
  61. package/dist/parser/extractors/generic.d.ts +8 -0
  62. package/dist/parser/extractors/generic.js +471 -0
  63. package/dist/parser/extractors/python.d.ts +8 -0
  64. package/dist/parser/extractors/python.js +133 -0
  65. package/dist/parser/extractors/ruby.d.ts +8 -0
  66. package/dist/parser/extractors/ruby.js +153 -0
  67. package/dist/parser/extractors/typescript.d.ts +10 -0
  68. package/dist/parser/extractors/typescript.js +365 -0
  69. package/dist/parser/languages.d.ts +32 -0
  70. package/dist/parser/languages.js +304 -0
  71. package/dist/resolver/call-resolver.d.ts +36 -0
  72. package/dist/resolver/call-resolver.js +178 -0
  73. package/dist/resolver/external-detector.d.ts +11 -0
  74. package/dist/resolver/external-detector.js +820 -0
  75. package/dist/resolver/fs-cache.d.ts +8 -0
  76. package/dist/resolver/fs-cache.js +36 -0
  77. package/dist/resolver/import-map.d.ts +12 -0
  78. package/dist/resolver/import-map.js +21 -0
  79. package/dist/resolver/import-resolver.d.ts +19 -0
  80. package/dist/resolver/import-resolver.js +310 -0
  81. package/dist/resolver/languages/csharp.d.ts +3 -0
  82. package/dist/resolver/languages/csharp.js +94 -0
  83. package/dist/resolver/languages/go.d.ts +3 -0
  84. package/dist/resolver/languages/go.js +197 -0
  85. package/dist/resolver/languages/java.d.ts +1 -0
  86. package/dist/resolver/languages/java.js +193 -0
  87. package/dist/resolver/languages/php.d.ts +3 -0
  88. package/dist/resolver/languages/php.js +75 -0
  89. package/dist/resolver/languages/python.d.ts +11 -0
  90. package/dist/resolver/languages/python.js +127 -0
  91. package/dist/resolver/languages/ruby.d.ts +24 -0
  92. package/dist/resolver/languages/ruby.js +110 -0
  93. package/dist/resolver/languages/rust.d.ts +1 -0
  94. package/dist/resolver/languages/rust.js +197 -0
  95. package/dist/resolver/languages/typescript.d.ts +35 -0
  96. package/dist/resolver/languages/typescript.js +416 -0
  97. package/dist/resolver/re-export-resolver.d.ts +24 -0
  98. package/dist/resolver/re-export-resolver.js +57 -0
  99. package/dist/resolver/symbol-table.d.ts +17 -0
  100. package/dist/resolver/symbol-table.js +60 -0
  101. package/dist/shared/extract-calls.d.ts +26 -0
  102. package/dist/shared/extract-calls.js +57 -0
  103. package/dist/shared/file-hash.d.ts +3 -0
  104. package/dist/shared/file-hash.js +10 -0
  105. package/dist/shared/filters.d.ts +3 -0
  106. package/dist/shared/filters.js +240 -0
  107. package/dist/shared/logger.d.ts +6 -0
  108. package/dist/shared/logger.js +17 -0
  109. package/dist/shared/qualified-name.d.ts +1 -0
  110. package/dist/shared/qualified-name.js +9 -0
  111. package/dist/shared/safe-path.d.ts +6 -0
  112. package/dist/shared/safe-path.js +29 -0
  113. package/dist/shared/schemas.d.ts +43 -0
  114. package/dist/shared/schemas.js +30 -0
  115. package/dist/shared/temp.d.ts +11 -0
  116. package/{src/shared/temp.ts → dist/shared/temp.js} +4 -5
  117. package/package.json +20 -6
  118. package/src/analysis/blast-radius.ts +0 -54
  119. package/src/analysis/communities.ts +0 -135
  120. package/src/analysis/context-builder.ts +0 -130
  121. package/src/analysis/diff.ts +0 -169
  122. package/src/analysis/enrich.ts +0 -110
  123. package/src/analysis/flows.ts +0 -112
  124. package/src/analysis/inheritance.ts +0 -34
  125. package/src/analysis/prompt-formatter.ts +0 -175
  126. package/src/analysis/risk-score.ts +0 -62
  127. package/src/analysis/search.ts +0 -76
  128. package/src/analysis/test-gaps.ts +0 -21
  129. package/src/cli.ts +0 -210
  130. package/src/commands/analyze.ts +0 -128
  131. package/src/commands/communities.ts +0 -19
  132. package/src/commands/context.ts +0 -182
  133. package/src/commands/diff.ts +0 -96
  134. package/src/commands/flows.ts +0 -19
  135. package/src/commands/parse.ts +0 -124
  136. package/src/commands/search.ts +0 -41
  137. package/src/commands/update.ts +0 -166
  138. package/src/graph/builder.ts +0 -209
  139. package/src/graph/edges.ts +0 -101
  140. package/src/graph/json-writer.ts +0 -43
  141. package/src/graph/loader.ts +0 -113
  142. package/src/graph/merger.ts +0 -25
  143. package/src/graph/types.ts +0 -283
  144. package/src/parser/batch.ts +0 -82
  145. package/src/parser/discovery.ts +0 -75
  146. package/src/parser/extractor.ts +0 -37
  147. package/src/parser/extractors/generic.ts +0 -132
  148. package/src/parser/extractors/python.ts +0 -133
  149. package/src/parser/extractors/ruby.ts +0 -147
  150. package/src/parser/extractors/typescript.ts +0 -350
  151. package/src/parser/languages.ts +0 -122
  152. package/src/resolver/call-resolver.ts +0 -244
  153. package/src/resolver/import-map.ts +0 -27
  154. package/src/resolver/import-resolver.ts +0 -72
  155. package/src/resolver/languages/csharp.ts +0 -7
  156. package/src/resolver/languages/go.ts +0 -7
  157. package/src/resolver/languages/java.ts +0 -7
  158. package/src/resolver/languages/php.ts +0 -7
  159. package/src/resolver/languages/python.ts +0 -35
  160. package/src/resolver/languages/ruby.ts +0 -21
  161. package/src/resolver/languages/rust.ts +0 -7
  162. package/src/resolver/languages/typescript.ts +0 -168
  163. package/src/resolver/re-export-resolver.ts +0 -66
  164. package/src/resolver/symbol-table.ts +0 -67
  165. package/src/shared/extract-calls.ts +0 -75
  166. package/src/shared/file-hash.ts +0 -12
  167. package/src/shared/filters.ts +0 -243
  168. package/src/shared/logger.ts +0 -17
  169. package/src/shared/qualified-name.ts +0 -5
  170. package/src/shared/safe-path.ts +0 -31
  171. package/src/shared/schemas.ts +0 -32
@@ -1,168 +0,0 @@
1
- /**
2
- * TypeScript/JavaScript import resolver.
3
- *
4
- * Handles:
5
- * - Relative imports with extension probing (.ts, .tsx, .js, .jsx)
6
- * - ESM .js → .ts remapping
7
- * - Directory index files
8
- * - tsconfig path aliases
9
- */
10
-
11
- import { existsSync, readFileSync } from 'fs';
12
- import { dirname, join, resolve as resolvePath } from 'path';
13
- import { log } from '../../shared/logger';
14
-
15
- const TS_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx'];
16
-
17
- /**
18
- * Resolve a TypeScript/JavaScript relative import to an absolute file path.
19
- * Returns null for non-relative (external package) imports.
20
- */
21
- export function resolve(fromAbsFile: string, modulePath: string, _repoRoot: string): string | null {
22
- if (!modulePath.startsWith('.')) return null;
23
-
24
- let base = join(dirname(fromAbsFile), modulePath);
25
-
26
- // ESM convention: .js in import -> .ts on disk
27
- if (modulePath.endsWith('.js')) base = base.slice(0, -3);
28
-
29
- // Try direct with extension
30
- for (const ext of TS_EXTENSIONS) {
31
- const candidate = base + ext;
32
- if (existsSync(candidate)) return resolvePath(candidate);
33
- }
34
-
35
- // Try index file in directory
36
- for (const ext of TS_EXTENSIONS) {
37
- const candidate = join(base, `index${ext}`);
38
- if (existsSync(candidate)) return resolvePath(candidate);
39
- }
40
-
41
- return null;
42
- }
43
-
44
- /**
45
- * Strip comments and trailing commas from JSON (tsconfig-compatible).
46
- * Handles strings correctly -- won't strip // inside "url://..." etc.
47
- */
48
- function stripJsonComments(str: string): string {
49
- let result = '';
50
- let i = 0;
51
- const len = str.length;
52
-
53
- while (i < len) {
54
- // String literal -- copy as-is
55
- if (str[i] === '"') {
56
- let j = i + 1;
57
- while (j < len && str[j] !== '"') {
58
- if (str[j] === '\\') j++; // skip escaped char
59
- j++;
60
- }
61
- result += str.substring(i, j + 1);
62
- i = j + 1;
63
- continue;
64
- }
65
-
66
- // Single-line comment
67
- if (str[i] === '/' && str[i + 1] === '/') {
68
- while (i < len && str[i] !== '\n') i++;
69
- continue;
70
- }
71
-
72
- // Block comment
73
- if (str[i] === '/' && str[i + 1] === '*') {
74
- i += 2;
75
- while (i < len && !(str[i] === '*' && str[i + 1] === '/')) i++;
76
- i += 2;
77
- continue;
78
- }
79
-
80
- // Trailing comma: comma followed by optional whitespace + closing bracket
81
- if (str[i] === ',') {
82
- let j = i + 1;
83
- while (j < len && (str[j] === ' ' || str[j] === '\t' || str[j] === '\n' || str[j] === '\r')) j++;
84
- if (str[j] === '}' || str[j] === ']') {
85
- i++;
86
- continue;
87
- }
88
- }
89
-
90
- result += str[i];
91
- i++;
92
- }
93
-
94
- return result;
95
- }
96
-
97
- /**
98
- * Load and parse tsconfig.json path aliases.
99
- *
100
- * Tries tsconfig.json first, then tsconfig.base.json.
101
- * Converts alias patterns like "@libs/*" into prefix → resolved dirs.
102
- */
103
- export function loadTsconfigAliases(repoRoot: string): Map<string, string[]> {
104
- const aliases = new Map<string, string[]>();
105
-
106
- for (const filename of ['tsconfig.json', 'tsconfig.base.json']) {
107
- const tsconfigPath = join(repoRoot, filename);
108
- if (!existsSync(tsconfigPath)) continue;
109
-
110
- try {
111
- const content = readFileSync(tsconfigPath, 'utf-8');
112
- const cleaned = stripJsonComments(content);
113
- const config = JSON.parse(cleaned);
114
- const paths = config?.compilerOptions?.paths;
115
- const baseUrl = config?.compilerOptions?.baseUrl || '.';
116
- const baseDir = join(repoRoot, baseUrl);
117
-
118
- if (paths) {
119
- for (const [alias, targets] of Object.entries(paths)) {
120
- // Convert alias pattern: "@libs/*" -> prefix "@libs/"
121
- const prefix = alias.replace('/*', '/').replace('*', '');
122
- const resolvedTargets = (targets as string[]).map((t) => {
123
- const targetPath = t.replace('/*', '').replace('*', '');
124
- return join(baseDir, targetPath);
125
- });
126
- aliases.set(prefix, resolvedTargets);
127
- }
128
- }
129
- } catch (err) {
130
- log.warn('Failed to parse tsconfig', { file: tsconfigPath, error: String(err) });
131
- }
132
- }
133
-
134
- return aliases;
135
- }
136
-
137
- /**
138
- * Resolve an import path using tsconfig aliases.
139
- *
140
- * Tries each alias prefix, and for matches, probes extensions and index files.
141
- */
142
- export function resolveWithAliases(
143
- modulePath: string,
144
- aliases: Map<string, string[]>,
145
- _repoRoot: string,
146
- ): string | null {
147
- for (const [prefix, targets] of aliases) {
148
- if (modulePath.startsWith(prefix)) {
149
- const rest = modulePath.slice(prefix.length);
150
-
151
- for (const targetBase of targets) {
152
- const base = join(targetBase, rest);
153
-
154
- for (const ext of TS_EXTENSIONS) {
155
- if (existsSync(base + ext)) return resolvePath(base + ext);
156
- }
157
- for (const ext of TS_EXTENSIONS) {
158
- const idx = join(base, `index${ext}`);
159
- if (existsSync(idx)) return resolvePath(idx);
160
- }
161
- // Try exact match (for directories with index)
162
- if (existsSync(base)) return resolvePath(base);
163
- }
164
- }
165
- }
166
-
167
- return null;
168
- }
@@ -1,66 +0,0 @@
1
- /**
2
- * Re-export (barrel) resolver.
3
- *
4
- * Follows `export { X } from './module'` chains so that the import map
5
- * points to the file where the symbol is actually defined, not the
6
- * barrel index file.
7
- *
8
- * Without this, an import like `import { Foo } from '@lib'` resolves
9
- * to `@lib/index.ts`, but `Foo` is defined in `@lib/foo.ts`.
10
- * The call resolver can't find `Foo` in the barrel's symbol table and
11
- * falls to lower-confidence tiers.
12
- */
13
-
14
- import { relative, resolve } from 'path';
15
- import { resolveImport } from './import-resolver';
16
-
17
- interface RawReExport {
18
- module: string;
19
- file: string;
20
- line: number;
21
- }
22
-
23
- /**
24
- * Build a map: barrel file (relative) → list of resolved re-export target files (relative).
25
- *
26
- * Follows one level of re-exports (covers >95% of real-world barrel patterns).
27
- */
28
- export function buildReExportMap(
29
- reExports: RawReExport[],
30
- repoDir: string,
31
- tsconfigAliases?: Map<string, string[]>,
32
- ): Map<string, string[]> {
33
- const barrelMap = new Map<string, string[]>();
34
-
35
- for (const re of reExports) {
36
- const absFrom = resolve(repoDir, re.file);
37
- const resolved = resolveImport(absFrom, re.module, 'typescript', repoDir, tsconfigAliases);
38
- if (!resolved) continue;
39
-
40
- const resolvedRel = relative(repoDir, resolved);
41
- const list = barrelMap.get(re.file);
42
- if (list) {
43
- if (!list.includes(resolvedRel)) list.push(resolvedRel);
44
- } else {
45
- barrelMap.set(re.file, [resolvedRel]);
46
- }
47
- }
48
-
49
- // Follow one extra level: if a re-export target is itself a barrel, flatten
50
- for (const [barrel, targets] of barrelMap) {
51
- const extra: string[] = [];
52
- for (const target of targets) {
53
- const nested = barrelMap.get(target);
54
- if (nested) {
55
- for (const n of nested) {
56
- if (n !== barrel && !targets.includes(n) && !extra.includes(n)) {
57
- extra.push(n);
58
- }
59
- }
60
- }
61
- }
62
- if (extra.length > 0) targets.push(...extra);
63
- }
64
-
65
- return barrelMap;
66
- }
@@ -1,67 +0,0 @@
1
- /**
2
- * Symbol table with dual-index lookup (GitNexus pattern).
3
- *
4
- * Provides both exact (file + name) and global (name-only) lookups.
5
- * The exact lookup is high-confidence; global lookup is lower-confidence
6
- * but useful when import resolution fails.
7
- */
8
-
9
- export interface SymbolTable {
10
- add(file: string, name: string, qualified: string): void;
11
- lookupExact(file: string, name: string): string | null;
12
- lookupInFile(file: string, name: string, className: string): string | null;
13
- isUnique(name: string): boolean;
14
- lookupGlobal(name: string): string[];
15
- readonly size: number;
16
- readonly fileCount: number;
17
- }
18
-
19
- export function createSymbolTable(): SymbolTable {
20
- const byFile = new Map<string, Map<string, string[]>>();
21
- const byName = new Map<string, string[]>();
22
-
23
- return {
24
- add(file, name, qualified) {
25
- if (!byFile.has(file)) byFile.set(file, new Map());
26
- const fileMap = byFile.get(file)!;
27
- if (!fileMap.has(name)) fileMap.set(name, []);
28
- fileMap.get(name)!.push(qualified);
29
-
30
- if (!byName.has(name)) byName.set(name, []);
31
- byName.get(name)!.push(qualified);
32
- },
33
-
34
- lookupExact(file, name) {
35
- const candidates = byFile.get(file)?.get(name);
36
- if (!candidates || candidates.length === 0) return null;
37
- // Only return if unambiguous within this file
38
- return candidates.length === 1 ? candidates[0] : null;
39
- },
40
-
41
- lookupInFile(file, name, className) {
42
- const candidates = byFile.get(file)?.get(name);
43
- if (!candidates || candidates.length === 0) return null;
44
- return candidates.find((q) => q.includes(`::${className}.${name}`)) ?? null;
45
- },
46
-
47
- isUnique(name) {
48
- return (byName.get(name)?.length ?? 0) === 1;
49
- },
50
-
51
- lookupGlobal(name) {
52
- return byName.get(name) ?? [];
53
- },
54
-
55
- get size() {
56
- let count = 0;
57
- for (const m of byFile.values()) {
58
- for (const arr of m.values()) count += arr.length;
59
- }
60
- return count;
61
- },
62
-
63
- get fileCount() {
64
- return byFile.size;
65
- },
66
- };
67
- }
@@ -1,75 +0,0 @@
1
- import type { SgNode } from '@ast-grep/napi';
2
- import type { RawCallSite } from '../graph/types';
3
- import { NOISE } from './filters';
4
-
5
- /**
6
- * Language-specific configuration for call extraction.
7
- * Each language provides its self/super patterns and how to find class context in the AST.
8
- */
9
- export interface CallExtractionConfig {
10
- /** Prefixes indicating a self-reference (e.g., 'self.', 'this.') */
11
- selfPrefixes: string[];
12
- /** Prefixes indicating a super-reference (e.g., 'super().', 'super.', 'base.') */
13
- superPrefixes: string[];
14
- /** Find the enclosing class/module/impl node from a call site */
15
- findEnclosingClass: (node: SgNode) => SgNode | null;
16
- /** Extract the parent class name from a class node (for super resolution) */
17
- getParentClass?: (classNode: SgNode) => string | undefined;
18
- /** Skip this callee entirely (e.g., TS skips this.field.method — handled by DI) */
19
- skipCallee?: (callee: string) => boolean;
20
- }
21
-
22
- /**
23
- * Shared call extraction for all languages.
24
- *
25
- * Parses `$CALLEE($$$ARGS)` pattern, detects self/super references
26
- * based on language config, and populates resolveInClass for
27
- * class-aware resolution downstream.
28
- */
29
- export function extractCalls(
30
- rootNode: SgNode,
31
- fp: string,
32
- config: CallExtractionConfig,
33
- calls: RawCallSite[],
34
- ): void {
35
- for (const m of rootNode.findAll('$CALLEE($$$ARGS)')) {
36
- const callee = m.getMatch('CALLEE')?.text();
37
- if (!callee) continue;
38
- if (config.skipCallee?.(callee)) continue;
39
-
40
- const callName = callee.includes('.') ? callee.split('.').pop()! : callee;
41
- if (NOISE.has(callName)) continue;
42
-
43
- let resolveInClass: string | undefined;
44
-
45
- // Check self-reference: callee must be exactly `prefix + methodName` (no further chaining)
46
- for (const prefix of config.selfPrefixes) {
47
- if (!callee.startsWith(prefix)) continue;
48
- const rest = callee.substring(prefix.length);
49
- if (rest.includes('.')) break; // chained access (e.g., this.field.method) — not a self call
50
- const classNode = config.findEnclosingClass(m);
51
- resolveInClass = classNode?.field('name')?.text();
52
- break;
53
- }
54
-
55
- // Check super-reference if no self match
56
- if (!resolveInClass) {
57
- for (const prefix of config.superPrefixes) {
58
- const matches = callee === prefix || (callee.startsWith(prefix) && !callee.substring(prefix.length).includes('.'));
59
- if (!matches) continue;
60
- const classNode = config.findEnclosingClass(m);
61
- if (classNode && config.getParentClass) {
62
- resolveInClass = config.getParentClass(classNode);
63
- }
64
- break;
65
- }
66
- }
67
-
68
- calls.push({
69
- source: fp,
70
- callName,
71
- line: m.range().start.line,
72
- ...(resolveInClass ? { resolveInClass } : {}),
73
- });
74
- }
75
- }
@@ -1,12 +0,0 @@
1
- import { createHash } from 'crypto';
2
- import { readFileSync } from 'fs';
3
-
4
- export function computeFileHash(filePath: string): string {
5
- const content = readFileSync(filePath);
6
- return createHash('sha256').update(content).digest('hex');
7
- }
8
-
9
- /** Hash a node's source text (function body, class body, etc.) */
10
- export function computeContentHash(sourceText: string): string {
11
- return createHash('sha256').update(sourceText).digest('hex');
12
- }
@@ -1,243 +0,0 @@
1
- export const SKIP_DIRS = new Set([
2
- 'node_modules',
3
- '.git',
4
- 'dist',
5
- 'build',
6
- '.next',
7
- 'coverage',
8
- 'vendor',
9
- '__pycache__',
10
- '.venv',
11
- 'venv',
12
- 'target',
13
- '.turbo',
14
- '.cache',
15
- '.output',
16
- 'out',
17
- '.nuxt',
18
- '.svelte-kit',
19
- '.idea',
20
- '.mypy_cache',
21
- '.tox',
22
- '.pytest_cache',
23
- '.eggs',
24
- 'bower_components',
25
- ]);
26
-
27
- /** File name patterns to skip during discovery (minified, bundled, vendored) */
28
- const SKIP_FILE_PATTERNS: RegExp[] = [
29
- /\.min\.\w+$/, // *.min.js, *.min.css
30
- /[.-]bundle\.\w+$/, // *.bundle.js, *-bundle.js
31
- /\.chunk\.\w+$/, // *.chunk.js (webpack)
32
- /\.packed\.\w+$/, // *.packed.js
33
- ];
34
-
35
- export function isSkippableFile(fileName: string): boolean {
36
- return SKIP_FILE_PATTERNS.some((p) => p.test(fileName));
37
- }
38
-
39
- export const NOISE = new Set([
40
- // JS/TS builtins
41
- 'log',
42
- 'error',
43
- 'warn',
44
- 'info',
45
- 'debug',
46
- 'trace',
47
- 'push',
48
- 'pop',
49
- 'shift',
50
- 'unshift',
51
- 'splice',
52
- 'slice',
53
- 'map',
54
- 'filter',
55
- 'reduce',
56
- 'forEach',
57
- 'find',
58
- 'findIndex',
59
- 'some',
60
- 'every',
61
- 'flat',
62
- 'flatMap',
63
- 'sort',
64
- 'reverse',
65
- 'join',
66
- 'split',
67
- 'trim',
68
- 'replace',
69
- 'match',
70
- 'test',
71
- 'includes',
72
- 'indexOf',
73
- 'lastIndexOf',
74
- 'startsWith',
75
- 'endsWith',
76
- 'keys',
77
- 'values',
78
- 'entries',
79
- 'assign',
80
- 'freeze',
81
- 'create',
82
- 'stringify',
83
- 'parse',
84
- 'toString',
85
- 'toLowerCase',
86
- 'toUpperCase',
87
- 'concat',
88
- 'charAt',
89
- 'substring',
90
- 'parseInt',
91
- 'parseFloat',
92
- 'isNaN',
93
- 'isFinite',
94
- 'isArray',
95
- 'resolve',
96
- 'reject',
97
- 'all',
98
- 'allSettled',
99
- 'race',
100
- 'any',
101
- 'then',
102
- 'catch',
103
- 'finally',
104
- 'get',
105
- 'set',
106
- 'has',
107
- 'delete',
108
- 'clear',
109
- 'add',
110
- 'next',
111
- 'return',
112
- 'throw',
113
- 'setTimeout',
114
- 'clearTimeout',
115
- 'setInterval',
116
- 'clearInterval',
117
- 'require',
118
- 'length',
119
- 'call',
120
- 'apply',
121
- 'bind',
122
- 'createElement',
123
- 'useState',
124
- 'useEffect',
125
- 'useRef',
126
- 'useCallback',
127
- 'useMemo',
128
- 'useContext',
129
- 'useReducer',
130
- 'render',
131
- // Test helpers
132
- 'expect',
133
- 'toBe',
134
- 'toEqual',
135
- 'toBeDefined',
136
- 'toBeNull',
137
- 'toBeUndefined',
138
- 'toBeTruthy',
139
- 'toBeFalsy',
140
- 'toContain',
141
- 'toHaveLength',
142
- 'toThrow',
143
- 'toHaveBeenCalled',
144
- 'toHaveBeenCalledWith',
145
- 'toMatchObject',
146
- 'toHaveBeenCalledTimes',
147
- 'toHaveProperty',
148
- 'describe',
149
- 'it',
150
- 'test',
151
- 'beforeEach',
152
- 'afterEach',
153
- 'beforeAll',
154
- 'afterAll',
155
- 'fn',
156
- 'spyOn',
157
- 'mock',
158
- 'mockResolvedValue',
159
- 'mockReturnValue',
160
- 'mockImplementation',
161
- 'mockReturnThis',
162
- 'now',
163
- 'toISOString',
164
- 'getTime',
165
- // Globals
166
- 'console',
167
- 'Math',
168
- 'Date',
169
- 'JSON',
170
- 'Object',
171
- 'Array',
172
- 'String',
173
- 'Number',
174
- 'Boolean',
175
- 'Promise',
176
- 'Error',
177
- 'Map',
178
- 'Set',
179
- 'RegExp',
180
- 'Buffer',
181
- 'process',
182
- // Python builtins
183
- 'print',
184
- 'len',
185
- 'range',
186
- 'enumerate',
187
- 'zip',
188
- 'isinstance',
189
- 'type',
190
- 'super',
191
- 'self',
192
- 'cls',
193
- 'None',
194
- 'True',
195
- 'False',
196
- 'append',
197
- 'extend',
198
- 'insert',
199
- 'remove',
200
- 'update',
201
- 'items',
202
- 'format',
203
- 'strip',
204
- 'upper',
205
- 'lower',
206
- // Ruby builtins
207
- 'puts',
208
- 'raise',
209
- 'yield',
210
- 'each',
211
- 'do',
212
- 'end',
213
- 'attr_accessor',
214
- 'attr_reader',
215
- 'attr_writer',
216
- 'respond_to',
217
- 'render',
218
- 'redirect_to',
219
- 'before_action',
220
- 'after_action',
221
- 'validates',
222
- 'has_many',
223
- 'belongs_to',
224
- 'has_one',
225
- 'new',
226
- 'initialize',
227
- // Go builtins
228
- 'fmt',
229
- 'Println',
230
- 'Printf',
231
- 'Sprintf',
232
- 'Errorf',
233
- 'make',
234
- 'panic',
235
- 'recover',
236
- 'defer',
237
- // Java builtins
238
- 'System',
239
- 'println',
240
- 'equals',
241
- 'hashCode',
242
- 'getClass',
243
- ]);
@@ -1,17 +0,0 @@
1
- // src/shared/logger.ts
2
- export const log = {
3
- info(msg: string, ctx?: Record<string, unknown>): void {
4
- process.stderr.write(`[INFO] ${msg}${ctx ? ` ${JSON.stringify(ctx)}` : ''}\n`);
5
- },
6
- debug(msg: string, ctx?: Record<string, unknown>): void {
7
- if (process.env.KODUS_GRAPH_DEBUG) {
8
- process.stderr.write(`[DEBUG] ${msg}${ctx ? ` ${JSON.stringify(ctx)}` : ''}\n`);
9
- }
10
- },
11
- warn(msg: string, ctx?: Record<string, unknown>): void {
12
- process.stderr.write(`[WARN] ${msg}${ctx ? ` ${JSON.stringify(ctx)}` : ''}\n`);
13
- },
14
- error(msg: string, ctx?: Record<string, unknown>): void {
15
- process.stderr.write(`[ERROR] ${msg}${ctx ? ` ${JSON.stringify(ctx)}` : ''}\n`);
16
- },
17
- };
@@ -1,5 +0,0 @@
1
- export function qualifiedName(filePath: string, name: string, className?: string, isTest?: boolean): string {
2
- if (isTest) return `${filePath}::test:${name}`;
3
- if (className) return `${filePath}::${className}.${name}`;
4
- return `${filePath}::${name}`;
5
- }
@@ -1,31 +0,0 @@
1
- import { realpathSync } from 'fs';
2
- import { relative, resolve } from 'path';
3
-
4
- /**
5
- * Validate that a resolved path is within the repository root.
6
- * Returns the validated absolute path.
7
- * Throws if the path escapes the root.
8
- */
9
- export function ensureWithinRoot(filePath: string, repoRoot: string): string {
10
- let absRoot: string;
11
- try {
12
- absRoot = realpathSync(resolve(repoRoot));
13
- } catch {
14
- absRoot = resolve(repoRoot);
15
- }
16
-
17
- let absPath: string;
18
- try {
19
- absPath = realpathSync(resolve(absRoot, filePath));
20
- } catch {
21
- // File doesn't exist yet or is unreadable — use resolve without symlink follow
22
- absPath = resolve(absRoot, filePath);
23
- }
24
-
25
- const rel = relative(absRoot, absPath);
26
- if (rel.startsWith('..') || resolve(absRoot, rel) !== absPath) {
27
- throw new Error(`Path escapes repository root: ${filePath}`);
28
- }
29
-
30
- return absPath;
31
- }