From 204326a9daab1f31c2688012d83889d1ca00f531 Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Thu, 19 Mar 2026 00:46:36 +0200 Subject: [PATCH 1/7] Add cgraph CLI tool and Claude Code indexing skill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a typer-based CLI (`cgraph`) that wraps the existing sync Graph and Project classes, enabling code indexing and knowledge graph queries from the terminal. Add a Claude Code skill (`skills/code-graph/`) so Claude can autonomously manage the full lifecycle — database, indexing, and querying — during coding sessions. Co-Authored-By: Claude Opus 4.6 --- AGENTS.md | 22 ++ Makefile | 5 +- api/cli.py | 283 +++++++++++++++++++++ pyproject.toml | 8 + skills/code-graph/SKILL.md | 87 +++++++ skills/code-graph/references/management.md | 89 +++++++ skills/code-graph/references/querying.md | 101 ++++++++ tests/test_cli.py | 100 ++++++++ uv.lock | 4 +- 9 files changed, 697 insertions(+), 2 deletions(-) create mode 100644 api/cli.py create mode 100644 skills/code-graph/SKILL.md create mode 100644 skills/code-graph/references/management.md create mode 100644 skills/code-graph/references/querying.md create mode 100644 tests/test_cli.py diff --git a/AGENTS.md b/AGENTS.md index a95e44e..30f169d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,6 +21,7 @@ Knowledge graph visualization tool for codebases. Python FastAPI backend + React ```text api/ # Python backend + cli.py # cgraph CLI tool (typer) index.py # FastAPI app, routes, auth, SPA serving graph.py # FalkorDB graph operations (sync + async) llm.py # GraphRAG + LiteLLM chat @@ -34,6 +35,7 @@ api/ # Python backend app/ # React frontend (Vite) src/components/ # React components (ForceGraph, chat, code-graph, etc.) src/lib/ # Utilities +skills/code-graph/ # Claude Code skill for code graph indexing/querying tests/ # Pytest backend tests endpoints/ # API endpoint integration tests e2e/ # Playwright E2E tests @@ -44,6 +46,7 @@ e2e/ # Playwright E2E tests ```bash make install # Install all deps (uv sync + npm install) +make install-cli # Install cgraph CLI entry point make build-dev # Build frontend (dev mode) make build-prod # Build frontend (production) make run-dev # Build dev frontend + run API with reload @@ -130,3 +133,22 @@ Key variables (see `.env.template` for full list): - `POST /api/analyze_folder` — Analyze local folder - `POST /api/analyze_repo` — Clone and analyze repo - `POST /api/switch_commit` — Switch to specific commit + +## CLI (`cgraph`) + +Typer-based CLI wrapping the sync `Graph` and `Project` classes. Outputs JSON to stdout, status to stderr. Entry point: `api/cli.py`. + +Install: `make install-cli` or `uv pip install -e .` + +```bash +cgraph ensure-db # Start FalkorDB if not running +cgraph index . --ignore node_modules # Index local folder +cgraph index-repo # Clone + index a repo +cgraph list # List indexed repos +cgraph search [--repo ] # Full-text prefix search +cgraph neighbors ... [--repo] [--rel] [--label] # Connected entities +cgraph paths [--repo] # Call-chain paths +cgraph info [--repo] # Repo stats + metadata +``` + +`--repo` defaults to the current directory name. Claude Code skill in `skills/code-graph/`. diff --git a/Makefile b/Makefile index 37aa58d..e3ff460 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: help install test e2e lint lint-py lint-fe clean build-dev build-prod run-dev run-prod docker-falkordb docker-stop +.PHONY: help install install-cli test e2e lint lint-py lint-fe clean build-dev build-prod run-dev run-prod docker-falkordb docker-stop help: ## Show this help message @echo 'Usage: make [target]' @@ -10,6 +10,9 @@ install: ## Install all dependencies (backend + frontend) uv sync --all-extras npm install --prefix ./app +install-cli: ## Install cgraph CLI entry point + uv pip install -e . + build-dev: ## Build frontend for development npm --prefix ./app run build:dev diff --git a/api/cli.py b/api/cli.py new file mode 100644 index 0000000..f8b175f --- /dev/null +++ b/api/cli.py @@ -0,0 +1,283 @@ +"""Code Graph CLI — index and query code knowledge graphs. + +Machine-readable JSON goes to stdout; human status goes to stderr. +""" + +import json +import os +import subprocess +import sys +import time +from pathlib import Path +from typing import List, Optional + +import typer + +app = typer.Typer( + help="Index codebases and query their knowledge graphs.", + no_args_is_help=True, +) + + +def _stderr(msg: str) -> None: + print(msg, file=sys.stderr) + + +def _json_out(data: object) -> None: + print(json.dumps(data, default=str)) + + +def _default_repo(repo: Optional[str]) -> str: + return repo if repo else Path.cwd().name + + +def _check_connection(host: str, port: int) -> bool: + """Check if FalkorDB/Redis is reachable via PING.""" + try: + import redis + + r = redis.Redis(host=host, port=port, socket_connect_timeout=2) + r.ping() + r.close() + return True + except Exception: + return False + + +# ── ensure-db ────────────────────────────────────────────────────────── + + +@app.command("ensure-db") +def ensure_db() -> None: + """Ensure FalkorDB is running, auto-starting a Docker container if needed.""" + + host = os.getenv("FALKORDB_HOST", "localhost") + port = int(os.getenv("FALKORDB_PORT", "6379")) + + if _check_connection(host, port): + _stderr(f"FalkorDB already running on {host}:{port}") + _json_out({"status": "ok", "host": host, "port": port}) + return + + _stderr( + f"FalkorDB not reachable on {host}:{port}, starting Docker container…" + ) + + # Reuse existing stopped container if present + inspect = subprocess.run( + [ + "docker", + "inspect", + "--format", + "{{.State.Running}}", + "falkordb-cgraph", + ], + capture_output=True, + text=True, + ) + + if inspect.returncode == 0: + if inspect.stdout.strip() == "false": + subprocess.run( + ["docker", "start", "falkordb-cgraph"], + check=True, + capture_output=True, + ) + _stderr("Started existing falkordb-cgraph container") + else: + subprocess.run( + [ + "docker", + "run", + "-d", + "--name", + "falkordb-cgraph", + "-p", + f"{port}:6379", + "falkordb/falkordb:latest", + ], + check=True, + capture_output=True, + ) + _stderr("Created and started falkordb-cgraph container") + + # Wait up to 30 s for connectivity + for _ in range(30): + if _check_connection(host, port): + _stderr("FalkorDB is ready") + _json_out({"status": "ok", "host": host, "port": port}) + return + time.sleep(1) + + _stderr("Timed out waiting for FalkorDB to become ready") + _json_out({"status": "error", "message": "timeout"}) + raise typer.Exit(code=1) + + +# ── index ────────────────────────────────────────────────────────────── + + +@app.command() +def index( + path: str = typer.Argument(".", help="Local folder to index"), + ignore: Optional[List[str]] = typer.Option( + None, "--ignore", help="Directories to ignore (repeatable)" + ), + repo: Optional[str] = typer.Option( + None, "--repo", help="Graph name (defaults to folder name)" + ), +) -> None: + """Index a local folder into the knowledge graph.""" + from .project import Project + + folder = Path(path).resolve() + if not folder.exists(): + _json_out({"status": "error", "message": f"path does not exist: {folder}"}) + raise typer.Exit(code=1) + + name = repo or folder.name + + # Try to detect git remote URL for metadata + url = None + try: + from pygit2.repository import Repository as GitRepo + + remote_url = GitRepo(str(folder)).remotes[0].url + url = ( + remote_url.replace("git@", "https://") + .replace(":", "/") + .replace(".git", "") + ) + except Exception: + pass + + _stderr(f"Indexing {folder} as '{name}'…") + project = Project(name, folder, url) + graph = project.analyze_sources(ignore=list(ignore) if ignore else []) + + stats = graph.stats() + _stderr(f"Done — {stats['node_count']} nodes, {stats['edge_count']} edges") + _json_out({"status": "ok", "repo": name, **stats}) + + +# ── index-repo ───────────────────────────────────────────────────────── + + +@app.command("index-repo") +def index_repo( + url: str = typer.Argument(..., help="Git repository URL to clone and index"), + ignore: Optional[List[str]] = typer.Option( + None, "--ignore", help="Directories to ignore (repeatable)" + ), +) -> None: + """Clone a git repository and index it into the knowledge graph.""" + from .project import Project + + _stderr(f"Cloning and indexing {url}…") + project = Project.from_git_repository(url) + graph = project.analyze_sources(ignore=list(ignore) if ignore else []) + + stats = graph.stats() + _stderr(f"Done — {stats['node_count']} nodes, {stats['edge_count']} edges") + _json_out({"status": "ok", "repo": project.name, **stats}) + + +# ── list ─────────────────────────────────────────────────────────────── + + +@app.command("list") +def list_repos() -> None: + """List all indexed repositories.""" + from .graph import get_repos + + repos = get_repos() + _json_out({"repos": repos}) + + +# ── search ───────────────────────────────────────────────────────────── + + +@app.command() +def search( + query: str = typer.Argument(..., help="Prefix to search for"), + repo: Optional[str] = typer.Option( + None, "--repo", help="Repository name (defaults to CWD name)" + ), +) -> None: + """Search for entities by prefix (full-text search).""" + from .graph import Graph + + name = _default_repo(repo) + g = Graph(name) + results = g.prefix_search(query) + _json_out({"repo": name, "results": results}) + + +# ── neighbors ────────────────────────────────────────────────────────── + + +@app.command() +def neighbors( + node_ids: List[int] = typer.Argument(..., help="Node IDs to query"), + repo: Optional[str] = typer.Option( + None, "--repo", help="Repository name (defaults to CWD name)" + ), + rel: Optional[str] = typer.Option( + None, "--rel", help="Filter by relationship type (e.g. CALLS, DEFINES)" + ), + label: Optional[str] = typer.Option( + None, "--label", help="Filter by destination label (e.g. Function, Class)" + ), +) -> None: + """Get neighboring entities of the given node(s).""" + from .graph import Graph + + name = _default_repo(repo) + g = Graph(name) + result = g.get_neighbors(node_ids, rel=rel, lbl=label) + _json_out({"repo": name, **result}) + + +# ── paths ────────────────────────────────────────────────────────────── + + +@app.command() +def paths( + src: int = typer.Argument(..., help="Source node ID"), + dest: int = typer.Argument(..., help="Destination node ID"), + repo: Optional[str] = typer.Option( + None, "--repo", help="Repository name (defaults to CWD name)" + ), +) -> None: + """Find call-chain paths between two nodes.""" + from .graph import Graph + + name = _default_repo(repo) + g = Graph(name) + result = g.find_paths(src, dest) + _json_out({"repo": name, "paths": result}) + + +# ── info ─────────────────────────────────────────────────────────────── + + +@app.command() +def info( + repo: Optional[str] = typer.Option( + None, "--repo", help="Repository name (defaults to CWD name)" + ), +) -> None: + """Show repository statistics and metadata.""" + from .graph import Graph + from .info import get_repo_info + + name = _default_repo(repo) + g = Graph(name) + stats = g.stats() + + metadata = get_repo_info(name) or {} + _json_out({"repo": name, **stats, "metadata": metadata}) + + +if __name__ == "__main__": + app() diff --git a/pyproject.toml b/pyproject.toml index 49438fb..83d1d6c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,8 +20,12 @@ dependencies = [ "multilspy @ git+https://github.com/AviAvni/multilspy.git@python-init-params", "javatools>=1.6.0,<2.0.0", "pygit2>=1.17.0,<2.0.0", + "typer>=0.24.0,<1.0.0", ] +[project.scripts] +cgraph = "api.cli:app" + [project.optional-dependencies] test = [ "pytest>=9.0.2,<10.0.0", @@ -29,5 +33,9 @@ test = [ "httpx>=0.28.0,<1.0.0", ] +[build-system] +requires = ["setuptools>=68.0"] +build-backend = "setuptools.build_meta" + [tool.setuptools.packages.find] where = ["."] diff --git a/skills/code-graph/SKILL.md b/skills/code-graph/SKILL.md new file mode 100644 index 0000000..38f9923 --- /dev/null +++ b/skills/code-graph/SKILL.md @@ -0,0 +1,87 @@ +--- +name: code-graph +description: "This skill should be used when understanding code structure, finding + dependencies between functions/classes, tracing call graphs, or exploring code + relationships. Trigger phrases include 'code graph', 'call graph', 'who calls', + 'what calls', 'find dependencies', 'code structure', 'inheritance', 'find paths'." +--- + +# Code Graph Skill + +You own the full `cgraph` lifecycle: database, indexing, and querying. The user should never need to run cgraph commands manually. + +## Ownership Rules + +- If `cgraph` commands fail with connection errors, run `cgraph ensure-db` first. +- If `cgraph` is not found, see `management.md` for installation instructions. +- Index at session start or after significant code changes: `cgraph index . --ignore node_modules --ignore venv --ignore .git --ignore __pycache__` +- No need to re-index between consecutive queries if no code has changed. +- All command output is JSON on stdout. Status/progress goes to stderr. +- `--repo` defaults to the current directory name if omitted. + +## Indexing + +```bash +# Index the current project +cgraph index . --ignore node_modules --ignore venv --ignore .git --ignore __pycache__ + +# Index a specific folder with a custom name +cgraph index /path/to/project --repo my-project --ignore node_modules + +# Clone and index a remote repository +cgraph index-repo https://github.com/org/repo --ignore node_modules +``` + +## Searching + +```bash +# Search for entities by prefix +cgraph search get_user +cgraph search parse +cgraph search MyClass +``` + +Results return JSON with node `id`, `labels` (File, Class, Function, etc.), and `properties` (name, path, src_start, src_end). Use the `id` for follow-up queries. + +## Exploring Relationships + +```bash +# Get all neighbors of a node +cgraph neighbors 42 --repo my-project + +# Filter by relationship type +cgraph neighbors 42 --rel CALLS # what does it call? +cgraph neighbors 42 --rel DEFINES # what does it define? + +# Filter by destination label +cgraph neighbors 42 --label Function # only function neighbors + +# Multiple nodes at once +cgraph neighbors 42 55 --repo my-project +``` + +## Finding Paths + +```bash +# Trace call-chain paths between two nodes +cgraph paths 42 99 --repo my-project +``` + +Follows CALLS edges to find how one function reaches another. + +## Repository Info + +```bash +# List all indexed repos +cgraph list + +# Show stats for a repo +cgraph info --repo my-project +``` + +## Working with Results + +- Search results include `path`, `src_start`, `src_end` — use the Read tool to load the actual source code at those locations. +- **Node labels**: File, Class, Function, Interface, Struct +- **Edge types**: DEFINES (hierarchy), CALLS (call graph), EXTENDS (inheritance), IMPLEMENTS (interfaces), RETURNS (return types), PARAMETERS (param types) +- See `querying.md` for detailed JSON structures and common query patterns. diff --git a/skills/code-graph/references/management.md b/skills/code-graph/references/management.md new file mode 100644 index 0000000..f968381 --- /dev/null +++ b/skills/code-graph/references/management.md @@ -0,0 +1,89 @@ +# Code Graph — Installation & Management + +## Prerequisites + +- **Docker**: Required for FalkorDB (the graph database) +- **Python 3.12+** +- **uv** (Python package manager) + +## Installation + +### From the code-graph repository + +```bash +cd /path/to/code-graph +uv sync --all-extras +uv pip install -e . +``` + +### Via pipx (standalone) + +```bash +pipx install code-graph-backend +``` + +After installation, verify: + +```bash +cgraph --help +``` + +## Database + +FalkorDB is automatically managed by the `ensure-db` command: + +```bash +cgraph ensure-db +``` + +This will: +1. Check if FalkorDB is reachable on `FALKORDB_HOST:FALKORDB_PORT` +2. If not, start a Docker container named `falkordb-cgraph` +3. Wait for connectivity and report status + +## Environment Variables + +| Variable | Default | Purpose | +|----------|---------|---------| +| `FALKORDB_HOST` | `localhost` | FalkorDB/Redis host | +| `FALKORDB_PORT` | `6379` | FalkorDB/Redis port | +| `FALKORDB_USERNAME` | _(none)_ | Auth username | +| `FALKORDB_PASSWORD` | _(none)_ | Auth password | + +## Supported Languages + +- **Python** (.py) — via tree-sitter +- **Java** (.java) — via multilspy +- **C#** (.cs) — via multilspy + +## Troubleshooting + +### `cgraph` not found + +Ensure the package is installed and the entry point is on your PATH: + +```bash +uv pip install -e . # from the code-graph repo +# or +pipx install code-graph-backend +``` + +### Connection refused + +FalkorDB is not running. Run: + +```bash +cgraph ensure-db +``` + +If Docker is not installed, install it first or start FalkorDB manually. + +### Stale index + +If code has changed significantly since last index, re-index: + +```bash +cgraph index . --ignore node_modules --ignore venv --ignore .git --ignore __pycache__ +``` + +Re-indexing overwrites the existing graph for the same repo name. diff --git a/skills/code-graph/references/querying.md b/skills/code-graph/references/querying.md new file mode 100644 index 0000000..269207d --- /dev/null +++ b/skills/code-graph/references/querying.md @@ -0,0 +1,101 @@ +# Code Graph — Querying Reference + +## JSON Structures + +### Node + +```json +{ + "id": 42, + "labels": ["Function"], + "properties": { + "name": "analyze_sources", + "path": "api/project.py", + "src_start": 79, + "src_end": 94, + "doc": "..." + } +} +``` + +### Edge + +```json +{ + "id": 10, + "relation": "CALLS", + "src_node": 42, + "dest_node": 55, + "properties": { + "pos": 83 + } +} +``` + +### Neighbors response + +```json +{ + "repo": "code-graph", + "nodes": [ /* node objects */ ], + "edges": [ /* edge objects */ ] +} +``` + +### Paths response + +```json +{ + "repo": "code-graph", + "paths": [ + [ node, edge, node, edge, node ] + ] +} +``` + +Each path is an alternating array of node and edge objects. + +## Common Query Patterns + +### "What does function X call?" + +```bash +cgraph search X +# note the id from results, e.g. 42 +cgraph neighbors 42 --rel CALLS +``` + +### "Who calls function X?" + +The graph stores directed edges `(caller)-[:CALLS]->(callee)`. To find callers, search for the function, then look for *incoming* CALLS edges. Since `neighbors` follows outgoing edges, use `find_paths` or search for likely callers and check their CALLS neighbors. + +### "What does class Y define?" + +```bash +cgraph search Y +# note the id, e.g. 55 +cgraph neighbors 55 --rel DEFINES +``` + +### "Trace path from function A to function B" + +```bash +cgraph search A +# note id, e.g. 10 +cgraph search B +# note id, e.g. 99 +cgraph paths 10 99 +``` + +### "What are the stats for this repo?" + +```bash +cgraph info --repo my-project +``` + +## Search Workflow + +1. **Search** — find entities by name prefix → get node IDs +2. **Neighbors** — explore connections from those nodes +3. **Paths** — trace call chains between specific nodes +4. **Read source** — use `path`, `src_start`, `src_end` from node properties to read the actual code diff --git a/tests/test_cli.py b/tests/test_cli.py new file mode 100644 index 0000000..9b19438 --- /dev/null +++ b/tests/test_cli.py @@ -0,0 +1,100 @@ +"""Tests for the cgraph CLI. + +Requires FalkorDB running on localhost:6379 (same as the rest of the test suite). +""" + +import json +import unittest +from pathlib import Path + +from typer.testing import CliRunner + +from api.cli import app + +runner = CliRunner() + + +class TestCLIList(unittest.TestCase): + """list command — always works if FalkorDB is reachable.""" + + def test_list_returns_json(self): + result = runner.invoke(app, ["list"]) + self.assertEqual(result.exit_code, 0) + data = json.loads(result.output) + self.assertIn("repos", data) + self.assertIsInstance(data["repos"], list) + + +class TestCLIEnsureDB(unittest.TestCase): + """ensure-db command — FalkorDB is already running in CI/test.""" + + def test_ensure_db_ok(self): + result = runner.invoke(app, ["ensure-db"]) + self.assertEqual(result.exit_code, 0) + data = json.loads(result.output) + self.assertEqual(data["status"], "ok") + + +class TestCLIIndex(unittest.TestCase): + """Index a small fixture directory and query it.""" + + FIXTURE_DIR = Path(__file__).parent / "source_files" / "py" + REPO_NAME = "cli_test_py" + + @classmethod + def setUpClass(cls): + result = runner.invoke( + app, + ["index", str(cls.FIXTURE_DIR), "--repo", cls.REPO_NAME], + ) + assert result.exit_code == 0, result.output + cls.index_data = json.loads(result.output) + + def test_index_status(self): + self.assertEqual(self.index_data["status"], "ok") + self.assertEqual(self.index_data["repo"], self.REPO_NAME) + + def test_index_created_nodes(self): + self.assertGreater(self.index_data["node_count"], 0) + + def test_info(self): + result = runner.invoke(app, ["info", "--repo", self.REPO_NAME]) + self.assertEqual(result.exit_code, 0) + data = json.loads(result.output) + self.assertIn("node_count", data) + self.assertIn("edge_count", data) + self.assertEqual(data["repo"], self.REPO_NAME) + + def test_search(self): + result = runner.invoke( + app, ["search", "src", "--repo", self.REPO_NAME] + ) + self.assertEqual(result.exit_code, 0) + data = json.loads(result.output) + self.assertIn("results", data) + # The fixture has a file named src.py, so we should find something + self.assertIsInstance(data["results"], list) + + def test_list_includes_repo(self): + result = runner.invoke(app, ["list"]) + self.assertEqual(result.exit_code, 0) + data = json.loads(result.output) + self.assertIn(self.REPO_NAME, data["repos"]) + + +class TestCLIHelp(unittest.TestCase): + """Smoke test: --help should always work without a DB.""" + + def test_main_help(self): + result = runner.invoke(app, ["--help"]) + self.assertEqual(result.exit_code, 0) + self.assertIn("index", result.output) + self.assertIn("search", result.output) + + def test_index_help(self): + result = runner.invoke(app, ["index", "--help"]) + self.assertEqual(result.exit_code, 0) + + +if __name__ == "__main__": + unittest.main() diff --git a/uv.lock b/uv.lock index 44c773a..dd1df3e 100644 --- a/uv.lock +++ b/uv.lock @@ -250,7 +250,7 @@ wheels = [ [[package]] name = "code-graph-backend" version = "0.4.2" -source = { virtual = "." } +source = { editable = "." } dependencies = [ { name = "falkordb" }, { name = "fastapi" }, @@ -264,6 +264,7 @@ dependencies = [ { name = "tree-sitter-c-sharp" }, { name = "tree-sitter-java" }, { name = "tree-sitter-python" }, + { name = "typer" }, { name = "uvicorn", extra = ["standard"] }, { name = "validators" }, ] @@ -292,6 +293,7 @@ requires-dist = [ { name = "tree-sitter-c-sharp", specifier = ">=0.23.1,<0.24.0" }, { name = "tree-sitter-java", specifier = ">=0.23.5,<0.24.0" }, { name = "tree-sitter-python", specifier = ">=0.25.0,<0.26.0" }, + { name = "typer", specifier = ">=0.24.0,<1.0.0" }, { name = "uvicorn", extras = ["standard"], specifier = ">=0.34.0,<1.0.0" }, { name = "validators", specifier = ">=0.35.0,<0.36.0" }, ] From 3a3a11cd15f2941e3253265dcc73e56eee59b0c4 Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Thu, 19 Mar 2026 00:49:26 +0200 Subject: [PATCH 2/7] docs: add CLI and Claude Code skill sections to README Co-Authored-By: Claude Opus 4.6 --- README.md | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/README.md b/README.md index 533b32e..dcd2630 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ code-graph/ │ ├── project.py # Repository cloning and analysis orchestration │ ├── info.py # Repository metadata stored in Redis/FalkorDB │ ├── prompts.py # LLM system and prompt templates +│ ├── cli.py # cgraph CLI tool (typer) │ ├── auto_complete.py # Prefix search helper │ ├── analyzers/ # Source analyzers (Python, Java, C#) │ ├── entities/ # Graph/entity models @@ -37,6 +38,7 @@ code-graph/ │ ├── package.json # Frontend dependencies and scripts │ ├── vite.config.ts # Vite config and /api proxy for dev mode │ └── tsconfig*.json # TypeScript config +├── skills/code-graph/ # Claude Code skill for CLI-driven indexing/querying ├── tests/ # Backend/unit and endpoint tests ├── e2e/ # End-to-end helpers and Playwright assets ├── Dockerfile # Unified container image @@ -145,6 +147,7 @@ In this mode, the FastAPI app serves the built React SPA from `app/dist` on `htt ```bash make install # Install backend + frontend dependencies +make install-cli # Install cgraph CLI entry point make build-dev # Build frontend in development mode make build-prod # Build frontend for production make run-dev # Build dev frontend + run Uvicorn with reload @@ -157,6 +160,58 @@ make clean # Remove build/test artifacts `make test` currently points at the right backend test entrypoint, but some legacy analyzer/git-history tests still need maintenance before the suite passes on a clean checkout. +## CLI Tool (`cgraph`) + +CodeGraph includes a CLI tool for indexing codebases and querying the knowledge graph directly from the terminal. All output is JSON (to stdout), with status messages on stderr. + +### Install + +```bash +make install-cli +# or +uv pip install -e . +``` + +### Usage + +```bash +# Ensure FalkorDB is running (auto-starts a Docker container if needed) +cgraph ensure-db + +# Index the current project +cgraph index . --ignore node_modules --ignore .git --ignore venv --ignore __pycache__ + +# Index a remote repository +cgraph index-repo https://github.com/user/repo --ignore node_modules + +# List indexed repos +cgraph list + +# Search for entities by name prefix +cgraph search parse_config + +# Explore relationships (what does node 42 call?) +cgraph neighbors 42 --rel CALLS + +# Find call-chain paths between two nodes +cgraph paths 42 99 + +# Show repo statistics +cgraph info +``` + +The `--repo` flag defaults to the current directory name. Run `cgraph --help` for full details. + +### Claude Code Skill + +A [Claude Code](https://docs.anthropic.com/en/docs/claude-code) skill is included in `skills/code-graph/`. Copy it to your skills directory to let Claude autonomously index and query codebases during coding sessions: + +```bash +cp -r skills/code-graph/ ~/.claude/skills/code-graph/ +``` + +Then ask Claude things like *"what functions call analyze_sources?"* or *"find the dependency chain between parse_config and send_request"* — it will handle the indexing and querying automatically. + ## Running with Docker ### Using Docker Compose From d019fee134cd1839d5fd240efaa0f162153beedc Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Thu, 19 Mar 2026 00:56:36 +0200 Subject: [PATCH 3/7] docs: add npx skills installer to README Co-Authored-By: Claude Opus 4.6 --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dcd2630..94bcb1d 100644 --- a/README.md +++ b/README.md @@ -204,7 +204,13 @@ The `--repo` flag defaults to the current directory name. Run `cgraph --help` fo ### Claude Code Skill -A [Claude Code](https://docs.anthropic.com/en/docs/claude-code) skill is included in `skills/code-graph/`. Copy it to your skills directory to let Claude autonomously index and query codebases during coding sessions: +A [Claude Code](https://docs.anthropic.com/en/docs/claude-code) skill is included in `skills/code-graph/`. Install it with: + +```bash +npx skills add FalkorDB/code-graph +``` + +Or copy manually: ```bash cp -r skills/code-graph/ ~/.claude/skills/code-graph/ From 8b807990c73e502310d0ae3a382c5d11e4887453 Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Thu, 19 Mar 2026 01:00:31 +0200 Subject: [PATCH 4/7] Add npm package for skill install via npx skills add Add package.json to skills/code-graph/ so the skill can be published to npm as @falkordb/code-graph and installed with: npx skills add @falkordb/code-graph Co-Authored-By: Claude Opus 4.6 --- README.md | 8 +------ skills/code-graph/package.json | 26 ++++++++++++++++++++++ skills/code-graph/references/management.md | 12 ++++++++-- 3 files changed, 37 insertions(+), 9 deletions(-) create mode 100644 skills/code-graph/package.json diff --git a/README.md b/README.md index 94bcb1d..589ed13 100644 --- a/README.md +++ b/README.md @@ -207,13 +207,7 @@ The `--repo` flag defaults to the current directory name. Run `cgraph --help` fo A [Claude Code](https://docs.anthropic.com/en/docs/claude-code) skill is included in `skills/code-graph/`. Install it with: ```bash -npx skills add FalkorDB/code-graph -``` - -Or copy manually: - -```bash -cp -r skills/code-graph/ ~/.claude/skills/code-graph/ +npx skills add @falkordb/code-graph ``` Then ask Claude things like *"what functions call analyze_sources?"* or *"find the dependency chain between parse_config and send_request"* — it will handle the indexing and querying automatically. diff --git a/skills/code-graph/package.json b/skills/code-graph/package.json new file mode 100644 index 0000000..80ea2ef --- /dev/null +++ b/skills/code-graph/package.json @@ -0,0 +1,26 @@ +{ + "name": "@falkordb/code-graph", + "version": "0.1.0", + "description": "Claude Code skill for code-graph: index codebases and query knowledge graphs (call graphs, dependencies, inheritance)", + "files": [ + "SKILL.md", + "references/" + ], + "keywords": [ + "claude-code", + "skill", + "agent-skill", + "code-graph", + "knowledge-graph", + "falkordb", + "call-graph", + "code-analysis" + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/FalkorDB/code-graph.git", + "directory": "skills/code-graph" + }, + "homepage": "https://github.com/FalkorDB/code-graph#cli-tool-cgraph" +} diff --git a/skills/code-graph/references/management.md b/skills/code-graph/references/management.md index f968381..1815af4 100644 --- a/skills/code-graph/references/management.md +++ b/skills/code-graph/references/management.md @@ -8,7 +8,15 @@ ## Installation -### From the code-graph repository +### Install the skill (Claude Code) + +```bash +npx skills add @falkordb/code-graph +``` + +### Install the cgraph CLI + +From the code-graph repository: ```bash cd /path/to/code-graph @@ -16,7 +24,7 @@ uv sync --all-extras uv pip install -e . ``` -### Via pipx (standalone) +Or standalone via pipx: ```bash pipx install code-graph-backend From bfcceacfb4844a0828b5e25f343dea236fe7aff0 Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Thu, 19 Mar 2026 01:04:18 +0200 Subject: [PATCH 5/7] Rename skill npm package to @falkordb/code-graph-skill Co-Authored-By: Claude Opus 4.6 --- README.md | 2 +- skills/code-graph/package.json | 2 +- skills/code-graph/references/management.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 589ed13..043eabd 100644 --- a/README.md +++ b/README.md @@ -207,7 +207,7 @@ The `--repo` flag defaults to the current directory name. Run `cgraph --help` fo A [Claude Code](https://docs.anthropic.com/en/docs/claude-code) skill is included in `skills/code-graph/`. Install it with: ```bash -npx skills add @falkordb/code-graph +npx skills add @falkordb/code-graph-skill ``` Then ask Claude things like *"what functions call analyze_sources?"* or *"find the dependency chain between parse_config and send_request"* — it will handle the indexing and querying automatically. diff --git a/skills/code-graph/package.json b/skills/code-graph/package.json index 80ea2ef..d935762 100644 --- a/skills/code-graph/package.json +++ b/skills/code-graph/package.json @@ -1,5 +1,5 @@ { - "name": "@falkordb/code-graph", + "name": "@falkordb/code-graph-skill", "version": "0.1.0", "description": "Claude Code skill for code-graph: index codebases and query knowledge graphs (call graphs, dependencies, inheritance)", "files": [ diff --git a/skills/code-graph/references/management.md b/skills/code-graph/references/management.md index 1815af4..2501a9f 100644 --- a/skills/code-graph/references/management.md +++ b/skills/code-graph/references/management.md @@ -11,7 +11,7 @@ ### Install the skill (Claude Code) ```bash -npx skills add @falkordb/code-graph +npx skills add @falkordb/code-graph-skill ``` ### Install the cgraph CLI From 71c6b0e347fe02d353bd38654561450e189450c1 Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Thu, 19 Mar 2026 09:31:17 +0200 Subject: [PATCH 6/7] fix: use GitHub shorthand for skills CLI installer The skills CLI uses GitHub org/repo format, not npm scoped packages. Install command: npx skills add FalkorDB/code-graph Co-Authored-By: Claude Opus 4.6 --- README.md | 2 +- skills/code-graph/package.json | 26 ---------------------- skills/code-graph/references/management.md | 2 +- 3 files changed, 2 insertions(+), 28 deletions(-) delete mode 100644 skills/code-graph/package.json diff --git a/README.md b/README.md index 043eabd..834fc43 100644 --- a/README.md +++ b/README.md @@ -207,7 +207,7 @@ The `--repo` flag defaults to the current directory name. Run `cgraph --help` fo A [Claude Code](https://docs.anthropic.com/en/docs/claude-code) skill is included in `skills/code-graph/`. Install it with: ```bash -npx skills add @falkordb/code-graph-skill +npx skills add FalkorDB/code-graph ``` Then ask Claude things like *"what functions call analyze_sources?"* or *"find the dependency chain between parse_config and send_request"* — it will handle the indexing and querying automatically. diff --git a/skills/code-graph/package.json b/skills/code-graph/package.json deleted file mode 100644 index d935762..0000000 --- a/skills/code-graph/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "@falkordb/code-graph-skill", - "version": "0.1.0", - "description": "Claude Code skill for code-graph: index codebases and query knowledge graphs (call graphs, dependencies, inheritance)", - "files": [ - "SKILL.md", - "references/" - ], - "keywords": [ - "claude-code", - "skill", - "agent-skill", - "code-graph", - "knowledge-graph", - "falkordb", - "call-graph", - "code-analysis" - ], - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/FalkorDB/code-graph.git", - "directory": "skills/code-graph" - }, - "homepage": "https://github.com/FalkorDB/code-graph#cli-tool-cgraph" -} diff --git a/skills/code-graph/references/management.md b/skills/code-graph/references/management.md index 2501a9f..f937ee6 100644 --- a/skills/code-graph/references/management.md +++ b/skills/code-graph/references/management.md @@ -11,7 +11,7 @@ ### Install the skill (Claude Code) ```bash -npx skills add @falkordb/code-graph-skill +npx skills add FalkorDB/code-graph ``` ### Install the cgraph CLI From 91062822eef31b95242bb44a591dca7d08ff541a Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Thu, 19 Mar 2026 11:07:39 +0200 Subject: [PATCH 7/7] fix: address PR review comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Validate path is a directory, not just exists (Comment 2) - Wrap all commands in try/except for JSON error output (Comments 3, 7) - Only auto-start Docker for localhost, not remote hosts (Comments 6, 9) - Catch Docker subprocess errors (FileNotFoundError, CalledProcessError) - Add comment to bare except in git remote detection (Comment 1) - Fix querying.md reference: find_paths → cgraph paths (Comment 5) - Add value placeholders to AGENTS.md CLI examples (Comment 8) Co-Authored-By: Claude Opus 4.6 --- AGENTS.md | 6 +- api/cli.py | 150 +++++++++++++++-------- skills/code-graph/references/querying.md | 2 +- 3 files changed, 102 insertions(+), 56 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 30f169d..0cb5407 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -146,9 +146,9 @@ cgraph index . --ignore node_modules # Index local folder cgraph index-repo # Clone + index a repo cgraph list # List indexed repos cgraph search [--repo ] # Full-text prefix search -cgraph neighbors ... [--repo] [--rel] [--label] # Connected entities -cgraph paths [--repo] # Call-chain paths -cgraph info [--repo] # Repo stats + metadata +cgraph neighbors ... [--repo ] [--rel ] [--label