The guide for every AI agent in this repository. CLAUDE.md holds only @AGENTS.md,
so Claude Code and every other agent read the same text.
CodeGraph Brain (cgis — Code Graph Intelligence System) is a Python library and CLI that parses source code into a multi-layer semantic graph with fully qualified name (FQN) resolution. It uses tree-sitter for AST extraction and stores the resulting graph in SQLite.
The CLI entry point is cgis ingest <path>, which produces a graph.json output.
# Run CLI
uv run cgis ingest <repo_path> --output graph.json
# Format
make format # runs: uv run ruff format .
# Lint (with auto-fix)
make lint # runs: uv run ruff check . --fix
# Type check (strict mypy — this is mandatory)
make type-check # runs: uv run mypy src
# Tests
make pytest # runs: uv run pytest
# Docstring coverage (minimum 90%)
make doc-coverage # runs: uv run interrogate src
# Full verification (run before every commit/PR)
make format && make lint && make type-check && make pytest && make doc-coverage
# Single test
uv run pytest tests/unit/test_python_extractor.py::test_extract_simple_function -v
# Line coverage (same scope as CI — see the Makefile comment on why scripts/ counts)
make coverage # runs: uv run pytest --cov=cgis --cov=scripts --cov-report=term-missing
make coverage-html # same, writes htmlcov/The pipeline runs in three phases: Extract → Resolve → Store.
IngestionPipeline (pipeline/__init__.py)
├── cgis.languages (registry) ← lazy: a pack is imported when its first file is met
│ ├── languages/python/ ← PythonExtractor + PACK
│ ├── languages/typescript/ ← TypeScriptExtractor (.ts/.tsx) + PACK
│ └── languages/go/ ← GoExtractor (.go) + go.mod reader + PACK (pilot)
├── ResolverEngine (resolver/engine.py) ← raw_call: targets → resolved FQN edges
├── SQLiteStore (storage/sqlite_store.py) ← persists graph; WAL mode
└── post-write passes (pipeline/) ← refresh_dispatch, SemanticUpliftEngine (pipeline/uplift.py)
└── QueryEngine (query/engine.py) ← BFS traversals (impact / flow)
Node FQN format: module.ClassName.method_name — fully dot-separated, derived from the file path (e.g., src/cgis/pipeline/__init__.py → src.cgis.pipeline.IngestionPipeline.run). Use cgis.languages.scope_of(path, source_root) to convert paths (it picks the file's own pack; the Python rule lives in languages/python/extractor.py). __init__.py strips the /__init__ suffix.
Raw call convention: Extractors emit edges with target = "raw_call:<name>". The ResolverEngine then resolves these to actual FQNs, or leaves them unresolved: the target becomes the bare raw name (or a pack fallback such as js_builtins.<name>), a virtual boundary node classified STDLIB, EXTERNAL or UNKNOWN. A raw name equal to the id of a real node keeps the raw_call: prefix, so the edge never joins that node (#661).
ResolverEngine resolution order for a call to name:
self.method→ look upmethodon the enclosing class via_class_methodsindex- Unqualified call →
_global_symbolsby name, among the caller's own language only (#675); if ambiguous, prefer the same-scope candidate viafile_global_symbols(a file's scope is its path, or themetadata["scope"]its FILE node declares — a Go package)
Node— immutable (frozen Pydantic).idis the FQN. Key fields:type: NodeType,file_path,start_line,end_line,confidence_score,domains,ontology_class.Edge— immutable (frozen Pydantic).source/targetare FQNs. Key fields:type: EdgeType,weight,confidence. A call leaves the extractor as targetraw_call:<name>; after resolution a resolved call scores 1.0 in every language (#674) unless its target is an UNKNOWN boundary node (capped at 0.8, #493), and an unresolved one scores 0.8. A DISPATCHES edge scores 0.5, and an unresolved class reference 0.5.NodeType— structural (FILE,MODULE,CLASS,FUNCTION,METHOD…), runtime (API_ENDPOINT,DB_TABLE…), semantic (DOMAIN_CONCEPT).EdgeType— structural (CONTAINS,DECLARES,IMPORTS), behavioral (CALLS,REFERENCES), semantic (HANDLES,PERSISTS,AUTHORIZES).
- Create
src/cgis/languages/<name>/with an extractor subclassingcgis.core.extractor.BaseExtractor(parse(code, file_path) -> tuple[list[Node], list[Edge]]; emitCALLSedges withtarget="raw_call:<name>", setNode.language = "<name>"). - In its
__init__.py, definePACK, subclassingcgis.core.extractor.LanguagePack(mark methods@override) (name,extensions,code_fence,is_test,scope_of,extractor,project_reader,index_facts,classify,resolution).resolution()returnsResolutionRules(cgis.core.resolution): type normalizers, steps at a position (after:import_map), a fallback with its confidence,reconcile_import, and optionallydispatch(which methods a call through another type's may run; default: class hierarchy) — all reading the index asResolutionView. Metadata other files resolve against goes in the manifest'scross_file_metadata.classifyanswers for references from the pack's own files and runs the whole chain; a source with no pack gets the generic tail inSymbolIndex.classify_fqn. - Add one
ManifesttoMANIFESTSinsrc/cgis/languages/__init__.py, with aloadfunction that imports the pack statically inside it (lazy, yet visible to the review-fingerprint walk). Nothing else names the pack: CLI, MCP server, guardian and scripts all go through the registry, and thesurface-packsforbidden-edge gate fails if a surface imports a pack directly. - A pack never imports another pack or
cgis.storage(cross-pack,pack-isolationgates). - A pack's
__init__imports no grammar: import the extractor insideextractor(). Config reading goes in aProjectReader(cgis.core.project) whose facts are data keyed byingest_statekey — the collector (cgis.import_names), not the pack, writes them. A key other than the four built-ins (_FACT_SHAPES) travels asImportNames.extra: recorded, compared, and merged into thestatethe pack'sindex_factsreads. Its value must read back equal from JSON (no tuples). - A FILE node's id comes from
file_node_id, its definitions' namespace fromscope_of; they differ only where several files share a scope (Go), which the FILE node declares asmetadata["scope"].
MyPy runs in strict mode (strict = true). All functions need full annotations including return types. The Pydantic plugin is active — use model_copy(update={...}) for immutable model updates.
tests/unit/ ← pure unit tests, no I/O
tests/integration/ ← (empty, planned)
tests/self_parsing/ ← (empty) intended to validate the engine by parsing itself
The self-parsing test (feeding src/cgis/ to the ingestion pipeline and asserting the resulting graph matches actual structure) is the canonical correctness validation described in the blueprint.