Start

Configuration

code-graph indexes a project with zero configuration. A .cg.yaml (or .cg.yml) at the indexed root is optional. The CLI, the MCP index tool and the visual view read it on every index. Command-line flags take precedence.

Project file

version: 1
python:
  source_roots: [lib, tools/scripts]   # directories Python imports from; replaces detection ("." = the indexed root)
exclude: ["legacy/**", "*.generated.ts"]  # paths no plugin indexes and the coverage scan leaves out
include: [src/generated, build/api]   # directories indexed although a built-in skip leaves them out
skip_dirs:
  add: [fixtures_big]                  # more directory names to skip everywhere
  keep: [static]                       # directory names skipped by default that hold project code here
generated:
  paths: ["src/api/generated/**"]      # generated files detection misses (kept out of the graph, listed by coverage)
  vendored: ["third_party/**"]         # vendored copies of other projects
  keep: ["src/schema.gen.ts"]          # hand-maintained files a detection rule would classify
  include: false                       # true: index them, labelled attrs.generated
frameworks:
  add: [nest]                          # turn on a framework layer detection did not pick
  remove: [flutter]                    # turn off one that was detected
auth:
  extra_patterns: ["requireTenantMember", "withOrgScope"]   # guard names (regex) that count as auth
secret:
  extra_patterns: ["verifyStripeSignature"]                 # guard names that check a shared secret / signature
gates: config/gates.json               # gate scenarios file (cg index --gates)
plans:
  dir: docs/plans                      # plans directory (--plans-dir / --plans)
  text_mention_dirs: [src, templates]  # where plan checks look for text mentions of a planned name
viz:
  presets:                             # canned queries for the visual view's starter cards
    - {id: orders_writes, label: what writes the orders table, mode: reaches, specs: ["table:orders"]}
apps:                                  # workspace: one `cg index <root>` builds one combined graph
  - {name: api, root: apps/api, role: backend}
  - {name: web, root: apps/web, role: frontend, links: [api]}   # backends it calls (default: every backend)
  - {name: orders, root: ../orders-api, role: backend}          # absolute or ../other-repo is allowed
platforms:
  targets: [ios, android, web]         # build targets (default: detected)
  paths: {"src/win32/**": [windows]}   # files built only for some targets
  file_suffixes: true                  # React Native .ios.ts / .android.ts / .native.ts / .web.ts
  path_conventions: true               # C / C++ win/ unix/ posix/ directories and *_win.c names
rust:
  targets: off                         # extra rust-analyzer runs: auto (default), off, or [windows, macos]
protocols:
  external: ["kafka:audit.*"]          # <protocol>:<name glob> handled outside the analysed repos
surface:
  ignore:                              # accepted `cg surface` findings; reason is required
    - {finding: unguarded, path: "app/Http/Controllers/HealthController.php", reason: public health probe}
lossy: [squash, "toDisplay*"]          # extra lossy-transform names / globs for `cg roundtrip`

Comments in the file are the meanings. Flags and env vars that override a key:

keyoverride
python.source_rootscg index --python-root DIR (repeatable)
generated.includecg index --include-generated
auth.extra_patterns--auth-pattern
gatescg index --gates FILE
plans.dir--plans-dir DIR / --plans DIR
viz.presetscg serve --presets FILE
appscg index --no-apps indexes the root as one project
rust.targetsCG_RUST_TARGETS (takes precedence; Rust, C and C++)

surface.ignore entries need a reason (cg config validate rejects one without) and are read from the file at query time (Attack surface). include walks only the path down to those directories; exclude still applies inside them. skip_dirs.keep also lets a walk into a hidden directory such as .storybook. frameworks names include laravel, nuxt, django, djangorestframework, django-ninja, flutter, nest, nextjs, astro, express, spring (aliases such as nestjs, next, fastify, drf work). platforms.targets accepts windows, linux, macos, ios, android, web; unix and native work in platforms.paths. Details: Python, Generated files, Platforms.

The settings are stored with the graph, so cg serve, cg routes, cg plan and the MCP server read plans, auth patterns and presets from the DB without repeating the flags.

cg config show [ROOT] prints each effective value and where it comes from (flag, .cg.yaml, preset or detection); --json prints the same as data. cg config validate [ROOT] checks the file and exits 2 when it is invalid. Unknown top-level keys are kept and listed under stats.config.ignored_keys; validate suggests a likely typo (skip_dir → skip_dirs).

Apps and workspace

cg index <root> --db out/mono.db indexes each app into out/mono.<app>.db and writes one combined out/mono.db for every app. Per-pair databases are not written. With a single app, out/mono.db is that app's graph.

Each app reads its own .cg.yaml. The root file's apps decides what is indexed. root is relative to the workspace, or absolute, or ../other-repo, so separate checkouts can share one config. A missing directory is rejected with the same message as a missing in-repo root.

role is backend (publishes routes, and may call other backends) or frontend (calls servers, does not publish routes). links: on a frontend is the allow-list of backends it calls; omit it to call every backend. Client calls in every app are matched against every other backend, including backend-to-backend HTTP.

