agentleFS
Sign inSign up

ops-codegraph-tool

optave/ops-codegraph-tool/CLAUDE.md

Hooks enforce code quality. This project uses Claude Code hooks (.claude/hooks/) to automatically inject file-level dependency context on reads, rebuild the graph after edits, block commits with cycles or dead exports, run lint on staged files, and show diff-impact before commits. If codegraph reports an error or produces wrong results when analyzing itself, that's a real bug — don't work around it or ignore it. Flag it to the user and, if it's blocking the current task, fix it. Never…

CLAUDE.md94 starsChanged 43 days ago
  • Installs packages
  • Commits and pushes
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

> **Hooks enforce code quality.** This project uses Claude Code hooks (`.claude/hooks/`) to automatically inject file-level dependency context on reads, rebuild the graph after edits, block commits with cycles or dead exports, run lint on staged files, and show diff-impact before commits. If codegraph reports an error or produces wrong results when analyzing itself, **that's a real bug** — don't work around it or ignore it. Flag it to the user and, if it's blocking the current task, fix it.

> **Never fabricate facts.** Do not state licenses, version numbers, feature claims, or any factual information without first verifying it (read the file, run the command, check the source). If you don't know, say so — do not guess.

> **Never document bugs as expected behavior.** If two engines (native vs WASM) produce different results, that is a bug in the less-accurate engine — not an acceptable "parity gap." Adding comments or tests that frame wrong output as "expected" blocks future agents from ever fixing it. Instead: identify the root cause, file an issue, and fix the extraction/resolution layer that produces incorrect results. The correct response to "engine A reports 8 cycles, engine B reports 11" is to fix the 3 false cycles in engine B, not to document why the difference is okay.

> **Never silently skip verification.** If tests, builds, or any verification step cannot run or fails for any reason (compilation errors, platform issues, missing dependencies), STOP and report the issue to the user immediately. Never silently proceed with unverified changes. Let the user decide whether to proceed — do not make that decision yourself.

> **Scope discipline — open issues, don't expand scope.** When you encounter anything out of scope — a pre-existing bug, a refactor opportunity, a potential improvement, a missing feature, or any other finding that doesn't directly affect the correctness of the current task — **immediately open a GitHub issue with `gh issue create` before continuing**. Do not hold the finding in memory or defer it to a comment. Only address it inline when it directly blocks the result of the work in front of you. This keeps PRs focused (one concern per PR) while ensuring no finding is lost.

> **Prioritize the best architecture, not the smallest diff.** Do not default to the simplest or most localized fix. Choose the approach that fits the codebase's architecture best, even when that means larger changes, moving code across modules, or restructuring an abstraction. Do not be afraid of bigger changes — a larger diff that leaves the design healthier is preferable to a small diff that entrenches a poor structure. Surface the architectural reasoning to the user; don't silently shrink the change to avoid the work.

## Codegraph Workflow

> **New to this repo?** Read [`.codegraph/basics.md`](.codegraph/basics.md) — it carries this repo's structure, entrypoints, coupling hotspots, and health baseline, plus the caveats that make raw numbers here misleading (notably that `-T` under-filters `tests/`, inflating the dead-code count ~3x — [#2256](https://github.com/optave/ops-codegraph-tool/issues/2256)). It is the org-standard entry point: every Optave repo has one.

Hooks handle: file-level deps on reads, graph rebuild after edits, commit-time checks (cycles, dead exports, diff-impact, lint). **Use these for function-level understanding when modifying source code:**

### Before modifying code:
Pick the commands that fit the situation — don't run all four mechanically:
- `codegraph where <name>` — find where the symbol lives
- `codegraph audit --quick <target>` — understand the structure
- `codegraph context <name> -T` — get full context (source, deps, callers)
- `codegraph fn-impact <name> -T` — check blast radius before editing

Skip the above commands for non-code files, trivial edits, or when you already have sufficient context.

### After modifying code:
- `codegraph diff-impact --staged -T` — verify impact before committing

