diff --git a/gitnexus/package-lock.json b/gitnexus/package-lock.json index 81e5dd0ce..0bbe7d72a 100644 --- a/gitnexus/package-lock.json +++ b/gitnexus/package-lock.json @@ -57,6 +57,7 @@ "@types/react-syntax-highlighter": "^15.5.13", "@vercel/node": "^5.5.16", "@vitejs/plugin-react": "^5.1.0", + "tree-sitter-wasms": "^0.1.13", "typescript": "^5.4.5", "vite": "^5.2.0", "vite-plugin-static-copy": "^3.1.4" @@ -9154,6 +9155,16 @@ "dev": true, "license": "MIT" }, + "node_modules/tree-sitter-wasms": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/tree-sitter-wasms/-/tree-sitter-wasms-0.1.13.tgz", + "integrity": "sha512-wT+cR6DwaIz80/vho3AvSF0N4txuNx/5bcRKoXouOfClpxh/qqrF4URNLQXbbt8MaAxeksZcZd1j8gcGjc+QxQ==", + "dev": true, + "license": "Unlicense", + "dependencies": { + "tree-sitter-wasms": "^0.1.11" + } + }, "node_modules/trim-lines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", diff --git a/gitnexus/package.json b/gitnexus/package.json index 6a0c60336..76571053b 100644 --- a/gitnexus/package.json +++ b/gitnexus/package.json @@ -58,6 +58,7 @@ "@types/react-syntax-highlighter": "^15.5.13", "@vercel/node": "^5.5.16", "@vitejs/plugin-react": "^5.1.0", + "tree-sitter-wasms": "^0.1.13", "typescript": "^5.4.5", "vite": "^5.2.0", "vite-plugin-static-copy": "^3.1.4" diff --git a/gitnexus/public/wasm/c/tree-sitter-c.wasm b/gitnexus/public/wasm/c/tree-sitter-c.wasm new file mode 100644 index 000000000..ceda238dc Binary files /dev/null and b/gitnexus/public/wasm/c/tree-sitter-c.wasm differ diff --git a/gitnexus/public/wasm/cpp/tree-sitter-cpp.wasm b/gitnexus/public/wasm/cpp/tree-sitter-cpp.wasm new file mode 100644 index 000000000..2d453db9d Binary files /dev/null and b/gitnexus/public/wasm/cpp/tree-sitter-cpp.wasm differ diff --git a/gitnexus/public/wasm/csharp/tree-sitter-csharp.wasm b/gitnexus/public/wasm/csharp/tree-sitter-csharp.wasm new file mode 100644 index 000000000..5c11b4f34 Binary files /dev/null and b/gitnexus/public/wasm/csharp/tree-sitter-csharp.wasm differ diff --git a/gitnexus/public/wasm/go/tree-sitter-go.wasm b/gitnexus/public/wasm/go/tree-sitter-go.wasm new file mode 100644 index 000000000..a20aba8a9 Binary files /dev/null and b/gitnexus/public/wasm/go/tree-sitter-go.wasm differ diff --git a/gitnexus/public/wasm/java/tree-sitter-java.wasm b/gitnexus/public/wasm/java/tree-sitter-java.wasm new file mode 100644 index 000000000..45022a965 Binary files /dev/null and b/gitnexus/public/wasm/java/tree-sitter-java.wasm differ diff --git a/gitnexus/public/wasm/rust/tree-sitter-rust.wasm b/gitnexus/public/wasm/rust/tree-sitter-rust.wasm new file mode 100644 index 000000000..5b3b213d0 Binary files /dev/null and b/gitnexus/public/wasm/rust/tree-sitter-rust.wasm differ diff --git a/gitnexus/src/App.tsx b/gitnexus/src/App.tsx index a9d9504e1..1288e502c 100644 --- a/gitnexus/src/App.tsx +++ b/gitnexus/src/App.tsx @@ -81,6 +81,8 @@ const AppContent = () => { console.log('📥 App.handleFileSelect - param received:', enableSmartClustering, 'provider exists:', !!getActiveProviderConfig()); const projectName = file.name.replace('.zip', ''); setProjectName(projectName); + // Set initial progress BEFORE entering loading mode to prevent black screen + setProgress({ phase: 'extracting', percent: 0, message: 'Starting...', detail: 'Preparing to extract files' }); setViewMode('loading'); try { @@ -136,6 +138,8 @@ const AppContent = () => { const projectName = firstPath.split('/')[0].replace(/-\d+$/, '') || 'repository'; setProjectName(projectName); + // Set initial progress BEFORE entering loading mode to prevent black screen + setProgress({ phase: 'extracting', percent: 0, message: 'Starting...', detail: 'Preparing to process files' }); setViewMode('loading'); try { diff --git a/gitnexus/src/components/DropZone.tsx b/gitnexus/src/components/DropZone.tsx index 66db06ba2..0bce617a9 100644 --- a/gitnexus/src/components/DropZone.tsx +++ b/gitnexus/src/components/DropZone.tsx @@ -25,10 +25,7 @@ export const DropZone = ({ onFileSelect, onGitClone }: DropZoneProps) => { useEffect(() => { const config = getActiveProviderConfig(); setHasLLMProvider(!!config); - // Auto-enable if provider is available - if (config) { - setEnableSmartClustering(true); - } + // Keep smart clustering OFF by default, user must opt-in }, []); const handleDragOver = useCallback((e: DragEvent) => { diff --git a/gitnexus/src/config/supported-languages.ts b/gitnexus/src/config/supported-languages.ts index bc7870622..15df37c54 100644 --- a/gitnexus/src/config/supported-languages.ts +++ b/gitnexus/src/config/supported-languages.ts @@ -2,12 +2,12 @@ export enum SupportedLanguages { JavaScript = 'javascript', TypeScript = 'typescript', Python = 'python', - // Java = 'java', - // C = 'c', - // CPlusPlus = 'cpp', - // CSharp = 'csharp', - // Go = 'go', - // Rust = 'rust', + Java = 'java', + C = 'c', + CPlusPlus = 'cpp', + CSharp = 'csharp', + Go = 'go', + Rust = 'rust', // PHP = 'php', // Ruby = 'ruby', // Swift = 'swift', diff --git a/gitnexus/src/core/ingestion/call-processor.ts b/gitnexus/src/core/ingestion/call-processor.ts index 77a7ef747..d0ac9b22e 100644 --- a/gitnexus/src/core/ingestion/call-processor.ts +++ b/gitnexus/src/core/ingestion/call-processor.ts @@ -23,6 +23,17 @@ const FUNCTION_NODE_TYPES = new Set([ // Common async variants 'async_function_declaration', 'async_arrow_function', + // Java + 'method_declaration', + 'constructor_declaration', + // C/C++ + // 'function_definition' already included above + // Go + // 'method_declaration' already included from Java + // C# + 'local_function_statement', + // Rust + 'function_item', ]); /** diff --git a/gitnexus/src/core/ingestion/heritage-processor.ts b/gitnexus/src/core/ingestion/heritage-processor.ts index 1abdece66..378a3bdd1 100644 --- a/gitnexus/src/core/ingestion/heritage-processor.ts +++ b/gitnexus/src/core/ingestion/heritage-processor.ts @@ -117,6 +117,33 @@ export const processHeritage = async ( }); } } + + // IMPLEMENTS (Rust): impl Trait for Struct + if (captureMap['heritage.trait'] && captureMap['heritage.class']) { + const structName = captureMap['heritage.class'].text; + const traitName = captureMap['heritage.trait'].text; + + // Resolve struct and trait IDs + const structId = symbolTable.lookupExact(file.path, structName) || + symbolTable.lookupFuzzy(structName)[0]?.nodeId || + generateId('Struct', `${file.path}:${structName}`); + + const traitId = symbolTable.lookupFuzzy(traitName)[0]?.nodeId || + generateId('Trait', `${traitName}`); + + if (structId && traitId) { + const relId = generateId('IMPLEMENTS', `${structId}->${traitId}`); + + graph.addRelationship({ + id: relId, + sourceId: structId, + targetId: traitId, + type: 'IMPLEMENTS', + confidence: 1.0, + reason: 'trait-impl', + }); + } + } }); // Cleanup diff --git a/gitnexus/src/core/ingestion/import-processor.ts b/gitnexus/src/core/ingestion/import-processor.ts index 9edc7d358..0dd9b4893 100644 --- a/gitnexus/src/core/ingestion/import-processor.ts +++ b/gitnexus/src/core/ingestion/import-processor.ts @@ -35,8 +35,24 @@ const resolveImportPath = ( const basePath = currentDir.join('/'); - // 3. Try extensions (prioritize .tsx for React projects) - const extensions = ['', '.tsx', '.ts', '.jsx', '.js', '/index.tsx', '/index.ts', '/index.jsx', '/index.js']; + // 3. Try extensions for all supported languages + const extensions = [ + '', + // TypeScript/JavaScript + '.tsx', '.ts', '.jsx', '.js', '/index.tsx', '/index.ts', '/index.jsx', '/index.js', + // Python + '.py', '/__init__.py', + // Java + '.java', + // C/C++ + '.c', '.h', '.cpp', '.hpp', '.cc', '.cxx', '.hxx', '.hh', + // C# + '.cs', + // Go + '.go', + // Rust + '.rs', '/mod.rs' + ]; for (const ext of extensions) { const candidate = basePath + ext; diff --git a/gitnexus/src/core/ingestion/parsing-processor.ts b/gitnexus/src/core/ingestion/parsing-processor.ts index 0b5546ce9..79d53216e 100644 --- a/gitnexus/src/core/ingestion/parsing-processor.ts +++ b/gitnexus/src/core/ingestion/parsing-processor.ts @@ -81,10 +81,37 @@ export const processParsing = async ( let nodeLabel = 'CodeElement'; + // Core types if (captureMap['definition.function']) nodeLabel = 'Function'; else if (captureMap['definition.class']) nodeLabel = 'Class'; else if (captureMap['definition.interface']) nodeLabel = 'Interface'; else if (captureMap['definition.method']) nodeLabel = 'Method'; + // Struct types (C, C++, Go, Rust, C#) + else if (captureMap['definition.struct']) nodeLabel = 'Struct'; + // Enum types + else if (captureMap['definition.enum']) nodeLabel = 'Enum'; + // Namespace/Module (C++, C#, Rust) + else if (captureMap['definition.namespace']) nodeLabel = 'Namespace'; + else if (captureMap['definition.module']) nodeLabel = 'Module'; + // Rust-specific + else if (captureMap['definition.trait']) nodeLabel = 'Trait'; + else if (captureMap['definition.impl']) nodeLabel = 'Impl'; + else if (captureMap['definition.type']) nodeLabel = 'TypeAlias'; + else if (captureMap['definition.const']) nodeLabel = 'Const'; + else if (captureMap['definition.static']) nodeLabel = 'Static'; + // C-specific + else if (captureMap['definition.typedef']) nodeLabel = 'Typedef'; + else if (captureMap['definition.macro']) nodeLabel = 'Macro'; + else if (captureMap['definition.union']) nodeLabel = 'Union'; + // C#-specific + else if (captureMap['definition.property']) nodeLabel = 'Property'; + else if (captureMap['definition.record']) nodeLabel = 'Record'; + else if (captureMap['definition.delegate']) nodeLabel = 'Delegate'; + // Java-specific + else if (captureMap['definition.annotation']) nodeLabel = 'Annotation'; + else if (captureMap['definition.constructor']) nodeLabel = 'Constructor'; + // C++ template + else if (captureMap['definition.template']) nodeLabel = 'Template'; const nodeId = generateId(nodeLabel, `${file.path}:${nodeName}`); diff --git a/gitnexus/src/core/ingestion/tree-sitter-queries.ts b/gitnexus/src/core/ingestion/tree-sitter-queries.ts index 674059649..464ed1f86 100644 --- a/gitnexus/src/core/ingestion/tree-sitter-queries.ts +++ b/gitnexus/src/core/ingestion/tree-sitter-queries.ts @@ -148,9 +148,184 @@ export const PYTHON_QUERIES = ` (identifier) @heritage.extends)) @heritage `; +// Java queries - works with tree-sitter-java +export const JAVA_QUERIES = ` +; Classes, Interfaces, Enums, Annotations +(class_declaration name: (identifier) @name) @definition.class +(interface_declaration name: (identifier) @name) @definition.interface +(enum_declaration name: (identifier) @name) @definition.enum +(annotation_type_declaration name: (identifier) @name) @definition.annotation + +; Methods & Constructors +(method_declaration name: (identifier) @name) @definition.method +(constructor_declaration name: (identifier) @name) @definition.constructor + +; Imports +(import_declaration (scoped_identifier) @import.source) @import + +; Calls +(method_invocation name: (identifier) @call.name) @call +(method_invocation object: (_) name: (identifier) @call.name) @call + +; Heritage - extends class +(class_declaration name: (identifier) @heritage.class + (superclass (type_identifier) @heritage.extends)) @heritage + +; Heritage - implements interfaces +(class_declaration name: (identifier) @heritage.class + (super_interfaces (type_list (type_identifier) @heritage.implements))) @heritage.impl +`; + +// C queries - works with tree-sitter-c +export const C_QUERIES = ` +; Functions +(function_definition declarator: (function_declarator declarator: (identifier) @name)) @definition.function +(declaration declarator: (function_declarator declarator: (identifier) @name)) @definition.function + +; Structs, Unions, Enums, Typedefs +(struct_specifier name: (type_identifier) @name) @definition.struct +(union_specifier name: (type_identifier) @name) @definition.union +(enum_specifier name: (type_identifier) @name) @definition.enum +(type_definition declarator: (type_identifier) @name) @definition.typedef + +; Macros +(preproc_function_def name: (identifier) @name) @definition.macro +(preproc_def name: (identifier) @name) @definition.macro + +; Includes +(preproc_include path: (_) @import.source) @import + +; Calls +(call_expression function: (identifier) @call.name) @call +(call_expression function: (field_expression field: (field_identifier) @call.name)) @call +`; + +// Go queries - works with tree-sitter-go +export const GO_QUERIES = ` +; Functions & Methods +(function_declaration name: (identifier) @name) @definition.function +(method_declaration name: (field_identifier) @name) @definition.method + +; Types +(type_declaration (type_spec name: (type_identifier) @name type: (struct_type))) @definition.struct +(type_declaration (type_spec name: (type_identifier) @name type: (interface_type))) @definition.interface +(type_declaration (type_spec name: (type_identifier) @name)) @definition.type + +; Imports +(import_declaration (import_spec path: (interpreted_string_literal) @import.source)) @import +(import_declaration (import_spec_list (import_spec path: (interpreted_string_literal) @import.source))) @import + +; Calls +(call_expression function: (identifier) @call.name) @call +(call_expression function: (selector_expression field: (field_identifier) @call.name)) @call +`; + +// C++ queries - works with tree-sitter-cpp +export const CPP_QUERIES = ` +; Classes, Structs, Namespaces +(class_specifier name: (type_identifier) @name) @definition.class +(struct_specifier name: (type_identifier) @name) @definition.struct +(namespace_definition name: (namespace_identifier) @name) @definition.namespace +(enum_specifier name: (type_identifier) @name) @definition.enum + +; Functions & Methods +(function_definition declarator: (function_declarator declarator: (identifier) @name)) @definition.function +(function_definition declarator: (function_declarator declarator: (qualified_identifier name: (identifier) @name))) @definition.method + +; Templates +(template_declaration (class_specifier name: (type_identifier) @name)) @definition.template +(template_declaration (function_definition declarator: (function_declarator declarator: (identifier) @name))) @definition.template + +; Includes +(preproc_include path: (_) @import.source) @import + +; Calls +(call_expression function: (identifier) @call.name) @call +(call_expression function: (field_expression field: (field_identifier) @call.name)) @call +(call_expression function: (qualified_identifier name: (identifier) @call.name)) @call +(call_expression function: (template_function name: (identifier) @call.name)) @call + +; Heritage +(class_specifier name: (type_identifier) @heritage.class + (base_class_clause (type_identifier) @heritage.extends)) @heritage +(class_specifier name: (type_identifier) @heritage.class + (base_class_clause (access_specifier) (type_identifier) @heritage.extends)) @heritage +`; + +// C# queries - works with tree-sitter-c-sharp +export const CSHARP_QUERIES = ` +; Types +(class_declaration name: (identifier) @name) @definition.class +(interface_declaration name: (identifier) @name) @definition.interface +(struct_declaration name: (identifier) @name) @definition.struct +(enum_declaration name: (identifier) @name) @definition.enum +(record_declaration name: (identifier) @name) @definition.record +(delegate_declaration name: (identifier) @name) @definition.delegate + +; Namespaces +(namespace_declaration name: (identifier) @name) @definition.namespace +(namespace_declaration name: (qualified_name) @name) @definition.namespace + +; Methods & Properties +(method_declaration name: (identifier) @name) @definition.method +(local_function_statement name: (identifier) @name) @definition.function +(constructor_declaration name: (identifier) @name) @definition.constructor +(property_declaration name: (identifier) @name) @definition.property + +; Using +(using_directive (qualified_name) @import.source) @import +(using_directive (identifier) @import.source) @import + +; Calls +(invocation_expression function: (identifier) @call.name) @call +(invocation_expression function: (member_access_expression name: (identifier) @call.name)) @call + +; Heritage +(class_declaration name: (identifier) @heritage.class + (base_list (simple_base_type (identifier) @heritage.extends))) @heritage +(class_declaration name: (identifier) @heritage.class + (base_list (simple_base_type (generic_name (identifier) @heritage.extends)))) @heritage +`; + +// Rust queries - works with tree-sitter-rust +export const RUST_QUERIES = ` +; Functions & Items +(function_item name: (identifier) @name) @definition.function +(struct_item name: (type_identifier) @name) @definition.struct +(enum_item name: (type_identifier) @name) @definition.enum +(trait_item name: (type_identifier) @name) @definition.trait +(impl_item type: (type_identifier) @name) @definition.impl +(mod_item name: (identifier) @name) @definition.module + +; Type aliases, const, static, macros +(type_item name: (type_identifier) @name) @definition.type +(const_item name: (identifier) @name) @definition.const +(static_item name: (identifier) @name) @definition.static +(macro_definition name: (identifier) @name) @definition.macro + +; Use statements +(use_declaration argument: (_) @import.source) @import + +; Calls +(call_expression function: (identifier) @call.name) @call +(call_expression function: (field_expression field: (field_identifier) @call.name)) @call +(call_expression function: (scoped_identifier name: (identifier) @call.name)) @call +(call_expression function: (generic_function function: (identifier) @call.name)) @call + +; Heritage (trait implementation) +(impl_item trait: (type_identifier) @heritage.trait type: (type_identifier) @heritage.class) @heritage +(impl_item trait: (generic_type type: (type_identifier) @heritage.trait) type: (type_identifier) @heritage.class) @heritage +`; + export const LANGUAGE_QUERIES: Record = { [SupportedLanguages.TypeScript]: TYPESCRIPT_QUERIES, [SupportedLanguages.JavaScript]: JAVASCRIPT_QUERIES, [SupportedLanguages.Python]: PYTHON_QUERIES, + [SupportedLanguages.Java]: JAVA_QUERIES, + [SupportedLanguages.C]: C_QUERIES, + [SupportedLanguages.Go]: GO_QUERIES, + [SupportedLanguages.CPlusPlus]: CPP_QUERIES, + [SupportedLanguages.CSharp]: CSHARP_QUERIES, + [SupportedLanguages.Rust]: RUST_QUERIES, }; \ No newline at end of file diff --git a/gitnexus/src/core/ingestion/utils.ts b/gitnexus/src/core/ingestion/utils.ts index f61b0da71..959eb55dc 100644 --- a/gitnexus/src/core/ingestion/utils.ts +++ b/gitnexus/src/core/ingestion/utils.ts @@ -4,11 +4,27 @@ import { SupportedLanguages } from '../../config/supported-languages'; * Map file extension to SupportedLanguage enum */ export const getLanguageFromFilename = (filename: string): SupportedLanguages | null => { + // TypeScript (including TSX) if (filename.endsWith('.tsx')) return SupportedLanguages.TypeScript; if (filename.endsWith('.ts')) return SupportedLanguages.TypeScript; + // JavaScript (including JSX) if (filename.endsWith('.jsx')) return SupportedLanguages.JavaScript; if (filename.endsWith('.js')) return SupportedLanguages.JavaScript; + // Python if (filename.endsWith('.py')) return SupportedLanguages.Python; + // Java + if (filename.endsWith('.java')) return SupportedLanguages.Java; + // C (source and headers) + if (filename.endsWith('.c') || filename.endsWith('.h')) return SupportedLanguages.C; + // C++ (all common extensions) + if (filename.endsWith('.cpp') || filename.endsWith('.cc') || filename.endsWith('.cxx') || + filename.endsWith('.hpp') || filename.endsWith('.hxx') || filename.endsWith('.hh')) return SupportedLanguages.CPlusPlus; + // C# + if (filename.endsWith('.cs')) return SupportedLanguages.CSharp; + // Go + if (filename.endsWith('.go')) return SupportedLanguages.Go; + // Rust + if (filename.endsWith('.rs')) return SupportedLanguages.Rust; return null; }; diff --git a/gitnexus/src/core/kuzu/kuzu-adapter.ts b/gitnexus/src/core/kuzu/kuzu-adapter.ts index c57a01d1f..73aa40b25 100644 --- a/gitnexus/src/core/kuzu/kuzu-adapter.ts +++ b/gitnexus/src/core/kuzu/kuzu-adapter.ts @@ -137,8 +137,14 @@ export const loadGraphToKuzu = async ( return nodeId.split(':')[0]; }; - const fromLabel = getNodeLabel(fromId); - const toLabel = getNodeLabel(toId); + // Reserved Cypher keywords need backtick escaping + const RESERVED_LABELS = ['Macro', 'Enum', 'Union', 'Const', 'Module', 'Struct']; + const escapeLabel = (label: string): string => { + return RESERVED_LABELS.includes(label) ? `\`${label}\`` : label; + }; + + const fromLabel = escapeLabel(getNodeLabel(fromId)); + const toLabel = escapeLabel(getNodeLabel(toId)); // INSERT with explicit node matching (including confidence and reason) const insertQuery = ` @@ -148,21 +154,24 @@ export const loadGraphToKuzu = async ( `; await conn.query(insertQuery); insertedRels++; - } catch { + } catch (err) { // Skip failed insertions (nodes might not exist, or relation pair not allowed by schema) skippedRels++; - if (import.meta.env.DEV) { - const match = line.match(/"([^"]*)","([^"]*)","([^"]*)",([0-9.]+),"([^"]*)"/); - if (match) { - const [, fromId, toId, relType] = match; - const getNodeLabel = (nodeId: string): string => { - if (nodeId.startsWith('comm_')) return 'Community'; - return nodeId.split(':')[0]; - }; - const fromLabel = getNodeLabel(fromId); - const toLabel = getNodeLabel(toId); - const key = `${relType}:${fromLabel}->` + toLabel; - skippedRelStats.set(key, (skippedRelStats.get(key) || 0) + 1); + const match = line.match(/"([^"]*)","([^"]*)","([^"]*)",([0-9.]+),"([^"]*)"/); + if (match) { + const [, fromId, toId, relType] = match; + const getNodeLabel = (nodeId: string): string => { + if (nodeId.startsWith('comm_')) return 'Community'; + return nodeId.split(':')[0]; + }; + const fromLabel = getNodeLabel(fromId); + const toLabel = getNodeLabel(toId); + const key = `${relType}:${fromLabel}->` + toLabel; + skippedRelStats.set(key, (skippedRelStats.get(key) || 0) + 1); + + // Log each skipped relation + if (import.meta.env.DEV) { + console.warn(`⚠️ Skipped: ${key} | "${fromId}" → "${toId}" | ${err instanceof Error ? err.message : String(err)}`); } } } diff --git a/gitnexus/src/core/kuzu/schema.ts b/gitnexus/src/core/kuzu/schema.ts index 49394d50c..437383c2d 100644 --- a/gitnexus/src/core/kuzu/schema.ts +++ b/gitnexus/src/core/kuzu/schema.ts @@ -12,7 +12,12 @@ // ============================================================================ // NODE TABLE NAMES // ============================================================================ -export const NODE_TABLES = ['File', 'Folder', 'Function', 'Class', 'Interface', 'Method', 'CodeElement', 'Community'] as const; +export const NODE_TABLES = [ + 'File', 'Folder', 'Function', 'Class', 'Interface', 'Method', 'CodeElement', 'Community', + // Multi-language support + 'Struct', 'Enum', 'Macro', 'Typedef', 'Union', 'Namespace', 'Trait', 'Impl', + 'TypeAlias', 'Const', 'Static', 'Property', 'Record', 'Delegate', 'Annotation', 'Constructor', 'Template', 'Module' +] as const; export type NodeTableName = typeof NODE_TABLES[number]; // ============================================================================ @@ -122,6 +127,41 @@ CREATE NODE TABLE Community ( PRIMARY KEY (id) )`; +// ============================================================================ +// MULTI-LANGUAGE NODE TABLE SCHEMAS +// ============================================================================ + +// Generic code element with startLine/endLine for C, C++, Rust, Go, Java, C# +const CODE_ELEMENT_BASE = (name: string) => ` +CREATE NODE TABLE \`${name}\` ( + id STRING, + name STRING, + filePath STRING, + startLine INT64, + endLine INT64, + content STRING, + PRIMARY KEY (id) +)`; + +export const STRUCT_SCHEMA = CODE_ELEMENT_BASE('Struct'); +export const ENUM_SCHEMA = CODE_ELEMENT_BASE('Enum'); +export const MACRO_SCHEMA = CODE_ELEMENT_BASE('Macro'); +export const TYPEDEF_SCHEMA = CODE_ELEMENT_BASE('Typedef'); +export const UNION_SCHEMA = CODE_ELEMENT_BASE('Union'); +export const NAMESPACE_SCHEMA = CODE_ELEMENT_BASE('Namespace'); +export const TRAIT_SCHEMA = CODE_ELEMENT_BASE('Trait'); +export const IMPL_SCHEMA = CODE_ELEMENT_BASE('Impl'); +export const TYPE_ALIAS_SCHEMA = CODE_ELEMENT_BASE('TypeAlias'); +export const CONST_SCHEMA = CODE_ELEMENT_BASE('Const'); +export const STATIC_SCHEMA = CODE_ELEMENT_BASE('Static'); +export const PROPERTY_SCHEMA = CODE_ELEMENT_BASE('Property'); +export const RECORD_SCHEMA = CODE_ELEMENT_BASE('Record'); +export const DELEGATE_SCHEMA = CODE_ELEMENT_BASE('Delegate'); +export const ANNOTATION_SCHEMA = CODE_ELEMENT_BASE('Annotation'); +export const CONSTRUCTOR_SCHEMA = CODE_ELEMENT_BASE('Constructor'); +export const TEMPLATE_SCHEMA = CODE_ELEMENT_BASE('Template'); +export const MODULE_SCHEMA = CODE_ELEMENT_BASE('Module'); + // ============================================================================ // RELATION TABLE SCHEMA // Single table with 'type' property - connects all node tables @@ -136,23 +176,79 @@ CREATE REL TABLE ${REL_TABLE_NAME} ( FROM File TO Interface, FROM File TO Method, FROM File TO CodeElement, + FROM File TO \`Struct\`, + FROM File TO \`Enum\`, + FROM File TO \`Macro\`, + FROM File TO Typedef, + FROM File TO \`Union\`, + FROM File TO Namespace, + FROM File TO Trait, + FROM File TO Impl, + FROM File TO TypeAlias, + FROM File TO \`Const\`, + FROM File TO Static, + FROM File TO Property, + FROM File TO Record, + FROM File TO Delegate, + FROM File TO Annotation, + FROM File TO Constructor, + FROM File TO Template, + FROM File TO \`Module\`, FROM Folder TO Folder, FROM Folder TO File, FROM Function TO Function, FROM Function TO Method, FROM Function TO Class, FROM Function TO Community, + FROM Function TO \`Macro\`, + FROM Function TO \`Struct\`, + FROM Function TO Template, + FROM Function TO \`Enum\`, + FROM Function TO Namespace, + FROM Function TO TypeAlias, FROM Class TO Method, FROM Class TO Function, FROM Class TO Class, FROM Class TO Interface, FROM Class TO Community, + FROM Class TO Template, FROM Method TO Function, FROM Method TO Method, FROM Method TO Class, FROM Method TO Community, + FROM Method TO Template, + FROM Method TO \`Struct\`, + FROM Template TO Template, + FROM Template TO Function, + FROM Template TO Method, + FROM Template TO Class, + FROM Template TO \`Struct\`, FROM CodeElement TO Community, FROM Interface TO Community, + FROM \`Struct\` TO Community, + FROM \`Struct\` TO Trait, + FROM \`Struct\` TO Function, + FROM \`Struct\` TO Method, + FROM \`Enum\` TO Community, + FROM \`Macro\` TO Community, + FROM Typedef TO Community, + FROM \`Union\` TO Community, + FROM Namespace TO Community, + FROM Trait TO Community, + FROM Impl TO Community, + FROM Impl TO Trait, + FROM TypeAlias TO Community, + FROM \`Const\` TO Community, + FROM Static TO Community, + FROM Property TO Community, + FROM Record TO Community, + FROM Delegate TO Community, + FROM Annotation TO Community, + FROM Constructor TO Community, + FROM Constructor TO Interface, + FROM Constructor TO Class, + FROM Template TO Community, + FROM \`Module\` TO Community, type STRING, confidence DOUBLE, reason STRING @@ -192,6 +288,25 @@ export const NODE_SCHEMA_QUERIES = [ METHOD_SCHEMA, CODE_ELEMENT_SCHEMA, COMMUNITY_SCHEMA, + // Multi-language support + STRUCT_SCHEMA, + ENUM_SCHEMA, + MACRO_SCHEMA, + TYPEDEF_SCHEMA, + UNION_SCHEMA, + NAMESPACE_SCHEMA, + TRAIT_SCHEMA, + IMPL_SCHEMA, + TYPE_ALIAS_SCHEMA, + CONST_SCHEMA, + STATIC_SCHEMA, + PROPERTY_SCHEMA, + RECORD_SCHEMA, + DELEGATE_SCHEMA, + ANNOTATION_SCHEMA, + CONSTRUCTOR_SCHEMA, + TEMPLATE_SCHEMA, + MODULE_SCHEMA, ]; export const REL_SCHEMA_QUERIES = [ diff --git a/gitnexus/src/core/tree-sitter/parser-loader.ts b/gitnexus/src/core/tree-sitter/parser-loader.ts index e6092f8b6..d5224ca4e 100644 --- a/gitnexus/src/core/tree-sitter/parser-loader.ts +++ b/gitnexus/src/core/tree-sitter/parser-loader.ts @@ -33,6 +33,12 @@ const getWasmPath = (language: SupportedLanguages, filePath?: string): string => [SupportedLanguages.JavaScript]: '/wasm/javascript/tree-sitter-javascript.wasm', [SupportedLanguages.TypeScript]: '/wasm/typescript/tree-sitter-typescript.wasm', [SupportedLanguages.Python]: '/wasm/python/tree-sitter-python.wasm', + [SupportedLanguages.Java]: '/wasm/java/tree-sitter-java.wasm', + [SupportedLanguages.C]: '/wasm/c/tree-sitter-c.wasm', + [SupportedLanguages.CPlusPlus]: '/wasm/cpp/tree-sitter-cpp.wasm', + [SupportedLanguages.CSharp]: '/wasm/csharp/tree-sitter-csharp.wasm', + [SupportedLanguages.Go]: '/wasm/go/tree-sitter-go.wasm', + [SupportedLanguages.Rust]: '/wasm/rust/tree-sitter-rust.wasm', }; return languageFileMap[language]; @@ -40,18 +46,27 @@ const getWasmPath = (language: SupportedLanguages, filePath?: string): string => export const loadLanguage = async (language: SupportedLanguages, filePath?: string): Promise => { if (!parser) await loadParser(); - const wasmPath = getWasmPath(language, filePath); - // Use wasmPath as cache key to differentiate ts vs tsx if (languageCache.has(wasmPath)) { parser!.setLanguage(languageCache.get(wasmPath)!); return; } - if (!wasmPath) throw new Error(`Unsupported language: ${language}`); + if (!wasmPath) { + console.error(`❌ [Parser] No WASM path configured for language: ${language}`); + throw new Error(`Unsupported language: ${language}`); + } - const loadedLanguage = await Parser.Language.load(wasmPath); - languageCache.set(wasmPath, loadedLanguage); - parser!.setLanguage(loadedLanguage); + try { + const loadedLanguage = await Parser.Language.load(wasmPath); + languageCache.set(wasmPath, loadedLanguage); + parser!.setLanguage(loadedLanguage); + } catch (error: unknown) { + const errorMessage = error instanceof Error ? error.message : String(error); + console.error(`❌ [Parser] Failed to load WASM grammar for ${language}`); + console.error(` WASM Path: ${wasmPath}`); + console.error(` Error: ${errorMessage}`); + throw new Error(`Failed to load grammar for ${language}: ${errorMessage}`); + } } diff --git a/gitnexus/src/services/git-clone.ts b/gitnexus/src/services/git-clone.ts index 8d4627377..b12bc6686 100644 --- a/gitnexus/src/services/git-clone.ts +++ b/gitnexus/src/services/git-clone.ts @@ -17,25 +17,22 @@ const initFS = () => { return fsName; }; -// Use public proxy in development, a custom proxy in production -const USE_OWN_PROXY = !import.meta.env.DEV; +// Hosted proxy URL - use this for localhost to avoid local proxy issues +const HOSTED_PROXY_URL = 'https://gitnexus.vercel.app/api/proxy'; /** - * Custom HTTP client that uses a query-param based proxy in production - * isomorphic-git's default corsProxy appends URL as path, which doesn't work - * well with Vercel's file-based routing. + * Custom HTTP client that uses a query-param based proxy + * - In development (localhost): uses the hosted Vercel proxy for reliability + * - In production: uses the local /api/proxy endpoint */ const createProxiedHttp = (): typeof http => { - if (!USE_OWN_PROXY) { - // In dev, use the public proxy via isomorphic-git's built-in corsProxy option - return http; - } - - // In production, wrap the HTTP client to use the custom proxy + const isDev = typeof window !== 'undefined' && window.location.hostname === 'localhost'; + return { request: async (config) => { - // Rewrite the URL to go through the proxy - const proxyUrl = `/api/proxy?url=${encodeURIComponent(config.url)}`; + // Use hosted proxy for localhost, local proxy for production + const proxyBase = isDev ? HOSTED_PROXY_URL : '/api/proxy'; + const proxyUrl = `${proxyBase}?url=${encodeURIComponent(config.url)}`; // Call the original http.request with the proxied URL return http.request({ @@ -100,9 +97,6 @@ export const cloneRepository = async ( http: httpClient, dir, url: repoUrl, - // Only use corsProxy in dev mode (with public proxy) - ...(import.meta.env.DEV ? { corsProxy: 'https://cors.isomorphic-git.org' } : {}), - singleBranch: true, depth: 1, // Auth callback for private repos (PAT stays client-side) onAuth: token ? () => ({ username: token, password: 'x-oauth-basic' }) : undefined,