gitnexus 1.3.4 → 1.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -156,7 +156,7 @@ GitNexus supports indexing multiple repositories. Each `gitnexus analyze` regist
156
156
 
157
157
  ## Supported Languages
158
158
 
159
- TypeScript, JavaScript, Python, Java, C, C++, C#, Go, Rust
159
+ TypeScript, JavaScript, Python, Java, C, C++, C#, Go, Rust, PHP, Swift
160
160
 
161
161
  ## Agent Skills
162
162
 
package/dist/cli/index.js CHANGED
@@ -31,11 +31,14 @@ import { augmentCommand } from './augment.js';
31
31
  import { wikiCommand } from './wiki.js';
32
32
  import { queryCommand, contextCommand, impactCommand, cypherCommand } from './tool.js';
33
33
  import { evalServerCommand } from './eval-server.js';
34
+ import { createRequire } from 'node:module';
35
+ const _require = createRequire(import.meta.url);
36
+ const pkg = _require('../../package.json');
34
37
  const program = new Command();
35
38
  program
36
39
  .name('gitnexus')
37
40
  .description('GitNexus local CLI and MCP server')
38
- .version('1.2.0');
41
+ .version(pkg.version);
39
42
  program
40
43
  .command('setup')
41
44
  .description('One-time setup: configure MCP for Cursor, Claude Code, OpenCode')
@@ -8,5 +8,6 @@ export declare enum SupportedLanguages {
8
8
  CSharp = "csharp",
9
9
  Go = "go",
10
10
  Rust = "rust",
11
- PHP = "php"
11
+ PHP = "php",
12
+ Swift = "swift"
12
13
  }
@@ -11,5 +11,5 @@ export var SupportedLanguages;
11
11
  SupportedLanguages["Rust"] = "rust";
12
12
  SupportedLanguages["PHP"] = "php";
13
13
  // Ruby = 'ruby',
14
- // Swift = 'swift',
14
+ SupportedLanguages["Swift"] = "swift";
15
15
  })(SupportedLanguages || (SupportedLanguages = {}));
@@ -31,6 +31,9 @@ const FUNCTION_NODE_TYPES = new Set([
31
31
  // Rust
32
32
  'function_item',
33
33
  'impl_item', // Methods inside impl blocks
34
+ // Swift
35
+ 'init_declaration',
36
+ 'deinit_declaration',
34
37
  ]);
