Explore a Codebase as an Interactive Knowledge Graph with GitNexus
Index any codebase into a knowledge graph and serve an interactive web UI for exploring symbols, call chains, clusters, and execution flows, tunneled via Cloudflare for remote access.
Requirements
- Node.js v18 or later is required for GitNexus and the proxy.
- Git is required, and the repo must have a
.gitdirectory. - Cloudflared is needed for tunneling; it is auto-installed to ~/.local/bin if missing.
How to use
Use this when the user wants to visually explore a codebase's architecture.
It also applies when the user asks for a knowledge graph or dependency graph of a repo.
It is also suitable when the user wants to share an interactive codebase explorer with someone.
The index lives in .gitnexus/ inside the repo and is auto-gitignored.
The tunnel URL (e.g., https://random-words.trycloudflare.com) is printed to stderr; share it, as anyone with the link can explore the graph.
The web UI renders all nodes in the browser: repos under ~5,000 files work well, while large repos (30k+ nodes) will be sluggish or crash the browser tab. The CLI/MCP tools work at any scale — only the web visualization has this limit.
Clone and build GitNexus as a one-time setup.
GITNEXUS_DIR="${GITNEXUS_DIR:-$HOME/.local/share/gitnexus}"
if [ ! -d "$GITNEXUS_DIR/gitnexus-web/dist" ]; then
git clone https://github.com/abhigyanpatwari/GitNexus.git "$GITNEXUS_DIR"
cd "$GITNEXUS_DIR/gitnexus-shared" && npm install && npm run build
cd "$GITNEXUS_DIR/gitnexus-web" && npm install
fi
Patch the web UI for remote access: the web UI defaults to localhost:4747 for API calls.
Patch it to use same-origin so it works through a tunnel/proxy.
In the file $GITNEXUS_DIR/gitnexus-web/src/config/ui-constants.ts, change the following.
export const DEFAULT_BACKEND_URL = 'http://localhost:4747';
Change it to the same-origin expression shown below.
export const DEFAULT_BACKEND_URL = typeof window !== 'undefined' && window.location.hostname !== 'localhost' ? window.location.origin : 'http://localhost:4747';
In the file $GITNEXUS_DIR/gitnexus-web/vite.config.ts, add allowedHosts: true inside the server: { } block — only needed if running dev mode instead of the production build.
server: {
allowedHosts: true,
// ... existing config
},
Then build the production bundle.
cd "$GITNEXUS_DIR/gitnexus-web" && npx vite build
Index the target repo.
cd /path/to/target-repo
npx gitnexus analyze --skip-agents-md
rm -rf .claude/ # remove Claude Code-specific artifacts
Add --embeddings for semantic search, though it is slower — minutes instead of seconds.
Create the proxy script.
Write it to a file (e.g., $GITNEXUS_DIR/proxy.mjs). It serves the production web UI and proxies /api/* to the GitNexus backend — same origin, no CORS issues, no sudo, no nginx.
import http from 'node:http';
import fs from 'node:fs';
import path from 'node:path';
const API_PORT = parseInt(process.env.API_PORT || '4747');
const DIST_DIR = process.argv[2] || './dist';
const PORT = parseInt(process.argv[3] || '8888');
const MIME = {
'.html': 'text/html', '.js': 'application/javascript', '.css': 'text/css',
'.json': 'application/json', '.png': 'image/png', '.svg': 'image/svg+xml',
'.ico': 'image/x-icon', '.woff2': 'font/woff2', '.woff': 'font/woff',
'.wasm': 'application/wasm',
};
function proxyToApi(req, res) {
const opts = {
hostname: '127.0.0.1', port: API_PORT,
path: req.url, method: req.method, headers: req.headers,
};
const proxy = http.request(opts, (upstream) => {
res.writeHead(upstream.statusCode, upstream.headers);
upstream.pipe(res, { end: true });
});
proxy.on('error', () => { res.writeHead(502); res.end('Backend unavailable'); });
req.pipe(proxy, { end: true });
}
function serveStatic(req, res) {
let filePath = path.join(DIST_DIR, req.url === '/' ? 'index.html' : req.url.split('?')[0]);
if (!fs.existsSync(filePath)) filePath = path.join(DIST_DIR, 'index.html');
const ext = path.extname(filePath);
const mime = MIME[ext] || 'application/octet-stream';
try {
const data = fs.readFileSync(filePath);
res.writeHead(200, { 'Content-Type': mime, 'Cache-Control': 'public, max-age=3600' });
res.end(data);
} catch { res.writeHead(404); res.end('Not found'); }
}
http.createServer((req, res) => {
if (req.url.startsWith('/api')) proxyToApi(req, res);
else serveStatic(req, res);
}).listen(PORT, () => console.log(`GitNexus proxy on http://localhost:${PORT}`));
Start the services: run the GitNexus backend API in one terminal and the proxy (web UI + API on one port) in another.
# Terminal 1: GitNexus backend API
npx gitnexus serve &
# Terminal 2: Proxy (web UI + API on one port)
node "$GITNEXUS_DIR/proxy.mjs" "$GITNEXUS_DIR/gitnexus-web/dist" 8888 &
Verify that curl -s http://localhost:8888/api/repos returns the indexed repo(s).
Optionally, tunnel with Cloudflare for remote access, installing cloudflared without sudo if needed.
# Install cloudflared if needed (no sudo)
if ! command -v cloudflared &>/dev/null; then
mkdir -p ~/.local/bin
curl -sL https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 \
-o ~/.local/bin/cloudflared
chmod +x ~/.local/bin/cloudflared
export PATH="$HOME/.local/bin:$PATH"
fi
# Start tunnel (--config /dev/null avoids conflicts with existing named tunnels)
cloudflared tunnel --config /dev/null --url http://localhost:8888 --no-autoupdate --protocol http2
Clean up: stop the services and remove the index from the target repo.
# Stop services
pkill -f "gitnexus serve"
pkill -f "proxy.mjs"
pkill -f cloudflared
# Remove index from the target repo
cd /path/to/target-repo
npx gitnexus clean
rm -rf .claude/
--config /dev/null is required for cloudflared if the user has an existing named tunnel config at ~/.cloudflared/config.yml.
Without it, the catch-all ingress rule in the config returns 404 for all quick tunnel requests.
A production build is mandatory for tunneling.
The Vite dev server blocks non-localhost hosts by default (allowedHosts); the production build + Node proxy avoids this entirely.
The web UI does not create .claude/ or CLAUDE.md; those are created by npx gitnexus analyze.
Use --skip-agents-md to suppress the markdown files, then rm -rf .claude/ for the rest — these are Claude Code integrations that hermes-agent users don't need.
Watch the browser memory limit: the web UI loads the entire graph into browser memory.
Repos with 5k+ files may be sluggish, and 30k+ files will likely crash the tab.
Embeddings are optional: --embeddings enables semantic search but takes minutes on large repos.
Skip it for quick exploration; add it if you want natural language queries via the AI chat panel.
Source: nousresearch/hermes-agent and its contributors. License: MIT.
Copyright (c) 2025 Nous Research
Translation and editorial adaptation by ClawCases.
>Resources
>Related Use Cases
Agent Merge-Conflict Arbiter
Resolve a git merge conflict between two agents' branches as an impartial third party: a neutral arbiter that takes both diffs and their stated intents and produces a merged result without overwriting either side.
Autonomous Project Management with Subagents
Coordinate projects autonomously: a main agent delegates work to PM subagents that track tasks in a shared STATE.yaml, update it as they progress, pick up unblocked work in parallel, and keep all task state in version-controlled files.
AI-Powered Earnings Tracker
Stay on top of earnings season across dozens of tech companies without manually checking multiple sources and remembering report dates for every AI/tech earnings report.