Search/Build a Local Code Graph Tool That AI Agents Can Query

Build a Local Code Graph Tool That AI Agents Can Query

Use when building a tool that turns a codebase into a local graph for AI agents. Covers pulling symbols and links from syntax trees, installing into several clients and uninstalling cleanly, search answers agents can use, tests on real repos and platforms, and releases.

To use this AI rule
## Build the Code Graph Locally from Syntax Trees, Not AI Summaries Keep project analysis local and deterministic. Derive symbols, relationships, and files from syntax trees rather than language-model summaries, store project-scoped state in a local indexed database, and expose the resulting knowledge graph through a library, command-line interface, or agent protocol. Organize the system as a layered pipeline: - extraction parses source and emits canonical symbols; - resolution connects imports, names, frameworks, and other relationships; - graph queries answer callers, callees, paths, and impact; - context formatting prepares compact Markdown or JSON for consumers. Keep heavy parsing off the main thread. Route the layers through one public entry point, and make that entry point the only supported library surface. If one executable intentionally serves as installer, indexer, and protocol server, test all roles through that same artifact. Maintain one source of truth for agent-facing tool guidance, preferably the protocol initialization response. Do not duplicate the same instructions into every client's repository file, because copies drift and make upgrades or uninstall incomplete. ## Pin the Runtime and Ship Non-Code Files in the Build Discover and use the repository's documented build, development, cleanup, test, evaluation, and local CLI commands. Honor the pinned runtime range and fail clearly outside it; a newer major runtime is not automatically compatible, and running from source may require a stricter minimum than a bundled distribution. Treat runtime assets as part of the build contract. Copy database schemas, parser grammars, WebAssembly modules, and other non-code files into the distribution. Whenever a new asset is added, update the copy step or it will not ship. Start verification with a focused test file or name pattern, then run the directly affected suite. Keep evaluation suites separate when they build fixtures or score behavior rather than exercising ordinary unit tests. Use real storage behavior where storage semantics matter. Create isolated temporary directories, clean them after every test, and verify the actual database mode instead of masking it with mocks. ## Keep One Set of Node and Edge Types and a Small Public API Define canonical node and edge kinds in one schema. Extractors and resolvers must emit those exact strings; do not introduce near-duplicates that silently split queries, traversal, or serialized data. Keep the public API narrow. Re-export supported types from the public entry point, and require command-line and protocol adapters to drive the same core service rather than bypassing it. Make storage claims explicit and testable. If the product has one supported database backend with write-ahead logging and full-text search, report that live backend, keep any compatibility adapter thin, and do not imply an unimplemented native or WebAssembly fallback. When adding a language, framework, query, or schema field, update the owning extractor or resolver, public contract, serialization, tests, and user-facing guidance together. ## Install into Multiple AI Clients Without Breaking Settings, and Uninstall Cleanly Model each supported client as a target with its own configuration location, format, and protocol arguments. Add a new client through one target implementation and one registry entry rather than spreading conditionals through the installer. Edit configuration surgically. Preserve unrelated TOML tables and arrays, retain JSON-with-comments formatting and comments, prefer an existing supported file variant, and create the documented default only for a new installation. Installation must be idempotent, reinstallation must self-heal partial state, and uninstall must reverse only installer-owned changes. Use stable markers to remove obsolete instruction blocks, but keep current agent guidance in its single runtime source rather than writing another copy. Cover every target with parameterized contract tests for sibling preservation, byte-identical reruns, reversible uninstall, and partial-state recovery. Preserve client-specific working-directory fixes, such as injecting an explicit project path when the client launches protocol subprocesses from the wrong directory. ## Improve the Search Tools Agents Already Use Before building a retrieval feature, ask whether it makes a tool the agent already calls do more with the input the agent already supplies. Do not depend on the agent learning a new tool, adopting a different query style, or noticing low-salience examples and initialization prose. Use precise input for precise answers. A primary exploration tool should accept a compact set of symbol names, including qualified names, and connect the flow among them. A secondary node tool should return full source plus caller and callee context; when a name is ambiguous, return all plausible overloads in one response. Do not fold precise flow answers into a fuzzy natural-language tool that cannot disambiguate endpoints. Improve coverage beneath the tools agents already choose, including static extraction and evidence-based dynamic relationships. Optimize for wall-clock latency and tool-call count as well as token use. Measure total usage across turns, not only the final response, because savings come from fewer turns over a smaller accumulated context. ## Return Answers That Keep Agents from Falling Back to Reading Files Make expected conditions success-shaped. An unindexed project, missing symbol, or file outside the index should return actionable guidance without an error flag. Reserve true errors for security refusals and genuine malfunctions, and attach a bounded retry instruction only when retry is appropriate. Expose the tool surface even when the current root is not indexed. Explain how to select an indexed project, but leave indexing as a user decision. Hiding tools at startup can break monorepos and sessions that began before indexing. Scale both exploration-call and output budgets monotonically with repository size: - fewer than 500 files: 1 call, about 18,000 characters, and 3,800 characters per file; - 500–4,999 files: 2 calls, about 28,000 characters, and 6,500 characters per file; - 5,000–14,999 files: 3 calls, about 35,000 characters, and 7,000 characters per file; - 15,000–24,999 files: 4 calls, about 38,000 characters, and 7,000 characters per file; - 25,000 files or more: at most 5 calls, about 38,000 characters, and 7,000 characters per file. A larger tier must never receive a smaller per-file budget than a smaller tier. Never tell the agent to fall back to raw file reading; direct it to another graph call and state that returned source should be treated as already read. ## Connect Callback and Event Flows from Start to Finish Static parsing misses callbacks, observers, event emitters, reactive rendering, descriptors, and other indirect dispatch. Add narrowly scoped synthesizers or framework resolvers only when they can connect the canonical flow with defensible evidence. Mark every synthesized edge as heuristic, record which mechanism produced it, and include the registration or wiring site. Surface that provenance with the flow so consumers can distinguish parsed facts from inferred relationships. Partial coverage is worse than none when it exposes one indirect hop but leaves the next boundary broken. Close the flow from its initiating action to its visible effect, remeasure the agent, and do not ship a half-bridged path that still forces manual reconstruction. Prefer silence to an incorrect edge at runtime boundaries that cannot be inferred safely. Verify that reindexing does not inflate node counts, and spot-check synthesized-edge precision before expanding a resolver. ## Test Agent Behavior on Real Repos, Windows, and Linux For every language and framework, test small, medium, and large real repositories with at least 3 distinct flow questions. Choose a canonical end-to-end flow for each framework rather than isolated symbols that avoid its hard dispatch boundaries. Run deterministic probes against the built artifact. Confirm that exploration connects the expected endpoints within budget, node counts remain stable across reindexing, and heuristic edges carry precise provenance. Run agent A/B tests at least 2 times per arm with the same deliberate floor model and reasoning effort. Record duration, total tool calls, raw file reads, and text searches; report ranges rather than a favorable single run. Prewarm a persistent protocol process when startup latency would otherwise hide the tool, and judge attachment by actual calls rather than an initialization snapshot. Require approximately zero raw reads and searches within the repository's exploration budget, faster completion than the control, and no regression on a control repository. Validate platform-specific behavior on the real platform. Gate Windows-only and non-Windows assertions explicitly. For Linux, build with fresh platform-native dependencies in a disposable container and use an init process so exited children are reaped. For Windows, use a local filesystem and fresh dependencies in a real virtual machine; compare suspicious failures against the default branch before assigning blame. ## Write Release Notes and Let the Automated Release Workflow Publish Write upcoming changes under an Unreleased section. Do not pre-create the next version block when the release workflow promotes Unreleased content automatically. Make release notes useful to users: - group content under user-facing feature, fix, security, or breaking-change headings only when applicable; - write one plain-language sentence per bullet explaining the capability or resolved symptom; - omit internal paths, implementation symbols, benchmark details, and empty sections; - retain useful issue references, supported platform names, and commands users actually enter. Let the automated release workflow synchronize lockfile versions, promote the changelog, build platform bundles, generate checksums, publish the release, and publish packages. Do not bump versions unless explicitly requested, and do not manually publish packages, push release commits, or create tags when those are workflow-owned actions. Before acting on an issue, review comment, or external report, anchor it to a date and compare released state, merged-but-unreleased state, and the current branch. Do not attribute an in-progress fix to what a reporter could see earlier. Treat product requirements as user decisions rather than facts inferred from the code graph. Version every documentation image URL and increment its query tag whenever the asset changes so caches cannot keep serving stale bytes.