gitnexus 1.3.3 → 1.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +1 -1
  2. package/dist/cli/ai-context.js +23 -52
  3. package/dist/cli/analyze.js +4 -1
  4. package/dist/cli/index.js +1 -0
  5. package/dist/cli/mcp.js +11 -22
  6. package/dist/cli/serve.d.ts +1 -0
  7. package/dist/cli/serve.js +2 -1
  8. package/dist/cli/setup.js +2 -2
  9. package/dist/cli/wiki.js +6 -2
  10. package/dist/config/supported-languages.d.ts +3 -1
  11. package/dist/config/supported-languages.js +2 -2
  12. package/dist/core/embeddings/embedder.js +40 -1
  13. package/dist/core/graph/types.d.ts +2 -0
  14. package/dist/core/ingestion/call-processor.js +39 -0
  15. package/dist/core/ingestion/entry-point-scoring.js +49 -1
  16. package/dist/core/ingestion/framework-detection.d.ts +15 -4
  17. package/dist/core/ingestion/framework-detection.js +146 -5
  18. package/dist/core/ingestion/import-processor.js +140 -0
  19. package/dist/core/ingestion/parsing-processor.js +61 -8
  20. package/dist/core/ingestion/process-processor.js +7 -1
  21. package/dist/core/ingestion/tree-sitter-queries.d.ts +2 -0
  22. package/dist/core/ingestion/tree-sitter-queries.js +414 -282
  23. package/dist/core/ingestion/utils.js +8 -0
  24. package/dist/core/ingestion/workers/parse-worker.d.ts +3 -0
  25. package/dist/core/ingestion/workers/parse-worker.js +240 -0
  26. package/dist/core/kuzu/csv-generator.js +4 -2
  27. package/dist/core/kuzu/kuzu-adapter.d.ts +9 -0
  28. package/dist/core/kuzu/kuzu-adapter.js +68 -9
  29. package/dist/core/kuzu/schema.d.ts +6 -6
  30. package/dist/core/kuzu/schema.js +16 -0
  31. package/dist/core/tree-sitter/parser-loader.js +4 -0
  32. package/dist/core/wiki/generator.js +2 -2
  33. package/dist/mcp/local/local-backend.js +25 -13
  34. package/dist/mcp/server.d.ts +9 -0
  35. package/dist/mcp/server.js +13 -2
  36. package/dist/mcp/staleness.js +2 -2
  37. package/dist/server/api.d.ts +7 -5
  38. package/dist/server/api.js +145 -127
  39. package/dist/server/mcp-http.d.ts +13 -0
  40. package/dist/server/mcp-http.js +100 -0
  41. package/package.json +6 -2
  42. package/scripts/patch-tree-sitter-swift.cjs +74 -0
  43. package/skills/gitnexus-cli.md +82 -0
  44. package/skills/{debugging.md → gitnexus-debugging.md} +12 -8
  45. package/skills/{exploring.md → gitnexus-exploring.md} +10 -7
  46. package/skills/gitnexus-guide.md +64 -0
  47. package/skills/{impact-analysis.md → gitnexus-impact-analysis.md} +14 -11
  48. package/skills/gitnexus-pr-review.md +163 -0
  49. package/skills/{refactoring.md → gitnexus-refactoring.md} +15 -7
@@ -1,8 +1,10 @@
1
1
  /**
2
2
  * Framework Detection
3
3
  *
4
- * Detects frameworks from file path patterns and provides entry point multipliers.
5
- * This enables framework-aware entry point scoring.
4
+ * Detects frameworks from:
5
+ * 1) file path patterns
6
+ * 2) AST definition text (decorators/annotations/attributes)
7
+ * and provides entry point multipliers for process scoring.
6
8
  *
7
9
  * DESIGN: Returns null for unknown frameworks, which causes a 1.0 multiplier
8
10
  * (no bonus, no penalty) - same behavior as before this feature.
@@ -146,6 +148,92 @@ export function detectFrameworkFromPath(filePath) {
146
148
  if ((p.includes('/src/') && (p.endsWith('/app.c') || p.endsWith('/app.cpp')))) {
147
149
  return { framework: 'c-cpp', entryPointMultiplier: 2.5, reason: 'c-app' };
148
150
  }
151
+ // ========== PHP / LARAVEL FRAMEWORKS ==========
152
+ // Laravel routes (highest - these ARE the entry point definitions)
153
+ if (p.includes('/routes/') && p.endsWith('.php')) {
154
+ return { framework: 'laravel', entryPointMultiplier: 3.0, reason: 'laravel-routes' };
155
+ }
156
+ // Laravel controllers (very high - receive HTTP requests)
157
+ if ((p.includes('/http/controllers/') || p.includes('/controllers/')) && p.endsWith('.php')) {
158
+ return { framework: 'laravel', entryPointMultiplier: 3.0, reason: 'laravel-controller' };
159
+ }
160
+ // Laravel controller by file name convention
161
+ if (p.endsWith('controller.php')) {
162
+ return { framework: 'laravel', entryPointMultiplier: 3.0, reason: 'laravel-controller-file' };
163
+ }
164
+ // Laravel console commands
165
+ if ((p.includes('/console/commands/') || p.includes('/commands/')) && p.endsWith('.php')) {
166
+ return { framework: 'laravel', entryPointMultiplier: 2.5, reason: 'laravel-command' };
167
+ }
168
+ // Laravel jobs (queue entry points)
169
+ if (p.includes('/jobs/') && p.endsWith('.php')) {
170
+ return { framework: 'laravel', entryPointMultiplier: 2.5, reason: 'laravel-job' };
171
+ }
172
+ // Laravel listeners (event-driven entry points)
173
+ if (p.includes('/listeners/') && p.endsWith('.php')) {
174
+ return { framework: 'laravel', entryPointMultiplier: 2.5, reason: 'laravel-listener' };
175
+ }
176
+ // Laravel middleware
177
+ if (p.includes('/http/middleware/') && p.endsWith('.php')) {
178
+ return { framework: 'laravel', entryPointMultiplier: 2.5, reason: 'laravel-middleware' };
179
+ }
180
+ // Laravel service providers
181
+ if (p.includes('/providers/') && p.endsWith('.php')) {
182
+ return { framework: 'laravel', entryPointMultiplier: 1.8, reason: 'laravel-provider' };
183
+ }
184
+ // Laravel policies
185
+ if (p.includes('/policies/') && p.endsWith('.php')) {
186
+ return { framework: 'laravel', entryPointMultiplier: 2.0, reason: 'laravel-policy' };
187
+ }
188
+ // Laravel models (important but not entry points per se)
189
+ if (p.includes('/models/') && p.endsWith('.php')) {
190
+ return { framework: 'laravel', entryPointMultiplier: 1.5, reason: 'laravel-model' };
191
+ }
192
+ // Laravel services (Service Repository pattern)
193
+ if (p.includes('/services/') && p.endsWith('.php')) {
194
+ return { framework: 'laravel', entryPointMultiplier: 1.8, reason: 'laravel-service' };
195
+ }
196
+ // Laravel repositories (Service Repository pattern)
197
+ if (p.includes('/repositories/') && p.endsWith('.php')) {
198
+ return { framework: 'laravel', entryPointMultiplier: 1.5, reason: 'laravel-repository' };
199
+ }
200
+ // ========== SWIFT / iOS ==========
201
+ // iOS App entry points (highest priority)
202
+ if (p.endsWith('/appdelegate.swift') || p.endsWith('/scenedelegate.swift') || p.endsWith('/app.swift')) {
203
+ return { framework: 'ios', entryPointMultiplier: 3.0, reason: 'ios-app-entry' };
204
+ }
205
+ // SwiftUI App entry (@main)
206
+ if (p.endsWith('app.swift') && p.includes('/sources/')) {
207
+ return { framework: 'swiftui', entryPointMultiplier: 3.0, reason: 'swiftui-app' };
208
+ }
209
+ // UIKit ViewControllers (high priority - screen entry points)
210
+ if ((p.includes('/viewcontrollers/') || p.includes('/controllers/') || p.includes('/screens/')) && p.endsWith('.swift')) {
211
+ return { framework: 'uikit', entryPointMultiplier: 2.5, reason: 'uikit-viewcontroller' };
212
+ }
213
+ // ViewController by filename convention
214
+ if (p.endsWith('viewcontroller.swift') || p.endsWith('vc.swift')) {
215
+ return { framework: 'uikit', entryPointMultiplier: 2.5, reason: 'uikit-viewcontroller-file' };
216
+ }
217
+ // Coordinator pattern (navigation entry points)
218
+ if (p.includes('/coordinators/') && p.endsWith('.swift')) {
219
+ return { framework: 'ios-coordinator', entryPointMultiplier: 2.5, reason: 'ios-coordinator' };
220
+ }
221
+ // Coordinator by filename
222
+ if (p.endsWith('coordinator.swift')) {
223
+ return { framework: 'ios-coordinator', entryPointMultiplier: 2.5, reason: 'ios-coordinator-file' };
224
+ }
225
+ // SwiftUI Views (moderate - reusable components)
226
+ if ((p.includes('/views/') || p.includes('/scenes/')) && p.endsWith('.swift')) {
227
+ return { framework: 'swiftui', entryPointMultiplier: 1.8, reason: 'swiftui-view' };
228
+ }
229
+ // Service layer
230
+ if (p.includes('/services/') && p.endsWith('.swift')) {
231
+ return { framework: 'ios-service', entryPointMultiplier: 1.8, reason: 'ios-service' };
232
+ }
233
+ // Router / navigation
234
+ if (p.includes('/router/') && p.endsWith('.swift')) {
235
+ return { framework: 'ios-router', entryPointMultiplier: 2.0, reason: 'ios-router' };
236
+ }
149
237
  // ========== GENERIC PATTERNS ==========
150
238
  // Any language: index files in API folders
151
239
  if (p.includes('/api/') && (p.endsWith('/index.ts') || p.endsWith('/index.js') ||
@@ -156,11 +244,11 @@ export function detectFrameworkFromPath(filePath) {
156
244
  return null;
157
245
  }
158
246
  // ============================================================================
159
- // FUTURE: AST-BASED PATTERNS (for Phase 3)
247
+ // AST-BASED FRAMEWORK DETECTION
160
248
  // ============================================================================
161
249
  /**
162
- * Patterns that indicate entry points within code (for future AST-based detection)
163
- * These would require parsing decorators/annotations in the code itself.
250
+ * Patterns that indicate framework entry points within code definitions.
251
+ * These are matched against AST node text (class/method/function declaration text).
164
252
  */
