gitnexus
ブラウザ上で大規模なコードを分析し、コード知識グラフを使ってインタラクティブな探索UIを構築したり、サーバー不要のコード解析ツールを開発したりするSkill。
📜 元の英語説明(参考)
Build client-side code knowledge graphs with built-in Graph RAG for code exploration. Use when: analyzing large codebases in the browser, building zero-server code intelligence tools, creating interactive code exploration UIs.
🇯🇵 日本人クリエイター向け解説
ブラウザ上で大規模なコードを分析し、コード知識グラフを使ってインタラクティブな探索UIを構築したり、サーバー不要のコード解析ツールを開発したりするSkill。
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o gitnexus.zip https://jpskill.com/download/14946.zip && unzip -o gitnexus.zip && rm gitnexus.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/14946.zip -OutFile "$d\gitnexus.zip"; Expand-Archive "$d\gitnexus.zip" -DestinationPath $d -Force; ri "$d\gitnexus.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
gitnexus.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
gitnexusフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
GitNexus
概要
クライアントサイドで動作するコード知識グラフを構築します。サーバーは不要です。tree-sitter WASM でコードを解析し、ファイル、関数、クラス、および依存関係のグラフを構築し、力指向レイアウトで視覚化し、自然言語によるコード探索のために Graph RAG でクエリを実行します。
手順
ユーザーがコード知識グラフ、ブラウザベースのコードエクスプローラー、またはコード用の Graph RAG の構築を要求した場合:
- tree-sitter WASM のセットアップ — 対象言語の言語文法をロードします
- コードベースの解析 — AST ノード(関数、クラス、インポート、エクスポート)を抽出します
- グラフの構築 — コードの関係を表すノードとエッジを作成します
- 視覚化 — 力指向グラフ(D3 または force-graph ライブラリ)でレンダリングします
- Graph RAG の有効化 — グラフノードを埋め込み、自然言語クエリを許可します
Tree-sitter WASM を使用したコード解析
import Parser from "web-tree-sitter";
interface CodeNode {
id: string;
type: "file" | "function" | "class" | "method" | "import" | "export";
name: string;
filePath: string;
startLine: number;
endLine: number;
code: string;
}
interface CodeEdge {
source: string;
target: string;
type: "contains" | "calls" | "imports" | "extends" | "implements";
}
async function initParser(language: string): Promise<Parser> {
await Parser.init();
const parser = new Parser();
const lang = await Parser.Language.load(`/tree-sitter-${language}.wasm`);
parser.setLanguage(lang);
return parser;
}
function extractNodes(tree: Parser.Tree, filePath: string): CodeNode[] {
const nodes: CodeNode[] = [];
nodes.push({ id: `file:${filePath}`, type: "file", name: filePath.split("/").pop()!, filePath, startLine: 0, endLine: tree.rootNode.endPosition.row, code: "" });
function walk(node: Parser.SyntaxNode) {
const nameNode = node.childForFieldName("name");
if ((node.type === "function_declaration" || node.type === "arrow_function") && nameNode) {
nodes.push({ id: `fn:${filePath}:${nameNode.text}`, type: "function", name: nameNode.text, filePath, startLine: node.startPosition.row, endLine: node.endPosition.row, code: node.text.slice(0, 500) });
}
if (node.type === "class_declaration" && nameNode) {
nodes.push({ id: `class:${filePath}:${nameNode.text}`, type: "class", name: nameNode.text, filePath, startLine: node.startPosition.row, endLine: node.endPosition.row, code: node.text.slice(0, 500) });
}
if (node.type === "import_statement") {
const source = node.descendantsOfType("string")[0];
if (source) nodes.push({ id: `import:${filePath}:${source.text}`, type: "import", name: source.text.replace(/['"]/g, ""), filePath, startLine: node.startPosition.row, endLine: node.endPosition.row, code: node.text });
}
for (const child of node.children) walk(child);
}
walk(tree.rootNode);
return nodes;
}
グラフの構築
function buildGraph(fileNodes: Map<string, CodeNode[]>): { nodes: CodeNode[]; edges: CodeEdge[] } {
const allNodes: CodeNode[] = [];
const edges: CodeEdge[] = [];
const functionIndex = new Map<string, string>();
for (const [, nodes] of fileNodes) {
allNodes.push(...nodes);
for (const node of nodes) {
if (node.type === "function" || node.type === "method") functionIndex.set(node.name, node.id);
}
}
for (const node of allNodes) {
const fileId = `file:${node.filePath}`;
if (node.type !== "file") edges.push({ source: fileId, target: node.id, type: "contains" });
if (node.type === "import") {
const targetFile = resolveImport(node.name, node.filePath);
if (targetFile) edges.push({ source: fileId, target: `file:${targetFile}`, type: "imports" });
}
if (node.type === "function" || node.type === "method") {
for (const [fnName, fnId] of functionIndex) {
if (fnId !== node.id && node.code.includes(fnName + "(")) edges.push({ source: node.id, target: fnId, type: "calls" });
}
}
}
return { nodes: allNodes, edges };
}
function resolveImport(importPath: string, fromFile: string): string | null {
if (importPath.startsWith(".")) {
return `${fromFile.split("/").slice(0, -1).join("/")}/${importPath.replace(/^\.\//, "")}.ts`;
}
return null;
}
Force-Graph による視覚化
import ForceGraph from "force-graph";
function renderGraph(container: HTMLElement, graph: { nodes: CodeNode[]; edges: CodeEdge[] }) {
const colorMap: Record<string, string> = { file: "#4a9eff", function: "#50c878", class: "#ff6b6b", method: "#ffa500", import: "#888888", export: "#dda0dd" };
ForceGraph()(container)
.graphData({
nodes: graph.nodes.map((n) => ({ id: n.id, name: n.name, type: n.type, val: n.type === "file" ? 8 : n.type === "class" ? 5 : 3 })),
links: graph.edges.map((e) => ({ source: e.source, target: e.target, type: e.type })),
})
.nodeColor((node: any) => colorMap[node.type] || "#999")
.nodeLabel((node: any) => `${node.type}: ${node.name}`)
.linkDirectionalArrowLength(4);
}
Graph RAG クエリ
import { pipeline } from "@xenova/transformers";
async function embedNodes(graph: { nodes: CodeNode[] }): Promise<Map<string, number[]>> {
const embeddings = new Map<string, number[]>();
const embedder = await pipeline("feature-extraction", "Xenova/all-MiniLM-L6-v2");
for (const node of graph.nodes) {
const text = `${node.type} "${node.name}" in ${node.filePath}: ${node.code.slice(0, 200)}`;
const result = await embedder(text, { pooling: "mean", normalize: true });
embeddings.set(node.id, Array.from(result.data));
}
return embeddings;
}
function searchGraph(query: number[], embeddings: Map<string, number[]>, topK = 10): string[] {
const scores: [string, number][] = [];
for (const [id, emb] of embeddings) {
let dot = 0, magA = 0, magB = 0;
for (let i = 0; i < query.length; i++) { dot += query[i] * emb[i]; magA += query[i] ** 2; magB += emb[i] 📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
GitNexus
Overview
Build client-side code knowledge graphs that run entirely in the browser — no server required. Parse code with tree-sitter WASM, construct a graph of files, functions, classes, and dependencies, visualize it with force-directed layouts, and query it with Graph RAG for natural-language code exploration.
Instructions
When a user asks to build a code knowledge graph, browser-based code explorer, or Graph RAG for code:
- Set up tree-sitter WASM — Load language grammars for the target languages
- Parse the codebase — Extract AST nodes (functions, classes, imports, exports)
- Build the graph — Create nodes and edges representing code relationships
- Visualize — Render with force-directed graph (D3 or force-graph library)
- Enable Graph RAG — Embed graph nodes, allow natural-language queries
Code Parsing with Tree-sitter WASM
import Parser from "web-tree-sitter";
interface CodeNode {
id: string;
type: "file" | "function" | "class" | "method" | "import" | "export";
name: string;
filePath: string;
startLine: number;
endLine: number;
code: string;
}
interface CodeEdge {
source: string;
target: string;
type: "contains" | "calls" | "imports" | "extends" | "implements";
}
async function initParser(language: string): Promise<Parser> {
await Parser.init();
const parser = new Parser();
const lang = await Parser.Language.load(`/tree-sitter-${language}.wasm`);
parser.setLanguage(lang);
return parser;
}
function extractNodes(tree: Parser.Tree, filePath: string): CodeNode[] {
const nodes: CodeNode[] = [];
nodes.push({ id: `file:${filePath}`, type: "file", name: filePath.split("/").pop()!, filePath, startLine: 0, endLine: tree.rootNode.endPosition.row, code: "" });
function walk(node: Parser.SyntaxNode) {
const nameNode = node.childForFieldName("name");
if ((node.type === "function_declaration" || node.type === "arrow_function") && nameNode) {
nodes.push({ id: `fn:${filePath}:${nameNode.text}`, type: "function", name: nameNode.text, filePath, startLine: node.startPosition.row, endLine: node.endPosition.row, code: node.text.slice(0, 500) });
}
if (node.type === "class_declaration" && nameNode) {
nodes.push({ id: `class:${filePath}:${nameNode.text}`, type: "class", name: nameNode.text, filePath, startLine: node.startPosition.row, endLine: node.endPosition.row, code: node.text.slice(0, 500) });
}
if (node.type === "import_statement") {
const source = node.descendantsOfType("string")[0];
if (source) nodes.push({ id: `import:${filePath}:${source.text}`, type: "import", name: source.text.replace(/['"]/g, ""), filePath, startLine: node.startPosition.row, endLine: node.endPosition.row, code: node.text });
}
for (const child of node.children) walk(child);
}
walk(tree.rootNode);
return nodes;
}
Graph Construction
function buildGraph(fileNodes: Map<string, CodeNode[]>): { nodes: CodeNode[]; edges: CodeEdge[] } {
const allNodes: CodeNode[] = [];
const edges: CodeEdge[] = [];
const functionIndex = new Map<string, string>();
for (const [, nodes] of fileNodes) {
allNodes.push(...nodes);
for (const node of nodes) {
if (node.type === "function" || node.type === "method") functionIndex.set(node.name, node.id);
}
}
for (const node of allNodes) {
const fileId = `file:${node.filePath}`;
if (node.type !== "file") edges.push({ source: fileId, target: node.id, type: "contains" });
if (node.type === "import") {
const targetFile = resolveImport(node.name, node.filePath);
if (targetFile) edges.push({ source: fileId, target: `file:${targetFile}`, type: "imports" });
}
if (node.type === "function" || node.type === "method") {
for (const [fnName, fnId] of functionIndex) {
if (fnId !== node.id && node.code.includes(fnName + "(")) edges.push({ source: node.id, target: fnId, type: "calls" });
}
}
}
return { nodes: allNodes, edges };
}
function resolveImport(importPath: string, fromFile: string): string | null {
if (importPath.startsWith(".")) {
return `${fromFile.split("/").slice(0, -1).join("/")}/${importPath.replace(/^\.\//, "")}.ts`;
}
return null;
}
Visualization with Force-Graph
import ForceGraph from "force-graph";
function renderGraph(container: HTMLElement, graph: { nodes: CodeNode[]; edges: CodeEdge[] }) {
const colorMap: Record<string, string> = { file: "#4a9eff", function: "#50c878", class: "#ff6b6b", method: "#ffa500", import: "#888888", export: "#dda0dd" };
ForceGraph()(container)
.graphData({
nodes: graph.nodes.map((n) => ({ id: n.id, name: n.name, type: n.type, val: n.type === "file" ? 8 : n.type === "class" ? 5 : 3 })),
links: graph.edges.map((e) => ({ source: e.source, target: e.target, type: e.type })),
})
.nodeColor((node: any) => colorMap[node.type] || "#999")
.nodeLabel((node: any) => `${node.type}: ${node.name}`)
.linkDirectionalArrowLength(4);
}
Graph RAG Query
import { pipeline } from "@xenova/transformers";
async function embedNodes(graph: { nodes: CodeNode[] }): Promise<Map<string, number[]>> {
const embeddings = new Map<string, number[]>();
const embedder = await pipeline("feature-extraction", "Xenova/all-MiniLM-L6-v2");
for (const node of graph.nodes) {
const text = `${node.type} "${node.name}" in ${node.filePath}: ${node.code.slice(0, 200)}`;
const result = await embedder(text, { pooling: "mean", normalize: true });
embeddings.set(node.id, Array.from(result.data));
}
return embeddings;
}
function searchGraph(query: number[], embeddings: Map<string, number[]>, topK = 10): string[] {
const scores: [string, number][] = [];
for (const [id, emb] of embeddings) {
let dot = 0, magA = 0, magB = 0;
for (let i = 0; i < query.length; i++) { dot += query[i] * emb[i]; magA += query[i] ** 2; magB += emb[i] ** 2; }
scores.push([id, dot / (Math.sqrt(magA) * Math.sqrt(magB))]);
}
return scores.sort((a, b) => b[1] - a[1]).slice(0, topK).map(([id]) => id);
}
Examples
Example 1: Build a Browser-Based Code Explorer for a React Project
npm create vite@latest code-nexus -- --template vanilla-ts
cd code-nexus
npm install web-tree-sitter force-graph @xenova/transformers
// main.ts — Parse a GitHub repo and render its knowledge graph
const parser = await initParser("typescript");
const files = await fetchRepoFiles("facebook/react", "packages/react/src");
const fileNodes = new Map<string, CodeNode[]>();
for (const file of files) {
const tree = parser.parse(file.content);
fileNodes.set(file.path, extractNodes(tree, file.path));
}
const graph = buildGraph(fileNodes);
renderGraph(document.getElementById("graph")!, graph);
// Result: interactive force-directed graph showing React's internal module structure
Example 2: Natural-Language Code Query with Graph RAG
// After building the graph, embed all nodes and query
const graph = buildGraph(fileNodes);
const embeddings = await embedNodes(graph);
// User asks a question about the codebase
const embedder = await pipeline("feature-extraction", "Xenova/all-MiniLM-L6-v2");
const qEmb = Array.from((await embedder("How does the authentication middleware work?", { pooling: "mean", normalize: true })).data);
const relevantIds = searchGraph(qEmb, embeddings, 5);
const context = relevantIds
.map((id) => graph.nodes.find((n) => n.id === id))
.filter(Boolean)
.map((n) => `[${n!.type}] ${n!.name} (${n!.filePath})\n${n!.code.slice(0, 300)}`)
.join("\n---\n");
// Pass context to LLM for a grounded answer about the codebase
const response = await fetch("/api/chat", {
method: "POST",
body: JSON.stringify({ messages: [
{ role: "system", content: `Answer using this code context:\n${context}` },
{ role: "user", content: "How does the authentication middleware work?" },
]}),
});
Guidelines
- Lazy-load grammars — Only load tree-sitter WASM grammars for languages present in the repo
- OPFS for large repos — Store cloned files in Origin Private File System for persistence
- Incremental parsing — Re-parse only changed files, not the entire repo
- Limit graph size — For repos with 1000+ files, allow filtering by directory or file type
- Web Workers — Run parsing and embedding in Web Workers to keep the UI responsive
- Cache embeddings — Store in IndexedDB so you don't re-embed on every page load