@anhnguyen02/catlas 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +59 -0
  2. package/dist/assembler/context-assembler.d.ts +17 -0
  3. package/dist/assembler/context-assembler.d.ts.map +1 -0
  4. package/dist/assembler/context-assembler.js +18 -0
  5. package/dist/assembler/context-assembler.js.map +1 -0
  6. package/dist/cli.d.ts +3 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +113 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/detector/project-detector.d.ts +10 -0
  11. package/dist/detector/project-detector.d.ts.map +1 -0
  12. package/dist/detector/project-detector.js +171 -0
  13. package/dist/detector/project-detector.js.map +1 -0
  14. package/dist/exporter/markdown.d.ts +5 -0
  15. package/dist/exporter/markdown.d.ts.map +1 -0
  16. package/dist/exporter/markdown.js +106 -0
  17. package/dist/exporter/markdown.js.map +1 -0
  18. package/dist/extractor/ctags-runner.d.ts +8 -0
  19. package/dist/extractor/ctags-runner.d.ts.map +1 -0
  20. package/dist/extractor/ctags-runner.js +99 -0
  21. package/dist/extractor/ctags-runner.js.map +1 -0
  22. package/dist/extractor/import-graph.d.ts +6 -0
  23. package/dist/extractor/import-graph.d.ts.map +1 -0
  24. package/dist/extractor/import-graph.js +127 -0
  25. package/dist/extractor/import-graph.js.map +1 -0
  26. package/dist/extractor/module-graph.d.ts +16 -0
  27. package/dist/extractor/module-graph.d.ts.map +1 -0
  28. package/dist/extractor/module-graph.js +108 -0
  29. package/dist/extractor/module-graph.js.map +1 -0
  30. package/dist/ranker/priority-ranker.d.ts +15 -0
  31. package/dist/ranker/priority-ranker.d.ts.map +1 -0
  32. package/dist/ranker/priority-ranker.js +49 -0
  33. package/dist/ranker/priority-ranker.js.map +1 -0
  34. package/dist/scanner/file-classifier.d.ts +9 -0
  35. package/dist/scanner/file-classifier.d.ts.map +1 -0
  36. package/dist/scanner/file-classifier.js +21 -0
  37. package/dist/scanner/file-classifier.js.map +1 -0
  38. package/dist/scanner/role-detector.d.ts +9 -0
  39. package/dist/scanner/role-detector.d.ts.map +1 -0
  40. package/dist/scanner/role-detector.js +39 -0
  41. package/dist/scanner/role-detector.js.map +1 -0
  42. package/dist/scanner/tree-builder.d.ts +8 -0
  43. package/dist/scanner/tree-builder.d.ts.map +1 -0
  44. package/dist/scanner/tree-builder.js +13 -0
  45. package/dist/scanner/tree-builder.js.map +1 -0
  46. package/dist/scanner/tree-walker.d.ts +7 -0
  47. package/dist/scanner/tree-walker.d.ts.map +1 -0
  48. package/dist/scanner/tree-walker.js +43 -0
  49. package/dist/scanner/tree-walker.js.map +1 -0
  50. package/dist/types.d.ts +34 -0
  51. package/dist/types.d.ts.map +1 -0
  52. package/dist/types.js +2 -0
  53. package/dist/types.js.map +1 -0
  54. package/dist/utils/cache.d.ts +18 -0
  55. package/dist/utils/cache.d.ts.map +1 -0
  56. package/dist/utils/cache.js +50 -0
  57. package/dist/utils/cache.js.map +1 -0
  58. package/dist/utils/diff.d.ts +10 -0
  59. package/dist/utils/diff.d.ts.map +1 -0
  60. package/dist/utils/diff.js +43 -0
  61. package/dist/utils/diff.js.map +1 -0
  62. package/dist/utils/gitignore.d.ts +9 -0
  63. package/dist/utils/gitignore.d.ts.map +1 -0
  64. package/dist/utils/gitignore.js +35 -0
  65. package/dist/utils/gitignore.js.map +1 -0
  66. package/dist/utils/logger.d.ts +7 -0
  67. package/dist/utils/logger.d.ts.map +1 -0
  68. package/dist/utils/logger.js +33 -0
  69. package/dist/utils/logger.js.map +1 -0
  70. package/package.json +51 -0