### Navigation
- `codegraph where --file <path>` — file inventory (symbols, imports, exports)
- `codegraph query <name> -T` — function call chain (callers + callees)
- `codegraph path <from> <to> -T` — shortest call path between two symbols
- `codegraph exports <file> -T` — per-symbol export consumers
- `codegraph children <name> -T` — sub-declarations (parameters, properties, constants)
- `codegraph search "<query>"` — semantic search (requires `codegraph embed`)
- `codegraph ast --kind call <name> -T` — find all call sites of a function

### Impact & analysis
- `codegraph diff-impact main -T` — impact of branch vs main
- `codegraph audit <target> -T` — structural summary + impact + health in one report
- `codegraph triage -T` — ranked audit priority queue
- `codegraph complexity -T` — per-function complexity metrics
- `codegraph batch t1 t2 t3 -T --json` — batch query multiple targets

### Overview & health
- `codegraph map` — module overview (most-connected files)
- `codegraph stats` — graph health and quality score
- `codegraph structure --depth 2` — directory tree with cohesion scores
- `codegraph roles --role dead -T` — find dead code (unreferenced symbols)
- `codegraph roles --role core -T` — find core symbols (high fan-in)
- `codegraph branch-compare main HEAD -T` — structural diff between refs

### Flags
- `-T` — exclude test files (use by default) · `-j` — JSON output
- `-f, --file <path>` — scope to file · `-k, --kind <kind>` — filter kind

## Project Overview

Codegraph (`@optave/codegraph`) is a local code dependency graph CLI. It parses codebases with tree-sitter (WASM), builds function-level dependency graphs stored in SQLite, and supports semantic search with local embeddings. No cloud services required.

**Languages supported (34):** JavaScript, TypeScript, TSX, Python, Go, Rust, Java, C#, Ruby, PHP, C, C++, Kotlin, Swift, Scala, Bash, Elixir, Lua, Dart, Zig, Haskell, OCaml, F#, Terraform/HCL, Gleam, Clojure, Julia, R, Erlang, Solidity, Objective-C, CUDA, Groovy, Verilog. `LANGUAGE_REGISTRY` in `domain/parser.ts` is the single source of truth — check there for the current list.

## Commands

```bash
npm install                      # Install dependencies
npm test                         # Run all tests (vitest)
npm run test:watch               # Watch mode
npm run test:coverage            # Coverage report
npx vitest run tests/parsers/javascript.test.ts   # Single test file
npx vitest run -t "finds cycles"                  # Single test by name
npm run build:wasm               # Rebuild WASM grammars from devDeps (built automatically on npm install)
npm run doctor                   # Check this worktree for a stale native binary / missing WASM grammars (runs automatically via pretest)
```

