| cgis_ingestA | Scan a local directory, extract all symbols, resolve links, and build the graph DB. Use this to initialise or refresh the code knowledge graph for a project.
Node FQNs are normalised relative to the workspace root so the graph is
portable across machines.
``db_path`` must name a database — it has to end in ``.db``, ``.sqlite`` or
``.sqlite3``, live in a directory that already exists, and not point at an
existing file that is not a SQLite database. cgis will not create parent
directories.
By default the ingest is **incremental**: only changed/new files are
re-scanned, and the summary reports both what changed this run and the
whole-graph total. When a change alters what other files resolve against — a
renamed, removed or added symbol, a deleted or new file, a changed base class
or re-export — the incremental run rebuilds the whole graph itself, so edges
in unchanged files never point at symbols that no longer exist. Set
``full_rebuild=True`` to force a re-scan of every file from scratch.
Data keys are opt-in (``data_keys=True``): they cost every ingest time and
graph size, and make JSON and YAML edits mark the graph stale.
|
| cgis_trace_flowA | Downstream subgraph of one FQN: everything it reaches within depth hops. Follows every edge except containment — in practice calls, imports,
inheritance, DI dependencies and references — between internal code, so this
answers "what does X depend on?". Containment and
stdlib/third-party nodes are left out unless ``include_structure`` /
``include_external`` ask for them (external covers stdlib, third-party and
unresolved call targets) — the same view as the CLI's ``trace``.
For what depends on X use
``cgis_analyze_impact``; for only the members of a module or class,
``cgis_get_structure``; for a source-included brief to read before editing
one symbol, ``cgis_context``.
Test code is left out unless ``include_tests`` asks for it, and at most
``max_nodes`` nodes come back, nearest first, as for impact.
``output_format="text"`` returns the subgraph as an indented tree, one line
per node with children in call-site order, so a flow reads top down — about
a fifth of the json for the same answer; ``"mermaid"`` (default) a diagram;
``"json"`` a joinable ``{root, nodes, edges, coverage}`` payload
with real FQNs (not display hashes) for agent/CI use. ``coverage`` counts
the calls the traversed functions make that resolved to nothing, and
``top_unresolved`` names the most frequent. Read the names, not only the
ratio: in Python most are methods on untyped locals (``logger.info``,
``items.append``), which cut nothing short. Use ``cgis_ingest`` first if
the database does not exist yet.
|
| cgis_analyze_impactA | Upstream subgraph of one FQN: everything that reaches it within depth hops. Follows every edge except containment — in practice callers, importers,
subclasses, type references and DI dependents — within internal code, so this
answers "what breaks if I change X?". The
enclosing class or file and stdlib/third-party nodes are left out unless
``include_structure`` / ``include_external`` ask for them — the same view as
the CLI's ``impact``. Test callers are left out unless ``include_tests``
asks for them, and counted in ``excluded_tests``. A file that imports a
symbol is listed, but the walk does not continue to the files importing
that file; a file's own impact still does. At most ``max_nodes`` nodes come
back, nearest first, and a cut is reported in ``truncated``. For what X
depends on use
``cgis_trace_flow``; for only the members of a module or class,
``cgis_get_structure``; for a source-included brief to read before editing
one symbol, ``cgis_context``.
``output_format="text"`` returns an indented tree of callers, one line
per node — the compact form to read; ``"mermaid"`` (default)
returns a diagram; ``"json"`` returns a joinable ``{root, nodes, edges,
coverage}`` payload with real FQNs — letting an agent compute set
differences (e.g. "which route handlers never reach ``verify_ownership``?")
directly. ``coverage`` counts unresolved calls whose name matches a
traversed function, method or class: callers that may be missing, named in
``top_unresolved``. It is an upper bound — a common name matches calls on
unrelated objects, which the names make visible.
|
| cgis_get_structureA | Members of a module or class — or, for a package prefix, the modules it holds. Follows containment (CONTAINS/DECLARES) only, so no call or import appears.
A package is not a node in the graph (#487), so its row and the edges to its
modules are synthesized for the answer and marked with a virtual file path.
For how the code connects use ``cgis_trace_flow`` (what it depends on) or
``cgis_analyze_impact`` (what depends on it).
Matches the CLI ``structure`` command. ``output_format="text"`` returns the
hierarchy as an indented tree, one line per member; ``"mermaid"`` (default) a
diagram of the hierarchy rooted at the given FQN; ``"json"`` the
joinable ``{root, nodes, edges}`` payload with real FQNs.
|
| cgis_driftA | Report per-domain architectural drift against declared ideal patterns. Returns JSON: ``any_critical`` verdict, per-domain reports (each carrying a
``fit`` block — nearest alphabet template + residual + good/weak/none band),
the observe-only quotient layer, and ``coverage`` (graph prefixes bound by no
domain). Call after ``cgis_ingest`` to learn whether your edits pushed a
domain past its drift tolerance.
``max_drift`` is now the default tolerance only for domains that omit
``drift_tolerance`` — it no longer caps domains that declare their own
(see #170).
``profile``: when set, score only domains with this profile (plus
profile-less ones). Use when your patterns.yaml mixes languages but the
graph holds one language — avoids false EMPTY reports for other-language
domains that would otherwise fail the gate.
``max_residual``: a domain whose nearest template is farther than this gets
``fit.band = "none"`` ("no template fits") — a grab-bag module or an
alphabet gap, independent of drift tolerance (#177).
|
| cgis_suggest_packagesA | Suggest sub-package boundaries for a package from its dependency communities. Returns JSON: modularity_q, divergence, direction (under/over/matched),
verdict (split/consolidate/aligned/leave/borderline/no_signal), the detected
communities (id + members), the cross-community bridge edges (cost of
splitting), and the thresholds used. Default layer is IMPORTS; set
``with_calls`` for the combined import+call graph. Run ``cgis_ingest`` first.
Members are the package's direct children by default, each sub-package one
node, so the verdict says whether to regroup them; ``level`` in the result
says which. ``all_descendants`` clusters every file below the package.
A mis-rooted graph (import targets resolve to no internal file) returns
``no_signal`` with a diagnostic note rather than a silent clean verdict.
|
| cgis_module_graphA | How one package is built inside: its modules and the dependencies between them. Nodes are the package's direct children, each sub-package one node (``expand``
lists every module instead). Edges are imports ``-->``, inheritance ``==>``
and calls ``-.->``, each kind transitively reduced so the direct layering
shows; an edge that closes a cycle is never dropped. Dependencies on code
outside the package are summarised per module under the diagram, not drawn.
Test modules and type-only imports are left out. The json form is
``{prefix, nodes, subpackages, edges:[{source,target,kind}], dropped, covered, outside}``;
``covered`` counts the imports not drawn because a drawn ``==>`` joins the
same pair: that arrow then also stands for the import, so read an import
closure over imports and extends together. An import that closes an import
cycle is always drawn.
Run ``cgis_ingest`` first.
|
| cgis_validateA | Report graph integrity as JSON: edge resolution stats + health verdict. Check this before trusting ``cgis_analyze_impact`` output — a high
unresolved ratio means callers are missing from the graph.
|
| cgis_overviewA | Where to start in a graph you know nothing about: sizes and a package map. Call this first in an unfamiliar repository — every other tool needs a name,
and this is the one that hands you some. Returns JSON: symbol counts by type,
file and edge totals, the unresolved-edge ratio, and the largest packages with
production and tests listed separately. Each ``prefix`` goes straight into
``cgis_get_structure`` (the modules it holds), ``cgis_find_symbol``
(``fqn_prefix``) or ``cgis_metrics`` (``scope``).
Listings are capped; ``packages_omitted`` appears when rows were cut. Entry
points are deliberately not reported — "nothing calls it" is not one on a
framework codebase, where most handlers have no incoming call edge.
|
| cgis_find_symbolA | Resolve a partial symbol name to candidate FQNs (substring match, ranked). Call this BEFORE ``cgis_trace_flow`` / ``cgis_analyze_impact`` /
``cgis_get_structure`` when you know a short name (e.g.
``get_reservation_prices``) but not its full FQN — it removes the
read-the-file-first guesswork. Returns JSON ``[{fqn, name, type, file,
line}]`` ranked exact > prefix > substring. ``kind`` filters by node type
(FUNCTION / METHOD / CLASS / …); ``fqn_prefix`` scopes the search. A prefix
that starts with the directories the graph was ingested below (``src.cgis``
for a graph of ``src/``) and covers no code is read without them.
|
| cgis_init_ontologyA | Propose a starter patterns.yaml from the measured graph (read-only). Returns the YAML text — save it yourself (e.g. to patterns.yaml), review
the proposed labels, then run ``cgis_drift`` with it. Tolerances are the
measured scores plus ``margin``: a baseline to ratchet down, not a verdict.
No files are written; the caller decides where to persist the output.
|
| cgis_diff_checkA | What else depends on what a diff changed: subclasses, callers and data keys. Call this before review or merge. It reads the lines ``base..head`` changed,
finds the graph symbols they fall in (and symbols the diff removed, read from
the base text), and lists the classes that extend a changed class and the
functions that call a changed or removed function. For each data key the
changed lines read, write or declare (``doc["avatar"]``, a mapping field) it
lists the key's other readers, writers and declarers, reads of a dotted path
a mapping does not declare, and reads of a declaration the diff removed;
common keys (``id``, ``name``, or used in many files) are left out. Key items
need a graph ingested with data keys on (``cgis_ingest(data_keys=True)`` or
``cgis ingest --data-keys``); on a graph without them the answer says so and
lists subclasses and callers only. Sites in test files are counted, not
listed. An empty list means no static dependants were found, not that the
change is safe: dispatch the graph cannot see is not covered. How to read
the list: caller and subclass items are leads to read; data-key items are
noisy — measured on one application (owner-api), about a quarter of listed
items were worth a look (docs/lab-notes/2026-10-19-data-key-diff-check.md).
Before judging a code site that reads or writes a key unaffected, read its
callers (cgis_analyze_impact): they decide what values reach it.
|
| cgis_contextA | Prompt-ready brief on one FQN: its source, class, direct callers and callees. Call this before editing a symbol, instead of reading its files. It follows
calls only, one hop by default. Source is included when the file is found
(see ``source_root``), and the domain when the graph was tagged with one.
For a multi-hop subgraph
over calls, imports, inheritance and references without source, use
``cgis_trace_flow`` (downstream) or
``cgis_analyze_impact`` (upstream).
Returns an XML-tagged prompt — the focal node's source, its enclosing class,
its architectural domain boundary, direct callers (upstream ripple) and
callees (downstream dependencies) — meant to be injected into your context
window in place of raw file dumps. Far more token-efficient than reading
whole files, and structured so boundaries stay unambiguous.
Use ``cgis_ingest`` first if the database does not exist. ``source_root``
locates source files on disk when the graph was ingested from a
sub-directory (e.g. ``"src"`` after ``cgis ingest ./src``); it is safe to
pass even when the stored paths already start with that segment (#228).
When no candidate exists the ``<source>`` block degrades gracefully to
"unavailable".
|
| cgis_metricsA | Whole-graph architectural metrics — coupling bottlenecks, God classes, PageRank. Returns JSON ``{bottlenecks, god_classes, critical, file_coupling, class_cohesion,
resolution}``
computed with vectorized DuckDB aggregations over the whole graph (fan-in/fan-out
coupling, declared-member counts, PageRank, per-file Ca/Ce/instability) — the
global "what are the hotspots?" view that complements the node-local
trace/impact/context tools. Requires the optional ``duckdb`` extra; an
unavailable dependency is reported as a normal ❌ message.
``file_coupling`` counts *files*, not calls: Ca is how many other files
depend on a file (by IMPORTS or CALLS), Ce how many it depends on, and
instability ``I = Ce / (Ca + Ce)`` runs from 0 (stable, expensive to change)
to 1 (volatile); it is null for a file linked to no other.
``class_cohesion`` ranks classes by LCOM4: the number of groups their instance
methods fall into when linked by a shared ``self`` attribute or a call. 1 is
cohesive; 2+ is a class doing unrelated jobs. Dunders (``__init__`` above
all), abstract, static and class methods are not counted.
``resolution`` is the share of edges the resolver could not place, by the
same rule as ``cgis validate``. Read the rankings through it: a node whose
calls are mostly unresolved looks uncoupled because its edges point nowhere.
Under ``scope``/``exclude`` it counts the edges the selected code *emits*.
``exclude`` drops any node whose FQN contains one of the given dot-segments
(e.g. ``["tests"]`` removes both ``tests.*`` and ``domains.*.tests.*``) so
test/vendor scaffolding stays out of the rankings.
``scope`` is its complement: it keeps only nodes under one of the given
dot-prefixes, anchored and cut on a dot boundary, so
``["domains.reservation"]`` is that subtree and not
``domains.reservation_archive``. Use it for a per-domain review. The two
compose, and they differ where it matters for PageRank — ``exclude`` removes
nodes from the propagation graph, ``scope`` filters the rows and lets rank
propagate over the whole graph, so a scoped run reports how central the
subtree is *globally*. Coupling in-degree likewise keeps counting callers
from outside the scope, which is the ripple a domain review is after (#239).
|
| cgis_find_orphansA | Classes nothing in production builds, extends or names — dead-code candidates. Finds classes that no test, type checker or linter flags, because each is
still imported somewhere: a package re-export keeps a class importable long
after its last real caller is gone. On one mid-sized backend this reported
43 of 1 789 classes, and the hand-written equivalent's findings were all
real and all deleted.
Two filters decide the answer. **Tests are not users** — a class built only
by its own test is exactly the shape being hunted. **A re-export is not a
use** — ``IMPORTS_SYMBOL`` does not count, or nothing is ever reported. What
counts is construction (``CALLS``), inheritance (``EXTENDS``) and being named
(``REFERENCES`` — an annotation, or a class handed to a framework); the last
keeps abstract ports and Protocols off the list.
``prefix`` narrows to one package on a dot boundary. ``include_tests`` counts
test code as a user, turning the report into "unreachable from anywhere".
Machine-generated classes are **hidden by default**, and ``include_generated``
puts them back. The query is right about them — nothing constructs a
betterproto stub — but nobody hand-deletes one either, so they are noise
rather than a finding. Measured on owner-api at b7d02fe6, five of six
reported orphans were generated entities and the sixth a nested pydantic
``Config``: the unfiltered report had no actionable row in it (#432).
Only **module-level** classes are considered by default; ``include_nested``
adds classes defined inside a class or function. Across eight measured
repositories none of the 46 nested rows was dead: most were a ``Meta`` /
``Config`` a metaclass reads, the rest live classes reached as
``self.Nested(...)``, which the resolver does not follow (#432).
Returns JSON ``{orphans, considered, test_sources, generated_excluded,
nested_excluded}``;
each orphan carries ``fqn``/``file``/``line``. **A listing is a candidate for
deletion, not a proof** — a class named only inside a decorator (#429) or
arriving through a star import is invisible here, so the sweep errs towards
reporting a live class rather than hiding a dead one. ``test_sources: 0`` in a
repository that has tests means the graph predates the ``is_test`` column:
re-ingest. ``generated_excluded`` counts every generated class left out of
the population under the same ``prefix``, referenced or not — so ``0`` on a
repository with generated code means the same for ``is_generated``, which has
no backfill: the marker is in the file header, not in the database.
|
| cgis_audit_reachabilityA | Reachability/authorization audit — which sources never reach a checkpoint. The headline use is **IDOR/authz coverage**: list every route handler that does
NOT transitively reach an ownership check. Reachability follows behavioral edges
(CALLS *and* FastAPI ``Depends()`` DEPENDS_ON), so a guard wired via DI counts.
Select sources with ``from_type`` (the ``ROUTE_HANDLER`` role — FastAPI handlers on
a router built in the same module — or a NodeType like ``FUNCTION``) and/or
``from_prefix`` (FQN prefix) — at least
one is required. Returns JSON ``{target, covered, gaps}`` where each gap carries
``fqn``/``file``/``line``. Generalizes to validators, event tracking, or
service-layer-boundary rules by pointing ``target`` at the required node.
A selection that matches no source returns a ❌ message, not an empty
``{covered: [], gaps: []}`` that would read as a passing audit (#467).
|
| cgis_fractalA | Report the motif census across the repository's structural tiers. Coarsens the graph along its own structure — symbol, class, module, then
directory levels trimmed from the leaf end — and measures the 13-triad
census at every rung. Returns JSON: one entry per layer (IMPORTS, CALLS)
with the full per-rung curve (groups, triads, entropy in bits, dominant
motif, tangle ratio) and the fit.
``verdict`` is the sign of ``slope`` (entropy bits per halving of the group
count) outside a ``2 * std_error`` dead-band: ``hierarchical`` means
coarsening ADDS motif diversity, ``flat`` means it destroys it,
``scale_invariant`` means the mix is the same at every scale, and
``no_signal`` means fewer than three rungs carried enough triads to fit.
Read the curve, not just the verdict — the fit is a lossy summary of a
non-linear curve. Observe-only: this tool enforces nothing and no gate
reads it. Call after ``cgis_ingest``.
|