gitnexus 1.3.4 → 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.
- package/README.md +1 -1
- package/dist/config/supported-languages.d.ts +2 -1
- package/dist/config/supported-languages.js +1 -1
- package/dist/core/ingestion/call-processor.js +39 -0
- package/dist/core/ingestion/entry-point-scoring.js +23 -0
- package/dist/core/ingestion/framework-detection.d.ts +3 -0
- package/dist/core/ingestion/framework-detection.js +41 -0
- package/dist/core/ingestion/import-processor.js +63 -0
- package/dist/core/ingestion/parsing-processor.js +11 -0
- package/dist/core/ingestion/tree-sitter-queries.d.ts +1 -0
- package/dist/core/ingestion/tree-sitter-queries.js +53 -0
- package/dist/core/ingestion/utils.js +2 -0
- package/dist/core/ingestion/workers/parse-worker.js +49 -0
- package/dist/core/kuzu/schema.d.ts +1 -1
- package/dist/core/kuzu/schema.js +8 -0
- package/dist/core/tree-sitter/parser-loader.js +2 -0
- package/package.json +5 -2
- package/scripts/patch-tree-sitter-swift.cjs +74 -0
- package/skills/gitnexus-pr-review.md +163 -0
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
|
|
|
@@ -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
|
};
|
|
@@ -10,6 +10,7 @@ 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 Swift from 'tree-sitter-swift';
|
|
13
14
|
import { SupportedLanguages } from '../../../config/supported-languages.js';
|
|
14
15
|
import { LANGUAGE_QUERIES } from '../tree-sitter-queries.js';
|
|
15
16
|
import { getLanguageFromFilename } from '../utils.js';
|
|
@@ -31,6 +32,7 @@ const languageMap = {
|
|
|
31
32
|
[SupportedLanguages.Go]: Go,
|
|
32
33
|
[SupportedLanguages.Rust]: Rust,
|
|
33
34
|
[SupportedLanguages.PHP]: PHP.php_only,
|
|
35
|
+
[SupportedLanguages.Swift]: Swift,
|
|
34
36
|
};
|
|
35
37
|
const setLanguage = (language, filePath) => {
|
|
36
38
|
const key = language === SupportedLanguages.TypeScript && filePath.endsWith('.tsx')
|
|
@@ -109,6 +111,16 @@ const isNodeExported = (node, name, language) => {
|
|
|
109
111
|
case 'c':
|
|
110
112
|
case 'cpp':
|
|
111
113
|
return false;
|
|
114
|
+
case 'swift':
|
|
115
|
+
while (current) {
|
|
116
|
+
if (current.type === 'modifiers' || current.type === 'visibility_modifier') {
|
|
117
|
+
const text = current.text || '';
|
|
118
|
+
if (text.includes('public') || text.includes('open'))
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
current = current.parent;
|
|
122
|
+
}
|
|
123
|
+
return false;
|
|
112
124
|
case 'php':
|
|
113
125
|
// Top-level classes/interfaces/traits are always accessible
|
|
114
126
|
// Methods/properties are exported only if they have 'public' modifier
|
|
@@ -140,6 +152,7 @@ const FUNCTION_NODE_TYPES = new Set([
|
|
|
140
152
|
'method_declaration', 'constructor_declaration',
|
|
141
153
|
'local_function_statement', 'function_item', 'impl_item',
|
|
142
154
|
'anonymous_function_creation_expression', // PHP anonymous functions
|
|
155
|
+
'init_declaration', 'deinit_declaration', // Swift initializers/deinitializers
|
|
143
156
|
]);
|
|
144
157
|
/** Walk up AST to find enclosing function, return its generateId or null for top-level */
|
|
145
158
|
const findEnclosingFunctionId = (node, filePath) => {
|
|
@@ -148,6 +161,11 @@ const findEnclosingFunctionId = (node, filePath) => {
|
|
|
148
161
|
if (FUNCTION_NODE_TYPES.has(current.type)) {
|
|
149
162
|
let funcName = null;
|
|
150
163
|
let label = 'Function';
|
|
164
|
+
if (current.type === 'init_declaration' || current.type === 'deinit_declaration') {
|
|
165
|
+
const funcName = current.type === 'init_declaration' ? 'init' : 'deinit';
|
|
166
|
+
const label = 'Constructor';
|
|
167
|
+
return generateId(label, `${filePath}:${funcName}`);
|
|
168
|
+
}
|
|
151
169
|
if (['function_declaration', 'function_definition', 'async_function_declaration',
|
|
152
170
|
'generator_function_declaration', 'function_item'].includes(current.type)) {
|
|
153
171
|
const nameNode = current.childForFieldName?.('name') ||
|
|
@@ -254,6 +272,37 @@ const BUILT_INS = new Set([
|
|
|
254
272
|
'preg_match', 'preg_match_all', 'preg_replace', 'preg_split',
|
|
255
273
|
'header', 'session_start', 'session_destroy', 'ob_start', 'ob_end_clean', 'ob_get_clean',
|
|
256
274
|
'dd', 'dump',
|
|
275
|
+
// Swift/iOS built-ins and standard library
|
|
276
|
+
'print', 'debugPrint', 'dump', 'fatalError', 'precondition', 'preconditionFailure',
|
|
277
|
+
'assert', 'assertionFailure', 'NSLog',
|
|
278
|
+
'abs', 'min', 'max', 'zip', 'stride', 'sequence', 'repeatElement',
|
|
279
|
+
'swap', 'withUnsafePointer', 'withUnsafeMutablePointer', 'withUnsafeBytes',
|
|
280
|
+
'autoreleasepool', 'unsafeBitCast', 'unsafeDowncast', 'numericCast',
|
|
281
|
+
'type', 'MemoryLayout',
|
|
282
|
+
// Swift collection/string methods (common noise)
|
|
283
|
+
'map', 'flatMap', 'compactMap', 'filter', 'reduce', 'forEach', 'contains',
|
|
284
|
+
'first', 'last', 'prefix', 'suffix', 'dropFirst', 'dropLast',
|
|
285
|
+
'sorted', 'reversed', 'enumerated', 'joined', 'split',
|
|
286
|
+
'append', 'insert', 'remove', 'removeAll', 'removeFirst', 'removeLast',
|
|
287
|
+
'isEmpty', 'count', 'index', 'startIndex', 'endIndex',
|
|
288
|
+
// UIKit/Foundation common methods (noise in call graph)
|
|
289
|
+
'addSubview', 'removeFromSuperview', 'layoutSubviews', 'setNeedsLayout',
|
|
290
|
+
'layoutIfNeeded', 'setNeedsDisplay', 'invalidateIntrinsicContentSize',
|
|
291
|
+
'addTarget', 'removeTarget', 'addGestureRecognizer',
|
|
292
|
+
'addConstraint', 'addConstraints', 'removeConstraint', 'removeConstraints',
|
|
293
|
+
'NSLocalizedString', 'Bundle',
|
|
294
|
+
'reloadData', 'reloadSections', 'reloadRows', 'performBatchUpdates',
|
|
295
|
+
'register', 'dequeueReusableCell', 'dequeueReusableSupplementaryView',
|
|
296
|
+
'beginUpdates', 'endUpdates', 'insertRows', 'deleteRows', 'insertSections', 'deleteSections',
|
|
297
|
+
'present', 'dismiss', 'pushViewController', 'popViewController', 'popToRootViewController',
|
|
298
|
+
'performSegue', 'prepare',
|
|
299
|
+
// GCD / async
|
|
300
|
+
'DispatchQueue', 'async', 'sync', 'asyncAfter',
|
|
301
|
+
'Task', 'withCheckedContinuation', 'withCheckedThrowingContinuation',
|
|
302
|
+
// Combine
|
|
303
|
+
'sink', 'store', 'assign', 'receive', 'subscribe',
|
|
304
|
+
// Notification / KVO
|
|
305
|
+
'addObserver', 'removeObserver', 'post', 'NotificationCenter',
|
|
257
306
|
]);
|
|
258
307
|
// ============================================================================
|
|
259
308
|
// 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
|
package/dist/core/kuzu/schema.js
CHANGED
|
@@ -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,6 +9,7 @@ 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 Swift from 'tree-sitter-swift';
|
|
12
13
|
import { SupportedLanguages } from '../../config/supported-languages.js';
|
|
13
14
|
let parser = null;
|
|
14
15
|
const languageMap = {
|
|
@@ -23,6 +24,7 @@ const languageMap = {
|
|
|
23
24
|
[SupportedLanguages.Go]: Go,
|
|
24
25
|
[SupportedLanguages.Rust]: Rust,
|
|
25
26
|
[SupportedLanguages.PHP]: PHP.php_only,
|
|
27
|
+
[SupportedLanguages.Swift]: Swift,
|
|
26
28
|
};
|
|
27
29
|
export const loadParser = async () => {
|
|
28
30
|
if (parser)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gitnexus",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.5",
|
|
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",
|
|
@@ -63,6 +65,7 @@
|
|
|
63
65
|
"tree-sitter-java": "^0.21.0",
|
|
64
66
|
"tree-sitter-javascript": "^0.21.0",
|
|
65
67
|
"tree-sitter-php": "^0.23.12",
|
|
68
|
+
"tree-sitter-swift": "^0.6.0",
|
|
66
69
|
"tree-sitter-python": "^0.21.0",
|
|
67
70
|
"tree-sitter-rust": "^0.21.0",
|
|
68
71
|
"tree-sitter-typescript": "^0.21.0",
|
|
@@ -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
|
+
```
|