gitnexus 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +181 -0
- package/dist/cli/ai-context.d.ts +21 -0
- package/dist/cli/ai-context.js +219 -0
- package/dist/cli/analyze.d.ts +10 -0
- package/dist/cli/analyze.js +118 -0
- package/dist/cli/clean.d.ts +8 -0
- package/dist/cli/clean.js +29 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +42 -0
- package/dist/cli/list.d.ts +6 -0
- package/dist/cli/list.js +27 -0
- package/dist/cli/mcp.d.ts +7 -0
- package/dist/cli/mcp.js +85 -0
- package/dist/cli/serve.d.ts +3 -0
- package/dist/cli/serve.js +5 -0
- package/dist/cli/status.d.ts +6 -0
- package/dist/cli/status.js +27 -0
- package/dist/config/ignore-service.d.ts +1 -0
- package/dist/config/ignore-service.js +208 -0
- package/dist/config/supported-languages.d.ts +11 -0
- package/dist/config/supported-languages.js +15 -0
- package/dist/core/embeddings/embedder.d.ts +60 -0
- package/dist/core/embeddings/embedder.js +205 -0
- package/dist/core/embeddings/embedding-pipeline.d.ts +50 -0
- package/dist/core/embeddings/embedding-pipeline.js +321 -0
- package/dist/core/embeddings/index.d.ts +9 -0
- package/dist/core/embeddings/index.js +9 -0
- package/dist/core/embeddings/text-generator.d.ts +24 -0
- package/dist/core/embeddings/text-generator.js +182 -0
- package/dist/core/embeddings/types.d.ts +87 -0
- package/dist/core/embeddings/types.js +32 -0
- package/dist/core/graph/graph.d.ts +2 -0
- package/dist/core/graph/graph.js +61 -0
- package/dist/core/graph/types.d.ts +50 -0
- package/dist/core/graph/types.js +1 -0
- package/dist/core/ingestion/ast-cache.d.ts +11 -0
- package/dist/core/ingestion/ast-cache.js +34 -0
- package/dist/core/ingestion/call-processor.d.ts +8 -0
- package/dist/core/ingestion/call-processor.js +269 -0
- package/dist/core/ingestion/cluster-enricher.d.ts +38 -0
- package/dist/core/ingestion/cluster-enricher.js +170 -0
- package/dist/core/ingestion/community-processor.d.ts +39 -0
- package/dist/core/ingestion/community-processor.js +269 -0
- package/dist/core/ingestion/entry-point-scoring.d.ts +39 -0
- package/dist/core/ingestion/entry-point-scoring.js +235 -0
- package/dist/core/ingestion/filesystem-walker.d.ts +5 -0
- package/dist/core/ingestion/filesystem-walker.js +26 -0
- package/dist/core/ingestion/framework-detection.d.ts +38 -0
- package/dist/core/ingestion/framework-detection.js +183 -0
- package/dist/core/ingestion/heritage-processor.d.ts +14 -0
- package/dist/core/ingestion/heritage-processor.js +134 -0
- package/dist/core/ingestion/import-processor.d.ts +8 -0
- package/dist/core/ingestion/import-processor.js +490 -0
- package/dist/core/ingestion/parsing-processor.d.ts +8 -0
- package/dist/core/ingestion/parsing-processor.js +249 -0
- package/dist/core/ingestion/pipeline.d.ts +2 -0
- package/dist/core/ingestion/pipeline.js +228 -0
- package/dist/core/ingestion/process-processor.d.ts +51 -0
- package/dist/core/ingestion/process-processor.js +278 -0
- package/dist/core/ingestion/structure-processor.d.ts +2 -0
- package/dist/core/ingestion/structure-processor.js +36 -0
- package/dist/core/ingestion/symbol-table.d.ts +33 -0
- package/dist/core/ingestion/symbol-table.js +38 -0
- package/dist/core/ingestion/tree-sitter-queries.d.ts +11 -0
- package/dist/core/ingestion/tree-sitter-queries.js +319 -0
- package/dist/core/ingestion/utils.d.ts +10 -0
- package/dist/core/ingestion/utils.js +44 -0
- package/dist/core/kuzu/csv-generator.d.ts +22 -0
- package/dist/core/kuzu/csv-generator.js +272 -0
- package/dist/core/kuzu/kuzu-adapter.d.ts +81 -0
- package/dist/core/kuzu/kuzu-adapter.js +568 -0
- package/dist/core/kuzu/schema.d.ts +53 -0
- package/dist/core/kuzu/schema.js +380 -0
- package/dist/core/search/bm25-index.d.ts +22 -0
- package/dist/core/search/bm25-index.js +52 -0
- package/dist/core/search/hybrid-search.d.ts +49 -0
- package/dist/core/search/hybrid-search.js +118 -0
- package/dist/core/tree-sitter/parser-loader.d.ts +4 -0
- package/dist/core/tree-sitter/parser-loader.js +42 -0
- package/dist/lib/utils.d.ts +1 -0
- package/dist/lib/utils.js +3 -0
- package/dist/mcp/core/embedder.d.ts +27 -0
- package/dist/mcp/core/embedder.js +93 -0
- package/dist/mcp/core/kuzu-adapter.d.ts +23 -0
- package/dist/mcp/core/kuzu-adapter.js +62 -0
- package/dist/mcp/local/local-backend.d.ts +73 -0
- package/dist/mcp/local/local-backend.js +752 -0
- package/dist/mcp/resources.d.ts +31 -0
- package/dist/mcp/resources.js +279 -0
- package/dist/mcp/server.d.ts +12 -0
- package/dist/mcp/server.js +130 -0
- package/dist/mcp/staleness.d.ts +15 -0
- package/dist/mcp/staleness.js +29 -0
- package/dist/mcp/tools.d.ts +24 -0
- package/dist/mcp/tools.js +160 -0
- package/dist/server/api.d.ts +6 -0
- package/dist/server/api.js +156 -0
- package/dist/storage/git.d.ts +7 -0
- package/dist/storage/git.js +39 -0
- package/dist/storage/repo-manager.d.ts +61 -0
- package/dist/storage/repo-manager.js +106 -0
- package/dist/types/pipeline.d.ts +28 -0
- package/dist/types/pipeline.js +16 -0
- package/package.json +80 -0
- package/skills/debugging.md +104 -0
- package/skills/exploring.md +112 -0
- package/skills/impact-analysis.md +114 -0
- package/skills/refactoring.md +119 -0
- package/vendor/leiden/index.cjs +355 -0
- package/vendor/leiden/utils.cjs +392 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entry Point Scoring
|
|
3
|
+
*
|
|
4
|
+
* Calculates entry point scores for process detection based on:
|
|
5
|
+
* 1. Call ratio (existing algorithm - callees / (callers + 1))
|
|
6
|
+
* 2. Export status (exported functions get higher priority)
|
|
7
|
+
* 3. Name patterns (functions matching entry point patterns like handle*, on*, *Controller)
|
|
8
|
+
* 4. Framework detection (path-based detection for Next.js, Express, Django, etc.)
|
|
9
|
+
*
|
|
10
|
+
* This module is language-agnostic - language-specific patterns are defined per language.
|
|
11
|
+
*/
|
|
12
|
+
import { detectFrameworkFromPath } from './framework-detection.js';
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// NAME PATTERNS - All 9 supported languages
|
|
15
|
+
// ============================================================================
|
|
16
|
+
/**
|
|
17
|
+
* Common entry point naming patterns by language
|
|
18
|
+
* These patterns indicate functions that are likely feature entry points
|
|
19
|
+
*/
|
|
20
|
+
const ENTRY_POINT_PATTERNS = {
|
|
21
|
+
// Universal patterns (apply to all languages)
|
|
22
|
+
'*': [
|
|
23
|
+
/^(main|init|bootstrap|start|run|setup|configure)$/i,
|
|
24
|
+
/^handle[A-Z]/, // handleLogin, handleSubmit
|
|
25
|
+
/^on[A-Z]/, // onClick, onSubmit
|
|
26
|
+
/Handler$/, // RequestHandler
|
|
27
|
+
/Controller$/, // UserController
|
|
28
|
+
/^process[A-Z]/, // processPayment
|
|
29
|
+
/^execute[A-Z]/, // executeQuery
|
|
30
|
+
/^perform[A-Z]/, // performAction
|
|
31
|
+
/^dispatch[A-Z]/, // dispatchEvent
|
|
32
|
+
/^trigger[A-Z]/, // triggerAction
|
|
33
|
+
/^fire[A-Z]/, // fireEvent
|
|
34
|
+
/^emit[A-Z]/, // emitEvent
|
|
35
|
+
],
|
|
36
|
+
// JavaScript/TypeScript
|
|
37
|
+
'javascript': [
|
|
38
|
+
/^use[A-Z]/, // React hooks (useEffect, etc.)
|
|
39
|
+
],
|
|
40
|
+
'typescript': [
|
|
41
|
+
/^use[A-Z]/, // React hooks
|
|
42
|
+
],
|
|
43
|
+
// Python
|
|
44
|
+
'python': [
|
|
45
|
+
/^app$/, // Flask/FastAPI app
|
|
46
|
+
/^(get|post|put|delete|patch)_/i, // REST conventions
|
|
47
|
+
/^api_/, // API functions
|
|
48
|
+
/^view_/, // Django views
|
|
49
|
+
],
|
|
50
|
+
// Java
|
|
51
|
+
'java': [
|
|
52
|
+
/^do[A-Z]/, // doGet, doPost (Servlets)
|
|
53
|
+
/^create[A-Z]/, // Factory patterns
|
|
54
|
+
/^build[A-Z]/, // Builder patterns
|
|
55
|
+
/Service$/, // UserService
|
|
56
|
+
],
|
|
57
|
+
// C#
|
|
58
|
+
'csharp': [
|
|
59
|
+
/^(Get|Post|Put|Delete)/, // ASP.NET conventions
|
|
60
|
+
/Action$/, // MVC actions
|
|
61
|
+
/^On[A-Z]/, // Event handlers
|
|
62
|
+
/Async$/, // Async entry points
|
|
63
|
+
],
|
|
64
|
+
// Go
|
|
65
|
+
'go': [
|
|
66
|
+
/Handler$/, // http.Handler pattern
|
|
67
|
+
/^Serve/, // ServeHTTP
|
|
68
|
+
/^New[A-Z]/, // Constructor pattern (returns new instance)
|
|
69
|
+
/^Make[A-Z]/, // Make functions
|
|
70
|
+
],
|
|
71
|
+
// Rust
|
|
72
|
+
'rust': [
|
|
73
|
+
/^(get|post|put|delete)_handler$/i,
|
|
74
|
+
/^handle_/, // handle_request
|
|
75
|
+
/^new$/, // Constructor pattern
|
|
76
|
+
/^run$/, // run entry point
|
|
77
|
+
/^spawn/, // Async spawn
|
|
78
|
+
],
|
|
79
|
+
// C - explicit main() boost (critical for C programs)
|
|
80
|
+
'c': [
|
|
81
|
+
/^main$/, // THE entry point
|
|
82
|
+
/^init_/, // Initialization functions
|
|
83
|
+
/^start_/, // Start functions
|
|
84
|
+
/^run_/, // Run functions
|
|
85
|
+
],
|
|
86
|
+
// C++ - same as C plus class patterns
|
|
87
|
+
'cpp': [
|
|
88
|
+
/^main$/, // THE entry point
|
|
89
|
+
/^init_/,
|
|
90
|
+
/^Create[A-Z]/, // Factory patterns
|
|
91
|
+
/^Run$/, // Run methods
|
|
92
|
+
/^Start$/, // Start methods
|
|
93
|
+
],
|
|
94
|
+
};
|
|
95
|
+
// ============================================================================
|
|
96
|
+
// UTILITY PATTERNS - Functions that should be penalized
|
|
97
|
+
// ============================================================================
|
|
98
|
+
/**
|
|
99
|
+
* Patterns that indicate utility/helper functions (NOT entry points)
|
|
100
|
+
* These get penalized in scoring
|
|
101
|
+
*/
|
|
102
|
+
const UTILITY_PATTERNS = [
|
|
103
|
+
/^(get|set|is|has|can|should|will|did)[A-Z]/, // Accessors/predicates
|
|
104
|
+
/^_/, // Private by convention
|
|
105
|
+
/^(format|parse|validate|convert|transform)/i, // Transformation utilities
|
|
106
|
+
/^(log|debug|error|warn|info)$/i, // Logging
|
|
107
|
+
/^(to|from)[A-Z]/, // Conversions
|
|
108
|
+
/^(encode|decode)/i, // Encoding utilities
|
|
109
|
+
/^(serialize|deserialize)/i, // Serialization
|
|
110
|
+
/^(clone|copy|deep)/i, // Cloning utilities
|
|
111
|
+
/^(merge|extend|assign)/i, // Object utilities
|
|
112
|
+
/^(filter|map|reduce|sort|find)/i, // Collection utilities (standalone)
|
|
113
|
+
/Helper$/,
|
|
114
|
+
/Util$/,
|
|
115
|
+
/Utils$/,
|
|
116
|
+
/^utils?$/i,
|
|
117
|
+
/^helpers?$/i,
|
|
118
|
+
];
|
|
119
|
+
// ============================================================================
|
|
120
|
+
// MAIN SCORING FUNCTION
|
|
121
|
+
// ============================================================================
|
|
122
|
+
/**
|
|
123
|
+
* Calculate an entry point score for a function/method
|
|
124
|
+
*
|
|
125
|
+
* Higher scores indicate better entry point candidates.
|
|
126
|
+
* Score = baseScore × exportMultiplier × nameMultiplier
|
|
127
|
+
*
|
|
128
|
+
* @param name - Function/method name
|
|
129
|
+
* @param language - Programming language
|
|
130
|
+
* @param isExported - Whether the function is exported/public
|
|
131
|
+
* @param callerCount - Number of functions that call this function
|
|
132
|
+
* @param calleeCount - Number of functions this function calls
|
|
133
|
+
* @returns Score and array of reasons explaining the score
|
|
134
|
+
*/
|
|
135
|
+
export function calculateEntryPointScore(name, language, isExported, callerCount, calleeCount, filePath = '' // Optional for backwards compatibility
|
|
136
|
+
) {
|
|
137
|
+
const reasons = [];
|
|
138
|
+
// Must have outgoing calls to be an entry point (we need to trace forward)
|
|
139
|
+
if (calleeCount === 0) {
|
|
140
|
+
return { score: 0, reasons: ['no-outgoing-calls'] };
|
|
141
|
+
}
|
|
142
|
+
// Base score: call ratio (existing algorithm)
|
|
143
|
+
// High ratio = calls many, called by few = likely entry point
|
|
144
|
+
const baseScore = calleeCount / (callerCount + 1);
|
|
145
|
+
reasons.push(`base:${baseScore.toFixed(2)}`);
|
|
146
|
+
// Export bonus: exported/public functions are more likely entry points
|
|
147
|
+
const exportMultiplier = isExported ? 2.0 : 1.0;
|
|
148
|
+
if (isExported) {
|
|
149
|
+
reasons.push('exported');
|
|
150
|
+
}
|
|
151
|
+
// Name pattern scoring
|
|
152
|
+
let nameMultiplier = 1.0;
|
|
153
|
+
// Check negative patterns first (utilities get penalized)
|
|
154
|
+
if (UTILITY_PATTERNS.some(p => p.test(name))) {
|
|
155
|
+
nameMultiplier = 0.3; // Significant penalty
|
|
156
|
+
reasons.push('utility-pattern');
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
// Check positive patterns
|
|
160
|
+
const universalPatterns = ENTRY_POINT_PATTERNS['*'] || [];
|
|
161
|
+
const langPatterns = ENTRY_POINT_PATTERNS[language] || [];
|
|
162
|
+
const allPatterns = [...universalPatterns, ...langPatterns];
|
|
163
|
+
if (allPatterns.some(p => p.test(name))) {
|
|
164
|
+
nameMultiplier = 1.5; // Bonus for matching entry point pattern
|
|
165
|
+
reasons.push('entry-pattern');
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// Framework detection bonus (Phase 2)
|
|
169
|
+
let frameworkMultiplier = 1.0;
|
|
170
|
+
if (filePath) {
|
|
171
|
+
const frameworkHint = detectFrameworkFromPath(filePath);
|
|
172
|
+
if (frameworkHint) {
|
|
173
|
+
frameworkMultiplier = frameworkHint.entryPointMultiplier;
|
|
174
|
+
reasons.push(`framework:${frameworkHint.reason}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// Calculate final score
|
|
178
|
+
const finalScore = baseScore * exportMultiplier * nameMultiplier * frameworkMultiplier;
|
|
179
|
+
return {
|
|
180
|
+
score: finalScore,
|
|
181
|
+
reasons,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
// ============================================================================
|
|
185
|
+
// HELPER FUNCTIONS
|
|
186
|
+
// ============================================================================
|
|
187
|
+
/**
|
|
188
|
+
* Check if a file path is a test file (should be excluded from entry points)
|
|
189
|
+
* Covers common test file patterns across all supported languages
|
|
190
|
+
*/
|
|
191
|
+
export function isTestFile(filePath) {
|
|
192
|
+
const p = filePath.toLowerCase().replace(/\\/g, '/');
|
|
193
|
+
return (
|
|
194
|
+
// JavaScript/TypeScript test patterns
|
|
195
|
+
p.includes('.test.') ||
|
|
196
|
+
p.includes('.spec.') ||
|
|
197
|
+
p.includes('__tests__/') ||
|
|
198
|
+
p.includes('__mocks__/') ||
|
|
199
|
+
// Generic test folders
|
|
200
|
+
p.includes('/test/') ||
|
|
201
|
+
p.includes('/tests/') ||
|
|
202
|
+
p.includes('/testing/') ||
|
|
203
|
+
// Python test patterns
|
|
204
|
+
p.endsWith('_test.py') ||
|
|
205
|
+
p.includes('/test_') ||
|
|
206
|
+
// Go test patterns
|
|
207
|
+
p.endsWith('_test.go') ||
|
|
208
|
+
// Java test patterns
|
|
209
|
+
p.includes('/src/test/') ||
|
|
210
|
+
// Rust test patterns (inline tests are different, but test files)
|
|
211
|
+
p.includes('/tests/') ||
|
|
212
|
+
// C# test patterns
|
|
213
|
+
p.includes('.tests/') ||
|
|
214
|
+
p.includes('tests.cs'));
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Check if a file path is likely a utility/helper file
|
|
218
|
+
* These might still have entry points but should be lower priority
|
|
219
|
+
*/
|
|
220
|
+
export function isUtilityFile(filePath) {
|
|
221
|
+
const p = filePath.toLowerCase().replace(/\\/g, '/');
|
|
222
|
+
return (p.includes('/utils/') ||
|
|
223
|
+
p.includes('/util/') ||
|
|
224
|
+
p.includes('/helpers/') ||
|
|
225
|
+
p.includes('/helper/') ||
|
|
226
|
+
p.includes('/common/') ||
|
|
227
|
+
p.includes('/shared/') ||
|
|
228
|
+
p.includes('/lib/') ||
|
|
229
|
+
p.endsWith('/utils.ts') ||
|
|
230
|
+
p.endsWith('/utils.js') ||
|
|
231
|
+
p.endsWith('/helpers.ts') ||
|
|
232
|
+
p.endsWith('/helpers.js') ||
|
|
233
|
+
p.endsWith('_utils.py') ||
|
|
234
|
+
p.endsWith('_helpers.py'));
|
|
235
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { glob } from 'glob';
|
|
4
|
+
import { shouldIgnorePath } from '../../config/ignore-service.js';
|
|
5
|
+
export const walkRepository = async (repoPath, onProgress) => {
|
|
6
|
+
const files = await glob('**/*', {
|
|
7
|
+
cwd: repoPath,
|
|
8
|
+
nodir: true,
|
|
9
|
+
dot: false,
|
|
10
|
+
});
|
|
11
|
+
const filtered = files.filter(file => !shouldIgnorePath(file));
|
|
12
|
+
const entries = [];
|
|
13
|
+
for (let i = 0; i < filtered.length; i++) {
|
|
14
|
+
const relativePath = filtered[i];
|
|
15
|
+
const fullPath = path.join(repoPath, relativePath);
|
|
16
|
+
try {
|
|
17
|
+
const content = await fs.readFile(fullPath, 'utf-8');
|
|
18
|
+
entries.push({ path: relativePath.replace(/\\/g, '/'), content });
|
|
19
|
+
onProgress?.(i + 1, filtered.length, relativePath);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
onProgress?.(i + 1, filtered.length, relativePath);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return entries;
|
|
26
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework Detection
|
|
3
|
+
*
|
|
4
|
+
* Detects frameworks from file path patterns and provides entry point multipliers.
|
|
5
|
+
* This enables framework-aware entry point scoring.
|
|
6
|
+
*
|
|
7
|
+
* DESIGN: Returns null for unknown frameworks, which causes a 1.0 multiplier
|
|
8
|
+
* (no bonus, no penalty) - same behavior as before this feature.
|
|
9
|
+
*/
|
|
10
|
+
export interface FrameworkHint {
|
|
11
|
+
framework: string;
|
|
12
|
+
entryPointMultiplier: number;
|
|
13
|
+
reason: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Detect framework from file path patterns
|
|
17
|
+
*
|
|
18
|
+
* This provides entry point multipliers based on well-known framework conventions.
|
|
19
|
+
* Returns null if no framework pattern is detected (falls back to 1.0 multiplier).
|
|
20
|
+
*/
|
|
21
|
+
export declare function detectFrameworkFromPath(filePath: string): FrameworkHint | null;
|
|
22
|
+
/**
|
|
23
|
+
* Patterns that indicate entry points within code (for future AST-based detection)
|
|
24
|
+
* These would require parsing decorators/annotations in the code itself.
|
|
25
|
+
*/
|
|
26
|
+
export declare const FRAMEWORK_AST_PATTERNS: {
|
|
27
|
+
nestjs: string[];
|
|
28
|
+
express: string[];
|
|
29
|
+
fastapi: string[];
|
|
30
|
+
flask: string[];
|
|
31
|
+
spring: string[];
|
|
32
|
+
jaxrs: string[];
|
|
33
|
+
aspnet: string[];
|
|
34
|
+
'go-http': string[];
|
|
35
|
+
actix: string[];
|
|
36
|
+
axum: string[];
|
|
37
|
+
rocket: string[];
|
|
38
|
+
};
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework Detection
|
|
3
|
+
*
|
|
4
|
+
* Detects frameworks from file path patterns and provides entry point multipliers.
|
|
5
|
+
* This enables framework-aware entry point scoring.
|
|
6
|
+
*
|
|
7
|
+
* DESIGN: Returns null for unknown frameworks, which causes a 1.0 multiplier
|
|
8
|
+
* (no bonus, no penalty) - same behavior as before this feature.
|
|
9
|
+
*/
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// PATH-BASED FRAMEWORK DETECTION
|
|
12
|
+
// ============================================================================
|
|
13
|
+
/**
|
|
14
|
+
* Detect framework from file path patterns
|
|
15
|
+
*
|
|
16
|
+
* This provides entry point multipliers based on well-known framework conventions.
|
|
17
|
+
* Returns null if no framework pattern is detected (falls back to 1.0 multiplier).
|
|
18
|
+
*/
|
|
19
|
+
export function detectFrameworkFromPath(filePath) {
|
|
20
|
+
// Normalize path separators and ensure leading slash for consistent matching
|
|
21
|
+
let p = filePath.toLowerCase().replace(/\\/g, '/');
|
|
22
|
+
if (!p.startsWith('/')) {
|
|
23
|
+
p = '/' + p; // Add leading slash so patterns like '/app/' match 'app/...'
|
|
24
|
+
}
|
|
25
|
+
// ========== JAVASCRIPT / TYPESCRIPT FRAMEWORKS ==========
|
|
26
|
+
// Next.js - Pages Router (high confidence)
|
|
27
|
+
if (p.includes('/pages/') && !p.includes('/_') && !p.includes('/api/')) {
|
|
28
|
+
if (p.endsWith('.tsx') || p.endsWith('.ts') || p.endsWith('.jsx') || p.endsWith('.js')) {
|
|
29
|
+
return { framework: 'nextjs-pages', entryPointMultiplier: 3.0, reason: 'nextjs-page' };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// Next.js - App Router (page.tsx files)
|
|
33
|
+
if (p.includes('/app/') && (p.endsWith('page.tsx') || p.endsWith('page.ts') ||
|
|
34
|
+
p.endsWith('page.jsx') || p.endsWith('page.js'))) {
|
|
35
|
+
return { framework: 'nextjs-app', entryPointMultiplier: 3.0, reason: 'nextjs-app-page' };
|
|
36
|
+
}
|
|
37
|
+
// Next.js - API Routes
|
|
38
|
+
if (p.includes('/pages/api/') || (p.includes('/app/') && p.includes('/api/') && p.endsWith('route.ts'))) {
|
|
39
|
+
return { framework: 'nextjs-api', entryPointMultiplier: 3.0, reason: 'nextjs-api-route' };
|
|
40
|
+
}
|
|
41
|
+
// Next.js - Layout files (moderate - they're entry-ish but not the main entry)
|
|
42
|
+
if (p.includes('/app/') && (p.endsWith('layout.tsx') || p.endsWith('layout.ts'))) {
|
|
43
|
+
return { framework: 'nextjs-app', entryPointMultiplier: 2.0, reason: 'nextjs-layout' };
|
|
44
|
+
}
|
|
45
|
+
// Express / Node.js routes
|
|
46
|
+
if (p.includes('/routes/') && (p.endsWith('.ts') || p.endsWith('.js'))) {
|
|
47
|
+
return { framework: 'express', entryPointMultiplier: 2.5, reason: 'routes-folder' };
|
|
48
|
+
}
|
|
49
|
+
// Generic controllers (MVC pattern)
|
|
50
|
+
if (p.includes('/controllers/') && (p.endsWith('.ts') || p.endsWith('.js'))) {
|
|
51
|
+
return { framework: 'mvc', entryPointMultiplier: 2.5, reason: 'controllers-folder' };
|
|
52
|
+
}
|
|
53
|
+
// Generic handlers
|
|
54
|
+
if (p.includes('/handlers/') && (p.endsWith('.ts') || p.endsWith('.js'))) {
|
|
55
|
+
return { framework: 'handlers', entryPointMultiplier: 2.5, reason: 'handlers-folder' };
|
|
56
|
+
}
|
|
57
|
+
// React components (lower priority - not all are entry points)
|
|
58
|
+
if ((p.includes('/components/') || p.includes('/views/')) &&
|
|
59
|
+
(p.endsWith('.tsx') || p.endsWith('.jsx'))) {
|
|
60
|
+
// Only boost if PascalCase filename (likely a component, not util)
|
|
61
|
+
const fileName = p.split('/').pop() || '';
|
|
62
|
+
if (/^[A-Z]/.test(fileName)) {
|
|
63
|
+
return { framework: 'react', entryPointMultiplier: 1.5, reason: 'react-component' };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// ========== PYTHON FRAMEWORKS ==========
|
|
67
|
+
// Django views (high confidence)
|
|
68
|
+
if (p.endsWith('views.py')) {
|
|
69
|
+
return { framework: 'django', entryPointMultiplier: 3.0, reason: 'django-views' };
|
|
70
|
+
}
|
|
71
|
+
// Django URL configs
|
|
72
|
+
if (p.endsWith('urls.py')) {
|
|
73
|
+
return { framework: 'django', entryPointMultiplier: 2.0, reason: 'django-urls' };
|
|
74
|
+
}
|
|
75
|
+
// FastAPI / Flask routers
|
|
76
|
+
if ((p.includes('/routers/') || p.includes('/endpoints/') || p.includes('/routes/')) &&
|
|
77
|
+
p.endsWith('.py')) {
|
|
78
|
+
return { framework: 'fastapi', entryPointMultiplier: 2.5, reason: 'api-routers' };
|
|
79
|
+
}
|
|
80
|
+
// Python API folder
|
|
81
|
+
if (p.includes('/api/') && p.endsWith('.py') && !p.endsWith('__init__.py')) {
|
|
82
|
+
return { framework: 'python-api', entryPointMultiplier: 2.0, reason: 'api-folder' };
|
|
83
|
+
}
|
|
84
|
+
// ========== JAVA FRAMEWORKS ==========
|
|
85
|
+
// Spring Boot controllers
|
|
86
|
+
if ((p.includes('/controller/') || p.includes('/controllers/')) && p.endsWith('.java')) {
|
|
87
|
+
return { framework: 'spring', entryPointMultiplier: 3.0, reason: 'spring-controller' };
|
|
88
|
+
}
|
|
89
|
+
// Spring Boot - files ending in Controller.java
|
|
90
|
+
if (p.endsWith('controller.java')) {
|
|
91
|
+
return { framework: 'spring', entryPointMultiplier: 3.0, reason: 'spring-controller-file' };
|
|
92
|
+
}
|
|
93
|
+
// Java service layer (often entry points for business logic)
|
|
94
|
+
if ((p.includes('/service/') || p.includes('/services/')) && p.endsWith('.java')) {
|
|
95
|
+
return { framework: 'java-service', entryPointMultiplier: 1.8, reason: 'java-service' };
|
|
96
|
+
}
|
|
97
|
+
// ========== C# / .NET FRAMEWORKS ==========
|
|
98
|
+
// ASP.NET Controllers
|
|
99
|
+
if (p.includes('/controllers/') && p.endsWith('.cs')) {
|
|
100
|
+
return { framework: 'aspnet', entryPointMultiplier: 3.0, reason: 'aspnet-controller' };
|
|
101
|
+
}
|
|
102
|
+
// ASP.NET - files ending in Controller.cs
|
|
103
|
+
if (p.endsWith('controller.cs')) {
|
|
104
|
+
return { framework: 'aspnet', entryPointMultiplier: 3.0, reason: 'aspnet-controller-file' };
|
|
105
|
+
}
|
|
106
|
+
// Blazor pages
|
|
107
|
+
if (p.includes('/pages/') && p.endsWith('.razor')) {
|
|
108
|
+
return { framework: 'blazor', entryPointMultiplier: 2.5, reason: 'blazor-page' };
|
|
109
|
+
}
|
|
110
|
+
// ========== GO FRAMEWORKS ==========
|
|
111
|
+
// Go handlers
|
|
112
|
+
if ((p.includes('/handlers/') || p.includes('/handler/')) && p.endsWith('.go')) {
|
|
113
|
+
return { framework: 'go-http', entryPointMultiplier: 2.5, reason: 'go-handlers' };
|
|
114
|
+
}
|
|
115
|
+
// Go routes
|
|
116
|
+
if (p.includes('/routes/') && p.endsWith('.go')) {
|
|
117
|
+
return { framework: 'go-http', entryPointMultiplier: 2.5, reason: 'go-routes' };
|
|
118
|
+
}
|
|
119
|
+
// Go controllers
|
|
120
|
+
if (p.includes('/controllers/') && p.endsWith('.go')) {
|
|
121
|
+
return { framework: 'go-mvc', entryPointMultiplier: 2.5, reason: 'go-controller' };
|
|
122
|
+
}
|
|
123
|
+
// Go main.go files (THE entry point)
|
|
124
|
+
if (p.endsWith('/main.go') || p.endsWith('/cmd/') && p.endsWith('.go')) {
|
|
125
|
+
return { framework: 'go', entryPointMultiplier: 3.0, reason: 'go-main' };
|
|
126
|
+
}
|
|
127
|
+
// ========== RUST FRAMEWORKS ==========
|
|
128
|
+
// Rust handlers/routes
|
|
129
|
+
if ((p.includes('/handlers/') || p.includes('/routes/')) && p.endsWith('.rs')) {
|
|
130
|
+
return { framework: 'rust-web', entryPointMultiplier: 2.5, reason: 'rust-handlers' };
|
|
131
|
+
}
|
|
132
|
+
// Rust main.rs (THE entry point)
|
|
133
|
+
if (p.endsWith('/main.rs')) {
|
|
134
|
+
return { framework: 'rust', entryPointMultiplier: 3.0, reason: 'rust-main' };
|
|
135
|
+
}
|
|
136
|
+
// Rust bin folder (executables)
|
|
137
|
+
if (p.includes('/bin/') && p.endsWith('.rs')) {
|
|
138
|
+
return { framework: 'rust', entryPointMultiplier: 2.5, reason: 'rust-bin' };
|
|
139
|
+
}
|
|
140
|
+
// ========== C / C++ ==========
|
|
141
|
+
// C/C++ main files
|
|
142
|
+
if (p.endsWith('/main.c') || p.endsWith('/main.cpp') || p.endsWith('/main.cc')) {
|
|
143
|
+
return { framework: 'c-cpp', entryPointMultiplier: 3.0, reason: 'c-main' };
|
|
144
|
+
}
|
|
145
|
+
// C/C++ src folder entry points (if named specifically)
|
|
146
|
+
if ((p.includes('/src/') && (p.endsWith('/app.c') || p.endsWith('/app.cpp')))) {
|
|
147
|
+
return { framework: 'c-cpp', entryPointMultiplier: 2.5, reason: 'c-app' };
|
|
148
|
+
}
|
|
149
|
+
// ========== GENERIC PATTERNS ==========
|
|
150
|
+
// Any language: index files in API folders
|
|
151
|
+
if (p.includes('/api/') && (p.endsWith('/index.ts') || p.endsWith('/index.js') ||
|
|
152
|
+
p.endsWith('/__init__.py'))) {
|
|
153
|
+
return { framework: 'api', entryPointMultiplier: 1.8, reason: 'api-index' };
|
|
154
|
+
}
|
|
155
|
+
// No framework detected - return null for graceful fallback (1.0 multiplier)
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
// ============================================================================
|
|
159
|
+
// FUTURE: AST-BASED PATTERNS (for Phase 3)
|
|
160
|
+
// ============================================================================
|
|
161
|
+
/**
|
|
162
|
+
* Patterns that indicate entry points within code (for future AST-based detection)
|
|
163
|
+
* These would require parsing decorators/annotations in the code itself.
|
|
164
|
+
*/
|
|
165
|
+
export const FRAMEWORK_AST_PATTERNS = {
|
|
166
|
+
// JavaScript/TypeScript decorators
|
|
167
|
+
'nestjs': ['@Controller', '@Get', '@Post', '@Put', '@Delete', '@Patch'],
|
|
168
|
+
'express': ['app.get', 'app.post', 'app.put', 'app.delete', 'router.get', 'router.post'],
|
|
169
|
+
// Python decorators
|
|
170
|
+
'fastapi': ['@app.get', '@app.post', '@app.put', '@app.delete', '@router.get'],
|
|
171
|
+
'flask': ['@app.route', '@blueprint.route'],
|
|
172
|
+
// Java annotations
|
|
173
|
+
'spring': ['@RestController', '@Controller', '@GetMapping', '@PostMapping', '@RequestMapping'],
|
|
174
|
+
'jaxrs': ['@Path', '@GET', '@POST', '@PUT', '@DELETE'],
|
|
175
|
+
// C# attributes
|
|
176
|
+
'aspnet': ['[ApiController]', '[HttpGet]', '[HttpPost]', '[Route]'],
|
|
177
|
+
// Go patterns (function signatures)
|
|
178
|
+
'go-http': ['http.Handler', 'http.HandlerFunc', 'ServeHTTP'],
|
|
179
|
+
// Rust macros
|
|
180
|
+
'actix': ['#[get', '#[post', '#[put', '#[delete'],
|
|
181
|
+
'axum': ['Router::new'],
|
|
182
|
+
'rocket': ['#[get', '#[post'],
|
|
183
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Heritage Processor
|
|
3
|
+
*
|
|
4
|
+
* Extracts class inheritance relationships:
|
|
5
|
+
* - EXTENDS: Class extends another Class (TS, JS, Python)
|
|
6
|
+
* - IMPLEMENTS: Class implements an Interface (TS only)
|
|
7
|
+
*/
|
|
8
|
+
import { KnowledgeGraph } from '../graph/types.js';
|
|
9
|
+
import { ASTCache } from './ast-cache.js';
|
|
10
|
+
import { SymbolTable } from './symbol-table.js';
|
|
11
|
+
export declare const processHeritage: (graph: KnowledgeGraph, files: {
|
|
12
|
+
path: string;
|
|
13
|
+
content: string;
|
|
14
|
+
}[], astCache: ASTCache, symbolTable: SymbolTable, onProgress?: (current: number, total: number) => void) => Promise<void>;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Heritage Processor
|
|
3
|
+
*
|
|
4
|
+
* Extracts class inheritance relationships:
|
|
5
|
+
* - EXTENDS: Class extends another Class (TS, JS, Python)
|
|
6
|
+
* - IMPLEMENTS: Class implements an Interface (TS only)
|
|
7
|
+
*/
|
|
8
|
+
import Parser from 'tree-sitter';
|
|
9
|
+
import { loadParser, loadLanguage } from '../tree-sitter/parser-loader.js';
|
|
10
|
+
import { LANGUAGE_QUERIES } from './tree-sitter-queries.js';
|
|
11
|
+
import { generateId } from '../../lib/utils.js';
|
|
12
|
+
import { getLanguageFromFilename, yieldToEventLoop } from './utils.js';
|
|
13
|
+
export const processHeritage = async (graph, files, astCache, symbolTable, onProgress) => {
|
|
14
|
+
const parser = await loadParser();
|
|
15
|
+
for (let i = 0; i < files.length; i++) {
|
|
16
|
+
const file = files[i];
|
|
17
|
+
onProgress?.(i + 1, files.length);
|
|
18
|
+
if (i % 20 === 0)
|
|
19
|
+
await yieldToEventLoop();
|
|
20
|
+
// 1. Check language support
|
|
21
|
+
const language = getLanguageFromFilename(file.path);
|
|
22
|
+
if (!language)
|
|
23
|
+
continue;
|
|
24
|
+
const queryStr = LANGUAGE_QUERIES[language];
|
|
25
|
+
if (!queryStr)
|
|
26
|
+
continue;
|
|
27
|
+
// 2. Load the language
|
|
28
|
+
await loadLanguage(language, file.path);
|
|
29
|
+
// 3. Get AST
|
|
30
|
+
let tree = astCache.get(file.path);
|
|
31
|
+
let wasReparsed = false;
|
|
32
|
+
if (!tree) {
|
|
33
|
+
// Use larger bufferSize for files > 32KB
|
|
34
|
+
try {
|
|
35
|
+
tree = parser.parse(file.content, undefined, { bufferSize: 1024 * 256 });
|
|
36
|
+
}
|
|
37
|
+
catch (parseError) {
|
|
38
|
+
// Skip files that can't be parsed
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
wasReparsed = true;
|
|
42
|
+
}
|
|
43
|
+
let query;
|
|
44
|
+
let matches;
|
|
45
|
+
try {
|
|
46
|
+
const language = parser.getLanguage();
|
|
47
|
+
query = new Parser.Query(language, queryStr);
|
|
48
|
+
matches = query.matches(tree.rootNode);
|
|
49
|
+
}
|
|
50
|
+
catch (queryError) {
|
|
51
|
+
console.warn(`Heritage query error for ${file.path}:`, queryError);
|
|
52
|
+
if (wasReparsed)
|
|
53
|
+
tree.delete?.();
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
// 4. Process heritage matches
|
|
57
|
+
matches.forEach(match => {
|
|
58
|
+
const captureMap = {};
|
|
59
|
+
match.captures.forEach(c => {
|
|
60
|
+
captureMap[c.name] = c.node;
|
|
61
|
+
});
|
|
62
|
+
// EXTENDS: Class extends another Class
|
|
63
|
+
if (captureMap['heritage.class'] && captureMap['heritage.extends']) {
|
|
64
|
+
const className = captureMap['heritage.class'].text;
|
|
65
|
+
const parentClassName = captureMap['heritage.extends'].text;
|
|
66
|
+
// Resolve both class IDs
|
|
67
|
+
const childId = symbolTable.lookupExact(file.path, className) ||
|
|
68
|
+
symbolTable.lookupFuzzy(className)[0]?.nodeId ||
|
|
69
|
+
generateId('Class', `${file.path}:${className}`);
|
|
70
|
+
const parentId = symbolTable.lookupFuzzy(parentClassName)[0]?.nodeId ||
|
|
71
|
+
generateId('Class', `${parentClassName}`);
|
|
72
|
+
if (childId && parentId && childId !== parentId) {
|
|
73
|
+
const relId = generateId('EXTENDS', `${childId}->${parentId}`);
|
|
74
|
+
graph.addRelationship({
|
|
75
|
+
id: relId,
|
|
76
|
+
sourceId: childId,
|
|
77
|
+
targetId: parentId,
|
|
78
|
+
type: 'EXTENDS',
|
|
79
|
+
confidence: 1.0,
|
|
80
|
+
reason: '',
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// IMPLEMENTS: Class implements Interface (TypeScript only)
|
|
85
|
+
if (captureMap['heritage.class'] && captureMap['heritage.implements']) {
|
|
86
|
+
const className = captureMap['heritage.class'].text;
|
|
87
|
+
const interfaceName = captureMap['heritage.implements'].text;
|
|
88
|
+
// Resolve class and interface IDs
|
|
89
|
+
const classId = symbolTable.lookupExact(file.path, className) ||
|
|
90
|
+
symbolTable.lookupFuzzy(className)[0]?.nodeId ||
|
|
91
|
+
generateId('Class', `${file.path}:${className}`);
|
|
92
|
+
const interfaceId = symbolTable.lookupFuzzy(interfaceName)[0]?.nodeId ||
|
|
93
|
+
generateId('Interface', `${interfaceName}`);
|
|
94
|
+
if (classId && interfaceId) {
|
|
95
|
+
const relId = generateId('IMPLEMENTS', `${classId}->${interfaceId}`);
|
|
96
|
+
graph.addRelationship({
|
|
97
|
+
id: relId,
|
|
98
|
+
sourceId: classId,
|
|
99
|
+
targetId: interfaceId,
|
|
100
|
+
type: 'IMPLEMENTS',
|
|
101
|
+
confidence: 1.0,
|
|
102
|
+
reason: '',
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// IMPLEMENTS (Rust): impl Trait for Struct
|
|
107
|
+
if (captureMap['heritage.trait'] && captureMap['heritage.class']) {
|
|
108
|
+
const structName = captureMap['heritage.class'].text;
|
|
109
|
+
const traitName = captureMap['heritage.trait'].text;
|
|
110
|
+
// Resolve struct and trait IDs
|
|
111
|
+
const structId = symbolTable.lookupExact(file.path, structName) ||
|
|
112
|
+
symbolTable.lookupFuzzy(structName)[0]?.nodeId ||
|
|
113
|
+
generateId('Struct', `${file.path}:${structName}`);
|
|
114
|
+
const traitId = symbolTable.lookupFuzzy(traitName)[0]?.nodeId ||
|
|
115
|
+
generateId('Trait', `${traitName}`);
|
|
116
|
+
if (structId && traitId) {
|
|
117
|
+
const relId = generateId('IMPLEMENTS', `${structId}->${traitId}`);
|
|
118
|
+
graph.addRelationship({
|
|
119
|
+
id: relId,
|
|
120
|
+
sourceId: structId,
|
|
121
|
+
targetId: traitId,
|
|
122
|
+
type: 'IMPLEMENTS',
|
|
123
|
+
confidence: 1.0,
|
|
124
|
+
reason: 'trait-impl',
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
// Cleanup
|
|
130
|
+
if (wasReparsed) {
|
|
131
|
+
tree.delete?.();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { KnowledgeGraph } from '../graph/types.js';
|
|
2
|
+
import { ASTCache } from './ast-cache.js';
|
|
3
|
+
export type ImportMap = Map<string, Set<string>>;
|
|
4
|
+
export declare const createImportMap: () => ImportMap;
|
|
5
|
+
export declare const processImports: (graph: KnowledgeGraph, files: {
|
|
6
|
+
path: string;
|
|
7
|
+
content: string;
|
|
8
|
+
}[], astCache: ASTCache, importMap: ImportMap, onProgress?: (current: number, total: number) => void, repoRoot?: string) => Promise<void>;
|