cg index . --db out/workspace.db

The same merge from graphs you already built is cg link --repo (workspace). An id shared by two apps is stored as repo: plus the original id. orders:Order selects one app; Order matches every app that defines it.

Framework presets

A detected language or framework brings a preset from cg_code_graph/presets/*.yaml:

presetwhat it adds
commonauth and secret name patterns, shared skip directories
php, python, typescript, dart, rust, c_cpp, kotlin, java, swiftper-language skip lists
laravelauth and signature middleware, plan prefixes, text-mention dirs
django, djangorestframework, django-ninjaview access decorators, DRF permissions, ninja auth
nest, nextjs, express, nuxtNest guards, Next.js auth helpers, Express-family middleware, Nuxt session helpers; the Express preset also lists session_only middleware (session(), cookie-session), which creates a session but is never an auth guard
react-routerReact Router and Remix route modules (presets/react-router.yaml)
springSpring Security method annotations and SecurityFilterChain guard names (presets/spring.yaml)

A guard matches on its name, ignoring namespace and arguments (auth:sanctum, AuthGuard('jwt')). Project guards match the shared name pattern or auth.extra_patterns. The same auth.extra_patterns select inline Laravel checks ($this->assertCanManage($order)) when the project is indexed. Merge order: common, languages, frameworks, .cg.yaml, then flags. cg coverage and cg routes name which presets and patterns matched. cg config show lists every preset value with its source.

Gate scenarios

A gates file names scenarios and the settings that are true in each (examples/bookstore.gates.json):

{"scenarios": [{
  "name": "new_inventory",
  "description": "free text",
  "setting_accessors": ["getSetting"],
  "true_settings": ["features.new_inventory.enabled"],
  "false_settings": []
}]}

Index with --gates FILE, then query with reaches … --gate auto (or none, or a scenario name). Each item reports gate_status: live, gated_target or gated_entry. Beta: one scenario per index (the first one).

Rust, C and C++ scenarios can also switch Cargo features, cfg atoms and macros. Atoms the scenario does not mention stay unknown, and unknown code stays live. Example: examples/native.gates.json.

{"scenarios": [{"name": "minimal_build",
  "features_off": ["kv-core/fs"], "cfg_true": ["unix"], "cfg_false": ["windows"],
  "defines_on": ["NDEBUG"], "defines_off": ["RB_THREADSAFE"]}]}

Viz presets and starter queries

Starter cards are built in this order: serve --presets FILE or viz.presets; sample presets for the bundled apps whose targets exist; then starter queries derived at index time (an unguarded write route, the most-written and most-read tables, the busiest connection and env key, the widest page, the most-called functions). cg starters --db DB and the MCP starters tool list them.

[
  {"id": "warehouse_reaches", "label": "what reaches the warehouse connection", "mode": "reaches",
   "specs": ["connection:warehouse", "table:warehouse_stock"]},
  {"id": "report_page_tables", "label": "report page -> tables", "mode": "downstream",
   "specs": ["page:/reports/:id"], "sinks": ["table", "column"]},
  {"id": "plan_preorders", "label": "plan overlay: pre-orders", "mode": "plan", "specs": ["preorders"]}
]

mode is reaches, impact, downstream, path or plan.

Environment variables

variableeffect
CG_MCP_TOOLS=LISTtools cg-mcp lists: core, names, globs, -name. Unset or empty means all (Choosing tools)
CG_NO_CACHE=1disable the TS, Dart and native SCIP caches (keyed by file content)
CG_NO_UPDATE_CHECK=1cg doctor does not ask PyPI for a newer release and cg --version does not show the cached notice (CLI reference); also off when CI is set
CG_NO_HOOKS=1git hooks installed by cg hooks do nothing for that command (CLI reference)
CG_NO_STALE_CHECK=1MCP tool replies skip the staleness index note: (MCP server)
CG_CACHE=DIRcache root (else %LOCALAPPDATA%\cg on Windows, $XDG_CACHE_HOME/cg, ~/.cache/cg). cg clean empties it (clean)
CG_NODE=PATHJavaScript runtime for the TypeScript extractor (Node.js or Bun; a path or a name on PATH); else node, then bun on PATH (Installing and updating cg)
CG_RUST_SCIP=0, CG_C_SCIP=0, CG_COMPDB, CG_CFAMILY, …Rust / C / C++ options (environment variables)
CG_JAVA_SCIP=1run scip-java on the Gradle / Maven build (opt-in; it runs the build and can clean build caches). One run per project root, shared with Kotlin (Java exact mode)
CG_JAVA_SCIP_FILEprebuilt scip-java index. Wins over CG_KOTLIN_SCIP_FILE when both are set
CG_KOTLIN_SCIP=1, CG_KOTLIN_SCIP_FILEsame scip-java run and index as the CG_JAVA_SCIP* names. Either opt-in starts the one run (Kotlin exact mode)

The CODEGRAPH_* names were removed in 0.19.0: rename CODEGRAPH_<NAME> to CG_<NAME>, and rename CODEGRAPH_CACHE and CODEGRAPH_CACHE_DIR to CG_CACHE.