Merge remote-tracking branch 'origin/main' into sm11-review-fixes

This commit is contained in:
Gergo Magyar 2026-04-09 09:36:17 +01:00
commit 50e073f05f
9 changed files with 42 additions and 9 deletions

View file

@ -52,7 +52,7 @@ https://github.com/user-attachments/assets/172685ba-8e54-4ea7-9ad1-e31a3398da72
| **What** | Index repos locally, connect AI agents via MCP | Visual graph explorer + AI chat in browser |
| **For** | Daily development with Cursor, Claude Code, Codex, Windsurf, OpenCode | Quick exploration, demos, one-off analysis |
| **Scale** | Full repos, any size | Limited by browser memory (~5k files), or unlimited via backend mode |
| **Install** | `npm install -g gitnexus` | No install —[gitnexus.vercel.app](https://gitnexus.vercel.app) |
| **Install** | `npm install -g gitnexus` | No install — [gitnexus.vercel.app](https://gitnexus.vercel.app) |
| **Storage** | LadybugDB native (fast, persistent) | LadybugDB WASM (in-memory, per session) |
| **Parsing** | Tree-sitter native bindings | Tree-sitter WASM |
| **Privacy** | Everything local, no network | Everything in-browser, no server |

View file

@ -201,7 +201,7 @@ interface OnboardingGuideProps {
}
export const OnboardingGuide = ({ isPolling }: OnboardingGuideProps) => {
const primary = isDev ? 'cd gitnexus && npm run serve' : 'npx gitnexus@latest serve';
const primary = isDev ? 'npm run --prefix gitnexus serve' : 'npx gitnexus@latest serve';
const termLabel = isDev ? 'Start backend' : 'Terminal';
// Step states: step 1 = copy command, step 2 = run/wait, step 3 = auto-connect
@ -277,7 +277,9 @@ export const OnboardingGuide = ({ isPolling }: OnboardingGuideProps) => {
state={step2State}
number={2}
title={isPolling ? 'Waiting for server to start' : 'Paste and run in your terminal'}
description={isPolling ? undefined : 'Open a new terminal window, paste, and hit Enter.'}
description={
isPolling ? undefined : 'Open a terminal at the project root, paste, and hit Enter.'
}
>
{isPolling && <PollingBar />}
</StepRow>

View file

@ -104,7 +104,8 @@
"overrides": {
"@huggingface/transformers": {
"onnxruntime-node": "$onnxruntime-node"
}
},
"tree-sitter-c": "0.23.2"
},
"engines": {
"node": ">=20.0.0"

View file

@ -1,3 +1,4 @@
import { isVerboseIngestionEnabled } from './utils/verbose.js';
import fs from 'fs/promises';
import path from 'path';
import { glob } from 'glob';
@ -43,6 +44,7 @@ export const walkRepositoryPaths = async (
const entries: ScannedFile[] = [];
let processed = 0;
let skippedLarge = 0;
const skippedLargePaths: string[] = [];
for (let start = 0; start < filtered.length; start += READ_CONCURRENCY) {
const batch = filtered.slice(start, start + READ_CONCURRENCY);
@ -52,6 +54,7 @@ export const walkRepositoryPaths = async (
const stat = await fs.stat(fullPath);
if (stat.size > MAX_FILE_SIZE) {
skippedLarge++;
skippedLargePaths.push(relativePath.replace(/\\/g, '/'));
return null;
}
return { path: relativePath.replace(/\\/g, '/'), size: stat.size };
@ -73,6 +76,11 @@ export const walkRepositoryPaths = async (
console.warn(
` Skipped ${skippedLarge} large files (>${MAX_FILE_SIZE / 1024}KB, likely generated/vendored)`,
);
if (isVerboseIngestionEnabled()) {
for (const p of skippedLargePaths) {
console.warn(` - ${p}`);
}
}
}
return entries;

View file

@ -212,7 +212,7 @@ export const addToGitignore = async (repoPath: string): Promise<void> => {
* Get the path to the global GitNexus directory
*/
export const getGlobalDir = (): string => {
return path.join(os.homedir(), '.gitnexus');
return process.env.GITNEXUS_HOME || path.join(os.homedir(), '.gitnexus');
};
/**

View file

@ -2,8 +2,8 @@ import { User } from './user';
import { Repo } from './repo';
export function processEntities(): void {
const user = new User();
const repo = new Repo();
const user = new User('alice');
const repo = new Repo('/tmp/repo');
user.save();
repo.save();
}

View file

@ -1,5 +1,7 @@
export class Repo {
constructor(private readonly path: string) {}
save(): boolean {
return false;
return this.path.length > 0;
}
}

View file

@ -1,5 +1,7 @@
export class User {
constructor(private readonly name: string) {}
save(): boolean {
return true;
return this.name.length > 0;
}
}

View file

@ -519,6 +519,16 @@ describe('TypeScript constructor-inferred type resolution', () => {
expect(saveMethods.length).toBe(2);
});
it('resolves explicit constructor calls for User and Repo', () => {
const calls = getRelationships(result, 'CALLS');
const userCtor = calls.find((c) => c.target === 'User' && c.targetFilePath === 'src/user.ts');
const repoCtor = calls.find((c) => c.target === 'Repo' && c.targetFilePath === 'src/repo.ts');
expect(userCtor).toBeDefined();
expect(repoCtor).toBeDefined();
expect(userCtor!.targetLabel).toBe('Class');
expect(repoCtor!.targetLabel).toBe('Class');
});
it('resolves user.save() to src/user.ts via constructor-inferred type', () => {
const calls = getRelationships(result, 'CALLS');
const userSave = calls.find((c) => c.target === 'save' && c.targetFilePath === 'src/user.ts');
@ -538,6 +548,14 @@ describe('TypeScript constructor-inferred type resolution', () => {
const saveCalls = calls.filter((c) => c.target === 'save');
expect(saveCalls.length).toBe(2);
});
it('resolves constructor calls for both User and Repo', () => {
const calls = getRelationships(result, 'CALLS');
const userCtor = calls.find((c) => c.target === 'User');
const repoCtor = calls.find((c) => c.target === 'Repo');
expect(userCtor).toBeDefined();
expect(repoCtor).toBeDefined();
});
});
// ---------------------------------------------------------------------------