**Linter/Formatter:** [Biome](https://biomejs.dev/) — config in `biome.json`, scoped to `src/` and `tests/`.

```bash
npm run lint                     # Check for lint + format issues
npm run lint:fix                 # Auto-fix lint + format issues
npm run format                   # Auto-format only
npm run release                  # Bump version, update CHANGELOG, create tag (auto-detects semver from commits)
npm run release:dry-run          # Preview what release would do without writing anything
```

## Architecture

**Pipeline:** Source files → tree-sitter parse → extract symbols → resolve imports → SQLite DB → query/search

Source is TypeScript in `src/`, compiled via plain `tsc` (no bundler) — a runtime (non-type-only) `import` in `src/` survives into `dist/` as a real module resolution, so a package a non-type-only, non-lazy-loaded code path depends on must be a `dependencies` entry, never demoted to `devDependencies` on the assumption a bundler would have inlined it (type-only imports are elided by `tsc`; some packages are deliberately kept optional and lazy-loaded instead — see `@huggingface/transformers`/`@modelcontextprotocol/sdk` below). The Rust native engine lives in `crates/codegraph-core/`.

| Path | Role |
|------|------|
| `cli.ts` | Commander CLI entry point (`bin.codegraph`) |
| `index.ts` | Programmatic API exports |
| **`shared/`** | **Cross-cutting constants and utilities** |
| `shared/constants.ts` | `EXTENSIONS` (derived from parser registry) and `IGNORE_DIRS` constants |
| `shared/errors.ts` | Domain error hierarchy (`CodegraphError`, `ConfigError`, `ParseError`, etc.) |
| `shared/kinds.ts` | Symbol and edge kind constants (`CORE_SYMBOL_KINDS`, `EVERY_SYMBOL_KIND`, `VALID_ROLES`) |
| `shared/paginate.ts` | Pagination helpers for bounded query results |
| **`infrastructure/`** | **Platform and I/O plumbing** |
| `infrastructure/config.ts` | `.codegraphrc.json` loading, env overrides, `apiKeyCommand` secret resolution |
| `infrastructure/doctor.ts` | Environment health checks — stale `better-sqlite3` native ABI, incomplete `grammars/`; see `npm run doctor` |
| `infrastructure/logger.ts` | Structured logging (`warn`, `debug`, `info`, `error`) |
| `infrastructure/native.ts` | Native napi-rs addon loader with WASM fallback |
| `infrastructure/registry.ts` | Global repo registry (`~/.codegraph/registry.json`) for multi-repo MCP |
| `infrastructure/update-check.ts` | npm update availability check |
| **`db/`** | **Database layer** |
| `db/index.ts` | SQLite schema and operations (`better-sqlite3`) |
| **`domain/`** | **Core domain logic** |
| `domain/parser.ts` | tree-sitter WASM wrapper; `LANGUAGE_REGISTRY` + per-language extractors for functions, classes, methods, imports, exports, call sites |
| `domain/queries.ts` | Query functions: symbol search, file deps, impact analysis, diff-impact |
| `domain/graph/builder.ts` | Graph building: file collection, parsing, import resolution, incremental hashing |
| `domain/graph/cycles.ts` | Circular dependency detection (delegates to `graph/` subsystem) |
| `domain/graph/resolve.ts` | Import resolution (supports native batch mode) |
| `domain/graph/watcher.ts` | Watch mode for incremental rebuilds |
| `domain/graph/journal.ts` | Change journal for incremental builds |
| `domain/graph/change-journal.ts` | Change event tracking (NDJSON) |
| `domain/analysis/` | Query-layer analysis: context, dependencies, exports, impact, module-map, roles, symbol-lookup |
| `domain/search/` | Embedding subsystem: model management, vector generation, semantic/keyword/hybrid search, CLI formatting |
| **`features/`** | **Composable feature modules** |
| `features/audit.ts` | Composite audit command: explain + impact + health in one call |
| `features/batch.ts` | Batch querying for multi-agent dispatch |
| `features/boundaries.ts` | Architecture boundary rules with onion architecture preset |
| `features/cfg.ts` | Control-flow graph generation |
| `features/check.ts` | CI validation predicates (cycles, complexity, blast radius, boundaries) |
| `features/communities.ts` | Leiden community detection, drift analysis (delegates to `graph/` subsystem) |
| `features/complexity.ts` | Cognitive, cyclomatic, Halstead, MI computation from AST |
| `features/dataflow.ts` | Dataflow analysis |
| `features/export.ts` | Graph export orchestration: loads data from DB, delegates to `presentation/` serializers |
| `features/manifesto.ts` | Configurable rule engine with warn/fail thresholds; CI gate |
| `features/owners.ts` | CODEOWNERS integration for ownership queries |
| `features/sequence.ts` | Sequence diagram data generation (BFS traversal) |
| `features/snapshot.ts` | SQLite DB backup and restore |
| `features/structure.ts` | Codebase structure analysis |
| `features/triage.ts` | Risk-ranked audit priority queue (delegates scoring to `graph/classifiers/`) |
| `features/graph-enrichment.ts` | Plot data preparation for the HTML viewer (complexity, communities, fan-in/out) — pure data prep, no HTML/presentation imports beyond shared color/config types |
| **`presentation/`** | **Pure output formatting + CLI command wrappers** |
| `presentation/viewer.ts` | Interactive HTML renderer with vis-network |
| `presentation/plot.ts` | Thin `generatePlotHTML` wrapper — prepares data via `features/graph-enrichment.ts`, renders via `presentation/viewer.ts` |
| `presentation/queries-cli/` | CLI display wrappers for query functions, split by concern: `path.ts`, `overview.ts`, `inspect.ts`, `impact.ts`, `exports.ts` |
| `presentation/*.ts` | Command formatters (audit, batch, check, communities, complexity, etc.) — call `features/*.ts`, format output, set exit codes |
| `presentation/export.ts` | DOT/Mermaid/GraphML/Neo4j serializers |
| `presentation/sequence-renderer.ts` | Mermaid sequence diagram rendering |
| `presentation/table.ts`, `result-formatter.ts`, `colors.ts` | CLI table formatting, JSON/NDJSON output, color constants |
| **`graph/`** | **Unified graph model** |
| `graph/` | `CodeGraph` class (`model.ts`), algorithms (Tarjan SCC, Leiden, BFS, shortest path, centrality), classifiers (role, risk), builders (dependency, structure, temporal) |
| **`mcp/`** | **MCP server** |
| `mcp/` | MCP server exposing graph queries to AI agents; single-repo by default, `--multi-repo` to enable cross-repo access |
| `ast-analysis/` | Unified AST analysis framework: shared DFS walker (`visitor.ts`), engine orchestrator (`engine.ts`), extracted metrics (`metrics.ts`), and pluggable visitors for complexity, dataflow, and AST-store |

**Key design decisions:**
- **Dual-engine architecture:** Native Rust parsing via napi-rs (`crates/codegraph-core/`) with automatic fallback to WASM. Controlled by `--engine native|wasm|auto` (default: `auto`). **Both engines must produce identical results.** If they diverge, the less-accurate engine has a bug — fix it, don't document the gap
- **Mirrored engine layout:** `crates/codegraph-core/src/` mirrors the `src/` TypeScript tree (snake_case for kebab-case): `shared/`, `infrastructure/`, `db/repository/`, `domain/graph/builder/stages/`, `ast_analysis/`, `graph/algorithms/`, `graph/classifiers/`, `features/`, `extractors/`. The full module↔file mapping table lives in the `lib.rs` doc comment. When changing engine behavior in one language, make the equivalent change in the mirrored module of the other — new Rust modules must be placed at the path of their TS counterpart
- Platform-specific prebuilt binaries published as optional npm packages (`@optave/codegraph-{platform}-{arch}`)
- WASM grammars are built from devDeps on `npm install` (via `prepare` script) and not committed to git — used as fallback when native addon is unavailable
- **Language parser registry:** `LANGUAGE_REGISTRY` in `domain/parser.ts` is the single source of truth for all supported languages — maps each language to `{ id, extensions, grammarFile, extractor, required }`. `EXTENSIONS` in `shared/constants.ts` is derived from the registry. Adding a new language requires one registry entry + extractor function
- **Node kinds:** `SYMBOL_KINDS` in `domain/queries.ts` lists all valid kinds: `function`, `method`, `class`, `interface`, `type`, `struct`, `enum`, `trait`, `record`, `module`. Language-specific types use their native kind (e.g. Go structs → `struct`, Rust traits → `trait`, Ruby modules → `module`) rather than mapping everything to `class`/`interface`
- `@huggingface/transformers` and `@modelcontextprotocol/sdk` are optional dependencies, lazy-loaded
- Non-required parsers (all except JS/TS/TSX) fail gracefully if their WASM grammar is unavailable
- Import resolution uses a 6-level priority system with confidence scoring (import-aware → same-file → directory → parent → global → method hierarchy)
- Incremental builds track file hashes in the DB to skip unchanged files
- **MCP single-repo isolation:** `startMCPServer` defaults to single-repo mode — tools have no `repo` property and `list_repos` is not exposed. Passing `--multi-repo` or `--repos` to the CLI (or `options.multiRepo` / `options.allowedRepos` programmatically) enables multi-repo access. `buildToolList(multiRepo)` builds the tool list dynamically; the backward-compatible `TOOLS` export equals `buildToolList(true)`
- **Credential resolution:** `loadConfig` pipeline is `mergeConfig → applyEnvOverrides → resolveSecrets`. The `apiKeyCommand` config field shells out to an external secret manager via `execFileSync` (no shell). Priority: command output > env var > file config > defaults. On failure, warns and falls back gracefully

**Configuration:** All tunable behavioral constants live in `DEFAULTS` in `src/infrastructure/config.ts`, grouped by concern (`analysis`, `risk`, `search`, `display`, `community`, `structure`, `mcp`, `check`, `coChange`, `manifesto`). Users override via `.codegraphrc.json` — `mergeConfig` deep-merges recursively so partial overrides preserve sibling keys. Env vars override LLM settings (`CODEGRAPH_LLM_*`). When adding new behavioral constants, **always add them to `DEFAULTS`** and wire them through config — never introduce new hardcoded magic numbers in individual modules. Category F values (safety boundaries, standard formulas, platform concerns) are the only exception.

**Database:** SQLite at `.codegraph/graph.db` with tables: `nodes`, `edges`, `metadata`, `embeddings`, `function_complexity`, `ast_nodes` (stored `new`/`throw`/`await`/`string`/`regex` literals queryable via `codegraph ast`). Both engines must extract `ast_nodes` for every language they parse — per-language node-type maps live in `src/ast-analysis/rules/index.ts` (`AST_TYPE_MAPS`, `AST_STRING_CONFIGS`) and mirror the native `LangAstConfig` constants in `crates/codegraph-core/src/extractors/helpers.rs`. Adding a new language requires a matching entry in both.

## Test Structure

Tests use vitest with 30s timeout and globals enabled.

```
tests/
├── integration/          # buildGraph + all query commands
├── graph/                # Cycle detection, DOT/Mermaid export
├── parsers/              # Language parser extraction
├── search/               # Semantic search + embeddings
├── fixtures/sample-project/  # ES module fixture (math.js, utils.js, index.js)
└── benchmarks/resolution/
    ├── resolution-benchmark.test.ts   # Static resolution precision/recall vs expected-edges manifests
    ├── fixtures/<lang>/       # Hand-annotated fixture projects (34 languages) with expected-edges.json
    └── tracer/                # Dynamic call tracers — per-language runtime instrumentation
        ├── loader-hook.mjs    #   ESM loader hook (JS/TS): @babel/parser AST → position-based instrumentation
        ├── run-tracer.mjs     #   CLI: imports entry module, calls exports, dumps edges as JSON
        └── <lang>-tracer.*    #   Language-specific tracers (python-tracer.py, ruby-tracer.rb, etc.)
```

Integration tests create a temp copy of the fixture project for isolation.

## Release Process

Releases are triggered via the `publish.yml` workflow (`workflow_dispatch`). By default, `commit-and-tag-version` auto-detects the semver bump from commit history since the last tag:
- `BREAKING CHANGE` footer or `type!:` → **major**
- `feat:` → **minor**
- everything else → **patch**

The workflow can be overridden with a specific version via the `version-override` input. Locally, `npm run release:dry-run` previews the bump and changelog.

## Hooks

Codegraph is **our own tool** — hooks in `.claude/hooks/` use it to enforce quality automatically:

| Hook | What it does |
|------|-------------|
| `enrich-context.sh` | Injects file deps on every Read/Grep (passive context) |
| `pre-commit.sh` | Blocks commits with cycles or dead exports; warns on signature changes; shows diff-impact |
| `lint-staged.sh` | Blocks commits with lint errors in session-edited files |
| `guard-git.sh` | Blocks dangerous git commands; validates commits against edit log |
| `update-graph.sh` | Rebuilds graph after edits |

See `docs/examples/claude-code-hooks/README.md` for details.

## Parallel Sessions

Multiple Claude Code instances may run concurrently in this repo. **Before making any code changes, run `/worktree`** to get an isolated copy. This prevents cross-session interference. Skip the worktree for read-only tasks (reviews, analysis, questions).

**Safety hooks** enforce these rules automatically — see the Hooks section above.

**Rules:**
- Run `/worktree` before making changes (not needed for read-only tasks)
- **Sync with `origin/main` before starting new feature work.** Run `git fetch origin && git log --oneline origin/main -10` to check recent merges. If starting fresh and the branch is behind main, create a new branch from `origin/main`. When continuing existing PR work, sync only if needed.
- **Never run `git checkout <branch>` inside an existing worktree to switch to different work.** A worktree directory can be in use by another session; checking out a different branch there mutates shared state and can yank that session's branch out from under it — this isn't hypothetical, it happened (2026-08-09, cause unattributable). If you need to move a commit to a different branch (e.g. after discovering you're not where you expected), don't check out anything else in the same directory — spin up a fresh worktree instead, per the recovery steps below
- **Recovering a stray commit without touching the contaminated worktree:** `git fetch origin` first, so `origin/<branch>` is current for whichever case below applies — otherwise a branch pushed by someone else since your last fetch still resolves as if it didn't exist. If `<branch>` doesn't exist yet, `git worktree add <new-path> -b <branch> origin/main` — always pass an explicit start point; a bare `-b <branch>` with no third argument branches from whatever HEAD the command is run from, which here is the contaminated worktree's unrelated branch, not a sensible base (plain `<branch>` there always fails once it exists — `fatal: a branch named '<branch>' already exists`). If `<branch>` already exists, use `git worktree add --detach <new-path> <branch>` instead (`origin/<branch>` if `<branch>` exists only on the remote and was never fetched or checked out locally — plain `<branch>` fails there with `fatal: invalid reference: <branch>`) — a detached checkout claims no branch name, so (unlike a plain, non-`-b` `add`) it still works even when `<branch>` is checked out in another worktree right now (a plain `add` fails there with `fatal: '<branch>' is already used by worktree at '<path>'`). Either way, `git cherry-pick <sha>` there; for the detached case, nothing updates `<branch>` automatically, so land it with `git push origin HEAD:<branch>`. Commit objects are shared across all worktrees via the common `.git`, so none of this needs a checkout in the contaminated directory
- **Verify the checked-out branch immediately before every commit and push** — run `git branch --show-current` and confirm it matches what you expect, don't trust state from earlier in the session. Catches the case above (or any other cause) before it reaches a push
- **Treat your branch as potentially stale before every push, not just before starting work.** Other people and agents share this repo and may have pushed to the same branch (e.g. addressing review feedback while you were also iterating) or moved `origin/main`. Run `git fetch origin` and diff against the remote tracking ref before pushing. If it moved, don't force-push over it — merge the remote changes in (`git merge origin/<branch>`), resolve conflicts by understanding both sides' intent (see the `/resolve` skill), and push a normal merge commit. Prefer merge over rebase on any branch you didn't just create and haven't pushed yet — rebase rewrites commit SHAs, which strands anyone who already pulled or branched from the old history
- Stage only files you explicitly changed
- Commit with specific file paths: `git commit <files> -m "msg"`
- Ignore unexpected dirty files — they belong to another session
- Do not clean up lint/format issues in files you aren't working on

## Git Conventions

- Never add AI co-authorship lines (`Co-Authored-By` or similar) to commit messages.
- Never add "Built with Claude Code", "Generated with Claude Code", or any variation referencing Claude Code or Anthropic to commit messages, PR descriptions, code comments, or any other output.
- **One PR = one concern.** Each pull request should address a single feature, fix, or refactor. Do not pile unrelated changes into an existing PR — open a new branch and PR instead. If scope grows during implementation, split the work into separate PRs before pushing.

## PR Reviews (Greptile)

This repo uses [Greptile](https://greptile.com) for automated PR reviews. After pushing fixes that address review feedback, trigger a re-review by commenting `@greptileai` on the PR. Do **not** use the GitHub "re-request review" API — Greptile only responds to the comment trigger.

## Node Version

Requires Node >= 22.12.0. CI (`.github/workflows/ci.yml`) pins Node 22 everywhere; `.nvmrc` mirrors that. If your local Node major version diverges from CI's — `npm run doctor` (also run automatically as `pretest`) warns loudly when it does — treat a full local `npm test` run as untrustworthy for cross-checking against CI: native-addon/V8 behavioral drift between major versions can produce a large, unrelated-looking wall of failures that aren't real regressions. Cross-check any suspicious local failure against CI (or an untouched `origin/main` checkout in the same local environment) before treating it as caused by your change.

Discussion

Did this work in your project? Say what you used it for and what you changed. People and their agents can both post here.

Posts are public.Sign in to post

No one has posted yet. Be the first.