35
38
  /**
36
39
  * Walk up the AST from a node to find the enclosing function/method.
@@ -44,6 +47,11 @@ const findEnclosingFunction = (node, filePath, symbolTable) => {
44
47
  let funcName = null;
45
48
  let label = 'Function';
46
49
  // Different node types have different name locations
50
+ // Swift init/deinit — handle before generic cases (more specific)
51
+ if (current.type === 'init_declaration' || current.type === 'deinit_declaration') {
52
+ const funcName = current.type === 'init_declaration' ? 'init' : 'deinit';
53
+ return generateId('Constructor', `${filePath}:${funcName}`);
54
+ }
47
55
  if (current.type === 'function_declaration' ||
48
56
  current.type === 'function_definition' ||
49
57
  current.type === 'async_function_declaration' ||
@@ -282,6 +290,37 @@ const BUILT_IN_NAMES = new Set([
282
290
  'mutex_lock', 'mutex_unlock', 'mutex_init',
283
291
  'kfree', 'kmalloc', 'kzalloc', 'kcalloc', 'krealloc', 'kvmalloc', 'kvfree',
284
292
  'get', 'put',
293
+ // Swift/iOS built-ins and standard library
294
+ 'print', 'debugPrint', 'dump', 'fatalError', 'precondition', 'preconditionFailure',
295
+ 'assert', 'assertionFailure', 'NSLog',
296
+ 'abs', 'min', 'max', 'zip', 'stride', 'sequence', 'repeatElement',
297
+ 'swap', 'withUnsafePointer', 'withUnsafeMutablePointer', 'withUnsafeBytes',
298
+ 'autoreleasepool', 'unsafeBitCast', 'unsafeDowncast', 'numericCast',
299
+ 'type', 'MemoryLayout',
300
+ // Swift collection/string methods (common noise)
301
+ 'map', 'flatMap', 'compactMap', 'filter', 'reduce', 'forEach', 'contains',
302
+ 'first', 'last', 'prefix', 'suffix', 'dropFirst', 'dropLast',
303
+ 'sorted', 'reversed', 'enumerated', 'joined', 'split',
304
+ 'append', 'insert', 'remove', 'removeAll', 'removeFirst', 'removeLast',
305
+ 'isEmpty', 'count', 'index', 'startIndex', 'endIndex',
306
+ // UIKit/Foundation common methods (noise in call graph)
307
+ 'addSubview', 'removeFromSuperview', 'layoutSubviews', 'setNeedsLayout',
308
+ 'layoutIfNeeded', 'setNeedsDisplay', 'invalidateIntrinsicContentSize',
309
+ 'addTarget', 'removeTarget', 'addGestureRecognizer',
310
+ 'addConstraint', 'addConstraints', 'removeConstraint', 'removeConstraints',
311
+ 'NSLocalizedString', 'Bundle',
312
+ 'reloadData', 'reloadSections', 'reloadRows', 'performBatchUpdates',
313
+ 'register', 'dequeueReusableCell', 'dequeueReusableSupplementaryView',
314
+ 'beginUpdates', 'endUpdates', 'insertRows', 'deleteRows', 'insertSections', 'deleteSections',
315
+ 'present', 'dismiss', 'pushViewController', 'popViewController', 'popToRootViewController',
316
+ 'performSegue', 'prepare',
317
+ // GCD / async
318
+ 'DispatchQueue', 'async', 'sync', 'asyncAfter',
319
+ 'Task', 'withCheckedContinuation', 'withCheckedThrowingContinuation',
320
+ // Combine
321
+ 'sink', 'store', 'assign', 'receive', 'subscribe',
322
+ // Notification / KVO
323
+ 'addObserver', 'removeObserver', 'post', 'NotificationCenter',
285
324
  ]);
286
325
  const isBuiltInOrNoise = (name) => BUILT_IN_NAMES.has(name);
287
326
  /**
@@ -91,6 +91,25 @@ const ENTRY_POINT_PATTERNS = {
91
91
  /^Run$/, // Run methods
92
92
  /^Start$/, // Start methods
93
93
  ],
94
+ // Swift / iOS
95
+ 'swift': [
96
+ /^viewDidLoad$/, // UIKit lifecycle
97
+ /^viewWillAppear$/, // UIKit lifecycle
98
+ /^viewDidAppear$/, // UIKit lifecycle
99
+ /^viewWillDisappear$/, // UIKit lifecycle
100
+ /^viewDidDisappear$/, // UIKit lifecycle
101
+ /^application\(/, // AppDelegate methods
102
+ /^scene\(/, // SceneDelegate methods
103
+ /^body$/, // SwiftUI View.body
104
+ /Coordinator$/, // Coordinator pattern
105
+ /^sceneDidBecomeActive$/, // SceneDelegate lifecycle
106
+ /^sceneWillResignActive$/, // SceneDelegate lifecycle
107
+ /^didFinishLaunchingWithOptions$/, // AppDelegate
108
+ /ViewController$/, // ViewController classes
109
+ /^configure[A-Z]/, // Configuration methods
110
+ /^setup[A-Z]/, // Setup methods
111
+ /^makeBody$/, // SwiftUI ViewModifier
112
+ ],
94
113
  // PHP / Laravel
95
114
  'php': [
96
115
  /Controller$/, // UserController (class name convention)
@@ -229,6 +248,10 @@ export function isTestFile(filePath) {
229
248
  p.includes('/src/test/') ||
230
249
  // Rust test patterns (inline tests are different, but test files)
231
250
  p.includes('/tests/') ||
251
+ // Swift/iOS test patterns
252
+ p.endsWith('tests.swift') ||
253
+ p.endsWith('test.swift') ||
254
+ p.includes('uitests/') ||
232
255
  // C# test patterns
233
256
  p.includes('.tests/') ||
234
257
  p.includes('tests.cs') ||
@@ -38,6 +38,9 @@ export declare const FRAMEWORK_AST_PATTERNS: {
38
38
  actix: string[];
39
39
  axum: string[];
40
40
  rocket: string[];
41
+ uikit: string[];
42
+ swiftui: string[];
43
+ combine: string[];
41
44
  };
42
45
  /**
43
46
  * Detect framework entry points from AST definition text (decorators/annotations/attributes).
@@ -197,6 +197,43 @@ export function detectFrameworkFromPath(filePath) {
197
197
  if (p.includes('/repositories/') && p.endsWith('.php')) {
198
198
  return { framework: 'laravel', entryPointMultiplier: 1.5, reason: 'laravel-repository' };
199
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
+ }
200
237
  // ========== GENERIC PATTERNS ==========
201
238
  // Any language: index files in API folders
202
239
  if (p.includes('/api/') && (p.endsWith('/index.ts') || p.endsWith('/index.js') ||
@@ -234,6 +271,10 @@ export const FRAMEWORK_AST_PATTERNS = {
234
271
  'actix': ['#[get', '#[post', '#[put', '#[delete'],
235
272
  'axum': ['Router::new'],
236
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'],
237
278
  };
238
279
  const AST_FRAMEWORK_PATTERNS_BY_LANGUAGE = {
239
280
  javascript: [
@@ -101,6 +101,34 @@ async function loadComposerConfig(repoRoot) {
101
101
  return null;
102
102
  }
103
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
+ }
104
132
  // ============================================================================
105
133
  // IMPORT PATH RESOLUTION
106
134
  // ============================================================================
@@ -123,6 +151,8 @@ const EXTENSIONS = [
123
151
  '.rs', '/mod.rs',
124
152
  // PHP
125
153
  '.php', '.phtml',
154
+ // Swift
155
+ '.swift',
126
156
  ];
127
157
  /**
128
158
  * Try to match a path (with extensions) against the known file set.
@@ -525,6 +555,7 @@ export const processImports = async (graph, files, astCache, importMap, onProgre
525
555
  const tsconfigPaths = await loadTsconfigPaths(effectiveRoot);
526
556
  const goModule = await loadGoModulePath(effectiveRoot);
527
557
  const composerConfig = await loadComposerConfig(effectiveRoot);
558
+ const swiftPackageConfig = await loadSwiftPackageConfig(effectiveRoot);
528
559
  // Helper: add an IMPORTS edge + update import map
529
560
  const addImportEdge = (filePath, resolvedPath) => {
530
561
  const sourceId = generateId('File', filePath);
@@ -644,6 +675,24 @@ export const processImports = async (graph, files, astCache, importMap, onProgre
644
675
  }
645
676
  return;
646
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
+ }
647
696
  // ---- Standard single-file resolution ----
648
697
  const resolvedPath = resolveImportPath(file.path, rawImportPath, allFilePaths, allFileList, normalizedFileList, resolveCache, language, tsconfigPaths, index);
649
698
  if (resolvedPath) {
@@ -669,6 +718,7 @@ export const processImportsFromExtracted = async (graph, files, extractedImports
669
718
  const tsconfigPaths = await loadTsconfigPaths(effectiveRoot);
670
719
  const goModule = await loadGoModulePath(effectiveRoot);
671
720
  const composerConfig = await loadComposerConfig(effectiveRoot);
721
+ const swiftPackageConfig = await loadSwiftPackageConfig(effectiveRoot);
672
722
  const addImportEdge = (filePath, resolvedPath) => {
673
723
  const sourceId = generateId('File', filePath);
674
724
  const targetId = generateId('File', resolvedPath);
@@ -764,6 +814,19 @@ export const processImportsFromExtracted = async (graph, files, extractedImports
764
814
  }
765
815
  continue;
766
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
+ }
767
830
  // Standard resolution (has its own internal cache)
768
831
  const resolvedPath = resolveImportPath(filePath, rawImportPath, allFilePaths, allFileList, normalizedFileList, resolveCache, language, tsconfigPaths, index);
769
832
  if (resolvedPath) {
@@ -126,6 +126,17 @@ const isNodeExported = (node, name, language) => {
126
126
  case 'c':
127
127
  case 'cpp':
128
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;
129
140
  default:
130
141
  return false;
131
142
  }
@@ -9,4 +9,5 @@ export declare const CPP_QUERIES = "\n; Classes, Structs, Namespaces\n(class_spe
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
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";
12
13
  export declare const LANGUAGE_QUERIES: Record<SupportedLanguages, string>;
@@ -384,6 +384,58 @@ export const PHP_QUERIES = `
384
384
  (use_declaration
385
385
  [(name) (qualified_name)] @heritage.trait))) @heritage
386
386
  `;
387
+ // Swift queries - works with tree-sitter-swift
388
+ export const SWIFT_QUERIES = `
389
+ ; Classes
390
+ (class_declaration "class" name: (type_identifier) @name) @definition.class
391
+
392
+ ; Structs
393
+ (class_declaration "struct" name: (type_identifier) @name) @definition.struct
394
+
395
+ ; Enums
396
+ (class_declaration "enum" name: (type_identifier) @name) @definition.enum
397
+
398
+ ; Extensions (mapped to class — no dedicated label in schema)
399
+ (class_declaration "extension" name: (user_type (type_identifier) @name)) @definition.class
400
+
401
+ ; Actors
402
+ (class_declaration "actor" name: (type_identifier) @name) @definition.class
403
+
404
+ ; Protocols (mapped to interface)
405
+ (protocol_declaration name: (type_identifier) @name) @definition.interface
406
+
407
+ ; Type aliases
408
+ (typealias_declaration name: (type_identifier) @name) @definition.type
409
+
410
+ ; Functions (top-level and methods)
411
+ (function_declaration name: (simple_identifier) @name) @definition.function
412
+
413
+ ; Protocol method declarations
414
+ (protocol_function_declaration name: (simple_identifier) @name) @definition.method
415
+
416
+ ; Initializers
417
+ (init_declaration) @definition.constructor
418
+
419
+ ; Properties (stored and computed)
420
+ (property_declaration (pattern (simple_identifier) @name)) @definition.property
421
+
422
+ ; Imports
423
+ (import_declaration (identifier (simple_identifier) @import.source)) @import
424
+
425
+ ; Calls - direct function calls
426
+ (call_expression (simple_identifier) @call.name) @call
427
+
428
+ ; Calls - member/navigation calls (obj.method())
429
+ (call_expression (navigation_expression (navigation_suffix (simple_identifier) @call.name))) @call
430
+
431
+ ; Heritage - class/struct/enum inheritance and protocol conformance
432
+ (class_declaration name: (type_identifier) @heritage.class
433
+ (inheritance_specifier inherits_from: (user_type (type_identifier) @heritage.extends))) @heritage
434
+
435
+ ; Heritage - protocol inheritance
436
+ (protocol_declaration name: (type_identifier) @heritage.class
437
+ (inheritance_specifier inherits_from: (user_type (type_identifier) @heritage.extends))) @heritage
438
+ `;
387
439
  export const LANGUAGE_QUERIES = {
388
440
  [SupportedLanguages.TypeScript]: TYPESCRIPT_QUERIES,
389
441
  [SupportedLanguages.JavaScript]: JAVASCRIPT_QUERIES,
@@ -395,4 +447,5 @@ export const LANGUAGE_QUERIES = {
395
447
  [SupportedLanguages.CSharp]: CSHARP_QUERIES,
396
448
  [SupportedLanguages.Rust]: RUST_QUERIES,
397
449
  [SupportedLanguages.PHP]: PHP_QUERIES,
450
+ [SupportedLanguages.Swift]: SWIFT_QUERIES,
398
451
  };
@@ -46,5 +46,7 @@ export const getLanguageFromFilename = (filename) => {
46
46
  filename.endsWith('.php5') || filename.endsWith('.php8')) {
47
47
  return SupportedLanguages.PHP;
48
48
  }
49
+ if (filename.endsWith('.swift'))
50
+ return SupportedLanguages.Swift;
49
51
  return null;
50
52
  };
@@ -10,7 +10,15 @@ import CSharp from 'tree-sitter-c-sharp';
10
10
  import Go from 'tree-sitter-go';
11
11
  import Rust from 'tree-sitter-rust';
12
12
  import PHP from 'tree-sitter-php';
13
+ import { createRequire } from 'node:module';
13
14
  import { SupportedLanguages } from '../../../config/supported-languages.js';
15
+ // tree-sitter-swift is an optionalDependency — may not be installed
16
+ const _require = createRequire(import.meta.url);
17
+ let Swift = null;
18
+ try {
19
+ Swift = _require('tree-sitter-swift');
20
+ }
21
+ catch { }
14
22
  import { LANGUAGE_QUERIES } from '../tree-sitter-queries.js';
15
23
  import { getLanguageFromFilename } from '../utils.js';
16
24
  import { detectFrameworkFromAST } from '../framework-detection.js';
@@ -31,6 +39,7 @@ const languageMap = {
31
39
  [SupportedLanguages.Go]: Go,
32
40
  [SupportedLanguages.Rust]: Rust,
33
41
  [SupportedLanguages.PHP]: PHP.php_only,
42
+ ...(Swift ? { [SupportedLanguages.Swift]: Swift } : {}),
34
43
  };
35
44
  const setLanguage = (language, filePath) => {
36
45
  const key = language === SupportedLanguages.TypeScript && filePath.endsWith('.tsx')
@@ -109,6 +118,16 @@ const isNodeExported = (node, name, language) => {
109
118
  case 'c':
110
119
  case 'cpp':
111
120
  return false;
121
+ case 'swift':
122
+ while (current) {
123
+ if (current.type === 'modifiers' || current.type === 'visibility_modifier') {
124
+ const text = current.text || '';
125
+ if (text.includes('public') || text.includes('open'))
126
+ return true;
127
+ }
128
+ current = current.parent;
129
+ }
130
+ return false;
112
131
  case 'php':
113
132
  // Top-level classes/interfaces/traits are always accessible
114
133
  // Methods/properties are exported only if they have 'public' modifier
@@ -140,6 +159,7 @@ const FUNCTION_NODE_TYPES = new Set([
140
159
  'method_declaration', 'constructor_declaration',
141
160
  'local_function_statement', 'function_item', 'impl_item',
142
161
  'anonymous_function_creation_expression', // PHP anonymous functions
162
+ 'init_declaration', 'deinit_declaration', // Swift initializers/deinitializers
143
163
  ]);
144
164
  /** Walk up AST to find enclosing function, return its generateId or null for top-level */