165
253
  export const FRAMEWORK_AST_PATTERNS = {
166
254
  // JavaScript/TypeScript decorators
@@ -176,8 +264,61 @@ export const FRAMEWORK_AST_PATTERNS = {
176
264
  'aspnet': ['[ApiController]', '[HttpGet]', '[HttpPost]', '[Route]'],
177
265
  // Go patterns (function signatures)
178
266
  'go-http': ['http.Handler', 'http.HandlerFunc', 'ServeHTTP'],
267
+ // PHP/Laravel
268
+ 'laravel': ['Route::get', 'Route::post', 'Route::put', 'Route::delete',
269
+ 'Route::resource', 'Route::apiResource', '#[Route('],
179
270
  // Rust macros
180
271
  'actix': ['#[get', '#[post', '#[put', '#[delete'],
181
272
  'axum': ['Router::new'],
182
273
  'rocket': ['#[get', '#[post'],
274
+ // Swift/iOS
275
+ 'uikit': ['viewDidLoad', 'viewWillAppear', 'viewDidAppear', 'UIViewController'],
276
+ 'swiftui': ['@main', 'WindowGroup', 'ContentView', '@StateObject', '@ObservedObject'],
277
+ 'combine': ['sink', 'assign', 'Publisher', 'Subscriber'],
183
278
  };
279
+ const AST_FRAMEWORK_PATTERNS_BY_LANGUAGE = {
280
+ javascript: [
281
+ { framework: 'nestjs', entryPointMultiplier: 3.2, reason: 'nestjs-decorator', patterns: FRAMEWORK_AST_PATTERNS.nestjs },
282
+ ],
283
+ typescript: [
284
+ { framework: 'nestjs', entryPointMultiplier: 3.2, reason: 'nestjs-decorator', patterns: FRAMEWORK_AST_PATTERNS.nestjs },
285
+ ],
286
+ python: [
287
+ { framework: 'fastapi', entryPointMultiplier: 3.0, reason: 'fastapi-decorator', patterns: FRAMEWORK_AST_PATTERNS.fastapi },
288
+ { framework: 'flask', entryPointMultiplier: 2.8, reason: 'flask-decorator', patterns: FRAMEWORK_AST_PATTERNS.flask },
289
+ ],
290
+ java: [
291
+ { framework: 'spring', entryPointMultiplier: 3.2, reason: 'spring-annotation', patterns: FRAMEWORK_AST_PATTERNS.spring },
292
+ { framework: 'jaxrs', entryPointMultiplier: 3.0, reason: 'jaxrs-annotation', patterns: FRAMEWORK_AST_PATTERNS.jaxrs },
293
+ ],
294
+ csharp: [
295
+ { framework: 'aspnet', entryPointMultiplier: 3.2, reason: 'aspnet-attribute', patterns: FRAMEWORK_AST_PATTERNS.aspnet },
296
+ ],
297
+ php: [
298
+ { framework: 'laravel', entryPointMultiplier: 3.0, reason: 'php-route-attribute', patterns: FRAMEWORK_AST_PATTERNS.laravel },
299
+ ],
300
+ };
301
+ /**
302
+ * Detect framework entry points from AST definition text (decorators/annotations/attributes).
303
+ * Returns null if no known pattern is found.
304
+ */
305
+ export function detectFrameworkFromAST(language, definitionText) {
306
+ if (!language || !definitionText)
307
+ return null;
308
+ const configs = AST_FRAMEWORK_PATTERNS_BY_LANGUAGE[language.toLowerCase()];
309
+ if (!configs || configs.length === 0)
310
+ return null;
311
+ const normalized = definitionText.toLowerCase();
312
+ for (const cfg of configs) {
313
+ for (const pattern of cfg.patterns) {
314
+ if (normalized.includes(pattern.toLowerCase())) {
315
+ return {
316
+ framework: cfg.framework,
317
+ entryPointMultiplier: cfg.entryPointMultiplier,
318
+ reason: cfg.reason,
319
+ };
320
+ }
321
+ }
322
+ }
323
+ return null;
324
+ }
@@ -78,6 +78,57 @@ async function loadGoModulePath(repoRoot) {
78
78
  }
79
79
  return null;
80
80
  }