@@ -0,0 +1,99 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import os from 'node:os';
3
+ import fs from 'node:fs';
4
+ import { log } from '../utils/logger.js';
5
+ export class CtagsRunner {
6
+ ctagsPath;
7
+ constructor() {
8
+ this.ctagsPath = this.findCtags();
9
+ }
10
+ findCtags() {
11
+ // macOS Homebrew paths (ARM and Intel)
12
+ const brewPaths = ['/opt/homebrew/bin/ctags', '/usr/local/bin/ctags'];
13
+ if (os.platform() === 'darwin') {
14
+ for (const p of brewPaths) {
15
+ if (fs.existsSync(p))
16
+ return p;
17
+ }
18
+ }
19
+ return 'ctags';
20
+ }
21
+ extract(fileDataList, rootDir) {
22
+ const files = fileDataList.map(f => f.path);
23
+ if (files.length === 0)
24
+ return;
25
+ // Run ctags:
26
+ // --output-format=json: Easy to parse
27
+ // --fields=+nKZS: n=line, K=long kind, Z=scope, S=signature
28
+ // Language-specific kinds to extract
29
+ const baseArgs = [
30
+ '--output-format=json',
31
+ '--fields=+nKZS',
32
+ '--kinds-TypeScript=fcima', // function, class, interface, method, alias
33
+ '--kinds-JavaScript=fcm', // function, class, method
34
+ '--kinds-Python=fcm', // function, class, method
35
+ '--kinds-Go=fstim', // function, struct, type, interface, method
36
+ '--kinds-Rust=fstPim', // function, struct, type, implementation, method
37
+ '--kinds-Java=cim', // class, interface, method
38
+ '--kinds-Kotlin=cim' // class, interface, method
39
+ ];
40
+ const CHUNK_SIZE = 200;
41
+ const lines = [];
42
+ for (let i = 0; i < files.length; i += CHUNK_SIZE) {
43
+ const chunk = files.slice(i, i + CHUNK_SIZE);
44
+ const args = [...baseArgs, ...chunk];
45
+ const result = spawnSync(this.ctagsPath, args, { cwd: rootDir, encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024 });
46
+ if (result.error || result.status !== 0) {
47
+ const errMsg = result.error ? result.error.message : `Process exited with status ${result.status}`;
48
+ log('warn', `Failed to run ctags cleanly. Error: ${errMsg}`);
49
+ if (result.error)
50
+ return; // stop if totally missing
51
+ }
52
+ lines.push(...result.stdout.split('\n').filter(l => l.trim()));
53
+ }
54
+ // Group symbols by file path
55
+ const symbolsByFile = new Map();
56
+ for (const line of lines) {
57
+ if (!line.startsWith('{'))
58
+ continue;
59
+ try {
60
+ const tag = JSON.parse(line);
61
+ // Skip local scope variables/functions if possible (ctags 'scope' field helps)
62
+ // If it has a "scope" and scopeKind is "function", it's a local closure/variable. Skip.
63
+ if (tag.scopeKind === 'function' || tag.scopeKind === 'method') {
64
+ continue;
65
+ }
66
+ // Safely determine kind
67
+ const validKinds = ['function', 'class', 'interface', 'type', 'variable', 'method', 'component', 'alias', 'constant', 'unknown'];
68
+ const kind = validKinds.includes(tag.kind) ? tag.kind : 'unknown';
69
+ // Basic heuristic for isExported (ctags may provide access: 'public' or roles string)
70
+ const isExported = tag.access === 'public' || (tag.roles && tag.roles.includes('exported')) || false;
71
+ const sym = {
72
+ name: tag.name,
73
+ kind,
74
+ signature: tag.signature || '',
75
+ line: tag.line || 0,
76
+ isExported,
77
+ fileRole: 'unknown',
78
+ path: tag.path,
79
+ inDegree: 0
80
+ };
81
+ if (!symbolsByFile.has(tag.path)) {
82
+ symbolsByFile.set(tag.path, []);
83
+ }
84
+ symbolsByFile.get(tag.path).push(sym);
85
+ }
86
+ catch (e) {
87
+ // ignore malformed json lines
88
+ }
89
+ }
90
+ // Attach back to FileData
91
+ for (const fd of fileDataList) {
92
+ const syms = symbolsByFile.get(fd.path) || [];
93
+ // set file role to the symbol
94
+ syms.forEach(s => s.fileRole = fd.role);
95
+ fd.symbols = syms;
96
+ }
97
+ }
98
+ }
99
+ //# sourceMappingURL=ctags-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ctags-runner.js","sourceRoot":"","sources":["../../src/extractor/ctags-runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { FileData } from '../types.js';
2
+ export declare class ImportGraph {
3
+ build(fileDataList: FileData[], language: string, rootDir: string): void;
4
+ private parseImports;
5
+ }
6
+ //# sourceMappingURL=import-graph.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import-graph.d.ts","sourceRoot":"","sources":["../../src/extractor/import-graph.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAE3C,qBAAa,WAAW;IACf,KAAK,CAAC,YAAY,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IA+E/E,OAAO,CAAC,YAAY;CAiDrB"}
@@ -0,0 +1,127 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ export class ImportGraph {
4
+ build(fileDataList, language, rootDir) {
5
+ const importMap = new Map(); // path -> array of imported relative paths
6
+ const inDegreeMap = new Map();
7
+ // Initialize inDegree map for all known files
8
+ fileDataList.forEach(fd => inDegreeMap.set(fd.path, 0));
9
+ // Pre-calculate stripped paths to avoid regex inside the loop
10
+ const strippedPaths = fileDataList.map(f => ({
11
+ path: f.path,
12
+ stripped: f.path.replace(/\.[a-z]+$/, '')
13
+ }));
14
+ // Fast exact match lookup
15
+ const exactLookup = new Map();
16
+ for (const f of strippedPaths) {
17
+ exactLookup.set(f.stripped, f.path);
18
+ exactLookup.set(`${f.stripped}/index`, f.path);
19
+ }
20
+ // 1. Parse imports
21
+ for (const fd of fileDataList) {
22
+ const content = fs.readFileSync(path.join(rootDir, fd.path), 'utf-8');
23
+ const imports = this.parseImports(content, language);
24
+ fd.imports = imports;
25
+ const dir = path.dirname(fd.path);
26
+ const resolvedImports = [];
27
+ for (const imp of imports) {
28
+ let resolvedPath = '';
29
+ let isAbsolute = false;
30
+ if (imp.startsWith('.')) {
31
+ let normalizedImp = imp;
32
+ if (language === 'python' && imp.match(/^\.+[a-zA-Z0-9_]/)) {
33
+ normalizedImp = imp.replace(/^(\.+)(.*)$/, (m, dots, rest) => {
34
+ if (dots.length === 1)
35
+ return `./${rest}`;
36
+ return '../'.repeat(dots.length - 1) + rest;
37
+ });
38
+ }
39
+ resolvedPath = path.posix.join(dir, normalizedImp).replace(/\\/g, '/');
40
+ }
41
+ else if (imp.startsWith('~/') || imp.startsWith('@/')) {
42
+ resolvedPath = imp.substring(2);
43
+ }
44
+ else {
45
+ // absolute or node_module. Ex: python domain.models
46
+ resolvedPath = imp.replace(/\./g, '/');
47
+ isAbsolute = true;
48
+ }
49
+ // Drop extensions or try to match exactly
50
+ const rp = resolvedPath.replace(/\.[a-z]+$/, '');
51
+ let matchPath = exactLookup.get(rp);
52
+ if (!matchPath && isAbsolute) {
53
+ // Fallback to suffix matching for absolute imports (e.g. Python imports missing src/ prefix)
54
+ const target = strippedPaths.find(f => {
55
+ return f.stripped.endsWith(`/${rp}`) || f.stripped.endsWith(`/${rp}/index`);
56
+ });
57
+ if (target)
58
+ matchPath = target.path;
59
+ }
60
+ if (matchPath) {
61
+ resolvedImports.push(matchPath);
62
+ // Increment in-degree
63
+ inDegreeMap.set(matchPath, (inDegreeMap.get(matchPath) || 0) + 1);
64
+ }
65
+ }
66
+ importMap.set(fd.path, resolvedImports);
67
+ }
68
+ // 2. Attach inDegree
69
+ for (const fd of fileDataList) {
70
+ fd.inDegree = inDegreeMap.get(fd.path) || 0;
71
+ }
72
+ }
73
+ parseImports(rawContent, language) {
74
+ // Strip comments to prevent false positive regex matches on strings/comments
75
+ const content = rawContent.replace(/\/\*[\s\S]*?\*\/|\/\/.*|#.*/g, '');
76
+ const imports = [];
77
+ if (language === 'typescript' || language === 'javascript') {
78
+ const es6Regex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/gs;
79
+ const exportRegex = /export\s+.*?\s+from\s+['"]([^'"]+)['"]/gs;
80
+ // side-effect imports: import 'y'
81
+ const sideEffectRegex = /import\s+['"]([^'"]+)['"]/g;
82
+ // dynamic imports: import('y')
83
+ const dynRegex = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
84
+ let match;
85
+ while ((match = es6Regex.exec(content)) !== null)
86
+ imports.push(match[1]);
87
+ while ((match = exportRegex.exec(content)) !== null)
88
+ imports.push(match[1]);
89
+ while ((match = sideEffectRegex.exec(content)) !== null)
90
+ imports.push(match[1]);
91
+ while ((match = dynRegex.exec(content)) !== null)
92
+ imports.push(match[1]);
93
+ }
94
+ else if (language === 'python') {
95
+ // from x import y
96
+ const pyRegex1 = /from\s+([^\s]+)\s+import/g;
97
+ // import x (only at start of line or after semicolon, to avoid matching 'from x import y')
98
+ const pyRegex2 = /(?:^|[\n;])\s*import\s+([^\r\n]+)/g;
99
+ let match;
100
+ while ((match = pyRegex1.exec(content)) !== null)
101
+ imports.push(match[1]);
102
+ while ((match = pyRegex2.exec(content)) !== null) {
103
+ const modules = match[1].split(',').map(m => m.trim().split(/\s+as\s+/)[0].trim());
104
+ imports.push(...modules);
105
+ }
106
+ }
107
+ else if (language === 'go') {
108
+ // Single import: import "fmt"
109
+ const goSingleRegex = /import\s+"([^"]+)"/g;
110
+ // Grouped import: import ( "fmt"\n "net/http" )
111
+ const goGroupRegex = /import\s*\(\s*([\s\S]*?)\)/g;
112
+ let match;
113
+ while ((match = goSingleRegex.exec(content)) !== null)
114
+ imports.push(match[1]);
115
+ while ((match = goGroupRegex.exec(content)) !== null) {
116
+ const block = match[1];
117
+ const lineRegex = /"([^"]+)"/g;
118
+ let lineMatch;
119
+ while ((lineMatch = lineRegex.exec(block)) !== null)
120
+ imports.push(lineMatch[1]);
121
+ }
122
+ }
123
+ // Ensure uniqueness
124
+ return [...new Set(imports)];
125
+ }
126
+ }
127
+ //# sourceMappingURL=import-graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import-graph.js","sourceRoot":"","sources":["../../src/extractor/import-graph.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { FileData, ModuleNode } from '../types.js';
2
+ export declare class ModuleGraph {
3
+ /**
4
+ * Build a module-level (directory-level) dependency graph.
5
+ * Aggregates file-level import relationships into directory-level dependencies.
6
+ * This shows the high-level architecture: which modules depend on which.
7
+ */
8
+ build(fileDataList: FileData[]): ModuleNode[];
9
+ /**
10
+ * Get the module directory for a file path.
11
+ * Uses first 2 segments as grouping key (e.g., "server/api" for "server/api/payment.ts").
12
+ * Root-level files get "." as their module.
13
+ */
14
+ private getModuleDir;
15
+ }
16
+ //# sourceMappingURL=module-graph.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-graph.d.ts","sourceRoot":"","sources":["../../src/extractor/module-graph.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAEvD,qBAAa,WAAW;IACtB;;;;OAIG;IACI,KAAK,CAAC,YAAY,EAAE,QAAQ,EAAE,GAAG,UAAU,EAAE;IAqGpD;;;;OAIG;IACH,OAAO,CAAC,YAAY;CAMrB"}
@@ -0,0 +1,108 @@
1
+ import path from 'node:path';
2
+ export class ModuleGraph {
3
+ /**
4
+ * Build a module-level (directory-level) dependency graph.
5
+ * Aggregates file-level import relationships into directory-level dependencies.
6
+ * This shows the high-level architecture: which modules depend on which.
7
+ */
8
+ build(fileDataList) {
9
+ // Map: module path → set of modules it depends on
10
+ const dependsOnMap = new Map();
11
+ // Map: module path → file count
12
+ const fileCountMap = new Map();
13
+ // 1. Count files per module and collect dependencies
14
+ for (const fd of fileDataList) {
15
+ const moduleDir = this.getModuleDir(fd.path);
16
+ fileCountMap.set(moduleDir, (fileCountMap.get(moduleDir) || 0) + 1);
17
+ if (!dependsOnMap.has(moduleDir)) {
18
+ dependsOnMap.set(moduleDir, new Set());
19
+ }
20
+ }
21
+ // 1.5 Pre-calculate lookups for fast import resolution
22
+ const strippedPaths = fileDataList.map(f => ({
23
+ path: f.path,
24
+ stripped: f.path.replace(/\.[a-z]+$/, '')
25
+ }));
26
+ const exactLookup = new Map();
27
+ for (const f of strippedPaths) {
28
+ exactLookup.set(f.stripped, f.path);
29
+ exactLookup.set(`${f.stripped}/index`, f.path);
30
+ }
31
+ // 2. Resolve imports to module-level dependencies
32
+ for (const fd of fileDataList) {
33
+ const sourceModule = this.getModuleDir(fd.path);
34
+ const deps = dependsOnMap.get(sourceModule);
35
+ for (const imp of fd.imports) {
36
+ let resolvedPath = '';
37
+ let isAbsolute = false;
38
+ if (imp.startsWith('.')) {
39
+ resolvedPath = path.posix.join(path.dirname(fd.path), imp).replace(/\\/g, '/');
40
+ }
41
+ else if (imp.startsWith('~/') || imp.startsWith('@/')) {
42
+ resolvedPath = imp.substring(2);
43
+ }
44
+ else {
45
+ resolvedPath = imp.replace(/\./g, '/');
46
+ isAbsolute = true;
47
+ }
48
+ const rp = resolvedPath.replace(/\.[a-z]+$/, '');
49
+ let targetPath = exactLookup.get(rp);
50
+ if (!targetPath && isAbsolute) {
51
+ const target = strippedPaths.find(f => f.stripped.endsWith(`/${rp}`) || f.stripped.endsWith(`/${rp}/index`));
52
+ if (target)
53
+ targetPath = target.path;
54
+ }
55
+ if (targetPath) {
56
+ const targetModule = this.getModuleDir(targetPath);
57
+ // Skip self-references (same module)
58
+ if (targetModule !== sourceModule) {
59
+ deps.add(targetModule);
60
+ }
61
+ }
62
+ }
63
+ }
64
+ // 3. Build reverse map (dependedBy)
65
+ const dependedByMap = new Map();
66
+ for (const moduleDir of dependsOnMap.keys()) {
67
+ dependedByMap.set(moduleDir, new Set());
68
+ }
69
+ for (const [moduleDir, deps] of dependsOnMap) {
70
+ for (const dep of deps) {
71
+ if (!dependedByMap.has(dep)) {
72
+ dependedByMap.set(dep, new Set());
73
+ }
74
+ dependedByMap.get(dep).add(moduleDir);
75
+ }
76
+ }
77
+ // 4. Build ModuleNode list (only modules with dependencies)
78
+ const nodes = [];
79
+ for (const [moduleDir, deps] of dependsOnMap) {
80
+ const dependedBy = dependedByMap.get(moduleDir) || new Set();
81
+ // Only include modules that have relationships
82
+ if (deps.size > 0 || dependedBy.size > 0) {
83
+ nodes.push({
84
+ path: moduleDir,
85
+ fileCount: fileCountMap.get(moduleDir) || 0,
86
+ dependsOn: [...deps].sort(),
87
+ dependedBy: [...dependedBy].sort(),
88
+ });
89
+ }
90
+ }
91
+ // Sort by most depended-on first
92
+ nodes.sort((a, b) => b.dependedBy.length - a.dependedBy.length);
93
+ return nodes;
94
+ }
95
+ /**
96
+ * Get the module directory for a file path.
97
+ * Uses first 2 segments as grouping key (e.g., "server/api" for "server/api/payment.ts").
98
+ * Root-level files get "." as their module.
99
+ */
100
+ getModuleDir(filePath) {
101
+ const dir = path.dirname(filePath);
102
+ if (dir === '.')
103
+ return '.';
104
+ const segments = dir.split('/');
105
+ return segments.length <= 2 ? dir : segments.slice(0, 2).join('/');
106
+ }
107
+ }
108
+ //# sourceMappingURL=module-graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-graph.js","sourceRoot":"","sources":["../../src/extractor/module-graph.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAA;AAG5B,MAAM,OAAO,WAAW;IACtB;;;;OAIG;IACI,KAAK,CAAC,YAAwB;QACnC,kDAAkD;QAClD,MAAM,YAAY,GAAG,IAAI,GAAG,EAAuB,CAAA;QACnD,gCAAgC;QAChC,MAAM,YAAY,GAAG,IAAI,GAAG,EAAkB,CAAA;QAE9C,qDAAqD;QACrD,KAAK,MAAM,EAAE,IAAI,YAAY,EAAE,CAAC;YAC9B,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,IAAI,CAAC,CAAA;YAC5C,YAAY,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;YAEnE,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;gBACjC,YAAY,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,GAAG,EAAE,CAAC,CAAA;YACxC,CAAC;QACH,CAAC;QAED,uDAAuD;QACvD,MAAM,aAAa,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC3C,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;SAC1C,CAAC,CAAC,CAAA;QAEH,MAAM,WAAW,GAAG,IAAI,GAAG,EAAkB,CAAA;QAC7C,KAAK,MAAM,CAAC,IAAI,aAAa,EAAE,CAAC;YAC9B,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAA;YACnC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAA;QAChD,CAAC;QAED,kDAAkD;QAClD,KAAK,MAAM,EAAE,IAAI,YAAY,EAAE,CAAC;YAC9B,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,IAAI,CAAC,CAAA;YAC/C,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,YAAY,CAAE,CAAA;YAE5C,KAAK,MAAM,GAAG,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC;gBAC7B,IAAI,YAAY,GAAG,EAAE,CAAA;gBACrB,IAAI,UAAU,GAAG,KAAK,CAAA;gBAEtB,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;oBACxB,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;gBAChF,CAAC;qBAAM,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;oBACxD,YAAY,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;gBACjC,CAAC;qBAAM,CAAC;oBACN,YAAY,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;oBACtC,UAAU,GAAG,IAAI,CAAA;gBACnB,CAAC;gBAED,MAAM,EAAE,GAAG,YAAY,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;gBAEhD,IAAI,UAAU,GAAuB,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;gBAExD,IAAI,CAAC,UAAU,IAAI,UAAU,EAAE,CAAC;oBAC9B,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAA;oBAC5G,IAAI,MAAM;wBAAE,UAAU,GAAG,MAAM,CAAC,IAAI,CAAA;gBACtC,CAAC;gBAED,IAAI,UAAU,EAAE,CAAC;oBACf,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,CAAA;oBAClD,qCAAqC;oBACrC,IAAI,YAAY,KAAK,YAAY,EAAE,CAAC;wBAClC,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;oBACxB,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,oCAAoC;QACpC,MAAM,aAAa,GAAG,IAAI,GAAG,EAAuB,CAAA;QACpD,KAAK,MAAM,SAAS,IAAI,YAAY,CAAC,IAAI,EAAE,EAAE,CAAC;YAC5C,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,GAAG,EAAE,CAAC,CAAA;QACzC,CAAC;QAED,KAAK,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,YAAY,EAAE,CAAC;YAC7C,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;oBAC5B,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,GAAG,EAAE,CAAC,CAAA;gBACnC,CAAC;gBACD,aAAa,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;YACxC,CAAC;QACH,CAAC;QAED,4DAA4D;QAC5D,MAAM,KAAK,GAAiB,EAAE,CAAA;QAC9B,KAAK,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,YAAY,EAAE,CAAC;YAC7C,MAAM,UAAU,GAAG,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,IAAI,GAAG,EAAE,CAAA;YAC5D,+CAA+C;YAC/C,IAAI,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,UAAU,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;gBACzC,KAAK,CAAC,IAAI,CAAC;oBACT,IAAI,EAAE,SAAS;oBACf,SAAS,EAAE,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC;oBAC3C,SAAS,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,EAAE;oBAC3B,UAAU,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,IAAI,EAAE;iBACnC,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;QAED,iCAAiC;QACjC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAA;QAE/D,OAAO,KAAK,CAAA;IACd,CAAC;IAED;;;;OAIG;IACK,YAAY,CAAC,QAAgB;QACnC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;QAClC,IAAI,GAAG,KAAK,GAAG;YAAE,OAAO,GAAG,CAAA;QAC3B,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAC/B,OAAO,QAAQ,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACpE,CAAC;CACF"}
@@ -0,0 +1,15 @@
1
+ import type { FileData, ExtractedSymbol } from '../types.js';
2
+ export declare class PriorityRanker {
3
+ private readonly FILE_BUDGET;
4
+ private readonly SYMBOL_BUDGET;
5
+ /**
6
+ * Rank and return the top most-imported files.
7
+ */
8
+ rankFiles(fileDataList: FileData[]): FileData[];
9
+ /**
10
+ * Rank and return the top most-impactful symbols.
11
+ * Focuses on exported functions, classes, interfaces from high-impact files.
12
+ */
13
+ rankSymbols(fileDataList: FileData[]): ExtractedSymbol[];
14
+ }
15
+ //# sourceMappingURL=priority-ranker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"priority-ranker.d.ts","sourceRoot":"","sources":["../../src/ranker/priority-ranker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE5D,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAK;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAM;IAEpC;;OAEG;IACI,SAAS,CAAC,YAAY,EAAE,QAAQ,EAAE,GAAG,QAAQ,EAAE;IAOtD;;;OAGG;IACI,WAAW,CAAC,YAAY,EAAE,QAAQ,EAAE,GAAG,eAAe,EAAE;CAkChE"}
@@ -0,0 +1,49 @@
1
+ export class PriorityRanker {
2
+ FILE_BUDGET = 30;
3
+ SYMBOL_BUDGET = 100;
4
+ /**
5
+ * Rank and return the top most-imported files.
6
+ */
7
+ rankFiles(fileDataList) {
8
+ return [...fileDataList]
9
+ .filter(fd => fd.inDegree >= 1)
10
+ .sort((a, b) => b.inDegree - a.inDegree)
11
+ .slice(0, this.FILE_BUDGET);
12
+ }
13
+ /**
14
+ * Rank and return the top most-impactful symbols.
15
+ * Focuses on exported functions, classes, interfaces from high-impact files.
16
+ */
17
+ rankSymbols(fileDataList) {
18
+ const keptSymbols = [];
19
+ for (const fd of fileDataList) {
20
+ // Keep files with at least 1 importer, or api-route handlers (always kept)
21
+ const isExempt = fd.role === 'api-route' || fd.role === 'entry';
22
+ if (!isExempt && fd.inDegree < 1)
23
+ continue;
24
+ for (const sym of fd.symbols) {
25
+ // Only keep high-value symbol kinds
26
+ const validKinds = ['function', 'class', 'interface', 'type', 'method', 'alias', 'component'];
27
+ if (!validKinds.includes(sym.kind) && sym.kind !== 'constant' && sym.kind !== 'variable') {
28
+ continue;
29
+ }
30
+ // Skip noise
31
+ if (sym.name.startsWith('_') || sym.name.toLowerCase().includes('helper')) {
32
+ continue;
33
+ }
34
+ const clonedSym = { ...sym, inDegree: fd.inDegree };
35
+ keptSymbols.push({ sym: clonedSym, inDegree: fd.inDegree, isExempt });
36
+ }
37
+ }
38
+ // Sort: exempt (api-routes) first, then by in-degree descending
39
+ keptSymbols.sort((a, b) => {
40
+ if (a.isExempt && !b.isExempt)
41
+ return -1;
42
+ if (!a.isExempt && b.isExempt)
43
+ return 1;
44
+ return b.inDegree - a.inDegree;
45
+ });
46
+ return keptSymbols.slice(0, this.SYMBOL_BUDGET).map(item => item.sym);
47
+ }
48
+ }
49
+ //# sourceMappingURL=priority-ranker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"priority-ranker.js","sourceRoot":"","sources":["../../src/ranker/priority-ranker.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,cAAc;IACR,WAAW,GAAG,EAAE,CAAA;IAChB,aAAa,GAAG,GAAG,CAAA;IAEpC;;OAEG;IACI,SAAS,CAAC,YAAwB;QACvC,OAAO,CAAC,GAAG,YAAY,CAAC;aACrB,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,QAAQ,IAAI,CAAC,CAAC;aAC9B,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC;aACvC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,CAAA;IAC/B,CAAC;IAED;;;OAGG;IACI,WAAW,CAAC,YAAwB;QACzC,MAAM,WAAW,GAAyE,EAAE,CAAA;QAE5F,KAAK,MAAM,EAAE,IAAI,YAAY,EAAE,CAAC;YAC9B,2EAA2E;YAC3E,MAAM,QAAQ,GAAG,EAAE,CAAC,IAAI,KAAK,WAAW,IAAI,EAAE,CAAC,IAAI,KAAK,OAAO,CAAA;YAC/D,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,QAAQ,GAAG,CAAC;gBAAE,SAAQ;YAE1C,KAAK,MAAM,GAAG,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC;gBAC7B,oCAAoC;gBACpC,MAAM,UAAU,GAAG,CAAC,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,CAAC,CAAA;gBAC7F,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,KAAK,UAAU,IAAI,GAAG,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;oBACzF,SAAQ;gBACV,CAAC;gBAED,aAAa;gBACb,IAAI,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAC1E,SAAQ;gBACV,CAAC;gBAED,MAAM,SAAS,GAAG,EAAE,GAAG,GAAG,EAAE,QAAQ,EAAE,EAAE,CAAC,QAAQ,EAAE,CAAA;gBACnD,WAAW,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAA;YACvE,CAAC;QACH,CAAC;QAED,gEAAgE;QAChE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YACxB,IAAI,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC,QAAQ;gBAAE,OAAO,CAAC,CAAC,CAAA;YACxC,IAAI,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,QAAQ;gBAAE,OAAO,CAAC,CAAA;YACvC,OAAO,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAA;QAChC,CAAC,CAAC,CAAA;QAEF,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACvE,CAAC;CACF"}
@@ -0,0 +1,9 @@
1
+ import type { FileRole, ProjectProfile } from '../types.js';
2
+ export declare class FileClassifier {
3
+ private matchers;
4
+ constructor(profile: ProjectProfile);
5
+ classify(filePath: string): {
6
+ role: FileRole;
7
+ };
8
+ }
9
+ //# sourceMappingURL=file-classifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-classifier.d.ts","sourceRoot":"","sources":["../../src/scanner/file-classifier.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAE3D,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAuE;gBAE3E,OAAO,EAAE,cAAc;IAS5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,QAAQ,CAAA;KAAE;CAQtD"}
@@ -0,0 +1,21 @@
1
+ import picomatch from 'picomatch';
2
+ export class FileClassifier {
3
+ matchers = [];
4
+ constructor(profile) {
5
+ for (const [pattern, role] of Object.entries(profile.rolePatterns)) {
6
+ this.matchers.push({
7
+ matcher: picomatch(pattern),
8
+ role
9
+ });
10
+ }
11
+ }
12
+ classify(filePath) {
13
+ for (const { matcher, role } of this.matchers) {
14
+ if (matcher(filePath)) {
15
+ return { role };
16
+ }
17
+ }
18
+ return { role: 'unknown' };
19
+ }
20
+ }
21
+ //# sourceMappingURL=file-classifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-classifier.js","sourceRoot":"","sources":["../../src/scanner/file-classifier.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,WAAW,CAAA;AAGjC,MAAM,OAAO,cAAc;IACjB,QAAQ,GAAqE,EAAE,CAAA;IAEvF,YAAY,OAAuB;QACjC,KAAK,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;YACnE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;gBACjB,OAAO,EAAE,SAAS,CAAC,OAAO,CAAC;gBAC3B,IAAI;aACL,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAEM,QAAQ,CAAC,QAAgB;QAC9B,KAAK,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC9C,IAAI,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACtB,OAAO,EAAE,IAAI,EAAE,CAAA;YACjB,CAAC;QACH,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAA;IAC5B,CAAC;CACF"}
@@ -0,0 +1,9 @@
1
+ import type { FileRole } from '../types.js';
2
+ export declare class RoleDetector {
3
+ /**
4
+ * Classify a file's role based on its path using generic heuristics.
5
+ * Works for any language/framework — no hardcoded convention files.
6
+ */
7
+ classify(filePath: string): FileRole;
8
+ }
9
+ //# sourceMappingURL=role-detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"role-detector.d.ts","sourceRoot":"","sources":["../../src/scanner/role-detector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AA2B3C,qBAAa,YAAY;IACvB;;;OAGG;IACI,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ;CAQ5C"}
@@ -0,0 +1,39 @@
1
+ const ROLE_PATTERNS = [
2
+ // Entry points
3
+ { test: /^(app|main|index)\.(ts|js|tsx|jsx|vue|py|go|rs|kt|java)$/, role: 'entry' },
4
+ // Config files
5
+ { test: /\.config\.(ts|js|mjs|cjs)$/, role: 'config' },
6
+ { test: /^(tsconfig|vite\.config|nuxt\.config|next\.config|webpack\.config|jest\.config|vitest\.config)/, role: 'config' },
7
+ { test: /^config\//, role: 'config' },
8
+ // API routes / handlers
9
+ { test: /\b(api|routes|endpoints|handlers|controllers)\b\/.*\.(ts|js|tsx|py|go|rs|kt|java)$/, role: 'api-route' },
10
+ // Pages / views
11
+ { test: /\b(pages|views)\b\//, role: 'page' },
12
+ // Components
13
+ { test: /\b(components)\b\//, role: 'component' },
14
+ // Services / business logic
15
+ { test: /\b(services|common|domain|usecases|use-cases)\b\//, role: 'service' },
16
+ // Composables / hooks
17
+ { test: /\b(composables|hooks)\b\//, role: 'composable' },
18
+ // Repository / data layer
19
+ { test: /\b(repositories|repos|models|entities|dal|database|db)\b\//, role: 'repository' },
20
+ // Internal
21
+ { test: /\b(internal|__internal__|private)\b\//, role: 'internal' },
22
+ // Utilities
23
+ { test: /\b(utils|helpers|lib|shared|pkg|support)\b\//, role: 'util' },
24
+ ];
25
+ export class RoleDetector {
26
+ /**
27
+ * Classify a file's role based on its path using generic heuristics.
28
+ * Works for any language/framework — no hardcoded convention files.
29
+ */
30
+ classify(filePath) {
31
+ for (const { test, role } of ROLE_PATTERNS) {
32
+ if (test.test(filePath)) {
33
+ return role;
34
+ }
35
+ }
36
+ return 'unknown';
37
+ }
38
+ }
39
+ //# sourceMappingURL=role-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"role-detector.js","sourceRoot":"","sources":["../../src/scanner/role-detector.ts"],"names":[],"mappings":"AAEA,MAAM,aAAa,GAA4C;IAC7D,eAAe;IACf,EAAE,IAAI,EAAE,0DAA0D,EAAE,IAAI,EAAE,OAAO,EAAE;IACnF,eAAe;IACf,EAAE,IAAI,EAAE,4BAA4B,EAAE,IAAI,EAAE,QAAQ,EAAE;IACtD,EAAE,IAAI,EAAE,gGAAgG,EAAE,IAAI,EAAE,QAAQ,EAAE;IAC1H,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,EAAE;IACrC,wBAAwB;IACxB,EAAE,IAAI,EAAE,oFAAoF,EAAE,IAAI,EAAE,WAAW,EAAE;IACjH,gBAAgB;IAChB,EAAE,IAAI,EAAE,qBAAqB,EAAE,IAAI,EAAE,MAAM,EAAE;IAC7C,aAAa;IACb,EAAE,IAAI,EAAE,oBAAoB,EAAE,IAAI,EAAE,WAAW,EAAE;IACjD,4BAA4B;IAC5B,EAAE,IAAI,EAAE,mDAAmD,EAAE,IAAI,EAAE,SAAS,EAAE;IAC9E,sBAAsB;IACtB,EAAE,IAAI,EAAE,2BAA2B,EAAE,IAAI,EAAE,YAAY,EAAE;IACzD,0BAA0B;IAC1B,EAAE,IAAI,EAAE,4DAA4D,EAAE,IAAI,EAAE,YAAY,EAAE;IAC1F,WAAW;IACX,EAAE,IAAI,EAAE,uCAAuC,EAAE,IAAI,EAAE,UAAU,EAAE;IACnE,YAAY;IACZ,EAAE,IAAI,EAAE,8CAA8C,EAAE,IAAI,EAAE,MAAM,EAAE;CACvE,CAAA;AAED,MAAM,OAAO,YAAY;IACvB;;;OAGG;IACI,QAAQ,CAAC,QAAgB;QAC9B,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,aAAa,EAAE,CAAC;YAC3C,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACxB,OAAO,IAAI,CAAA;YACb,CAAC;QACH,CAAC;QACD,OAAO,SAAS,CAAA;IAClB,CAAC;CACF"}
@@ -0,0 +1,8 @@
1
+ export declare class TreeBuilder {
2
+ /**
3
+ * Build a complete, unrolled list of all file paths.
4
+ * This provides an exact "X-Ray" mapping so AI agents can target files precisely.
5
+ */
6
+ build(filePaths: string[]): string;
7
+ }
8
+ //# sourceMappingURL=tree-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree-builder.d.ts","sourceRoot":"","sources":["../../src/scanner/tree-builder.ts"],"names":[],"mappings":"AAEA,qBAAa,WAAW;IACtB;;;OAGG;IACI,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM;CAM1C"}
@@ -0,0 +1,13 @@
1
+ export class TreeBuilder {
2
+ /**
3
+ * Build a complete, unrolled list of all file paths.
4
+ * This provides an exact "X-Ray" mapping so AI agents can target files precisely.
5
+ */
6
+ build(filePaths) {
7
+ return filePaths
8
+ .slice()
9
+ .sort((a, b) => a.localeCompare(b))
10
+ .join('\n');
11
+ }
12
+ }
13
+ //# sourceMappingURL=tree-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree-builder.js","sourceRoot":"","sources":["../../src/scanner/tree-builder.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,WAAW;IACtB;;;OAGG;IACI,KAAK,CAAC,SAAmB;QAC9B,OAAO,SAAS;aACb,KAAK,EAAE;aACP,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;aAClC,IAAI,CAAC,IAAI,CAAC,CAAA;IACf,CAAC;CACF"}
@@ -0,0 +1,7 @@
1
+ export declare class TreeWalker {
2
+ private ig;
3
+ private rootDir;
4
+ constructor(rootDir: string);
5
+ walk(dir?: string): string[];
6
+ }
7
+ //# sourceMappingURL=tree-walker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree-walker.d.ts","sourceRoot":"","sources":["../../src/scanner/tree-walker.ts"],"names":[],"mappings":"AAIA,qBAAa,UAAU;IACrB,OAAO,CAAC,EAAE,CAAW;IACrB,OAAO,CAAC,OAAO,CAAQ;gBAEX,OAAO,EAAE,MAAM;IAYpB,IAAI,CAAC,GAAG,GAAE,MAAqB,GAAG,MAAM,EAAE;CA8BlD"}