145
165
  const findEnclosingFunctionId = (node, filePath) => {
@@ -148,6 +168,11 @@ const findEnclosingFunctionId = (node, filePath) => {
148
168
  if (FUNCTION_NODE_TYPES.has(current.type)) {
149
169
  let funcName = null;
150
170
  let label = 'Function';
171
+ if (current.type === 'init_declaration' || current.type === 'deinit_declaration') {
172
+ const funcName = current.type === 'init_declaration' ? 'init' : 'deinit';
173
+ const label = 'Constructor';
174
+ return generateId(label, `${filePath}:${funcName}`);
175
+ }
151
176
  if (['function_declaration', 'function_definition', 'async_function_declaration',
152
177
  'generator_function_declaration', 'function_item'].includes(current.type)) {
153
178
  const nameNode = current.childForFieldName?.('name') ||
@@ -254,6 +279,37 @@ const BUILT_INS = new Set([
254
279
  'preg_match', 'preg_match_all', 'preg_replace', 'preg_split',
255
280
  'header', 'session_start', 'session_destroy', 'ob_start', 'ob_end_clean', 'ob_get_clean',
256
281
  'dd', 'dump',
282
+ // Swift/iOS built-ins and standard library
283
+ 'print', 'debugPrint', 'dump', 'fatalError', 'precondition', 'preconditionFailure',
284
+ 'assert', 'assertionFailure', 'NSLog',
285
+ 'abs', 'min', 'max', 'zip', 'stride', 'sequence', 'repeatElement',
286
+ 'swap', 'withUnsafePointer', 'withUnsafeMutablePointer', 'withUnsafeBytes',
287
+ 'autoreleasepool', 'unsafeBitCast', 'unsafeDowncast', 'numericCast',
288
+ 'type', 'MemoryLayout',
289
+ // Swift collection/string methods (common noise)
290
+ 'map', 'flatMap', 'compactMap', 'filter', 'reduce', 'forEach', 'contains',
291
+ 'first', 'last', 'prefix', 'suffix', 'dropFirst', 'dropLast',
292
+ 'sorted', 'reversed', 'enumerated', 'joined', 'split',
293
+ 'append', 'insert', 'remove', 'removeAll', 'removeFirst', 'removeLast',
294
+ 'isEmpty', 'count', 'index', 'startIndex', 'endIndex',
295
+ // UIKit/Foundation common methods (noise in call graph)
296
+ 'addSubview', 'removeFromSuperview', 'layoutSubviews', 'setNeedsLayout',
297
+ 'layoutIfNeeded', 'setNeedsDisplay', 'invalidateIntrinsicContentSize',
298
+ 'addTarget', 'removeTarget', 'addGestureRecognizer',
299
+ 'addConstraint', 'addConstraints', 'removeConstraint', 'removeConstraints',
300
+ 'NSLocalizedString', 'Bundle',
301
+ 'reloadData', 'reloadSections', 'reloadRows', 'performBatchUpdates',
302
+ 'register', 'dequeueReusableCell', 'dequeueReusableSupplementaryView',
303
+ 'beginUpdates', 'endUpdates', 'insertRows', 'deleteRows', 'insertSections', 'deleteSections',
304
+ 'present', 'dismiss', 'pushViewController', 'popViewController', 'popToRootViewController',
305
+ 'performSegue', 'prepare',
306
+ // GCD / async
307
+ 'DispatchQueue', 'async', 'sync', 'asyncAfter',
308
+ 'Task', 'withCheckedContinuation', 'withCheckedThrowingContinuation',
309
+ // Combine
310
+ 'sink', 'store', 'assign', 'receive', 'subscribe',
311
+ // Notification / KVO
312
+ 'addObserver', 'removeObserver', 'post', 'NotificationCenter',
257
313
  ]);
258
314
  // ============================================================================
259
315
  // Label detection from capture map
@@ -41,7 +41,7 @@ export declare const ANNOTATION_SCHEMA: string;
41
41
  export declare const CONSTRUCTOR_SCHEMA: string;
42
42
  export declare const TEMPLATE_SCHEMA: string;
43
43
  export declare const MODULE_SCHEMA: string;
44
- export declare const RELATION_SCHEMA = "\nCREATE REL TABLE CodeRelation (\n FROM File TO File,\n FROM File TO Folder,\n FROM File TO Function,\n FROM File TO Class,\n FROM File TO Interface,\n FROM File TO Method,\n FROM File TO CodeElement,\n FROM File TO `Struct`,\n FROM File TO `Enum`,\n FROM File TO `Macro`,\n FROM File TO `Typedef`,\n FROM File TO `Union`,\n FROM File TO `Namespace`,\n FROM File TO `Trait`,\n FROM File TO `Impl`,\n FROM File TO `TypeAlias`,\n FROM File TO `Const`,\n FROM File TO `Static`,\n FROM File TO `Property`,\n FROM File TO `Record`,\n FROM File TO `Delegate`,\n FROM File TO `Annotation`,\n FROM File TO `Constructor`,\n FROM File TO `Template`,\n FROM File TO `Module`,\n FROM Folder TO Folder,\n FROM Folder TO File,\n FROM Function TO Function,\n FROM Function TO Method,\n FROM Function TO Class,\n FROM Function TO Community,\n FROM Function TO `Macro`,\n FROM Function TO `Struct`,\n FROM Function TO `Template`,\n FROM Function TO `Enum`,\n FROM Function TO `Namespace`,\n FROM Function TO `TypeAlias`,\n FROM Function TO `Module`,\n FROM Function TO `Impl`,\n FROM Function TO Interface,\n FROM Function TO `Constructor`,\n FROM Function TO `Const`,\n FROM Function TO `Typedef`,\n FROM Function TO `Union`,\n FROM Class TO Method,\n FROM Class TO Function,\n FROM Class TO Class,\n FROM Class TO Interface,\n FROM Class TO Community,\n FROM Class TO `Template`,\n FROM Class TO `TypeAlias`,\n FROM Class TO `Struct`,\n FROM Class TO `Enum`,\n FROM Class TO `Annotation`,\n FROM Class TO `Constructor`,\n FROM Class TO `Trait`,\n FROM Class TO `Macro`,\n FROM Class TO `Impl`,\n FROM Class TO `Union`,\n FROM Class TO `Namespace`,\n FROM Class TO `Typedef`,\n FROM Method TO Function,\n FROM Method TO Method,\n FROM Method TO Class,\n FROM Method TO Community,\n FROM Method TO `Template`,\n FROM Method TO `Struct`,\n FROM Method TO `TypeAlias`,\n FROM Method TO `Enum`,\n FROM Method TO `Macro`,\n FROM Method TO `Namespace`,\n FROM Method TO `Module`,\n FROM Method TO `Impl`,\n FROM Method TO Interface,\n FROM Method TO `Constructor`,\n FROM Method TO `Property`,\n FROM `Template` TO `Template`,\n FROM `Template` TO Function,\n FROM `Template` TO Method,\n FROM `Template` TO Class,\n FROM `Template` TO `Struct`,\n FROM `Template` TO `TypeAlias`,\n FROM `Template` TO `Enum`,\n FROM `Template` TO `Macro`,\n FROM `Template` TO Interface,\n FROM `Template` TO `Constructor`,\n FROM `Module` TO `Module`,\n FROM CodeElement TO Community,\n FROM Interface TO Community,\n FROM Interface TO Function,\n FROM Interface TO Method,\n FROM Interface TO Class,\n FROM Interface TO Interface,\n FROM Interface TO `TypeAlias`,\n FROM Interface TO `Struct`,\n FROM Interface TO `Constructor`,\n FROM `Struct` TO Community,\n FROM `Struct` TO `Trait`,\n FROM `Struct` TO `Struct`,\n FROM `Struct` TO Class,\n FROM `Struct` TO `Enum`,\n FROM `Struct` TO Function,\n FROM `Struct` TO Method,\n FROM `Enum` TO Community,\n FROM `Macro` TO Community,\n FROM `Macro` TO Function,\n FROM `Macro` TO Method,\n FROM `Module` TO Function,\n FROM `Module` TO Method,\n FROM `Typedef` TO Community,\n FROM `Union` TO Community,\n FROM `Namespace` TO Community,\n FROM `Namespace` TO `Struct`,\n FROM `Trait` TO Community,\n FROM `Impl` TO Community,\n FROM `Impl` TO `Trait`,\n FROM `Impl` TO `Struct`,\n FROM `Impl` TO `Impl`,\n FROM `TypeAlias` TO Community,\n FROM `TypeAlias` TO `Trait`,\n FROM `Const` TO Community,\n FROM `Static` TO Community,\n FROM `Property` TO Community,\n FROM `Record` TO Community,\n FROM `Delegate` TO Community,\n FROM `Annotation` TO Community,\n FROM `Constructor` TO Community,\n FROM `Constructor` TO Interface,\n FROM `Constructor` TO Class,\n FROM `Constructor` TO Method,\n FROM `Constructor` TO Function,\n FROM `Constructor` TO `Constructor`,\n FROM `Constructor` TO `Struct`,\n FROM `Constructor` TO `Macro`,\n FROM `Constructor` TO `Template`,\n FROM `Constructor` TO `TypeAlias`,\n FROM `Constructor` TO `Enum`,\n FROM `Constructor` TO `Annotation`,\n FROM `Constructor` TO `Impl`,\n FROM `Constructor` TO `Namespace`,\n FROM `Constructor` TO `Module`,\n FROM `Template` TO Community,\n FROM `Module` TO Community,\n FROM Function TO Process,\n FROM Method TO Process,\n FROM Class TO Process,\n FROM Interface TO Process,\n FROM `Struct` TO Process,\n FROM `Constructor` TO Process,\n FROM `Module` TO Process,\n FROM `Macro` TO Process,\n FROM `Impl` TO Process,\n FROM `Typedef` TO Process,\n FROM `TypeAlias` TO Process,\n FROM `Enum` TO Process,\n FROM `Union` TO Process,\n FROM `Namespace` TO Process,\n FROM `Trait` TO Process,\n FROM `Const` TO Process,\n FROM `Static` TO Process,\n FROM `Property` TO Process,\n FROM `Record` TO Process,\n FROM `Delegate` TO Process,\n FROM `Annotation` TO Process,\n FROM `Template` TO Process,\n FROM CodeElement TO Process,\n type STRING,\n confidence DOUBLE,\n reason STRING,\n step INT32\n)";
44
+ export declare const RELATION_SCHEMA = "\nCREATE REL TABLE CodeRelation (\n FROM File TO File,\n FROM File TO Folder,\n FROM File TO Function,\n FROM File TO Class,\n FROM File TO Interface,\n FROM File TO Method,\n FROM File TO CodeElement,\n FROM File TO `Struct`,\n FROM File TO `Enum`,\n FROM File TO `Macro`,\n FROM File TO `Typedef`,\n FROM File TO `Union`,\n FROM File TO `Namespace`,\n FROM File TO `Trait`,\n FROM File TO `Impl`,\n FROM File TO `TypeAlias`,\n FROM File TO `Const`,\n FROM File TO `Static`,\n FROM File TO `Property`,\n FROM File TO `Record`,\n FROM File TO `Delegate`,\n FROM File TO `Annotation`,\n FROM File TO `Constructor`,\n FROM File TO `Template`,\n FROM File TO `Module`,\n FROM Folder TO Folder,\n FROM Folder TO File,\n FROM Function TO Function,\n FROM Function TO Method,\n FROM Function TO Class,\n FROM Function TO Community,\n FROM Function TO `Macro`,\n FROM Function TO `Struct`,\n FROM Function TO `Template`,\n FROM Function TO `Enum`,\n FROM Function TO `Namespace`,\n FROM Function TO `TypeAlias`,\n FROM Function TO `Module`,\n FROM Function TO `Impl`,\n FROM Function TO Interface,\n FROM Function TO `Constructor`,\n FROM Function TO `Const`,\n FROM Function TO `Typedef`,\n FROM Function TO `Union`,\n FROM Function TO `Property`,\n FROM Class TO Method,\n FROM Class TO Function,\n FROM Class TO Class,\n FROM Class TO Interface,\n FROM Class TO Community,\n FROM Class TO `Template`,\n FROM Class TO `TypeAlias`,\n FROM Class TO `Struct`,\n FROM Class TO `Enum`,\n FROM Class TO `Annotation`,\n FROM Class TO `Constructor`,\n FROM Class TO `Trait`,\n FROM Class TO `Macro`,\n FROM Class TO `Impl`,\n FROM Class TO `Union`,\n FROM Class TO `Namespace`,\n FROM Class TO `Typedef`,\n FROM Method TO Function,\n FROM Method TO Method,\n FROM Method TO Class,\n FROM Method TO Community,\n FROM Method TO `Template`,\n FROM Method TO `Struct`,\n FROM Method TO `TypeAlias`,\n FROM Method TO `Enum`,\n FROM Method TO `Macro`,\n FROM Method TO `Namespace`,\n FROM Method TO `Module`,\n FROM Method TO `Impl`,\n FROM Method TO Interface,\n FROM Method TO `Constructor`,\n FROM Method TO `Property`,\n FROM `Template` TO `Template`,\n FROM `Template` TO Function,\n FROM `Template` TO Method,\n FROM `Template` TO Class,\n FROM `Template` TO `Struct`,\n FROM `Template` TO `TypeAlias`,\n FROM `Template` TO `Enum`,\n FROM `Template` TO `Macro`,\n FROM `Template` TO Interface,\n FROM `Template` TO `Constructor`,\n FROM `Module` TO `Module`,\n FROM CodeElement TO Community,\n FROM Interface TO Community,\n FROM Interface TO Function,\n FROM Interface TO Method,\n FROM Interface TO Class,\n FROM Interface TO Interface,\n FROM Interface TO `TypeAlias`,\n FROM Interface TO `Struct`,\n FROM Interface TO `Constructor`,\n FROM `Struct` TO Community,\n FROM `Struct` TO `Trait`,\n FROM `Struct` TO `Struct`,\n FROM `Struct` TO Class,\n FROM `Struct` TO `Enum`,\n FROM `Struct` TO Function,\n FROM `Struct` TO Method,\n FROM `Struct` TO Interface,\n FROM `Enum` TO `Enum`,\n FROM `Enum` TO Community,\n FROM `Enum` TO Class,\n FROM `Enum` TO Interface,\n FROM `Macro` TO Community,\n FROM `Macro` TO Function,\n FROM `Macro` TO Method,\n FROM `Module` TO Function,\n FROM `Module` TO Method,\n FROM `Typedef` TO Community,\n FROM `Union` TO Community,\n FROM `Namespace` TO Community,\n FROM `Namespace` TO `Struct`,\n FROM `Trait` TO Community,\n FROM `Impl` TO Community,\n FROM `Impl` TO `Trait`,\n FROM `Impl` TO `Struct`,\n FROM `Impl` TO `Impl`,\n FROM `TypeAlias` TO Community,\n FROM `TypeAlias` TO `Trait`,\n FROM `TypeAlias` TO Class,\n FROM `Const` TO Community,\n FROM `Static` TO Community,\n FROM `Property` TO Community,\n FROM `Record` TO Community,\n FROM `Delegate` TO Community,\n FROM `Annotation` TO Community,\n FROM `Constructor` TO Community,\n FROM `Constructor` TO Interface,\n FROM `Constructor` TO Class,\n FROM `Constructor` TO Method,\n FROM `Constructor` TO Function,\n FROM `Constructor` TO `Constructor`,\n FROM `Constructor` TO `Struct`,\n FROM `Constructor` TO `Macro`,\n FROM `Constructor` TO `Template`,\n FROM `Constructor` TO `TypeAlias`,\n FROM `Constructor` TO `Enum`,\n FROM `Constructor` TO `Annotation`,\n FROM `Constructor` TO `Impl`,\n FROM `Constructor` TO `Namespace`,\n FROM `Constructor` TO `Module`,\n FROM `Constructor` TO `Property`,\n FROM `Constructor` TO `Typedef`,\n FROM `Template` TO Community,\n FROM `Module` TO Community,\n FROM Function TO Process,\n FROM Method TO Process,\n FROM Class TO Process,\n FROM Interface TO Process,\n FROM `Struct` TO Process,\n FROM `Constructor` TO Process,\n FROM `Module` TO Process,\n FROM `Macro` TO Process,\n FROM `Impl` TO Process,\n FROM `Typedef` TO Process,\n FROM `TypeAlias` TO Process,\n FROM `Enum` TO Process,\n FROM `Union` TO Process,\n FROM `Namespace` TO Process,\n FROM `Trait` TO Process,\n FROM `Const` TO Process,\n FROM `Static` TO Process,\n FROM `Property` TO Process,\n FROM `Record` TO Process,\n FROM `Delegate` TO Process,\n FROM `Annotation` TO Process,\n FROM `Template` TO Process,\n FROM CodeElement TO Process,\n type STRING,\n confidence DOUBLE,\n reason STRING,\n step INT32\n)";
45
45
  export declare const EMBEDDING_SCHEMA = "\nCREATE NODE TABLE CodeEmbedding (\n nodeId STRING,\n embedding FLOAT[384],\n PRIMARY KEY (nodeId)\n)";
46
46
  /**
47
47
  * Create vector index for semantic search
@@ -219,6 +219,7 @@ CREATE REL TABLE ${REL_TABLE_NAME} (
219
219
  FROM Function TO \`Const\`,
220
220
  FROM Function TO \`Typedef\`,
221
221
  FROM Function TO \`Union\`,
222
+ FROM Function TO \`Property\`,
222
223
  FROM Class TO Method,
223
224
  FROM Class TO Function,
224
225
  FROM Class TO Class,
@@ -278,7 +279,11 @@ CREATE REL TABLE ${REL_TABLE_NAME} (
278
279
  FROM \`Struct\` TO \`Enum\`,
279
280
  FROM \`Struct\` TO Function,
280
281
  FROM \`Struct\` TO Method,
282
+ FROM \`Struct\` TO Interface,
283
+ FROM \`Enum\` TO \`Enum\`,
281
284
  FROM \`Enum\` TO Community,
285
+ FROM \`Enum\` TO Class,
286
+ FROM \`Enum\` TO Interface,
282
287
  FROM \`Macro\` TO Community,
283
288
  FROM \`Macro\` TO Function,
284
289
  FROM \`Macro\` TO Method,
@@ -295,6 +300,7 @@ CREATE REL TABLE ${REL_TABLE_NAME} (
295
300
  FROM \`Impl\` TO \`Impl\`,
296
301
  FROM \`TypeAlias\` TO Community,
297
302
  FROM \`TypeAlias\` TO \`Trait\`,
303
+ FROM \`TypeAlias\` TO Class,
298
304
  FROM \`Const\` TO Community,
299
305
  FROM \`Static\` TO Community,
300
306
  FROM \`Property\` TO Community,
@@ -316,6 +322,8 @@ CREATE REL TABLE ${REL_TABLE_NAME} (
316
322
  FROM \`Constructor\` TO \`Impl\`,
317
323
  FROM \`Constructor\` TO \`Namespace\`,
318
324
  FROM \`Constructor\` TO \`Module\`,
325
+ FROM \`Constructor\` TO \`Property\`,
326
+ FROM \`Constructor\` TO \`Typedef\`,
319
327
  FROM \`Template\` TO Community,
320
328
  FROM \`Module\` TO Community,
321
329
  FROM Function TO Process,
@@ -9,7 +9,15 @@ import CSharp from 'tree-sitter-c-sharp';
9
9
  import Go from 'tree-sitter-go';
10
10
  import Rust from 'tree-sitter-rust';
11
11
  import PHP from 'tree-sitter-php';
12
+ import { createRequire } from 'node:module';
12
13
  import { SupportedLanguages } from '../../config/supported-languages.js';
14
+ // tree-sitter-swift is an optionalDependency — may not be installed
15
+ const _require = createRequire(import.meta.url);
16
+ let Swift = null;
17
+ try {
18
+ Swift = _require('tree-sitter-swift');
19
+ }
20
+ catch { }
13
21
  let parser = null;
14
22
  const languageMap = {
15
23
  [SupportedLanguages.JavaScript]: JavaScript,
@@ -23,6 +31,7 @@ const languageMap = {
23
31
  [SupportedLanguages.Go]: Go,
24
32
  [SupportedLanguages.Rust]: Rust,
25
33
  [SupportedLanguages.PHP]: PHP.php_only,
34
+ ...(Swift ? { [SupportedLanguages.Swift]: Swift } : {}),
26
35
  };
27
36
  export const loadParser = async () => {
28
37
  if (parser)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.3.4",
3
+ "version": "1.3.6",
4
4
  "description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
5
5
  "author": "Abhigyan Patwari",
6
6
  "license": "PolyForm-Noncommercial-1.0.0",
@@ -32,13 +32,15 @@
32
32
  "files": [
33
33
  "dist",
34
34
  "hooks",
35
+ "scripts",
35
36
  "skills",
36
37
  "vendor"
37
38
  ],
38
39
  "scripts": {
39
40
  "build": "tsc",
40
41
  "dev": "tsx watch src/cli/index.ts",
41
- "prepare": "npm run build"
42
+ "prepare": "npm run build",
43
+ "postinstall": "node scripts/patch-tree-sitter-swift.cjs"
42
44
  },
43
45
  "dependencies": {
44
46
  "@huggingface/transformers": "^3.0.0",
@@ -69,6 +71,9 @@
69
71
  "typescript": "^5.4.5",
70
72
  "uuid": "^13.0.0"
71
73
  },
74
+ "optionalDependencies": {
75
+ "tree-sitter-swift": "^0.6.0"
76
+ },
72
77
  "devDependencies": {
73
78
  "@types/cli-progress": "^3.11.6",
74
79
  "@types/cors": "^2.8.17",
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * WORKAROUND: tree-sitter-swift@0.6.0 binding.gyp build failure
4
+ *
5
+ * Background:
6
+ * tree-sitter-swift@0.6.0's binding.gyp contains an "actions" array that
7
+ * invokes `tree-sitter generate` to regenerate parser.c from grammar.js.
8
+ * This is intended for grammar developers, but the published npm package
9
+ * already ships pre-generated parser files (parser.c, scanner.c), so the
10
+ * actions are unnecessary for consumers. Since consumers don't have
11
+ * tree-sitter-cli installed, the actions always fail during `npm install`.
12
+ *
13
+ * Why we can't just upgrade:
14
+ * tree-sitter-swift@0.7.1 fixes this (removes postinstall, ships prebuilds),
15
+ * but it requires tree-sitter@^0.22.1. The upstream project pins tree-sitter
16
+ * to ^0.21.0 and all other grammar packages depend on that version.
17
+ * Upgrading tree-sitter would be a separate breaking change.
18
+ *
19
+ * How this workaround works:
20
+ * 1. tree-sitter-swift's own postinstall fails (npm warns but continues)
21
+ * 2. This script runs as gitnexus's postinstall
22
+ * 3. It removes the "actions" array from binding.gyp
23
+ * 4. It rebuilds the native binding with the cleaned binding.gyp
24
+ *
25
+ * TODO: Remove this script when tree-sitter is upgraded to ^0.22.x,
26
+ * which allows using tree-sitter-swift@0.7.1+ directly.
27
+ */
28
+ const fs = require('fs');
29
+ const path = require('path');
30
+ const { execSync } = require('child_process');
31
+
32
+ const swiftDir = path.join(__dirname, '..', 'node_modules', 'tree-sitter-swift');
33
+ const bindingPath = path.join(swiftDir, 'binding.gyp');
34
+
35
+ try {
36
+ if (!fs.existsSync(bindingPath)) {
37
+ process.exit(0);
38
+ }
39
+
40
+ const content = fs.readFileSync(bindingPath, 'utf8');
41
+ let needsRebuild = false;
42
+
43
+ if (content.includes('"actions"')) {
44
+ // Strip Python-style comments (#) before JSON parsing
45
+ const cleaned = content.replace(/#[^\n]*/g, '');
46
+ const gyp = JSON.parse(cleaned);
47
+
48
+ if (gyp.targets && gyp.targets[0] && gyp.targets[0].actions) {
49
+ delete gyp.targets[0].actions;
50
+ fs.writeFileSync(bindingPath, JSON.stringify(gyp, null, 2) + '\n');
51
+ console.log('[tree-sitter-swift] Patched binding.gyp (removed actions array)');
52
+ needsRebuild = true;
53
+ }
54
+ }
55
+
56
+ // Check if native binding exists
57
+ const bindingNode = path.join(swiftDir, 'build', 'Release', 'tree_sitter_swift_binding.node');
58
+ if (!fs.existsSync(bindingNode)) {
59
+ needsRebuild = true;
60
+ }
61
+
62
+ if (needsRebuild) {
63
+ console.log('[tree-sitter-swift] Rebuilding native binding...');
64
+ execSync('npx node-gyp rebuild', {
65
+ cwd: swiftDir,
66
+ stdio: 'pipe',
67
+ timeout: 120000,
68
+ });
69
+ console.log('[tree-sitter-swift] Native binding built successfully');
70
+ }
71
+ } catch (err) {
72
+ console.warn('[tree-sitter-swift] Could not build native binding:', err.message);
73
+ console.warn('[tree-sitter-swift] You may need to manually run: cd node_modules/tree-sitter-swift && npx node-gyp rebuild');
74
+ }
@@ -0,0 +1,163 @@
1
+ ---
2
+ name: gitnexus-pr-review
3
+ description: "Use when the user wants to review a pull request, understand what a PR changes, assess risk of merging, or check for missing test coverage. Examples: \"Review this PR\", \"What does PR #42 change?\", \"Is this PR safe to merge?\""
4
+ ---
5
+
6
+ # PR Review with GitNexus
7
+
8
+ ## When to Use
9
+
10
+ - "Review this PR"
11
+ - "What does PR #42 change?"
12
+ - "Is this safe to merge?"
13
+ - "What's the blast radius of this PR?"
14
+ - "Are there missing tests for this PR?"
15
+ - Reviewing someone else's code changes before merge
16
+
17
+ ## Workflow
18
+
19
+ ```
20
+ 1. gh pr diff <number> → Get the raw diff
21
+ 2. gitnexus_detect_changes({scope: "compare", base_ref: "main"}) → Map diff to affected flows
22
+ 3. For each changed symbol:
23
+ gitnexus_impact({target: "<symbol>", direction: "upstream"}) → Blast radius per change
24
+ 4. gitnexus_context({name: "<key symbol>"}) → Understand callers/callees
25
+ 5. READ gitnexus://repo/{name}/processes → Check affected execution flows
26
+ 6. Summarize findings with risk assessment
27
+ ```
28
+
29
+ > If "Index is stale" → run `npx gitnexus analyze` in terminal before reviewing.
30
+
31
+ ## Checklist
32
+
33
+ ```
34
+ - [ ] Fetch PR diff (gh pr diff or git diff base...head)
35
+ - [ ] gitnexus_detect_changes to map changes to affected execution flows
36
+ - [ ] gitnexus_impact on each non-trivial changed symbol
37
+ - [ ] Review d=1 items (WILL BREAK) — are callers updated?
38
+ - [ ] gitnexus_context on key changed symbols to understand full picture
39
+ - [ ] Check if affected processes have test coverage
40
+ - [ ] Assess overall risk level
41
+ - [ ] Write review summary with findings
42
+ ```
43
+
44
+ ## Review Dimensions
45
+
46
+ | Dimension | How GitNexus Helps |
47
+ | --- | --- |
48
+ | **Correctness** | `context` shows callers — are they all compatible with the change? |
49
+ | **Blast radius** | `impact` shows d=1/d=2/d=3 dependents — anything missed? |
50
+ | **Completeness** | `detect_changes` shows all affected flows — are they all handled? |
51
+ | **Test coverage** | `impact({includeTests: true})` shows which tests touch changed code |
52
+ | **Breaking changes** | d=1 upstream items that aren't updated in the PR = potential breakage |
53
+
54
+ ## Risk Assessment
55
+
56
+ | Signal | Risk |
57
+ | --- | --- |
58
+ | Changes touch <3 symbols, 0-1 processes | LOW |
59
+ | Changes touch 3-10 symbols, 2-5 processes | MEDIUM |
60
+ | Changes touch >10 symbols or many processes | HIGH |
61
+ | Changes touch auth, payments, or data integrity code | CRITICAL |
62
+ | d=1 callers exist outside the PR diff | Potential breakage — flag it |
63
+
64
+ ## Tools
65
+
66
+ **gitnexus_detect_changes** — map PR diff to affected execution flows:
67
+
68
+ ```
69
+ gitnexus_detect_changes({scope: "compare", base_ref: "main"})
70
+
71
+ → Changed: 8 symbols in 4 files
72
+ → Affected processes: CheckoutFlow, RefundFlow, WebhookHandler
73
+ → Risk: MEDIUM
74
+ ```
75
+
76
+ **gitnexus_impact** — blast radius per changed symbol:
77
+
78
+ ```
79
+ gitnexus_impact({target: "validatePayment", direction: "upstream"})
80
+
81
+ → d=1 (WILL BREAK):
82
+ - processCheckout (src/checkout.ts:42) [CALLS, 100%]
83
+ - webhookHandler (src/webhooks.ts:15) [CALLS, 100%]
84
+
85
+ → d=2 (LIKELY AFFECTED):
86
+ - checkoutRouter (src/routes/checkout.ts:22) [CALLS, 95%]
87
+ ```
88
+
89
+ **gitnexus_impact with tests** — check test coverage:
90
+
91
+ ```
92
+ gitnexus_impact({target: "validatePayment", direction: "upstream", includeTests: true})
93
+
94
+ → Tests that cover this symbol:
95
+ - validatePayment.test.ts [direct]
96
+ - checkout.integration.test.ts [via processCheckout]
97
+ ```
98
+
99
+ **gitnexus_context** — understand a changed symbol's role:
100
+
101
+ ```
102
+ gitnexus_context({name: "validatePayment"})
103
+
104
+ → Incoming calls: processCheckout, webhookHandler
105
+ → Outgoing calls: verifyCard, fetchRates
106
+ → Processes: CheckoutFlow (step 3/7), RefundFlow (step 1/5)
107
+ ```
108
+
109
+ ## Example: "Review PR #42"
110
+
111
+ ```
112
+ 1. gh pr diff 42 > /tmp/pr42.diff
113
+ → 4 files changed: payments.ts, checkout.ts, types.ts, utils.ts
114
+
115
+ 2. gitnexus_detect_changes({scope: "compare", base_ref: "main"})
116
+ → Changed symbols: validatePayment, PaymentInput, formatAmount
117
+ → Affected processes: CheckoutFlow, RefundFlow
118
+ → Risk: MEDIUM
119
+
120
+ 3. gitnexus_impact({target: "validatePayment", direction: "upstream"})
121
+ → d=1: processCheckout, webhookHandler (WILL BREAK)
122
+ → webhookHandler is NOT in the PR diff — potential breakage!
123
+
124
+ 4. gitnexus_impact({target: "PaymentInput", direction: "upstream"})
125
+ → d=1: validatePayment (in PR), createPayment (NOT in PR)
126
+ → createPayment uses the old PaymentInput shape — breaking change!
127
+
128
+ 5. gitnexus_context({name: "formatAmount"})
129
+ → Called by 12 functions — but change is backwards-compatible (added optional param)
130
+
131
+ 6. Review summary:
132
+ - MEDIUM risk — 3 changed symbols affect 2 execution flows
133
+ - BUG: webhookHandler calls validatePayment but isn't updated for new signature
134
+ - BUG: createPayment depends on PaymentInput type which changed
135
+ - OK: formatAmount change is backwards-compatible
136
+ - Tests: checkout.test.ts covers processCheckout path, but no webhook test
137
+ ```
138
+
139
+ ## Review Output Format
140
+
141
+ Structure your review as:
142
+
143
+ ```markdown
144
+ ## PR Review: <title>
145
+
146
+ **Risk: LOW / MEDIUM / HIGH / CRITICAL**
147
+
148
+ ### Changes Summary
149
+ - <N> symbols changed across <M> files
150
+ - <P> execution flows affected
151
+
152
+ ### Findings
153
+ 1. **[severity]** Description of finding
154
+ - Evidence from GitNexus tools
155
+ - Affected callers/flows
156
+
157
+ ### Missing Coverage
158
+ - Callers not updated in PR: ...
159
+ - Untested flows: ...
160
+
161
+ ### Recommendation
162
+ APPROVE / REQUEST CHANGES / NEEDS DISCUSSION
163
+ ```