81
+ async function loadComposerConfig(repoRoot) {
82
+ try {
83
+ const composerPath = path.join(repoRoot, 'composer.json');
84
+ const raw = await fs.readFile(composerPath, 'utf-8');
85
+ const composer = JSON.parse(raw);
86
+ const psr4Raw = composer.autoload?.['psr-4'] ?? {};
87
+ const psr4Dev = composer['autoload-dev']?.['psr-4'] ?? {};
88
+ const merged = { ...psr4Raw, ...psr4Dev };
89
+ const psr4 = new Map();
90
+ for (const [ns, dir] of Object.entries(merged)) {
91
+ const nsNorm = ns.replace(/\\+$/, '');
92
+ const dirNorm = dir.replace(/\\/g, '/').replace(/\/+$/, '');
93
+ psr4.set(nsNorm, dirNorm);
94
+ }
95
+ if (isDev) {
96
+ console.log(`📦 Loaded ${psr4.size} PSR-4 mappings from composer.json`);
97
+ }
98
+ return { psr4 };
99
+ }
100
+ catch {
101
+ return null;
102
+ }
103
+ }
104
+ async function loadSwiftPackageConfig(repoRoot) {
105
+ // Swift imports are module-name based (e.g., `import SiuperModel`)
106
+ // SPM convention: Sources/<TargetName>/ or Package/Sources/<TargetName>/
107
+ // We scan for these directories to build a target map
108
+ const targets = new Map();
109
+ const sourceDirs = ['Sources', 'Package/Sources', 'src'];
110
+ for (const sourceDir of sourceDirs) {
111
+ try {
112
+ const fullPath = path.join(repoRoot, sourceDir);
113
+ const entries = await fs.readdir(fullPath, { withFileTypes: true });
114
+ for (const entry of entries) {
115
+ if (entry.isDirectory()) {
116
+ targets.set(entry.name, sourceDir + '/' + entry.name);
117
+ }
118
+ }
119
+ }
120
+ catch {
121
+ // Directory doesn't exist
122
+ }
123
+ }
124
+ if (targets.size > 0) {
125
+ if (isDev) {
126
+ console.log(`📦 Loaded ${targets.size} Swift package targets`);
127
+ }
128
+ return { targets };
129
+ }
130
+ return null;
131
+ }
81
132
  // ============================================================================
82
133
  // IMPORT PATH RESOLUTION
83
134
  // ============================================================================
@@ -98,6 +149,10 @@ const EXTENSIONS = [
98
149
  '.go',
99
150
  // Rust
100
151
  '.rs', '/mod.rs',
152
+ // PHP
153
+ '.php', '.phtml',
154
+ // Swift
155
+ '.swift',
101
156
  ];
102
157
  /**
103
158
  * Try to match a path (with extensions) against the known file set.
@@ -447,6 +502,39 @@ function resolveGoPackage(importPath, goModule, normalizedFileList, allFileList)
447
502
  return matches;
448
503
  }
449
504
  // ============================================================================
505
+ // PHP PSR-4 IMPORT RESOLUTION
506
+ // ============================================================================
507
+ /**
508
+ * Resolve a PHP use-statement import path using PSR-4 mappings.
509
+ * e.g. "App\Http\Controllers\UserController" -> "app/Http/Controllers/UserController.php"
510
+ */
511
+ function resolvePhpImport(importPath, composerConfig, allFiles, normalizedFileList, allFileList, index) {
512
+ // Normalize: replace backslashes with forward slashes
513
+ const normalized = importPath.replace(/\\/g, '/');
514
+ // Try PSR-4 resolution if composer.json was found
515
+ if (composerConfig) {
516
+ // Sort namespaces by length descending (longest match wins)
517
+ const sorted = [...composerConfig.psr4.entries()].sort((a, b) => b[0].length - a[0].length);
518
+ for (const [nsPrefix, dirPrefix] of sorted) {
519
+ const nsPrefixSlash = nsPrefix.replace(/\\/g, '/');
520
+ if (normalized.startsWith(nsPrefixSlash + '/') || normalized === nsPrefixSlash) {
521
+ const remainder = normalized.slice(nsPrefixSlash.length).replace(/^\//, '');
522
+ const filePath = dirPrefix + (remainder ? '/' + remainder : '') + '.php';
523
+ if (allFiles.has(filePath))
524
+ return filePath;
525
+ if (index) {
526
+ const result = index.getInsensitive(filePath);
527
+ if (result)
528
+ return result;
529
+ }
530
+ }
531
+ }
532
+ }
533
+ // Fallback: suffix matching (works without composer.json)
534
+ const pathParts = normalized.split('/').filter(Boolean);
535
+ return suffixResolve(pathParts, normalizedFileList, allFileList, index);
536
+ }
537
+ // ============================================================================
450
538
  // MAIN IMPORT PROCESSOR
451
539
  // ============================================================================
452
540
  export const processImports = async (graph, files, astCache, importMap, onProgress, repoRoot, allPaths) => {
@@ -466,6 +554,8 @@ export const processImports = async (graph, files, astCache, importMap, onProgre
466
554
  const effectiveRoot = repoRoot || '';
467
555
  const tsconfigPaths = await loadTsconfigPaths(effectiveRoot);
468
556
  const goModule = await loadGoModulePath(effectiveRoot);
557
+ const composerConfig = await loadComposerConfig(effectiveRoot);
558
+ const swiftPackageConfig = await loadSwiftPackageConfig(effectiveRoot);
469
559
  // Helper: add an IMPORTS edge + update import map
470
560
  const addImportEdge = (filePath, resolvedPath) => {
471
561
  const sourceId = generateId('File', filePath);
@@ -577,6 +667,32 @@ export const processImports = async (graph, files, astCache, importMap, onProgre
577
667
  }
578
668
  // Fall through if no files found (package might be external)
579
669
  }
670
+ // ---- PHP: handle namespace-based imports (use statements) ----
671
+ if (language === SupportedLanguages.PHP) {
672
+ const resolved = resolvePhpImport(rawImportPath, composerConfig, allFilePaths, normalizedFileList, allFileList, index);
673
+ if (resolved) {
674
+ addImportEdge(file.path, resolved);
675
+ }
676
+ return;
677
+ }
678
+ // ---- Swift: handle module imports ----
679
+ if (language === SupportedLanguages.Swift && swiftPackageConfig) {
680
+ // Swift imports are module names: `import SiuperModel`
681
+ // Resolve to the module's source directory → all .swift files in it
682
+ const targetDir = swiftPackageConfig.targets.get(rawImportPath);
683
+ if (targetDir) {
684
+ // Find all .swift files in this target directory
685
+ const dirPrefix = targetDir + '/';
686
+ for (const filePath2 of allFileList) {
687
+ if (filePath2.startsWith(dirPrefix) && filePath2.endsWith('.swift')) {
688
+ addImportEdge(file.path, filePath2);
689
+ }
690
+ }
691
+ return;
692
+ }
693
+ // External framework (Foundation, UIKit, etc.) — skip
694
+ return;
695
+ }
580
696
  // ---- Standard single-file resolution ----
581
697
  const resolvedPath = resolveImportPath(file.path, rawImportPath, allFilePaths, allFileList, normalizedFileList, resolveCache, language, tsconfigPaths, index);
582
698
  if (resolvedPath) {
@@ -601,6 +717,8 @@ export const processImportsFromExtracted = async (graph, files, extractedImports
601
717
  const effectiveRoot = repoRoot || '';
602
718
  const tsconfigPaths = await loadTsconfigPaths(effectiveRoot);
603
719
  const goModule = await loadGoModulePath(effectiveRoot);
720
+ const composerConfig = await loadComposerConfig(effectiveRoot);
721
+ const swiftPackageConfig = await loadSwiftPackageConfig(effectiveRoot);
604
722
  const addImportEdge = (filePath, resolvedPath) => {
605
723
  const sourceId = generateId('File', filePath);
606
724
  const targetId = generateId('File', resolvedPath);
@@ -687,6 +805,28 @@ export const processImportsFromExtracted = async (graph, files, extractedImports
687
805
  continue;
688
806
  }
689
807
  }
808
+ // PHP: handle namespace-based imports (use statements)
809
+ if (language === SupportedLanguages.PHP) {
810
+ const resolved = resolvePhpImport(rawImportPath, composerConfig, allFilePaths, normalizedFileList, allFileList, index);
811
+ if (resolved) {
812
+ resolveCache.set(cacheKey, resolved);
813
+ addImportEdge(filePath, resolved);
814
+ }
815
+ continue;
816
+ }
817
+ // Swift: handle module imports
818
+ if (language === SupportedLanguages.Swift && swiftPackageConfig) {
819
+ const targetDir = swiftPackageConfig.targets.get(rawImportPath);
820
+ if (targetDir) {
821
+ const dirPrefix = targetDir + '/';
822
+ for (const fp of allFileList) {
823
+ if (fp.startsWith(dirPrefix) && fp.endsWith('.swift')) {
824
+ addImportEdge(filePath, fp);
825
+ }
826
+ }
827
+ }
828
+ continue;
829
+ }
690
830
  // Standard resolution (has its own internal cache)
691
831
  const resolvedPath = resolveImportPath(filePath, rawImportPath, allFilePaths, allFileList, normalizedFileList, resolveCache, language, tsconfigPaths, index);
692
832
  if (resolvedPath) {
@@ -3,6 +3,38 @@ import { loadParser, loadLanguage } from '../tree-sitter/parser-loader.js';
3
3
  import { LANGUAGE_QUERIES } from './tree-sitter-queries.js';
4
4
  import { generateId } from '../../lib/utils.js';
5
5
  import { getLanguageFromFilename, yieldToEventLoop } from './utils.js';
6
+ import { detectFrameworkFromAST } from './framework-detection.js';
7
+ const getDefinitionNodeFromCaptures = (captureMap) => {
8
+ const definitionKeys = [
9
+ 'definition.function',
10
+ 'definition.class',
11
+ 'definition.interface',
12
+ 'definition.method',
13
+ 'definition.struct',
14
+ 'definition.enum',
15
+ 'definition.namespace',
16
+ 'definition.module',
17
+ 'definition.trait',
18
+ 'definition.impl',
19
+ 'definition.type',
20
+ 'definition.const',
21
+ 'definition.static',
22
+ 'definition.typedef',
23
+ 'definition.macro',
24
+ 'definition.union',
25
+ 'definition.property',
26
+ 'definition.record',
27
+ 'definition.delegate',
28
+ 'definition.annotation',
29
+ 'definition.constructor',
30
+ 'definition.template',
31
+ ];
32
+ for (const key of definitionKeys) {
33
+ if (captureMap[key])
34
+ return captureMap[key];
35
+ }
36
+ return null;
37
+ };
6
38
  // ============================================================================
7
39
  // EXPORT DETECTION - Language-specific visibility detection
8
40
  // ============================================================================
@@ -94,6 +126,17 @@ const isNodeExported = (node, name, language) => {
94
126
  case 'c':
95
127
  case 'cpp':
96
128
  return false;
129
+ // Swift: Check for 'public' or 'open' access modifiers
130
+ case 'swift':
131
+ while (current) {
132
+ if (current.type === 'modifiers' || current.type === 'visibility_modifier') {
133
+ const text = current.text || '';
134
+ if (text.includes('public') || text.includes('open'))
135
+ return true;
136
+ }
137
+ current = current.parent;
138
+ }
139
+ return false;
97
140
  default:
98
141
  return false;
99
142
  }
@@ -248,14 +291,24 @@ const processParsingSequential = async (graph, files, symbolTable, astCache, onF
248
291
  const node = {
249
292
  id: nodeId,
250
293
  label: nodeLabel,
251
- properties: {
252
- name: nodeName,
253
- filePath: file.path,
254
- startLine: nameNode.startPosition.row,
255
- endLine: nameNode.endPosition.row,
256
- language: language,
257
- isExported: isNodeExported(nameNode, nodeName, language),
258
- }
294
+ properties: (() => {
295
+ const definitionNode = getDefinitionNodeFromCaptures(captureMap);
296
+ const frameworkHint = definitionNode
297
+ ? detectFrameworkFromAST(language, definitionNode.text || '')
298
+ : null;
299
+ return {
300
+ name: nodeName,
301
+ filePath: file.path,
302
+ startLine: nameNode.startPosition.row,
303
+ endLine: nameNode.endPosition.row,
304
+ language: language,
305
+ isExported: isNodeExported(nameNode, nodeName, language),
306
+ ...(frameworkHint ? {
307
+ astFrameworkMultiplier: frameworkHint.entryPointMultiplier,
308
+ astFrameworkReason: frameworkHint.reason,
309
+ } : {}),
310
+ };
311
+ })()
259
312
  };
260
313
  graph.addNode(node);
261
314
  symbolTable.add(file.path, nodeName, nodeId, nodeLabel);
@@ -178,8 +178,14 @@ const findEntryPoints = (graph, reverseCallsEdges, callsEdges) => {
178
178
  if (callees.length === 0)
179
179
  continue;
180
180
  // Calculate entry point score using new scoring system
181
- const { score, reasons } = calculateEntryPointScore(node.properties.name, node.properties.language || 'javascript', node.properties.isExported ?? false, callers.length, callees.length, filePath // Pass filePath for framework detection
181
+ const { score: baseScore, reasons } = calculateEntryPointScore(node.properties.name, node.properties.language || 'javascript', node.properties.isExported ?? false, callers.length, callees.length, filePath // Pass filePath for framework detection
182
182
  );
183
+ let score = baseScore;
184
+ const astFrameworkMultiplier = node.properties.astFrameworkMultiplier ?? 1.0;
185
+ if (astFrameworkMultiplier > 1.0) {
186
+ score *= astFrameworkMultiplier;
187
+ reasons.push(`framework-ast:${node.properties.astFrameworkReason || 'decorator'}`);
188
+ }
183
189
  if (score > 0) {
184
190
  entryPointCandidates.push({ id: node.id, score, reasons });
185
191
  }
@@ -8,4 +8,6 @@ export declare const GO_QUERIES = "\n; Functions & Methods\n(function_declaratio
8
8
  export declare const CPP_QUERIES = "\n; Classes, Structs, Namespaces\n(class_specifier name: (type_identifier) @name) @definition.class\n(struct_specifier name: (type_identifier) @name) @definition.struct\n(namespace_definition name: (namespace_identifier) @name) @definition.namespace\n(enum_specifier name: (type_identifier) @name) @definition.enum\n\n; Functions & Methods\n(function_definition declarator: (function_declarator declarator: (identifier) @name)) @definition.function\n(function_definition declarator: (function_declarator declarator: (qualified_identifier name: (identifier) @name))) @definition.method\n\n; Templates\n(template_declaration (class_specifier name: (type_identifier) @name)) @definition.template\n(template_declaration (function_definition declarator: (function_declarator declarator: (identifier) @name))) @definition.template\n\n; Includes\n(preproc_include path: (_) @import.source) @import\n\n; Calls\n(call_expression function: (identifier) @call.name) @call\n(call_expression function: (field_expression field: (field_identifier) @call.name)) @call\n(call_expression function: (qualified_identifier name: (identifier) @call.name)) @call\n(call_expression function: (template_function name: (identifier) @call.name)) @call\n\n; Heritage\n(class_specifier name: (type_identifier) @heritage.class\n (base_class_clause (type_identifier) @heritage.extends)) @heritage\n(class_specifier name: (type_identifier) @heritage.class\n (base_class_clause (access_specifier) (type_identifier) @heritage.extends)) @heritage\n";
9
9
  export declare const CSHARP_QUERIES = "\n; Types\n(class_declaration name: (identifier) @name) @definition.class\n(interface_declaration name: (identifier) @name) @definition.interface\n(struct_declaration name: (identifier) @name) @definition.struct\n(enum_declaration name: (identifier) @name) @definition.enum\n(record_declaration name: (identifier) @name) @definition.record\n(delegate_declaration name: (identifier) @name) @definition.delegate\n\n; Namespaces\n(namespace_declaration name: (identifier) @name) @definition.namespace\n(namespace_declaration name: (qualified_name) @name) @definition.namespace\n\n; Methods & Properties\n(method_declaration name: (identifier) @name) @definition.method\n(local_function_statement name: (identifier) @name) @definition.function\n(constructor_declaration name: (identifier) @name) @definition.constructor\n(property_declaration name: (identifier) @name) @definition.property\n\n; Using\n(using_directive (qualified_name) @import.source) @import\n(using_directive (identifier) @import.source) @import\n\n; Calls\n(invocation_expression function: (identifier) @call.name) @call\n(invocation_expression function: (member_access_expression name: (identifier) @call.name)) @call\n\n; Heritage\n(class_declaration name: (identifier) @heritage.class\n (base_list (simple_base_type (identifier) @heritage.extends))) @heritage\n(class_declaration name: (identifier) @heritage.class\n (base_list (simple_base_type (generic_name (identifier) @heritage.extends)))) @heritage\n";
10
10
  export declare const RUST_QUERIES = "\n; Functions & Items\n(function_item name: (identifier) @name) @definition.function\n(struct_item name: (type_identifier) @name) @definition.struct\n(enum_item name: (type_identifier) @name) @definition.enum\n(trait_item name: (type_identifier) @name) @definition.trait\n(impl_item type: (type_identifier) @name) @definition.impl\n(mod_item name: (identifier) @name) @definition.module\n\n; Type aliases, const, static, macros\n(type_item name: (type_identifier) @name) @definition.type\n(const_item name: (identifier) @name) @definition.const\n(static_item name: (identifier) @name) @definition.static\n(macro_definition name: (identifier) @name) @definition.macro\n\n; Use statements\n(use_declaration argument: (_) @import.source) @import\n\n; Calls\n(call_expression function: (identifier) @call.name) @call\n(call_expression function: (field_expression field: (field_identifier) @call.name)) @call\n(call_expression function: (scoped_identifier name: (identifier) @call.name)) @call\n(call_expression function: (generic_function function: (identifier) @call.name)) @call\n\n; Heritage (trait implementation)\n(impl_item trait: (type_identifier) @heritage.trait type: (type_identifier) @heritage.class) @heritage\n(impl_item trait: (generic_type type: (type_identifier) @heritage.trait) type: (type_identifier) @heritage.class) @heritage\n";
11
+ export declare const PHP_QUERIES = "\n; \u2500\u2500 Namespace \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(namespace_definition\n name: (namespace_name) @name) @definition.namespace\n\n; \u2500\u2500 Classes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(class_declaration\n name: (name) @name) @definition.class\n\n; \u2500\u2500 Interfaces \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(interface_declaration\n name: (name) @name) @definition.interface\n\n; \u2500\u2500 Traits \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(trait_declaration\n name: (name) @name) @definition.trait\n\n; \u2500\u2500 Enums (PHP 8.1) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(enum_declaration\n name: (name) @name) @definition.enum\n\n; \u2500\u2500 Top-level functions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(function_definition\n name: (name) @name) @definition.function\n\n; \u2500\u2500 Methods (including constructors) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(method_declaration\n name: (name) @name) @definition.method\n\n; \u2500\u2500 Class properties (including Eloquent $fillable, $casts, etc.) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(property_declaration\n (property_element\n (variable_name\n (name) @name))) @definition.property\n\n; \u2500\u2500 Imports: use statements \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n; Simple: use App\\Models\\User;\n(namespace_use_declaration\n (namespace_use_clause\n (qualified_name) @import.source)) @import\n\n; \u2500\u2500 Function/method calls \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n; Regular function call: foo()\n(function_call_expression\n function: (name) @call.name) @call\n\n; Method call: $obj->method()\n(member_call_expression\n name: (name) @call.name) @call\n\n; Nullsafe method call: $obj?->method()\n(nullsafe_member_call_expression\n name: (name) @call.name) @call\n\n; Static call: Foo::bar() (php_only uses scoped_call_expression)\n(scoped_call_expression\n name: (name) @call.name) @call\n\n; \u2500\u2500 Heritage: extends \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(class_declaration\n name: (name) @heritage.class\n (base_clause\n [(name) (qualified_name)] @heritage.extends)) @heritage\n\n; \u2500\u2500 Heritage: implements \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(class_declaration\n name: (name) @heritage.class\n (class_interface_clause\n [(name) (qualified_name)] @heritage.implements)) @heritage.impl\n\n; \u2500\u2500 Heritage: use trait (must capture enclosing class name) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n(class_declaration\n name: (name) @heritage.class\n body: (declaration_list\n (use_declaration\n [(name) (qualified_name)] @heritage.trait))) @heritage\n";
12
+ export declare const SWIFT_QUERIES = "\n; Classes\n(class_declaration \"class\" name: (type_identifier) @name) @definition.class\n\n; Structs\n(class_declaration \"struct\" name: (type_identifier) @name) @definition.struct\n\n; Enums\n(class_declaration \"enum\" name: (type_identifier) @name) @definition.enum\n\n; Extensions (mapped to class \u2014 no dedicated label in schema)\n(class_declaration \"extension\" name: (user_type (type_identifier) @name)) @definition.class\n\n; Actors\n(class_declaration \"actor\" name: (type_identifier) @name) @definition.class\n\n; Protocols (mapped to interface)\n(protocol_declaration name: (type_identifier) @name) @definition.interface\n\n; Type aliases\n(typealias_declaration name: (type_identifier) @name) @definition.type\n\n; Functions (top-level and methods)\n(function_declaration name: (simple_identifier) @name) @definition.function\n\n; Protocol method declarations\n(protocol_function_declaration name: (simple_identifier) @name) @definition.method\n\n; Initializers\n(init_declaration) @definition.constructor\n\n; Properties (stored and computed)\n(property_declaration (pattern (simple_identifier) @name)) @definition.property\n\n; Imports\n(import_declaration (identifier (simple_identifier) @import.source)) @import\n\n; Calls - direct function calls\n(call_expression (simple_identifier) @call.name) @call\n\n; Calls - member/navigation calls (obj.method())\n(call_expression (navigation_expression (navigation_suffix (simple_identifier) @call.name))) @call\n\n; Heritage - class/struct/enum inheritance and protocol conformance\n(class_declaration name: (type_identifier) @heritage.class\n (inheritance_specifier inherits_from: (user_type (type_identifier) @heritage.extends))) @heritage\n\n; Heritage - protocol inheritance\n(protocol_declaration name: (type_identifier) @heritage.class\n (inheritance_specifier inherits_from: (user_type (type_identifier) @heritage.extends))) @heritage\n";
11
13
  export declare const LANGUAGE_QUERIES: Record<SupportedLanguages, string>;