Symbol index with associated code reading tools for AI coding agent navigation
Project description
uncoded
AI coding agents navigate codebases poorly. They grep for guessed keywords, skim the first few lines of files, and fill gaps from pretraining rather than reading the actual code. The result is plausible-looking output built on a hallucinated understanding of code that's sitting right there, unread.
uncoded builds a static navigation index so agents can orient themselves at the start of a task and navigate deterministically to what they need — no guessing, no grep.
It also ships uncoded body to read symbol bodies and uncoded refs for
finding every reference to a symbol — callers, dead-symbol checks,
pre-rename footprint.
What it generates
Running uncoded sync produces:
.uncoded/namespace.yaml — a hierarchical YAML file listing every symbol:
directories, files, classes (with attributes and methods), functions. Covers
all configured source roots. An agent can load this at the start of a task
and immediately know the full vocabulary of the codebase.
.uncoded/stubs/ — one .pyi stub per source file, with imports, full
signatures (parameter names, types, return types), module constants, and
class attributes.
.claude/skills/coherence-review/SKILL.md and
.agents/skills/coherence-review/SKILL.md — a coherence review skill,
written to both Claude Code and Codex skill directories (see
Coherence review below).
uncoded also injects a navigation protocol into CLAUDE.md/AGENTS.md,
so agents working in the repo pick up the instructions automatically.
Install uv
uncoded runs via uv. Install uv if you don't
already have it; no separate uncoded install is needed — uvx runs it
from PyPI on demand.
Configure
Add a [tool.uncoded] section to your pyproject.toml:
[tool.uncoded]
source-roots = ["src", "tests"]
Use
uvx uncoded sync
Run it from the repo root. It reads pyproject.toml to find your source
roots, builds the index, and updates CLAUDE.md/AGENTS.md.
Commit the generated .uncoded/ directory so agents working
in the repo always have a current index.
Keep it current with pre-commit
Add uncoded sync as a pre-commit hook so the index stays in sync
automatically:
- repo: local
hooks:
- id: uncoded
name: uncoded
entry: uvx uncoded sync
language: system
pass_filenames: false
Like ruff format: if uncoded sync modifies any files, the commit
fails and you stage the updated index before committing again.
You can also set up your CI to run pre-commit run --all-files to verify
the index is up to date.
Verify the index is fresh
For CI or scripted checks that must not modify the working tree, use
the check subcommand:
uvx uncoded check
It runs the same pipeline but writes nothing. Exits 0 if every generated file is byte-identical to what a rebuild would produce, and 1 otherwise — printing which files would change. A stale index is a silent failure mode (agents read misleading names and signatures), so gating on this in CI is worthwhile even alongside a pre-commit hook.
Retrieve a symbol body
Use the body subcommand when you need a symbol's implementation, not just
its signature from the stub:
uvx uncoded body <name_path> --in <relative_path>
name_path is a slash-separated path: one segment (fn) for a top-level
symbol, two for a class member (Class/method). --in is the source file's
path (relative to cwd). The command prints the source text of the
symbol to stdout, byte-identical to what's on disk — no reformatting, no
ast.unparse normalisation.
For example, to retrieve the body of resolve_body from src/uncoded/body.py:
uvx uncoded body resolve_body --in src/uncoded/body.py
Find references to a symbol
Use the refs subcommand for impact analysis: run it before a rename,
signature change, or delete, or to confirm a symbol is dead before removing it:
uvx uncoded refs <name_path> --in <relative_path>
name_path follows the same convention as body: one segment for a top-level
symbol, two for a class member (Class/method). Output is one reference per
line as <rel_path>:<line>:<col>, with line and column 1-indexed and results
sorted by path, then line, then column. Exits 0 on success; empty output
means no references.
For example, to find all callers of resolve_body:
uvx uncoded refs resolve_body --in src/uncoded/body.py
How agents use it
When uncoded is set up, a navigation section is automatically maintained in
the configured instruction files (by default, CLAUDE.md and AGENTS.md).
Agents following that protocol:
- Read
.uncoded/namespace.yamlto orient — every symbol, at a glance. - Read the relevant
.pyistubs to understand imports, signatures, constants, and class members. - Run
uvx uncoded body <name_path> --in <relative_path>when they need implementation detail for a specific symbol. - Run
uvx uncoded refs <name_path> --in <relative_path>to find every reference to a symbol — callers, dead-symbol checks. See Find references to a symbol for detail. - Edit a symbol using
Editwithuncoded body's output asold_string. - Rename across the codebase using
uncoded refsto enumerate every site, thenEditat each. - Safely delete by running
uncoded refsfirst — the output must be empty — thenEditto remove.
The split is deliberate: uncoded provides a stable map and signature index;
uncoded body resolves a symbol's source body; uncoded refs maps every
reference. No grep, no stale line-number coordinates, no offset arithmetic.
Coherence review
AI coding agents tend to leave codebases in an incoherent state: names that
no longer match behaviour, docstrings that describe stale signatures, dead
symbols, pattern changes applied in some places but not others. uncoded sync
installs a /coherence-review skill that runs a structured diagnostic sweep to
find these problems.
Invoke it in Claude Code:
/coherence-review
The review works in four sweeps:
- Orient — loads
namespace.yamland forms a vocabulary map. - Lexical — scans the namespace for naming inconsistency: concept
duplication, qualifier accretion (
_v2,_legacy,_final), vocabulary islands, name collision with drift. - Promissory — checks each public symbol's name / signature / docstring
triple for internal disagreement. Names and signatures come from the
stub; docstrings come from
uncoded body. - Structural — checks for boundary violations (private symbols imported across modules), overgrown public surfaces, cross-domain imports, and zero-reference public symbols.
Output is a timestamped Markdown report saved to .uncoded/reviews/, with
verbatim evidence and a confidence level (high / medium / low) for each
finding. The review only reports — it proposes no fixes. The human decides
what to follow up.
Dev setup
Clone, install dependencies, and wire up the pre-commit hooks:
git clone https://github.com/alimanfoo/uncoded
cd uncoded
uv sync --extra dev
uv run pre-commit install
Run the tests (branch coverage is enforced; see [tool.coverage.report] in
pyproject.toml for the threshold):
uv run pytest
To run a subset of tests without the coverage gate:
uv run pytest tests/test_stubs.py --no-cov
Run the same checks CI's lint job runs:
uv run pre-commit run --all-files
Note for Windows contributors
CLAUDE.md is a symlink to AGENTS.md (single source). macOS and Linux
handle this transparently; on Windows, git's core.symlinks setting must
be enabled, or the checkout writes CLAUDE.md as a plain file containing
the literal string AGENTS.md and the navigation section drifts.
Releasing
GitHub releases publish to PyPI through .github/workflows/publish.yml.
The workflow uses PyPI Trusted Publishing, so it does not need a PYPI_TOKEN
or any other long-lived publishing secret.
The PyPI trusted publisher is configured for:
- PyPI project:
uncoded - Owner:
alimanfoo - Repository:
uncoded - Workflow:
publish.yml - Environment:
pypi
To release, create and publish a GitHub release from the release tag. The
published release event builds the source distribution and wheel, then
uploads them to PyPI.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file uncoded-1.0.1.tar.gz.
File metadata
- Download URL: uncoded-1.0.1.tar.gz
- Upload date:
- Size: 84.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9e3254c84aeb04edec6b4838a43d775d40fd24ad175b2a62ba7c7211d9ce475b
|
|
| MD5 |
7b4e9cce3a4a1aced27a6f55b0284ef1
|
|
| BLAKE2b-256 |
45c15db6d866bf3a8ead291b2a5cfa9512a2e1fd50ae71a55c6359bdf1f91fdf
|
Provenance
The following attestation bundles were made for uncoded-1.0.1.tar.gz:
Publisher:
publish.yml on alimanfoo/uncoded
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
uncoded-1.0.1.tar.gz -
Subject digest:
9e3254c84aeb04edec6b4838a43d775d40fd24ad175b2a62ba7c7211d9ce475b - Sigstore transparency entry: 1572663724
- Sigstore integration time:
-
Permalink:
alimanfoo/uncoded@b5b3dca0b8f5edd470c562c870a90733c77d6646 -
Branch / Tag:
refs/tags/v1.0.1 - Owner: https://github.com/alimanfoo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b5b3dca0b8f5edd470c562c870a90733c77d6646 -
Trigger Event:
release
-
Statement type:
File details
Details for the file uncoded-1.0.1-py3-none-any.whl.
File metadata
- Download URL: uncoded-1.0.1-py3-none-any.whl
- Upload date:
- Size: 34.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bcfa860e2963658cec564e1bd12340829872c546e012870314c78bf1a336442c
|
|
| MD5 |
abdffdc54ff9935ae3b72ea4ef08b4a5
|
|
| BLAKE2b-256 |
ad61eaf3d12832ea01c8fea9c07e4c7044c329126b9d75f5c27a618d3f2f3da5
|
Provenance
The following attestation bundles were made for uncoded-1.0.1-py3-none-any.whl:
Publisher:
publish.yml on alimanfoo/uncoded
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
uncoded-1.0.1-py3-none-any.whl -
Subject digest:
bcfa860e2963658cec564e1bd12340829872c546e012870314c78bf1a336442c - Sigstore transparency entry: 1572663737
- Sigstore integration time:
-
Permalink:
alimanfoo/uncoded@b5b3dca0b8f5edd470c562c870a90733c77d6646 -
Branch / Tag:
refs/tags/v1.0.1 - Owner: https://github.com/alimanfoo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b5b3dca0b8f5edd470c562c870a90733c77d6646 -
Trigger Event:
release
-
Statement type: